* langhooks.c: Fix formatting.
[official-gcc.git] / boehm-gc / win32_threads.c
blob38de099fa87cc677351945ec35066d9442a57e36
1 #if defined(GC_WIN32_THREADS)
3 #include "private/gc_priv.h"
5 #if 0
6 #define STRICT
7 #include <windows.h>
8 #endif
10 #define MAX_THREADS 64
12 struct thread_entry {
13 LONG in_use;
14 DWORD id;
15 HANDLE handle;
16 void *stack; /* The cold end of the stack. */
17 /* 0 ==> entry not valid. */
18 /* !in_use ==> stack == 0 */
19 CONTEXT context;
20 GC_bool suspended;
23 volatile GC_bool GC_please_stop = FALSE;
25 volatile struct thread_entry thread_table[MAX_THREADS];
27 void GC_push_thread_structures GC_PROTO((void))
29 /* Unlike the other threads implementations, the thread table here */
30 /* contains no pointers to the collectable heap. Thus we have */
31 /* no private structures we need to preserve. */
34 void GC_stop_world()
36 DWORD thread_id = GetCurrentThreadId();
37 int i;
39 GC_please_stop = TRUE;
40 for (i = 0; i < MAX_THREADS; i++)
41 if (thread_table[i].stack != 0
42 && thread_table[i].id != thread_id) {
43 # ifdef MSWINCE
44 /* SuspendThread will fail if thread is running kernel code */
45 while (SuspendThread(thread_table[i].handle) == (DWORD)-1)
46 Sleep(10);
47 # else
48 /* Apparently the Windows 95 GetOpenFileName call creates */
49 /* a thread that does not properly get cleaned up, and */
50 /* SuspendThread on its descriptor may provoke a crash. */
51 /* This reduces the probability of that event, though it still */
52 /* appears there's a race here. */
53 DWORD exitCode;
54 if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&
55 exitCode != STILL_ACTIVE) {
56 thread_table[i].stack = 0;
57 thread_table[i].in_use = FALSE;
58 CloseHandle(thread_table[i].handle);
59 BZERO((void *)(&thread_table[i].context), sizeof(CONTEXT));
60 continue;
62 if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
63 ABORT("SuspendThread failed");
64 # endif
65 thread_table[i].suspended = TRUE;
69 void GC_start_world()
71 DWORD thread_id = GetCurrentThreadId();
72 int i;
73 for (i = 0; i < MAX_THREADS; i++)
74 if (thread_table[i].stack != 0 && thread_table[i].suspended
75 && thread_table[i].id != thread_id) {
76 if (ResumeThread(thread_table[i].handle) == (DWORD)-1)
77 ABORT("ResumeThread failed");
78 thread_table[i].suspended = FALSE;
80 GC_please_stop = FALSE;
83 # ifdef _MSC_VER
84 # pragma warning(disable:4715)
85 # endif
86 ptr_t GC_current_stackbottom()
88 DWORD thread_id = GetCurrentThreadId();
89 int i;
90 for (i = 0; i < MAX_THREADS; i++)
91 if (thread_table[i].stack && thread_table[i].id == thread_id)
92 return thread_table[i].stack;
93 ABORT("no thread table entry for current thread");
95 # ifdef _MSC_VER
96 # pragma warning(default:4715)
97 # endif
99 # ifdef MSWINCE
100 /* The VirtualQuery calls below won't work properly on WinCE, but */
101 /* since each stack is restricted to an aligned 64K region of */
102 /* virtual memory we can just take the next lowest multiple of 64K. */
103 # define GC_get_lo_stack_addr(s) \
104 ((ptr_t)(((DWORD)(s) - 1) & 0xFFFF0000))
105 # else
106 static ptr_t GC_get_lo_stack_addr(ptr_t s)
108 ptr_t bottom;
109 MEMORY_BASIC_INFORMATION info;
110 VirtualQuery(s, &info, sizeof(info));
111 do {
112 bottom = info.BaseAddress;
113 VirtualQuery(bottom - 1, &info, sizeof(info));
114 } while ((info.Protect & PAGE_READWRITE)
115 && !(info.Protect & PAGE_GUARD));
116 return(bottom);
118 # endif
120 void GC_push_all_stacks()
122 DWORD thread_id = GetCurrentThreadId();
123 int i;
124 for (i = 0; i < MAX_THREADS; i++)
125 if (thread_table[i].stack) {
126 ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack);
127 if (thread_table[i].id == thread_id)
128 GC_push_all_stack((ptr_t)&i, thread_table[i].stack);
129 else {
130 thread_table[i].context.ContextFlags
131 = (CONTEXT_INTEGER|CONTEXT_CONTROL);
132 if (!GetThreadContext(thread_table[i].handle,
133 /* cast away volatile qualifier */
134 (LPCONTEXT)&thread_table[i].context))
135 ABORT("GetThreadContext failed");
136 # ifdef I386
137 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 */
312 void GC_get_next_stack(char *start, char **lo, char **hi)
314 int i;
315 # define ADDR_LIMIT (char *)(-1L)
316 char * current_min = ADDR_LIMIT;
318 for (i = 0; i < MAX_THREADS; i++) {
319 char * s = (char *)thread_table[i].stack;
321 if (0 != s && s > start && s < current_min) {
322 current_min = s;
325 *hi = current_min;
326 if (current_min == ADDR_LIMIT) {
327 *lo = ADDR_LIMIT;
328 return;
330 *lo = GC_get_lo_stack_addr(current_min);
331 if (*lo < start) *lo = start;
334 #if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))
336 HANDLE WINAPI GC_CreateThread(
337 LPSECURITY_ATTRIBUTES lpThreadAttributes,
338 DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
339 LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
341 return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
342 lpParameter, dwCreationFlags, lpThreadId);
345 #else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
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;
354 DWORD WINAPI thread_start(LPVOID arg);
356 HANDLE WINAPI GC_CreateThread(
357 LPSECURITY_ATTRIBUTES lpThreadAttributes,
358 DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
359 LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
361 HANDLE thread_h = NULL;
362 HANDLE child_ready_h, parent_ready_h;
364 int i;
365 thread_args args;
367 /* allocate thread slot */
368 LOCK();
369 for (i = 0; i != MAX_THREADS && thread_table[i].in_use; i++)
371 if (i != MAX_THREADS) {
372 thread_table[i].in_use = TRUE;
374 UNLOCK();
376 if (i != MAX_THREADS) {
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)) {
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;
389 thread_h = CreateThread(lpThreadAttributes,
390 dwStackSize, thread_start,
391 &args,
392 dwCreationFlags & ~CREATE_SUSPENDED,
393 lpThreadId);
395 if (thread_h) {
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);
402 /* wait for child to fill in stack and copy args */
403 WaitForSingleObject (child_ready_h, INFINITE);
405 /* suspend the child if requested */
406 if (dwCreationFlags & CREATE_SUSPENDED)
407 SuspendThread (thread_h);
409 /* let child call given function now (or when resumed) */
410 SetEvent (parent_ready_h);
412 } else {
413 CloseHandle (parent_ready_h);
418 CloseHandle (child_ready_h);
420 if (thread_h == NULL)
421 thread_table[i].in_use = FALSE;
423 } else { /* no thread slot found */
424 SetLastError (ERROR_TOO_MANY_TCBS);
427 return thread_h;
430 static DWORD WINAPI thread_start(LPVOID arg)
432 DWORD ret = 0;
433 thread_args args = *(thread_args *)arg;
435 /* wait for parent to fill in ID and handle */
436 WaitForSingleObject (args.parent_ready_h, INFINITE);
437 ResetEvent (args.parent_ready_h);
439 /* fill in stack; tell parent this is done */
440 args.entry->stack = GC_get_stack_base();
441 SetEvent (args.child_ready_h);
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);
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__
465 #endif /* __GNUC__ */
467 return ret;
469 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
471 #ifdef MSWINCE
473 typedef struct {
474 HINSTANCE hInstance;
475 HINSTANCE hPrevInstance;
476 LPWSTR lpCmdLine;
477 int nShowCmd;
478 } main_thread_args;
480 DWORD WINAPI main_thread_start(LPVOID arg);
482 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
483 LPWSTR lpCmdLine, int nShowCmd)
485 DWORD exit_code = 1;
487 main_thread_args args = {
488 hInstance, hPrevInstance, lpCmdLine, nShowCmd
490 HANDLE thread_h;
491 DWORD thread_id;
493 /* initialize everything */
494 InitializeCriticalSection(&GC_allocate_ml);
495 GC_init();
497 /* start the main thread */
498 thread_h = GC_CreateThread(
499 NULL, 0, main_thread_start, &args, 0, &thread_id);
501 if (thread_h != NULL)
503 WaitForSingleObject (thread_h, INFINITE);
504 GetExitCodeThread (thread_h, &exit_code);
505 CloseHandle (thread_h);
508 GC_deinit();
509 DeleteCriticalSection(&GC_allocate_ml);
511 return (int) exit_code;
514 DWORD WINAPI main_thread_start(LPVOID arg)
516 main_thread_args * args = (main_thread_args *) arg;
518 return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance,
519 args->lpCmdLine, args->nShowCmd);
522 # else /* !MSWINCE */
524 LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
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.
531 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
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:
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. */
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
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");
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,
575 DUPLICATE_SAME_ACCESS)) {
576 DWORD last_error = GetLastError();
577 GC_printf1("Last error code: %lx\n", last_error);
578 ABORT("DuplicateHandle failed");
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);
586 break;
587 case DLL_THREAD_DETACH:
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));
605 UNLOCK();
607 break;
608 case DLL_PROCESS_DETACH:
610 int i;
612 LOCK();
613 for (i = 0; i < MAX_THREADS; ++i)
615 if (thread_table[i].in_use)
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));
623 UNLOCK();
625 GC_deinit();
626 DeleteCriticalSection(&GC_allocate_ml);
628 break;
631 return TRUE;
634 # endif /* !MSWINCE */
636 #endif /* GC_WIN32_THREADS */