• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 a 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