17db96d56Sopenharmony_ci/* 27db96d56Sopenharmony_ci * Portable condition variable support for windows and pthreads. 37db96d56Sopenharmony_ci * Everything is inline, this header can be included where needed. 47db96d56Sopenharmony_ci * 57db96d56Sopenharmony_ci * APIs generally return 0 on success and non-zero on error, 67db96d56Sopenharmony_ci * and the caller needs to use its platform's error mechanism to 77db96d56Sopenharmony_ci * discover the error (errno, or GetLastError()) 87db96d56Sopenharmony_ci * 97db96d56Sopenharmony_ci * Note that some implementations cannot distinguish between a 107db96d56Sopenharmony_ci * condition variable wait time-out and successful wait. Most often 117db96d56Sopenharmony_ci * the difference is moot anyway since the wait condition must be 127db96d56Sopenharmony_ci * re-checked. 137db96d56Sopenharmony_ci * PyCOND_TIMEDWAIT, in addition to returning negative on error, 147db96d56Sopenharmony_ci * thus returns 0 on regular success, 1 on timeout 157db96d56Sopenharmony_ci * or 2 if it can't tell. 167db96d56Sopenharmony_ci * 177db96d56Sopenharmony_ci * There are at least two caveats with using these condition variables, 187db96d56Sopenharmony_ci * due to the fact that they may be emulated with Semaphores on 197db96d56Sopenharmony_ci * Windows: 207db96d56Sopenharmony_ci * 1) While PyCOND_SIGNAL() will wake up at least one thread, we 217db96d56Sopenharmony_ci * cannot currently guarantee that it will be one of the threads 227db96d56Sopenharmony_ci * already waiting in a PyCOND_WAIT() call. It _could_ cause 237db96d56Sopenharmony_ci * the wakeup of a subsequent thread to try a PyCOND_WAIT(), 247db96d56Sopenharmony_ci * including the thread doing the PyCOND_SIGNAL() itself. 257db96d56Sopenharmony_ci * The same applies to PyCOND_BROADCAST(), if N threads are waiting 267db96d56Sopenharmony_ci * then at least N threads will be woken up, but not necessarily 277db96d56Sopenharmony_ci * those already waiting. 287db96d56Sopenharmony_ci * For this reason, don't make the scheduling assumption that a 297db96d56Sopenharmony_ci * specific other thread will get the wakeup signal 307db96d56Sopenharmony_ci * 2) The _mutex_ must be held when calling PyCOND_SIGNAL() and 317db96d56Sopenharmony_ci * PyCOND_BROADCAST(). 327db96d56Sopenharmony_ci * While e.g. the posix standard strongly recommends that the mutex 337db96d56Sopenharmony_ci * associated with the condition variable is held when a 347db96d56Sopenharmony_ci * pthread_cond_signal() call is made, this is not a hard requirement, 357db96d56Sopenharmony_ci * although scheduling will not be "reliable" if it isn't. Here 367db96d56Sopenharmony_ci * the mutex is used for internal synchronization of the emulated 377db96d56Sopenharmony_ci * Condition Variable. 387db96d56Sopenharmony_ci */ 397db96d56Sopenharmony_ci 407db96d56Sopenharmony_ci#ifndef _CONDVAR_IMPL_H_ 417db96d56Sopenharmony_ci#define _CONDVAR_IMPL_H_ 427db96d56Sopenharmony_ci 437db96d56Sopenharmony_ci#include "Python.h" 447db96d56Sopenharmony_ci#include "pycore_condvar.h" 457db96d56Sopenharmony_ci 467db96d56Sopenharmony_ci#ifdef _POSIX_THREADS 477db96d56Sopenharmony_ci/* 487db96d56Sopenharmony_ci * POSIX support 497db96d56Sopenharmony_ci */ 507db96d56Sopenharmony_ci 517db96d56Sopenharmony_ci/* These private functions are implemented in Python/thread_pthread.h */ 527db96d56Sopenharmony_ciint _PyThread_cond_init(PyCOND_T *cond); 537db96d56Sopenharmony_civoid _PyThread_cond_after(long long us, struct timespec *abs); 547db96d56Sopenharmony_ci 557db96d56Sopenharmony_ci/* The following functions return 0 on success, nonzero on error */ 567db96d56Sopenharmony_ci#define PyMUTEX_INIT(mut) pthread_mutex_init((mut), NULL) 577db96d56Sopenharmony_ci#define PyMUTEX_FINI(mut) pthread_mutex_destroy(mut) 587db96d56Sopenharmony_ci#define PyMUTEX_LOCK(mut) pthread_mutex_lock(mut) 597db96d56Sopenharmony_ci#define PyMUTEX_UNLOCK(mut) pthread_mutex_unlock(mut) 607db96d56Sopenharmony_ci 617db96d56Sopenharmony_ci#define PyCOND_INIT(cond) _PyThread_cond_init(cond) 627db96d56Sopenharmony_ci#define PyCOND_FINI(cond) pthread_cond_destroy(cond) 637db96d56Sopenharmony_ci#define PyCOND_SIGNAL(cond) pthread_cond_signal(cond) 647db96d56Sopenharmony_ci#define PyCOND_BROADCAST(cond) pthread_cond_broadcast(cond) 657db96d56Sopenharmony_ci#define PyCOND_WAIT(cond, mut) pthread_cond_wait((cond), (mut)) 667db96d56Sopenharmony_ci 677db96d56Sopenharmony_ci/* return 0 for success, 1 on timeout, -1 on error */ 687db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 697db96d56Sopenharmony_ciPyCOND_TIMEDWAIT(PyCOND_T *cond, PyMUTEX_T *mut, long long us) 707db96d56Sopenharmony_ci{ 717db96d56Sopenharmony_ci struct timespec abs; 727db96d56Sopenharmony_ci _PyThread_cond_after(us, &abs); 737db96d56Sopenharmony_ci int ret = pthread_cond_timedwait(cond, mut, &abs); 747db96d56Sopenharmony_ci if (ret == ETIMEDOUT) { 757db96d56Sopenharmony_ci return 1; 767db96d56Sopenharmony_ci } 777db96d56Sopenharmony_ci if (ret) { 787db96d56Sopenharmony_ci return -1; 797db96d56Sopenharmony_ci } 807db96d56Sopenharmony_ci return 0; 817db96d56Sopenharmony_ci} 827db96d56Sopenharmony_ci 837db96d56Sopenharmony_ci#elif defined(NT_THREADS) 847db96d56Sopenharmony_ci/* 857db96d56Sopenharmony_ci * Windows (XP, 2003 server and later, as well as (hopefully) CE) support 867db96d56Sopenharmony_ci * 877db96d56Sopenharmony_ci * Emulated condition variables ones that work with XP and later, plus 887db96d56Sopenharmony_ci * example native support on VISTA and onwards. 897db96d56Sopenharmony_ci */ 907db96d56Sopenharmony_ci 917db96d56Sopenharmony_ci#if _PY_EMULATED_WIN_CV 927db96d56Sopenharmony_ci 937db96d56Sopenharmony_ci/* The mutex is a CriticalSection object and 947db96d56Sopenharmony_ci The condition variables is emulated with the help of a semaphore. 957db96d56Sopenharmony_ci 967db96d56Sopenharmony_ci This implementation still has the problem that the threads woken 977db96d56Sopenharmony_ci with a "signal" aren't necessarily those that are already 987db96d56Sopenharmony_ci waiting. It corresponds to listing 2 in: 997db96d56Sopenharmony_ci http://birrell.org/andrew/papers/ImplementingCVs.pdf 1007db96d56Sopenharmony_ci 1017db96d56Sopenharmony_ci Generic emulations of the pthread_cond_* API using 1027db96d56Sopenharmony_ci earlier Win32 functions can be found on the web. 1037db96d56Sopenharmony_ci The following read can be give background information to these issues, 1047db96d56Sopenharmony_ci but the implementations are all broken in some way. 1057db96d56Sopenharmony_ci http://www.cse.wustl.edu/~schmidt/win32-cv-1.html 1067db96d56Sopenharmony_ci*/ 1077db96d56Sopenharmony_ci 1087db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 1097db96d56Sopenharmony_ciPyMUTEX_INIT(PyMUTEX_T *cs) 1107db96d56Sopenharmony_ci{ 1117db96d56Sopenharmony_ci InitializeCriticalSection(cs); 1127db96d56Sopenharmony_ci return 0; 1137db96d56Sopenharmony_ci} 1147db96d56Sopenharmony_ci 1157db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 1167db96d56Sopenharmony_ciPyMUTEX_FINI(PyMUTEX_T *cs) 1177db96d56Sopenharmony_ci{ 1187db96d56Sopenharmony_ci DeleteCriticalSection(cs); 1197db96d56Sopenharmony_ci return 0; 1207db96d56Sopenharmony_ci} 1217db96d56Sopenharmony_ci 1227db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 1237db96d56Sopenharmony_ciPyMUTEX_LOCK(PyMUTEX_T *cs) 1247db96d56Sopenharmony_ci{ 1257db96d56Sopenharmony_ci EnterCriticalSection(cs); 1267db96d56Sopenharmony_ci return 0; 1277db96d56Sopenharmony_ci} 1287db96d56Sopenharmony_ci 1297db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 1307db96d56Sopenharmony_ciPyMUTEX_UNLOCK(PyMUTEX_T *cs) 1317db96d56Sopenharmony_ci{ 1327db96d56Sopenharmony_ci LeaveCriticalSection(cs); 1337db96d56Sopenharmony_ci return 0; 1347db96d56Sopenharmony_ci} 1357db96d56Sopenharmony_ci 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 1387db96d56Sopenharmony_ciPyCOND_INIT(PyCOND_T *cv) 1397db96d56Sopenharmony_ci{ 1407db96d56Sopenharmony_ci /* A semaphore with a "large" max value, The positive value 1417db96d56Sopenharmony_ci * is only needed to catch those "lost wakeup" events and 1427db96d56Sopenharmony_ci * race conditions when a timed wait elapses. 1437db96d56Sopenharmony_ci */ 1447db96d56Sopenharmony_ci cv->sem = CreateSemaphore(NULL, 0, 100000, NULL); 1457db96d56Sopenharmony_ci if (cv->sem==NULL) 1467db96d56Sopenharmony_ci return -1; 1477db96d56Sopenharmony_ci cv->waiting = 0; 1487db96d56Sopenharmony_ci return 0; 1497db96d56Sopenharmony_ci} 1507db96d56Sopenharmony_ci 1517db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 1527db96d56Sopenharmony_ciPyCOND_FINI(PyCOND_T *cv) 1537db96d56Sopenharmony_ci{ 1547db96d56Sopenharmony_ci return CloseHandle(cv->sem) ? 0 : -1; 1557db96d56Sopenharmony_ci} 1567db96d56Sopenharmony_ci 1577db96d56Sopenharmony_ci/* this implementation can detect a timeout. Returns 1 on timeout, 1587db96d56Sopenharmony_ci * 0 otherwise (and -1 on error) 1597db96d56Sopenharmony_ci */ 1607db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 1617db96d56Sopenharmony_ci_PyCOND_WAIT_MS(PyCOND_T *cv, PyMUTEX_T *cs, DWORD ms) 1627db96d56Sopenharmony_ci{ 1637db96d56Sopenharmony_ci DWORD wait; 1647db96d56Sopenharmony_ci cv->waiting++; 1657db96d56Sopenharmony_ci PyMUTEX_UNLOCK(cs); 1667db96d56Sopenharmony_ci /* "lost wakeup bug" would occur if the caller were interrupted here, 1677db96d56Sopenharmony_ci * but we are safe because we are using a semaphore which has an internal 1687db96d56Sopenharmony_ci * count. 1697db96d56Sopenharmony_ci */ 1707db96d56Sopenharmony_ci wait = WaitForSingleObjectEx(cv->sem, ms, FALSE); 1717db96d56Sopenharmony_ci PyMUTEX_LOCK(cs); 1727db96d56Sopenharmony_ci if (wait != WAIT_OBJECT_0) 1737db96d56Sopenharmony_ci --cv->waiting; 1747db96d56Sopenharmony_ci /* Here we have a benign race condition with PyCOND_SIGNAL. 1757db96d56Sopenharmony_ci * When failure occurs or timeout, it is possible that 1767db96d56Sopenharmony_ci * PyCOND_SIGNAL also decrements this value 1777db96d56Sopenharmony_ci * and signals releases the mutex. This is benign because it 1787db96d56Sopenharmony_ci * just means an extra spurious wakeup for a waiting thread. 1797db96d56Sopenharmony_ci * ('waiting' corresponds to the semaphore's "negative" count and 1807db96d56Sopenharmony_ci * we may end up with e.g. (waiting == -1 && sem.count == 1). When 1817db96d56Sopenharmony_ci * a new thread comes along, it will pass right through, having 1827db96d56Sopenharmony_ci * adjusted it to (waiting == 0 && sem.count == 0). 1837db96d56Sopenharmony_ci */ 1847db96d56Sopenharmony_ci 1857db96d56Sopenharmony_ci if (wait == WAIT_FAILED) 1867db96d56Sopenharmony_ci return -1; 1877db96d56Sopenharmony_ci /* return 0 on success, 1 on timeout */ 1887db96d56Sopenharmony_ci return wait != WAIT_OBJECT_0; 1897db96d56Sopenharmony_ci} 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 1927db96d56Sopenharmony_ciPyCOND_WAIT(PyCOND_T *cv, PyMUTEX_T *cs) 1937db96d56Sopenharmony_ci{ 1947db96d56Sopenharmony_ci int result = _PyCOND_WAIT_MS(cv, cs, INFINITE); 1957db96d56Sopenharmony_ci return result >= 0 ? 0 : result; 1967db96d56Sopenharmony_ci} 1977db96d56Sopenharmony_ci 1987db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 1997db96d56Sopenharmony_ciPyCOND_TIMEDWAIT(PyCOND_T *cv, PyMUTEX_T *cs, long long us) 2007db96d56Sopenharmony_ci{ 2017db96d56Sopenharmony_ci return _PyCOND_WAIT_MS(cv, cs, (DWORD)(us/1000)); 2027db96d56Sopenharmony_ci} 2037db96d56Sopenharmony_ci 2047db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2057db96d56Sopenharmony_ciPyCOND_SIGNAL(PyCOND_T *cv) 2067db96d56Sopenharmony_ci{ 2077db96d56Sopenharmony_ci /* this test allows PyCOND_SIGNAL to be a no-op unless required 2087db96d56Sopenharmony_ci * to wake someone up, thus preventing an unbounded increase of 2097db96d56Sopenharmony_ci * the semaphore's internal counter. 2107db96d56Sopenharmony_ci */ 2117db96d56Sopenharmony_ci if (cv->waiting > 0) { 2127db96d56Sopenharmony_ci /* notifying thread decreases the cv->waiting count so that 2137db96d56Sopenharmony_ci * a delay between notify and actual wakeup of the target thread 2147db96d56Sopenharmony_ci * doesn't cause a number of extra ReleaseSemaphore calls. 2157db96d56Sopenharmony_ci */ 2167db96d56Sopenharmony_ci cv->waiting--; 2177db96d56Sopenharmony_ci return ReleaseSemaphore(cv->sem, 1, NULL) ? 0 : -1; 2187db96d56Sopenharmony_ci } 2197db96d56Sopenharmony_ci return 0; 2207db96d56Sopenharmony_ci} 2217db96d56Sopenharmony_ci 2227db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2237db96d56Sopenharmony_ciPyCOND_BROADCAST(PyCOND_T *cv) 2247db96d56Sopenharmony_ci{ 2257db96d56Sopenharmony_ci int waiting = cv->waiting; 2267db96d56Sopenharmony_ci if (waiting > 0) { 2277db96d56Sopenharmony_ci cv->waiting = 0; 2287db96d56Sopenharmony_ci return ReleaseSemaphore(cv->sem, waiting, NULL) ? 0 : -1; 2297db96d56Sopenharmony_ci } 2307db96d56Sopenharmony_ci return 0; 2317db96d56Sopenharmony_ci} 2327db96d56Sopenharmony_ci 2337db96d56Sopenharmony_ci#else /* !_PY_EMULATED_WIN_CV */ 2347db96d56Sopenharmony_ci 2357db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2367db96d56Sopenharmony_ciPyMUTEX_INIT(PyMUTEX_T *cs) 2377db96d56Sopenharmony_ci{ 2387db96d56Sopenharmony_ci InitializeSRWLock(cs); 2397db96d56Sopenharmony_ci return 0; 2407db96d56Sopenharmony_ci} 2417db96d56Sopenharmony_ci 2427db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2437db96d56Sopenharmony_ciPyMUTEX_FINI(PyMUTEX_T *cs) 2447db96d56Sopenharmony_ci{ 2457db96d56Sopenharmony_ci return 0; 2467db96d56Sopenharmony_ci} 2477db96d56Sopenharmony_ci 2487db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2497db96d56Sopenharmony_ciPyMUTEX_LOCK(PyMUTEX_T *cs) 2507db96d56Sopenharmony_ci{ 2517db96d56Sopenharmony_ci AcquireSRWLockExclusive(cs); 2527db96d56Sopenharmony_ci return 0; 2537db96d56Sopenharmony_ci} 2547db96d56Sopenharmony_ci 2557db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2567db96d56Sopenharmony_ciPyMUTEX_UNLOCK(PyMUTEX_T *cs) 2577db96d56Sopenharmony_ci{ 2587db96d56Sopenharmony_ci ReleaseSRWLockExclusive(cs); 2597db96d56Sopenharmony_ci return 0; 2607db96d56Sopenharmony_ci} 2617db96d56Sopenharmony_ci 2627db96d56Sopenharmony_ci 2637db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2647db96d56Sopenharmony_ciPyCOND_INIT(PyCOND_T *cv) 2657db96d56Sopenharmony_ci{ 2667db96d56Sopenharmony_ci InitializeConditionVariable(cv); 2677db96d56Sopenharmony_ci return 0; 2687db96d56Sopenharmony_ci} 2697db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2707db96d56Sopenharmony_ciPyCOND_FINI(PyCOND_T *cv) 2717db96d56Sopenharmony_ci{ 2727db96d56Sopenharmony_ci return 0; 2737db96d56Sopenharmony_ci} 2747db96d56Sopenharmony_ci 2757db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2767db96d56Sopenharmony_ciPyCOND_WAIT(PyCOND_T *cv, PyMUTEX_T *cs) 2777db96d56Sopenharmony_ci{ 2787db96d56Sopenharmony_ci return SleepConditionVariableSRW(cv, cs, INFINITE, 0) ? 0 : -1; 2797db96d56Sopenharmony_ci} 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_ci/* This implementation makes no distinction about timeouts. Signal 2827db96d56Sopenharmony_ci * 2 to indicate that we don't know. 2837db96d56Sopenharmony_ci */ 2847db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2857db96d56Sopenharmony_ciPyCOND_TIMEDWAIT(PyCOND_T *cv, PyMUTEX_T *cs, long long us) 2867db96d56Sopenharmony_ci{ 2877db96d56Sopenharmony_ci return SleepConditionVariableSRW(cv, cs, (DWORD)(us/1000), 0) ? 2 : -1; 2887db96d56Sopenharmony_ci} 2897db96d56Sopenharmony_ci 2907db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2917db96d56Sopenharmony_ciPyCOND_SIGNAL(PyCOND_T *cv) 2927db96d56Sopenharmony_ci{ 2937db96d56Sopenharmony_ci WakeConditionVariable(cv); 2947db96d56Sopenharmony_ci return 0; 2957db96d56Sopenharmony_ci} 2967db96d56Sopenharmony_ci 2977db96d56Sopenharmony_ciPy_LOCAL_INLINE(int) 2987db96d56Sopenharmony_ciPyCOND_BROADCAST(PyCOND_T *cv) 2997db96d56Sopenharmony_ci{ 3007db96d56Sopenharmony_ci WakeAllConditionVariable(cv); 3017db96d56Sopenharmony_ci return 0; 3027db96d56Sopenharmony_ci} 3037db96d56Sopenharmony_ci 3047db96d56Sopenharmony_ci 3057db96d56Sopenharmony_ci#endif /* _PY_EMULATED_WIN_CV */ 3067db96d56Sopenharmony_ci 3077db96d56Sopenharmony_ci#endif /* _POSIX_THREADS, NT_THREADS */ 3087db96d56Sopenharmony_ci 3097db96d56Sopenharmony_ci#endif /* _CONDVAR_IMPL_H_ */ 310