Skip to content

Commit 2deb0f6

Browse files
committed
Use pthread_condattr_setclock if possible
1 parent 13f0ced commit 2deb0f6

4 files changed

Lines changed: 85 additions & 51 deletions

File tree

Makefile.pre.in

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -939,7 +939,8 @@ regen-opcode-targets:
939939
$(srcdir)/Python/opcode_targets.h.new
940940
$(UPDATE_FILE) $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/opcode_targets.h.new
941941

942-
Python/ceval.o: $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/ceval_gil.h
942+
Python/ceval.o: $(srcdir)/Python/opcode_targets.h $(srcdir)/Python/ceval_gil.h \
943+
$(srcdir)/Python/condvar.h
943944

944945
Python/frozen.o: $(srcdir)/Python/importlib.h $(srcdir)/Python/importlib_external.h \
945946
$(srcdir)/Python/importlib_zipimport.h
@@ -1838,7 +1839,7 @@ patchcheck: @DEF_MAKE_RULE@
18381839

18391840
# Dependencies
18401841

1841-
Python/thread.o: @THREADHEADERS@
1842+
Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h
18421843

18431844
# Declare targets that aren't real files
18441845
.PHONY: all build_all sharedmods check-clean-src oldsharedmods test quicktest

Python/ceval.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ PyEval_InitThreads(void)
171171
{
172172
if (gil_created())
173173
return;
174+
PyThread_init_thread();
174175
create_gil();
175176
take_gil(_PyThreadState_GET());
176177
_PyRuntime.ceval.pending.main_thread = PyThread_get_thread_ident();

Python/condvar.h

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -48,27 +48,18 @@
4848
* POSIX support
4949
*/
5050

51-
#define PyCOND_ADD_MICROSECONDS(tv, interval) \
52-
do { /* TODO: add overflow and truncation checks */ \
53-
tv.tv_usec += (long) interval; \
54-
tv.tv_sec += tv.tv_usec / 1000000; \
55-
tv.tv_usec %= 1000000; \
56-
} while (0)
57-
58-
/* We assume all modern POSIX systems have gettimeofday() */
59-
#ifdef GETTIMEOFDAY_NO_TZ
60-
#define PyCOND_GETTIMEOFDAY(ptv) gettimeofday(ptv)
61-
#else
62-
#define PyCOND_GETTIMEOFDAY(ptv) gettimeofday(ptv, (struct timezone *)NULL)
63-
#endif
51+
/* These private functions are implemented in Python/thread_pthread.h */
52+
int _PyThread_cond_init(PyCOND_T *cond);
53+
void _PyThread_cond_after(long long us, struct timespec *abs);
54+
int _PyThread_cond_timedwait(PyCOND_T *cond, PyMUTEX_T *mut, struct timespec *abs);
6455

6556
/* The following functions return 0 on success, nonzero on error */
6657
#define PyMUTEX_INIT(mut) pthread_mutex_init((mut), NULL)
6758
#define PyMUTEX_FINI(mut) pthread_mutex_destroy(mut)
6859
#define PyMUTEX_LOCK(mut) pthread_mutex_lock(mut)
6960
#define PyMUTEX_UNLOCK(mut) pthread_mutex_unlock(mut)
7061

71-
#define PyCOND_INIT(cond) pthread_cond_init((cond), NULL)
62+
#define PyCOND_INIT(cond) _PyThread_cond_init(cond)
7263
#define PyCOND_FINI(cond) pthread_cond_destroy(cond)
7364
#define PyCOND_SIGNAL(cond) pthread_cond_signal(cond)
7465
#define PyCOND_BROADCAST(cond) pthread_cond_broadcast(cond)
@@ -78,22 +69,9 @@ do { /* TODO: add overflow and truncation checks */ \
7869
Py_LOCAL_INLINE(int)
7970
PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long long us)
8071
{
81-
int r;
82-
struct timespec ts;
83-
struct timeval deadline;
84-
85-
PyCOND_GETTIMEOFDAY(&deadline);
86-
PyCOND_ADD_MICROSECONDS(deadline, us);
87-
ts.tv_sec = deadline.tv_sec;
88-
ts.tv_nsec = deadline.tv_usec * 1000;
89-
90-
r = pthread_cond_timedwait((cond), (mut), &ts);
91-
if (r == ETIMEDOUT)
92-
return 1;
93-
else if (r)
94-
return -1;
95-
else
96-
return 0;
72+
struct timespec abs;
73+
_PyThread_cond_after(us, &abs);
74+
return _PyThread_cond_timedwait(cond, mut, &abs);
9775
}
9876

9977
#elif defined(NT_THREADS)

Python/thread_pthread.h

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,6 @@
5656
#endif
5757
#endif
5858

59-
#if !defined(pthread_attr_default)
60-
# define pthread_attr_default ((pthread_attr_t *)NULL)
61-
#endif
62-
#if !defined(pthread_mutexattr_default)
63-
# define pthread_mutexattr_default ((pthread_mutexattr_t *)NULL)
64-
#endif
65-
#if !defined(pthread_condattr_default)
66-
# define pthread_condattr_default ((pthread_condattr_t *)NULL)
67-
#endif
68-
6959

7060
/* Whether or not to use semaphores directly rather than emulating them with
7161
* mutexes and condition variables:
@@ -110,6 +100,70 @@ do { \
110100
} while(0)
111101

112102

103+
/*
104+
* pthread_cond support
105+
*/
106+
107+
#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
108+
// monotonic is supported statically. It doesn't mean it works on runtime.
109+
#define CONDATTR_MONOTONIC
110+
#endif
111+
112+
// NULL when pthread_condattr_setclock(CLOCK_MONOTONIC) is not supported.
113+
static pthread_condattr_t *condattr_monotonic = NULL;
114+
115+
static void
116+
init_condattr()
117+
{
118+
#ifdef CONDATTR_MONOTONIC
119+
static pthread_condattr_t ca;
120+
pthread_condattr_init(&ca);
121+
if (pthread_condattr_setclock(&ca, CLOCK_MONOTONIC) == 0) {
122+
condattr_monotonic = &ca; // Use monotonic clock
123+
}
124+
#endif
125+
}
126+
127+
int
128+
_PyThread_cond_init(PyCOND_T *cond)
129+
{
130+
return pthread_cond_init(cond, condattr_monotonic);
131+
}
132+
133+
void
134+
_PyThread_cond_after(long long us, struct timespec *abs)
135+
{
136+
#ifdef CONDATTR_MONOTONIC
137+
if (condattr_monotonic) {
138+
clock_gettime(CLOCK_MONOTONIC, abs);
139+
abs->tv_sec += us / 1000000;
140+
abs->tv_nsec += (us % 1000000) * 1000;
141+
abs->tv_sec += abs->tv_nsec / 1000000000;
142+
abs->tv_nsec %= 1000000000;
143+
return;
144+
}
145+
#endif
146+
147+
struct timespec ts;
148+
MICROSECONDS_TO_TIMESPEC(us, ts);
149+
*abs = ts;
150+
}
151+
152+
int
153+
_PyThread_cond_timedwait(PyCOND_T *cond, PyMUTEX_T *mut, struct timespec *abs)
154+
{
155+
int r = pthread_cond_timedwait(cond, mut, abs);
156+
if (r == ETIMEDOUT) {
157+
return 1;
158+
}
159+
if (r) {
160+
return -1;
161+
}
162+
return 0;
163+
}
164+
165+
166+
113167
/* A pthread mutex isn't sufficient to model the Python lock type
114168
* because, according to Draft 5 of the docs (P1003.4a/D5), both of the
115169
* following are undefined:
@@ -146,6 +200,7 @@ PyThread__init_thread(void)
146200
extern void pthread_init(void);
147201
pthread_init();
148202
#endif
203+
init_condattr();
149204
}
150205

151206
/*
@@ -462,8 +517,7 @@ PyThread_allocate_lock(void)
462517
memset((void *)lock, '\0', sizeof(pthread_lock));
463518
lock->locked = 0;
464519

465-
status = pthread_mutex_init(&lock->mut,
466-
pthread_mutexattr_default);
520+
status = pthread_mutex_init(&lock->mut, NULL);
467521
CHECK_STATUS_PTHREAD("pthread_mutex_init");
468522
/* Mark the pthread mutex underlying a Python mutex as
469523
pure happens-before. We can't simply mark the
@@ -472,8 +526,7 @@ PyThread_allocate_lock(void)
472526
will cause errors. */
473527
_Py_ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(&lock->mut);
474528

475-
status = pthread_cond_init(&lock->lock_released,
476-
pthread_condattr_default);
529+
status = _PyThread_cond_init(&lock->lock_released);
477530
CHECK_STATUS_PTHREAD("pthread_cond_init");
478531

479532
if (error) {
@@ -532,18 +585,19 @@ PyThread_acquire_lock_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds,
532585
success = PY_LOCK_ACQUIRED;
533586
}
534587
else if (microseconds != 0) {
535-
struct timespec ts;
536-
if (microseconds > 0)
537-
MICROSECONDS_TO_TIMESPEC(microseconds, ts);
588+
struct timespec abs;
589+
if (microseconds > 0) {
590+
_PyThread_cond_after(microseconds, &abs);
591+
}
538592
/* continue trying until we get the lock */
539593

540594
/* mut must be locked by me -- part of the condition
541595
* protocol */
542596
while (success == PY_LOCK_FAILURE) {
543597
if (microseconds > 0) {
544-
status = pthread_cond_timedwait(
598+
status = _PyThread_cond_timedwait(
545599
&thelock->lock_released,
546-
&thelock->mut, &ts);
600+
&thelock->mut, &abs);
547601
if (status == ETIMEDOUT)
548602
break;
549603
CHECK_STATUS_PTHREAD("pthread_cond_timed_wait");

0 commit comments

Comments
 (0)