blob: 67f5ed51747543c961a5f94627a58e11309390d3 [file] [log] [blame]
Guido van Rossumc3f82b61995-01-17 16:29:31 +00001
2/* This code implemented by Dag.Gruneau@elsa.preseco.comm.se */
Guido van Rossum706262b2000-05-04 18:47:15 +00003/* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru */
Tim Peterse64ef932002-02-28 21:34:34 +00004/* Eliminated some memory leaks, gsw@agere.com */
Guido van Rossumc3f82b61995-01-17 16:29:31 +00005
Guido van Rossum49b12261997-08-14 20:12:58 +00006#include <windows.h>
7#include <limits.h>
Thomas Wouters0e3f5912006-08-11 14:57:12 +00008#ifdef HAVE_PROCESS_H
Guido van Rossum49b12261997-08-14 20:12:58 +00009#include <process.h>
Thomas Wouters0e3f5912006-08-11 14:57:12 +000010#endif
Guido van Rossumc3f82b61995-01-17 16:29:31 +000011
Guido van Rossum706262b2000-05-04 18:47:15 +000012typedef struct NRMUTEX {
13 LONG owned ;
14 DWORD thread_id ;
15 HANDLE hevent ;
16} NRMUTEX, *PNRMUTEX ;
17
Guido van Rossum706262b2000-05-04 18:47:15 +000018typedef PVOID WINAPI interlocked_cmp_xchg_t(PVOID *dest, PVOID exc, PVOID comperand) ;
19
20/* Sorry mate, but we haven't got InterlockedCompareExchange in Win95! */
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000021static PVOID WINAPI
22interlocked_cmp_xchg(PVOID *dest, PVOID exc, PVOID comperand)
Guido van Rossum706262b2000-05-04 18:47:15 +000023{
24 static LONG spinlock = 0 ;
25 PVOID result ;
Guido van Rossumede8c6e2000-05-11 12:53:51 +000026 DWORD dwSleep = 0;
Guido van Rossum706262b2000-05-04 18:47:15 +000027
28 /* Acqire spinlock (yielding control to other threads if cant aquire for the moment) */
Guido van Rossumede8c6e2000-05-11 12:53:51 +000029 while(InterlockedExchange(&spinlock, 1))
30 {
31 // Using Sleep(0) can cause a priority inversion.
32 // Sleep(0) only yields the processor if there's
33 // another thread of the same priority that's
34 // ready to run. If a high-priority thread is
35 // trying to acquire the lock, which is held by
36 // a low-priority thread, then the low-priority
37 // thread may never get scheduled and hence never
38 // free the lock. NT attempts to avoid priority
39 // inversions by temporarily boosting the priority
40 // of low-priority runnable threads, but the problem
41 // can still occur if there's a medium-priority
42 // thread that's always runnable. If Sleep(1) is used,
43 // then the thread unconditionally yields the CPU. We
44 // only do this for the second and subsequent even
45 // iterations, since a millisecond is a long time to wait
46 // if the thread can be scheduled in again sooner
47 // (~100,000 instructions).
48 // Avoid priority inversion: 0, 1, 0, 1,...
49 Sleep(dwSleep);
50 dwSleep = !dwSleep;
51 }
Guido van Rossum706262b2000-05-04 18:47:15 +000052 result = *dest ;
53 if (result == comperand)
54 *dest = exc ;
55 /* Release spinlock */
56 spinlock = 0 ;
57 return result ;
58} ;
59
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000060static interlocked_cmp_xchg_t *ixchg;
61
62BOOL
63InitializeNonRecursiveMutex(PNRMUTEX mutex)
Guido van Rossum706262b2000-05-04 18:47:15 +000064{
65 if (!ixchg)
66 {
67 /* Sorely, Win95 has no InterlockedCompareExchange API (Win98 has), so we have to use emulation */
68 HANDLE kernel = GetModuleHandle("kernel32.dll") ;
69 if (!kernel || (ixchg = (interlocked_cmp_xchg_t *)GetProcAddress(kernel, "InterlockedCompareExchange")) == NULL)
70 ixchg = interlocked_cmp_xchg ;
71 }
72
73 mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */
74 mutex->thread_id = 0 ;
75 mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
76 return mutex->hevent != NULL ; /* TRUE if the mutex is created */
77}
78
Guido van Rossum582acec2000-06-28 22:07:35 +000079#ifdef InterlockedCompareExchange
80#undef InterlockedCompareExchange
81#endif
Guido van Rossum706262b2000-05-04 18:47:15 +000082#define InterlockedCompareExchange(dest,exchange,comperand) (ixchg((dest), (exchange), (comperand)))
83
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000084VOID
85DeleteNonRecursiveMutex(PNRMUTEX mutex)
Guido van Rossum706262b2000-05-04 18:47:15 +000086{
87 /* No in-use check */
88 CloseHandle(mutex->hevent) ;
89 mutex->hevent = NULL ; /* Just in case */
90}
91
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000092DWORD
93EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait)
Guido van Rossum706262b2000-05-04 18:47:15 +000094{
95 /* Assume that the thread waits successfully */
96 DWORD ret ;
97
98 /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */
99 if (!wait)
100 {
101 if (InterlockedCompareExchange((PVOID *)&mutex->owned, (PVOID)0, (PVOID)-1) != (PVOID)-1)
102 return WAIT_TIMEOUT ;
103 ret = WAIT_OBJECT_0 ;
104 }
105 else
106 ret = InterlockedIncrement(&mutex->owned) ?
107 /* Some thread owns the mutex, let's wait... */
108 WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ;
109
110 mutex->thread_id = GetCurrentThreadId() ; /* We own it */
111 return ret ;
112}
113
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000114BOOL
115LeaveNonRecursiveMutex(PNRMUTEX mutex)
Guido van Rossum706262b2000-05-04 18:47:15 +0000116{
117 /* We don't own the mutex */
118 mutex->thread_id = 0 ;
119 return
120 InterlockedDecrement(&mutex->owned) < 0 ||
121 SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */
122}
123
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000124PNRMUTEX
125AllocNonRecursiveMutex(void)
Guido van Rossum706262b2000-05-04 18:47:15 +0000126{
127 PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ;
128 if (mutex && !InitializeNonRecursiveMutex(mutex))
129 {
130 free(mutex) ;
131 mutex = NULL ;
132 }
133 return mutex ;
134}
135
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000136void
137FreeNonRecursiveMutex(PNRMUTEX mutex)
Guido van Rossum706262b2000-05-04 18:47:15 +0000138{
139 if (mutex)
140 {
141 DeleteNonRecursiveMutex(mutex) ;
142 free(mutex) ;
143 }
144}
145
Guido van Rossum65d5b571998-12-21 19:32:43 +0000146long PyThread_get_thread_ident(void);
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000147
148/*
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000149 * Initialization of the C package, should not be needed.
150 */
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000151static void
152PyThread__init_thread(void)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000153{
154}
155
156/*
157 * Thread support.
158 */
Guido van Rossum3c288632001-10-16 21:13:49 +0000159
160typedef struct {
161 void (*func)(void*);
Tim Peters2e7e7df2003-07-04 04:40:45 +0000162 void *arg;
Guido van Rossum3c288632001-10-16 21:13:49 +0000163 long id;
164 HANDLE done;
165} callobj;
166
167static int
168bootstrap(void *call)
169{
170 callobj *obj = (callobj*)call;
171 /* copy callobj since other thread might free it before we're done */
172 void (*func)(void*) = obj->func;
173 void *arg = obj->arg;
174
175 obj->id = PyThread_get_thread_ident();
176 ReleaseSemaphore(obj->done, 1, NULL);
177 func(arg);
178 return 0;
179}
180
Tim Peters2e7e7df2003-07-04 04:40:45 +0000181long
182PyThread_start_new_thread(void (*func)(void *), void *arg)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000183{
Thomas Wouters477c8d52006-05-27 19:21:47 +0000184 Py_uintptr_t rv;
Tim Peters2e7e7df2003-07-04 04:40:45 +0000185 callobj obj;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000186
Tim Peters2e7e7df2003-07-04 04:40:45 +0000187 dprintf(("%ld: PyThread_start_new_thread called\n",
188 PyThread_get_thread_ident()));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000189 if (!initialized)
Guido van Rossum65d5b571998-12-21 19:32:43 +0000190 PyThread_init_thread();
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000191
Tim Peters2e7e7df2003-07-04 04:40:45 +0000192 obj.id = -1; /* guilty until proved innocent */
193 obj.func = func;
194 obj.arg = arg;
195 obj.done = CreateSemaphore(NULL, 0, 1, NULL);
196 if (obj.done == NULL)
197 return -1;
Guido van Rossum3c288632001-10-16 21:13:49 +0000198
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000199 rv = _beginthread(bootstrap, _pythread_stacksize, &obj);
Thomas Wouters477c8d52006-05-27 19:21:47 +0000200 if (rv == (Py_uintptr_t)-1) {
Tim Peters2e7e7df2003-07-04 04:40:45 +0000201 /* I've seen errno == EAGAIN here, which means "there are
202 * too many threads".
203 */
204 dprintf(("%ld: PyThread_start_new_thread failed: %p errno %d\n",
205 PyThread_get_thread_ident(), rv, errno));
206 obj.id = -1;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000207 }
Tim Peters2e7e7df2003-07-04 04:40:45 +0000208 else {
209 dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n",
210 PyThread_get_thread_ident(), rv));
211 /* wait for thread to initialize, so we can get its id */
212 WaitForSingleObject(obj.done, INFINITE);
213 assert(obj.id != -1);
214 }
215 CloseHandle((HANDLE)obj.done);
216 return obj.id;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000217}
218
219/*
220 * Return the thread Id instead of an handle. The Id is said to uniquely identify the
221 * thread in the system
222 */
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000223long
224PyThread_get_thread_ident(void)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000225{
226 if (!initialized)
Guido van Rossum65d5b571998-12-21 19:32:43 +0000227 PyThread_init_thread();
Guido van Rossum706262b2000-05-04 18:47:15 +0000228
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000229 return GetCurrentThreadId();
230}
231
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000232static void
233do_PyThread_exit_thread(int no_cleanup)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000234{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000235 dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident()));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000236 if (!initialized)
237 if (no_cleanup)
238 _exit(0);
239 else
240 exit(0);
Guido van Rossum49b12261997-08-14 20:12:58 +0000241 _endthread();
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000242}
243
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000244void
245PyThread_exit_thread(void)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000246{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000247 do_PyThread_exit_thread(0);
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000248}
249
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000250void
251PyThread__exit_thread(void)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000252{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000253 do_PyThread_exit_thread(1);
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000254}
255
256#ifndef NO_EXIT_PROG
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000257static void
258do_PyThread_exit_prog(int status, int no_cleanup)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000259{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000260 dprintf(("PyThread_exit_prog(%d) called\n", status));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000261 if (!initialized)
262 if (no_cleanup)
263 _exit(status);
264 else
265 exit(status);
266}
267
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000268void
269PyThread_exit_prog(int status)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000270{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000271 do_PyThread_exit_prog(status, 0);
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000272}
273
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000274void
275PyThread__exit_prog(int status)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000276{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000277 do_PyThread_exit_prog(status, 1);
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000278}
279#endif /* NO_EXIT_PROG */
280
281/*
282 * Lock support. It has too be implemented as semaphores.
283 * I [Dag] tried to implement it with mutex but I could find a way to
284 * tell whether a thread already own the lock or not.
285 */
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000286PyThread_type_lock
287PyThread_allocate_lock(void)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000288{
Guido van Rossum706262b2000-05-04 18:47:15 +0000289 PNRMUTEX aLock;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000290
Guido van Rossum65d5b571998-12-21 19:32:43 +0000291 dprintf(("PyThread_allocate_lock called\n"));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000292 if (!initialized)
Guido van Rossum65d5b571998-12-21 19:32:43 +0000293 PyThread_init_thread();
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000294
Guido van Rossum706262b2000-05-04 18:47:15 +0000295 aLock = AllocNonRecursiveMutex() ;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000296
Fred Drakea44d3532000-06-30 15:01:00 +0000297 dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000298
Guido van Rossum65d5b571998-12-21 19:32:43 +0000299 return (PyThread_type_lock) aLock;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000300}
301
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000302void
303PyThread_free_lock(PyThread_type_lock aLock)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000304{
Fred Drakea44d3532000-06-30 15:01:00 +0000305 dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000306
Guido van Rossum706262b2000-05-04 18:47:15 +0000307 FreeNonRecursiveMutex(aLock) ;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000308}
309
310/*
311 * Return 1 on success if the lock was acquired
312 *
313 * and 0 if the lock was not acquired. This means a 0 is returned
314 * if the lock has already been acquired by this thread!
315 */
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000316int
317PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000318{
Guido van Rossum706262b2000-05-04 18:47:15 +0000319 int success ;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000320
Fred Drakea44d3532000-06-30 15:01:00 +0000321 dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000322
Georg Brandlaf410b52005-07-08 22:26:13 +0000323 success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000324
Fred Drakea44d3532000-06-30 15:01:00 +0000325 dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000326
327 return success;
328}
329
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000330void
331PyThread_release_lock(PyThread_type_lock aLock)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000332{
Fred Drakea44d3532000-06-30 15:01:00 +0000333 dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000334
Guido van Rossum706262b2000-05-04 18:47:15 +0000335 if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock)))
Fred Drakea44d3532000-06-30 15:01:00 +0000336 dprintf(("%ld: Could not PyThread_release_lock(%p) error: %l\n", PyThread_get_thread_ident(), aLock, GetLastError()));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000337}
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000338
339/* minimum/maximum thread stack sizes supported */
340#define THREAD_MIN_STACKSIZE 0x8000 /* 32kB */
341#define THREAD_MAX_STACKSIZE 0x10000000 /* 256MB */
342
343/* set the thread stack size.
344 * Return 0 if size is valid, -1 otherwise.
345 */
346static int
347_pythread_nt_set_stacksize(size_t size)
348{
349 /* set to default */
350 if (size == 0) {
351 _pythread_stacksize = 0;
352 return 0;
353 }
354
355 /* valid range? */
356 if (size >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) {
357 _pythread_stacksize = size;
358 return 0;
359 }
360
361 return -1;
362}
363
364#define THREAD_SET_STACKSIZE(x) _pythread_nt_set_stacksize(x)