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