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