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
12typedef struct NRMUTEX {
13    LONG   owned ;
14    DWORD  thread_id ;
15    HANDLE hevent ;
16} NRMUTEX, *PNRMUTEX ;
17
18
19BOOL
20InitializeNonRecursiveMutex(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
28VOID
29DeleteNonRecursiveMutex(PNRMUTEX mutex)
30{
31    /* No in-use check */
32    CloseHandle(mutex->hevent) ;
33    mutex->hevent = NULL ; /* Just in case */
34}
35
36DWORD
37EnterNonRecursiveMutex(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
58BOOL
59LeaveNonRecursiveMutex(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
68PNRMUTEX
69AllocNonRecursiveMutex(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
80void
81FreeNonRecursiveMutex(PNRMUTEX mutex)
82{
83    if (mutex)
84    {
85        DeleteNonRecursiveMutex(mutex) ;
86        free(mutex) ;
87    }
88}
89
90long PyThread_get_thread_ident(void);
91
92/*
93 * Initialization of the C package, should not be needed.
94 */
95static void
96PyThread__init_thread(void)
97{
98}
99
100/*
101 * Thread support.
102 */
103
104typedef 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
110thread start function and the internally used one. */
111#if defined(MS_WINCE)
112static DWORD WINAPI
113#else
114static unsigned __stdcall
115#endif
116bootstrap(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
126long
127PyThread_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 */
184long
185PyThread_get_thread_ident(void)
186{
187    if (!initialized)
188        PyThread_init_thread();
189
190    return GetCurrentThreadId();
191}
192
193void
194PyThread_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 */
211PyThread_type_lock
212PyThread_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
227void
228PyThread_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 */
241int
242PyThread_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
255void
256PyThread_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 */
271static int
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
296int
297PyThread_create_key(void)
298{
299    return (int) TlsAlloc();
300}
301
302void
303PyThread_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 */
311int
312PyThread_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
328void *
329PyThread_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
343void
344PyThread_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 */
355void
356PyThread_ReInitTLS(void)
357{}
358
359#endif
360