Home | History | Annotate | Download | only in Python
      1 
      2 /* This code implemented by Dag.Gruneau (at) elsa.preseco.comm.se */
      3 /* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch (at) iso.ru */
      4 /* Eliminated some memory leaks, gsw (at) 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
     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
     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
     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
     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
     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
     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
     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
    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
    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
    185 PyThread_get_thread_ident(void)
    186 {
    187     if (!initialized)
    188         PyThread_init_thread();
    189 
    190     return GetCurrentThreadId();
    191 }
    192 
    193 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
    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
    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
    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
    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
    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
    297 PyThread_create_key(void)
    298 {
    299     return (int) TlsAlloc();
    300 }
    301 
    302 void
    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
    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 *
    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
    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
    356 PyThread_ReInitTLS(void)
    357 {}
    358 
    359 #endif
    360