OSDN Git Service

38de099fa87cc677351945ec35066d9442a57e36
[pf3gnuchains/gcc-fork.git] / boehm-gc / win32_threads.c
1 #if defined(GC_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 #if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))
335
336 HANDLE WINAPI GC_CreateThread(
337     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
338     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
339     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
340 {
341     return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
342                         lpParameter, dwCreationFlags, lpThreadId);
343 }
344
345 #else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
346
347 typedef struct {
348     HANDLE child_ready_h, parent_ready_h;
349     volatile struct thread_entry * entry;
350     LPTHREAD_START_ROUTINE start;
351     LPVOID param;
352 } thread_args;
353
354 DWORD WINAPI thread_start(LPVOID arg);
355
356 HANDLE WINAPI GC_CreateThread(
357     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
358     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
359     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
360 {
361     HANDLE thread_h = NULL;
362     HANDLE child_ready_h, parent_ready_h;
363
364     int i;
365     thread_args args;
366
367     /* allocate thread slot */
368     LOCK();
369     for (i = 0; i != MAX_THREADS && thread_table[i].in_use; i++)
370         ;
371     if (i != MAX_THREADS) {
372         thread_table[i].in_use = TRUE;
373     }
374     UNLOCK();
375
376     if (i != MAX_THREADS) {
377
378         /* create unnamed unsignalled events */
379         if (child_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
380             if (parent_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
381
382                 /* set up thread arguments */
383                 args.child_ready_h = child_ready_h;
384                 args.parent_ready_h = parent_ready_h;
385                 args.entry = &thread_table[i];
386                 args.start = lpStartAddress;
387                 args.param = lpParameter;
388
389                 thread_h = CreateThread(lpThreadAttributes,
390                                         dwStackSize, thread_start,
391                                         &args,
392                                         dwCreationFlags & ~CREATE_SUSPENDED,
393                                         lpThreadId);
394
395                 if (thread_h) {
396
397                     /* fill in ID and handle; tell child this is done */
398                     thread_table[i].id = *lpThreadId;
399                     thread_table[i].handle = thread_h;
400                     SetEvent (parent_ready_h);
401
402                     /* wait for child to fill in stack and copy args */
403                     WaitForSingleObject (child_ready_h, INFINITE);
404
405                     /* suspend the child if requested */
406                     if (dwCreationFlags & CREATE_SUSPENDED)
407                         SuspendThread (thread_h);
408
409                     /* let child call given function now (or when resumed) */
410                     SetEvent (parent_ready_h);
411
412                 } else {
413                     CloseHandle (parent_ready_h);
414                 }
415             }
416         }
417
418         CloseHandle (child_ready_h);
419
420         if (thread_h == NULL)
421             thread_table[i].in_use = FALSE;
422
423     } else { /* no thread slot found */
424         SetLastError (ERROR_TOO_MANY_TCBS);
425     }
426
427     return thread_h;
428 }
429
430 static DWORD WINAPI thread_start(LPVOID arg)
431 {
432     DWORD ret = 0;
433     thread_args args = *(thread_args *)arg;
434
435     /* wait for parent to fill in ID and handle */
436     WaitForSingleObject (args.parent_ready_h, INFINITE);
437     ResetEvent (args.parent_ready_h);
438
439     /* fill in stack; tell parent this is done */
440     args.entry->stack = GC_get_stack_base();
441     SetEvent (args.child_ready_h);
442
443     /* wait for parent to tell us to go (in case it needs to suspend us) */
444     WaitForSingleObject (args.parent_ready_h, INFINITE);
445     CloseHandle (args.parent_ready_h);
446
447     /* Clear the thread entry even if we exit with an exception.        */
448     /* This is probably pointless, since an uncaught exception is       */
449     /* supposed to result in the process being killed.                  */
450 #ifndef __GNUC__
451     __try {
452 #endif /* __GNUC__ */
453         ret = args.start (args.param);
454 #ifndef __GNUC__
455     } __finally {
456 #endif /* __GNUC__ */
457         LOCK();
458         args.entry->stack = 0;
459         args.entry->in_use = FALSE;
460               /* cast away volatile qualifier */
461         BZERO((void *) &args.entry->context, sizeof(CONTEXT));
462         UNLOCK();
463 #ifndef __GNUC__
464     }
465 #endif /* __GNUC__ */
466
467     return ret;
468 }
469 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
470
471 #ifdef MSWINCE
472
473 typedef struct {
474     HINSTANCE hInstance;
475     HINSTANCE hPrevInstance;
476     LPWSTR lpCmdLine;
477     int nShowCmd;
478 } main_thread_args;
479
480 DWORD WINAPI main_thread_start(LPVOID arg);
481
482 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
483                    LPWSTR lpCmdLine, int nShowCmd)
484 {
485     DWORD exit_code = 1;
486
487     main_thread_args args = {
488         hInstance, hPrevInstance, lpCmdLine, nShowCmd
489     };
490     HANDLE thread_h;
491     DWORD thread_id;
492
493     /* initialize everything */
494     InitializeCriticalSection(&GC_allocate_ml);
495     GC_init();
496
497     /* start the main thread */
498     thread_h = GC_CreateThread(
499         NULL, 0, main_thread_start, &args, 0, &thread_id);
500
501     if (thread_h != NULL)
502     {
503         WaitForSingleObject (thread_h, INFINITE);
504         GetExitCodeThread (thread_h, &exit_code);
505         CloseHandle (thread_h);
506     }
507
508     GC_deinit();
509     DeleteCriticalSection(&GC_allocate_ml);
510
511     return (int) exit_code;
512 }
513
514 DWORD WINAPI main_thread_start(LPVOID arg)
515 {
516     main_thread_args * args = (main_thread_args *) arg;
517
518     return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance,
519                                args->lpCmdLine, args->nShowCmd);
520 }
521
522 # else /* !MSWINCE */
523
524 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
525
526 /*
527  * This isn't generally safe, since DllMain is not premptible.
528  * If another thread holds the lock while this runs we're in trouble.
529  * Pontus Rydin suggests wrapping the thread start routine instead.
530  */
531 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
532 {
533   switch (reason) {
534   case DLL_PROCESS_ATTACH:
535     InitializeCriticalSection(&GC_allocate_ml);
536     GC_init();  /* Force initialization before thread attach.   */
537     /* fall through */
538   case DLL_THREAD_ATTACH:
539     {
540       int i;
541       /* It appears to be unsafe to acquire a lock here, since this     */
542       /* code is apparently not preeemptible on some systems.           */
543       /* (This is based on complaints, not on Microsoft's official      */
544       /* documentation, which says this should perform "only simple     */
545       /* inititalization tasks".)                                       */
546       /* Hence we make do with nonblocking synchronization.             */
547
548       /* The following should be a noop according to the win32  */
549       /* documentation.  There is empirical evidence that it    */
550       /* isn't.         - HB                                    */
551 #     ifdef MPROTECT_VDB
552        if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
553 #     endif
554
555       for (i = 0;
556                                /* cast away volatile qualifier */
557            InterlockedExchange((LPLONG) &thread_table[i].in_use, 1) != 0;
558            i++) {
559         /* Compare-and-swap would make this cleaner, but that's not     */
560         /* supported before Windows 98 and NT 4.0.  In Windows 2000,    */
561         /* InterlockedExchange is supposed to be replaced by            */
562         /* InterlockedExchangePointer, but that's not really what I     */
563         /* want here.                                                   */
564         if (i == MAX_THREADS - 1)
565           ABORT("too many threads");
566       }
567       thread_table[i].id = GetCurrentThreadId();
568       if (!DuplicateHandle(GetCurrentProcess(),
569                            GetCurrentThread(),
570                            GetCurrentProcess(),
571                            /* cast away volatile qualifier */
572                            (HANDLE *) &thread_table[i].handle,
573                            0,
574                            0,
575                            DUPLICATE_SAME_ACCESS)) {
576         DWORD last_error = GetLastError();
577         GC_printf1("Last error code: %lx\n", last_error);
578         ABORT("DuplicateHandle failed");
579       }
580       thread_table[i].stack = GC_get_stack_base();
581       /* If this thread is being created while we are trying to stop    */
582       /* the world, wait here.  Hopefully this can't happen on any      */
583       /* systems that don't allow us to block here.                     */
584       while (GC_please_stop) Sleep(20);
585     }
586     break;
587   case DLL_THREAD_DETACH:
588     {
589       int i;
590       DWORD thread_id = GetCurrentThreadId();
591       LOCK();
592       for (i = 0;
593            i < MAX_THREADS &&
594            (thread_table[i].stack == 0 || thread_table[i].id != thread_id);
595            i++) {}
596       if (i >= MAX_THREADS) {
597           WARN("thread %ld not found on detach", (GC_word)thread_id);
598       } else {
599           thread_table[i].stack = 0;
600           thread_table[i].in_use = FALSE;
601           CloseHandle(thread_table[i].handle);
602             /* cast away volatile qualifier */
603           BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));
604       }
605       UNLOCK();
606     }
607     break;
608   case DLL_PROCESS_DETACH:
609     {
610       int i;
611
612       LOCK();
613       for (i = 0; i < MAX_THREADS; ++i)
614       {
615           if (thread_table[i].in_use)
616           {
617               thread_table[i].stack = 0;
618               thread_table[i].in_use = FALSE;
619               CloseHandle(thread_table[i].handle);
620               BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));
621           }
622       }
623       UNLOCK();
624
625       GC_deinit();
626       DeleteCriticalSection(&GC_allocate_ml);
627     }
628     break;
629
630   }
631   return TRUE;
632 }
633
634 # endif /* !MSWINCE */
635
636 #endif /* GC_WIN32_THREADS */