Index: thread_nt.h =================================================================== --- thread_nt.h (revision 72946) +++ thread_nt.h (working copy) @@ -2,6 +2,7 @@ /* This code implemented by Dag.Gruneau@elsa.preseco.comm.se */ /* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru */ /* Eliminated some memory leaks, gsw@agere.com */ +/* EXPERIMENTAL critical section & semaphore code by Phillip Sitbon, phillip@sitbon.net */ #include #include @@ -9,15 +10,120 @@ #include #endif -typedef struct NRMUTEX { +#define WITH_CRITICAL_SECTION_LOCKING_BREAKING_THREAD_LOCKS +// #define WITH_SEMAPHORE_LOCKING + +#ifdef WITH_CRITICAL_SECTION_LOCKING_BREAKING_THREAD_LOCKS + /* Implement locking as a pseudo-recursive critical section */ + +typedef CRITICAL_SECTION NTMUTEX; +typedef LPCRITICAL_SECTION PNTMUTEX; + +BOOL +InitializeNtMutex(PNTMUTEX mutex) +{ + InitializeCriticalSection(mutex); + /* Spin mutex would be: InitializeCriticalSectionAndSpinCount(mutex, SpinCount#); */ + return TRUE; /* failure is a segfault for this function */ +} + +VOID +DeleteNtMutex(PNTMUTEX mutex) +{ + /* ideally would set the pointer to zero here, might be a good reason to + encapsulate CRITICAL_SECTION */ + DeleteCriticalSection(mutex); +} + + +DWORD +EnterNtMutex(PNTMUTEX mutex, BOOL wait) +{ + const DWORD ThisThread = GetCurrentThreadId(); + + /* + We can only effectively disallow recursion when the + caller doesn't want to wait. There are two other options + in the presence of recursion: + (1) allow recursion no matter what, or + (2) force a deadlock (i.e. acquire an unused semaphore). + + Option 1 breaks code that does sanity checks assuming that + we can't lock it if we own it already without blocking. + Option 2 preserves semantics, but is not desirable. + + The solution here disallows recursion when wait == 0, and + silently allows it otherwise. + */ + + if ( !wait ) + { + /* warning: using NT internals. this value is always the thread id, + but we are bypassing the opaqueness of CRITICAL_SECTION */ + if ( ((DWORD)(mutex->OwningThread) == ThisThread) || + (TryEnterCriticalSection(mutex) == FALSE) ) /* try to acquire if not owner */ + return WAIT_TIMEOUT; /* recursion or unavailable */ + } + else /* wait always passed as INFINITE anyway, so ignore it */ + EnterCriticalSection(mutex); + + return WAIT_OBJECT_0; +} + +BOOL +LeaveNtMutex(PNTMUTEX mutex) +{ + /* It might be important here to fail if a thread not owning this object + tries to release it. It will work with events AFAIK, but not critical + sections. + */ + LeaveCriticalSection(mutex); + return TRUE; +} + +#elif defined(WITH_SEMAPHORE_LOCKING) + /* Implement locking as a semaphore */ + +typedef HANDLE NTMUTEX; +typedef LPHANDLE PNTMUTEX; + +BOOL +InitializeNtMutex(PNTMUTEX mutex) +{ + *mutex = CreateSemaphore(NULL, 1, LONG_MAX, NULL); + return *mutex != NULL; +} + +VOID +DeleteNtMutex(PNTMUTEX mutex) +{ + CloseHandle(*mutex); +} + + +DWORD +EnterNtMutex(PNTMUTEX mutex, DWORD wait) /* second parameter type changed to DWORD */ +{ + return WaitForSingleObject(*mutex, wait); +} + +BOOL +LeaveNtMutex(PNTMUTEX mutex) +{ + return ReleaseSemaphore(*mutex, 1, NULL); +} + +#else /* Use an event-based non-recursive mutex */ + +typedef struct NTMUTEX { LONG owned ; DWORD thread_id ; HANDLE hevent ; -} NRMUTEX, *PNRMUTEX ; +} NTMUTEX, *PNTMUTEX ; BOOL -InitializeNonRecursiveMutex(PNRMUTEX mutex) +InitializeNtMutex(PNTMUTEX mutex) { mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */ mutex->thread_id = 0 ; @@ -26,7 +132,7 @@ } VOID -DeleteNonRecursiveMutex(PNRMUTEX mutex) +DeleteNtMutex(PNTMUTEX mutex) { /* No in-use check */ CloseHandle(mutex->hevent) ; @@ -34,7 +140,7 @@ } DWORD -EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait) +EnterNtMutex(PNTMUTEX mutex, BOOL wait) { /* Assume that the thread waits successfully */ DWORD ret ; @@ -56,7 +162,7 @@ } BOOL -LeaveNonRecursiveMutex(PNRMUTEX mutex) +LeaveNtMutex(PNTMUTEX mutex) { /* We don't own the mutex */ mutex->thread_id = 0 ; @@ -65,11 +171,14 @@ SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */ } -PNRMUTEX -AllocNonRecursiveMutex(void) +#endif /* WITH_CRITICAL_SECTION_GIL */ + + +PNTMUTEX +AllocNtMutex(void) { - PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ; - if (mutex && !InitializeNonRecursiveMutex(mutex)) + PNTMUTEX mutex = (PNTMUTEX)malloc(sizeof(NTMUTEX)) ; + if (mutex && !InitializeNtMutex(mutex)) { free(mutex) ; mutex = NULL ; @@ -78,11 +187,11 @@ } void -FreeNonRecursiveMutex(PNRMUTEX mutex) +FreeNtMutex(PNTMUTEX mutex) { if (mutex) { - DeleteNonRecursiveMutex(mutex) ; + DeleteNtMutex(mutex) ; free(mutex) ; } } @@ -213,21 +322,16 @@ } #endif /* NO_EXIT_PROG */ -/* - * Lock support. It has too be implemented as semaphores. - * I [Dag] tried to implement it with mutex but I could find a way to - * tell whether a thread already own the lock or not. - */ PyThread_type_lock PyThread_allocate_lock(void) { - PNRMUTEX aLock; + PNTMUTEX aLock; dprintf(("PyThread_allocate_lock called\n")); if (!initialized) PyThread_init_thread(); - aLock = AllocNonRecursiveMutex() ; + aLock = AllocNtMutex() ; dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock)); @@ -239,7 +343,7 @@ { dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); - FreeNonRecursiveMutex(aLock) ; + FreeNtMutex(aLock) ; } /* @@ -255,7 +359,7 @@ dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag)); - success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ; + success = aLock && EnterNtMutex((PNTMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ; dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success)); @@ -267,7 +371,7 @@ { dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); - if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock))) + if (!(aLock && LeaveNtMutex((PNTMUTEX) aLock))) dprintf(("%ld: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError())); }