Back to index

plt-scheme  4.2.1
win32_threads.c
Go to the documentation of this file.
00001 #include "private/gc_priv.h"
00002 
00003 #if defined(GC_WIN32_THREADS) 
00004 
00005 #include <windows.h>
00006 
00007 #ifdef CYGWIN32
00008 # include <errno.h>
00009 
00010  /* Cygwin-specific forward decls */
00011 # undef pthread_create 
00012 # undef pthread_sigmask 
00013 # undef pthread_join 
00014 # undef pthread_detach
00015 # undef dlopen 
00016 
00017 # define DEBUG_CYGWIN_THREADS 0
00018 
00019   void * GC_start_routine(void * arg);
00020   void GC_thread_exit_proc(void *arg);
00021 
00022 #endif
00023 
00024 /* The type of the first argument to InterlockedExchange.      */
00025 /* Documented to be LONG volatile *, but at least gcc likes    */
00026 /* this better.                                                */
00027 typedef LONG * IE_t;
00028 
00029 #ifndef MAX_THREADS
00030 # define MAX_THREADS 256
00031     /* FIXME:                                           */
00032     /* Things may get quite slow for large numbers of threads, */
00033     /* since we look them up with sequential search.           */
00034 #endif
00035 
00036 GC_bool GC_thr_initialized = FALSE;
00037 
00038 DWORD GC_main_thread = 0;
00039 
00040 struct GC_thread_Rep {
00041   LONG in_use; /* Updated without lock.   */
00042                      /* We assert that unused    */
00043                      /* entries have invalid ids of     */
00044                      /* zero and zero stack fields.  */
00045   DWORD id;
00046   HANDLE handle;
00047   ptr_t stack_base;  /* The cold end of the stack.   */
00048                      /* 0 ==> entry not valid.   */
00049                      /* !in_use ==> stack_base == 0     */
00050   GC_bool suspended;
00051 
00052 # ifdef CYGWIN32
00053     void *status; /* hold exit value until join in case it's a pointer */
00054     pthread_t pthread_id;
00055     short flags;            /* Protected by GC lock.    */
00056 #      define FINISHED 1    /* Thread has exited.       */
00057 #      define DETACHED 2    /* Thread is intended to be detached.     */
00058 # endif
00059 };
00060 
00061 typedef volatile struct GC_thread_Rep * GC_thread;
00062 
00063 /*
00064  * We generally assume that volatile ==> memory ordering, at least among
00065  * volatiles.
00066  */
00067 
00068 volatile GC_bool GC_please_stop = FALSE;
00069 
00070 volatile struct GC_thread_Rep thread_table[MAX_THREADS];
00071 
00072 volatile LONG GC_max_thread_index = 0; /* Largest index in thread_table      */
00073                                    /* that was ever used.             */
00074 
00075 extern LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
00076 
00077 /*
00078  * This may be called from DllMain, and hence operates under unusual
00079  * constraints.
00080  */
00081 static GC_thread GC_new_thread(void) {
00082   int i;
00083   /* It appears to be unsafe to acquire a lock here, since this       */
00084   /* code is apparently not preeemptible on some systems.      */
00085   /* (This is based on complaints, not on Microsoft's official */
00086   /* documentation, which says this should perform "only simple       */
00087   /* initialization tasks".)                                   */
00088   /* Hence we make do with nonblocking synchronization.        */
00089 
00090   /* The following should be a noop according to the win32     */
00091   /* documentation.  There is empirical evidence that it       */
00092   /* isn't.          - HB                               */
00093 # if defined(MPROTECT_VDB)
00094    if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
00095 # endif
00096                 /* cast away volatile qualifier */
00097   for (i = 0; InterlockedExchange((IE_t)&thread_table[i].in_use,1) != 0; i++) {
00098     /* Compare-and-swap would make this cleaner, but that's not       */
00099     /* supported before Windows 98 and NT 4.0.  In Windows 2000,      */
00100     /* InterlockedExchange is supposed to be replaced by              */
00101     /* InterlockedExchangePointer, but that's not really what I              */
00102     /* want here.                                              */
00103     if (i == MAX_THREADS - 1)
00104       ABORT("too many threads");
00105   }
00106   /* Update GC_max_thread_index if necessary.  The following is safe, */
00107   /* and unlike CompareExchange-based solutions seems to work on all  */
00108   /* Windows95 and later platforms.                                   */
00109   /* Unfortunately, GC_max_thread_index may be temporarily out of     */
00110   /* bounds, so readers have to compensate.                           */
00111   while (i > GC_max_thread_index) {
00112     InterlockedIncrement((IE_t)&GC_max_thread_index);
00113   }
00114   if (GC_max_thread_index >= MAX_THREADS) {
00115     /* We overshot due to simultaneous increments.      */
00116     /* Setting it to MAX_THREADS-1 is always safe.      */
00117     GC_max_thread_index = MAX_THREADS - 1;
00118   }
00119   
00120 # ifdef CYGWIN32
00121     thread_table[i].pthread_id = pthread_self();
00122 # endif
00123   if (!DuplicateHandle(GetCurrentProcess(),
00124                       GetCurrentThread(),
00125                      GetCurrentProcess(),
00126                      (HANDLE*)&thread_table[i].handle,
00127                      0,
00128                      0,
00129                      DUPLICATE_SAME_ACCESS)) {
00130        DWORD last_error = GetLastError();
00131        GC_printf1("Last error code: %lx\n", last_error);
00132        ABORT("DuplicateHandle failed");
00133   }
00134   thread_table[i].stack_base = GC_get_stack_base();
00135   /* Up until this point, GC_push_all_stacks considers this thread    */
00136   /* invalid.                                                  */
00137   if (thread_table[i].stack_base == NULL) 
00138     ABORT("Failed to find stack base in GC_new_thread");
00139   /* Up until this point, this entry is viewed as reserved but invalid       */
00140   /* by GC_delete_thread.                                      */
00141   thread_table[i].id = GetCurrentThreadId();
00142   /* If this thread is being created while we are trying to stop      */
00143   /* the world, wait here.  Hopefully this can't happen on any */
00144   /* systems that don't allow us to block here.                */
00145   while (GC_please_stop) Sleep(20);
00146   return thread_table + i;
00147 }
00148 
00149 /*
00150  * GC_max_thread_index may temporarily be larger than MAX_THREADS.
00151  * To avoid subscript errors, we check on access.
00152  */
00153 #ifdef __GNUC__
00154 __inline__
00155 #endif
00156 LONG GC_get_max_thread_index()
00157 {
00158   LONG my_max = GC_max_thread_index;
00159 
00160   if (my_max >= MAX_THREADS) return MAX_THREADS-1;
00161   return my_max;
00162 }
00163 
00164 /* This is intended to be lock-free, though that               */
00165 /* assumes that the CloseHandle becomes visible before the            */
00166 /* in_use assignment.                                                 */
00167 static void GC_delete_gc_thread(GC_thread thr)
00168 {
00169     CloseHandle(thr->handle);
00170       /* cast away volatile qualifier */
00171     thr->stack_base = 0;
00172     thr->id = 0;
00173 #   ifdef CYGWIN32
00174       thr->pthread_id = 0;
00175 #   endif /* CYGWIN32 */
00176     thr->in_use = FALSE;
00177 }
00178 
00179 static void GC_delete_thread(DWORD thread_id) {
00180   int i;
00181   LONG my_max = GC_get_max_thread_index();
00182 
00183   for (i = 0;
00184        i <= my_max &&
00185        (!thread_table[i].in_use || thread_table[i].id != thread_id);
00186        /* Must still be in_use, since nobody else can store our thread_id. */
00187        i++) {}
00188   if (i > my_max) {
00189     WARN("Removing nonexistent thread %ld\n", (GC_word)thread_id);
00190   } else {
00191     GC_delete_gc_thread(thread_table+i);
00192   }
00193 }
00194 
00195 
00196 #ifdef CYGWIN32
00197 
00198 /* Return a GC_thread corresponding to a given pthread_t.      */
00199 /* Returns 0 if it's not there.                                */
00200 /* We assume that this is only called for pthread ids that     */
00201 /* have not yet terminated or are still joinable.              */
00202 static GC_thread GC_lookup_thread(pthread_t id)
00203 {
00204   int i;
00205   LONG my_max = GC_get_max_thread_index();
00206 
00207   for (i = 0;
00208        i <= my_max &&
00209        (!thread_table[i].in_use || thread_table[i].pthread_id != id
00210        || !thread_table[i].in_use);
00211        /* Must still be in_use, since nobody else can store our thread_id. */
00212        i++);
00213   if (i > my_max) return 0;
00214   return thread_table + i;
00215 }
00216 
00217 #endif /* CYGWIN32 */
00218 
00219 void GC_push_thread_structures GC_PROTO((void))
00220 {
00221     /* Unlike the other threads implementations, the thread table here       */
00222     /* contains no pointers to the collectable heap.  Thus we have    */
00223     /* no private structures we need to preserve.                     */
00224 # ifdef CYGWIN32
00225   { int i; /* pthreads may keep a pointer in the thread exit value */
00226     LONG my_max = GC_get_max_thread_index();
00227 
00228     for (i = 0; i <= my_max; i++)
00229       if (thread_table[i].in_use)
00230        GC_push_all((ptr_t)&(thread_table[i].status),
00231                     (ptr_t)(&(thread_table[i].status)+1));
00232   }
00233 # endif
00234 }
00235 
00236 /* Defined in misc.c */
00237 extern CRITICAL_SECTION GC_write_cs;
00238 
00239 void GC_stop_world()
00240 {
00241   DWORD thread_id = GetCurrentThreadId();
00242   int i;
00243 
00244   if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()");
00245 
00246   GC_please_stop = TRUE;
00247 # ifndef CYGWIN32
00248     EnterCriticalSection(&GC_write_cs);
00249 # endif /* !CYGWIN32 */
00250   for (i = 0; i <= GC_get_max_thread_index(); i++)
00251     if (thread_table[i].stack_base != 0
00252        && thread_table[i].id != thread_id) {
00253 #     ifdef MSWINCE
00254         /* SuspendThread will fail if thread is running kernel code */
00255        while (SuspendThread(thread_table[i].handle) == (DWORD)-1)
00256          Sleep(10);
00257 #     else
00258        /* Apparently the Windows 95 GetOpenFileName call creates      */
00259        /* a thread that does not properly get cleaned up, and         */
00260        /* SuspendThread on its descriptor may provoke a crash.        */
00261        /* This reduces the probability of that event, though it still */
00262        /* appears there's a race here.                                */
00263        DWORD exitCode; 
00264        if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&
00265             exitCode != STILL_ACTIVE) {
00266           thread_table[i].stack_base = 0; /* prevent stack from being pushed */
00267 #         ifndef CYGWIN32
00268             /* this breaks pthread_join on Cygwin, which is guaranteed to  */
00269            /* only see user pthreads                                     */
00270            thread_table[i].in_use = FALSE;
00271            CloseHandle(thread_table[i].handle);
00272 #         endif
00273          continue;
00274        }
00275        if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
00276          ABORT("SuspendThread failed");
00277 #     endif
00278       thread_table[i].suspended = TRUE;
00279     }
00280 # ifndef CYGWIN32
00281     LeaveCriticalSection(&GC_write_cs);
00282 # endif /* !CYGWIN32 */
00283 }
00284 
00285 void GC_start_world()
00286 {
00287   DWORD thread_id = GetCurrentThreadId();
00288   int i;
00289   LONG my_max = GC_get_max_thread_index();
00290 
00291   for (i = 0; i <= my_max; i++)
00292     if (thread_table[i].stack_base != 0 && thread_table[i].suspended
00293        && thread_table[i].id != thread_id) {
00294       if (ResumeThread(thread_table[i].handle) == (DWORD)-1)
00295        ABORT("ResumeThread failed");
00296       thread_table[i].suspended = FALSE;
00297     }
00298   GC_please_stop = FALSE;
00299 }
00300 
00301 # ifdef _MSC_VER
00302 #   pragma warning(disable:4715)
00303 # endif
00304 ptr_t GC_current_stackbottom()
00305 {
00306   DWORD thread_id = GetCurrentThreadId();
00307   int i;
00308   LONG my_max = GC_get_max_thread_index();
00309 
00310   for (i = 0; i <= my_max; i++)
00311     if (thread_table[i].stack_base && thread_table[i].id == thread_id)
00312       return thread_table[i].stack_base;
00313   ABORT("no thread table entry for current thread");
00314 }
00315 # ifdef _MSC_VER
00316 #   pragma warning(default:4715)
00317 # endif
00318 
00319 # ifdef MSWINCE
00320     /* The VirtualQuery calls below won't work properly on WinCE, but */
00321     /* since each stack is restricted to an aligned 64K region of     */
00322     /* virtual memory we can just take the next lowest multiple of 64K.      */
00323 #   define GC_get_stack_min(s) \
00324         ((ptr_t)(((DWORD)(s) - 1) & 0xFFFF0000))
00325 # else
00326     static ptr_t GC_get_stack_min(ptr_t s)
00327     {
00328        ptr_t bottom;
00329        MEMORY_BASIC_INFORMATION info;
00330        VirtualQuery(s, &info, sizeof(info));
00331        do {
00332            bottom = info.BaseAddress;
00333            VirtualQuery(bottom - 1, &info, sizeof(info));
00334        } while ((info.Protect & PAGE_READWRITE)
00335                && !(info.Protect & PAGE_GUARD));
00336        return(bottom);
00337     }
00338 # endif
00339 
00340 void GC_push_all_stacks()
00341 {
00342   DWORD thread_id = GetCurrentThreadId();
00343   GC_bool found_me = FALSE;
00344   int i;
00345   int dummy;
00346   ptr_t sp, stack_min;
00347   GC_thread thread;
00348   LONG my_max = GC_get_max_thread_index();
00349   
00350   for (i = 0; i <= my_max; i++) {
00351     thread = thread_table + i;
00352     if (thread -> in_use && thread -> stack_base) {
00353       if (thread -> id == thread_id) {
00354        sp = (ptr_t) &dummy;
00355        found_me = TRUE;
00356       } else {
00357         CONTEXT context;
00358         context.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL;
00359         if (!GetThreadContext(thread_table[i].handle, &context))
00360          ABORT("GetThreadContext failed");
00361 
00362         /* Push all registers that might point into the heap.  Frame  */
00363         /* pointer registers are included in case client code was     */
00364         /* compiled with the 'omit frame pointer' optimisation.              */
00365 #       define PUSH1(reg) GC_push_one((word)context.reg)
00366 #       define PUSH2(r1,r2) PUSH1(r1), PUSH1(r2)
00367 #       define PUSH4(r1,r2,r3,r4) PUSH2(r1,r2), PUSH2(r3,r4)
00368 #       if defined(I386)
00369           PUSH4(Edi,Esi,Ebx,Edx), PUSH2(Ecx,Eax), PUSH1(Ebp);
00370          sp = (ptr_t)context.Esp;
00371 #       elif defined(ARM32)
00372          PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11),PUSH1(R12);
00373          sp = (ptr_t)context.Sp;
00374 #       elif defined(SHx)
00375          PUSH4(R0,R1,R2,R3), PUSH4(R4,R5,R6,R7), PUSH4(R8,R9,R10,R11);
00376          PUSH2(R12,R13), PUSH1(R14);
00377          sp = (ptr_t)context.R15;
00378 #       elif defined(MIPS)
00379          PUSH4(IntAt,IntV0,IntV1,IntA0), PUSH4(IntA1,IntA2,IntA3,IntT0);
00380          PUSH4(IntT1,IntT2,IntT3,IntT4), PUSH4(IntT5,IntT6,IntT7,IntS0);
00381          PUSH4(IntS1,IntS2,IntS3,IntS4), PUSH4(IntS5,IntS6,IntS7,IntT8);
00382          PUSH4(IntT9,IntK0,IntK1,IntS8);
00383          sp = (ptr_t)context.IntSp;
00384 #       elif defined(PPC)
00385          PUSH4(Gpr0, Gpr3, Gpr4, Gpr5),  PUSH4(Gpr6, Gpr7, Gpr8, Gpr9);
00386          PUSH4(Gpr10,Gpr11,Gpr12,Gpr14), PUSH4(Gpr15,Gpr16,Gpr17,Gpr18);
00387          PUSH4(Gpr19,Gpr20,Gpr21,Gpr22), PUSH4(Gpr23,Gpr24,Gpr25,Gpr26);
00388          PUSH4(Gpr27,Gpr28,Gpr29,Gpr30), PUSH1(Gpr31);
00389          sp = (ptr_t)context.Gpr1;
00390 #       elif defined(ALPHA)
00391          PUSH4(IntV0,IntT0,IntT1,IntT2), PUSH4(IntT3,IntT4,IntT5,IntT6);
00392          PUSH4(IntT7,IntS0,IntS1,IntS2), PUSH4(IntS3,IntS4,IntS5,IntFp);
00393          PUSH4(IntA0,IntA1,IntA2,IntA3), PUSH4(IntA4,IntA5,IntT8,IntT9);
00394          PUSH4(IntT10,IntT11,IntT12,IntAt);
00395          sp = (ptr_t)context.IntSp;
00396 #       else
00397 #         error "architecture is not supported"
00398 #       endif
00399       }
00400 
00401       stack_min = GC_get_stack_min(thread->stack_base);
00402 
00403       if (sp >= stack_min && sp < thread->stack_base)
00404         GC_push_all_stack(sp, thread->stack_base);
00405       else {
00406         WARN("Thread stack pointer 0x%lx out of range, pushing everything\n",
00407             (unsigned long)sp);
00408         GC_push_all_stack(stack_min, thread->stack_base);
00409       }
00410     }
00411   }
00412   if (!found_me) ABORT("Collecting from unknown thread.");
00413 }
00414 
00415 void GC_get_next_stack(char *start, char **lo, char **hi)
00416 {
00417     int i;
00418 #   define ADDR_LIMIT (char *)(-1L)
00419     char * current_min = ADDR_LIMIT;
00420     LONG my_max = GC_get_max_thread_index();
00421   
00422     for (i = 0; i <= my_max; i++) {
00423        char * s = (char *)thread_table[i].stack_base;
00424 
00425        if (0 != s && s > start && s < current_min) {
00426            current_min = s;
00427        }
00428     }
00429     *hi = current_min;
00430     if (current_min == ADDR_LIMIT) {
00431        *lo = ADDR_LIMIT;
00432        return;
00433     }
00434     *lo = GC_get_stack_min(current_min);
00435     if (*lo < start) *lo = start;
00436 }
00437 
00438 #if !defined(CYGWIN32)
00439 
00440 #if !defined(MSWINCE) && defined(GC_DLL)
00441 
00442 /* We register threads from DllMain */
00443 
00444 GC_API HANDLE WINAPI GC_CreateThread(
00445     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
00446     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
00447     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
00448 {
00449     return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
00450                         lpParameter, dwCreationFlags, lpThreadId);
00451 }
00452 
00453 #else /* defined(MSWINCE) || !defined(GC_DLL))  */
00454 
00455 /* We have no DllMain to take care of new threads.  Thus we    */
00456 /* must properly intercept thread creation.                    */
00457 
00458 typedef struct {
00459     LPTHREAD_START_ROUTINE start;
00460     LPVOID param;
00461 } thread_args;
00462 
00463 static DWORD WINAPI thread_start(LPVOID arg);
00464 
00465 GC_API HANDLE WINAPI GC_CreateThread(
00466     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
00467     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
00468     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
00469 {
00470     HANDLE thread_h = NULL;
00471 
00472     thread_args *args;
00473 
00474     if (!GC_is_initialized) GC_init();
00475               /* make sure GC is initialized (i.e. main thread is attached) */
00476     
00477     args = GC_malloc_uncollectable(sizeof(thread_args)); 
00478        /* Handed off to and deallocated by child thread.       */
00479     if (0 == args) {
00480        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
00481         return NULL;
00482     }
00483 
00484     /* set up thread arguments */
00485        args -> start = lpStartAddress;
00486        args -> param = lpParameter;
00487 
00488     thread_h = CreateThread(lpThreadAttributes,
00489                          dwStackSize, thread_start,
00490                          args, dwCreationFlags,
00491                          lpThreadId);
00492 
00493     return thread_h;
00494 }
00495 
00496 static DWORD WINAPI thread_start(LPVOID arg)
00497 {
00498     DWORD ret = 0;
00499     thread_args *args = (thread_args *)arg;
00500 
00501     GC_new_thread();
00502 
00503     /* Clear the thread entry even if we exit with an exception.      */
00504     /* This is probably pointless, since an uncaught exception is     */
00505     /* supposed to result in the process being killed.                */
00506 #ifndef __GNUC__
00507     __try {
00508 #endif /* __GNUC__ */
00509        ret = args->start (args->param);
00510 #ifndef __GNUC__
00511     } __finally {
00512 #endif /* __GNUC__ */
00513        GC_free(args);
00514        GC_delete_thread(GetCurrentThreadId());
00515 #ifndef __GNUC__
00516     }
00517 #endif /* __GNUC__ */
00518 
00519     return ret;
00520 }
00521 #endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))  */
00522 
00523 #endif /* !CYGWIN32 */
00524 
00525 #ifdef MSWINCE
00526 
00527 typedef struct {
00528     HINSTANCE hInstance;
00529     HINSTANCE hPrevInstance;
00530     LPWSTR lpCmdLine;
00531     int nShowCmd;
00532 } main_thread_args;
00533 
00534 DWORD WINAPI main_thread_start(LPVOID arg);
00535 
00536 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
00537                  LPWSTR lpCmdLine, int nShowCmd)
00538 {
00539     DWORD exit_code = 1;
00540 
00541     main_thread_args args = {
00542        hInstance, hPrevInstance, lpCmdLine, nShowCmd
00543     };
00544     HANDLE thread_h;
00545     DWORD thread_id;
00546 
00547     /* initialize everything */
00548     GC_init();
00549 
00550     /* start the main thread */
00551     thread_h = GC_CreateThread(
00552        NULL, 0, main_thread_start, &args, 0, &thread_id);
00553 
00554     if (thread_h != NULL)
00555     {
00556        WaitForSingleObject (thread_h, INFINITE);
00557        GetExitCodeThread (thread_h, &exit_code);
00558        CloseHandle (thread_h);
00559     }
00560 
00561     GC_deinit();
00562     DeleteCriticalSection(&GC_allocate_ml);
00563 
00564     return (int) exit_code;
00565 }
00566 
00567 DWORD WINAPI main_thread_start(LPVOID arg)
00568 {
00569     main_thread_args * args = (main_thread_args *) arg;
00570 
00571     return (DWORD) GC_WinMain (args->hInstance, args->hPrevInstance,
00572                             args->lpCmdLine, args->nShowCmd);
00573 }
00574 
00575 # else /* !MSWINCE */
00576 
00577 /* Called by GC_init() - we hold the allocation lock.   */
00578 void GC_thr_init() {
00579     if (GC_thr_initialized) return;
00580     GC_main_thread = GetCurrentThreadId();
00581     GC_thr_initialized = TRUE;
00582 
00583     /* Add the initial thread, so we can stop it.       */
00584     GC_new_thread();
00585 }
00586 
00587 #ifdef CYGWIN32
00588 
00589 struct start_info {
00590     void *(*start_routine)(void *);
00591     void *arg;
00592     GC_bool detached;
00593 };
00594 
00595 int GC_pthread_join(pthread_t pthread_id, void **retval) {
00596     int result;
00597     int i;
00598     GC_thread me;
00599 
00600 #   if DEBUG_CYGWIN_THREADS
00601       GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n",
00602                (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);
00603 #   endif
00604 
00605     /* Thread being joined might not have registered itself yet. */
00606     /* After the join,thread id may have been recycled.         */
00607     /* FIXME: It would be better if this worked more like       */
00608     /* pthread_support.c.                                */
00609 
00610     while ((me = GC_lookup_thread(pthread_id)) == 0) Sleep(10);
00611 
00612     result = pthread_join(pthread_id, retval);
00613 
00614     GC_delete_gc_thread(me);
00615 
00616 #   if DEBUG_CYGWIN_THREADS
00617       GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n",
00618                (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);
00619 #   endif
00620 
00621     return result;
00622 }
00623 
00624 /* Cygwin-pthreads calls CreateThread internally, but it's not
00625  * easily interceptible by us..
00626  *   so intercept pthread_create instead
00627  */
00628 int
00629 GC_pthread_create(pthread_t *new_thread,
00630                 const pthread_attr_t *attr,
00631                   void *(*start_routine)(void *), void *arg) {
00632     int result;
00633     struct start_info * si;
00634 
00635     if (!GC_is_initialized) GC_init();
00636               /* make sure GC is initialized (i.e. main thread is attached) */
00637     
00638     /* This is otherwise saved only in an area mmapped by the thread */
00639     /* library, which isn't visible to the collector.           */
00640     si = GC_malloc_uncollectable(sizeof(struct start_info)); 
00641     if (0 == si) return(EAGAIN);
00642 
00643     si -> start_routine = start_routine;
00644     si -> arg = arg;
00645     if (attr != 0 &&
00646         pthread_attr_getdetachstate(attr, &si->detached)
00647        == PTHREAD_CREATE_DETACHED) {
00648       si->detached = TRUE;
00649     }
00650 
00651 #   if DEBUG_CYGWIN_THREADS
00652       GC_printf2("About to create a thread from 0x%x(0x%x)\n",
00653                (int)pthread_self(), GetCurrentThreadId);
00654 #   endif
00655     result = pthread_create(new_thread, attr, GC_start_routine, si); 
00656 
00657     if (result) { /* failure */
00658        GC_free(si);
00659     } 
00660 
00661     return(result);
00662 }
00663 
00664 void * GC_start_routine(void * arg)
00665 {
00666     struct start_info * si = arg;
00667     void * result;
00668     void *(*start)(void *);
00669     void *start_arg;
00670     pthread_t pthread_id;
00671     GC_thread me;
00672     GC_bool detached;
00673     int i;
00674 
00675 #   if DEBUG_CYGWIN_THREADS
00676       GC_printf2("thread 0x%x(0x%x) starting...\n",(int)pthread_self(),
00677                                              GetCurrentThreadId());
00678 #   endif
00679 
00680     /* If a GC occurs before the thread is registered, that GC will   */
00681     /* ignore this thread.  That's fine, since it will block trying to  */
00682     /* acquire the allocation lock, and won't yet hold interesting    */
00683     /* pointers.                                               */
00684     LOCK();
00685     /* We register the thread here instead of in the parent, so that  */
00686     /* we don't need to hold the allocation lock during pthread_create. */
00687     me = GC_new_thread();
00688     UNLOCK();
00689 
00690     start = si -> start_routine;
00691     start_arg = si -> arg;
00692     if (si-> detached) me -> flags |= DETACHED;
00693     me -> pthread_id = pthread_id = pthread_self();
00694 
00695     GC_free(si); /* was allocated uncollectable */
00696 
00697     pthread_cleanup_push(GC_thread_exit_proc, (void *)me);
00698     result = (*start)(start_arg);
00699     me -> status = result;
00700     pthread_cleanup_pop(0);
00701 
00702 #   if DEBUG_CYGWIN_THREADS
00703       GC_printf2("thread 0x%x(0x%x) returned from start routine.\n",
00704                (int)pthread_self(),GetCurrentThreadId());
00705 #   endif
00706 
00707     return(result);
00708 }
00709 
00710 void GC_thread_exit_proc(void *arg)
00711 {
00712     GC_thread me = (GC_thread)arg;
00713     int i;
00714 
00715 #   if DEBUG_CYGWIN_THREADS
00716       GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",
00717                (int)pthread_self(),GetCurrentThreadId());
00718 #   endif
00719 
00720     LOCK();
00721     if (me -> flags & DETACHED) {
00722       GC_delete_thread(GetCurrentThreadId());
00723     } else {
00724       /* deallocate it as part of join */
00725       me -> flags |= FINISHED;
00726     }
00727     UNLOCK();
00728 }
00729 
00730 /* nothing required here... */
00731 int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) {
00732   return pthread_sigmask(how, set, oset);
00733 }
00734 
00735 int GC_pthread_detach(pthread_t thread)
00736 {
00737     int result;
00738     GC_thread thread_gc_id;
00739     
00740     LOCK();
00741     thread_gc_id = GC_lookup_thread(thread);
00742     UNLOCK();
00743     result = pthread_detach(thread);
00744     if (result == 0) {
00745       LOCK();
00746       thread_gc_id -> flags |= DETACHED;
00747       /* Here the pthread thread id may have been recycled. */
00748       if (thread_gc_id -> flags & FINISHED) {
00749         GC_delete_gc_thread(thread_gc_id);
00750       }
00751       UNLOCK();
00752     }
00753     return result;
00754 }
00755 
00756 #else /* !CYGWIN32 */
00757 
00758 /*
00759  * We avoid acquiring locks here, since this doesn't seem to be preemptable.
00760  * Pontus Rydin suggests wrapping the thread start routine instead.
00761  */
00762 #ifdef GC_DLL
00763 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
00764 {
00765   switch (reason) {
00766   case DLL_PROCESS_ATTACH:
00767     GC_init();       /* Force initialization before thread attach.    */
00768     /* fall through */
00769   case DLL_THREAD_ATTACH:
00770     GC_ASSERT(GC_thr_initialized);
00771     if (GC_main_thread != GetCurrentThreadId()) {
00772         GC_new_thread();
00773     } /* o.w. we already did it during GC_thr_init(), called by GC_init() */
00774     break;
00775 
00776   case DLL_THREAD_DETACH:
00777     GC_delete_thread(GetCurrentThreadId());
00778     break;
00779 
00780   case DLL_PROCESS_DETACH:
00781     {
00782       int i;
00783 
00784       LOCK();
00785       for (i = 0; i <= GC_get_max_thread_index(); ++i)
00786       {
00787           if (thread_table[i].in_use)
00788            GC_delete_gc_thread(thread_table + i);
00789       }
00790       UNLOCK();
00791 
00792       GC_deinit();
00793       DeleteCriticalSection(&GC_allocate_ml);
00794     }
00795     break;
00796 
00797   }
00798   return TRUE;
00799 }
00800 #endif /* GC_DLL */
00801 #endif /* !CYGWIN32 */
00802 
00803 # endif /* !MSWINCE */
00804 
00805 #endif /* GC_WIN32_THREADS */