OSDN Git Service

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