/Python/thread_nt.h

http://unladen-swallow.googlecode.com/ · C Header · 311 lines · 222 code · 49 blank · 40 comment · 28 complexity · 50d1b517ddc922ed5eb98740f7c878e9 MD5 · raw file

  1. /* This code implemented by Dag.Gruneau@elsa.preseco.comm.se */
  2. /* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru */
  3. /* Eliminated some memory leaks, gsw@agere.com */
  4. #include <windows.h>
  5. #include <limits.h>
  6. #ifdef HAVE_PROCESS_H
  7. #include <process.h>
  8. #endif
  9. typedef struct NRMUTEX {
  10. LONG owned ;
  11. DWORD thread_id ;
  12. HANDLE hevent ;
  13. } NRMUTEX, *PNRMUTEX ;
  14. BOOL
  15. InitializeNonRecursiveMutex(PNRMUTEX mutex)
  16. {
  17. mutex->owned = -1 ; /* No threads have entered NonRecursiveMutex */
  18. mutex->thread_id = 0 ;
  19. mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ;
  20. return mutex->hevent != NULL ; /* TRUE if the mutex is created */
  21. }
  22. VOID
  23. DeleteNonRecursiveMutex(PNRMUTEX mutex)
  24. {
  25. /* No in-use check */
  26. CloseHandle(mutex->hevent) ;
  27. mutex->hevent = NULL ; /* Just in case */
  28. }
  29. DWORD
  30. EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait)
  31. {
  32. /* Assume that the thread waits successfully */
  33. DWORD ret ;
  34. /* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */
  35. if (!wait)
  36. {
  37. if (InterlockedCompareExchange(&mutex->owned, 0, -1) != -1)
  38. return WAIT_TIMEOUT ;
  39. ret = WAIT_OBJECT_0 ;
  40. }
  41. else
  42. ret = InterlockedIncrement(&mutex->owned) ?
  43. /* Some thread owns the mutex, let's wait... */
  44. WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ;
  45. mutex->thread_id = GetCurrentThreadId() ; /* We own it */
  46. return ret ;
  47. }
  48. BOOL
  49. LeaveNonRecursiveMutex(PNRMUTEX mutex)
  50. {
  51. /* We don't own the mutex */
  52. mutex->thread_id = 0 ;
  53. return
  54. InterlockedDecrement(&mutex->owned) < 0 ||
  55. SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */
  56. }
  57. PNRMUTEX
  58. AllocNonRecursiveMutex(void)
  59. {
  60. PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ;
  61. if (mutex && !InitializeNonRecursiveMutex(mutex))
  62. {
  63. free(mutex) ;
  64. mutex = NULL ;
  65. }
  66. return mutex ;
  67. }
  68. void
  69. FreeNonRecursiveMutex(PNRMUTEX mutex)
  70. {
  71. if (mutex)
  72. {
  73. DeleteNonRecursiveMutex(mutex) ;
  74. free(mutex) ;
  75. }
  76. }
  77. long PyThread_get_thread_ident(void);
  78. /*
  79. * Initialization of the C package, should not be needed.
  80. */
  81. static void
  82. PyThread__init_thread(void)
  83. {
  84. }
  85. /*
  86. * Thread support.
  87. */
  88. typedef struct {
  89. void (*func)(void*);
  90. void *arg;
  91. long id;
  92. HANDLE done;
  93. } callobj;
  94. static int
  95. bootstrap(void *call)
  96. {
  97. callobj *obj = (callobj*)call;
  98. /* copy callobj since other thread might free it before we're done */
  99. void (*func)(void*) = obj->func;
  100. void *arg = obj->arg;
  101. obj->id = PyThread_get_thread_ident();
  102. ReleaseSemaphore(obj->done, 1, NULL);
  103. func(arg);
  104. return 0;
  105. }
  106. long
  107. PyThread_start_new_thread(void (*func)(void *), void *arg)
  108. {
  109. Py_uintptr_t rv;
  110. callobj obj;
  111. dprintf(("%ld: PyThread_start_new_thread called\n",
  112. PyThread_get_thread_ident()));
  113. if (!initialized)
  114. PyThread_init_thread();
  115. obj.id = -1; /* guilty until proved innocent */
  116. obj.func = func;
  117. obj.arg = arg;
  118. obj.done = CreateSemaphore(NULL, 0, 1, NULL);
  119. if (obj.done == NULL)
  120. return -1;
  121. rv = _beginthread(bootstrap,
  122. Py_SAFE_DOWNCAST(_pythread_stacksize,
  123. Py_ssize_t, int),
  124. &obj);
  125. if (rv == (Py_uintptr_t)-1) {
  126. /* I've seen errno == EAGAIN here, which means "there are
  127. * too many threads".
  128. */
  129. dprintf(("%ld: PyThread_start_new_thread failed: %p errno %d\n",
  130. PyThread_get_thread_ident(), (void*)rv, errno));
  131. obj.id = -1;
  132. }
  133. else {
  134. dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n",
  135. PyThread_get_thread_ident(), (void*)rv));
  136. /* wait for thread to initialize, so we can get its id */
  137. WaitForSingleObject(obj.done, INFINITE);
  138. assert(obj.id != -1);
  139. }
  140. CloseHandle((HANDLE)obj.done);
  141. return obj.id;
  142. }
  143. /*
  144. * Return the thread Id instead of an handle. The Id is said to uniquely identify the
  145. * thread in the system
  146. */
  147. long
  148. PyThread_get_thread_ident(void)
  149. {
  150. if (!initialized)
  151. PyThread_init_thread();
  152. return GetCurrentThreadId();
  153. }
  154. static void
  155. do_PyThread_exit_thread(int no_cleanup)
  156. {
  157. dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident()));
  158. if (!initialized)
  159. if (no_cleanup)
  160. _exit(0);
  161. else
  162. exit(0);
  163. _endthread();
  164. }
  165. void
  166. PyThread_exit_thread(void)
  167. {
  168. do_PyThread_exit_thread(0);
  169. }
  170. void
  171. PyThread__exit_thread(void)
  172. {
  173. do_PyThread_exit_thread(1);
  174. }
  175. #ifndef NO_EXIT_PROG
  176. static void
  177. do_PyThread_exit_prog(int status, int no_cleanup)
  178. {
  179. dprintf(("PyThread_exit_prog(%d) called\n", status));
  180. if (!initialized)
  181. if (no_cleanup)
  182. _exit(status);
  183. else
  184. exit(status);
  185. }
  186. void
  187. PyThread_exit_prog(int status)
  188. {
  189. do_PyThread_exit_prog(status, 0);
  190. }
  191. void
  192. PyThread__exit_prog(int status)
  193. {
  194. do_PyThread_exit_prog(status, 1);
  195. }
  196. #endif /* NO_EXIT_PROG */
  197. /*
  198. * Lock support. It has too be implemented as semaphores.
  199. * I [Dag] tried to implement it with mutex but I could find a way to
  200. * tell whether a thread already own the lock or not.
  201. */
  202. PyThread_type_lock
  203. PyThread_allocate_lock(void)
  204. {
  205. PNRMUTEX aLock;
  206. dprintf(("PyThread_allocate_lock called\n"));
  207. if (!initialized)
  208. PyThread_init_thread();
  209. aLock = AllocNonRecursiveMutex() ;
  210. dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock));
  211. return (PyThread_type_lock) aLock;
  212. }
  213. void
  214. PyThread_free_lock(PyThread_type_lock aLock)
  215. {
  216. dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
  217. FreeNonRecursiveMutex(aLock) ;
  218. }
  219. /*
  220. * Return 1 on success if the lock was acquired
  221. *
  222. * and 0 if the lock was not acquired. This means a 0 is returned
  223. * if the lock has already been acquired by this thread!
  224. */
  225. int
  226. PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
  227. {
  228. int success ;
  229. dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag));
  230. success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ;
  231. dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success));
  232. return success;
  233. }
  234. void
  235. PyThread_release_lock(PyThread_type_lock aLock)
  236. {
  237. dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
  238. if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock)))
  239. dprintf(("%ld: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError()));
  240. }
  241. /* minimum/maximum thread stack sizes supported */
  242. #define THREAD_MIN_STACKSIZE 0x8000 /* 32kB */
  243. #define THREAD_MAX_STACKSIZE 0x10000000 /* 256MB */
  244. /* set the thread stack size.
  245. * Return 0 if size is valid, -1 otherwise.
  246. */
  247. static int
  248. _pythread_nt_set_stacksize(size_t size)
  249. {
  250. /* set to default */
  251. if (size == 0) {
  252. _pythread_stacksize = 0;
  253. return 0;
  254. }
  255. /* valid range? */
  256. if (size >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) {
  257. _pythread_stacksize = size;
  258. return 0;
  259. }
  260. return -1;
  261. }
  262. #define THREAD_SET_STACKSIZE(x) _pythread_nt_set_stacksize(x)