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 GC_push_all_stack((char *) thread_table
[i
].context
.Esp
,
148 thread_table
[i
].stack
);
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
);
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
);
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
);
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
);
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
);
301 --> architecture
not supported
312 void GC_get_next_stack(char *start
, char **lo
, char **hi
)
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
) {
326 if (current_min
== ADDR_LIMIT
) {
330 *lo
= GC_get_lo_stack_addr(current_min
);
331 if (*lo
< start
) *lo
= start
;
338 HANDLE child_ready_h
, parent_ready_h
;
339 volatile struct thread_entry
* entry
;
340 LPTHREAD_START_ROUTINE start
;
344 DWORD WINAPI
thread_start(LPVOID arg
);
346 HANDLE WINAPI
GC_CreateThread(
347 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
348 DWORD dwStackSize
, LPTHREAD_START_ROUTINE lpStartAddress
,
349 LPVOID lpParameter
, DWORD dwCreationFlags
, LPDWORD lpThreadId
)
351 HANDLE thread_h
= NULL
;
352 HANDLE child_ready_h
, parent_ready_h
;
357 /* allocate thread slot */
359 for (i
= 0; i
!= MAX_THREADS
&& thread_table
[i
].in_use
; i
++)
361 if (i
!= MAX_THREADS
) {
362 thread_table
[i
].in_use
= TRUE
;
366 if (i
!= MAX_THREADS
) {
368 /* create unnamed unsignalled events */
369 if (child_ready_h
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
)) {
370 if (parent_ready_h
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
)) {
372 /* set up thread arguments */
373 args
.child_ready_h
= child_ready_h
;
374 args
.parent_ready_h
= parent_ready_h
;
375 args
.entry
= &thread_table
[i
];
376 args
.start
= lpStartAddress
;
377 args
.param
= lpParameter
;
379 thread_h
= CreateThread(lpThreadAttributes
,
380 dwStackSize
, thread_start
,
382 dwCreationFlags
& ~CREATE_SUSPENDED
,
387 /* fill in ID and handle; tell child this is done */
388 thread_table
[i
].id
= *lpThreadId
;
389 thread_table
[i
].handle
= thread_h
;
390 SetEvent (parent_ready_h
);
392 /* wait for child to fill in stack and copy args */
393 WaitForSingleObject (child_ready_h
, INFINITE
);
395 /* suspend the child if requested */
396 if (dwCreationFlags
& CREATE_SUSPENDED
)
397 SuspendThread (thread_h
);
399 /* let child call given function now (or when resumed) */
400 SetEvent (parent_ready_h
);
403 CloseHandle (parent_ready_h
);
408 CloseHandle (child_ready_h
);
410 if (thread_h
== NULL
)
411 thread_table
[i
].in_use
= FALSE
;
413 } else { /* no thread slot found */
414 SetLastError (ERROR_TOO_MANY_TCBS
);
420 static DWORD WINAPI
thread_start(LPVOID arg
)
423 thread_args args
= *(thread_args
*)arg
;
425 /* wait for parent to fill in ID and handle */
426 WaitForSingleObject (args
.parent_ready_h
, INFINITE
);
427 ResetEvent (args
.parent_ready_h
);
429 /* fill in stack; tell parent this is done */
430 args
.entry
->stack
= GC_get_stack_base();
431 SetEvent (args
.child_ready_h
);
433 /* wait for parent to tell us to go (in case it needs to suspend us) */
434 WaitForSingleObject (args
.parent_ready_h
, INFINITE
);
435 CloseHandle (args
.parent_ready_h
);
437 /* Clear the thread entry even if we exit with an exception. */
438 /* This is probably pointless, since an uncaught exception is */
439 /* supposed to result in the process being killed. */
441 ret
= args
.start (args
.param
);
444 args
.entry
->stack
= 0;
445 args
.entry
->in_use
= FALSE
;
446 /* cast away volatile qualifier */
447 BZERO((void *) &args
.entry
->context
, sizeof(CONTEXT
));
456 HINSTANCE hPrevInstance
;
461 DWORD WINAPI
main_thread_start(LPVOID arg
);
463 int WINAPI
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
,
464 LPWSTR lpCmdLine
, int nShowCmd
)
468 main_thread_args args
= {
469 hInstance
, hPrevInstance
, lpCmdLine
, nShowCmd
474 /* initialize everything */
475 InitializeCriticalSection(&GC_allocate_ml
);
478 /* start the main thread */
479 thread_h
= GC_CreateThread(
480 NULL
, 0, main_thread_start
, &args
, 0, &thread_id
);
482 if (thread_h
!= NULL
)
484 WaitForSingleObject (thread_h
, INFINITE
);
485 GetExitCodeThread (thread_h
, &exit_code
);
486 CloseHandle (thread_h
);
490 DeleteCriticalSection(&GC_allocate_ml
);
492 return (int) exit_code
;
495 DWORD WINAPI
main_thread_start(LPVOID arg
)
497 main_thread_args
* args
= (main_thread_args
*) arg
;
499 return (DWORD
) GC_WinMain (args
->hInstance
, args
->hPrevInstance
,
500 args
->lpCmdLine
, args
->nShowCmd
);
503 # else /* !MSWINCE */
505 LONG WINAPI
GC_write_fault_handler(struct _EXCEPTION_POINTERS
*exc_info
);
508 * This isn't generally safe, since DllMain is not premptible.
509 * If another thread holds the lock while this runs we're in trouble.
510 * Pontus Rydin suggests wrapping the thread start routine instead.
512 BOOL WINAPI
DllMain(HINSTANCE inst
, ULONG reason
, LPVOID reserved
)
515 case DLL_PROCESS_ATTACH
:
516 InitializeCriticalSection(&GC_allocate_ml
);
517 GC_init(); /* Force initialization before thread attach. */
519 case DLL_THREAD_ATTACH
:
522 /* It appears to be unsafe to acquire a lock here, since this */
523 /* code is apparently not preeemptible on some systems. */
524 /* (This is based on complaints, not on Microsoft's official */
525 /* documentation, which says this should perform "only simple */
526 /* inititalization tasks".) */
527 /* Hence we make do with nonblocking synchronization. */
529 /* The following should be a noop according to the win32 */
530 /* documentation. There is empirical evidence that it */
533 if (GC_incremental
) SetUnhandledExceptionFilter(GC_write_fault_handler
);
537 /* cast away volatile qualifier */
538 InterlockedExchange((LPLONG
) &thread_table
[i
].in_use
, 1) != 0;
540 /* Compare-and-swap would make this cleaner, but that's not */
541 /* supported before Windows 98 and NT 4.0. In Windows 2000, */
542 /* InterlockedExchange is supposed to be replaced by */
543 /* InterlockedExchangePointer, but that's not really what I */
545 if (i
== MAX_THREADS
- 1)
546 ABORT("too many threads");
548 thread_table
[i
].id
= GetCurrentThreadId();
549 if (!DuplicateHandle(GetCurrentProcess(),
552 /* cast away volatile qualifier */
553 (HANDLE
*) &thread_table
[i
].handle
,
556 DUPLICATE_SAME_ACCESS
)) {
557 DWORD last_error
= GetLastError();
558 GC_printf1("Last error code: %lx\n", last_error
);
559 ABORT("DuplicateHandle failed");
561 thread_table
[i
].stack
= GC_get_stack_base();
562 /* If this thread is being created while we are trying to stop */
563 /* the world, wait here. Hopefully this can't happen on any */
564 /* systems that don't allow us to block here. */
565 while (GC_please_stop
) Sleep(20);
568 case DLL_THREAD_DETACH
:
571 DWORD thread_id
= GetCurrentThreadId();
575 (thread_table
[i
].stack
== 0 || thread_table
[i
].id
!= thread_id
);
577 if (i
>= MAX_THREADS
) {
578 WARN("thread %ld not found on detach", (GC_word
)thread_id
);
580 thread_table
[i
].stack
= 0;
581 thread_table
[i
].in_use
= FALSE
;
582 CloseHandle(thread_table
[i
].handle
);
583 /* cast away volatile qualifier */
584 BZERO((void *) &thread_table
[i
].context
, sizeof(CONTEXT
));
589 case DLL_PROCESS_DETACH
:
594 for (i
= 0; i
< MAX_THREADS
; ++i
)
596 if (thread_table
[i
].in_use
)
598 thread_table
[i
].stack
= 0;
599 thread_table
[i
].in_use
= FALSE
;
600 CloseHandle(thread_table
[i
].handle
);
601 BZERO((void *) &thread_table
[i
].context
, sizeof(CONTEXT
));
607 DeleteCriticalSection(&GC_allocate_ml
);
615 # endif /* !MSWINCE */
617 #endif /* GC_WIN32_THREADS */