2003-06-12 Aldy Hernandez <aldyh@redhat.com>
[official-gcc.git] / boehm-gc / win32_threads.c
blob954b18d7018a96dae616907db17dd1fb0095205f
1 #if defined(GC_WIN32_THREADS)
3 #include "private/gc_priv.h"
5 #if 0
6 #define STRICT
7 #include <windows.h>
8 #endif
10 #define MAX_THREADS 64
12 struct thread_entry {
13 LONG in_use;
14 DWORD id;
15 HANDLE handle;
16 void *stack; /* The cold end of the stack. */
17 /* 0 ==> entry not valid. */
18 /* !in_use ==> stack == 0 */
19 CONTEXT context;
20 GC_bool suspended;
23 volatile GC_bool GC_please_stop = FALSE;
25 volatile struct thread_entry thread_table[MAX_THREADS];
27 void GC_push_thread_structures GC_PROTO((void))
29 /* Unlike the other threads implementations, the thread table here */
30 /* contains no pointers to the collectable heap. Thus we have */
31 /* no private structures we need to preserve. */
34 void GC_stop_world()
36 DWORD thread_id = GetCurrentThreadId();
37 int i;
39 GC_please_stop = TRUE;
40 for (i = 0; i < MAX_THREADS; i++)
41 if (thread_table[i].stack != 0
42 && thread_table[i].id != thread_id) {
43 # ifdef MSWINCE
44 /* SuspendThread will fail if thread is running kernel code */
45 while (SuspendThread(thread_table[i].handle) == (DWORD)-1)
46 Sleep(10);
47 # else
48 /* Apparently the Windows 95 GetOpenFileName call creates */
49 /* a thread that does not properly get cleaned up, and */
50 /* SuspendThread on its descriptor may provoke a crash. */
51 /* This reduces the probability of that event, though it still */
52 /* appears there's a race here. */
53 DWORD exitCode;
54 if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&
55 exitCode != STILL_ACTIVE) {
56 thread_table[i].stack = 0;
57 thread_table[i].in_use = FALSE;
58 CloseHandle(thread_table[i].handle);
59 BZERO((void *)(&thread_table[i].context), sizeof(CONTEXT));
60 continue;
62 if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
63 ABORT("SuspendThread failed");
64 # endif
65 thread_table[i].suspended = TRUE;
69 void GC_start_world()
71 DWORD thread_id = GetCurrentThreadId();
72 int i;
73 for (i = 0; i < MAX_THREADS; i++)
74 if (thread_table[i].stack != 0 && thread_table[i].suspended
75 && thread_table[i].id != thread_id) {
76 if (ResumeThread(thread_table[i].handle) == (DWORD)-1)
77 ABORT("ResumeThread failed");
78 thread_table[i].suspended = FALSE;
80 GC_please_stop = FALSE;
83 # ifdef _MSC_VER
84 # pragma warning(disable:4715)
85 # endif
86 ptr_t GC_current_stackbottom()
88 DWORD thread_id = GetCurrentThreadId();
89 int i;
90 for (i = 0; i < MAX_THREADS; i++)
91 if (thread_table[i].stack && thread_table[i].id == thread_id)
92 return thread_table[i].stack;
93 ABORT("no thread table entry for current thread");
95 # ifdef _MSC_VER
96 # pragma warning(default:4715)
97 # endif
99 # ifdef MSWINCE
100 /* The VirtualQuery calls below won't work properly on WinCE, but */
101 /* since each stack is restricted to an aligned 64K region of */
102 /* virtual memory we can just take the next lowest multiple of 64K. */
103 # define GC_get_lo_stack_addr(s) \
104 ((ptr_t)(((DWORD)(s) - 1) & 0xFFFF0000))
105 # else
106 static ptr_t GC_get_lo_stack_addr(ptr_t s)
108 ptr_t bottom;
109 MEMORY_BASIC_INFORMATION info;
110 VirtualQuery(s, &info, sizeof(info));
111 do {
112 bottom = info.BaseAddress;
113 VirtualQuery(bottom - 1, &info, sizeof(info));
114 } while ((info.Protect & PAGE_READWRITE)
115 && !(info.Protect & PAGE_GUARD));
116 return(bottom);
118 # endif
120 void GC_push_all_stacks()
122 DWORD thread_id = GetCurrentThreadId();
123 int i;
124 for (i = 0; i < MAX_THREADS; i++)
125 if (thread_table[i].stack) {
126 ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack);
127 if (thread_table[i].id == thread_id)
128 GC_push_all_stack((ptr_t)&i, thread_table[i].stack);
129 else {
130 thread_table[i].context.ContextFlags
131 = (CONTEXT_INTEGER|CONTEXT_CONTROL);
132 if (!GetThreadContext(thread_table[i].handle,
133 /* cast away volatile qualifier */
134 (LPCONTEXT)&thread_table[i].context))
135 ABORT("GetThreadContext failed");
136 # ifdef I386
137 GC_push_one ((word) thread_table[i].context.Edi);
138 GC_push_one ((word) thread_table[i].context.Esi);
139 GC_push_one ((word) thread_table[i].context.Ebp);
140 GC_push_one ((word) thread_table[i].context.Ebx);
141 GC_push_one ((word) thread_table[i].context.Edx);
142 GC_push_one ((word) thread_table[i].context.Ecx);
143 GC_push_one ((word) thread_table[i].context.Eax);
144 if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack
145 || thread_table[i].context.Esp < (DWORD)bottom) {
146 WARN("Thread stack pointer 0x%lx out of range, pushing everything",
147 thread_table[i].context.Esp);
148 GC_push_all_stack((char *) bottom, thread_table[i].stack);
149 } else {
150 GC_push_all_stack((char *) thread_table[i].context.Esp,
151 thread_table[i].stack);
153 # else
154 # ifdef ARM32
155 if (thread_table[i].context.Sp >= (DWORD)thread_table[i].stack
156 || thread_table[i].context.Sp < (DWORD)bottom)
157 ABORT("Thread stack pointer out of range");
158 GC_push_one ((word) thread_table[i].context.R0);
159 GC_push_one ((word) thread_table[i].context.R1);
160 GC_push_one ((word) thread_table[i].context.R2);
161 GC_push_one ((word) thread_table[i].context.R3);
162 GC_push_one ((word) thread_table[i].context.R4);
163 GC_push_one ((word) thread_table[i].context.R5);
164 GC_push_one ((word) thread_table[i].context.R6);
165 GC_push_one ((word) thread_table[i].context.R7);
166 GC_push_one ((word) thread_table[i].context.R8);
167 GC_push_one ((word) thread_table[i].context.R9);
168 GC_push_one ((word) thread_table[i].context.R10);
169 GC_push_one ((word) thread_table[i].context.R11);
170 GC_push_one ((word) thread_table[i].context.R12);
171 GC_push_all_stack((char *) thread_table[i].context.Sp,
172 thread_table[i].stack);
173 # else
174 # ifdef SHx
175 if (thread_table[i].context.R15 >= (DWORD)thread_table[i].stack
176 || thread_table[i].context.R15 < (DWORD)bottom)
177 ABORT("Thread stack pointer out of range");
178 GC_push_one ((word) thread_table[i].context.R0);
179 GC_push_one ((word) thread_table[i].context.R1);
180 GC_push_one ((word) thread_table[i].context.R2);
181 GC_push_one ((word) thread_table[i].context.R3);
182 GC_push_one ((word) thread_table[i].context.R4);
183 GC_push_one ((word) thread_table[i].context.R5);
184 GC_push_one ((word) thread_table[i].context.R6);
185 GC_push_one ((word) thread_table[i].context.R7);
186 GC_push_one ((word) thread_table[i].context.R8);
187 GC_push_one ((word) thread_table[i].context.R9);
188 GC_push_one ((word) thread_table[i].context.R10);
189 GC_push_one ((word) thread_table[i].context.R11);
190 GC_push_one ((word) thread_table[i].context.R12);
191 GC_push_one ((word) thread_table[i].context.R13);
192 GC_push_one ((word) thread_table[i].context.R14);
193 GC_push_all_stack((char *) thread_table[i].context.R15,
194 thread_table[i].stack);
195 # else
196 # ifdef MIPS
197 if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack
198 || thread_table[i].context.IntSp < (DWORD)bottom)
199 ABORT("Thread stack pointer out of range");
200 GC_push_one ((word) thread_table[i].context.IntAt);
201 GC_push_one ((word) thread_table[i].context.IntV0);
202 GC_push_one ((word) thread_table[i].context.IntV1);
203 GC_push_one ((word) thread_table[i].context.IntA0);
204 GC_push_one ((word) thread_table[i].context.IntA1);
205 GC_push_one ((word) thread_table[i].context.IntA2);
206 GC_push_one ((word) thread_table[i].context.IntA3);
207 GC_push_one ((word) thread_table[i].context.IntT0);
208 GC_push_one ((word) thread_table[i].context.IntT1);
209 GC_push_one ((word) thread_table[i].context.IntT2);
210 GC_push_one ((word) thread_table[i].context.IntT3);
211 GC_push_one ((word) thread_table[i].context.IntT4);
212 GC_push_one ((word) thread_table[i].context.IntT5);
213 GC_push_one ((word) thread_table[i].context.IntT6);
214 GC_push_one ((word) thread_table[i].context.IntT7);
215 GC_push_one ((word) thread_table[i].context.IntS0);
216 GC_push_one ((word) thread_table[i].context.IntS1);
217 GC_push_one ((word) thread_table[i].context.IntS2);
218 GC_push_one ((word) thread_table[i].context.IntS3);
219 GC_push_one ((word) thread_table[i].context.IntS4);
220 GC_push_one ((word) thread_table[i].context.IntS5);
221 GC_push_one ((word) thread_table[i].context.IntS6);
222 GC_push_one ((word) thread_table[i].context.IntS7);
223 GC_push_one ((word) thread_table[i].context.IntT8);
224 GC_push_one ((word) thread_table[i].context.IntT9);
225 GC_push_one ((word) thread_table[i].context.IntK0);
226 GC_push_one ((word) thread_table[i].context.IntK1);
227 GC_push_one ((word) thread_table[i].context.IntS8);
228 GC_push_all_stack((char *) thread_table[i].context.IntSp,
229 thread_table[i].stack);
230 # else
231 # ifdef PPC
232 if (thread_table[i].context.Gpr1 >= (DWORD)thread_table[i].stack
233 || thread_table[i].context.Gpr1 < (DWORD)bottom)
234 ABORT("Thread stack pointer out of range");
235 GC_push_one ((word) thread_table[i].context.Gpr0);
236 /* Gpr1 is stack pointer */
237 /* Gpr2 is global pointer */
238 GC_push_one ((word) thread_table[i].context.Gpr3);
239 GC_push_one ((word) thread_table[i].context.Gpr4);
240 GC_push_one ((word) thread_table[i].context.Gpr5);
241 GC_push_one ((word) thread_table[i].context.Gpr6);
242 GC_push_one ((word) thread_table[i].context.Gpr7);
243 GC_push_one ((word) thread_table[i].context.Gpr8);
244 GC_push_one ((word) thread_table[i].context.Gpr9);
245 GC_push_one ((word) thread_table[i].context.Gpr10);
246 GC_push_one ((word) thread_table[i].context.Gpr11);
247 GC_push_one ((word) thread_table[i].context.Gpr12);
248 /* Gpr13 is reserved for the kernel */
249 GC_push_one ((word) thread_table[i].context.Gpr14);
250 GC_push_one ((word) thread_table[i].context.Gpr15);
251 GC_push_one ((word) thread_table[i].context.Gpr16);
252 GC_push_one ((word) thread_table[i].context.Gpr17);
253 GC_push_one ((word) thread_table[i].context.Gpr18);
254 GC_push_one ((word) thread_table[i].context.Gpr19);
255 GC_push_one ((word) thread_table[i].context.Gpr20);
256 GC_push_one ((word) thread_table[i].context.Gpr21);
257 GC_push_one ((word) thread_table[i].context.Gpr22);
258 GC_push_one ((word) thread_table[i].context.Gpr23);
259 GC_push_one ((word) thread_table[i].context.Gpr24);
260 GC_push_one ((word) thread_table[i].context.Gpr25);
261 GC_push_one ((word) thread_table[i].context.Gpr26);
262 GC_push_one ((word) thread_table[i].context.Gpr27);
263 GC_push_one ((word) thread_table[i].context.Gpr28);
264 GC_push_one ((word) thread_table[i].context.Gpr29);
265 GC_push_one ((word) thread_table[i].context.Gpr30);
266 GC_push_one ((word) thread_table[i].context.Gpr31);
267 GC_push_all_stack((char *) thread_table[i].context.Gpr1,
268 thread_table[i].stack);
269 # else
270 # ifdef ALPHA
271 if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack
272 || thread_table[i].context.IntSp < (DWORD)bottom)
273 ABORT("Thread stack pointer out of range");
274 GC_push_one ((word) thread_table[i].context.IntV0);
275 GC_push_one ((word) thread_table[i].context.IntT0);
276 GC_push_one ((word) thread_table[i].context.IntT1);
277 GC_push_one ((word) thread_table[i].context.IntT2);
278 GC_push_one ((word) thread_table[i].context.IntT3);
279 GC_push_one ((word) thread_table[i].context.IntT4);
280 GC_push_one ((word) thread_table[i].context.IntT5);
281 GC_push_one ((word) thread_table[i].context.IntT6);
282 GC_push_one ((word) thread_table[i].context.IntT7);
283 GC_push_one ((word) thread_table[i].context.IntS0);
284 GC_push_one ((word) thread_table[i].context.IntS1);
285 GC_push_one ((word) thread_table[i].context.IntS2);
286 GC_push_one ((word) thread_table[i].context.IntS3);
287 GC_push_one ((word) thread_table[i].context.IntS4);
288 GC_push_one ((word) thread_table[i].context.IntS5);
289 GC_push_one ((word) thread_table[i].context.IntFp);
290 GC_push_one ((word) thread_table[i].context.IntA0);
291 GC_push_one ((word) thread_table[i].context.IntA1);
292 GC_push_one ((word) thread_table[i].context.IntA2);
293 GC_push_one ((word) thread_table[i].context.IntA3);
294 GC_push_one ((word) thread_table[i].context.IntA4);
295 GC_push_one ((word) thread_table[i].context.IntA5);
296 GC_push_one ((word) thread_table[i].context.IntT8);
297 GC_push_one ((word) thread_table[i].context.IntT9);
298 GC_push_one ((word) thread_table[i].context.IntT10);
299 GC_push_one ((word) thread_table[i].context.IntT11);
300 GC_push_one ((word) thread_table[i].context.IntT12);
301 GC_push_one ((word) thread_table[i].context.IntAt);
302 GC_push_all_stack((char *) thread_table[i].context.IntSp,
303 thread_table[i].stack);
304 # else
305 --> architecture not supported
306 # endif /* !ALPHA */
307 # endif /* !PPC */
308 # endif /* !MIPS */
309 # endif /* !SHx */
310 # endif /* !ARM32 */
311 # endif /* !I386 */
316 void GC_get_next_stack(char *start, char **lo, char **hi)
318 int i;
319 # define ADDR_LIMIT (char *)(-1L)
320 char * current_min = ADDR_LIMIT;
322 for (i = 0; i < MAX_THREADS; i++) {
323 char * s = (char *)thread_table[i].stack;
325 if (0 != s && s > start && s < current_min) {
326 current_min = s;
329 *hi = current_min;
330 if (current_min == ADDR_LIMIT) {
331 *lo = ADDR_LIMIT;
332 return;
334 *lo = GC_get_lo_stack_addr(current_min);
335 if (*lo < start) *lo = start;
338 #if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))
340 HANDLE WINAPI GC_CreateThread(
341 LPSECURITY_ATTRIBUTES lpThreadAttributes,
342 DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
343 LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
345 return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
346 lpParameter, dwCreationFlags, lpThreadId);
349 #else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
351 typedef struct {
352 HANDLE child_ready_h, parent_ready_h;
353 volatile struct thread_entry * entry;
354 LPTHREAD_START_ROUTINE start;
355 LPVOID param;
356 } thread_args;
358 DWORD WINAPI thread_start(LPVOID arg);
360 HANDLE WINAPI GC_CreateThread(
361 LPSECURITY_ATTRIBUTES lpThreadAttributes,
362 DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
363 LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
365 HANDLE thread_h = NULL;
366 HANDLE child_ready_h, parent_ready_h;
368 int i;
369 thread_args args;
371 /* allocate thread slot */
372 LOCK();
373 for (i = 0; i != MAX_THREADS && thread_table[i].in_use; i++)
375 if (i != MAX_THREADS) {
376 thread_table[i].in_use = TRUE;
378 UNLOCK();
380 if (i != MAX_THREADS) {
382 /* create unnamed unsignalled events */
383 if (child_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
384 if (parent_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
386 /* set up thread arguments */
387 args.child_ready_h = child_ready_h;
388 args.parent_ready_h = parent_ready_h;
389 args.entry = &thread_table[i];
390 args.start = lpStartAddress;
391 args.param = lpParameter;
393 thread_h = CreateThread(lpThreadAttributes,
394 dwStackSize, thread_start,
395 &args,
396 dwCreationFlags & ~CREATE_SUSPENDED,
397 lpThreadId);
399 if (thread_h) {
401 /* fill in ID and handle; tell child this is done */
402 thread_table[i].id = *lpThreadId;
403 thread_table[i].handle = thread_h;
404 SetEvent (parent_ready_h);
406 /* wait for child to fill in stack and copy args */
407 WaitForSingleObject (child_ready_h, INFINITE);
409 /* suspend the child if requested */
410 if (dwCreationFlags & CREATE_SUSPENDED)
411 SuspendThread (thread_h);
413 /* let child call given function now (or when resumed) */
414 SetEvent (parent_ready_h);
416 } else {
417 CloseHandle (parent_ready_h);
422 CloseHandle (child_ready_h);
424 if (thread_h == NULL)
425 thread_table[i].in_use = FALSE;
427 } else { /* no thread slot found */
428 SetLastError (ERROR_TOO_MANY_TCBS);
431 return thread_h;
434 static DWORD WINAPI thread_start(LPVOID arg)
436 DWORD ret = 0;
437 thread_args args = *(thread_args *)arg;
439 /* wait for parent to fill in ID and handle */
440 WaitForSingleObject (args.parent_ready_h, INFINITE);
441 ResetEvent (args.parent_ready_h);
443 /* fill in stack; tell parent this is done */
444 args.entry->stack = GC_get_stack_base();
445 SetEvent (args.child_ready_h);
447 /* wait for parent to tell us to go (in case it needs to suspend us) */
448 WaitForSingleObject (args.parent_ready_h, INFINITE);
449 CloseHandle (args.parent_ready_h);
451 /* Clear the thread entry even if we exit with an exception. */
452 /* This is probably pointless, since an uncaught exception is */
453 /* supposed to result in the process being killed. */
454 #ifndef __GNUC__
455 __try {
456 #endif /* __GNUC__ */
457 ret = args.start (args.param);
458 #ifndef __GNUC__
459 } __finally {
460 #endif /* __GNUC__ */
461 LOCK();
462 args.entry->stack = 0;
463 args.entry->in_use = FALSE;
464 /* cast away volatile qualifier */
465 BZERO((void *) &args.entry->context, sizeof(CONTEXT));
466 UNLOCK();
467 #ifndef __GNUC__
469 #endif /* __GNUC__ */
471 return ret;
473 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
475 #ifdef MSWINCE
477 typedef struct {
478 HINSTANCE hInstance;
479 HINSTANCE hPrevInstance;
480 LPWSTR lpCmdLine;
481 int nShowCmd;
482 } main_thread_args;
484 DWORD WINAPI main_thread_start(LPVOID arg);
486 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
487 LPWSTR lpCmdLine, int nShowCmd)
489 DWORD exit_code = 1;
491 main_thread_args args = {
492 hInstance, hPrevInstance, lpCmdLine, nShowCmd
494 HANDLE thread_h;
495 DWORD thread_id;
497 /* initialize everything */
498 InitializeCriticalSection(&GC_allocate_ml);
499 GC_init();
501 /* start the main thread */
502 thread_h = GC_CreateThread(
503 NULL, 0, main_thread_start, &args, 0, &thread_id);
505 if (thread_h != NULL)
507 WaitForSingleObject (thread_h, INFINITE);
508 GetExitCodeThread (thread_h, &exit_code);
509 CloseHandle (thread_h);
512 GC_deinit();
513 DeleteCriticalSection(&GC_allocate_ml);
515 return (int) exit_code;
518 DWORD WINAPI main_thread_start(LPVOID arg)
520 main_thread_args * args = (main_thread_args *) arg;
522 return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance,
523 args->lpCmdLine, args->nShowCmd);
526 # else /* !MSWINCE */
528 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
530 #ifdef GC_DLL
533 * This isn't generally safe, since DllMain is not premptible.
534 * If another thread holds the lock while this runs we're in trouble.
535 * Pontus Rydin suggests wrapping the thread start routine instead.
537 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
539 switch (reason) {
540 case DLL_PROCESS_ATTACH:
541 InitializeCriticalSection(&GC_allocate_ml);
542 GC_init(); /* Force initialization before thread attach. */
543 /* fall through */
544 case DLL_THREAD_ATTACH:
546 int i;
547 /* It appears to be unsafe to acquire a lock here, since this */
548 /* code is apparently not preeemptible on some systems. */
549 /* (This is based on complaints, not on Microsoft's official */
550 /* documentation, which says this should perform "only simple */
551 /* inititalization tasks".) */
552 /* Hence we make do with nonblocking synchronization. */
554 /* The following should be a noop according to the win32 */
555 /* documentation. There is empirical evidence that it */
556 /* isn't. - HB */
557 # ifdef MPROTECT_VDB
558 if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
559 # endif
561 for (i = 0;
562 /* cast away volatile qualifier */
563 InterlockedExchange((LPLONG) &thread_table[i].in_use, 1) != 0;
564 i++) {
565 /* Compare-and-swap would make this cleaner, but that's not */
566 /* supported before Windows 98 and NT 4.0. In Windows 2000, */
567 /* InterlockedExchange is supposed to be replaced by */
568 /* InterlockedExchangePointer, but that's not really what I */
569 /* want here. */
570 if (i == MAX_THREADS - 1)
571 ABORT("too many threads");
573 thread_table[i].id = GetCurrentThreadId();
574 if (!DuplicateHandle(GetCurrentProcess(),
575 GetCurrentThread(),
576 GetCurrentProcess(),
577 /* cast away volatile qualifier */
578 (HANDLE *) &thread_table[i].handle,
581 DUPLICATE_SAME_ACCESS)) {
582 DWORD last_error = GetLastError();
583 GC_printf1("Last error code: %lx\n", last_error);
584 ABORT("DuplicateHandle failed");
586 thread_table[i].stack = GC_get_stack_base();
587 /* If this thread is being created while we are trying to stop */
588 /* the world, wait here. Hopefully this can't happen on any */
589 /* systems that don't allow us to block here. */
590 while (GC_please_stop) Sleep(20);
592 break;
593 case DLL_THREAD_DETACH:
595 int i;
596 DWORD thread_id = GetCurrentThreadId();
597 LOCK();
598 for (i = 0;
599 i < MAX_THREADS &&
600 (thread_table[i].stack == 0 || thread_table[i].id != thread_id);
601 i++) {}
602 if (i >= MAX_THREADS) {
603 WARN("thread %ld not found on detach", (GC_word)thread_id);
604 } else {
605 thread_table[i].stack = 0;
606 thread_table[i].in_use = FALSE;
607 CloseHandle(thread_table[i].handle);
608 /* cast away volatile qualifier */
609 BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));
611 UNLOCK();
613 break;
614 case DLL_PROCESS_DETACH:
616 int i;
618 LOCK();
619 for (i = 0; i < MAX_THREADS; ++i)
621 if (thread_table[i].in_use)
623 thread_table[i].stack = 0;
624 thread_table[i].in_use = FALSE;
625 CloseHandle(thread_table[i].handle);
626 BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));
629 UNLOCK();
631 GC_deinit();
632 DeleteCriticalSection(&GC_allocate_ml);
634 break;
637 return TRUE;
640 # endif /* GC_DLL */
642 # endif /* !MSWINCE */
644 #endif /* GC_WIN32_THREADS */