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 typedef struct NRMUTEX {
13 LONG owned ;
14 DWORD thread_id ;
15 HANDLE hevent ;
16 } NRMUTEX, *PNRMUTEX ;
17
18
19 BOOL
InitializeNonRecursiveMutex(PNRMUTEX mutex)20 InitializeNonRecursiveMutex(PNRMUTEX mutex)
21 {
22 mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */
23 mutex->thread_id = 0 ;
24 mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
25 return mutex->hevent != NULL ; /* TRUE if the mutex is created */
26 }
27
28 VOID
DeleteNonRecursiveMutex(PNRMUTEX mutex)29 DeleteNonRecursiveMutex(PNRMUTEX mutex)
30 {
31 /* No in-use check */
32 CloseHandle(mutex->hevent) ;
33 mutex->hevent = NULL ; /* Just in case */
34 }
35
36 DWORD
EnterNonRecursiveMutex(PNRMUTEX mutex,BOOL wait)37 EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait)
38 {
39 /* Assume that the thread waits successfully */
40 DWORD ret ;
41
42 /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */
43 if (!wait)
44 {
45 if (InterlockedCompareExchange(&mutex->owned, 0, -1) != -1)
46 return WAIT_TIMEOUT ;
47 ret = WAIT_OBJECT_0 ;
48 }
49 else
50 ret = InterlockedIncrement(&mutex->owned) ?
51 /* Some thread owns the mutex, let's wait... */
52 WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ;
53
54 mutex->thread_id = GetCurrentThreadId() ; /* We own it */
55 return ret ;
56 }
57
58 BOOL
LeaveNonRecursiveMutex(PNRMUTEX mutex)59 LeaveNonRecursiveMutex(PNRMUTEX mutex)
60 {
61 /* We don't own the mutex */
62 mutex->thread_id = 0 ;
63 return
64 InterlockedDecrement(&mutex->owned) < 0 ||
65 SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */
66 }
67
68 PNRMUTEX
AllocNonRecursiveMutex(void)69 AllocNonRecursiveMutex(void)
70 {
71 PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ;
72 if (mutex && !InitializeNonRecursiveMutex(mutex))
73 {
74 free(mutex) ;
75 mutex = NULL ;
76 }
77 return mutex ;
78 }
79
80 void
FreeNonRecursiveMutex(PNRMUTEX mutex)81 FreeNonRecursiveMutex(PNRMUTEX mutex)
82 {
83 if (mutex)
84 {
85 DeleteNonRecursiveMutex(mutex) ;
86 free(mutex) ;
87 }
88 }
89
90 long PyThread_get_thread_ident(void);
91
92 /*
93 * Initialization of the C package, should not be needed.
94 */
95 static void
PyThread__init_thread(void)96 PyThread__init_thread(void)
97 {
98 }
99
100 /*
101 * Thread support.
102 */
103
104 typedef struct {
105 void (*func)(void*);
106 void *arg;
107 } callobj;
108
109 /* thunker to call adapt between the function type used by the system's
110 thread start function and the internally used one. */
111 #if defined(MS_WINCE)
112 static DWORD WINAPI
113 #else
114 static unsigned __stdcall
115 #endif
bootstrap(void * call)116 bootstrap(void *call)
117 {
118 callobj *obj = (callobj*)call;
119 void (*func)(void*) = obj->func;
120 void *arg = obj->arg;
121 HeapFree(GetProcessHeap(), 0, obj);
122 func(arg);
123 return 0;
124 }
125
126 long
PyThread_start_new_thread(void (* func)(void *),void * arg)127 PyThread_start_new_thread(void (*func)(void *), void *arg)
128 {
129 HANDLE hThread;
130 unsigned threadID;
131 callobj *obj;
132
133 dprintf(("%ld: PyThread_start_new_thread called\n",
134 PyThread_get_thread_ident()));
135 if (!initialized)
136 PyThread_init_thread();
137
138 obj = (callobj*)HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
139 if (!obj)
140 return -1;
141 obj->func = func;
142 obj->arg = arg;
143 #if defined(MS_WINCE)
144 hThread = CreateThread(NULL,
145 Py_SAFE_DOWNCAST(_pythread_stacksize, Py_ssize_t, SIZE_T),
146 bootstrap, obj, 0, &threadID);
147 #else
148 hThread = (HANDLE)_beginthreadex(0,
149 Py_SAFE_DOWNCAST(_pythread_stacksize,
150 Py_ssize_t, unsigned int),
151 bootstrap, obj,
152 0, &threadID);
153 #endif
154 if (hThread == 0) {
155 #if defined(MS_WINCE)
156 /* Save error in variable, to prevent PyThread_get_thread_ident
157 from clobbering it. */
158 unsigned e = GetLastError();
159 dprintf(("%ld: PyThread_start_new_thread failed, win32 error code %u\n",
160 PyThread_get_thread_ident(), e));
161 #else
162 /* I've seen errno == EAGAIN here, which means "there are
163 * too many threads".
164 */
165 int e = errno;
166 dprintf(("%ld: PyThread_start_new_thread failed, errno %d\n",
167 PyThread_get_thread_ident(), e));
168 #endif
169 threadID = (unsigned)-1;
170 HeapFree(GetProcessHeap(), 0, obj);
171 }
172 else {
173 dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n",
174 PyThread_get_thread_ident(), (void*)hThread));
175 CloseHandle(hThread);
176 }
177 return (long) threadID;
178 }
179
180 /*
181 * Return the thread Id instead of an handle. The Id is said to uniquely identify the
182 * thread in the system
183 */
184 long
PyThread_get_thread_ident(void)185 PyThread_get_thread_ident(void)
186 {
187 if (!initialized)
188 PyThread_init_thread();
189
190 return GetCurrentThreadId();
191 }
192
193 void
PyThread_exit_thread(void)194 PyThread_exit_thread(void)
195 {
196 dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident()));
197 if (!initialized)
198 exit(0);
199 #if defined(MS_WINCE)
200 ExitThread(0);
201 #else
202 _endthreadex(0);
203 #endif
204 }
205
206 /*
207 * Lock support. It has too be implemented as semaphores.
208 * I [Dag] tried to implement it with mutex but I could find a way to
209 * tell whether a thread already own the lock or not.
210 */
211 PyThread_type_lock
PyThread_allocate_lock(void)212 PyThread_allocate_lock(void)
213 {
214 PNRMUTEX aLock;
215
216 dprintf(("PyThread_allocate_lock called\n"));
217 if (!initialized)
218 PyThread_init_thread();
219
220 aLock = AllocNonRecursiveMutex() ;
221
222 dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock));
223
224 return (PyThread_type_lock) aLock;
225 }
226
227 void
PyThread_free_lock(PyThread_type_lock aLock)228 PyThread_free_lock(PyThread_type_lock aLock)
229 {
230 dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
231
232 FreeNonRecursiveMutex(aLock) ;
233 }
234
235 /*
236 * Return 1 on success if the lock was acquired
237 *
238 * and 0 if the lock was not acquired. This means a 0 is returned
239 * if the lock has already been acquired by this thread!
240 */
241 int
PyThread_acquire_lock(PyThread_type_lock aLock,int waitflag)242 PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
243 {
244 int success ;
245
246 dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag));
247
248 success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ;
249
250 dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success));
251
252 return success;
253 }
254
255 void
PyThread_release_lock(PyThread_type_lock aLock)256 PyThread_release_lock(PyThread_type_lock aLock)
257 {
258 dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
259
260 if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock)))
261 dprintf(("%ld: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError()));
262 }
263
264 /* minimum/maximum thread stack sizes supported */
265 #define THREAD_MIN_STACKSIZE 0x8000 /* 32kB */
266 #define THREAD_MAX_STACKSIZE 0x10000000 /* 256MB */
267
268 /* set the thread stack size.
269 * Return 0 if size is valid, -1 otherwise.
270 */
271 static int
_pythread_nt_set_stacksize(size_t size)272 _pythread_nt_set_stacksize(size_t size)
273 {
274 /* set to default */
275 if (size == 0) {
276 _pythread_stacksize = 0;
277 return 0;
278 }
279
280 /* valid range? */
281 if (size >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) {
282 _pythread_stacksize = size;
283 return 0;
284 }
285
286 return -1;
287 }
288
289 #define THREAD_SET_STACKSIZE(x) _pythread_nt_set_stacksize(x)
290
291
292 /* use native Windows TLS functions */
293 #define Py_HAVE_NATIVE_TLS
294
295 #ifdef Py_HAVE_NATIVE_TLS
296 int
PyThread_create_key(void)297 PyThread_create_key(void)
298 {
299 return (int) TlsAlloc();
300 }
301
302 void
PyThread_delete_key(int key)303 PyThread_delete_key(int key)
304 {
305 TlsFree(key);
306 }
307
308 /* We must be careful to emulate the strange semantics implemented in thread.c,
309 * where the value is only set if it hasn't been set before.
310 */
311 int
PyThread_set_key_value(int key,void * value)312 PyThread_set_key_value(int key, void *value)
313 {
314 BOOL ok;
315 void *oldvalue;
316
317 assert(value != NULL);
318 oldvalue = TlsGetValue(key);
319 if (oldvalue != NULL)
320 /* ignore value if already set */
321 return 0;
322 ok = TlsSetValue(key, value);
323 if (!ok)
324 return -1;
325 return 0;
326 }
327
328 void *
PyThread_get_key_value(int key)329 PyThread_get_key_value(int key)
330 {
331 /* because TLS is used in the Py_END_ALLOW_THREAD macro,
332 * it is necessary to preserve the windows error state, because
333 * it is assumed to be preserved across the call to the macro.
334 * Ideally, the macro should be fixed, but it is simpler to
335 * do it here.
336 */
337 DWORD error = GetLastError();
338 void *result = TlsGetValue(key);
339 SetLastError(error);
340 return result;
341 }
342
343 void
PyThread_delete_key_value(int key)344 PyThread_delete_key_value(int key)
345 {
346 /* NULL is used as "key missing", and it is also the default
347 * given by TlsGetValue() if nothing has been set yet.
348 */
349 TlsSetValue(key, NULL);
350 }
351
352 /* reinitialization of TLS is not necessary after fork when using
353 * the native TLS functions. And forking isn't supported on Windows either.
354 */
355 void
PyThread_ReInitTLS(void)356 PyThread_ReInitTLS(void)
357 {}
358
359 #endif
360