# HG changeset patch # Parent dfceb98767c0e5f492cd8f7d43becda48bac05c2 Make lock objects support timeouts properly diff -r dfceb98767c0 Python/thread_nt.h --- a/Python/thread_nt.h Sat Mar 19 17:47:26 2011 +0800 +++ b/Python/thread_nt.h Sun Mar 20 17:27:06 2011 +0000 @@ -11,7 +11,7 @@ typedef struct NRMUTEX { LONG owned ; - DWORD thread_id ; + LONG timeouts ; HANDLE hevent ; } NRMUTEX, *PNRMUTEX ; @@ -20,7 +20,7 @@ InitializeNonRecursiveMutex(PNRMUTEX mutex) { mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */ - mutex->thread_id = 0 ; + mutex->timeouts = 0; mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ; return mutex->hevent != NULL ; /* TRUE if the mutex is created */ } @@ -36,30 +36,55 @@ DWORD EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds) { - /* Assume that the thread waits successfully */ DWORD ret ; + LONG owned, timeouts ; - /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */ + /* mutex->owned - mutex->timeouts is an estimate (but never an + under-estimate) of the number threads currently waiting for the mutex. + -1 means unlocked and 0 means locked but no waiters. */ if (milliseconds == 0) { - if (InterlockedCompareExchange(&mutex->owned, 0, -1) != -1) + owned = InterlockedCompareExchange(&mutex->owned, 0, -1) ; + if (owned == -1) + goto success ; /* fast path */ + else if (owned - mutex->timeouts != -1) /* harmless race */ return WAIT_TIMEOUT ; - ret = WAIT_OBJECT_0 ; + /* fall through to slow path expecting to get lock */ } - else - ret = InterlockedIncrement(&mutex->owned) ? - /* Some thread owns the mutex, let's wait... */ - WaitForSingleObject(mutex->hevent, milliseconds) : WAIT_OBJECT_0 ; - mutex->thread_id = GetCurrentThreadId() ; /* We own it */ - return ret ; + if (InterlockedIncrement(&mutex->owned) == 0) + goto success ; /* fast path */ + + /* slow path */ + ret = WaitForSingleObject(mutex->hevent, milliseconds) ; + if (ret != WAIT_OBJECT_0) + { + /* XXX We should really worry about mutex->owned and + mutex->timeouts overflowing -- might happen if a thread + holds the lock for a *very* long time */ + InterlockedIncrement(&mutex->timeouts) ; + return ret ; + } + +success: + /* If mutex->timeouts is non-zero then it will prevent use of the + fast path. We attempt to rezero it (adjusting mutex->owned + accordingly) using atomic operations. Since we own the lock the + worst interference we might get from other threads is that they + might increment mutex->timeout without us noticing: but that is + harmless. */ + if ((timeouts = mutex->timeouts) != 0) + { + InterlockedExchangeAdd(&mutex->timeouts, -timeouts) ; + InterlockedExchangeAdd(&mutex->owned, -timeouts) ; + } + + return WAIT_OBJECT_0 ; } BOOL LeaveNonRecursiveMutex(PNRMUTEX mutex) { - /* We don't own the mutex */ - mutex->thread_id = 0 ; return InterlockedDecrement(&mutex->owned) < 0 || SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */