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