1 #if defined(GC_WIN32_THREADS)
3 #include "private/gc_priv.h"
10 #define MAX_THREADS 64
16 void *stack
; /* The cold end of the stack. */
17 /* 0 ==> entry not valid. */
18 /* !in_use ==> stack == 0 */
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. */
36 DWORD thread_id
= GetCurrentThreadId();
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
) {
44 /* SuspendThread will fail if thread is running kernel code */
45 while (SuspendThread(thread_table
[i
].handle
) == (DWORD
)-1)
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. */
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
));
62 if (SuspendThread(thread_table
[i
].handle
) == (DWORD
)-1)
63 ABORT("SuspendThread failed");
65 thread_table
[i
].suspended
= TRUE
;
71 DWORD thread_id
= GetCurrentThreadId();
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
;
84 # pragma warning(disable:4715)
86 ptr_t
GC_current_stackbottom()
88 DWORD thread_id
= GetCurrentThreadId();
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");
96 # pragma warning(default:4715)
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))
106 static ptr_t
GC_get_lo_stack_addr(ptr_t s
)
109 MEMORY_BASIC_INFORMATION info
;
110 VirtualQuery(s
, &info
, sizeof(info
));
112 bottom
= info
.BaseAddress
;
113 VirtualQuery(bottom
- 1, &info
, sizeof(info
));
114 } while ((info
.Protect
& PAGE_READWRITE
)
115 && !(info
.Protect
& PAGE_GUARD
));
120 void GC_push_all_stacks()
122 DWORD thread_id
= GetCurrentThreadId();
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
);
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");
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
);
153 GC_push_all_stack((char *) thread_table
[i
].context
.Esp
,
154 thread_table
[i
].stack
);
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
);
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
);
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
);
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
);
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
);
308 --> architecture
not supported
319 void GC_get_next_stack(char *start
, char **lo
, char **hi
)
322 # define ADDR_LIMIT (char *)(-1L)
323 char * current_min
= ADDR_LIMIT
;
325 for (i
= 0; i
< MAX_THREADS
; i
++) {
326 char * s
= (char *)thread_table
[i
].stack
;
328 if (0 != s
&& s
> start
&& s
< current_min
) {
333 if (current_min
== ADDR_LIMIT
) {
337 *lo
= GC_get_lo_stack_addr(current_min
);
338 if (*lo
< start
) *lo
= start
;
341 #if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))
343 HANDLE WINAPI
GC_CreateThread(
344 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
345 DWORD dwStackSize
, LPTHREAD_START_ROUTINE lpStartAddress
,
346 LPVOID lpParameter
, DWORD dwCreationFlags
, LPDWORD lpThreadId
)
348 return CreateThread(lpThreadAttributes
, dwStackSize
, lpStartAddress
,
349 lpParameter
, dwCreationFlags
, lpThreadId
);
352 #else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
355 HANDLE child_ready_h
, parent_ready_h
;
356 volatile struct thread_entry
* entry
;
357 LPTHREAD_START_ROUTINE start
;
361 DWORD WINAPI
thread_start(LPVOID arg
);
363 HANDLE WINAPI
GC_CreateThread(
364 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
365 DWORD dwStackSize
, LPTHREAD_START_ROUTINE lpStartAddress
,
366 LPVOID lpParameter
, DWORD dwCreationFlags
, LPDWORD lpThreadId
)
368 HANDLE thread_h
= NULL
;
369 HANDLE child_ready_h
, parent_ready_h
;
374 /* allocate thread slot */
376 for (i
= 0; i
!= MAX_THREADS
&& thread_table
[i
].in_use
; i
++)
378 if (i
!= MAX_THREADS
) {
379 thread_table
[i
].in_use
= TRUE
;
383 if (i
!= MAX_THREADS
) {
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
)) {
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
;
396 thread_h
= CreateThread(lpThreadAttributes
,
397 dwStackSize
, thread_start
,
399 dwCreationFlags
& ~CREATE_SUSPENDED
,
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
);
409 /* wait for child to fill in stack and copy args */
410 WaitForSingleObject (child_ready_h
, INFINITE
);
412 /* suspend the child if requested */
413 if (dwCreationFlags
& CREATE_SUSPENDED
)
414 SuspendThread (thread_h
);
416 /* let child call given function now (or when resumed) */
417 SetEvent (parent_ready_h
);
420 CloseHandle (parent_ready_h
);
425 CloseHandle (child_ready_h
);
427 if (thread_h
== NULL
)
428 thread_table
[i
].in_use
= FALSE
;
430 } else { /* no thread slot found */
431 SetLastError (ERROR_TOO_MANY_TCBS
);
437 static DWORD WINAPI
thread_start(LPVOID arg
)
440 thread_args args
= *(thread_args
*)arg
;
442 /* wait for parent to fill in ID and handle */
443 WaitForSingleObject (args
.parent_ready_h
, INFINITE
);
444 ResetEvent (args
.parent_ready_h
);
446 /* fill in stack; tell parent this is done */
447 args
.entry
->stack
= GC_get_stack_base();
448 SetEvent (args
.child_ready_h
);
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
);
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. */
459 #endif /* __GNUC__ */
460 ret
= args
.start (args
.param
);
463 #endif /* __GNUC__ */
465 args
.entry
->stack
= 0;
466 args
.entry
->in_use
= FALSE
;
467 /* cast away volatile qualifier */
468 BZERO((void *) &args
.entry
->context
, sizeof(CONTEXT
));
472 #endif /* __GNUC__ */
476 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
482 HINSTANCE hPrevInstance
;
487 DWORD WINAPI
main_thread_start(LPVOID arg
);
489 int WINAPI
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
,
490 LPWSTR lpCmdLine
, int nShowCmd
)
494 main_thread_args args
= {
495 hInstance
, hPrevInstance
, lpCmdLine
, nShowCmd
500 /* initialize everything */
501 InitializeCriticalSection(&GC_allocate_ml
);
504 /* start the main thread */
505 thread_h
= GC_CreateThread(
506 NULL
, 0, main_thread_start
, &args
, 0, &thread_id
);
508 if (thread_h
!= NULL
)
510 WaitForSingleObject (thread_h
, INFINITE
);
511 GetExitCodeThread (thread_h
, &exit_code
);
512 CloseHandle (thread_h
);
516 DeleteCriticalSection(&GC_allocate_ml
);
518 return (int) exit_code
;
521 DWORD WINAPI
main_thread_start(LPVOID arg
)
523 main_thread_args
* args
= (main_thread_args
*) arg
;
525 return (DWORD
) GC_WinMain (args
->hInstance
, args
->hPrevInstance
,
526 args
->lpCmdLine
, args
->nShowCmd
);
529 # else /* !MSWINCE */
531 LONG WINAPI
GC_write_fault_handler(struct _EXCEPTION_POINTERS
*exc_info
);
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.
538 BOOL WINAPI
DllMain(HINSTANCE inst
, ULONG reason
, LPVOID reserved
)
541 case DLL_PROCESS_ATTACH
:
542 InitializeCriticalSection(&GC_allocate_ml
);
543 GC_init(); /* Force initialization before thread attach. */
545 case DLL_THREAD_ATTACH
:
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. */
555 /* The following should be a noop according to the win32 */
556 /* documentation. There is empirical evidence that it */
559 if (GC_incremental
) SetUnhandledExceptionFilter(GC_write_fault_handler
);
563 /* cast away volatile qualifier */
564 InterlockedExchange((LPLONG
) &thread_table
[i
].in_use
, 1) != 0;
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 */
571 if (i
== MAX_THREADS
- 1)
572 ABORT("too many threads");
574 thread_table
[i
].id
= GetCurrentThreadId();
575 if (!DuplicateHandle(GetCurrentProcess(),
578 /* cast away volatile qualifier */
579 (HANDLE
*) &thread_table
[i
].handle
,
582 DUPLICATE_SAME_ACCESS
)) {
583 DWORD last_error
= GetLastError();
584 GC_printf1("Last error code: %lx\n", last_error
);
585 ABORT("DuplicateHandle failed");
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);
594 case DLL_THREAD_DETACH
:
597 DWORD thread_id
= GetCurrentThreadId();
601 (thread_table
[i
].stack
== 0 || thread_table
[i
].id
!= thread_id
);
603 if (i
>= MAX_THREADS
) {
604 WARN("thread %ld not found on detach", (GC_word
)thread_id
);
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
));
615 case DLL_PROCESS_DETACH
:
620 for (i
= 0; i
< MAX_THREADS
; ++i
)
622 if (thread_table
[i
].in_use
)
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
));
633 DeleteCriticalSection(&GC_allocate_ml
);
641 # endif /* !MSWINCE */
643 #endif /* GC_WIN32_THREADS */