diff options
Diffstat (limited to 'Python/thread_nt.h')
-rw-r--r-- | Python/thread_nt.h | 381 |
1 files changed, 121 insertions, 260 deletions
diff --git a/Python/thread_nt.h b/Python/thread_nt.h index a5246dd..a161d7c 100644 --- a/Python/thread_nt.h +++ b/Python/thread_nt.h @@ -9,143 +9,85 @@ #include <process.h> #endif -/* options */ -#ifndef _PY_USE_CV_LOCKS -#define _PY_USE_CV_LOCKS 1 /* use locks based on cond vars */ -#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 { + LONG owned ; + DWORD thread_id ; + HANDLE hevent ; +} NRMUTEX, *PNRMUTEX ; -#include "condvar.h" -typedef struct _NRMUTEX -{ - PyMUTEX_T cs; - PyCOND_T cv; - int locked; -} NRMUTEX; -typedef NRMUTEX *PNRMUTEX; - -PNRMUTEX -AllocNonRecursiveMutex() +BOOL +InitializeNonRecursiveMutex(PNRMUTEX mutex) { - PNRMUTEX m = (PNRMUTEX)PyMem_RawMalloc(sizeof(NRMUTEX)); - if (!m) - return NULL; - if (PyCOND_INIT(&m->cv)) - goto fail; - if (PyMUTEX_INIT(&m->cs)) { - PyCOND_FINI(&m->cv); - goto fail; - } - m->locked = 0; - return m; -fail: - PyMem_RawFree(m); - return NULL; + mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */ + mutex->thread_id = 0 ; + mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ; + return mutex->hevent != NULL ; /* TRUE if the mutex is created */ } VOID -FreeNonRecursiveMutex(PNRMUTEX mutex) +DeleteNonRecursiveMutex(PNRMUTEX mutex) { - if (mutex) { - PyCOND_FINI(&mutex->cv); - PyMUTEX_FINI(&mutex->cs); - PyMem_RawFree(mutex); - } + /* No in-use check */ + CloseHandle(mutex->hevent) ; + mutex->hevent = NULL ; /* Just in case */ } DWORD -EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds) +EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait) { - DWORD result = WAIT_OBJECT_0; - if (PyMUTEX_LOCK(&mutex->cs)) - return WAIT_FAILED; - if (milliseconds == INFINITE) { - while (mutex->locked) { - if (PyCOND_WAIT(&mutex->cv, &mutex->cs)) { - result = WAIT_FAILED; - break; - } - } - } else if (milliseconds != 0) { - /* wait at least until the target */ - DWORD now, target = GetTickCount() + milliseconds; - while (mutex->locked) { - if (PyCOND_TIMEDWAIT(&mutex->cv, &mutex->cs, (long long)milliseconds*1000) < 0) { - result = WAIT_FAILED; - break; - } - now = GetTickCount(); - if (target <= now) - break; - milliseconds = target-now; - } + /* Assume that the thread waits successfully */ + DWORD ret ; + + /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */ + if (!wait) + { + if (InterlockedCompareExchange(&mutex->owned, 0, -1) != -1) + return WAIT_TIMEOUT ; + ret = WAIT_OBJECT_0 ; } - if (!mutex->locked) { - mutex->locked = 1; - result = WAIT_OBJECT_0; - } else if (result == WAIT_OBJECT_0) - result = WAIT_TIMEOUT; - /* else, it is WAIT_FAILED */ - PyMUTEX_UNLOCK(&mutex->cs); /* must ignore result here */ - return result; + else + ret = InterlockedIncrement(&mutex->owned) ? + /* Some thread owns the mutex, let's wait... */ + WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ; + + mutex->thread_id = GetCurrentThreadId() ; /* We own it */ + return ret ; } BOOL LeaveNonRecursiveMutex(PNRMUTEX mutex) { - BOOL result; - if (PyMUTEX_LOCK(&mutex->cs)) - return FALSE; - mutex->locked = 0; - /* condvar APIs return 0 on success. We need to return TRUE on success. */ - result = !PyCOND_SIGNAL(&mutex->cv); - PyMUTEX_UNLOCK(&mutex->cs); - return result; + /* 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 */ } -#else /* if ! _PY_USE_CV_LOCKS */ - -/* NR-locks based on a kernel mutex */ -#define PNRMUTEX HANDLE - PNRMUTEX -AllocNonRecursiveMutex() +AllocNonRecursiveMutex(void) { - return CreateSemaphore(NULL, 1, 1, NULL); + PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ; + if (mutex && !InitializeNonRecursiveMutex(mutex)) + { + free(mutex) ; + mutex = NULL ; + } + return mutex ; } -VOID +void FreeNonRecursiveMutex(PNRMUTEX mutex) { - /* No in-use check */ - CloseHandle(mutex); -} - -DWORD -EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds) -{ - return WaitForSingleObjectEx(mutex, milliseconds, FALSE); -} - -BOOL -LeaveNonRecursiveMutex(PNRMUTEX mutex) -{ - return ReleaseSemaphore(mutex, 1, NULL); + if (mutex) + { + DeleteNonRecursiveMutex(mutex) ; + free(mutex) ; + } } -#endif /* _PY_USE_CV_LOCKS */ -unsigned long PyThread_get_thread_ident(void); - -#ifdef PY_HAVE_THREAD_NATIVE_ID -unsigned long PyThread_get_thread_native_id(void); -#endif +long PyThread_get_thread_ident(void); /* * Initialization of the C package, should not be needed. @@ -166,7 +108,11 @@ typedef struct { /* thunker to call adapt between the function type used by the system's thread start function and the internally used one. */ +#if defined(MS_WINCE) +static DWORD WINAPI +#else static unsigned __stdcall +#endif bootstrap(void *call) { callobj *obj = (callobj*)call; @@ -177,52 +123,65 @@ bootstrap(void *call) return 0; } -unsigned long +long PyThread_start_new_thread(void (*func)(void *), void *arg) { HANDLE hThread; unsigned threadID; callobj *obj; - dprintf(("%lu: PyThread_start_new_thread called\n", + dprintf(("%ld: PyThread_start_new_thread called\n", PyThread_get_thread_ident())); if (!initialized) PyThread_init_thread(); obj = (callobj*)HeapAlloc(GetProcessHeap(), 0, sizeof(*obj)); if (!obj) - return PYTHREAD_INVALID_THREAD_ID; + return -1; obj->func = func; obj->arg = arg; - PyThreadState *tstate = _PyThreadState_GET(); - size_t stacksize = tstate ? tstate->interp->pythread_stacksize : 0; +#if defined(MS_WINCE) + hThread = CreateThread(NULL, + Py_SAFE_DOWNCAST(_pythread_stacksize, Py_ssize_t, SIZE_T), + bootstrap, obj, 0, &threadID); +#else hThread = (HANDLE)_beginthreadex(0, - Py_SAFE_DOWNCAST(stacksize, Py_ssize_t, unsigned int), + Py_SAFE_DOWNCAST(_pythread_stacksize, + Py_ssize_t, unsigned int), bootstrap, obj, 0, &threadID); +#endif if (hThread == 0) { +#if defined(MS_WINCE) + /* Save error in variable, to prevent PyThread_get_thread_ident + from clobbering it. */ + unsigned e = GetLastError(); + dprintf(("%ld: PyThread_start_new_thread failed, win32 error code %u\n", + PyThread_get_thread_ident(), e)); +#else /* I've seen errno == EAGAIN here, which means "there are * too many threads". */ int e = errno; - dprintf(("%lu: PyThread_start_new_thread failed, errno %d\n", + dprintf(("%ld: PyThread_start_new_thread failed, errno %d\n", PyThread_get_thread_ident(), e)); +#endif threadID = (unsigned)-1; HeapFree(GetProcessHeap(), 0, obj); } else { - dprintf(("%lu: PyThread_start_new_thread succeeded: %p\n", + dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n", PyThread_get_thread_ident(), (void*)hThread)); CloseHandle(hThread); } - return threadID; + return (long) threadID; } /* * Return the thread Id instead of a handle. The Id is said to uniquely identify the * thread in the system */ -unsigned long +long PyThread_get_thread_ident(void) { if (!initialized) @@ -231,36 +190,21 @@ PyThread_get_thread_ident(void) return GetCurrentThreadId(); } -#ifdef PY_HAVE_THREAD_NATIVE_ID -/* - * Return the native Thread ID (TID) of the calling thread. - * The native ID of a thread is valid and guaranteed to be unique system-wide - * from the time the thread is created until the thread has been terminated. - */ -unsigned long -PyThread_get_thread_native_id(void) -{ - if (!initialized) { - PyThread_init_thread(); - } - - DWORD native_id; - native_id = GetCurrentThreadId(); - return (unsigned long) native_id; -} -#endif - -void _Py_NO_RETURN +void PyThread_exit_thread(void) { - dprintf(("%lu: PyThread_exit_thread called\n", PyThread_get_thread_ident())); + dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident())); if (!initialized) exit(0); +#if defined(MS_WINCE) + ExitThread(0); +#else _endthreadex(0); +#endif } /* - * Lock support. It has to be implemented as semaphores. + * 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. */ @@ -275,7 +219,7 @@ PyThread_allocate_lock(void) aLock = AllocNonRecursiveMutex() ; - dprintf(("%lu: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock)); + dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock)); return (PyThread_type_lock) aLock; } @@ -283,7 +227,7 @@ PyThread_allocate_lock(void) void PyThread_free_lock(PyThread_type_lock aLock) { - dprintf(("%lu: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); + dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); FreeNonRecursiveMutex(aLock) ; } @@ -294,61 +238,32 @@ PyThread_free_lock(PyThread_type_lock aLock) * and 0 if the lock was not acquired. This means a 0 is returned * if the lock has already been acquired by this thread! */ -PyLockStatus -PyThread_acquire_lock_timed(PyThread_type_lock aLock, - PY_TIMEOUT_T microseconds, int intr_flag) +int +PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag) { - /* Fow now, intr_flag does nothing on Windows, and lock acquires are - * uninterruptible. */ - PyLockStatus success; - PY_TIMEOUT_T milliseconds; - - if (microseconds >= 0) { - milliseconds = microseconds / 1000; - if (microseconds % 1000 > 0) - ++milliseconds; - if (milliseconds > PY_DWORD_MAX) { - Py_FatalError("Timeout larger than PY_TIMEOUT_MAX"); - } - } - else { - milliseconds = INFINITE; - } + int success ; - dprintf(("%lu: PyThread_acquire_lock_timed(%p, %lld) called\n", - PyThread_get_thread_ident(), aLock, microseconds)); + dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag)); - if (aLock && EnterNonRecursiveMutex((PNRMUTEX)aLock, - (DWORD)milliseconds) == WAIT_OBJECT_0) { - success = PY_LOCK_ACQUIRED; - } - else { - success = PY_LOCK_FAILURE; - } + success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ; - dprintf(("%lu: PyThread_acquire_lock(%p, %lld) -> %d\n", - PyThread_get_thread_ident(), aLock, microseconds, success)); + dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success)); return success; } -int -PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag) -{ - return PyThread_acquire_lock_timed(aLock, waitflag ? -1 : 0, 0); -} void PyThread_release_lock(PyThread_type_lock aLock) { - dprintf(("%lu: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); + dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock))) - dprintf(("%lu: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError())); + dprintf(("%ld: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError())); } /* minimum/maximum thread stack sizes supported */ -#define THREAD_MIN_STACKSIZE 0x8000 /* 32 KiB */ -#define THREAD_MAX_STACKSIZE 0x10000000 /* 256 MiB */ +#define THREAD_MIN_STACKSIZE 0x8000 /* 32kB */ +#define THREAD_MAX_STACKSIZE 0x10000000 /* 256MB */ /* set the thread stack size. * Return 0 if size is valid, -1 otherwise. @@ -358,13 +273,13 @@ _pythread_nt_set_stacksize(size_t size) { /* set to default */ if (size == 0) { - _PyInterpreterState_GET_UNSAFE()->pythread_stacksize = 0; + _pythread_stacksize = 0; return 0; } /* valid range? */ if (size >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) { - _PyInterpreterState_GET_UNSAFE()->pythread_stacksize = size; + _pythread_stacksize = size; return 0; } @@ -374,18 +289,14 @@ _pythread_nt_set_stacksize(size_t size) #define THREAD_SET_STACKSIZE(x) _pythread_nt_set_stacksize(x) -/* Thread Local Storage (TLS) API - - This API is DEPRECATED since Python 3.7. See PEP 539 for details. -*/ +/* use native Windows TLS functions */ +#define Py_HAVE_NATIVE_TLS +#ifdef Py_HAVE_NATIVE_TLS int PyThread_create_key(void) { - DWORD result = TlsAlloc(); - if (result == TLS_OUT_OF_INDEXES) - return -1; - return (int)result; + return (int) TlsAlloc(); } void @@ -394,11 +305,24 @@ PyThread_delete_key(int key) TlsFree(key); } +/* We must be careful to emulate the strange semantics implemented in thread.c, + * where the value is only set if it hasn't been set before. + */ int PyThread_set_key_value(int key, void *value) { - BOOL ok = TlsSetValue(key, value); - return ok ? 0 : -1; + BOOL ok; + void *oldvalue; + + assert(value != NULL); + oldvalue = TlsGetValue(key); + if (oldvalue != NULL) + /* ignore value if already set */ + return 0; + ok = TlsSetValue(key, value); + if (!ok) + return -1; + return 0; } void * @@ -425,74 +349,11 @@ PyThread_delete_key_value(int key) TlsSetValue(key, NULL); } - /* reinitialization of TLS is not necessary after fork when using * the native TLS functions. And forking isn't supported on Windows either. */ void PyThread_ReInitTLS(void) -{ -} - - -/* Thread Specific Storage (TSS) API - - Platform-specific components of TSS API implementation. -*/ +{} -int -PyThread_tss_create(Py_tss_t *key) -{ - assert(key != NULL); - /* If the key has been created, function is silently skipped. */ - if (key->_is_initialized) { - return 0; - } - - DWORD result = TlsAlloc(); - if (result == TLS_OUT_OF_INDEXES) { - return -1; - } - /* In Windows, platform-specific key type is DWORD. */ - key->_key = result; - key->_is_initialized = 1; - return 0; -} - -void -PyThread_tss_delete(Py_tss_t *key) -{ - assert(key != NULL); - /* If the key has not been created, function is silently skipped. */ - if (!key->_is_initialized) { - return; - } - - TlsFree(key->_key); - key->_key = TLS_OUT_OF_INDEXES; - key->_is_initialized = 0; -} - -int -PyThread_tss_set(Py_tss_t *key, void *value) -{ - assert(key != NULL); - BOOL ok = TlsSetValue(key->_key, value); - return ok ? 0 : -1; -} - -void * -PyThread_tss_get(Py_tss_t *key) -{ - assert(key != NULL); - /* because TSS is used in the Py_END_ALLOW_THREAD macro, - * it is necessary to preserve the windows error state, because - * it is assumed to be preserved across the call to the macro. - * Ideally, the macro should be fixed, but it is simpler to - * do it here. - */ - DWORD error = GetLastError(); - void *result = TlsGetValue(key->_key); - SetLastError(error); - return result; -} +#endif |