blob: 0b7e84ece16fdc3fb0f5b3ab24c1f901edf07980 [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>
8#include <process.h>
Guido van Rossumc3f82b61995-01-17 16:29:31 +00009
Guido van Rossum706262b2000-05-04 18:47:15 +000010typedef struct NRMUTEX {
11 LONG owned ;
12 DWORD thread_id ;
13 HANDLE hevent ;
14} NRMUTEX, *PNRMUTEX ;
15
Guido van Rossum706262b2000-05-04 18:47:15 +000016typedef PVOID WINAPI interlocked_cmp_xchg_t(PVOID *dest, PVOID exc, PVOID comperand) ;
17
18/* Sorry mate, but we haven't got InterlockedCompareExchange in Win95! */
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000019static PVOID WINAPI
20interlocked_cmp_xchg(PVOID *dest, PVOID exc, PVOID comperand)
Guido van Rossum706262b2000-05-04 18:47:15 +000021{
22 static LONG spinlock = 0 ;
23 PVOID result ;
Guido van Rossumede8c6e2000-05-11 12:53:51 +000024 DWORD dwSleep = 0;
Guido van Rossum706262b2000-05-04 18:47:15 +000025
26 /* Acqire spinlock (yielding control to other threads if cant aquire for the moment) */
Guido van Rossumede8c6e2000-05-11 12:53:51 +000027 while(InterlockedExchange(&spinlock, 1))
28 {
29 // Using Sleep(0) can cause a priority inversion.
30 // Sleep(0) only yields the processor if there's
31 // another thread of the same priority that's
32 // ready to run. If a high-priority thread is
33 // trying to acquire the lock, which is held by
34 // a low-priority thread, then the low-priority
35 // thread may never get scheduled and hence never
36 // free the lock. NT attempts to avoid priority
37 // inversions by temporarily boosting the priority
38 // of low-priority runnable threads, but the problem
39 // can still occur if there's a medium-priority
40 // thread that's always runnable. If Sleep(1) is used,
41 // then the thread unconditionally yields the CPU. We
42 // only do this for the second and subsequent even
43 // iterations, since a millisecond is a long time to wait
44 // if the thread can be scheduled in again sooner
45 // (~100,000 instructions).
46 // Avoid priority inversion: 0, 1, 0, 1,...
47 Sleep(dwSleep);
48 dwSleep = !dwSleep;
49 }
Guido van Rossum706262b2000-05-04 18:47:15 +000050 result = *dest ;
51 if (result == comperand)
52 *dest = exc ;
53 /* Release spinlock */
54 spinlock = 0 ;
55 return result ;
56} ;
57
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000058static interlocked_cmp_xchg_t *ixchg;
59
60BOOL
61InitializeNonRecursiveMutex(PNRMUTEX mutex)
Guido van Rossum706262b2000-05-04 18:47:15 +000062{
63 if (!ixchg)
64 {
65 /* Sorely, Win95 has no InterlockedCompareExchange API (Win98 has), so we have to use emulation */
66 HANDLE kernel = GetModuleHandle("kernel32.dll") ;
67 if (!kernel || (ixchg = (interlocked_cmp_xchg_t *)GetProcAddress(kernel, "InterlockedCompareExchange")) == NULL)
68 ixchg = interlocked_cmp_xchg ;
69 }
70
71 mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */
72 mutex->thread_id = 0 ;
73 mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
74 return mutex->hevent != NULL ; /* TRUE if the mutex is created */
75}
76
Guido van Rossum582acec2000-06-28 22:07:35 +000077#ifdef InterlockedCompareExchange
78#undef InterlockedCompareExchange
79#endif
Guido van Rossum706262b2000-05-04 18:47:15 +000080#define InterlockedCompareExchange(dest,exchange,comperand) (ixchg((dest), (exchange), (comperand)))
81
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000082VOID
83DeleteNonRecursiveMutex(PNRMUTEX mutex)
Guido van Rossum706262b2000-05-04 18:47:15 +000084{
85 /* No in-use check */
86 CloseHandle(mutex->hevent) ;
87 mutex->hevent = NULL ; /* Just in case */
88}
89
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000090DWORD
91EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait)
Guido van Rossum706262b2000-05-04 18:47:15 +000092{
93 /* Assume that the thread waits successfully */
94 DWORD ret ;
95
96 /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */
97 if (!wait)
98 {
99 if (InterlockedCompareExchange((PVOID *)&mutex->owned, (PVOID)0, (PVOID)-1) != (PVOID)-1)
100 return WAIT_TIMEOUT ;
101 ret = WAIT_OBJECT_0 ;
102 }
103 else
104 ret = InterlockedIncrement(&mutex->owned) ?
105 /* Some thread owns the mutex, let's wait... */
106 WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ;
107
108 mutex->thread_id = GetCurrentThreadId() ; /* We own it */
109 return ret ;
110}
111
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000112BOOL
113LeaveNonRecursiveMutex(PNRMUTEX mutex)
Guido van Rossum706262b2000-05-04 18:47:15 +0000114{
115 /* We don't own the mutex */
116 mutex->thread_id = 0 ;
117 return
118 InterlockedDecrement(&mutex->owned) < 0 ||
119 SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */
120}
121
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000122PNRMUTEX
123AllocNonRecursiveMutex(void)
Guido van Rossum706262b2000-05-04 18:47:15 +0000124{
125 PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ;
126 if (mutex && !InitializeNonRecursiveMutex(mutex))
127 {
128 free(mutex) ;
129 mutex = NULL ;
130 }
131 return mutex ;
132}
133
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000134void
135FreeNonRecursiveMutex(PNRMUTEX mutex)
Guido van Rossum706262b2000-05-04 18:47:15 +0000136{
137 if (mutex)
138 {
139 DeleteNonRecursiveMutex(mutex) ;
140 free(mutex) ;
141 }
142}
143
Guido van Rossum65d5b571998-12-21 19:32:43 +0000144long PyThread_get_thread_ident(void);
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000145
146/*
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000147 * Initialization of the C package, should not be needed.
148 */
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000149static void
150PyThread__init_thread(void)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000151{
152}
153
154/*
155 * Thread support.
156 */
Guido van Rossum3c288632001-10-16 21:13:49 +0000157
158typedef struct {
159 void (*func)(void*);
Tim Peters2e7e7df2003-07-04 04:40:45 +0000160 void *arg;
Guido van Rossum3c288632001-10-16 21:13:49 +0000161 long id;
162 HANDLE done;
163} callobj;
164
165static int
166bootstrap(void *call)
167{
168 callobj *obj = (callobj*)call;
169 /* copy callobj since other thread might free it before we're done */
170 void (*func)(void*) = obj->func;
171 void *arg = obj->arg;
172
173 obj->id = PyThread_get_thread_ident();
174 ReleaseSemaphore(obj->done, 1, NULL);
175 func(arg);
176 return 0;
177}
178
Tim Peters2e7e7df2003-07-04 04:40:45 +0000179long
180PyThread_start_new_thread(void (*func)(void *), void *arg)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000181{
Thomas Wouters477c8d52006-05-27 19:21:47 +0000182 Py_uintptr_t rv;
Tim Peters2e7e7df2003-07-04 04:40:45 +0000183 callobj obj;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000184
Tim Peters2e7e7df2003-07-04 04:40:45 +0000185 dprintf(("%ld: PyThread_start_new_thread called\n",
186 PyThread_get_thread_ident()));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000187 if (!initialized)
Guido van Rossum65d5b571998-12-21 19:32:43 +0000188 PyThread_init_thread();
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000189
Tim Peters2e7e7df2003-07-04 04:40:45 +0000190 obj.id = -1; /* guilty until proved innocent */
191 obj.func = func;
192 obj.arg = arg;
193 obj.done = CreateSemaphore(NULL, 0, 1, NULL);
194 if (obj.done == NULL)
195 return -1;
Guido van Rossum3c288632001-10-16 21:13:49 +0000196
Tim Peters2e7e7df2003-07-04 04:40:45 +0000197 rv = _beginthread(bootstrap, 0, &obj); /* use default stack size */
Thomas Wouters477c8d52006-05-27 19:21:47 +0000198 if (rv == (Py_uintptr_t)-1) {
Tim Peters2e7e7df2003-07-04 04:40:45 +0000199 /* I've seen errno == EAGAIN here, which means "there are
200 * too many threads".
201 */
202 dprintf(("%ld: PyThread_start_new_thread failed: %p errno %d\n",
203 PyThread_get_thread_ident(), rv, errno));
204 obj.id = -1;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000205 }
Tim Peters2e7e7df2003-07-04 04:40:45 +0000206 else {
207 dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n",
208 PyThread_get_thread_ident(), rv));
209 /* wait for thread to initialize, so we can get its id */
210 WaitForSingleObject(obj.done, INFINITE);
211 assert(obj.id != -1);
212 }
213 CloseHandle((HANDLE)obj.done);
214 return obj.id;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000215}
216
217/*
218 * Return the thread Id instead of an handle. The Id is said to uniquely identify the
219 * thread in the system
220 */
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000221long
222PyThread_get_thread_ident(void)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000223{
224 if (!initialized)
Guido van Rossum65d5b571998-12-21 19:32:43 +0000225 PyThread_init_thread();
Guido van Rossum706262b2000-05-04 18:47:15 +0000226
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000227 return GetCurrentThreadId();
228}
229
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000230static void
231do_PyThread_exit_thread(int no_cleanup)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000232{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000233 dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident()));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000234 if (!initialized)
235 if (no_cleanup)
236 _exit(0);
237 else
238 exit(0);
Guido van Rossum49b12261997-08-14 20:12:58 +0000239 _endthread();
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000240}
241
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000242void
243PyThread_exit_thread(void)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000244{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000245 do_PyThread_exit_thread(0);
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000246}
247
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000248void
249PyThread__exit_thread(void)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000250{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000251 do_PyThread_exit_thread(1);
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000252}
253
254#ifndef NO_EXIT_PROG
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000255static void
256do_PyThread_exit_prog(int status, int no_cleanup)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000257{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000258 dprintf(("PyThread_exit_prog(%d) called\n", status));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000259 if (!initialized)
260 if (no_cleanup)
261 _exit(status);
262 else
263 exit(status);
264}
265
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000266void
267PyThread_exit_prog(int status)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000268{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000269 do_PyThread_exit_prog(status, 0);
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000270}
271
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000272void
273PyThread__exit_prog(int status)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000274{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000275 do_PyThread_exit_prog(status, 1);
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000276}
277#endif /* NO_EXIT_PROG */
278
279/*
280 * Lock support. It has too be implemented as semaphores.
281 * I [Dag] tried to implement it with mutex but I could find a way to
282 * tell whether a thread already own the lock or not.
283 */
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000284PyThread_type_lock
285PyThread_allocate_lock(void)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000286{
Guido van Rossum706262b2000-05-04 18:47:15 +0000287 PNRMUTEX aLock;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000288
Guido van Rossum65d5b571998-12-21 19:32:43 +0000289 dprintf(("PyThread_allocate_lock called\n"));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000290 if (!initialized)
Guido van Rossum65d5b571998-12-21 19:32:43 +0000291 PyThread_init_thread();
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000292
Guido van Rossum706262b2000-05-04 18:47:15 +0000293 aLock = AllocNonRecursiveMutex() ;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000294
Fred Drakea44d3532000-06-30 15:01:00 +0000295 dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000296
Guido van Rossum65d5b571998-12-21 19:32:43 +0000297 return (PyThread_type_lock) aLock;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000298}
299
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000300void
301PyThread_free_lock(PyThread_type_lock aLock)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000302{
Fred Drakea44d3532000-06-30 15:01:00 +0000303 dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000304
Guido van Rossum706262b2000-05-04 18:47:15 +0000305 FreeNonRecursiveMutex(aLock) ;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000306}
307
308/*
309 * Return 1 on success if the lock was acquired
310 *
311 * and 0 if the lock was not acquired. This means a 0 is returned
312 * if the lock has already been acquired by this thread!
313 */
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000314int
315PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000316{
Guido van Rossum706262b2000-05-04 18:47:15 +0000317 int success ;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000318
Fred Drakea44d3532000-06-30 15:01:00 +0000319 dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000320
Georg Brandlaf410b52005-07-08 22:26:13 +0000321 success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ;
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000322
Fred Drakea44d3532000-06-30 15:01:00 +0000323 dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000324
325 return success;
326}
327
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000328void
329PyThread_release_lock(PyThread_type_lock aLock)
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000330{
Fred Drakea44d3532000-06-30 15:01:00 +0000331 dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
Guido van Rossumc3f82b61995-01-17 16:29:31 +0000332
Guido van Rossum706262b2000-05-04 18:47:15 +0000333 if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock)))
Fred Drakea44d3532000-06-30 15:01:00 +0000334 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 +0000335}