OSDN Git Service

2003-04-28 Mohan Embar <gnustuff@thisiscool.com>
[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           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);
152           }
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 */
312       }
313     }
314 }
315
316 void GC_get_next_stack(char *start, char **lo, char **hi)
317 {
318     int i;
319 #   define ADDR_LIMIT (char *)(-1L)
320     char * current_min = ADDR_LIMIT;
321
322     for (i = 0; i < MAX_THREADS; i++) {
323         char * s = (char *)thread_table[i].stack;
324
325         if (0 != s && s > start && s < current_min) {
326             current_min = s;
327         }
328     }
329     *hi = current_min;
330     if (current_min == ADDR_LIMIT) {
331         *lo = ADDR_LIMIT;
332         return;
333     }
334     *lo = GC_get_lo_stack_addr(current_min);
335     if (*lo < start) *lo = start;
336 }
337
338 #if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))
339
340 HANDLE WINAPI GC_CreateThread(
341     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
342     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
343     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
344 {
345     return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
346                         lpParameter, dwCreationFlags, lpThreadId);
347 }
348
349 #else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
350
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;
357
358 DWORD WINAPI thread_start(LPVOID arg);
359
360 HANDLE WINAPI GC_CreateThread(
361     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
362     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
363     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
364 {
365     HANDLE thread_h = NULL;
366     HANDLE child_ready_h, parent_ready_h;
367
368     int i;
369     thread_args args;
370
371     /* allocate thread slot */
372     LOCK();
373     for (i = 0; i != MAX_THREADS && thread_table[i].in_use; i++)
374         ;
375     if (i != MAX_THREADS) {
376         thread_table[i].in_use = TRUE;
377     }
378     UNLOCK();
379
380     if (i != MAX_THREADS) {
381
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)) {
385
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;
392
393                 thread_h = CreateThread(lpThreadAttributes,
394                                         dwStackSize, thread_start,
395                                         &args,
396                                         dwCreationFlags & ~CREATE_SUSPENDED,
397                                         lpThreadId);
398
399                 if (thread_h) {
400
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);
405
406                     /* wait for child to fill in stack and copy args */
407                     WaitForSingleObject (child_ready_h, INFINITE);
408
409                     /* suspend the child if requested */
410                     if (dwCreationFlags & CREATE_SUSPENDED)
411                         SuspendThread (thread_h);
412
413                     /* let child call given function now (or when resumed) */
414                     SetEvent (parent_ready_h);
415
416                 } else {
417                     CloseHandle (parent_ready_h);
418                 }
419             }
420         }
421
422         CloseHandle (child_ready_h);
423
424         if (thread_h == NULL)
425             thread_table[i].in_use = FALSE;
426
427     } else { /* no thread slot found */
428         SetLastError (ERROR_TOO_MANY_TCBS);
429     }
430
431     return thread_h;
432 }
433
434 static DWORD WINAPI thread_start(LPVOID arg)
435 {
436     DWORD ret = 0;
437     thread_args args = *(thread_args *)arg;
438
439     /* wait for parent to fill in ID and handle */
440     WaitForSingleObject (args.parent_ready_h, INFINITE);
441     ResetEvent (args.parent_ready_h);
442
443     /* fill in stack; tell parent this is done */
444     args.entry->stack = GC_get_stack_base();
445     SetEvent (args.child_ready_h);
446
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);
450
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__
468     }
469 #endif /* __GNUC__ */
470
471     return ret;
472 }
473 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
474
475 #ifdef MSWINCE
476
477 typedef struct {
478     HINSTANCE hInstance;
479     HINSTANCE hPrevInstance;
480     LPWSTR lpCmdLine;
481     int nShowCmd;
482 } main_thread_args;
483
484 DWORD WINAPI main_thread_start(LPVOID arg);
485
486 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
487                    LPWSTR lpCmdLine, int nShowCmd)
488 {
489     DWORD exit_code = 1;
490
491     main_thread_args args = {
492         hInstance, hPrevInstance, lpCmdLine, nShowCmd
493     };
494     HANDLE thread_h;
495     DWORD thread_id;
496
497     /* initialize everything */
498     InitializeCriticalSection(&GC_allocate_ml);
499     GC_init();
500
501     /* start the main thread */
502     thread_h = GC_CreateThread(
503         NULL, 0, main_thread_start, &args, 0, &thread_id);
504
505     if (thread_h != NULL)
506     {
507         WaitForSingleObject (thread_h, INFINITE);
508         GetExitCodeThread (thread_h, &exit_code);
509         CloseHandle (thread_h);
510     }
511
512     GC_deinit();
513     DeleteCriticalSection(&GC_allocate_ml);
514
515     return (int) exit_code;
516 }
517
518 DWORD WINAPI main_thread_start(LPVOID arg)
519 {
520     main_thread_args * args = (main_thread_args *) arg;
521
522     return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance,
523                                args->lpCmdLine, args->nShowCmd);
524 }
525
526 # else /* !MSWINCE */
527
528 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
529
530 #ifdef GC_DLL
531
532 /*
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.
536  */
537 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
538 {
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:
545     {
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.             */
553
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
560
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");
572       }
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,
579                            0,
580                            0,
581                            DUPLICATE_SAME_ACCESS)) {
582         DWORD last_error = GetLastError();
583         GC_printf1("Last error code: %lx\n", last_error);
584         ABORT("DuplicateHandle failed");
585       }
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);
591     }
592     break;
593   case DLL_THREAD_DETACH:
594     {
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));
610       }
611       UNLOCK();
612     }
613     break;
614   case DLL_PROCESS_DETACH:
615     {
616       int i;
617
618       LOCK();
619       for (i = 0; i < MAX_THREADS; ++i)
620       {
621           if (thread_table[i].in_use)
622           {
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));
627           }
628       }
629       UNLOCK();
630
631       GC_deinit();
632       DeleteCriticalSection(&GC_allocate_ml);
633     }
634     break;
635
636   }
637   return TRUE;
638 }
639
640 #   endif /* GC_DLL */
641
642 # endif /* !MSWINCE */
643
644 #endif /* GC_WIN32_THREADS */