blob: 951b8ad47f07beaafb79c39498128ca8341e59ec [file] [log] [blame]
Serhiy Storchaka009b8112015-03-18 21:53:15 +02001/*
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +00002 * Portable condition variable support for windows and pthreads.
3 * Everything is inline, this header can be included where needed.
4 *
5 * APIs generally return 0 on success and non-zero on error,
6 * and the caller needs to use its platform's error mechanism to
7 * discover the error (errno, or GetLastError())
8 *
9 * Note that some implementations cannot distinguish between a
10 * condition variable wait time-out and successful wait. Most often
11 * the difference is moot anyway since the wait condition must be
12 * re-checked.
13 * PyCOND_TIMEDWAIT, in addition to returning negative on error,
14 * thus returns 0 on regular success, 1 on timeout
15 * or 2 if it can't tell.
Kristjan Valur Jonsson0006aac2012-06-19 16:30:28 +000016 *
17 * There are at least two caveats with using these condition variables,
18 * due to the fact that they may be emulated with Semaphores on
19 * Windows:
20 * 1) While PyCOND_SIGNAL() will wake up at least one thread, we
21 * cannot currently guarantee that it will be one of the threads
22 * already waiting in a PyCOND_WAIT() call. It _could_ cause
23 * the wakeup of a subsequent thread to try a PyCOND_WAIT(),
24 * including the thread doing the PyCOND_SIGNAL() itself.
25 * The same applies to PyCOND_BROADCAST(), if N threads are waiting
26 * then at least N threads will be woken up, but not necessarily
27 * those already waiting.
28 * For this reason, don't make the scheduling assumption that a
29 * specific other thread will get the wakeup signal
30 * 2) The _mutex_ must be held when calling PyCOND_SIGNAL() and
31 * PyCOND_BROADCAST().
32 * While e.g. the posix standard strongly recommends that the mutex
33 * associated with the condition variable is held when a
34 * pthread_cond_signal() call is made, this is not a hard requirement,
35 * although scheduling will not be "reliable" if it isn't. Here
36 * the mutex is used for internal synchronization of the emulated
37 * Condition Variable.
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +000038 */
39
Eric Snow2ebc5ce2017-09-07 23:51:28 -060040#ifndef _CONDVAR_IMPL_H_
41#define _CONDVAR_IMPL_H_
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +000042
43#include "Python.h"
Eric Snow2ebc5ce2017-09-07 23:51:28 -060044#include "internal/condvar.h"
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +000045
46#ifdef _POSIX_THREADS
47/*
48 * POSIX support
49 */
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +000050
51#define PyCOND_ADD_MICROSECONDS(tv, interval) \
Kristján Valur Jónsson33096fe2014-05-08 10:36:27 +000052do { /* TODO: add overflow and truncation checks */ \
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +000053 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
64
65/* The following functions return 0 on success, nonzero on error */
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +000066#define PyMUTEX_INIT(mut) pthread_mutex_init((mut), NULL)
67#define PyMUTEX_FINI(mut) pthread_mutex_destroy(mut)
68#define PyMUTEX_LOCK(mut) pthread_mutex_lock(mut)
69#define PyMUTEX_UNLOCK(mut) pthread_mutex_unlock(mut)
70
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +000071#define PyCOND_INIT(cond) pthread_cond_init((cond), NULL)
72#define PyCOND_FINI(cond) pthread_cond_destroy(cond)
73#define PyCOND_SIGNAL(cond) pthread_cond_signal(cond)
74#define PyCOND_BROADCAST(cond) pthread_cond_broadcast(cond)
75#define PyCOND_WAIT(cond, mut) pthread_cond_wait((cond), (mut))
76
77/* return 0 for success, 1 on timeout, -1 on error */
78Py_LOCAL_INLINE(int)
Benjamin Petersonaf580df2016-09-06 10:46:49 -070079PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long long us)
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +000080{
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;
Serhiy Storchaka009b8112015-03-18 21:53:15 +020095 else
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +000096 return 0;
97}
98
99#elif defined(NT_THREADS)
100/*
101 * Windows (XP, 2003 server and later, as well as (hopefully) CE) support
102 *
103 * Emulated condition variables ones that work with XP and later, plus
104 * example native support on VISTA and onwards.
105 */
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000106
107#if _PY_EMULATED_WIN_CV
108
109/* The mutex is a CriticalSection object and
110 The condition variables is emulated with the help of a semaphore.
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000111
Kristjan Valur Jonsson0006aac2012-06-19 16:30:28 +0000112 This implementation still has the problem that the threads woken
113 with a "signal" aren't necessarily those that are already
114 waiting. It corresponds to listing 2 in:
115 http://birrell.org/andrew/papers/ImplementingCVs.pdf
116
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000117 Generic emulations of the pthread_cond_* API using
118 earlier Win32 functions can be found on the Web.
Kristján Valur Jónsson32eccca2013-03-19 20:18:37 -0700119 The following read can be give background information to these issues,
120 but the implementations are all broken in some way.
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000121 http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
122*/
123
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000124Py_LOCAL_INLINE(int)
125PyMUTEX_INIT(PyMUTEX_T *cs)
126{
127 InitializeCriticalSection(cs);
128 return 0;
129}
130
131Py_LOCAL_INLINE(int)
132PyMUTEX_FINI(PyMUTEX_T *cs)
133{
134 DeleteCriticalSection(cs);
135 return 0;
136}
137
138Py_LOCAL_INLINE(int)
139PyMUTEX_LOCK(PyMUTEX_T *cs)
140{
141 EnterCriticalSection(cs);
142 return 0;
143}
144
145Py_LOCAL_INLINE(int)
146PyMUTEX_UNLOCK(PyMUTEX_T *cs)
147{
148 LeaveCriticalSection(cs);
149 return 0;
150}
151
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000152
153Py_LOCAL_INLINE(int)
154PyCOND_INIT(PyCOND_T *cv)
155{
156 /* A semaphore with a "large" max value, The positive value
157 * is only needed to catch those "lost wakeup" events and
158 * race conditions when a timed wait elapses.
159 */
160 cv->sem = CreateSemaphore(NULL, 0, 100000, NULL);
161 if (cv->sem==NULL)
162 return -1;
163 cv->waiting = 0;
164 return 0;
165}
166
167Py_LOCAL_INLINE(int)
168PyCOND_FINI(PyCOND_T *cv)
169{
170 return CloseHandle(cv->sem) ? 0 : -1;
171}
172
173/* this implementation can detect a timeout. Returns 1 on timeout,
174 * 0 otherwise (and -1 on error)
175 */
176Py_LOCAL_INLINE(int)
177_PyCOND_WAIT_MS(PyCOND_T *cv, PyMUTEX_T *cs, DWORD ms)
178{
179 DWORD wait;
180 cv->waiting++;
181 PyMUTEX_UNLOCK(cs);
182 /* "lost wakeup bug" would occur if the caller were interrupted here,
Raymond Hettinger15f44ab2016-08-30 10:47:49 -0700183 * but we are safe because we are using a semaphore which has an internal
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000184 * count.
185 */
Martin v. Löwisb26a9b12013-01-25 14:25:48 +0100186 wait = WaitForSingleObjectEx(cv->sem, ms, FALSE);
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000187 PyMUTEX_LOCK(cs);
188 if (wait != WAIT_OBJECT_0)
189 --cv->waiting;
190 /* Here we have a benign race condition with PyCOND_SIGNAL.
191 * When failure occurs or timeout, it is possible that
192 * PyCOND_SIGNAL also decrements this value
193 * and signals releases the mutex. This is benign because it
194 * just means an extra spurious wakeup for a waiting thread.
Kristjan Valur Jonsson16170772012-06-19 10:10:09 +0000195 * ('waiting' corresponds to the semaphore's "negative" count and
196 * we may end up with e.g. (waiting == -1 && sem.count == 1). When
197 * a new thread comes along, it will pass right throuhgh, having
198 * adjusted it to (waiting == 0 && sem.count == 0).
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000199 */
Serhiy Storchaka009b8112015-03-18 21:53:15 +0200200
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000201 if (wait == WAIT_FAILED)
202 return -1;
203 /* return 0 on success, 1 on timeout */
204 return wait != WAIT_OBJECT_0;
205}
206
207Py_LOCAL_INLINE(int)
208PyCOND_WAIT(PyCOND_T *cv, PyMUTEX_T *cs)
209{
210 int result = _PyCOND_WAIT_MS(cv, cs, INFINITE);
211 return result >= 0 ? 0 : result;
212}
213
214Py_LOCAL_INLINE(int)
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700215PyCOND_TIMEDWAIT(PyCOND_T *cv, PyMUTEX_T *cs, long long us)
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000216{
Kristján Valur Jónsson33096fe2014-05-08 10:36:27 +0000217 return _PyCOND_WAIT_MS(cv, cs, (DWORD)(us/1000));
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000218}
219
220Py_LOCAL_INLINE(int)
221PyCOND_SIGNAL(PyCOND_T *cv)
222{
Kristjan Valur Jonsson16170772012-06-19 10:10:09 +0000223 /* this test allows PyCOND_SIGNAL to be a no-op unless required
224 * to wake someone up, thus preventing an unbounded increase of
225 * the semaphore's internal counter.
226 */
227 if (cv->waiting > 0) {
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000228 /* notifying thread decreases the cv->waiting count so that
Kristjan Valur Jonsson16170772012-06-19 10:10:09 +0000229 * a delay between notify and actual wakeup of the target thread
230 * doesn't cause a number of extra ReleaseSemaphore calls.
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000231 */
232 cv->waiting--;
233 return ReleaseSemaphore(cv->sem, 1, NULL) ? 0 : -1;
234 }
235 return 0;
236}
237
238Py_LOCAL_INLINE(int)
239PyCOND_BROADCAST(PyCOND_T *cv)
240{
Kristján Valur Jónsson32eccca2013-03-19 20:18:37 -0700241 int waiting = cv->waiting;
242 if (waiting > 0) {
243 cv->waiting = 0;
244 return ReleaseSemaphore(cv->sem, waiting, NULL) ? 0 : -1;
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000245 }
246 return 0;
247}
248
Eric Snow2ebc5ce2017-09-07 23:51:28 -0600249#else /* !_PY_EMULATED_WIN_CV */
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000250
251Py_LOCAL_INLINE(int)
252PyMUTEX_INIT(PyMUTEX_T *cs)
253{
254 InitializeSRWLock(cs);
255 return 0;
256}
257
258Py_LOCAL_INLINE(int)
259PyMUTEX_FINI(PyMUTEX_T *cs)
260{
261 return 0;
262}
263
264Py_LOCAL_INLINE(int)
265PyMUTEX_LOCK(PyMUTEX_T *cs)
266{
267 AcquireSRWLockExclusive(cs);
268 return 0;
269}
270
271Py_LOCAL_INLINE(int)
272PyMUTEX_UNLOCK(PyMUTEX_T *cs)
273{
274 ReleaseSRWLockExclusive(cs);
275 return 0;
276}
277
278
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000279Py_LOCAL_INLINE(int)
280PyCOND_INIT(PyCOND_T *cv)
281{
282 InitializeConditionVariable(cv);
283 return 0;
284}
285Py_LOCAL_INLINE(int)
286PyCOND_FINI(PyCOND_T *cv)
287{
288 return 0;
289}
290
291Py_LOCAL_INLINE(int)
292PyCOND_WAIT(PyCOND_T *cv, PyMUTEX_T *cs)
293{
294 return SleepConditionVariableSRW(cv, cs, INFINITE, 0) ? 0 : -1;
295}
296
297/* This implementation makes no distinction about timeouts. Signal
298 * 2 to indicate that we don't know.
299 */
300Py_LOCAL_INLINE(int)
Benjamin Petersonaf580df2016-09-06 10:46:49 -0700301PyCOND_TIMEDWAIT(PyCOND_T *cv, PyMUTEX_T *cs, long long us)
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000302{
Kristján Valur Jónsson33096fe2014-05-08 10:36:27 +0000303 return SleepConditionVariableSRW(cv, cs, (DWORD)(us/1000), 0) ? 2 : -1;
Kristján Valur Jónssone75ff352012-06-18 20:30:44 +0000304}
305
306Py_LOCAL_INLINE(int)
307PyCOND_SIGNAL(PyCOND_T *cv)
308{
309 WakeConditionVariable(cv);
310 return 0;
311}
312
313Py_LOCAL_INLINE(int)
314PyCOND_BROADCAST(PyCOND_T *cv)
315{
316 WakeAllConditionVariable(cv);
317 return 0;
318}
319
320
321#endif /* _PY_EMULATED_WIN_CV */
322
323#endif /* _POSIX_THREADS, NT_THREADS */
324
Eric Snow2ebc5ce2017-09-07 23:51:28 -0600325#endif /* _CONDVAR_IMPL_H_ */