* arm.h (ASM_OUTPUT_REG_PUSH, ASM_OUTPUT_REG_POP): Wrap in
[official-gcc.git] / boehm-gc / win32_threads.c
blob10cfef9d9eb073eaffff9febff9411cf07a6e20d
1 #if defined(GC_WIN32_THREADS)
3 #include "private/gc_priv.h"
4 #include <windows.h>
6 #ifdef CYGWIN32
7 # include <errno.h>
9 /* Cygwin-specific forward decls */
10 # undef pthread_create
11 # undef pthread_sigmask
12 # undef pthread_join
13 # undef dlopen
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);
21 #endif
23 #ifndef MAX_THREADS
24 # define MAX_THREADS 64
25 #endif
27 struct thread_entry {
28 LONG in_use;
29 DWORD id;
30 HANDLE handle;
31 void *stack; /* The cold end of the stack. */
32 /* 0 ==> entry not valid. */
33 /* !in_use ==> stack == 0 */
34 CONTEXT context;
35 GC_bool suspended;
37 # ifdef CYGWIN32
38 void *status; /* hold exit value until join in case it's a pointer */
39 pthread_t pthread_id;
40 # endif
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. */
53 # ifdef CYGWIN32
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));
58 # endif
61 void GC_stop_world()
63 DWORD thread_id = GetCurrentThreadId();
64 int i;
66 #ifdef CYGWIN32
67 if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()");
68 #endif
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) {
74 # ifdef MSWINCE
75 /* SuspendThread will fail if thread is running kernel code */
76 while (SuspendThread(thread_table[i].handle) == (DWORD)-1)
77 Sleep(10);
78 # else
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. */
84 DWORD exitCode;
85 if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&
86 exitCode != STILL_ACTIVE) {
87 thread_table[i].stack = 0; /* prevent stack from being pushed */
88 # ifndef CYGWIN32
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));
94 # endif
95 continue;
97 if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
98 ABORT("SuspendThread failed");
99 # endif
100 thread_table[i].suspended = TRUE;
104 void GC_start_world()
106 DWORD thread_id = GetCurrentThreadId();
107 int i;
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;
118 # ifdef _MSC_VER
119 # pragma warning(disable:4715)
120 # endif
121 ptr_t GC_current_stackbottom()
123 DWORD thread_id = GetCurrentThreadId();
124 int i;
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");
130 # ifdef _MSC_VER
131 # pragma warning(default:4715)
132 # endif
134 # ifdef MSWINCE
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))
140 # else
141 static ptr_t GC_get_lo_stack_addr(ptr_t s)
143 ptr_t bottom;
144 MEMORY_BASIC_INFORMATION info;
145 VirtualQuery(s, &info, sizeof(info));
146 do {
147 bottom = info.BaseAddress;
148 VirtualQuery(bottom - 1, &info, sizeof(info));
149 } while ((info.Protect & PAGE_READWRITE)
150 && !(info.Protect & PAGE_GUARD));
151 return(bottom);
153 # endif
155 void GC_push_all_stacks()
157 DWORD thread_id = GetCurrentThreadId();
158 int i;
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);
164 else {
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");
171 # ifdef I386
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);
184 } else {
185 GC_push_all_stack((char *) thread_table[i].context.Esp,
186 thread_table[i].stack);
188 # else
189 # ifdef ARM32
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);
208 # else
209 # ifdef SHx
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);
230 # else
231 # ifdef MIPS
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);
265 # else
266 # ifdef PPC
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);
304 # else
305 # ifdef ALPHA
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);
339 # else
340 --> architecture not supported
341 # endif /* !ALPHA */
342 # endif /* !PPC */
343 # endif /* !MIPS */
344 # endif /* !SHx */
345 # endif /* !ARM32 */
346 # endif /* !I386 */
351 void GC_get_next_stack(char *start, char **lo, char **hi)
353 int i;
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) {
361 current_min = s;
364 *hi = current_min;
365 if (current_min == ADDR_LIMIT) {
366 *lo = ADDR_LIMIT;
367 return;
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. */
393 typedef struct {
394 HANDLE child_ready_h, parent_ready_h;
395 volatile struct thread_entry * entry;
396 LPTHREAD_START_ROUTINE start;
397 LPVOID param;
398 } thread_args;
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;
410 int i;
411 thread_args args;
413 /* allocate thread slot */
414 LOCK();
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;
420 UNLOCK();
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,
437 &args,
438 dwCreationFlags & ~CREATE_SUSPENDED,
439 lpThreadId);
441 if (thread_h) {
443 /* fill in ID and handle; tell child this is done */
444 thread_table[i].id = *lpThreadId;
445 if (!DuplicateHandle(GetCurrentProcess(),
446 thread_h,
447 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);
468 } else {
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);
483 return thread_h;
486 static DWORD WINAPI thread_start(LPVOID arg)
488 DWORD ret = 0;
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. */
506 #ifndef __GNUC__
507 __try {
508 #endif /* __GNUC__ */
509 ret = args.start (args.param);
510 #ifndef __GNUC__
511 } __finally {
512 #endif /* __GNUC__ */
513 LOCK();
514 args.entry->stack = 0;
515 args.entry->in_use = FALSE;
516 /* cast away volatile qualifier */
517 BZERO((void *) &args.entry->context, sizeof(CONTEXT));
518 UNLOCK();
519 #ifndef __GNUC__
521 #endif /* __GNUC__ */
523 return ret;
525 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
527 #endif /* !CYGWIN32 */
529 #ifdef MSWINCE
531 typedef struct {
532 HINSTANCE hInstance;
533 HINSTANCE hPrevInstance;
534 LPWSTR lpCmdLine;
535 int nShowCmd;
536 } main_thread_args;
538 DWORD WINAPI main_thread_start(LPVOID arg);
540 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
541 LPWSTR lpCmdLine, int nShowCmd)
543 DWORD exit_code = 1;
545 main_thread_args args = {
546 hInstance, hPrevInstance, lpCmdLine, nShowCmd
548 HANDLE thread_h;
549 DWORD thread_id;
551 /* initialize everything */
552 InitializeCriticalSection(&GC_allocate_ml);
553 GC_init();
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);
566 GC_deinit();
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() {
589 int i;
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 */
599 /* isn't. - HB */
600 # if defined(MPROTECT_VDB)
601 if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
602 # endif
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 */
609 /* want here. */
610 if (i == MAX_THREADS - 1)
611 ABORT("too many threads");
613 thread_table[i].id = GetCurrentThreadId();
614 # ifdef CYGWIN32
615 thread_table[i].pthread_id = pthread_self();
616 # endif
617 if (!DuplicateHandle(GetCurrentProcess(),
618 GetCurrentThread(),
619 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) {
638 int i;
640 LOCK();
641 for (i = 0;
642 i < MAX_THREADS &&
643 (!thread_table[i].in_use || thread_table[i].id != thread_id);
644 i++) {}
645 if (i >= MAX_THREADS ) {
646 WARN("thread %ld not found on detach", (GC_word)thread_id);
647 } else {
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));
654 UNLOCK();
657 #ifdef CYGWIN32
659 /* Called by GC_init() - we hold the allocation lock. */
660 void GC_thr_init() {
661 if (GC_thr_initialized) return;
662 GC_thr_initialized = TRUE;
664 #if 0
665 /* this might already be handled in GC_init... */
666 InitializeCriticalSection(&GC_allocate_ml);
667 #endif
669 /* Add the initial thread, so we can stop it. */
670 threadAttach();
673 struct start_info {
674 void *(*start_routine)(void *);
675 void *arg;
678 int GC_pthread_join(pthread_t pthread_id, void **retval) {
679 int result;
680 int i;
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);
685 # endif
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);
692 LOCK();
693 for (i = 0; !thread_table[i].in_use || thread_table[i].pthread_id != pthread_id;
694 i++) {
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");
700 UNLOCK();
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);
706 # endif
708 return result;
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) {
719 int result;
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;
731 si -> arg = arg;
733 # if DEBUG_CYGWIN_THREADS
734 GC_printf2("About to create a thread from 0x%x(0x%x)\n",(int)pthread_self(),
735 GetCurrentThreadId);
736 # endif
737 result = pthread_create(new_thread, attr, GC_start_routine, si);
739 if (result) { /* failure */
740 GC_free(si);
743 return(result);
746 void * GC_start_routine(void * arg)
748 struct start_info * si = arg;
749 void * result;
750 void *(*start)(void *);
751 void *start_arg;
752 pthread_t pthread_id;
753 int i;
755 # if DEBUG_CYGWIN_THREADS
756 GC_printf2("thread 0x%x(0x%x) starting...\n",(int)pthread_self(),
757 GetCurrentThreadId());
758 # endif
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 */
763 /* pointers. */
764 LOCK();
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. */
767 threadAttach();
768 UNLOCK();
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());
783 # endif
785 LOCK();
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;
791 UNLOCK();
793 return(result);
796 void GC_thread_exit_proc(void *arg)
798 pthread_t pthread_id = (pthread_t)arg;
799 int i;
801 # if DEBUG_CYGWIN_THREADS
802 GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",
803 (int)pthread_self(),GetCurrentThreadId());
804 # endif
806 LOCK();
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");
811 UNLOCK();
813 #if 0
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 = ???
816 #endif
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.
832 #ifdef GC_DLL
833 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
835 switch (reason) {
836 case DLL_PROCESS_ATTACH:
837 InitializeCriticalSection(&GC_allocate_ml);
838 GC_init(); /* Force initialization before thread attach. */
839 /* fall through */
840 case DLL_THREAD_ATTACH:
841 threadAttach();
842 break;
844 case DLL_THREAD_DETACH:
845 threadDetach(GetCurrentThreadId());
846 break;
848 case DLL_PROCESS_DETACH:
850 int i;
852 LOCK();
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));
863 UNLOCK();
865 GC_deinit();
866 DeleteCriticalSection(&GC_allocate_ml);
868 break;
871 return TRUE;
873 #endif /* GC_DLL */
874 #endif /* !CYGWIN32 */
876 # endif /* !MSWINCE */
878 #endif /* GC_WIN32_THREADS */