1#include "pycore_interp.h" // _PyInterpreterState.threads.stacksize 2 3/* This code implemented by Dag.Gruneau@elsa.preseco.comm.se */ 4/* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru */ 5/* Eliminated some memory leaks, gsw@agere.com */ 6 7#include <windows.h> 8#include <limits.h> 9#ifdef HAVE_PROCESS_H 10#include <process.h> 11#endif 12 13/* options */ 14#ifndef _PY_USE_CV_LOCKS 15#define _PY_USE_CV_LOCKS 1 /* use locks based on cond vars */ 16#endif 17 18/* Now, define a non-recursive mutex using either condition variables 19 * and critical sections (fast) or using operating system mutexes 20 * (slow) 21 */ 22 23#if _PY_USE_CV_LOCKS 24 25#include "condvar.h" 26 27typedef struct _NRMUTEX 28{ 29 PyMUTEX_T cs; 30 PyCOND_T cv; 31 int locked; 32} NRMUTEX; 33typedef NRMUTEX *PNRMUTEX; 34 35static PNRMUTEX 36AllocNonRecursiveMutex(void) 37{ 38 PNRMUTEX m = (PNRMUTEX)PyMem_RawMalloc(sizeof(NRMUTEX)); 39 if (!m) 40 return NULL; 41 if (PyCOND_INIT(&m->cv)) 42 goto fail; 43 if (PyMUTEX_INIT(&m->cs)) { 44 PyCOND_FINI(&m->cv); 45 goto fail; 46 } 47 m->locked = 0; 48 return m; 49fail: 50 PyMem_RawFree(m); 51 return NULL; 52} 53 54static VOID 55FreeNonRecursiveMutex(PNRMUTEX mutex) 56{ 57 if (mutex) { 58 PyCOND_FINI(&mutex->cv); 59 PyMUTEX_FINI(&mutex->cs); 60 PyMem_RawFree(mutex); 61 } 62} 63 64static DWORD 65EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds) 66{ 67 DWORD result = WAIT_OBJECT_0; 68 if (PyMUTEX_LOCK(&mutex->cs)) 69 return WAIT_FAILED; 70 if (milliseconds == INFINITE) { 71 while (mutex->locked) { 72 if (PyCOND_WAIT(&mutex->cv, &mutex->cs)) { 73 result = WAIT_FAILED; 74 break; 75 } 76 } 77 } else if (milliseconds != 0) { 78 /* wait at least until the deadline */ 79 _PyTime_t nanoseconds = _PyTime_FromNanoseconds((_PyTime_t)milliseconds * 1000000); 80 _PyTime_t deadline = _PyTime_Add(_PyTime_GetPerfCounter(), nanoseconds); 81 while (mutex->locked) { 82 _PyTime_t microseconds = _PyTime_AsMicroseconds(nanoseconds, 83 _PyTime_ROUND_TIMEOUT); 84 if (PyCOND_TIMEDWAIT(&mutex->cv, &mutex->cs, microseconds) < 0) { 85 result = WAIT_FAILED; 86 break; 87 } 88 nanoseconds = deadline - _PyTime_GetPerfCounter(); 89 if (nanoseconds <= 0) { 90 break; 91 } 92 } 93 } 94 if (!mutex->locked) { 95 mutex->locked = 1; 96 result = WAIT_OBJECT_0; 97 } else if (result == WAIT_OBJECT_0) 98 result = WAIT_TIMEOUT; 99 /* else, it is WAIT_FAILED */ 100 PyMUTEX_UNLOCK(&mutex->cs); /* must ignore result here */ 101 return result; 102} 103 104static BOOL 105LeaveNonRecursiveMutex(PNRMUTEX mutex) 106{ 107 BOOL result; 108 if (PyMUTEX_LOCK(&mutex->cs)) 109 return FALSE; 110 mutex->locked = 0; 111 /* condvar APIs return 0 on success. We need to return TRUE on success. */ 112 result = !PyCOND_SIGNAL(&mutex->cv); 113 PyMUTEX_UNLOCK(&mutex->cs); 114 return result; 115} 116 117#else /* if ! _PY_USE_CV_LOCKS */ 118 119/* NR-locks based on a kernel mutex */ 120#define PNRMUTEX HANDLE 121 122static PNRMUTEX 123AllocNonRecursiveMutex(void) 124{ 125 return CreateSemaphore(NULL, 1, 1, NULL); 126} 127 128static VOID 129FreeNonRecursiveMutex(PNRMUTEX mutex) 130{ 131 /* No in-use check */ 132 CloseHandle(mutex); 133} 134 135static DWORD 136EnterNonRecursiveMutex(PNRMUTEX mutex, DWORD milliseconds) 137{ 138 return WaitForSingleObjectEx(mutex, milliseconds, FALSE); 139} 140 141static BOOL 142LeaveNonRecursiveMutex(PNRMUTEX mutex) 143{ 144 return ReleaseSemaphore(mutex, 1, NULL); 145} 146#endif /* _PY_USE_CV_LOCKS */ 147 148unsigned long PyThread_get_thread_ident(void); 149 150#ifdef PY_HAVE_THREAD_NATIVE_ID 151unsigned long PyThread_get_thread_native_id(void); 152#endif 153 154/* 155 * Initialization of the C package, should not be needed. 156 */ 157static void 158PyThread__init_thread(void) 159{ 160} 161 162/* 163 * Thread support. 164 */ 165 166typedef struct { 167 void (*func)(void*); 168 void *arg; 169} callobj; 170 171/* thunker to call adapt between the function type used by the system's 172thread start function and the internally used one. */ 173static unsigned __stdcall 174bootstrap(void *call) 175{ 176 callobj *obj = (callobj*)call; 177 void (*func)(void*) = obj->func; 178 void *arg = obj->arg; 179 HeapFree(GetProcessHeap(), 0, obj); 180 func(arg); 181 return 0; 182} 183 184unsigned long 185PyThread_start_new_thread(void (*func)(void *), void *arg) 186{ 187 HANDLE hThread; 188 unsigned threadID; 189 callobj *obj; 190 191 dprintf(("%lu: PyThread_start_new_thread called\n", 192 PyThread_get_thread_ident())); 193 if (!initialized) 194 PyThread_init_thread(); 195 196 obj = (callobj*)HeapAlloc(GetProcessHeap(), 0, sizeof(*obj)); 197 if (!obj) 198 return PYTHREAD_INVALID_THREAD_ID; 199 obj->func = func; 200 obj->arg = arg; 201 PyThreadState *tstate = _PyThreadState_GET(); 202 size_t stacksize = tstate ? tstate->interp->threads.stacksize : 0; 203 hThread = (HANDLE)_beginthreadex(0, 204 Py_SAFE_DOWNCAST(stacksize, Py_ssize_t, unsigned int), 205 bootstrap, obj, 206 0, &threadID); 207 if (hThread == 0) { 208 /* I've seen errno == EAGAIN here, which means "there are 209 * too many threads". 210 */ 211 int e = errno; 212 dprintf(("%lu: PyThread_start_new_thread failed, errno %d\n", 213 PyThread_get_thread_ident(), e)); 214 threadID = (unsigned)-1; 215 HeapFree(GetProcessHeap(), 0, obj); 216 } 217 else { 218 dprintf(("%lu: PyThread_start_new_thread succeeded: %p\n", 219 PyThread_get_thread_ident(), (void*)hThread)); 220 CloseHandle(hThread); 221 } 222 return threadID; 223} 224 225/* 226 * Return the thread Id instead of a handle. The Id is said to uniquely identify the 227 * thread in the system 228 */ 229unsigned long 230PyThread_get_thread_ident(void) 231{ 232 if (!initialized) 233 PyThread_init_thread(); 234 235 return GetCurrentThreadId(); 236} 237 238#ifdef PY_HAVE_THREAD_NATIVE_ID 239/* 240 * Return the native Thread ID (TID) of the calling thread. 241 * The native ID of a thread is valid and guaranteed to be unique system-wide 242 * from the time the thread is created until the thread has been terminated. 243 */ 244unsigned long 245PyThread_get_thread_native_id(void) 246{ 247 if (!initialized) { 248 PyThread_init_thread(); 249 } 250 251 DWORD native_id; 252 native_id = GetCurrentThreadId(); 253 return (unsigned long) native_id; 254} 255#endif 256 257void _Py_NO_RETURN 258PyThread_exit_thread(void) 259{ 260 dprintf(("%lu: PyThread_exit_thread called\n", PyThread_get_thread_ident())); 261 if (!initialized) 262 exit(0); 263 _endthreadex(0); 264} 265 266/* 267 * Lock support. It has to be implemented as semaphores. 268 * I [Dag] tried to implement it with mutex but I could find a way to 269 * tell whether a thread already own the lock or not. 270 */ 271PyThread_type_lock 272PyThread_allocate_lock(void) 273{ 274 PNRMUTEX aLock; 275 276 dprintf(("PyThread_allocate_lock called\n")); 277 if (!initialized) 278 PyThread_init_thread(); 279 280 aLock = AllocNonRecursiveMutex() ; 281 282 dprintf(("%lu: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock)); 283 284 return (PyThread_type_lock) aLock; 285} 286 287void 288PyThread_free_lock(PyThread_type_lock aLock) 289{ 290 dprintf(("%lu: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); 291 292 FreeNonRecursiveMutex(aLock) ; 293} 294 295// WaitForSingleObject() accepts timeout in milliseconds in the range 296// [0; 0xFFFFFFFE] (DWORD type). INFINITE value (0xFFFFFFFF) means no 297// timeout. 0xFFFFFFFE milliseconds is around 49.7 days. 298const DWORD TIMEOUT_MS_MAX = 0xFFFFFFFE; 299 300/* 301 * Return 1 on success if the lock was acquired 302 * 303 * and 0 if the lock was not acquired. This means a 0 is returned 304 * if the lock has already been acquired by this thread! 305 */ 306PyLockStatus 307PyThread_acquire_lock_timed(PyThread_type_lock aLock, 308 PY_TIMEOUT_T microseconds, int intr_flag) 309{ 310 /* Fow now, intr_flag does nothing on Windows, and lock acquires are 311 * uninterruptible. */ 312 PyLockStatus success; 313 PY_TIMEOUT_T milliseconds; 314 315 if (microseconds >= 0) { 316 milliseconds = microseconds / 1000; 317 // Round milliseconds away from zero 318 if (microseconds % 1000 > 0) { 319 milliseconds++; 320 } 321 if (milliseconds > (PY_TIMEOUT_T)TIMEOUT_MS_MAX) { 322 // bpo-41710: PyThread_acquire_lock_timed() cannot report timeout 323 // overflow to the caller, so clamp the timeout to 324 // [0, TIMEOUT_MS_MAX] milliseconds. 325 // 326 // _thread.Lock.acquire() and _thread.RLock.acquire() raise an 327 // OverflowError if microseconds is greater than PY_TIMEOUT_MAX. 328 milliseconds = TIMEOUT_MS_MAX; 329 } 330 assert(milliseconds != INFINITE); 331 } 332 else { 333 milliseconds = INFINITE; 334 } 335 336 dprintf(("%lu: PyThread_acquire_lock_timed(%p, %lld) called\n", 337 PyThread_get_thread_ident(), aLock, microseconds)); 338 339 if (aLock && EnterNonRecursiveMutex((PNRMUTEX)aLock, 340 (DWORD)milliseconds) == WAIT_OBJECT_0) { 341 success = PY_LOCK_ACQUIRED; 342 } 343 else { 344 success = PY_LOCK_FAILURE; 345 } 346 347 dprintf(("%lu: PyThread_acquire_lock(%p, %lld) -> %d\n", 348 PyThread_get_thread_ident(), aLock, microseconds, success)); 349 350 return success; 351} 352int 353PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag) 354{ 355 return PyThread_acquire_lock_timed(aLock, waitflag ? -1 : 0, 0); 356} 357 358void 359PyThread_release_lock(PyThread_type_lock aLock) 360{ 361 dprintf(("%lu: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); 362 363 if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock))) 364 dprintf(("%lu: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError())); 365} 366 367/* minimum/maximum thread stack sizes supported */ 368#define THREAD_MIN_STACKSIZE 0x8000 /* 32 KiB */ 369#define THREAD_MAX_STACKSIZE 0x10000000 /* 256 MiB */ 370 371/* set the thread stack size. 372 * Return 0 if size is valid, -1 otherwise. 373 */ 374static int 375_pythread_nt_set_stacksize(size_t size) 376{ 377 /* set to default */ 378 if (size == 0) { 379 _PyInterpreterState_GET()->threads.stacksize = 0; 380 return 0; 381 } 382 383 /* valid range? */ 384 if (size >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) { 385 _PyInterpreterState_GET()->threads.stacksize = size; 386 return 0; 387 } 388 389 return -1; 390} 391 392#define THREAD_SET_STACKSIZE(x) _pythread_nt_set_stacksize(x) 393 394 395/* Thread Local Storage (TLS) API 396 397 This API is DEPRECATED since Python 3.7. See PEP 539 for details. 398*/ 399 400int 401PyThread_create_key(void) 402{ 403 DWORD result = TlsAlloc(); 404 if (result == TLS_OUT_OF_INDEXES) 405 return -1; 406 return (int)result; 407} 408 409void 410PyThread_delete_key(int key) 411{ 412 TlsFree(key); 413} 414 415int 416PyThread_set_key_value(int key, void *value) 417{ 418 BOOL ok = TlsSetValue(key, value); 419 return ok ? 0 : -1; 420} 421 422void * 423PyThread_get_key_value(int key) 424{ 425 /* because TLS is used in the Py_END_ALLOW_THREAD macro, 426 * it is necessary to preserve the windows error state, because 427 * it is assumed to be preserved across the call to the macro. 428 * Ideally, the macro should be fixed, but it is simpler to 429 * do it here. 430 */ 431 DWORD error = GetLastError(); 432 void *result = TlsGetValue(key); 433 SetLastError(error); 434 return result; 435} 436 437void 438PyThread_delete_key_value(int key) 439{ 440 /* NULL is used as "key missing", and it is also the default 441 * given by TlsGetValue() if nothing has been set yet. 442 */ 443 TlsSetValue(key, NULL); 444} 445 446 447/* reinitialization of TLS is not necessary after fork when using 448 * the native TLS functions. And forking isn't supported on Windows either. 449 */ 450void 451PyThread_ReInitTLS(void) 452{ 453} 454 455 456/* Thread Specific Storage (TSS) API 457 458 Platform-specific components of TSS API implementation. 459*/ 460 461int 462PyThread_tss_create(Py_tss_t *key) 463{ 464 assert(key != NULL); 465 /* If the key has been created, function is silently skipped. */ 466 if (key->_is_initialized) { 467 return 0; 468 } 469 470 DWORD result = TlsAlloc(); 471 if (result == TLS_OUT_OF_INDEXES) { 472 return -1; 473 } 474 /* In Windows, platform-specific key type is DWORD. */ 475 key->_key = result; 476 key->_is_initialized = 1; 477 return 0; 478} 479 480void 481PyThread_tss_delete(Py_tss_t *key) 482{ 483 assert(key != NULL); 484 /* If the key has not been created, function is silently skipped. */ 485 if (!key->_is_initialized) { 486 return; 487 } 488 489 TlsFree(key->_key); 490 key->_key = TLS_OUT_OF_INDEXES; 491 key->_is_initialized = 0; 492} 493 494int 495PyThread_tss_set(Py_tss_t *key, void *value) 496{ 497 assert(key != NULL); 498 BOOL ok = TlsSetValue(key->_key, value); 499 return ok ? 0 : -1; 500} 501 502void * 503PyThread_tss_get(Py_tss_t *key) 504{ 505 assert(key != NULL); 506 /* because TSS is used in the Py_END_ALLOW_THREAD macro, 507 * it is necessary to preserve the windows error state, because 508 * it is assumed to be preserved across the call to the macro. 509 * Ideally, the macro should be fixed, but it is simpler to 510 * do it here. 511 */ 512 DWORD error = GetLastError(); 513 void *result = TlsGetValue(key->_key); 514 SetLastError(error); 515 return result; 516} 517