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 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
);
150 GC_push_all_stack((char *) thread_table
[i
].context
.Esp
,
151 thread_table
[i
].stack
);
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
);
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
);
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
);
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
);
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
);
305 --> architecture
not supported
316 void GC_get_next_stack(char *start
, char **lo
, char **hi
)
319 # define ADDR_LIMIT (char *)(-1L)
320 char * current_min
= ADDR_LIMIT
;
322 for (i
= 0; i
< MAX_THREADS
; i
++) {
323 char * s
= (char *)thread_table
[i
].stack
;
325 if (0 != s
&& s
> start
&& s
< current_min
) {
330 if (current_min
== ADDR_LIMIT
) {
334 *lo
= GC_get_lo_stack_addr(current_min
);
335 if (*lo
< start
) *lo
= start
;
338 #if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))
340 HANDLE WINAPI
GC_CreateThread(
341 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
342 DWORD dwStackSize
, LPTHREAD_START_ROUTINE lpStartAddress
,
343 LPVOID lpParameter
, DWORD dwCreationFlags
, LPDWORD lpThreadId
)
345 return CreateThread(lpThreadAttributes
, dwStackSize
, lpStartAddress
,
346 lpParameter
, dwCreationFlags
, lpThreadId
);
349 #else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
352 HANDLE child_ready_h
, parent_ready_h
;
353 volatile struct thread_entry
* entry
;
354 LPTHREAD_START_ROUTINE start
;
358 DWORD WINAPI
thread_start(LPVOID arg
);
360 HANDLE WINAPI
GC_CreateThread(
361 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
362 DWORD dwStackSize
, LPTHREAD_START_ROUTINE lpStartAddress
,
363 LPVOID lpParameter
, DWORD dwCreationFlags
, LPDWORD lpThreadId
)
365 HANDLE thread_h
= NULL
;
366 HANDLE child_ready_h
, parent_ready_h
;
371 /* allocate thread slot */
373 for (i
= 0; i
!= MAX_THREADS
&& thread_table
[i
].in_use
; i
++)
375 if (i
!= MAX_THREADS
) {
376 thread_table
[i
].in_use
= TRUE
;
380 if (i
!= MAX_THREADS
) {
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
)) {
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
;
393 thread_h
= CreateThread(lpThreadAttributes
,
394 dwStackSize
, thread_start
,
396 dwCreationFlags
& ~CREATE_SUSPENDED
,
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
);
406 /* wait for child to fill in stack and copy args */
407 WaitForSingleObject (child_ready_h
, INFINITE
);
409 /* suspend the child if requested */
410 if (dwCreationFlags
& CREATE_SUSPENDED
)
411 SuspendThread (thread_h
);
413 /* let child call given function now (or when resumed) */
414 SetEvent (parent_ready_h
);
417 CloseHandle (parent_ready_h
);
422 CloseHandle (child_ready_h
);
424 if (thread_h
== NULL
)
425 thread_table
[i
].in_use
= FALSE
;
427 } else { /* no thread slot found */
428 SetLastError (ERROR_TOO_MANY_TCBS
);
434 static DWORD WINAPI
thread_start(LPVOID arg
)
437 thread_args args
= *(thread_args
*)arg
;
439 /* wait for parent to fill in ID and handle */
440 WaitForSingleObject (args
.parent_ready_h
, INFINITE
);
441 ResetEvent (args
.parent_ready_h
);
443 /* fill in stack; tell parent this is done */
444 args
.entry
->stack
= GC_get_stack_base();
445 SetEvent (args
.child_ready_h
);
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
);
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. */
456 #endif /* __GNUC__ */
457 ret
= args
.start (args
.param
);
460 #endif /* __GNUC__ */
462 args
.entry
->stack
= 0;
463 args
.entry
->in_use
= FALSE
;
464 /* cast away volatile qualifier */
465 BZERO((void *) &args
.entry
->context
, sizeof(CONTEXT
));
469 #endif /* __GNUC__ */
473 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
479 HINSTANCE hPrevInstance
;
484 DWORD WINAPI
main_thread_start(LPVOID arg
);
486 int WINAPI
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
,
487 LPWSTR lpCmdLine
, int nShowCmd
)
491 main_thread_args args
= {
492 hInstance
, hPrevInstance
, lpCmdLine
, nShowCmd
497 /* initialize everything */
498 InitializeCriticalSection(&GC_allocate_ml
);
501 /* start the main thread */
502 thread_h
= GC_CreateThread(
503 NULL
, 0, main_thread_start
, &args
, 0, &thread_id
);
505 if (thread_h
!= NULL
)
507 WaitForSingleObject (thread_h
, INFINITE
);
508 GetExitCodeThread (thread_h
, &exit_code
);
509 CloseHandle (thread_h
);
513 DeleteCriticalSection(&GC_allocate_ml
);
515 return (int) exit_code
;
518 DWORD WINAPI
main_thread_start(LPVOID arg
)
520 main_thread_args
* args
= (main_thread_args
*) arg
;
522 return (DWORD
) GC_WinMain (args
->hInstance
, args
->hPrevInstance
,
523 args
->lpCmdLine
, args
->nShowCmd
);
526 # else /* !MSWINCE */
528 LONG WINAPI
GC_write_fault_handler(struct _EXCEPTION_POINTERS
*exc_info
);
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.
537 BOOL WINAPI
DllMain(HINSTANCE inst
, ULONG reason
, LPVOID reserved
)
540 case DLL_PROCESS_ATTACH
:
541 InitializeCriticalSection(&GC_allocate_ml
);
542 GC_init(); /* Force initialization before thread attach. */
544 case DLL_THREAD_ATTACH
:
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. */
554 /* The following should be a noop according to the win32 */
555 /* documentation. There is empirical evidence that it */
558 if (GC_incremental
) SetUnhandledExceptionFilter(GC_write_fault_handler
);
562 /* cast away volatile qualifier */
563 InterlockedExchange((LPLONG
) &thread_table
[i
].in_use
, 1) != 0;
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 */
570 if (i
== MAX_THREADS
- 1)
571 ABORT("too many threads");
573 thread_table
[i
].id
= GetCurrentThreadId();
574 if (!DuplicateHandle(GetCurrentProcess(),
577 /* cast away volatile qualifier */
578 (HANDLE
*) &thread_table
[i
].handle
,
581 DUPLICATE_SAME_ACCESS
)) {
582 DWORD last_error
= GetLastError();
583 GC_printf1("Last error code: %lx\n", last_error
);
584 ABORT("DuplicateHandle failed");
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);
593 case DLL_THREAD_DETACH
:
596 DWORD thread_id
= GetCurrentThreadId();
600 (thread_table
[i
].stack
== 0 || thread_table
[i
].id
!= thread_id
);
602 if (i
>= MAX_THREADS
) {
603 WARN("thread %ld not found on detach", (GC_word
)thread_id
);
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
));
614 case DLL_PROCESS_DETACH
:
619 for (i
= 0; i
< MAX_THREADS
; ++i
)
621 if (thread_table
[i
].in_use
)
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
));
632 DeleteCriticalSection(&GC_allocate_ml
);
642 # endif /* !MSWINCE */
644 #endif /* GC_WIN32_THREADS */