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         ULONGLONG now, target = GetTickCount64() + 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 = GetTickCount64();
85             if (target <= now)
86                 break;
87             milliseconds = (DWORD)(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