summaryrefslogtreecommitdiffstats
path: root/Python/thread_nt.h
diff options
context:
space:
mode:
Diffstat (limited to 'Python/thread_nt.h')
-rw-r--r--Python/thread_nt.h381
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