bpo-12822: use monotonic clock for condvar if possible (GH-11723)

This commit is contained in:
Inada Naoki 2019-02-20 10:00:09 +09:00 committed by GitHub
parent 46a97920fe
commit 001fee14e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 87 additions and 51 deletions

View File

@ -939,7 +939,8 @@ regen-opcode-targets:
$(srcdir)/Python/opcode_targets.h.new $(srcdir)/Python/opcode_targets.h.new
$(UPDATE_FILE) $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/opcode_targets.h.new $(UPDATE_FILE) $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/opcode_targets.h.new
Python/ceval.o: $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/ceval_gil.h Python/ceval.o: $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/ceval_gil.h \
$(srcdir)/Python/condvar.h
Python/frozen.o: $(srcdir)/Python/importlib.h $(srcdir)/Python/importlib_external.h \ Python/frozen.o: $(srcdir)/Python/importlib.h $(srcdir)/Python/importlib_external.h \
$(srcdir)/Python/importlib_zipimport.h $(srcdir)/Python/importlib_zipimport.h
@ -1838,7 +1839,7 @@ patchcheck: @DEF_MAKE_RULE@
# Dependencies # Dependencies
Python/thread.o: @THREADHEADERS@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h
# Declare targets that aren't real files # Declare targets that aren't real files
.PHONY: all build_all sharedmods check-clean-src oldsharedmods test quicktest .PHONY: all build_all sharedmods check-clean-src oldsharedmods test quicktest

View File

@ -0,0 +1,2 @@
Use monotonic clock for ``pthread_cond_timedwait`` when
``pthread_condattr_setclock`` and ``CLOCK_MONOTONIC`` are available.

View File

@ -171,6 +171,7 @@ PyEval_InitThreads(void)
{ {
if (gil_created()) if (gil_created())
return; return;
PyThread_init_thread();
create_gil(); create_gil();
take_gil(_PyThreadState_GET()); take_gil(_PyThreadState_GET());
_PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident(); _PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();

View File

@ -48,19 +48,9 @@
* POSIX support * POSIX support
*/ */
#define PyCOND_ADD_MICROSECONDS(tv, interval) \ /* These private functions are implemented in Python/thread_pthread.h */
do { /* TODO: add overflow and truncation checks */ \ int _PyThread_cond_init(PyCOND_T *cond);
tv.tv_usec += (long) interval; \ void _PyThread_cond_after(long long us, struct timespec *abs);
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
/* The following functions return 0 on success, nonzero on error */ /* The following functions return 0 on success, nonzero on error */
#define PyMUTEX_INIT(mut) pthread_mutex_init((mut), NULL) #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_LOCK(mut) pthread_mutex_lock(mut)
#define PyMUTEX_UNLOCK(mut) pthread_mutex_unlock(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_FINI(cond) pthread_cond_destroy(cond)
#define PyCOND_SIGNAL(cond) pthread_cond_signal(cond) #define PyCOND_SIGNAL(cond) pthread_cond_signal(cond)
#define PyCOND_BROADCAST(cond) pthread_cond_broadcast(cond) #define PyCOND_BROADCAST(cond) pthread_cond_broadcast(cond)
@ -78,22 +68,16 @@ do { /* TODO: add overflow and truncation checks */ \
Py_LOCAL_INLINE(int) Py_LOCAL_INLINE(int)
PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long long us) PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long long us)
{ {
int r; struct timespec abs;
struct timespec ts; _PyThread_cond_after(us, &abs);
struct timeval deadline; int ret = pthread_cond_timedwait(cond, mut, &abs);
if (ret == ETIMEDOUT) {
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)
return 1; return 1;
else if (r) }
if (ret) {
return -1; return -1;
else }
return 0; return 0;
} }
#elif defined(NT_THREADS) #elif defined(NT_THREADS)

View File

@ -56,16 +56,6 @@
#endif #endif
#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 /* Whether or not to use semaphores directly rather than emulating them with
* mutexes and condition variables: * mutexes and condition variables:
@ -110,6 +100,56 @@ do { \
} while(0) } 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 /* 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 * because, according to Draft 5 of the docs (P1003.4a/D5), both of the
* following are undefined: * following are undefined:
@ -146,6 +186,7 @@ PyThread__init_thread(void)
extern void pthread_init(void); extern void pthread_init(void);
pthread_init(); pthread_init();
#endif #endif
init_condattr();
} }
/* /*
@ -462,8 +503,7 @@ PyThread_allocate_lock(void)
memset((void *)lock, '\0', sizeof(pthread_lock)); memset((void *)lock, '\0', sizeof(pthread_lock));
lock->locked = 0; lock->locked = 0;
status = pthread_mutex_init(&lock->mut, status = pthread_mutex_init(&lock->mut, NULL);
pthread_mutexattr_default);
CHECK_STATUS_PTHREAD("pthread_mutex_init"); CHECK_STATUS_PTHREAD("pthread_mutex_init");
/* Mark the pthread mutex underlying a Python mutex as /* Mark the pthread mutex underlying a Python mutex as
pure happens-before. We can't simply mark the pure happens-before. We can't simply mark the
@ -472,8 +512,7 @@ PyThread_allocate_lock(void)
will cause errors. */ will cause errors. */
_Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(&lock->mut); _Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(&lock->mut);
status = pthread_cond_init(&lock->lock_released, status = _PyThread_cond_init(&lock->lock_released);
pthread_condattr_default);
CHECK_STATUS_PTHREAD("pthread_cond_init"); CHECK_STATUS_PTHREAD("pthread_cond_init");
if (error) { if (error) {
@ -532,9 +571,10 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
success = PY_LOCK_ACQUIRED; success = PY_LOCK_ACQUIRED;
} }
else if (microseconds != 0) { else if (microseconds != 0) {
struct timespec ts; struct timespec abs;
if (microseconds > 0) if (microseconds > 0) {
MICROSECONDS_TO_TIMESPEC(microseconds, ts); _PyThread_cond_after(microseconds, &abs);
}
/* continue trying until we get the lock */ /* continue trying until we get the lock */
/* mut must be locked by me -- part of the condition /* 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) { if (microseconds > 0) {
status = pthread_cond_timedwait( status = pthread_cond_timedwait(
&thelock->lock_released, &thelock->lock_released,
&thelock->mut, &ts); &thelock->mut, &abs);
if (status == 1) {
break;
}
if (status == ETIMEDOUT) if (status == ETIMEDOUT)
break; break;
CHECK_STATUS_PTHREAD("pthread_cond_timed_wait"); CHECK_STATUS_PTHREAD("pthread_cond_timedwait");
} }
else { else {
status = pthread_cond_wait( status = pthread_cond_wait(

3
configure vendored
View File

@ -11448,7 +11448,8 @@ for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
memrchr mbrtowc mkdirat mkfifo \ memrchr mbrtowc mkdirat mkfifo \
mkfifoat mknod mknodat mktime mremap nice openat pathconf pause pipe2 plock poll \ mkfifoat mknod mknodat mktime mremap nice openat pathconf pause pipe2 plock poll \
posix_fallocate posix_fadvise posix_spawn posix_spawnp pread preadv preadv2 \ posix_fallocate posix_fadvise posix_spawn posix_spawnp pread preadv preadv2 \
pthread_init pthread_kill putenv pwrite pwritev pwritev2 readlink readlinkat readv realpath renameat \ pthread_condattr_setclock pthread_init pthread_kill putenv pwrite pwritev pwritev2 \
readlink readlinkat readv realpath renameat \
sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \
setgid sethostname \ setgid sethostname \
setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \ setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \

View File

@ -3506,7 +3506,8 @@ AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
memrchr mbrtowc mkdirat mkfifo \ memrchr mbrtowc mkdirat mkfifo \
mkfifoat mknod mknodat mktime mremap nice openat pathconf pause pipe2 plock poll \ mkfifoat mknod mknodat mktime mremap nice openat pathconf pause pipe2 plock poll \
posix_fallocate posix_fadvise posix_spawn posix_spawnp pread preadv preadv2 \ posix_fallocate posix_fadvise posix_spawn posix_spawnp pread preadv preadv2 \
pthread_init pthread_kill putenv pwrite pwritev pwritev2 readlink readlinkat readv realpath renameat \ pthread_condattr_setclock pthread_init pthread_kill putenv pwrite pwritev pwritev2 \
readlink readlinkat readv realpath renameat \
sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \
setgid sethostname \ setgid sethostname \
setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \ setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \

View File

@ -765,6 +765,9 @@
/* Define if your compiler supports function prototype */ /* Define if your compiler supports function prototype */
#undef HAVE_PROTOTYPES #undef HAVE_PROTOTYPES
/* Define to 1 if you have the `pthread_condattr_setclock' function. */
#undef HAVE_PTHREAD_CONDATTR_SETCLOCK
/* Defined for Solaris 2.6 bug in pthread header. */ /* Defined for Solaris 2.6 bug in pthread header. */
#undef HAVE_PTHREAD_DESTRUCTOR #undef HAVE_PTHREAD_DESTRUCTOR