1 #if defined(GC_WIN32_THREADS)
3 #include "private/gc_priv.h"
9 /* Cygwin-specific forward decls */
10 # undef pthread_create
11 # undef pthread_sigmask
15 # define DEBUG_CYGWIN_THREADS 0
17 GC_bool GC_thr_initialized
= FALSE
;
18 void * GC_start_routine(void * arg
);
19 void GC_thread_exit_proc(void *arg
);
24 # define MAX_THREADS 64
31 void *stack
; /* The cold end of the stack. */
32 /* 0 ==> entry not valid. */
33 /* !in_use ==> stack == 0 */
38 void *status
; /* hold exit value until join in case it's a pointer */
44 volatile GC_bool GC_please_stop
= FALSE
;
46 volatile struct thread_entry thread_table
[MAX_THREADS
];
48 void GC_push_thread_structures
GC_PROTO((void))
50 /* Unlike the other threads implementations, the thread table here */
51 /* contains no pointers to the collectable heap. Thus we have */
52 /* no private structures we need to preserve. */
54 { int i
; /* pthreads may keep a pointer in the thread exit value */
55 for (i
= 0; i
< MAX_THREADS
; i
++)
56 if (thread_table
[i
].in_use
) GC_push_all((ptr_t
)&(thread_table
[i
].status
),(ptr_t
)(&(thread_table
[i
].status
)+1));
63 DWORD thread_id
= GetCurrentThreadId();
67 if (!GC_thr_initialized
) ABORT("GC_stop_world() called before GC_thr_init()");
70 GC_please_stop
= TRUE
;
71 for (i
= 0; i
< MAX_THREADS
; i
++)
72 if (thread_table
[i
].stack
!= 0
73 && thread_table
[i
].id
!= thread_id
) {
75 /* SuspendThread will fail if thread is running kernel code */
76 while (SuspendThread(thread_table
[i
].handle
) == (DWORD
)-1)
79 /* Apparently the Windows 95 GetOpenFileName call creates */
80 /* a thread that does not properly get cleaned up, and */
81 /* SuspendThread on its descriptor may provoke a crash. */
82 /* This reduces the probability of that event, though it still */
83 /* appears there's a race here. */
85 if (GetExitCodeThread(thread_table
[i
].handle
,&exitCode
) &&
86 exitCode
!= STILL_ACTIVE
) {
87 thread_table
[i
].stack
= 0; /* prevent stack from being pushed */
89 /* this breaks pthread_join on Cygwin, which is guaranteed to */
90 /* only see user pthreads */
91 thread_table
[i
].in_use
= FALSE
;
92 CloseHandle(thread_table
[i
].handle
);
93 BZERO((void *)(&thread_table
[i
].context
), sizeof(CONTEXT
));
97 if (SuspendThread(thread_table
[i
].handle
) == (DWORD
)-1)
98 ABORT("SuspendThread failed");
100 thread_table
[i
].suspended
= TRUE
;
104 void GC_start_world()
106 DWORD thread_id
= GetCurrentThreadId();
108 for (i
= 0; i
< MAX_THREADS
; i
++)
109 if (thread_table
[i
].stack
!= 0 && thread_table
[i
].suspended
110 && thread_table
[i
].id
!= thread_id
) {
111 if (ResumeThread(thread_table
[i
].handle
) == (DWORD
)-1)
112 ABORT("ResumeThread failed");
113 thread_table
[i
].suspended
= FALSE
;
115 GC_please_stop
= FALSE
;
119 # pragma warning(disable:4715)
121 ptr_t
GC_current_stackbottom()
123 DWORD thread_id
= GetCurrentThreadId();
125 for (i
= 0; i
< MAX_THREADS
; i
++)
126 if (thread_table
[i
].stack
&& thread_table
[i
].id
== thread_id
)
127 return thread_table
[i
].stack
;
128 ABORT("no thread table entry for current thread");
131 # pragma warning(default:4715)
135 /* The VirtualQuery calls below won't work properly on WinCE, but */
136 /* since each stack is restricted to an aligned 64K region of */
137 /* virtual memory we can just take the next lowest multiple of 64K. */
138 # define GC_get_lo_stack_addr(s) \
139 ((ptr_t)(((DWORD)(s) - 1) & 0xFFFF0000))
141 static ptr_t
GC_get_lo_stack_addr(ptr_t s
)
144 MEMORY_BASIC_INFORMATION info
;
145 VirtualQuery(s
, &info
, sizeof(info
));
147 bottom
= info
.BaseAddress
;
148 VirtualQuery(bottom
- 1, &info
, sizeof(info
));
149 } while ((info
.Protect
& PAGE_READWRITE
)
150 && !(info
.Protect
& PAGE_GUARD
));
155 void GC_push_all_stacks()
157 DWORD thread_id
= GetCurrentThreadId();
159 for (i
= 0; i
< MAX_THREADS
; i
++)
160 if (thread_table
[i
].stack
) {
161 ptr_t bottom
= GC_get_lo_stack_addr(thread_table
[i
].stack
);
162 if (thread_table
[i
].id
== thread_id
)
163 GC_push_all_stack((ptr_t
)&i
, thread_table
[i
].stack
);
165 thread_table
[i
].context
.ContextFlags
166 = (CONTEXT_INTEGER
|CONTEXT_CONTROL
);
167 if (!GetThreadContext(thread_table
[i
].handle
,
168 /* cast away volatile qualifier */
169 (LPCONTEXT
)&thread_table
[i
].context
))
170 ABORT("GetThreadContext failed");
172 GC_push_one ((word
) thread_table
[i
].context
.Edi
);
173 GC_push_one ((word
) thread_table
[i
].context
.Esi
);
174 GC_push_one ((word
) thread_table
[i
].context
.Ebp
);
175 GC_push_one ((word
) thread_table
[i
].context
.Ebx
);
176 GC_push_one ((word
) thread_table
[i
].context
.Edx
);
177 GC_push_one ((word
) thread_table
[i
].context
.Ecx
);
178 GC_push_one ((word
) thread_table
[i
].context
.Eax
);
179 if (thread_table
[i
].context
.Esp
>= (DWORD
)thread_table
[i
].stack
180 || thread_table
[i
].context
.Esp
< (DWORD
)bottom
) {
181 WARN("Thread stack pointer 0x%lx out of range, pushing everything",
182 thread_table
[i
].context
.Esp
);
183 GC_push_all_stack((char *) bottom
, thread_table
[i
].stack
);
185 GC_push_all_stack((char *) thread_table
[i
].context
.Esp
,
186 thread_table
[i
].stack
);
190 if (thread_table
[i
].context
.Sp
>= (DWORD
)thread_table
[i
].stack
191 || thread_table
[i
].context
.Sp
< (DWORD
)bottom
)
192 ABORT("Thread stack pointer out of range");
193 GC_push_one ((word
) thread_table
[i
].context
.R0
);
194 GC_push_one ((word
) thread_table
[i
].context
.R1
);
195 GC_push_one ((word
) thread_table
[i
].context
.R2
);
196 GC_push_one ((word
) thread_table
[i
].context
.R3
);
197 GC_push_one ((word
) thread_table
[i
].context
.R4
);
198 GC_push_one ((word
) thread_table
[i
].context
.R5
);
199 GC_push_one ((word
) thread_table
[i
].context
.R6
);
200 GC_push_one ((word
) thread_table
[i
].context
.R7
);
201 GC_push_one ((word
) thread_table
[i
].context
.R8
);
202 GC_push_one ((word
) thread_table
[i
].context
.R9
);
203 GC_push_one ((word
) thread_table
[i
].context
.R10
);
204 GC_push_one ((word
) thread_table
[i
].context
.R11
);
205 GC_push_one ((word
) thread_table
[i
].context
.R12
);
206 GC_push_all_stack((char *) thread_table
[i
].context
.Sp
,
207 thread_table
[i
].stack
);
210 if (thread_table
[i
].context
.R15
>= (DWORD
)thread_table
[i
].stack
211 || thread_table
[i
].context
.R15
< (DWORD
)bottom
)
212 ABORT("Thread stack pointer out of range");
213 GC_push_one ((word
) thread_table
[i
].context
.R0
);
214 GC_push_one ((word
) thread_table
[i
].context
.R1
);
215 GC_push_one ((word
) thread_table
[i
].context
.R2
);
216 GC_push_one ((word
) thread_table
[i
].context
.R3
);
217 GC_push_one ((word
) thread_table
[i
].context
.R4
);
218 GC_push_one ((word
) thread_table
[i
].context
.R5
);
219 GC_push_one ((word
) thread_table
[i
].context
.R6
);
220 GC_push_one ((word
) thread_table
[i
].context
.R7
);
221 GC_push_one ((word
) thread_table
[i
].context
.R8
);
222 GC_push_one ((word
) thread_table
[i
].context
.R9
);
223 GC_push_one ((word
) thread_table
[i
].context
.R10
);
224 GC_push_one ((word
) thread_table
[i
].context
.R11
);
225 GC_push_one ((word
) thread_table
[i
].context
.R12
);
226 GC_push_one ((word
) thread_table
[i
].context
.R13
);
227 GC_push_one ((word
) thread_table
[i
].context
.R14
);
228 GC_push_all_stack((char *) thread_table
[i
].context
.R15
,
229 thread_table
[i
].stack
);
232 if (thread_table
[i
].context
.IntSp
>= (DWORD
)thread_table
[i
].stack
233 || thread_table
[i
].context
.IntSp
< (DWORD
)bottom
)
234 ABORT("Thread stack pointer out of range");
235 GC_push_one ((word
) thread_table
[i
].context
.IntAt
);
236 GC_push_one ((word
) thread_table
[i
].context
.IntV0
);
237 GC_push_one ((word
) thread_table
[i
].context
.IntV1
);
238 GC_push_one ((word
) thread_table
[i
].context
.IntA0
);
239 GC_push_one ((word
) thread_table
[i
].context
.IntA1
);
240 GC_push_one ((word
) thread_table
[i
].context
.IntA2
);
241 GC_push_one ((word
) thread_table
[i
].context
.IntA3
);
242 GC_push_one ((word
) thread_table
[i
].context
.IntT0
);
243 GC_push_one ((word
) thread_table
[i
].context
.IntT1
);
244 GC_push_one ((word
) thread_table
[i
].context
.IntT2
);
245 GC_push_one ((word
) thread_table
[i
].context
.IntT3
);
246 GC_push_one ((word
) thread_table
[i
].context
.IntT4
);
247 GC_push_one ((word
) thread_table
[i
].context
.IntT5
);
248 GC_push_one ((word
) thread_table
[i
].context
.IntT6
);
249 GC_push_one ((word
) thread_table
[i
].context
.IntT7
);
250 GC_push_one ((word
) thread_table
[i
].context
.IntS0
);
251 GC_push_one ((word
) thread_table
[i
].context
.IntS1
);
252 GC_push_one ((word
) thread_table
[i
].context
.IntS2
);
253 GC_push_one ((word
) thread_table
[i
].context
.IntS3
);
254 GC_push_one ((word
) thread_table
[i
].context
.IntS4
);
255 GC_push_one ((word
) thread_table
[i
].context
.IntS5
);
256 GC_push_one ((word
) thread_table
[i
].context
.IntS6
);
257 GC_push_one ((word
) thread_table
[i
].context
.IntS7
);
258 GC_push_one ((word
) thread_table
[i
].context
.IntT8
);
259 GC_push_one ((word
) thread_table
[i
].context
.IntT9
);
260 GC_push_one ((word
) thread_table
[i
].context
.IntK0
);
261 GC_push_one ((word
) thread_table
[i
].context
.IntK1
);
262 GC_push_one ((word
) thread_table
[i
].context
.IntS8
);
263 GC_push_all_stack((char *) thread_table
[i
].context
.IntSp
,
264 thread_table
[i
].stack
);
267 if (thread_table
[i
].context
.Gpr1
>= (DWORD
)thread_table
[i
].stack
268 || thread_table
[i
].context
.Gpr1
< (DWORD
)bottom
)
269 ABORT("Thread stack pointer out of range");
270 GC_push_one ((word
) thread_table
[i
].context
.Gpr0
);
271 /* Gpr1 is stack pointer */
272 /* Gpr2 is global pointer */
273 GC_push_one ((word
) thread_table
[i
].context
.Gpr3
);
274 GC_push_one ((word
) thread_table
[i
].context
.Gpr4
);
275 GC_push_one ((word
) thread_table
[i
].context
.Gpr5
);
276 GC_push_one ((word
) thread_table
[i
].context
.Gpr6
);
277 GC_push_one ((word
) thread_table
[i
].context
.Gpr7
);
278 GC_push_one ((word
) thread_table
[i
].context
.Gpr8
);
279 GC_push_one ((word
) thread_table
[i
].context
.Gpr9
);
280 GC_push_one ((word
) thread_table
[i
].context
.Gpr10
);
281 GC_push_one ((word
) thread_table
[i
].context
.Gpr11
);
282 GC_push_one ((word
) thread_table
[i
].context
.Gpr12
);
283 /* Gpr13 is reserved for the kernel */
284 GC_push_one ((word
) thread_table
[i
].context
.Gpr14
);
285 GC_push_one ((word
) thread_table
[i
].context
.Gpr15
);
286 GC_push_one ((word
) thread_table
[i
].context
.Gpr16
);
287 GC_push_one ((word
) thread_table
[i
].context
.Gpr17
);
288 GC_push_one ((word
) thread_table
[i
].context
.Gpr18
);
289 GC_push_one ((word
) thread_table
[i
].context
.Gpr19
);
290 GC_push_one ((word
) thread_table
[i
].context
.Gpr20
);
291 GC_push_one ((word
) thread_table
[i
].context
.Gpr21
);
292 GC_push_one ((word
) thread_table
[i
].context
.Gpr22
);
293 GC_push_one ((word
) thread_table
[i
].context
.Gpr23
);
294 GC_push_one ((word
) thread_table
[i
].context
.Gpr24
);
295 GC_push_one ((word
) thread_table
[i
].context
.Gpr25
);
296 GC_push_one ((word
) thread_table
[i
].context
.Gpr26
);
297 GC_push_one ((word
) thread_table
[i
].context
.Gpr27
);
298 GC_push_one ((word
) thread_table
[i
].context
.Gpr28
);
299 GC_push_one ((word
) thread_table
[i
].context
.Gpr29
);
300 GC_push_one ((word
) thread_table
[i
].context
.Gpr30
);
301 GC_push_one ((word
) thread_table
[i
].context
.Gpr31
);
302 GC_push_all_stack((char *) thread_table
[i
].context
.Gpr1
,
303 thread_table
[i
].stack
);
306 if (thread_table
[i
].context
.IntSp
>= (DWORD
)thread_table
[i
].stack
307 || thread_table
[i
].context
.IntSp
< (DWORD
)bottom
)
308 ABORT("Thread stack pointer out of range");
309 GC_push_one ((word
) thread_table
[i
].context
.IntV0
);
310 GC_push_one ((word
) thread_table
[i
].context
.IntT0
);
311 GC_push_one ((word
) thread_table
[i
].context
.IntT1
);
312 GC_push_one ((word
) thread_table
[i
].context
.IntT2
);
313 GC_push_one ((word
) thread_table
[i
].context
.IntT3
);
314 GC_push_one ((word
) thread_table
[i
].context
.IntT4
);
315 GC_push_one ((word
) thread_table
[i
].context
.IntT5
);
316 GC_push_one ((word
) thread_table
[i
].context
.IntT6
);
317 GC_push_one ((word
) thread_table
[i
].context
.IntT7
);
318 GC_push_one ((word
) thread_table
[i
].context
.IntS0
);
319 GC_push_one ((word
) thread_table
[i
].context
.IntS1
);
320 GC_push_one ((word
) thread_table
[i
].context
.IntS2
);
321 GC_push_one ((word
) thread_table
[i
].context
.IntS3
);
322 GC_push_one ((word
) thread_table
[i
].context
.IntS4
);
323 GC_push_one ((word
) thread_table
[i
].context
.IntS5
);
324 GC_push_one ((word
) thread_table
[i
].context
.IntFp
);
325 GC_push_one ((word
) thread_table
[i
].context
.IntA0
);
326 GC_push_one ((word
) thread_table
[i
].context
.IntA1
);
327 GC_push_one ((word
) thread_table
[i
].context
.IntA2
);
328 GC_push_one ((word
) thread_table
[i
].context
.IntA3
);
329 GC_push_one ((word
) thread_table
[i
].context
.IntA4
);
330 GC_push_one ((word
) thread_table
[i
].context
.IntA5
);
331 GC_push_one ((word
) thread_table
[i
].context
.IntT8
);
332 GC_push_one ((word
) thread_table
[i
].context
.IntT9
);
333 GC_push_one ((word
) thread_table
[i
].context
.IntT10
);
334 GC_push_one ((word
) thread_table
[i
].context
.IntT11
);
335 GC_push_one ((word
) thread_table
[i
].context
.IntT12
);
336 GC_push_one ((word
) thread_table
[i
].context
.IntAt
);
337 GC_push_all_stack((char *) thread_table
[i
].context
.IntSp
,
338 thread_table
[i
].stack
);
340 --> architecture
not supported
351 void GC_get_next_stack(char *start
, char **lo
, char **hi
)
354 # define ADDR_LIMIT (char *)(-1L)
355 char * current_min
= ADDR_LIMIT
;
357 for (i
= 0; i
< MAX_THREADS
; i
++) {
358 char * s
= (char *)thread_table
[i
].stack
;
360 if (0 != s
&& s
> start
&& s
< current_min
) {
365 if (current_min
== ADDR_LIMIT
) {
369 *lo
= GC_get_lo_stack_addr(current_min
);
370 if (*lo
< start
) *lo
= start
;
373 #if !defined(CYGWIN32)
375 #if !defined(MSWINCE) && defined(GC_DLL)
377 /* We register threads from DllMain */
379 GC_API HANDLE WINAPI
GC_CreateThread(
380 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
381 DWORD dwStackSize
, LPTHREAD_START_ROUTINE lpStartAddress
,
382 LPVOID lpParameter
, DWORD dwCreationFlags
, LPDWORD lpThreadId
)
384 return CreateThread(lpThreadAttributes
, dwStackSize
, lpStartAddress
,
385 lpParameter
, dwCreationFlags
, lpThreadId
);
388 #else /* defined(MSWINCE) || !defined(GC_DLL)) */
390 /* We have no DllMain to take care of new threads. Thus we */
391 /* must properly intercept thread creation. */
394 HANDLE child_ready_h
, parent_ready_h
;
395 volatile struct thread_entry
* entry
;
396 LPTHREAD_START_ROUTINE start
;
400 static DWORD WINAPI
thread_start(LPVOID arg
);
402 GC_API HANDLE WINAPI
GC_CreateThread(
403 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
404 DWORD dwStackSize
, LPTHREAD_START_ROUTINE lpStartAddress
,
405 LPVOID lpParameter
, DWORD dwCreationFlags
, LPDWORD lpThreadId
)
407 HANDLE thread_h
= NULL
;
408 HANDLE child_ready_h
, parent_ready_h
;
413 /* allocate thread slot */
415 for (i
= 0; i
!= MAX_THREADS
&& thread_table
[i
].in_use
; i
++)
417 if (i
!= MAX_THREADS
) {
418 thread_table
[i
].in_use
= TRUE
;
422 if (i
!= MAX_THREADS
) {
424 /* create unnamed unsignalled events */
425 if (child_ready_h
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
)) {
426 if (parent_ready_h
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
)) {
428 /* set up thread arguments */
429 args
.child_ready_h
= child_ready_h
;
430 args
.parent_ready_h
= parent_ready_h
;
431 args
.entry
= &thread_table
[i
];
432 args
.start
= lpStartAddress
;
433 args
.param
= lpParameter
;
435 thread_h
= CreateThread(lpThreadAttributes
,
436 dwStackSize
, thread_start
,
438 dwCreationFlags
& ~CREATE_SUSPENDED
,
443 /* fill in ID and handle; tell child this is done */
444 thread_table
[i
].id
= *lpThreadId
;
445 if (!DuplicateHandle(GetCurrentProcess(),
448 (PHANDLE
) &thread_table
[i
].handle
,
451 DUPLICATE_SAME_ACCESS
)) {
452 DWORD last_error
= GetLastError();
453 GC_printf1("Last error code: %lx\n", last_error
);
454 ABORT("DuplicateHandle failed");
456 SetEvent (parent_ready_h
);
458 /* wait for child to fill in stack and copy args */
459 WaitForSingleObject (child_ready_h
, INFINITE
);
461 /* suspend the child if requested */
462 if (dwCreationFlags
& CREATE_SUSPENDED
)
463 SuspendThread (thread_h
);
465 /* let child call given function now (or when resumed) */
466 SetEvent (parent_ready_h
);
469 CloseHandle (parent_ready_h
);
474 CloseHandle (child_ready_h
);
476 if (thread_h
== NULL
)
477 thread_table
[i
].in_use
= FALSE
;
479 } else { /* no thread slot found */
480 SetLastError (ERROR_TOO_MANY_TCBS
);
486 static DWORD WINAPI
thread_start(LPVOID arg
)
489 thread_args args
= *(thread_args
*)arg
;
491 /* wait for parent to fill in ID and handle */
492 WaitForSingleObject (args
.parent_ready_h
, INFINITE
);
493 ResetEvent (args
.parent_ready_h
);
495 /* fill in stack; tell parent this is done */
496 args
.entry
->stack
= GC_get_stack_base();
497 SetEvent (args
.child_ready_h
);
499 /* wait for parent to tell us to go (in case it needs to suspend us) */
500 WaitForSingleObject (args
.parent_ready_h
, INFINITE
);
501 CloseHandle (args
.parent_ready_h
);
503 /* Clear the thread entry even if we exit with an exception. */
504 /* This is probably pointless, since an uncaught exception is */
505 /* supposed to result in the process being killed. */
508 #endif /* __GNUC__ */
509 ret
= args
.start (args
.param
);
512 #endif /* __GNUC__ */
514 args
.entry
->stack
= 0;
515 args
.entry
->in_use
= FALSE
;
516 /* cast away volatile qualifier */
517 BZERO((void *) &args
.entry
->context
, sizeof(CONTEXT
));
521 #endif /* __GNUC__ */
525 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
527 #endif /* !CYGWIN32 */
533 HINSTANCE hPrevInstance
;
538 DWORD WINAPI
main_thread_start(LPVOID arg
);
540 int WINAPI
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
,
541 LPWSTR lpCmdLine
, int nShowCmd
)
545 main_thread_args args
= {
546 hInstance
, hPrevInstance
, lpCmdLine
, nShowCmd
551 /* initialize everything */
552 InitializeCriticalSection(&GC_allocate_ml
);
555 /* start the main thread */
556 thread_h
= GC_CreateThread(
557 NULL
, 0, main_thread_start
, &args
, 0, &thread_id
);
559 if (thread_h
!= NULL
)
561 WaitForSingleObject (thread_h
, INFINITE
);
562 GetExitCodeThread (thread_h
, &exit_code
);
563 CloseHandle (thread_h
);
567 DeleteCriticalSection(&GC_allocate_ml
);
569 return (int) exit_code
;
572 DWORD WINAPI
main_thread_start(LPVOID arg
)
574 main_thread_args
* args
= (main_thread_args
*) arg
;
576 return (DWORD
) GC_WinMain (args
->hInstance
, args
->hPrevInstance
,
577 args
->lpCmdLine
, args
->nShowCmd
);
580 # else /* !MSWINCE */
582 LONG WINAPI
GC_write_fault_handler(struct _EXCEPTION_POINTERS
*exc_info
);
584 /* threadAttach/threadDetach routines used by both CYGWIN and DLL
585 * implementation, since both recieve explicit notification on thread
586 * creation/destruction.
588 static void threadAttach() {
590 /* It appears to be unsafe to acquire a lock here, since this */
591 /* code is apparently not preeemptible on some systems. */
592 /* (This is based on complaints, not on Microsoft's official */
593 /* documentation, which says this should perform "only simple */
594 /* inititalization tasks".) */
595 /* Hence we make do with nonblocking synchronization. */
597 /* The following should be a noop according to the win32 */
598 /* documentation. There is empirical evidence that it */
600 # if defined(MPROTECT_VDB)
601 if (GC_incremental
) SetUnhandledExceptionFilter(GC_write_fault_handler
);
603 /* cast away volatile qualifier */
604 for (i
= 0; InterlockedExchange((LONG
*)&thread_table
[i
].in_use
,1) != 0; i
++) {
605 /* Compare-and-swap would make this cleaner, but that's not */
606 /* supported before Windows 98 and NT 4.0. In Windows 2000, */
607 /* InterlockedExchange is supposed to be replaced by */
608 /* InterlockedExchangePointer, but that's not really what I */
610 if (i
== MAX_THREADS
- 1)
611 ABORT("too many threads");
613 thread_table
[i
].id
= GetCurrentThreadId();
615 thread_table
[i
].pthread_id
= pthread_self();
617 if (!DuplicateHandle(GetCurrentProcess(),
620 (HANDLE
*)&thread_table
[i
].handle
,
623 DUPLICATE_SAME_ACCESS
)) {
624 DWORD last_error
= GetLastError();
625 GC_printf1("Last error code: %lx\n", last_error
);
626 ABORT("DuplicateHandle failed");
628 thread_table
[i
].stack
= GC_get_stack_base();
629 if (thread_table
[i
].stack
== NULL
)
630 ABORT("Failed to find stack base in threadAttach");
631 /* If this thread is being created while we are trying to stop */
632 /* the world, wait here. Hopefully this can't happen on any */
633 /* systems that don't allow us to block here. */
634 while (GC_please_stop
) Sleep(20);
637 static void threadDetach(DWORD thread_id
) {
643 (!thread_table
[i
].in_use
|| thread_table
[i
].id
!= thread_id
);
645 if (i
>= MAX_THREADS
) {
646 WARN("thread %ld not found on detach", (GC_word
)thread_id
);
648 thread_table
[i
].stack
= 0;
649 thread_table
[i
].in_use
= FALSE
;
650 CloseHandle(thread_table
[i
].handle
);
651 /* cast away volatile qualifier */
652 BZERO((void *)&thread_table
[i
].context
, sizeof(CONTEXT
));
659 /* Called by GC_init() - we hold the allocation lock. */
661 if (GC_thr_initialized
) return;
662 GC_thr_initialized
= TRUE
;
665 /* this might already be handled in GC_init... */
666 InitializeCriticalSection(&GC_allocate_ml
);
669 /* Add the initial thread, so we can stop it. */
674 void *(*start_routine
)(void *);
678 int GC_pthread_join(pthread_t pthread_id
, void **retval
) {
682 # if DEBUG_CYGWIN_THREADS
683 GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n",(int)pthread_self(),
684 GetCurrentThreadId(), (int)pthread_id
);
687 /* Can't do any table lookups here, because thread being joined
688 might not have registered itself yet */
690 result
= pthread_join(pthread_id
, retval
);
693 for (i
= 0; !thread_table
[i
].in_use
|| thread_table
[i
].pthread_id
!= pthread_id
;
695 if (i
== MAX_THREADS
- 1) {
696 GC_printf1("Failed to find thread 0x%x in pthread_join()\n", pthread_id
);
697 ABORT("thread not found on detach");
701 threadDetach(thread_table
[i
].id
);
703 # if DEBUG_CYGWIN_THREADS
704 GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n",
705 (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id
);
711 /* Cygwin-pthreads calls CreateThread internally, but it's not
712 * easily interceptible by us..
713 * so intercept pthread_create instead
716 GC_pthread_create(pthread_t
*new_thread
,
717 const pthread_attr_t
*attr
,
718 void *(*start_routine
)(void *), void *arg
) {
720 struct start_info
* si
;
722 if (!GC_is_initialized
) GC_init();
723 /* make sure GC is initialized (i.e. main thread is attached) */
725 /* This is otherwise saved only in an area mmapped by the thread */
726 /* library, which isn't visible to the collector. */
727 si
= GC_malloc_uncollectable(sizeof(struct start_info
));
728 if (0 == si
) return(EAGAIN
);
730 si
-> start_routine
= start_routine
;
733 # if DEBUG_CYGWIN_THREADS
734 GC_printf2("About to create a thread from 0x%x(0x%x)\n",(int)pthread_self(),
737 result
= pthread_create(new_thread
, attr
, GC_start_routine
, si
);
739 if (result
) { /* failure */
746 void * GC_start_routine(void * arg
)
748 struct start_info
* si
= arg
;
750 void *(*start
)(void *);
752 pthread_t pthread_id
;
755 # if DEBUG_CYGWIN_THREADS
756 GC_printf2("thread 0x%x(0x%x) starting...\n",(int)pthread_self(),
757 GetCurrentThreadId());
760 /* If a GC occurs before the thread is registered, that GC will */
761 /* ignore this thread. That's fine, since it will block trying to */
762 /* acquire the allocation lock, and won't yet hold interesting */
765 /* We register the thread here instead of in the parent, so that */
766 /* we don't need to hold the allocation lock during pthread_create. */
770 start
= si
-> start_routine
;
771 start_arg
= si
-> arg
;
772 pthread_id
= pthread_self();
774 GC_free(si
); /* was allocated uncollectable */
776 pthread_cleanup_push(GC_thread_exit_proc
, pthread_id
);
777 result
= (*start
)(start_arg
);
778 pthread_cleanup_pop(0);
780 # if DEBUG_CYGWIN_THREADS
781 GC_printf2("thread 0x%x(0x%x) returned from start routine.\n",
782 (int)pthread_self(),GetCurrentThreadId());
786 for (i
= 0; thread_table
[i
].pthread_id
!= pthread_id
; i
++) {
787 if (i
== MAX_THREADS
- 1)
788 ABORT("thread not found on exit");
790 thread_table
[i
].status
= result
;
796 void GC_thread_exit_proc(void *arg
)
798 pthread_t pthread_id
= (pthread_t
)arg
;
801 # if DEBUG_CYGWIN_THREADS
802 GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",
803 (int)pthread_self(),GetCurrentThreadId());
807 for (i
= 0; thread_table
[i
].pthread_id
!= pthread_id
; i
++) {
808 if (i
== MAX_THREADS
- 1)
809 ABORT("thread not found on exit");
814 /* TODO: we need a way to get the exit value after a pthread_exit so we can stash it safely away */
815 thread_table
[i
].status
= ???
819 /* nothing required here... */
820 int GC_pthread_sigmask(int how
, const sigset_t
*set
, sigset_t
*oset
) {
821 return pthread_sigmask(how
, set
, oset
);
823 int GC_pthread_detach(pthread_t thread
) {
824 return pthread_detach(thread
);
826 #else /* !CYGWIN32 */
829 * We avoid acquiring locks here, since this doesn't seem to be preemptable.
830 * Pontus Rydin suggests wrapping the thread start routine instead.
833 BOOL WINAPI
DllMain(HINSTANCE inst
, ULONG reason
, LPVOID reserved
)
836 case DLL_PROCESS_ATTACH
:
837 InitializeCriticalSection(&GC_allocate_ml
);
838 GC_init(); /* Force initialization before thread attach. */
840 case DLL_THREAD_ATTACH
:
844 case DLL_THREAD_DETACH
:
845 threadDetach(GetCurrentThreadId());
848 case DLL_PROCESS_DETACH
:
853 for (i
= 0; i
< MAX_THREADS
; ++i
)
855 if (thread_table
[i
].in_use
)
857 thread_table
[i
].stack
= 0;
858 thread_table
[i
].in_use
= FALSE
;
859 CloseHandle(thread_table
[i
].handle
);
860 BZERO((void *) &thread_table
[i
].context
, sizeof(CONTEXT
));
866 DeleteCriticalSection(&GC_allocate_ml
);
874 #endif /* !CYGWIN32 */
876 # endif /* !MSWINCE */
878 #endif /* GC_WIN32_THREADS */