| #include "pycore_interp.h" // _PyInterpreterState.threads.stacksize | |
| #include "pycore_time.h" // _PyTime_AsMicroseconds() | |
| /* 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 */ | |
| #include <windows.h> | |
| #include <limits.h> | |
| #ifdef HAVE_PROCESS_H | |
| #include <process.h> | |
| #endif | |
| unsigned long PyThread_get_thread_ident(void); | |
| #ifdef PY_HAVE_THREAD_NATIVE_ID | |
| unsigned long PyThread_get_thread_native_id(void); | |
| #endif | |
| /* | |
| * Initialization for the current runtime. | |
| */ | |
| static void | |
| PyThread__init_thread(void) | |
| { | |
| // Initialization of the C package should not be needed. | |
| } | |
| /* | |
| * Thread support. | |
| */ | |
| typedef struct { | |
| void (*func)(void*); | |
| void *arg; | |
| } callobj; | |
| /* thunker to call adapt between the function type used by the system's | |
| thread start function and the internally used one. */ | |
| static unsigned __stdcall | |
| bootstrap(void *call) | |
| { | |
| callobj *obj = (callobj*)call; | |
| void (*func)(void*) = obj->func; | |
| void *arg = obj->arg; | |
| HeapFree(GetProcessHeap(), 0, obj); | |
| func(arg); | |
| return 0; | |
| } | |
| int | |
| PyThread_start_joinable_thread(void (*func)(void *), void *arg, | |
| PyThread_ident_t* ident, PyThread_handle_t* handle) { | |
| HANDLE hThread; | |
| unsigned threadID; | |
| callobj *obj; | |
| if (!initialized) | |
| PyThread_init_thread(); | |
| obj = (callobj*)HeapAlloc(GetProcessHeap(), 0, sizeof(*obj)); | |
| if (!obj) | |
| return -1; | |
| obj->func = func; | |
| obj->arg = arg; | |
| PyThreadState *tstate = _PyThreadState_GET(); | |
| size_t stacksize = tstate ? tstate->interp->threads.stacksize : 0; | |
| hThread = (HANDLE)_beginthreadex(0, | |
| Py_SAFE_DOWNCAST(stacksize, Py_ssize_t, unsigned int), | |
| bootstrap, obj, | |
| 0, &threadID); | |
| if (hThread == 0) { | |
| /* I've seen errno == EAGAIN here, which means "there are | |
| * too many threads". | |
| */ | |
| HeapFree(GetProcessHeap(), 0, obj); | |
| return -1; | |
| } | |
| *ident = threadID; | |
| // The cast is safe since HANDLE is pointer-sized | |
| *handle = (PyThread_handle_t) hThread; | |
| return 0; | |
| } | |
| unsigned long | |
| PyThread_start_new_thread(void (*func)(void *), void *arg) { | |
| PyThread_handle_t handle; | |
| PyThread_ident_t ident; | |
| if (PyThread_start_joinable_thread(func, arg, &ident, &handle)) { | |
| return PYTHREAD_INVALID_THREAD_ID; | |
| } | |
| CloseHandle((HANDLE) handle); | |
| // The cast is safe since the ident is really an unsigned int | |
| return (unsigned long) ident; | |
| } | |
| int | |
| PyThread_join_thread(PyThread_handle_t handle) { | |
| HANDLE hThread = (HANDLE) handle; | |
| int errored = (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0); | |
| CloseHandle(hThread); | |
| return errored; | |
| } | |
| int | |
| PyThread_detach_thread(PyThread_handle_t handle) { | |
| HANDLE hThread = (HANDLE) handle; | |
| return (CloseHandle(hThread) == 0); | |
| } | |
| /* | |
| * Return the thread Id instead of a handle. The Id is said to uniquely identify the | |
| * thread in the system | |
| */ | |
| PyThread_ident_t | |
| PyThread_get_thread_ident_ex(void) | |
| { | |
| if (!initialized) | |
| PyThread_init_thread(); | |
| return GetCurrentThreadId(); | |
| } | |
| unsigned long | |
| PyThread_get_thread_ident(void) | |
| { | |
| return (unsigned long) PyThread_get_thread_ident_ex(); | |
| } | |
| #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 | |
| PyThread_exit_thread(void) | |
| { | |
| if (!initialized) | |
| exit(0); | |
| _endthreadex(0); | |
| Py_UNREACHABLE(); | |
| } | |
| void _Py_NO_RETURN | |
| PyThread_hang_thread(void) | |
| { | |
| while (1) { | |
| SleepEx(INFINITE, TRUE); | |
| } | |
| } | |
| /* minimum/maximum thread stack sizes supported */ | |
| #define THREAD_MIN_STACKSIZE 0x8000 /* 32 KiB */ | |
| #define THREAD_MAX_STACKSIZE 0x10000000 /* 256 MiB */ | |
| /* set the thread stack size. | |
| * Return 0 if size is valid, -1 otherwise. | |
| */ | |
| static int | |
| _pythread_nt_set_stacksize(size_t size) | |
| { | |
| /* set to default */ | |
| if (size == 0) { | |
| _PyInterpreterState_GET()->threads.stacksize = 0; | |
| return 0; | |
| } | |
| /* valid range? */ | |
| if (size >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) { | |
| _PyInterpreterState_GET()->threads.stacksize = size; | |
| return 0; | |
| } | |
| return -1; | |
| } | |
| #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. | |
| */ | |
| int | |
| PyThread_create_key(void) | |
| { | |
| DWORD result = TlsAlloc(); | |
| if (result == TLS_OUT_OF_INDEXES) | |
| return -1; | |
| return (int)result; | |
| } | |
| void | |
| PyThread_delete_key(int key) | |
| { | |
| TlsFree(key); | |
| } | |
| int | |
| PyThread_set_key_value(int key, void *value) | |
| { | |
| BOOL ok = TlsSetValue(key, value); | |
| return ok ? 0 : -1; | |
| } | |
| void * | |
| PyThread_get_key_value(int key) | |
| { | |
| return TlsGetValue(key); | |
| } | |
| void | |
| PyThread_delete_key_value(int key) | |
| { | |
| /* NULL is used as "key missing", and it is also the default | |
| * given by TlsGetValue() if nothing has been set yet. | |
| */ | |
| 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); | |
| int err = GetLastError(); | |
| void *r = TlsGetValue(key->_key); | |
| if (r || !GetLastError()) { | |
| SetLastError(err); | |
| } | |
| return r; | |
| } |