diff --git a/Python/thread_nt.h b/Python/thread_nt.h --- a/Python/thread_nt.h +++ b/Python/thread_nt.h @@ -9,6 +9,221 @@ #include #endif +/* code for condition variables on windows. Provide the native ones available + * on VISTA and onwards, along with emulated ones that work with XP and later + */ + +#define _PY_EMULATED_WIN_CV 0 /* use emulated condition variables */ +#define _PY_USE_CV_LOCKS 1 /* use locks based on cond vars */ + +#if !defined NTDDI_VISTA || NTDDI_VERSION < NTDDI_VISTA +#undef _PY_EMULATED_WIN_CV +#define _PY_EMULATED_WIN_CV 1 +#endif + +/* the "critical section" object to use with the condition variable. + * use the fast SRWLock if available since it outperforms + * the classic CriticalSection objects + */ +#if _PY_EMULATED_WIN_CV +typedef CRITICAL_SECTION _PY_WIN_CS; +static __inline int +_PyWinCS_Init(_PY_WIN_CS *cs) +{ + InitializeCriticalSection(cs); + return 0; +} +static __inline void +_PyWinCS_Fini(_PY_WIN_CS *cs) +{ + DeleteCriticalSection(cs); +} +static __inline void +_PyWinCS_Acquire(_PY_WIN_CS *cs) +{ + EnterCriticalSection(cs); +} +static __inline void +_PyWinCS_Release(_PY_WIN_CS *cs) +{ + LeaveCriticalSection(cs); +} + +#else + +typedef SRWLOCK _PY_WIN_CS; +static __inline int +_PyWinCS_Init(_PY_WIN_CS *cs) +{ + InitializeSRWLock(cs); + return 0; +} +static __inline void +_PyWinCS_Fini(_PY_WIN_CS *cs) +{} +static __inline void +_PyWinCS_Acquire(_PY_WIN_CS *cs) +{ + AcquireSRWLockExclusive(cs); +} +static __inline void +_PyWinCS_Release(_PY_WIN_CS *cs) +{ + ReleaseSRWLockExclusive(cs); +} + +#endif + +/* The ConditionVariable object. From XP onwards it is easily emulated with + * a Semaphore + */ + +#if _PY_EMULATED_WIN_CV +typedef struct __PY_WIN_CV +{ + HANDLE sem; + int waiting; +} _PY_WIN_CV; + +static __inline int +_PyWinCV_Init(_PY_WIN_CV *cv) +{ + cv->sem = CreateSemaphore(NULL, 0, 100000, NULL); + if (cv->sem==NULL) + return -1; + cv->waiting = 0; + return 0; +} +static __inline void +_PyWinCV_Fini(_PY_WIN_CV *cv) +{ + CloseHandle(cv->sem); +} +static __inline int +_PyWinCV_Wait(_PY_WIN_CV *cv, _PY_WIN_CS *cs, DWORD ms) +{ + DWORD wait; + cv->waiting++; + _PyWinCS_Release(cs); + wait = WaitForSingleObject(cv->sem, ms); + _PyWinCS_Acquire(cs); + cv->waiting--; + return wait != WAIT_TIMEOUT; +} +static __inline void +_PyWinCV_Notify(_PY_WIN_CV *cv) +{ + if (cv->waiting) + ReleaseSemaphore(cv->sem, 1, NULL); +} + +#else + +typedef CONDITION_VARIABLE _PY_WIN_CV; + +static __inline int +_PyWinCV_Init(_PY_WIN_CV *cv) +{ + InitializeConditionVariable(cv); + return 0; +} +static __inline void +_PyWinCV_Fini(_PY_WIN_CV *cv) +{} +static __inline int +_PyWinCV_Wait(_PY_WIN_CV *cv, _PY_WIN_CS *cs, DWORD ms) +{ + return SleepConditionVariableSRW(cv, cs, ms, 0) != FALSE; +} +static __inline void +_PyWinCV_Notify(_PY_WIN_CV *cv) +{ + WakeConditionVariable(cv); +} + +#endif + + +/* Now, define a non-recursive mutex using either condition variables + * and critical sections (fast) or using operating system mutexes + * (slow) + */ + +#if _PY_USE_CV_LOCKS +typedef struct _NRMUTEX +{ + _PY_WIN_CS cs; + _PY_WIN_CV cv; + int locked; +} NRMUTEX; +typedef NRMUTEX *PNRMUTEX; + +PNRMUTEX +AllocNonRecursiveMutex() +{ + PNRMUTEX m = (PNRMUTEX)malloc(sizeof(NRMUTEX)); + if (!m) + return NULL; + if (_PyWinCV_Init(&m->cv)) { + free(m); + return NULL; + } + _PyWinCS_Init(&m->cs); + m->locked = 0; + return m; +} + +VOID +FreeNonRecursiveMutex(PNRMUTEX mutex) +{ + if (mutex) { + _PyWinCV_Fini(&mutex->cv); + _PyWinCS_Fini(&mutex->cs); + free(mutex); + } +} + +DWORD +EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds) +{ + DWORD result; + _PyWinCS_Acquire(&mutex->cs); + if (milliseconds == INFINITE) { + while (mutex->locked) + _PyWinCV_Wait(&mutex->cv, &mutex->cs, milliseconds); + } else if (milliseconds != 0) { + /* wait at least until the target */ + DWORD now, target = GetTickCount() + milliseconds; + while (mutex->locked) { + _PyWinCV_Wait(&mutex->cv, &mutex->cs, milliseconds); + now = GetTickCount(); + if (target <= now) + break; + milliseconds = target-now; + } + } + if (!mutex->locked) { + mutex->locked = 1; + result = WAIT_OBJECT_0; + } else + result = WAIT_TIMEOUT; + _PyWinCS_Release(&mutex->cs); + return result; +} + +BOOL +LeaveNonRecursiveMutex(PNRMUTEX mutex) +{ + _PyWinCS_Acquire(&mutex->cs); + mutex->locked = 0; + _PyWinCV_Notify(&mutex->cv); + _PyWinCS_Release(&mutex->cs); + return TRUE; +} + +#else + +/* NR-locks based on a kernel mutex */ #define PNRMUTEX HANDLE PNRMUTEX @@ -35,6 +250,7 @@ { return ReleaseSemaphore(mutex, 1, NULL); } +#endif long PyThread_get_thread_ident(void);