You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

359 lines
8.6 KiB

31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
An Anonymous Coward on c.l.py posted a little program with bizarre behavior, creating many threads very quickly. A long debugging session revealed that the Windows implementation of PyThread_start_new_thread() was choked with "laziness" errors: 1. It checked MS _beginthread() for a failure return, but when that happened it returned heap trash as the function result, instead of an id of -1 (the proper error-return value). 2. It didn't consider that the Win32 CreateSemaphore() can fail. 3. When creating a great many threads very quickly, it's quite possible that any particular bootstrap call can take virtually any amount of time to return. But the code waited for a maximum of 5 seconds, and didn't check to see whether the semaphore it was waiting for got signaled. If it in fact timed out, the function could again return heap trash as the function result. This is actually what confused the test program, as the heap trash usually turned out to be 0, and then multiple threads all got id 0 simultaneously, confusing the hell out of threading.py's _active dict (mapping id to thread object). A variety of baffling behaviors followed from that. WRT #1 and #2, error returns are checked now, and "thread.error: can't start new thread" gets raised now if a new thread (or new semaphore) can't be created. WRT #3, we now wait for the semaphore without a timeout. Also removed useless local vrbls, folded long lines, and changed callobj to a stack auto (it was going thru malloc/free instead, for no discernible reason). Bugfix candidate.
23 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
31 years ago
  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. } callobj;
  92. /* thunker to call adapt between the function type used by the system's
  93. thread start function and the internally used one. */
  94. #if defined(MS_WINCE)
  95. static DWORD WINAPI
  96. #else
  97. static unsigned __stdcall
  98. #endif
  99. bootstrap(void *call)
  100. {
  101. callobj *obj = (callobj*)call;
  102. void (*func)(void*) = obj->func;
  103. void *arg = obj->arg;
  104. HeapFree(GetProcessHeap(), 0, obj);
  105. func(arg);
  106. return 0;
  107. }
  108. long
  109. PyThread_start_new_thread(void (*func)(void *), void *arg)
  110. {
  111. HANDLE hThread;
  112. unsigned threadID;
  113. callobj *obj;
  114. dprintf(("%ld: PyThread_start_new_thread called\n",
  115. PyThread_get_thread_ident()));
  116. if (!initialized)
  117. PyThread_init_thread();
  118. obj = (callobj*)HeapAlloc(GetProcessHeap(), 0, sizeof(*obj));
  119. if (!obj)
  120. return -1;
  121. obj->func = func;
  122. obj->arg = arg;
  123. #if defined(MS_WINCE)
  124. hThread = CreateThread(NULL,
  125. Py_SAFE_DOWNCAST(_pythread_stacksize, Py_ssize_t, SIZE_T),
  126. bootstrap, obj, 0, &threadID);
  127. #else
  128. hThread = (HANDLE)_beginthreadex(0,
  129. Py_SAFE_DOWNCAST(_pythread_stacksize,
  130. Py_ssize_t, unsigned int),
  131. bootstrap, obj,
  132. 0, &threadID);
  133. #endif
  134. if (hThread == 0) {
  135. #if defined(MS_WINCE)
  136. /* Save error in variable, to prevent PyThread_get_thread_ident
  137. from clobbering it. */
  138. unsigned e = GetLastError();
  139. dprintf(("%ld: PyThread_start_new_thread failed, win32 error code %u\n",
  140. PyThread_get_thread_ident(), e));
  141. #else
  142. /* I've seen errno == EAGAIN here, which means "there are
  143. * too many threads".
  144. */
  145. int e = errno;
  146. dprintf(("%ld: PyThread_start_new_thread failed, errno %d\n",
  147. PyThread_get_thread_ident(), e));
  148. #endif
  149. threadID = (unsigned)-1;
  150. HeapFree(GetProcessHeap(), 0, obj);
  151. }
  152. else {
  153. dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n",
  154. PyThread_get_thread_ident(), (void*)hThread));
  155. CloseHandle(hThread);
  156. }
  157. return (long) threadID;
  158. }
  159. /*
  160. * Return the thread Id instead of an handle. The Id is said to uniquely identify the
  161. * thread in the system
  162. */
  163. long
  164. PyThread_get_thread_ident(void)
  165. {
  166. if (!initialized)
  167. PyThread_init_thread();
  168. return GetCurrentThreadId();
  169. }
  170. void
  171. PyThread_exit_thread(void)
  172. {
  173. dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident()));
  174. if (!initialized)
  175. exit(0);
  176. #if defined(MS_WINCE)
  177. ExitThread(0);
  178. #else
  179. _endthreadex(0);
  180. #endif
  181. }
  182. /*
  183. * Lock support. It has too be implemented as semaphores.
  184. * I [Dag] tried to implement it with mutex but I could find a way to
  185. * tell whether a thread already own the lock or not.
  186. */
  187. PyThread_type_lock
  188. PyThread_allocate_lock(void)
  189. {
  190. PNRMUTEX aLock;
  191. dprintf(("PyThread_allocate_lock called\n"));
  192. if (!initialized)
  193. PyThread_init_thread();
  194. aLock = AllocNonRecursiveMutex() ;
  195. dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock));
  196. return (PyThread_type_lock) aLock;
  197. }
  198. void
  199. PyThread_free_lock(PyThread_type_lock aLock)
  200. {
  201. dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
  202. FreeNonRecursiveMutex(aLock) ;
  203. }
  204. /*
  205. * Return 1 on success if the lock was acquired
  206. *
  207. * and 0 if the lock was not acquired. This means a 0 is returned
  208. * if the lock has already been acquired by this thread!
  209. */
  210. int
  211. PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag)
  212. {
  213. int success ;
  214. dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag));
  215. success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ;
  216. dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success));
  217. return success;
  218. }
  219. void
  220. PyThread_release_lock(PyThread_type_lock aLock)
  221. {
  222. dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock));
  223. if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock)))
  224. dprintf(("%ld: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError()));
  225. }
  226. /* minimum/maximum thread stack sizes supported */
  227. #define THREAD_MIN_STACKSIZE 0x8000 /* 32kB */
  228. #define THREAD_MAX_STACKSIZE 0x10000000 /* 256MB */
  229. /* set the thread stack size.
  230. * Return 0 if size is valid, -1 otherwise.
  231. */
  232. static int
  233. _pythread_nt_set_stacksize(size_t size)
  234. {
  235. /* set to default */
  236. if (size == 0) {
  237. _pythread_stacksize = 0;
  238. return 0;
  239. }
  240. /* valid range? */
  241. if (size >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) {
  242. _pythread_stacksize = size;
  243. return 0;
  244. }
  245. return -1;
  246. }
  247. #define THREAD_SET_STACKSIZE(x) _pythread_nt_set_stacksize(x)
  248. /* use native Windows TLS functions */
  249. #define Py_HAVE_NATIVE_TLS
  250. #ifdef Py_HAVE_NATIVE_TLS
  251. int
  252. PyThread_create_key(void)
  253. {
  254. return (int) TlsAlloc();
  255. }
  256. void
  257. PyThread_delete_key(int key)
  258. {
  259. TlsFree(key);
  260. }
  261. /* We must be careful to emulate the strange semantics implemented in thread.c,
  262. * where the value is only set if it hasn't been set before.
  263. */
  264. int
  265. PyThread_set_key_value(int key, void *value)
  266. {
  267. BOOL ok;
  268. void *oldvalue;
  269. assert(value != NULL);
  270. oldvalue = TlsGetValue(key);
  271. if (oldvalue != NULL)
  272. /* ignore value if already set */
  273. return 0;
  274. ok = TlsSetValue(key, value);
  275. if (!ok)
  276. return -1;
  277. return 0;
  278. }
  279. void *
  280. PyThread_get_key_value(int key)
  281. {
  282. /* because TLS is used in the Py_END_ALLOW_THREAD macro,
  283. * it is necessary to preserve the windows error state, because
  284. * it is assumed to be preserved across the call to the macro.
  285. * Ideally, the macro should be fixed, but it is simpler to
  286. * do it here.
  287. */
  288. DWORD error = GetLastError();
  289. void *result = TlsGetValue(key);
  290. SetLastError(error);
  291. return result;
  292. }
  293. void
  294. PyThread_delete_key_value(int key)
  295. {
  296. /* NULL is used as "key missing", and it is also the default
  297. * given by TlsGetValue() if nothing has been set yet.
  298. */
  299. TlsSetValue(key, NULL);
  300. }
  301. /* reinitialization of TLS is not necessary after fork when using
  302. * the native TLS functions. And forking isn't supported on Windows either.
  303. */
  304. void
  305. PyThread_ReInitTLS(void)
  306. {}
  307. #endif