OSDN Git Service

Add fix test from solaris27_mutex_init
[pf3gnuchains/gcc-fork.git] / boehm-gc / win32_threads.c
1 #if defined(GC_WIN32_THREADS) || defined(WIN32_THREADS)
2
3 #include "private/gc_priv.h"
4
5 #if 0
6 #define STRICT
7 #include <windows.h>
8 #endif
9
10 #define MAX_THREADS 64
11
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;
21 };
22
23 volatile GC_bool GC_please_stop = FALSE;
24
25 volatile struct thread_entry thread_table[MAX_THREADS];
26
27 void GC_push_thread_structures GC_PROTO((void))
28 {
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.                       */
32 }
33
34 void GC_stop_world()
35 {
36   DWORD thread_id = GetCurrentThreadId();
37   int i;
38
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;
61         }
62         if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
63           ABORT("SuspendThread failed");
64 #     endif
65       thread_table[i].suspended = TRUE;
66     }
67 }
68
69 void GC_start_world()
70 {
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;
79     }
80   GC_please_stop = FALSE;
81 }
82
83 # ifdef _MSC_VER
84 #   pragma warning(disable:4715)
85 # endif
86 ptr_t GC_current_stackbottom()
87 {
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");
94 }
95 # ifdef _MSC_VER
96 #   pragma warning(default:4715)
97 # endif
98
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)
107     {
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);
117     }
118 # endif
119
120 void GC_push_all_stacks()
121 {
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           if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack
138               || thread_table[i].context.Esp < (DWORD)bottom)
139               ABORT("Thread stack pointer out of range");
140           GC_push_one ((word) thread_table[i].context.Edi);
141           GC_push_one ((word) thread_table[i].context.Esi);
142           GC_push_one ((word) thread_table[i].context.Ebp);
143           GC_push_one ((word) thread_table[i].context.Ebx);
144           GC_push_one ((word) thread_table[i].context.Edx);
145           GC_push_one ((word) thread_table[i].context.Ecx);
146           GC_push_one ((word) thread_table[i].context.Eax);
147           GC_push_all_stack((char *) thread_table[i].context.Esp,
148                             thread_table[i].stack);
149 #       else
150 #       ifdef ARM32
151           if (thread_table[i].context.Sp >= (DWORD)thread_table[i].stack
152               || thread_table[i].context.Sp < (DWORD)bottom)
153               ABORT("Thread stack pointer out of range");
154           GC_push_one ((word) thread_table[i].context.R0);
155           GC_push_one ((word) thread_table[i].context.R1);
156           GC_push_one ((word) thread_table[i].context.R2);
157           GC_push_one ((word) thread_table[i].context.R3);
158           GC_push_one ((word) thread_table[i].context.R4);
159           GC_push_one ((word) thread_table[i].context.R5);
160           GC_push_one ((word) thread_table[i].context.R6);
161           GC_push_one ((word) thread_table[i].context.R7);
162           GC_push_one ((word) thread_table[i].context.R8);
163           GC_push_one ((word) thread_table[i].context.R9);
164           GC_push_one ((word) thread_table[i].context.R10);
165           GC_push_one ((word) thread_table[i].context.R11);
166           GC_push_one ((word) thread_table[i].context.R12);
167           GC_push_all_stack((char *) thread_table[i].context.Sp,
168                             thread_table[i].stack);
169 #       else
170 #       ifdef SHx
171           if (thread_table[i].context.R15 >= (DWORD)thread_table[i].stack
172               || thread_table[i].context.R15 < (DWORD)bottom)
173               ABORT("Thread stack pointer out of range");
174           GC_push_one ((word) thread_table[i].context.R0);
175           GC_push_one ((word) thread_table[i].context.R1);
176           GC_push_one ((word) thread_table[i].context.R2);
177           GC_push_one ((word) thread_table[i].context.R3);
178           GC_push_one ((word) thread_table[i].context.R4);
179           GC_push_one ((word) thread_table[i].context.R5);
180           GC_push_one ((word) thread_table[i].context.R6);
181           GC_push_one ((word) thread_table[i].context.R7);
182           GC_push_one ((word) thread_table[i].context.R8);
183           GC_push_one ((word) thread_table[i].context.R9);
184           GC_push_one ((word) thread_table[i].context.R10);
185           GC_push_one ((word) thread_table[i].context.R11);
186           GC_push_one ((word) thread_table[i].context.R12);
187           GC_push_one ((word) thread_table[i].context.R13);
188           GC_push_one ((word) thread_table[i].context.R14);
189           GC_push_all_stack((char *) thread_table[i].context.R15,
190                             thread_table[i].stack);
191 #       else
192 #       ifdef MIPS
193           if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack
194               || thread_table[i].context.IntSp < (DWORD)bottom)
195               ABORT("Thread stack pointer out of range");
196           GC_push_one ((word) thread_table[i].context.IntAt);
197           GC_push_one ((word) thread_table[i].context.IntV0);
198           GC_push_one ((word) thread_table[i].context.IntV1);
199           GC_push_one ((word) thread_table[i].context.IntA0);
200           GC_push_one ((word) thread_table[i].context.IntA1);
201           GC_push_one ((word) thread_table[i].context.IntA2);
202           GC_push_one ((word) thread_table[i].context.IntA3);
203           GC_push_one ((word) thread_table[i].context.IntT0);
204           GC_push_one ((word) thread_table[i].context.IntT1);
205           GC_push_one ((word) thread_table[i].context.IntT2);
206           GC_push_one ((word) thread_table[i].context.IntT3);
207           GC_push_one ((word) thread_table[i].context.IntT4);
208           GC_push_one ((word) thread_table[i].context.IntT5);
209           GC_push_one ((word) thread_table[i].context.IntT6);
210           GC_push_one ((word) thread_table[i].context.IntT7);
211           GC_push_one ((word) thread_table[i].context.IntS0);
212           GC_push_one ((word) thread_table[i].context.IntS1);
213           GC_push_one ((word) thread_table[i].context.IntS2);
214           GC_push_one ((word) thread_table[i].context.IntS3);
215           GC_push_one ((word) thread_table[i].context.IntS4);
216           GC_push_one ((word) thread_table[i].context.IntS5);
217           GC_push_one ((word) thread_table[i].context.IntS6);
218           GC_push_one ((word) thread_table[i].context.IntS7);
219           GC_push_one ((word) thread_table[i].context.IntT8);
220           GC_push_one ((word) thread_table[i].context.IntT9);
221           GC_push_one ((word) thread_table[i].context.IntK0);
222           GC_push_one ((word) thread_table[i].context.IntK1);
223           GC_push_one ((word) thread_table[i].context.IntS8);
224           GC_push_all_stack((char *) thread_table[i].context.IntSp,
225                             thread_table[i].stack);
226 #       else
227 #       ifdef PPC
228           if (thread_table[i].context.Gpr1 >= (DWORD)thread_table[i].stack
229               || thread_table[i].context.Gpr1 < (DWORD)bottom)
230               ABORT("Thread stack pointer out of range");
231           GC_push_one ((word) thread_table[i].context.Gpr0);
232           /* Gpr1 is stack pointer */
233           /* Gpr2 is global pointer */
234           GC_push_one ((word) thread_table[i].context.Gpr3);
235           GC_push_one ((word) thread_table[i].context.Gpr4);
236           GC_push_one ((word) thread_table[i].context.Gpr5);
237           GC_push_one ((word) thread_table[i].context.Gpr6);
238           GC_push_one ((word) thread_table[i].context.Gpr7);
239           GC_push_one ((word) thread_table[i].context.Gpr8);
240           GC_push_one ((word) thread_table[i].context.Gpr9);
241           GC_push_one ((word) thread_table[i].context.Gpr10);
242           GC_push_one ((word) thread_table[i].context.Gpr11);
243           GC_push_one ((word) thread_table[i].context.Gpr12);
244           /* Gpr13 is reserved for the kernel */
245           GC_push_one ((word) thread_table[i].context.Gpr14);
246           GC_push_one ((word) thread_table[i].context.Gpr15);
247           GC_push_one ((word) thread_table[i].context.Gpr16);
248           GC_push_one ((word) thread_table[i].context.Gpr17);
249           GC_push_one ((word) thread_table[i].context.Gpr18);
250           GC_push_one ((word) thread_table[i].context.Gpr19);
251           GC_push_one ((word) thread_table[i].context.Gpr20);
252           GC_push_one ((word) thread_table[i].context.Gpr21);
253           GC_push_one ((word) thread_table[i].context.Gpr22);
254           GC_push_one ((word) thread_table[i].context.Gpr23);
255           GC_push_one ((word) thread_table[i].context.Gpr24);
256           GC_push_one ((word) thread_table[i].context.Gpr25);
257           GC_push_one ((word) thread_table[i].context.Gpr26);
258           GC_push_one ((word) thread_table[i].context.Gpr27);
259           GC_push_one ((word) thread_table[i].context.Gpr28);
260           GC_push_one ((word) thread_table[i].context.Gpr29);
261           GC_push_one ((word) thread_table[i].context.Gpr30);
262           GC_push_one ((word) thread_table[i].context.Gpr31);
263           GC_push_all_stack((char *) thread_table[i].context.Gpr1,
264                             thread_table[i].stack);
265 #       else
266 #       ifdef ALPHA
267           if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack
268               || thread_table[i].context.IntSp < (DWORD)bottom)
269               ABORT("Thread stack pointer out of range");
270           GC_push_one ((word) thread_table[i].context.IntV0);
271           GC_push_one ((word) thread_table[i].context.IntT0);
272           GC_push_one ((word) thread_table[i].context.IntT1);
273           GC_push_one ((word) thread_table[i].context.IntT2);
274           GC_push_one ((word) thread_table[i].context.IntT3);
275           GC_push_one ((word) thread_table[i].context.IntT4);
276           GC_push_one ((word) thread_table[i].context.IntT5);
277           GC_push_one ((word) thread_table[i].context.IntT6);
278           GC_push_one ((word) thread_table[i].context.IntT7);
279           GC_push_one ((word) thread_table[i].context.IntS0);
280           GC_push_one ((word) thread_table[i].context.IntS1);
281           GC_push_one ((word) thread_table[i].context.IntS2);
282           GC_push_one ((word) thread_table[i].context.IntS3);
283           GC_push_one ((word) thread_table[i].context.IntS4);
284           GC_push_one ((word) thread_table[i].context.IntS5);
285           GC_push_one ((word) thread_table[i].context.IntFp);
286           GC_push_one ((word) thread_table[i].context.IntA0);
287           GC_push_one ((word) thread_table[i].context.IntA1);
288           GC_push_one ((word) thread_table[i].context.IntA2);
289           GC_push_one ((word) thread_table[i].context.IntA3);
290           GC_push_one ((word) thread_table[i].context.IntA4);
291           GC_push_one ((word) thread_table[i].context.IntA5);
292           GC_push_one ((word) thread_table[i].context.IntT8);
293           GC_push_one ((word) thread_table[i].context.IntT9);
294           GC_push_one ((word) thread_table[i].context.IntT10);
295           GC_push_one ((word) thread_table[i].context.IntT11);
296           GC_push_one ((word) thread_table[i].context.IntT12);
297           GC_push_one ((word) thread_table[i].context.IntAt);
298           GC_push_all_stack((char *) thread_table[i].context.IntSp,
299                             thread_table[i].stack);
300 #       else
301               --> architecture not supported
302 #       endif /* !ALPHA */
303 #       endif /* !PPC */
304 #       endif /* !MIPS */
305 #       endif /* !SHx */
306 #       endif /* !ARM32 */
307 #       endif /* !I386 */
308       }
309     }
310 }
311
312 void GC_get_next_stack(char *start, char **lo, char **hi)
313 {
314     int i;
315 #   define ADDR_LIMIT (char *)(-1L)
316     char * current_min = ADDR_LIMIT;
317
318     for (i = 0; i < MAX_THREADS; i++) {
319         char * s = (char *)thread_table[i].stack;
320
321         if (0 != s && s > start && s < current_min) {
322             current_min = s;
323         }
324     }
325     *hi = current_min;
326     if (current_min == ADDR_LIMIT) {
327         *lo = ADDR_LIMIT;
328         return;
329     }
330     *lo = GC_get_lo_stack_addr(current_min);
331     if (*lo < start) *lo = start;
332 }
333
334
335 # ifdef MSWINCE
336
337 typedef struct {
338     HANDLE child_ready_h, parent_ready_h;
339     volatile struct thread_entry * entry;
340     LPTHREAD_START_ROUTINE start;
341     LPVOID param;
342 } thread_args;
343
344 DWORD WINAPI thread_start(LPVOID arg);
345
346 HANDLE WINAPI GC_CreateThread(
347     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
348     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
349     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
350 {
351     HANDLE thread_h = NULL;
352     HANDLE child_ready_h, parent_ready_h;
353
354     int i;
355     thread_args args;
356
357     /* allocate thread slot */
358     LOCK();
359     for (i = 0; i != MAX_THREADS && thread_table[i].in_use; i++)
360         ;
361     if (i != MAX_THREADS) {
362         thread_table[i].in_use = TRUE;
363     }
364     UNLOCK();
365
366     if (i != MAX_THREADS) {
367
368         /* create unnamed unsignalled events */
369         if (child_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
370             if (parent_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
371
372                 /* set up thread arguments */
373                 args.child_ready_h = child_ready_h;
374                 args.parent_ready_h = parent_ready_h;
375                 args.entry = &thread_table[i];
376                 args.start = lpStartAddress;
377                 args.param = lpParameter;
378
379                 thread_h = CreateThread(lpThreadAttributes,
380                                         dwStackSize, thread_start,
381                                         &args,
382                                         dwCreationFlags & ~CREATE_SUSPENDED,
383                                         lpThreadId);
384
385                 if (thread_h) {
386
387                     /* fill in ID and handle; tell child this is done */
388                     thread_table[i].id = *lpThreadId;
389                     thread_table[i].handle = thread_h;
390                     SetEvent (parent_ready_h);
391
392                     /* wait for child to fill in stack and copy args */
393                     WaitForSingleObject (child_ready_h, INFINITE);
394
395                     /* suspend the child if requested */
396                     if (dwCreationFlags & CREATE_SUSPENDED)
397                         SuspendThread (thread_h);
398
399                     /* let child call given function now (or when resumed) */
400                     SetEvent (parent_ready_h);
401
402                 } else {
403                     CloseHandle (parent_ready_h);
404                 }
405             }
406         }
407
408         CloseHandle (child_ready_h);
409
410         if (thread_h == NULL)
411             thread_table[i].in_use = FALSE;
412
413     } else { /* no thread slot found */
414         SetLastError (ERROR_TOO_MANY_TCBS);
415     }
416
417     return thread_h;
418 }
419
420 static DWORD WINAPI thread_start(LPVOID arg)
421 {
422     DWORD ret = 0;
423     thread_args args = *(thread_args *)arg;
424
425     /* wait for parent to fill in ID and handle */
426     WaitForSingleObject (args.parent_ready_h, INFINITE);
427     ResetEvent (args.parent_ready_h);
428
429     /* fill in stack; tell parent this is done */
430     args.entry->stack = GC_get_stack_base();
431     SetEvent (args.child_ready_h);
432
433     /* wait for parent to tell us to go (in case it needs to suspend us) */
434     WaitForSingleObject (args.parent_ready_h, INFINITE);
435     CloseHandle (args.parent_ready_h);
436
437     /* Clear the thread entry even if we exit with an exception.        */
438     /* This is probably pointless, since an uncaught exception is       */
439     /* supposed to result in the process being killed.                  */
440     __try {
441         ret = args.start (args.param);
442     } __finally {
443         LOCK();
444         args.entry->stack = 0;
445         args.entry->in_use = FALSE;
446               /* cast away volatile qualifier */
447         BZERO((void *) &args.entry->context, sizeof(CONTEXT));
448         UNLOCK();
449     }
450
451     return ret;
452 }
453
454 typedef struct {
455     HINSTANCE hInstance;
456     HINSTANCE hPrevInstance;
457     LPWSTR lpCmdLine;
458     int nShowCmd;
459 } main_thread_args;
460
461 DWORD WINAPI main_thread_start(LPVOID arg);
462
463 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
464                    LPWSTR lpCmdLine, int nShowCmd)
465 {
466     DWORD exit_code = 1;
467
468     main_thread_args args = {
469         hInstance, hPrevInstance, lpCmdLine, nShowCmd
470     };
471     HANDLE thread_h;
472     DWORD thread_id;
473
474     /* initialize everything */
475     InitializeCriticalSection(&GC_allocate_ml);
476     GC_init();
477
478     /* start the main thread */
479     thread_h = GC_CreateThread(
480         NULL, 0, main_thread_start, &args, 0, &thread_id);
481
482     if (thread_h != NULL)
483     {
484         WaitForSingleObject (thread_h, INFINITE);
485         GetExitCodeThread (thread_h, &exit_code);
486         CloseHandle (thread_h);
487     }
488
489     GC_deinit();
490     DeleteCriticalSection(&GC_allocate_ml);
491
492     return (int) exit_code;
493 }
494
495 DWORD WINAPI main_thread_start(LPVOID arg)
496 {
497     main_thread_args * args = (main_thread_args *) arg;
498
499     return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance,
500                                args->lpCmdLine, args->nShowCmd);
501 }
502
503 # else /* !MSWINCE */
504
505 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
506
507 /*
508  * This isn't generally safe, since DllMain is not premptible.
509  * If another thread holds the lock while this runs we're in trouble.
510  * Pontus Rydin suggests wrapping the thread start routine instead.
511  */
512 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
513 {
514   switch (reason) {
515   case DLL_PROCESS_ATTACH:
516     InitializeCriticalSection(&GC_allocate_ml);
517     GC_init();  /* Force initialization before thread attach.   */
518     /* fall through */
519   case DLL_THREAD_ATTACH:
520     {
521       int i;
522       /* It appears to be unsafe to acquire a lock here, since this     */
523       /* code is apparently not preeemptible on some systems.           */
524       /* (This is based on complaints, not on Microsoft's official      */
525       /* documentation, which says this should perform "only simple     */
526       /* inititalization tasks".)                                       */
527       /* Hence we make do with nonblocking synchronization.             */
528
529       /* The following should be a noop according to the win32  */
530       /* documentation.  There is empirical evidence that it    */
531       /* isn't.         - HB                                    */
532 #     ifdef MPROTECT_VDB
533        if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
534 #     endif
535
536       for (i = 0;
537                                /* cast away volatile qualifier */
538            InterlockedExchange((LPLONG) &thread_table[i].in_use, 1) != 0;
539            i++) {
540         /* Compare-and-swap would make this cleaner, but that's not     */
541         /* supported before Windows 98 and NT 4.0.  In Windows 2000,    */
542         /* InterlockedExchange is supposed to be replaced by            */
543         /* InterlockedExchangePointer, but that's not really what I     */
544         /* want here.                                                   */
545         if (i == MAX_THREADS - 1)
546           ABORT("too many threads");
547       }
548       thread_table[i].id = GetCurrentThreadId();
549       if (!DuplicateHandle(GetCurrentProcess(),
550                            GetCurrentThread(),
551                            GetCurrentProcess(),
552                            /* cast away volatile qualifier */
553                            (HANDLE *) &thread_table[i].handle,
554                            0,
555                            0,
556                            DUPLICATE_SAME_ACCESS)) {
557         DWORD last_error = GetLastError();
558         GC_printf1("Last error code: %lx\n", last_error);
559         ABORT("DuplicateHandle failed");
560       }
561       thread_table[i].stack = GC_get_stack_base();
562       /* If this thread is being created while we are trying to stop    */
563       /* the world, wait here.  Hopefully this can't happen on any      */
564       /* systems that don't allow us to block here.                     */
565       while (GC_please_stop) Sleep(20);
566     }
567     break;
568   case DLL_THREAD_DETACH:
569     {
570       int i;
571       DWORD thread_id = GetCurrentThreadId();
572       LOCK();
573       for (i = 0;
574            i < MAX_THREADS &&
575            (thread_table[i].stack == 0 || thread_table[i].id != thread_id);
576            i++) {}
577       if (i >= MAX_THREADS) {
578           WARN("thread %ld not found on detach", (GC_word)thread_id);
579       } else {
580           thread_table[i].stack = 0;
581           thread_table[i].in_use = FALSE;
582           CloseHandle(thread_table[i].handle);
583             /* cast away volatile qualifier */
584           BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));
585       }
586       UNLOCK();
587     }
588     break;
589   case DLL_PROCESS_DETACH:
590     {
591       int i;
592
593       LOCK();
594       for (i = 0; i < MAX_THREADS; ++i)
595       {
596           if (thread_table[i].in_use)
597           {
598               thread_table[i].stack = 0;
599               thread_table[i].in_use = FALSE;
600               CloseHandle(thread_table[i].handle);
601               BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));
602           }
603       }
604       UNLOCK();
605
606       GC_deinit();
607       DeleteCriticalSection(&GC_allocate_ml);
608     }
609     break;
610
611   }
612   return TRUE;
613 }
614
615 # endif /* !MSWINCE */
616
617 #endif /* WIN32_THREADS */