/src/cpython/Python/condvar.h
Line | Count | Source (jump to first uncovered line) |
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_IMPL_H_ |
41 | | #define _CONDVAR_IMPL_H_ |
42 | | |
43 | | #include "Python.h" |
44 | | #include "pycore_pythread.h" // _POSIX_THREADS |
45 | | |
46 | | |
47 | | #ifdef _POSIX_THREADS |
48 | | /* |
49 | | * POSIX support |
50 | | */ |
51 | | |
52 | | /* These private functions are implemented in Python/thread_pthread.h */ |
53 | | int _PyThread_cond_init(PyCOND_T *cond); |
54 | | void _PyThread_cond_after(long long us, struct timespec *abs); |
55 | | |
56 | | /* The following functions return 0 on success, nonzero on error */ |
57 | 32 | #define PyMUTEX_INIT(mut) pthread_mutex_init((mut), NULL) |
58 | 0 | #define PyMUTEX_FINI(mut) pthread_mutex_destroy(mut) |
59 | 93.3k | #define PyMUTEX_LOCK(mut) pthread_mutex_lock(mut) |
60 | 93.3k | #define PyMUTEX_UNLOCK(mut) pthread_mutex_unlock(mut) |
61 | | |
62 | 32 | #define PyCOND_INIT(cond) _PyThread_cond_init(cond) |
63 | 0 | #define PyCOND_FINI(cond) pthread_cond_destroy(cond) |
64 | 62.2k | #define PyCOND_SIGNAL(cond) pthread_cond_signal(cond) |
65 | | #define PyCOND_BROADCAST(cond) pthread_cond_broadcast(cond) |
66 | 0 | #define PyCOND_WAIT(cond, mut) pthread_cond_wait((cond), (mut)) |
67 | | |
68 | | /* return 0 for success, 1 on timeout, -1 on error */ |
69 | | Py_LOCAL_INLINE(int) |
70 | | PyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long long us) |
71 | 0 | { |
72 | 0 | struct timespec abs_timeout; |
73 | 0 | _PyThread_cond_after(us, &abs_timeout); |
74 | 0 | int ret = pthread_cond_timedwait(cond, mut, &abs_timeout); |
75 | 0 | if (ret == ETIMEDOUT) { |
76 | 0 | return 1; |
77 | 0 | } |
78 | 0 | if (ret) { |
79 | 0 | return -1; |
80 | 0 | } |
81 | 0 | return 0; |
82 | 0 | } |
83 | | |
84 | | #elif defined(NT_THREADS) |
85 | | /* |
86 | | * Windows (XP, 2003 server and later, as well as (hopefully) CE) support |
87 | | * |
88 | | * Emulated condition variables ones that work with XP and later, plus |
89 | | * example native support on VISTA and onwards. |
90 | | */ |
91 | | |
92 | | #if _PY_EMULATED_WIN_CV |
93 | | |
94 | | /* The mutex is a CriticalSection object and |
95 | | The condition variables is emulated with the help of a semaphore. |
96 | | |
97 | | This implementation still has the problem that the threads woken |
98 | | with a "signal" aren't necessarily those that are already |
99 | | waiting. It corresponds to listing 2 in: |
100 | | http://birrell.org/andrew/papers/ImplementingCVs.pdf |
101 | | |
102 | | Generic emulations of the pthread_cond_* API using |
103 | | earlier Win32 functions can be found on the web. |
104 | | The following read can be give background information to these issues, |
105 | | but the implementations are all broken in some way. |
106 | | http://www.cse.wustl.edu/~schmidt/win32-cv-1.html |
107 | | */ |
108 | | |
109 | | Py_LOCAL_INLINE(int) |
110 | | PyMUTEX_INIT(PyMUTEX_T *cs) |
111 | | { |
112 | | InitializeCriticalSection(cs); |
113 | | return 0; |
114 | | } |
115 | | |
116 | | Py_LOCAL_INLINE(int) |
117 | | PyMUTEX_FINI(PyMUTEX_T *cs) |
118 | | { |
119 | | DeleteCriticalSection(cs); |
120 | | return 0; |
121 | | } |
122 | | |
123 | | Py_LOCAL_INLINE(int) |
124 | | PyMUTEX_LOCK(PyMUTEX_T *cs) |
125 | | { |
126 | | EnterCriticalSection(cs); |
127 | | return 0; |
128 | | } |
129 | | |
130 | | Py_LOCAL_INLINE(int) |
131 | | PyMUTEX_UNLOCK(PyMUTEX_T *cs) |
132 | | { |
133 | | LeaveCriticalSection(cs); |
134 | | return 0; |
135 | | } |
136 | | |
137 | | |
138 | | Py_LOCAL_INLINE(int) |
139 | | PyCOND_INIT(PyCOND_T *cv) |
140 | | { |
141 | | /* A semaphore with a "large" max value, The positive value |
142 | | * is only needed to catch those "lost wakeup" events and |
143 | | * race conditions when a timed wait elapses. |
144 | | */ |
145 | | cv->sem = CreateSemaphore(NULL, 0, 100000, NULL); |
146 | | if (cv->sem==NULL) |
147 | | return -1; |
148 | | cv->waiting = 0; |
149 | | return 0; |
150 | | } |
151 | | |
152 | | Py_LOCAL_INLINE(int) |
153 | | PyCOND_FINI(PyCOND_T *cv) |
154 | | { |
155 | | return CloseHandle(cv->sem) ? 0 : -1; |
156 | | } |
157 | | |
158 | | /* this implementation can detect a timeout. Returns 1 on timeout, |
159 | | * 0 otherwise (and -1 on error) |
160 | | */ |
161 | | Py_LOCAL_INLINE(int) |
162 | | _PyCOND_WAIT_MS(PyCOND_T *cv, PyMUTEX_T *cs, DWORD ms) |
163 | | { |
164 | | DWORD wait; |
165 | | cv->waiting++; |
166 | | PyMUTEX_UNLOCK(cs); |
167 | | /* "lost wakeup bug" would occur if the caller were interrupted here, |
168 | | * but we are safe because we are using a semaphore which has an internal |
169 | | * count. |
170 | | */ |
171 | | wait = WaitForSingleObjectEx(cv->sem, ms, FALSE); |
172 | | PyMUTEX_LOCK(cs); |
173 | | if (wait != WAIT_OBJECT_0) |
174 | | --cv->waiting; |
175 | | /* Here we have a benign race condition with PyCOND_SIGNAL. |
176 | | * When failure occurs or timeout, it is possible that |
177 | | * PyCOND_SIGNAL also decrements this value |
178 | | * and signals releases the mutex. This is benign because it |
179 | | * just means an extra spurious wakeup for a waiting thread. |
180 | | * ('waiting' corresponds to the semaphore's "negative" count and |
181 | | * we may end up with e.g. (waiting == -1 && sem.count == 1). When |
182 | | * a new thread comes along, it will pass right through, having |
183 | | * adjusted it to (waiting == 0 && sem.count == 0). |
184 | | */ |
185 | | |
186 | | if (wait == WAIT_FAILED) |
187 | | return -1; |
188 | | /* return 0 on success, 1 on timeout */ |
189 | | return wait != WAIT_OBJECT_0; |
190 | | } |
191 | | |
192 | | Py_LOCAL_INLINE(int) |
193 | | PyCOND_WAIT(PyCOND_T *cv, PyMUTEX_T *cs) |
194 | | { |
195 | | int result = _PyCOND_WAIT_MS(cv, cs, INFINITE); |
196 | | return result >= 0 ? 0 : result; |
197 | | } |
198 | | |
199 | | Py_LOCAL_INLINE(int) |
200 | | PyCOND_TIMEDWAIT(PyCOND_T *cv, PyMUTEX_T *cs, long long us) |
201 | | { |
202 | | return _PyCOND_WAIT_MS(cv, cs, (DWORD)(us/1000)); |
203 | | } |
204 | | |
205 | | Py_LOCAL_INLINE(int) |
206 | | PyCOND_SIGNAL(PyCOND_T *cv) |
207 | | { |
208 | | /* this test allows PyCOND_SIGNAL to be a no-op unless required |
209 | | * to wake someone up, thus preventing an unbounded increase of |
210 | | * the semaphore's internal counter. |
211 | | */ |
212 | | if (cv->waiting > 0) { |
213 | | /* notifying thread decreases the cv->waiting count so that |
214 | | * a delay between notify and actual wakeup of the target thread |
215 | | * doesn't cause a number of extra ReleaseSemaphore calls. |
216 | | */ |
217 | | cv->waiting--; |
218 | | return ReleaseSemaphore(cv->sem, 1, NULL) ? 0 : -1; |
219 | | } |
220 | | return 0; |
221 | | } |
222 | | |
223 | | Py_LOCAL_INLINE(int) |
224 | | PyCOND_BROADCAST(PyCOND_T *cv) |
225 | | { |
226 | | int waiting = cv->waiting; |
227 | | if (waiting > 0) { |
228 | | cv->waiting = 0; |
229 | | return ReleaseSemaphore(cv->sem, waiting, NULL) ? 0 : -1; |
230 | | } |
231 | | return 0; |
232 | | } |
233 | | |
234 | | #else /* !_PY_EMULATED_WIN_CV */ |
235 | | |
236 | | Py_LOCAL_INLINE(int) |
237 | | PyMUTEX_INIT(PyMUTEX_T *cs) |
238 | | { |
239 | | InitializeSRWLock(cs); |
240 | | return 0; |
241 | | } |
242 | | |
243 | | Py_LOCAL_INLINE(int) |
244 | | PyMUTEX_FINI(PyMUTEX_T *cs) |
245 | | { |
246 | | return 0; |
247 | | } |
248 | | |
249 | | Py_LOCAL_INLINE(int) |
250 | | PyMUTEX_LOCK(PyMUTEX_T *cs) |
251 | | { |
252 | | AcquireSRWLockExclusive(cs); |
253 | | return 0; |
254 | | } |
255 | | |
256 | | Py_LOCAL_INLINE(int) |
257 | | PyMUTEX_UNLOCK(PyMUTEX_T *cs) |
258 | | { |
259 | | ReleaseSRWLockExclusive(cs); |
260 | | return 0; |
261 | | } |
262 | | |
263 | | Py_LOCAL_INLINE(int) |
264 | | PyCOND_INIT(PyCOND_T *cv) |
265 | | { |
266 | | InitializeConditionVariable(cv); |
267 | | return 0; |
268 | | } |
269 | | |
270 | | Py_LOCAL_INLINE(int) |
271 | | PyCOND_FINI(PyCOND_T *cv) |
272 | | { |
273 | | return 0; |
274 | | } |
275 | | |
276 | | Py_LOCAL_INLINE(int) |
277 | | PyCOND_WAIT(PyCOND_T *cv, PyMUTEX_T *cs) |
278 | | { |
279 | | return SleepConditionVariableSRW(cv, cs, INFINITE, 0) ? 0 : -1; |
280 | | } |
281 | | |
282 | | /* return 0 for success, 1 on timeout, -1 on error */ |
283 | | Py_LOCAL_INLINE(int) |
284 | | PyCOND_TIMEDWAIT(PyCOND_T *cv, PyMUTEX_T *cs, long long us) |
285 | | { |
286 | | BOOL success = SleepConditionVariableSRW(cv, cs, (DWORD)(us/1000), 0); |
287 | | if (!success) { |
288 | | if (GetLastError() == ERROR_TIMEOUT) { |
289 | | return 1; |
290 | | } |
291 | | return -1; |
292 | | } |
293 | | return 0; |
294 | | } |
295 | | |
296 | | Py_LOCAL_INLINE(int) |
297 | | PyCOND_SIGNAL(PyCOND_T *cv) |
298 | | { |
299 | | WakeConditionVariable(cv); |
300 | | return 0; |
301 | | } |
302 | | |
303 | | Py_LOCAL_INLINE(int) |
304 | | PyCOND_BROADCAST(PyCOND_T *cv) |
305 | | { |
306 | | WakeAllConditionVariable(cv); |
307 | | return 0; |
308 | | } |
309 | | |
310 | | |
311 | | #endif /* _PY_EMULATED_WIN_CV */ |
312 | | |
313 | | #endif /* _POSIX_THREADS, NT_THREADS */ |
314 | | |
315 | | #endif /* _CONDVAR_IMPL_H_ */ |