[interp] Small fixes (#11667)
[mono-project.git] / libgc / win32_threads.c
blob425d41a9892239993327756e908800caec4547ae
1 #include "private/gc_priv.h"
3 #if defined(GC_WIN32_THREADS)
5 #include <windows.h>
7 #ifdef CYGWIN32
8 # include <errno.h>
10 /* Cygwin-specific forward decls */
11 # undef pthread_create
12 # undef pthread_sigmask
13 # undef pthread_join
14 # undef pthread_detach
15 # undef dlopen
17 # define DEBUG_CYGWIN_THREADS 0
19 void * GC_start_routine(void * arg);
20 void GC_thread_exit_proc(void *arg);
22 #endif
24 /* The type of the first argument to InterlockedExchange. */
25 /* Documented to be LONG volatile *, but at least gcc likes */
26 /* this better. */
27 typedef LONG * IE_t;
29 #ifndef MAX_THREADS
30 # define MAX_THREADS 256
31 /* FIXME: */
32 /* Things may get quite slow for large numbers of threads, */
33 /* since we look them up with sequential search. */
34 #endif
36 GC_bool GC_thr_initialized = FALSE;
38 DWORD GC_main_thread = 0;
40 struct GC_thread_Rep {
41 LONG in_use; /* Updated without lock. */
42 /* We assert that unused */
43 /* entries have invalid ids of */
44 /* zero and zero stack fields. */
45 DWORD id;
46 HANDLE handle;
47 ptr_t stack_base; /* The cold end of the stack. */
48 /* 0 ==> entry not valid. */
49 /* !in_use ==> stack_base == 0 */
50 GC_bool suspended;
51 GC_bool thread_blocked;
53 # ifdef CYGWIN32
54 void *status; /* hold exit value until join in case it's a pointer */
55 pthread_t pthread_id;
56 short flags; /* Protected by GC lock. */
57 # define FINISHED 1 /* Thread has exited. */
58 # define DETACHED 2 /* Thread is intended to be detached. */
59 # endif
62 typedef volatile struct GC_thread_Rep * GC_thread;
65 * We generally assume that volatile ==> memory ordering, at least among
66 * volatiles.
69 volatile GC_bool GC_please_stop = FALSE;
71 volatile struct GC_thread_Rep thread_table[MAX_THREADS];
73 volatile LONG GC_max_thread_index = 0; /* Largest index in thread_table */
74 /* that was ever used. */
76 extern LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
78 int GC_thread_is_registered (void)
80 #if defined(GC_DLL) || defined(GC_INSIDE_DLL)
81 /* Registered by DllMain */
82 return 1;
83 #else
84 /* FIXME: */
85 return 0;
86 #endif
89 void GC_allow_register_threads (void)
91 /* No-op for GC pre-v7. */
94 int GC_register_my_thread (struct GC_stack_base *sb)
96 # if defined(GC_DLL) || defined(GC_INSIDE_DLL)
97 /* Registered by DllMain. */
98 return GC_DUPLICATE;
99 # else
100 /* TODO: Implement. */
101 return GC_UNIMPLEMENTED;
102 # endif
105 void GC_register_altstack (void *stack, int stack_size, void *altstack, int altstack_size)
110 * This may be called from DllMain, and hence operates under unusual
111 * constraints.
113 static GC_thread GC_new_thread(void) {
114 int i;
115 /* It appears to be unsafe to acquire a lock here, since this */
116 /* code is apparently not preeemptible on some systems. */
117 /* (This is based on complaints, not on Microsoft's official */
118 /* documentation, which says this should perform "only simple */
119 /* initialization tasks".) */
120 /* Hence we make do with nonblocking synchronization. */
122 /* The following should be a noop according to the win32 */
123 /* documentation. There is empirical evidence that it */
124 /* isn't. - HB */
125 # if defined(MPROTECT_VDB)
126 if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
127 # endif
128 /* cast away volatile qualifier */
129 for (i = 0; InterlockedExchange((IE_t)&thread_table[i].in_use,1) != 0; i++) {
130 /* Compare-and-swap would make this cleaner, but that's not */
131 /* supported before Windows 98 and NT 4.0. In Windows 2000, */
132 /* InterlockedExchange is supposed to be replaced by */
133 /* InterlockedExchangePointer, but that's not really what I */
134 /* want here. */
135 if (i == MAX_THREADS - 1)
136 ABORT("too many threads");
138 /* Update GC_max_thread_index if necessary. The following is safe, */
139 /* and unlike CompareExchange-based solutions seems to work on all */
140 /* Windows95 and later platforms. */
141 /* Unfortunately, GC_max_thread_index may be temporarily out of */
142 /* bounds, so readers have to compensate. */
143 while (i > GC_max_thread_index) {
144 InterlockedIncrement((IE_t)&GC_max_thread_index);
146 if (GC_max_thread_index >= MAX_THREADS) {
147 /* We overshot due to simultaneous increments. */
148 /* Setting it to MAX_THREADS-1 is always safe. */
149 GC_max_thread_index = MAX_THREADS - 1;
152 # ifdef CYGWIN32
153 thread_table[i].pthread_id = pthread_self();
154 # endif
155 if (!DuplicateHandle(GetCurrentProcess(),
156 GetCurrentThread(),
157 GetCurrentProcess(),
158 (HANDLE*)&thread_table[i].handle,
161 DUPLICATE_SAME_ACCESS)) {
162 DWORD last_error = GetLastError();
163 GC_printf1("Last error code: %lx\n", last_error);
164 ABORT("DuplicateHandle failed");
166 thread_table[i].stack_base = GC_get_stack_base();
167 /* Up until this point, GC_push_all_stacks considers this thread */
168 /* invalid. */
169 if (thread_table[i].stack_base == NULL)
170 ABORT("Failed to find stack base in GC_new_thread");
171 /* Up until this point, this entry is viewed as reserved but invalid */
172 /* by GC_delete_thread. */
173 thread_table[i].id = GetCurrentThreadId();
174 /* If this thread is being created while we are trying to stop */
175 /* the world, wait here. Hopefully this can't happen on any */
176 /* systems that don't allow us to block here. */
177 while (GC_please_stop) Sleep(20);
178 GC_ASSERT(!thread_table[i]->thread_blocked);
179 return thread_table + i;
183 * GC_max_thread_index may temporarily be larger than MAX_THREADS.
184 * To avoid subscript errors, we check on access.
186 #ifdef __GNUC__
187 __inline__
188 #endif
189 static LONG GC_get_max_thread_index()
191 LONG my_max = GC_max_thread_index;
193 if (my_max >= MAX_THREADS) return MAX_THREADS-1;
194 return my_max;
197 /* This is intended to be lock-free, though that */
198 /* assumes that the CloseHandle becomes visible before the */
199 /* in_use assignment. */
200 static void GC_delete_gc_thread(GC_thread thr)
202 CloseHandle(thr->handle);
203 /* cast away volatile qualifier */
204 thr->stack_base = 0;
205 thr->id = 0;
206 # ifdef CYGWIN32
207 thr->pthread_id = 0;
208 # endif /* CYGWIN32 */
209 thr->in_use = FALSE;
212 static void GC_delete_thread(DWORD thread_id) {
213 int i;
214 LONG my_max = GC_get_max_thread_index();
216 for (i = 0;
217 i <= my_max &&
218 (!thread_table[i].in_use || thread_table[i].id != thread_id);
219 /* Must still be in_use, since nobody else can store our thread_id. */
220 i++) {}
221 if (i > my_max) {
222 WARN("Removing nonexistent thread %ld\n", (GC_word)thread_id);
223 } else {
224 GC_delete_gc_thread(thread_table+i);
228 #ifdef CYGWIN32
230 /* Return a GC_thread corresponding to a given pthread_t. */
231 /* Returns 0 if it's not there. */
232 /* We assume that this is only called for pthread ids that */
233 /* have not yet terminated or are still joinable. */
234 static GC_thread GC_lookup_thread(pthread_t id)
236 int i;
237 LONG my_max = GC_get_max_thread_index();
239 for (i = 0;
240 i <= my_max &&
241 (!thread_table[i].in_use || thread_table[i].pthread_id != id
242 || !thread_table[i].in_use);
243 /* Must still be in_use, since nobody else can store our thread_id. */
244 i++);
245 if (i > my_max) return 0;
246 return thread_table + i;
249 #else
251 static GC_thread GC_lookup_thread(DWORD id)
253 int i;
254 LONG max = GC_get_max_thread_index();
256 for (i = 0; i <= max; i++)
257 if (thread_table[i].in_use && thread_table[i].id == id)
258 return &thread_table[i];
260 return NULL;
263 #endif /* CYGWIN32 */
265 void GC_push_thread_structures GC_PROTO((void))
267 /* Unlike the other threads implementations, the thread table here */
268 /* contains no pointers to the collectable heap. Thus we have */
269 /* no private structures we need to preserve. */
270 # ifdef CYGWIN32
271 { int i; /* pthreads may keep a pointer in the thread exit value */
272 LONG my_max = GC_get_max_thread_index();
274 for (i = 0; i <= my_max; i++)
275 if (thread_table[i].in_use)
276 GC_push_all((ptr_t)&(thread_table[i].status),
277 (ptr_t)(&(thread_table[i].status)+1));
279 # endif
282 /* Wrappers for functions that are likely to block for an appreciable */
283 /* length of time. Must be called in pairs, if at all. */
284 /* Nothing much beyond the system call itself should be executed */
285 /* between these. */
287 void GC_start_blocking(void) {
288 GC_thread me;
289 LOCK();
290 #ifdef CYGWIN32
291 me = GC_lookup_thread(pthread_self());
292 #else
293 me = GC_lookup_thread(GetCurrentThreadId());
294 #endif
295 me->thread_blocked = TRUE;
296 UNLOCK();
299 void GC_end_blocking(void) {
300 GC_thread me;
301 LOCK(); /* This will block if the world is stopped. */
302 #ifdef CYGWIN32
303 me = GC_lookup_thread(pthread_self());
304 #else
305 me = GC_lookup_thread(GetCurrentThreadId());
306 #endif
307 me->thread_blocked = FALSE;
308 UNLOCK();
311 /* Defined in misc.c */
312 extern CRITICAL_SECTION GC_write_cs;
314 void GC_stop_world()
316 DWORD thread_id = GetCurrentThreadId();
317 int i;
319 if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()");
321 GC_please_stop = TRUE;
322 # ifndef CYGWIN32
323 EnterCriticalSection(&GC_write_cs);
324 # endif /* !CYGWIN32 */
325 for (i = 0; i <= GC_get_max_thread_index(); i++)
326 if (thread_table[i].stack_base != 0
327 && thread_table[i].id != thread_id) {
328 if (thread_table [i].thread_blocked)
329 continue;
330 # ifdef MSWINCE
331 /* SuspendThread will fail if thread is running kernel code */
332 while (SuspendThread(thread_table[i].handle) == (DWORD)-1)
333 Sleep(10);
334 # else
335 /* Apparently the Windows 95 GetOpenFileName call creates */
336 /* a thread that does not properly get cleaned up, and */
337 /* SuspendThread on its descriptor may provoke a crash. */
338 /* This reduces the probability of that event, though it still */
339 /* appears there's a race here. */
340 DWORD exitCode;
341 if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&
342 exitCode != STILL_ACTIVE) {
343 thread_table[i].stack_base = 0; /* prevent stack from being pushed */
344 # ifndef CYGWIN32
345 /* this breaks pthread_join on Cygwin, which is guaranteed to */
346 /* only see user pthreads */
347 thread_table[i].in_use = FALSE;
348 CloseHandle(thread_table[i].handle);
349 # endif
350 continue;
352 if (SuspendThread(thread_table[i].handle) == (DWORD)-1) {
353 thread_table[i].stack_base = 0; /* prevent stack from being pushed */
354 # ifndef CYGWIN32
355 /* this breaks pthread_join on Cygwin, which is guaranteed to */
356 /* only see user pthreads */
357 thread_table[i].in_use = FALSE;
358 CloseHandle(thread_table[i].handle);
359 # endif
361 # endif
362 thread_table[i].suspended = TRUE;
364 # ifndef CYGWIN32
365 LeaveCriticalSection(&GC_write_cs);
366 # endif /* !CYGWIN32 */
369 void GC_start_world()
371 DWORD thread_id = GetCurrentThreadId();
372 int i;
373 LONG my_max = GC_get_max_thread_index();
375 for (i = 0; i <= my_max; i++)
376 if (thread_table[i].stack_base != 0 && thread_table[i].suspended
377 && thread_table[i].id != thread_id) {
378 if (ResumeThread(thread_table[i].handle) == (DWORD)-1)
379 ABORT("ResumeThread failed");
380 thread_table[i].suspended = FALSE;
382 GC_please_stop = FALSE;
385 # ifdef _MSC_VER
386 # pragma warning(disable:4715)
387 # endif
388 ptr_t GC_current_stackbottom()
390 DWORD thread_id = GetCurrentThreadId();
391 int i;
392 LONG my_max = GC_get_max_thread_index();
394 for (i = 0; i <= my_max; i++)
395 if (thread_table[i].stack_base && thread_table[i].id == thread_id)
396 return thread_table[i].stack_base;
397 ABORT("no thread table entry for current thread");
399 # ifdef _MSC_VER
400 # pragma warning(default:4715)
401 # endif
403 # ifdef MSWINCE
404 /* The VirtualQuery calls below won't work properly on WinCE, but */
405 /* since each stack is restricted to an aligned 64K region of */
406 /* virtual memory we can just take the next lowest multiple of 64K. */
407 # define GC_get_stack_min(s) \
408 ((ptr_t)(((DWORD)(s) - 1) & 0xFFFF0000))
409 # else
410 static ptr_t GC_get_stack_min(ptr_t s)
412 ptr_t bottom;
413 MEMORY_BASIC_INFORMATION info;
414 VirtualQuery(s, &info, sizeof(info));
415 do {
416 bottom = info.BaseAddress;
417 VirtualQuery(bottom - 1, &info, sizeof(info));
418 } while ((info.Protect & PAGE_READWRITE)
419 && !(info.Protect & PAGE_GUARD));
420 return(bottom);
422 # endif
424 void GC_push_all_stacks()
426 DWORD thread_id = GetCurrentThreadId();
427 GC_bool found_me = FALSE;
428 int i;
429 int dummy;
430 ptr_t sp, stack_min;
431 GC_thread thread;
432 LONG my_max = GC_get_max_thread_index();
434 for (i = 0; i <= my_max; i++) {
435 thread = thread_table + i;
436 if (thread -> in_use && thread -> stack_base) {
437 if (thread -> id == thread_id) {
438 sp = (ptr_t) &dummy;
439 found_me = TRUE;
440 } else {
441 CONTEXT context;
442 context.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL;
443 if (!GetThreadContext(thread_table[i].handle, &context))
444 ABORT("GetThreadContext failed");
446 /* Push all registers that might point into the heap. Frame */
447 /* pointer registers are included in case client code was */
448 /* compiled with the 'omit frame pointer' optimisation. */
449 # define PUSH1(reg) GC_push_one((word)context.reg)
450 # define PUSH2(r1,r2) PUSH1(r1), PUSH1(r2)
451 # define PUSH4(r1,r2,r3,r4) PUSH2(r1,r2), PUSH2(r3,r4)
452 # if defined(I386)
453 PUSH4(Edi,Esi,Ebx,Edx), PUSH2(Ecx,Eax), PUSH1(Ebp);
454 sp = (ptr_t)context.Esp;
455 # elif defined(X86_64)
456 PUSH4(Rax,Rcx,Rdx,Rbx); PUSH2(Rbp, Rsi); PUSH1(Rdi);
457 PUSH4(R8, R9, R10, R11); PUSH4(R12, R13, R14, R15);
458 sp = (ptr_t)context.Rsp;
459 # elif defined(ARM32)
460 PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11),PUSH1(R12);
461 sp = (ptr_t)context.Sp;
462 # elif defined(SHx)
463 PUSH4(R0,R1,R2,R3), PUSH4(R4,R5,R6,R7), PUSH4(R8,R9,R10,R11);
464 PUSH2(R12,R13), PUSH1(R14);
465 sp = (ptr_t)context.R15;
466 # elif defined(MIPS)
467 PUSH4(IntAt,IntV0,IntV1,IntA0), PUSH4(IntA1,IntA2,IntA3,IntT0);
468 PUSH4(IntT1,IntT2,IntT3,IntT4), PUSH4(IntT5,IntT6,IntT7,IntS0);
469 PUSH4(IntS1,IntS2,IntS3,IntS4), PUSH4(IntS5,IntS6,IntS7,IntT8);
470 PUSH4(IntT9,IntK0,IntK1,IntS8);
471 sp = (ptr_t)context.IntSp;
472 # elif defined(PPC)
473 PUSH4(Gpr0, Gpr3, Gpr4, Gpr5), PUSH4(Gpr6, Gpr7, Gpr8, Gpr9);
474 PUSH4(Gpr10,Gpr11,Gpr12,Gpr14), PUSH4(Gpr15,Gpr16,Gpr17,Gpr18);
475 PUSH4(Gpr19,Gpr20,Gpr21,Gpr22), PUSH4(Gpr23,Gpr24,Gpr25,Gpr26);
476 PUSH4(Gpr27,Gpr28,Gpr29,Gpr30), PUSH1(Gpr31);
477 sp = (ptr_t)context.Gpr1;
478 # elif defined(ALPHA)
479 PUSH4(IntV0,IntT0,IntT1,IntT2), PUSH4(IntT3,IntT4,IntT5,IntT6);
480 PUSH4(IntT7,IntS0,IntS1,IntS2), PUSH4(IntS3,IntS4,IntS5,IntFp);
481 PUSH4(IntA0,IntA1,IntA2,IntA3), PUSH4(IntA4,IntA5,IntT8,IntT9);
482 PUSH4(IntT10,IntT11,IntT12,IntAt);
483 sp = (ptr_t)context.IntSp;
484 # else
485 # error "architecture is not supported"
486 # endif
489 stack_min = GC_get_stack_min(thread->stack_base);
491 if (sp >= stack_min && sp < thread->stack_base)
492 GC_push_all_stack(sp, thread->stack_base);
493 else {
494 WARN("Thread stack pointer 0x%lx out of range, pushing everything\n",
495 (unsigned long)sp);
496 GC_push_all_stack(stack_min, thread->stack_base);
500 if (!found_me) ABORT("Collecting from unknown thread.");
503 void GC_get_next_stack(char *start, char **lo, char **hi)
505 int i;
506 # define ADDR_LIMIT (char *)(-1L)
507 char * current_min = ADDR_LIMIT;
508 LONG my_max = GC_get_max_thread_index();
510 for (i = 0; i <= my_max; i++) {
511 char * s = (char *)thread_table[i].stack_base;
513 if (0 != s && s > start && s < current_min) {
514 current_min = s;
517 *hi = current_min;
518 if (current_min == ADDR_LIMIT) {
519 *lo = ADDR_LIMIT;
520 return;
522 *lo = GC_get_stack_min(current_min);
523 if (*lo < start) *lo = start;
526 #if !defined(CYGWIN32)
528 #if !defined(MSWINCE) && defined(GC_DLL)
530 /* We register threads from DllMain */
532 GC_API HANDLE WINAPI GC_CreateThread(
533 LPSECURITY_ATTRIBUTES lpThreadAttributes,
534 DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
535 LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
537 return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
538 lpParameter, dwCreationFlags, lpThreadId);
541 #else /* defined(MSWINCE) || !defined(GC_DLL)) */
543 /* We have no DllMain to take care of new threads. Thus we */
544 /* must properly intercept thread creation. */
546 typedef struct {
547 LPTHREAD_START_ROUTINE start;
548 LPVOID param;
549 } thread_args;
551 static DWORD WINAPI thread_start(LPVOID arg);
553 GC_API HANDLE WINAPI GC_CreateThread(
554 LPSECURITY_ATTRIBUTES lpThreadAttributes,
555 DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
556 LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
558 HANDLE thread_h = NULL;
560 thread_args *args;
562 if (!GC_is_initialized) GC_init();
563 /* make sure GC is initialized (i.e. main thread is attached) */
565 args = GC_malloc_uncollectable(sizeof(thread_args));
566 /* Handed off to and deallocated by child thread. */
567 if (0 == args) {
568 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
569 return NULL;
572 /* set up thread arguments */
573 args -> start = lpStartAddress;
574 args -> param = lpParameter;
576 thread_h = CreateThread(lpThreadAttributes,
577 dwStackSize, thread_start,
578 args, dwCreationFlags,
579 lpThreadId);
581 return thread_h;
584 static DWORD WINAPI thread_start(LPVOID arg)
586 DWORD ret = 0;
587 thread_args *args = (thread_args *)arg;
589 GC_new_thread();
591 /* Clear the thread entry even if we exit with an exception. */
592 /* This is probably pointless, since an uncaught exception is */
593 /* supposed to result in the process being killed. */
594 #ifndef __GNUC__
595 __try {
596 #endif /* __GNUC__ */
597 ret = args->start (args->param);
598 #ifndef __GNUC__
599 } __finally {
600 #endif /* __GNUC__ */
601 GC_free(args);
602 GC_delete_thread(GetCurrentThreadId());
603 #ifndef __GNUC__
605 #endif /* __GNUC__ */
607 return ret;
609 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
611 #endif /* !CYGWIN32 */
613 #ifdef MSWINCE
615 typedef struct {
616 HINSTANCE hInstance;
617 HINSTANCE hPrevInstance;
618 LPWSTR lpCmdLine;
619 int nShowCmd;
620 } main_thread_args;
622 DWORD WINAPI main_thread_start(LPVOID arg);
624 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
625 LPWSTR lpCmdLine, int nShowCmd)
627 DWORD exit_code = 1;
629 main_thread_args args = {
630 hInstance, hPrevInstance, lpCmdLine, nShowCmd
632 HANDLE thread_h;
633 DWORD thread_id;
635 /* initialize everything */
636 GC_init();
638 /* start the main thread */
639 thread_h = GC_CreateThread(
640 NULL, 0, main_thread_start, &args, 0, &thread_id);
642 if (thread_h != NULL)
644 WaitForSingleObject (thread_h, INFINITE);
645 GetExitCodeThread (thread_h, &exit_code);
646 CloseHandle (thread_h);
649 GC_deinit();
650 DeleteCriticalSection(&GC_allocate_ml);
652 return (int) exit_code;
655 DWORD WINAPI main_thread_start(LPVOID arg)
657 main_thread_args * args = (main_thread_args *) arg;
659 return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance,
660 args->lpCmdLine, args->nShowCmd);
663 # else /* !MSWINCE */
665 /* Called by GC_init() - we hold the allocation lock. */
666 void GC_thr_init() {
667 if (GC_thr_initialized) return;
668 GC_main_thread = GetCurrentThreadId();
669 GC_thr_initialized = TRUE;
671 /* Add the initial thread, so we can stop it. */
672 GC_new_thread();
675 #ifdef CYGWIN32
677 struct start_info {
678 void *(*start_routine)(void *);
679 void *arg;
680 GC_bool detached;
683 int GC_pthread_join(pthread_t pthread_id, void **retval) {
684 int result;
685 int i;
686 GC_thread me;
688 # if DEBUG_CYGWIN_THREADS
689 GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n",
690 (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);
691 # endif
693 /* Thread being joined might not have registered itself yet. */
694 /* After the join,thread id may have been recycled. */
695 /* FIXME: It would be better if this worked more like */
696 /* pthread_support.c. */
698 while ((me = GC_lookup_thread(pthread_id)) == 0) Sleep(10);
700 result = pthread_join(pthread_id, retval);
702 GC_delete_gc_thread(me);
704 # if DEBUG_CYGWIN_THREADS
705 GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n",
706 (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);
707 # endif
709 return result;
712 /* Cygwin-pthreads calls CreateThread internally, but it's not
713 * easily interceptible by us..
714 * so intercept pthread_create instead
717 GC_pthread_create(pthread_t *new_thread,
718 const pthread_attr_t *attr,
719 void *(*start_routine)(void *), void *arg) {
720 int result;
721 struct start_info * si;
723 if (!GC_is_initialized) GC_init();
724 /* make sure GC is initialized (i.e. main thread is attached) */
726 /* This is otherwise saved only in an area mmapped by the thread */
727 /* library, which isn't visible to the collector. */
728 si = GC_malloc_uncollectable(sizeof(struct start_info));
729 if (0 == si) return(EAGAIN);
731 si -> start_routine = start_routine;
732 si -> arg = arg;
733 if (attr != 0 &&
734 pthread_attr_getdetachstate(attr, &si->detached)
735 == PTHREAD_CREATE_DETACHED) {
736 si->detached = TRUE;
739 # if DEBUG_CYGWIN_THREADS
740 GC_printf2("About to create a thread from 0x%x(0x%x)\n",
741 (int)pthread_self(), GetCurrentThreadId);
742 # endif
743 result = pthread_create(new_thread, attr, GC_start_routine, si);
745 if (result) { /* failure */
746 GC_free(si);
749 return(result);
752 void * GC_start_routine(void * arg)
754 struct start_info * si = arg;
755 void * result;
756 void *(*start)(void *);
757 void *start_arg;
758 pthread_t pthread_id;
759 GC_thread me;
760 GC_bool detached;
761 int i;
763 # if DEBUG_CYGWIN_THREADS
764 GC_printf2("thread 0x%x(0x%x) starting...\n",(int)pthread_self(),
765 GetCurrentThreadId());
766 # endif
768 /* If a GC occurs before the thread is registered, that GC will */
769 /* ignore this thread. That's fine, since it will block trying to */
770 /* acquire the allocation lock, and won't yet hold interesting */
771 /* pointers. */
772 LOCK();
773 /* We register the thread here instead of in the parent, so that */
774 /* we don't need to hold the allocation lock during pthread_create. */
775 me = GC_new_thread();
776 UNLOCK();
778 start = si -> start_routine;
779 start_arg = si -> arg;
780 if (si-> detached) me -> flags |= DETACHED;
781 me -> pthread_id = pthread_id = pthread_self();
783 GC_free(si); /* was allocated uncollectable */
785 pthread_cleanup_push(GC_thread_exit_proc, (void *)me);
786 result = (*start)(start_arg);
787 me -> status = result;
788 pthread_cleanup_pop(0);
790 # if DEBUG_CYGWIN_THREADS
791 GC_printf2("thread 0x%x(0x%x) returned from start routine.\n",
792 (int)pthread_self(),GetCurrentThreadId());
793 # endif
795 return(result);
798 void GC_thread_exit_proc(void *arg)
800 GC_thread me = (GC_thread)arg;
801 int i;
803 # if DEBUG_CYGWIN_THREADS
804 GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",
805 (int)pthread_self(),GetCurrentThreadId());
806 # endif
808 LOCK();
809 if (me -> flags & DETACHED) {
810 GC_delete_thread(GetCurrentThreadId());
811 } else {
812 /* deallocate it as part of join */
813 me -> flags |= FINISHED;
815 UNLOCK();
818 /* nothing required here... */
819 int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) {
820 return pthread_sigmask(how, set, oset);
823 int GC_pthread_detach(pthread_t thread)
825 int result;
826 GC_thread thread_gc_id;
828 LOCK();
829 thread_gc_id = GC_lookup_thread(thread);
830 UNLOCK();
831 result = pthread_detach(thread);
832 if (result == 0) {
833 LOCK();
834 thread_gc_id -> flags |= DETACHED;
835 /* Here the pthread thread id may have been recycled. */
836 if (thread_gc_id -> flags & FINISHED) {
837 GC_delete_gc_thread(thread_gc_id);
839 UNLOCK();
841 return result;
844 #else /* !CYGWIN32 */
847 * We avoid acquiring locks here, since this doesn't seem to be preemptable.
848 * Pontus Rydin suggests wrapping the thread start routine instead.
850 #if defined(GC_DLL) || defined(GC_INSIDE_DLL)
851 BOOL WINAPI GC_DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
853 switch (reason) {
854 case DLL_PROCESS_ATTACH:
855 GC_init(); /* Force initialization before thread attach. */
856 /* fall through */
857 case DLL_THREAD_ATTACH:
858 GC_ASSERT(GC_thr_initialized);
859 if (GC_main_thread != GetCurrentThreadId()) {
860 GC_new_thread();
861 } /* o.w. we already did it during GC_thr_init(), called by GC_init() */
862 break;
864 case DLL_THREAD_DETACH:
865 GC_delete_thread(GetCurrentThreadId());
866 break;
868 case DLL_PROCESS_DETACH:
870 int i;
872 LOCK();
873 for (i = 0; i <= GC_get_max_thread_index(); ++i)
875 if (thread_table[i].in_use)
876 GC_delete_gc_thread(thread_table + i);
878 UNLOCK();
880 GC_deinit();
881 DeleteCriticalSection(&GC_allocate_ml);
883 break;
886 return TRUE;
888 #endif /* GC_DLL */
889 #endif /* !CYGWIN32 */
891 # endif /* !MSWINCE */
893 #endif /* GC_WIN32_THREADS */