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