Line data Source code
1 : /*
2 : * 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.
16 : *
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.
38 : */
39 :
40 : #ifndef _CONDVAR_H_
41 : #define _CONDVAR_H_
42 :
43 : #include "Python.h"
44 :
45 : #ifndef _POSIX_THREADS
46 : /* This means pthreads are not implemented in libc headers, hence the macro
47 : not present in unistd.h. But they still can be implemented as an external
48 : library (e.g. gnu pth in pthread emulation) */
49 : # ifdef HAVE_PTHREAD_H
50 : # include <pthread.h> /* _POSIX_THREADS */
51 : # endif
52 : #endif
53 :
54 : #ifdef _POSIX_THREADS
55 : /*
56 : * POSIX support
57 : */
58 : #define Py_HAVE_CONDVAR
59 :
60 : #include <pthread.h>
61 :
62 : #define PyCOND_ADD_MICROSECONDS(tv, interval) \
63 : do { \
64 : tv.tv_usec += (long) interval; \
65 : tv.tv_sec += tv.tv_usec / 1000000; \
66 : tv.tv_usec %= 1000000; \
67 : } while (0)
68 :
69 : /* We assume all modern POSIX systems have gettimeofday() */
70 : #ifdef GETTIMEOFDAY_NO_TZ
71 : #define PyCOND_GETTIMEOFDAY(ptv) gettimeofday(ptv)
72 : #else
73 : #define PyCOND_GETTIMEOFDAY(ptv) gettimeofday(ptv, (struct timezone *)NULL)
74 : #endif
75 :
76 : /* The following functions return 0 on success, nonzero on error */
77 : #define PyMUTEX_T pthread_mutex_t
78 : #define PyMUTEX_INIT(mut) pthread_mutex_init((mut), NULL)
79 : #define PyMUTEX_FINI(mut) pthread_mutex_destroy(mut)
80 : #define PyMUTEX_LOCK(mut) pthread_mutex_lock(mut)
81 : #define PyMUTEX_UNLOCK(mut) pthread_mutex_unlock(mut)
82 :
83 : #define PyCOND_T pthread_cond_t
84 : #define PyCOND_INIT(cond) pthread_cond_init((cond), NULL)
85 : #define PyCOND_FINI(cond) pthread_cond_destroy(cond)
86 : #define PyCOND_SIGNAL(cond) pthread_cond_signal(cond)
87 : #define PyCOND_BROADCAST(cond) pthread_cond_broadcast(cond)
88 : #define PyCOND_WAIT(cond, mut) pthread_cond_wait((cond), (mut))
89 :
90 : /* return 0 for success, 1 on timeout, -1 on error */
91 : Py_LOCAL_INLINE(int)
92 2 : PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long us)
93 : {
94 : int r;
95 : struct timespec ts;
96 : struct timeval deadline;
97 :
98 2 : PyCOND_GETTIMEOFDAY(&deadline);
99 2 : PyCOND_ADD_MICROSECONDS(deadline, us);
100 2 : ts.tv_sec = deadline.tv_sec;
101 2 : ts.tv_nsec = deadline.tv_usec * 1000;
102 :
103 2 : r = pthread_cond_timedwait((cond), (mut), &ts);
104 2 : if (r == ETIMEDOUT)
105 0 : return 1;
106 2 : else if (r)
107 0 : return -1;
108 : else
109 2 : return 0;
110 : }
111 :
112 : #elif defined(NT_THREADS)
113 : /*
114 : * Windows (XP, 2003 server and later, as well as (hopefully) CE) support
115 : *
116 : * Emulated condition variables ones that work with XP and later, plus
117 : * example native support on VISTA and onwards.
118 : */
119 : #define Py_HAVE_CONDVAR
120 :
121 :
122 : /* include windows if it hasn't been done before */
123 : #define WIN32_LEAN_AND_MEAN
124 : #include <windows.h>
125 :
126 : /* options */
127 : /* non-emulated condition variables are provided for those that want
128 : * to target Windows Vista. Modify this macro to enable them.
129 : */
130 : #ifndef _PY_EMULATED_WIN_CV
131 : #define _PY_EMULATED_WIN_CV 1 /* use emulated condition variables */
132 : #endif
133 :
134 : /* fall back to emulation if not targeting Vista */
135 : #if !defined NTDDI_VISTA || NTDDI_VERSION < NTDDI_VISTA
136 : #undef _PY_EMULATED_WIN_CV
137 : #define _PY_EMULATED_WIN_CV 1
138 : #endif
139 :
140 :
141 : #if _PY_EMULATED_WIN_CV
142 :
143 : /* The mutex is a CriticalSection object and
144 : The condition variables is emulated with the help of a semaphore.
145 : Semaphores are available on Windows XP (2003 server) and later.
146 : We use a Semaphore rather than an auto-reset event, because although
147 : an auto-resent event might appear to solve the lost-wakeup bug (race
148 : condition between releasing the outer lock and waiting) because it
149 : maintains state even though a wait hasn't happened, there is still
150 : a lost wakeup problem if more than one thread are interrupted in the
151 : critical place. A semaphore solves that, because its state is counted,
152 : not Boolean.
153 : Because it is ok to signal a condition variable with no one
154 : waiting, we need to keep track of the number of
155 : waiting threads. Otherwise, the semaphore's state could rise
156 : without bound. This also helps reduce the number of "spurious wakeups"
157 : that would otherwise happen.
158 :
159 : This implementation still has the problem that the threads woken
160 : with a "signal" aren't necessarily those that are already
161 : waiting. It corresponds to listing 2 in:
162 : http://birrell.org/andrew/papers/ImplementingCVs.pdf
163 :
164 : Generic emulations of the pthread_cond_* API using
165 : earlier Win32 functions can be found on the Web.
166 : The following read can be edificating (or not):
167 : http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
168 :
169 : See also
170 : */
171 :
172 : typedef CRITICAL_SECTION PyMUTEX_T;
173 :
174 : Py_LOCAL_INLINE(int)
175 : PyMUTEX_INIT(PyMUTEX_T *cs)
176 : {
177 : InitializeCriticalSection(cs);
178 : return 0;
179 : }
180 :
181 : Py_LOCAL_INLINE(int)
182 : PyMUTEX_FINI(PyMUTEX_T *cs)
183 : {
184 : DeleteCriticalSection(cs);
185 : return 0;
186 : }
187 :
188 : Py_LOCAL_INLINE(int)
189 : PyMUTEX_LOCK(PyMUTEX_T *cs)
190 : {
191 : EnterCriticalSection(cs);
192 : return 0;
193 : }
194 :
195 : Py_LOCAL_INLINE(int)
196 : PyMUTEX_UNLOCK(PyMUTEX_T *cs)
197 : {
198 : LeaveCriticalSection(cs);
199 : return 0;
200 : }
201 :
202 : /* The ConditionVariable object. From XP onwards it is easily emulated with
203 : * a Semaphore
204 : */
205 :
206 : typedef struct _PyCOND_T
207 : {
208 : HANDLE sem;
209 : int waiting; /* to allow PyCOND_SIGNAL to be a no-op */
210 : } PyCOND_T;
211 :
212 : Py_LOCAL_INLINE(int)
213 : PyCOND_INIT(PyCOND_T *cv)
214 : {
215 : /* A semaphore with a "large" max value, The positive value
216 : * is only needed to catch those "lost wakeup" events and
217 : * race conditions when a timed wait elapses.
218 : */
219 : cv->sem = CreateSemaphore(NULL, 0, 100000, NULL);
220 : if (cv->sem==NULL)
221 : return -1;
222 : cv->waiting = 0;
223 : return 0;
224 : }
225 :
226 : Py_LOCAL_INLINE(int)
227 : PyCOND_FINI(PyCOND_T *cv)
228 : {
229 : return CloseHandle(cv->sem) ? 0 : -1;
230 : }
231 :
232 : /* this implementation can detect a timeout. Returns 1 on timeout,
233 : * 0 otherwise (and -1 on error)
234 : */
235 : Py_LOCAL_INLINE(int)
236 : _PyCOND_WAIT_MS(PyCOND_T *cv, PyMUTEX_T *cs, DWORD ms)
237 : {
238 : DWORD wait;
239 : cv->waiting++;
240 : PyMUTEX_UNLOCK(cs);
241 : /* "lost wakeup bug" would occur if the caller were interrupted here,
242 : * but we are safe because we are using a semaphore wich has an internal
243 : * count.
244 : */
245 : wait = WaitForSingleObject(cv->sem, ms);
246 : PyMUTEX_LOCK(cs);
247 : if (wait != WAIT_OBJECT_0)
248 : --cv->waiting;
249 : /* Here we have a benign race condition with PyCOND_SIGNAL.
250 : * When failure occurs or timeout, it is possible that
251 : * PyCOND_SIGNAL also decrements this value
252 : * and signals releases the mutex. This is benign because it
253 : * just means an extra spurious wakeup for a waiting thread.
254 : * ('waiting' corresponds to the semaphore's "negative" count and
255 : * we may end up with e.g. (waiting == -1 && sem.count == 1). When
256 : * a new thread comes along, it will pass right throuhgh, having
257 : * adjusted it to (waiting == 0 && sem.count == 0).
258 : */
259 :
260 : if (wait == WAIT_FAILED)
261 : return -1;
262 : /* return 0 on success, 1 on timeout */
263 : return wait != WAIT_OBJECT_0;
264 : }
265 :
266 : Py_LOCAL_INLINE(int)
267 : PyCOND_WAIT(PyCOND_T *cv, PyMUTEX_T *cs)
268 : {
269 : int result = _PyCOND_WAIT_MS(cv, cs, INFINITE);
270 : return result >= 0 ? 0 : result;
271 : }
272 :
273 : Py_LOCAL_INLINE(int)
274 : PyCOND_TIMEDWAIT(PyCOND_T *cv, PyMUTEX_T *cs, long us)
275 : {
276 : return _PyCOND_WAIT_MS(cv, cs, us/1000);
277 : }
278 :
279 : Py_LOCAL_INLINE(int)
280 : PyCOND_SIGNAL(PyCOND_T *cv)
281 : {
282 : /* this test allows PyCOND_SIGNAL to be a no-op unless required
283 : * to wake someone up, thus preventing an unbounded increase of
284 : * the semaphore's internal counter.
285 : */
286 : if (cv->waiting > 0) {
287 : /* notifying thread decreases the cv->waiting count so that
288 : * a delay between notify and actual wakeup of the target thread
289 : * doesn't cause a number of extra ReleaseSemaphore calls.
290 : */
291 : cv->waiting--;
292 : return ReleaseSemaphore(cv->sem, 1, NULL) ? 0 : -1;
293 : }
294 : return 0;
295 : }
296 :
297 : Py_LOCAL_INLINE(int)
298 : PyCOND_BROADCAST(PyCOND_T *cv)
299 : {
300 : if (cv->waiting > 0) {
301 : return ReleaseSemaphore(cv->sem, cv->waiting, NULL) ? 0 : -1;
302 : cv->waiting = 0;
303 : }
304 : return 0;
305 : }
306 :
307 : #else
308 :
309 : /* Use native Win7 primitives if build target is Win7 or higher */
310 :
311 : /* SRWLOCK is faster and better than CriticalSection */
312 : typedef SRWLOCK PyMUTEX_T;
313 :
314 : Py_LOCAL_INLINE(int)
315 : PyMUTEX_INIT(PyMUTEX_T *cs)
316 : {
317 : InitializeSRWLock(cs);
318 : return 0;
319 : }
320 :
321 : Py_LOCAL_INLINE(int)
322 : PyMUTEX_FINI(PyMUTEX_T *cs)
323 : {
324 : return 0;
325 : }
326 :
327 : Py_LOCAL_INLINE(int)
328 : PyMUTEX_LOCK(PyMUTEX_T *cs)
329 : {
330 : AcquireSRWLockExclusive(cs);
331 : return 0;
332 : }
333 :
334 : Py_LOCAL_INLINE(int)
335 : PyMUTEX_UNLOCK(PyMUTEX_T *cs)
336 : {
337 : ReleaseSRWLockExclusive(cs);
338 : return 0;
339 : }
340 :
341 :
342 : typedef CONDITION_VARIABLE PyCOND_T;
343 :
344 : Py_LOCAL_INLINE(int)
345 : PyCOND_INIT(PyCOND_T *cv)
346 : {
347 : InitializeConditionVariable(cv);
348 : return 0;
349 : }
350 : Py_LOCAL_INLINE(int)
351 : PyCOND_FINI(PyCOND_T *cv)
352 : {
353 : return 0;
354 : }
355 :
356 : Py_LOCAL_INLINE(int)
357 : PyCOND_WAIT(PyCOND_T *cv, PyMUTEX_T *cs)
358 : {
359 : return SleepConditionVariableSRW(cv, cs, INFINITE, 0) ? 0 : -1;
360 : }
361 :
362 : /* This implementation makes no distinction about timeouts. Signal
363 : * 2 to indicate that we don't know.
364 : */
365 : Py_LOCAL_INLINE(int)
366 : PyCOND_TIMEDWAIT(PyCOND_T *cv, PyMUTEX_T *cs, long us)
367 : {
368 : return SleepConditionVariableSRW(cv, cs, us/1000, 0) ? 2 : -1;
369 : }
370 :
371 : Py_LOCAL_INLINE(int)
372 : PyCOND_SIGNAL(PyCOND_T *cv)
373 : {
374 : WakeConditionVariable(cv);
375 : return 0;
376 : }
377 :
378 : Py_LOCAL_INLINE(int)
379 : PyCOND_BROADCAST(PyCOND_T *cv)
380 : {
381 : WakeAllConditionVariable(cv);
382 : return 0;
383 : }
384 :
385 :
386 : #endif /* _PY_EMULATED_WIN_CV */
387 :
388 : #endif /* _POSIX_THREADS, NT_THREADS */
389 :
390 : #endif /* _CONDVAR_H_ */
|