mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
bpo-12822: use monotonic clock for condvar if possible (GH-11723)
This commit is contained in:
parent
46a97920fe
commit
001fee14e0
8 changed files with 87 additions and 51 deletions
|
@ -171,6 +171,7 @@ PyEval_InitThreads(void)
|
|||
{
|
||||
if (gil_created())
|
||||
return;
|
||||
PyThread_init_thread();
|
||||
create_gil();
|
||||
take_gil(_PyThreadState_GET());
|
||||
_PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();
|
||||
|
|
|
@ -48,19 +48,9 @@
|
|||
* POSIX support
|
||||
*/
|
||||
|
||||
#define PyCOND_ADD_MICROSECONDS(tv, interval) \
|
||||
do { /* TODO: add overflow and truncation checks */ \
|
||||
tv.tv_usec += (long) interval; \
|
||||
tv.tv_sec += tv.tv_usec / 1000000; \
|
||||
tv.tv_usec %= 1000000; \
|
||||
} while (0)
|
||||
|
||||
/* We assume all modern POSIX systems have gettimeofday() */
|
||||
#ifdef GETTIMEOFDAY_NO_TZ
|
||||
#define PyCOND_GETTIMEOFDAY(ptv) gettimeofday(ptv)
|
||||
#else
|
||||
#define PyCOND_GETTIMEOFDAY(ptv) gettimeofday(ptv, (struct timezone *)NULL)
|
||||
#endif
|
||||
/* These private functions are implemented in Python/thread_pthread.h */
|
||||
int _PyThread_cond_init(PyCOND_T *cond);
|
||||
void _PyThread_cond_after(long long us, struct timespec *abs);
|
||||
|
||||
/* The following functions return 0 on success, nonzero on error */
|
||||
#define PyMUTEX_INIT(mut) pthread_mutex_init((mut), NULL)
|
||||
|
@ -68,7 +58,7 @@ do { /* TODO: add overflow and truncation checks */ \
|
|||
#define PyMUTEX_LOCK(mut) pthread_mutex_lock(mut)
|
||||
#define PyMUTEX_UNLOCK(mut) pthread_mutex_unlock(mut)
|
||||
|
||||
#define PyCOND_INIT(cond) pthread_cond_init((cond), NULL)
|
||||
#define PyCOND_INIT(cond) _PyThread_cond_init(cond)
|
||||
#define PyCOND_FINI(cond) pthread_cond_destroy(cond)
|
||||
#define PyCOND_SIGNAL(cond) pthread_cond_signal(cond)
|
||||
#define PyCOND_BROADCAST(cond) pthread_cond_broadcast(cond)
|
||||
|
@ -78,22 +68,16 @@ do { /* TODO: add overflow and truncation checks */ \
|
|||
Py_LOCAL_INLINE(int)
|
||||
PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long long us)
|
||||
{
|
||||
int r;
|
||||
struct timespec ts;
|
||||
struct timeval deadline;
|
||||
|
||||
PyCOND_GETTIMEOFDAY(&deadline);
|
||||
PyCOND_ADD_MICROSECONDS(deadline, us);
|
||||
ts.tv_sec = deadline.tv_sec;
|
||||
ts.tv_nsec = deadline.tv_usec * 1000;
|
||||
|
||||
r = pthread_cond_timedwait((cond), (mut), &ts);
|
||||
if (r == ETIMEDOUT)
|
||||
struct timespec abs;
|
||||
_PyThread_cond_after(us, &abs);
|
||||
int ret = pthread_cond_timedwait(cond, mut, &abs);
|
||||
if (ret == ETIMEDOUT) {
|
||||
return 1;
|
||||
else if (r)
|
||||
}
|
||||
if (ret) {
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#elif defined(NT_THREADS)
|
||||
|
|
|
@ -56,16 +56,6 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(pthread_attr_default)
|
||||
# define pthread_attr_default ((pthread_attr_t *)NULL)
|
||||
#endif
|
||||
#if !defined(pthread_mutexattr_default)
|
||||
# define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL)
|
||||
#endif
|
||||
#if !defined(pthread_condattr_default)
|
||||
# define pthread_condattr_default ((pthread_condattr_t *)NULL)
|
||||
#endif
|
||||
|
||||
|
||||
/* Whether or not to use semaphores directly rather than emulating them with
|
||||
* mutexes and condition variables:
|
||||
|
@ -110,6 +100,56 @@ do { \
|
|||
} while(0)
|
||||
|
||||
|
||||
/*
|
||||
* pthread_cond support
|
||||
*/
|
||||
|
||||
#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
|
||||
// monotonic is supported statically. It doesn't mean it works on runtime.
|
||||
#define CONDATTR_MONOTONIC
|
||||
#endif
|
||||
|
||||
// NULL when pthread_condattr_setclock(CLOCK_MONOTONIC) is not supported.
|
||||
static pthread_condattr_t *condattr_monotonic = NULL;
|
||||
|
||||
static void
|
||||
init_condattr()
|
||||
{
|
||||
#ifdef CONDATTR_MONOTONIC
|
||||
static pthread_condattr_t ca;
|
||||
pthread_condattr_init(&ca);
|
||||
if (pthread_condattr_setclock(&ca, CLOCK_MONOTONIC) == 0) {
|
||||
condattr_monotonic = &ca; // Use monotonic clock
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
_PyThread_cond_init(PyCOND_T *cond)
|
||||
{
|
||||
return pthread_cond_init(cond, condattr_monotonic);
|
||||
}
|
||||
|
||||
void
|
||||
_PyThread_cond_after(long long us, struct timespec *abs)
|
||||
{
|
||||
#ifdef CONDATTR_MONOTONIC
|
||||
if (condattr_monotonic) {
|
||||
clock_gettime(CLOCK_MONOTONIC, abs);
|
||||
abs->tv_sec += us / 1000000;
|
||||
abs->tv_nsec += (us % 1000000) * 1000;
|
||||
abs->tv_sec += abs->tv_nsec / 1000000000;
|
||||
abs->tv_nsec %= 1000000000;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct timespec ts;
|
||||
MICROSECONDS_TO_TIMESPEC(us, ts);
|
||||
*abs = ts;
|
||||
}
|
||||
|
||||
|
||||
/* A pthread mutex isn't sufficient to model the Python lock type
|
||||
* because, according to Draft 5 of the docs (P1003.4a/D5), both of the
|
||||
* following are undefined:
|
||||
|
@ -146,6 +186,7 @@ PyThread__init_thread(void)
|
|||
extern void pthread_init(void);
|
||||
pthread_init();
|
||||
#endif
|
||||
init_condattr();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -462,8 +503,7 @@ PyThread_allocate_lock(void)
|
|||
memset((void *)lock, '\0', sizeof(pthread_lock));
|
||||
lock->locked = 0;
|
||||
|
||||
status = pthread_mutex_init(&lock->mut,
|
||||
pthread_mutexattr_default);
|
||||
status = pthread_mutex_init(&lock->mut, NULL);
|
||||
CHECK_STATUS_PTHREAD("pthread_mutex_init");
|
||||
/* Mark the pthread mutex underlying a Python mutex as
|
||||
pure happens-before. We can't simply mark the
|
||||
|
@ -472,8 +512,7 @@ PyThread_allocate_lock(void)
|
|||
will cause errors. */
|
||||
_Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(&lock->mut);
|
||||
|
||||
status = pthread_cond_init(&lock->lock_released,
|
||||
pthread_condattr_default);
|
||||
status = _PyThread_cond_init(&lock->lock_released);
|
||||
CHECK_STATUS_PTHREAD("pthread_cond_init");
|
||||
|
||||
if (error) {
|
||||
|
@ -532,9 +571,10 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
|
|||
success = PY_LOCK_ACQUIRED;
|
||||
}
|
||||
else if (microseconds != 0) {
|
||||
struct timespec ts;
|
||||
if (microseconds > 0)
|
||||
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
|
||||
struct timespec abs;
|
||||
if (microseconds > 0) {
|
||||
_PyThread_cond_after(microseconds, &abs);
|
||||
}
|
||||
/* continue trying until we get the lock */
|
||||
|
||||
/* mut must be locked by me -- part of the condition
|
||||
|
@ -543,10 +583,13 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
|
|||
if (microseconds > 0) {
|
||||
status = pthread_cond_timedwait(
|
||||
&thelock->lock_released,
|
||||
&thelock->mut, &ts);
|
||||
&thelock->mut, &abs);
|
||||
if (status == 1) {
|
||||
break;
|
||||
}
|
||||
if (status == ETIMEDOUT)
|
||||
break;
|
||||
CHECK_STATUS_PTHREAD("pthread_cond_timed_wait");
|
||||
CHECK_STATUS_PTHREAD("pthread_cond_timedwait");
|
||||
}
|
||||
else {
|
||||
status = pthread_cond_wait(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue