root/gc/win32_threads.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GC_new_thread
  2. GC_get_max_thread_index
  3. GC_delete_gc_thread
  4. GC_delete_thread
  5. GC_lookup_thread
  6. GC_PROTO
  7. GC_stop_world
  8. GC_start_world
  9. GC_current_stackbottom
  10. GC_get_stack_min
  11. GC_push_all_stacks
  12. GC_get_next_stack
  13. GC_CreateThread
  14. thread_args
  15. GC_CreateThread
  16. thread_start
  17. main_thread_args
  18. WinMain
  19. main_thread_start
  20. GC_thr_init
  21. GC_pthread_join
  22. GC_pthread_create
  23. GC_start_routine
  24. GC_thread_exit_proc
  25. GC_pthread_sigmask
  26. GC_pthread_detach
  27. DllMain

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

/* [<][>][^][v][top][bottom][index][help] */