OSDN Git Service

* dyn_load.c: On NetBSD, include machine/elf_machdep.h and define
[pf3gnuchains/gcc-fork.git] / boehm-gc / win32_threads.c
index 980faf3..5604290 100644 (file)
-#if defined(GC_WIN32_THREADS)
-
 #include "private/gc_priv.h"
 
-#if 0
-#define STRICT
+#if defined(GC_WIN32_THREADS) 
+
 #include <windows.h>
+
+#ifdef CYGWIN32
+# include <errno.h>
+
+ /* Cygwin-specific forward decls */
+# undef pthread_create 
+# undef pthread_sigmask 
+# undef pthread_join 
+# undef dlopen 
+
+# define DEBUG_CYGWIN_THREADS 0
+
+  void * GC_start_routine(void * arg);
+  void GC_thread_exit_proc(void *arg);
+
 #endif
 
-#define MAX_THREADS 64
+/* The type of the first argument to InterlockedExchange.      */
+/* Documented to be LONG volatile *, but at least gcc likes    */
+/* this better.                                                        */
+typedef LONG * IE_t;
+
+#ifndef MAX_THREADS
+# define MAX_THREADS 256
+    /* FIXME:                                                  */
+    /* Things may get quite slow for large numbers of threads, */
+    /* since we look them up with sequential search.           */
+#endif
+
+GC_bool GC_thr_initialized = FALSE;
+
+DWORD GC_main_thread = 0;
 
-struct thread_entry {
-  LONG in_use;
+struct GC_thread_Rep {
+  LONG in_use; /* Updated without lock.        */
+                       /* We assert that unused        */
+                       /* entries have invalid ids of  */
+                       /* zero and zero stack fields.  */
   DWORD id;
   HANDLE handle;
-  void *stack;         /* The cold end of the stack.   */
+  ptr_t stack_base;    /* The cold end of the stack.   */
                        /* 0 ==> entry not valid.       */
-                       /* !in_use ==> stack == 0       */
-  CONTEXT context;
+                       /* !in_use ==> stack_base == 0  */
   GC_bool suspended;
+
+# ifdef CYGWIN32
+    void *status; /* hold exit value until join in case it's a pointer */
+    pthread_t pthread_id;
+    short flags;               /* Protected by GC lock.        */
+#      define FINISHED 1       /* Thread has exited.   */
+#      define DETACHED 2       /* Thread is intended to be detached.   */
+# endif
 };
 
+typedef volatile struct GC_thread_Rep * GC_thread;
+
+/*
+ * We generally assume that volatile ==> memory ordering, at least among
+ * volatiles.
+ */
+
 volatile GC_bool GC_please_stop = FALSE;
 
-volatile struct thread_entry thread_table[MAX_THREADS];
+volatile struct GC_thread_Rep thread_table[MAX_THREADS];
+
+volatile LONG GC_max_thread_index = 0; /* Largest index in thread_table        */
+                                      /* that was ever used.           */
+
+extern LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
+
+/*
+ * This may be called from DllMain, and hence operates under unusual
+ * constraints.
+ */
+static GC_thread GC_new_thread(void) {
+  int i;
+  /* It appears to be unsafe to acquire a lock here, since this        */
+  /* code is apparently not preeemptible on some systems.      */
+  /* (This is based on complaints, not on Microsoft's official */
+  /* documentation, which says this should perform "only simple        */
+  /* initialization tasks".)                                   */
+  /* Hence we make do with nonblocking synchronization.                */
+
+  /* The following should be a noop according to the win32     */
+  /* documentation.  There is empirical evidence that it       */
+  /* isn't.            - HB                                    */
+# if defined(MPROTECT_VDB)
+   if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
+# endif
+                /* cast away volatile qualifier */
+  for (i = 0; InterlockedExchange((IE_t)&thread_table[i].in_use,1) != 0; i++) {
+    /* Compare-and-swap would make this cleaner, but that's not        */
+    /* supported before Windows 98 and NT 4.0.  In Windows 2000,       */
+    /* InterlockedExchange is supposed to be replaced by               */
+    /* InterlockedExchangePointer, but that's not really what I                */
+    /* want here.                                                      */
+    if (i == MAX_THREADS - 1)
+      ABORT("too many threads");
+  }
+  /* Update GC_max_thread_index if necessary.  The following is safe,  */
+  /* and unlike CompareExchange-based solutions seems to work on all   */
+  /* Windows95 and later platforms.                                    */
+  /* Unfortunately, GC_max_thread_index may be temporarily out of      */
+  /* bounds, so readers have to compensate.                            */
+  while (i > GC_max_thread_index) {
+    InterlockedIncrement((IE_t)&GC_max_thread_index);
+  }
+  if (GC_max_thread_index >= MAX_THREADS) {
+    /* We overshot due to simultaneous increments.     */
+    /* Setting it to MAX_THREADS-1 is always safe.     */
+    GC_max_thread_index = MAX_THREADS - 1;
+  }
+  
+# ifdef CYGWIN32
+    thread_table[i].pthread_id = pthread_self();
+# endif
+  if (!DuplicateHandle(GetCurrentProcess(),
+                      GetCurrentThread(),
+                      GetCurrentProcess(),
+                      (HANDLE*)&thread_table[i].handle,
+                      0,
+                      0,
+                      DUPLICATE_SAME_ACCESS)) {
+       DWORD last_error = GetLastError();
+       GC_printf1("Last error code: %lx\n", last_error);
+       ABORT("DuplicateHandle failed");
+  }
+  thread_table[i].stack_base = GC_get_stack_base();
+  /* Up until this point, GC_push_all_stacks considers this thread     */
+  /* invalid.                                                          */
+  if (thread_table[i].stack_base == NULL) 
+    ABORT("Failed to find stack base in GC_new_thread");
+  /* Up until this point, this entry is viewed as reserved but invalid */
+  /* by GC_delete_thread.                                              */
+  thread_table[i].id = GetCurrentThreadId();
+  /* If this thread is being created while we are trying to stop       */
+  /* the world, wait here.  Hopefully this can't happen on any */
+  /* systems that don't allow us to block here.                        */
+  while (GC_please_stop) Sleep(20);
+  return thread_table + i;
+}
+
+/*
+ * GC_max_thread_index may temporarily be larger than MAX_THREADS.
+ * To avoid subscript errors, we check on access.
+ */
+#ifdef __GNUC__
+__inline__
+#endif
+LONG GC_get_max_thread_index()
+{
+  LONG my_max = GC_max_thread_index;
+
+  if (my_max >= MAX_THREADS) return MAX_THREADS-1;
+  return my_max;
+}
+
+/* This is intended to be lock-free, though that                       */
+/* assumes that the CloseHandle becomes visible before the             */
+/* in_use assignment.                                                  */
+static void GC_delete_gc_thread(GC_thread thr)
+{
+    CloseHandle(thr->handle);
+      /* cast away volatile qualifier */
+    thr->stack_base = 0;
+    thr->id = 0;
+#   ifdef CYGWIN32
+      thr->pthread_id = 0;
+#   endif /* CYGWIN32 */
+    thr->in_use = FALSE;
+}
+
+static void GC_delete_thread(DWORD thread_id) {
+  int i;
+  LONG my_max = GC_get_max_thread_index();
+
+  for (i = 0;
+       i <= my_max &&
+       (!thread_table[i].in_use || thread_table[i].id != thread_id);
+       /* Must still be in_use, since nobody else can store our thread_id. */
+       i++) {}
+  if (i > my_max) {
+    WARN("Removing nonexisiting thread %ld\n", (GC_word)thread_id);
+  } else {
+    GC_delete_gc_thread(thread_table+i);
+  }
+}
+
+
+#ifdef CYGWIN32
+
+/* Return a GC_thread corresponding to a given pthread_t.      */
+/* Returns 0 if it's not there.                                        */
+/* We assume that this is only called for pthread ids that     */
+/* have not yet terminated or are still joinable.              */
+static GC_thread GC_lookup_thread(pthread_t id)
+{
+  int i;
+  LONG my_max = GC_get_max_thread_index();
+
+  for (i = 0;
+       i <= my_max &&
+       (!thread_table[i].in_use || thread_table[i].pthread_id != id
+       || !thread_table[i].in_use);
+       /* Must still be in_use, since nobody else can store our thread_id. */
+       i++);
+  if (i > my_max) return 0;
+  return thread_table + i;
+}
+
+#endif /* CYGWIN32 */
 
 void GC_push_thread_structures GC_PROTO((void))
 {
     /* Unlike the other threads implementations, the thread table here */
     /* contains no pointers to the collectable heap.  Thus we have     */
     /* no private structures we need to preserve.                      */
+# ifdef CYGWIN32
+  { int i; /* pthreads may keep a pointer in the thread exit value */
+    LONG my_max = GC_get_max_thread_index();
+
+    for (i = 0; i <= my_max; i++)
+      if (thread_table[i].in_use)
+       GC_push_all((ptr_t)&(thread_table[i].status),
+                    (ptr_t)(&(thread_table[i].status)+1));
+  }
+# endif
 }
 
 void GC_stop_world()
@@ -36,9 +237,11 @@ void GC_stop_world()
   DWORD thread_id = GetCurrentThreadId();
   int i;
 
+  if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()");
+
   GC_please_stop = TRUE;
-  for (i = 0; i < MAX_THREADS; i++)
-    if (thread_table[i].stack != 0
+  for (i = 0; i <= GC_get_max_thread_index(); i++)
+    if (thread_table[i].stack_base != 0
        && thread_table[i].id != thread_id) {
 #     ifdef MSWINCE
         /* SuspendThread will fail if thread is running kernel code */
@@ -53,11 +256,14 @@ void GC_stop_world()
        DWORD exitCode; 
        if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&
             exitCode != STILL_ACTIVE) {
-            thread_table[i].stack = 0;
+          thread_table[i].stack_base = 0; /* prevent stack from being pushed */
+#         ifndef CYGWIN32
+            /* this breaks pthread_join on Cygwin, which is guaranteed to  */
+           /* only see user pthreads                                      */
            thread_table[i].in_use = FALSE;
            CloseHandle(thread_table[i].handle);
-           BZERO((void *)(&thread_table[i].context), sizeof(CONTEXT));
-           continue;
+#         endif
+         continue;
        }
        if (SuspendThread(thread_table[i].handle) == (DWORD)-1)
          ABORT("SuspendThread failed");
@@ -70,8 +276,10 @@ void GC_start_world()
 {
   DWORD thread_id = GetCurrentThreadId();
   int i;
-  for (i = 0; i < MAX_THREADS; i++)
-    if (thread_table[i].stack != 0 && thread_table[i].suspended
+  LONG my_max = GC_get_max_thread_index();
+
+  for (i = 0; i <= my_max; i++)
+    if (thread_table[i].stack_base != 0 && thread_table[i].suspended
        && thread_table[i].id != thread_id) {
       if (ResumeThread(thread_table[i].handle) == (DWORD)-1)
        ABORT("ResumeThread failed");
@@ -87,9 +295,11 @@ ptr_t GC_current_stackbottom()
 {
   DWORD thread_id = GetCurrentThreadId();
   int i;
-  for (i = 0; i < MAX_THREADS; i++)
-    if (thread_table[i].stack && thread_table[i].id == thread_id)
-      return thread_table[i].stack;
+  LONG my_max = GC_get_max_thread_index();
+
+  for (i = 0; i <= my_max; i++)
+    if (thread_table[i].stack_base && thread_table[i].id == thread_id)
+      return thread_table[i].stack_base;
   ABORT("no thread table entry for current thread");
 }
 # ifdef _MSC_VER
@@ -100,10 +310,10 @@ ptr_t GC_current_stackbottom()
     /* The VirtualQuery calls below won't work properly on WinCE, but  */
     /* since each stack is restricted to an aligned 64K region of      */
     /* virtual memory we can just take the next lowest multiple of 64K.        */
-#   define GC_get_lo_stack_addr(s) \
+#   define GC_get_stack_min(s) \
         ((ptr_t)(((DWORD)(s) - 1) & 0xFFFF0000))
 # else
-    static ptr_t GC_get_lo_stack_addr(ptr_t s)
+    static ptr_t GC_get_stack_min(ptr_t s)
     {
        ptr_t bottom;
        MEMORY_BASIC_INFORMATION info;
@@ -120,193 +330,76 @@ ptr_t GC_current_stackbottom()
 void GC_push_all_stacks()
 {
   DWORD thread_id = GetCurrentThreadId();
+  GC_bool found_me = FALSE;
   int i;
-  for (i = 0; i < MAX_THREADS; i++)
-    if (thread_table[i].stack) {
-      ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack);
-      if (thread_table[i].id == thread_id)
-       GC_push_all_stack((ptr_t)&i, thread_table[i].stack);
-      else {
-       thread_table[i].context.ContextFlags
-                       = (CONTEXT_INTEGER|CONTEXT_CONTROL);
-       if (!GetThreadContext(thread_table[i].handle,
-                               /* cast away volatile qualifier */
-                               (LPCONTEXT)&thread_table[i].context))
+  int dummy;
+  ptr_t sp, stack_min;
+  GC_thread thread;
+  LONG my_max = GC_get_max_thread_index();
+  
+  for (i = 0; i <= my_max; i++) {
+    thread = thread_table + i;
+    if (thread -> in_use && thread -> stack_base) {
+      if (thread -> id == thread_id) {
+       sp = (ptr_t) &dummy;
+       found_me = TRUE;
+      } else {
+        CONTEXT context;
+        context.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL;
+        if (!GetThreadContext(thread_table[i].handle, &context))
          ABORT("GetThreadContext failed");
-#      ifdef I386
-         if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack
-             || thread_table[i].context.Esp < (DWORD)bottom)
-             ABORT("Thread stack pointer out of range");
-         GC_push_one ((word) thread_table[i].context.Edi);
-         GC_push_one ((word) thread_table[i].context.Esi);
-         GC_push_one ((word) thread_table[i].context.Ebp);
-         GC_push_one ((word) thread_table[i].context.Ebx);
-         GC_push_one ((word) thread_table[i].context.Edx);
-         GC_push_one ((word) thread_table[i].context.Ecx);
-         GC_push_one ((word) thread_table[i].context.Eax);
-         GC_push_all_stack((char *) thread_table[i].context.Esp,
-                           thread_table[i].stack);
-#       else
-#       ifdef ARM32
-         if (thread_table[i].context.Sp >= (DWORD)thread_table[i].stack
-             || thread_table[i].context.Sp < (DWORD)bottom)
-             ABORT("Thread stack pointer out of range");
-         GC_push_one ((word) thread_table[i].context.R0);
-         GC_push_one ((word) thread_table[i].context.R1);
-         GC_push_one ((word) thread_table[i].context.R2);
-         GC_push_one ((word) thread_table[i].context.R3);
-         GC_push_one ((word) thread_table[i].context.R4);
-         GC_push_one ((word) thread_table[i].context.R5);
-         GC_push_one ((word) thread_table[i].context.R6);
-         GC_push_one ((word) thread_table[i].context.R7);
-         GC_push_one ((word) thread_table[i].context.R8);
-         GC_push_one ((word) thread_table[i].context.R9);
-         GC_push_one ((word) thread_table[i].context.R10);
-         GC_push_one ((word) thread_table[i].context.R11);
-         GC_push_one ((word) thread_table[i].context.R12);
-         GC_push_all_stack((char *) thread_table[i].context.Sp,
-                           thread_table[i].stack);
-#       else
-#      ifdef SHx
-         if (thread_table[i].context.R15 >= (DWORD)thread_table[i].stack
-             || thread_table[i].context.R15 < (DWORD)bottom)
-             ABORT("Thread stack pointer out of range");
-         GC_push_one ((word) thread_table[i].context.R0);
-         GC_push_one ((word) thread_table[i].context.R1);
-         GC_push_one ((word) thread_table[i].context.R2);
-         GC_push_one ((word) thread_table[i].context.R3);
-         GC_push_one ((word) thread_table[i].context.R4);
-         GC_push_one ((word) thread_table[i].context.R5);
-         GC_push_one ((word) thread_table[i].context.R6);
-         GC_push_one ((word) thread_table[i].context.R7);
-         GC_push_one ((word) thread_table[i].context.R8);
-         GC_push_one ((word) thread_table[i].context.R9);
-         GC_push_one ((word) thread_table[i].context.R10);
-         GC_push_one ((word) thread_table[i].context.R11);
-         GC_push_one ((word) thread_table[i].context.R12);
-         GC_push_one ((word) thread_table[i].context.R13);
-         GC_push_one ((word) thread_table[i].context.R14);
-         GC_push_all_stack((char *) thread_table[i].context.R15,
-                           thread_table[i].stack);
+
+        /* Push all registers that might point into the heap.  Frame   */
+        /* pointer registers are included in case client code was      */
+        /* compiled with the 'omit frame pointer' optimisation.                */
+#       define PUSH1(reg) GC_push_one((word)context.reg)
+#       define PUSH2(r1,r2) PUSH1(r1), PUSH1(r2)
+#       define PUSH4(r1,r2,r3,r4) PUSH2(r1,r2), PUSH2(r3,r4)
+#       if defined(I386)
+          PUSH4(Edi,Esi,Ebx,Edx), PUSH2(Ecx,Eax), PUSH1(Ebp);
+         sp = (ptr_t)context.Esp;
+#       elif defined(ARM32)
+         PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11),PUSH1(R12);
+         sp = (ptr_t)context.Sp;
+#       elif defined(SHx)
+         PUSH4(R0,R1,R2,R3), PUSH4(R4,R5,R6,R7), PUSH4(R8,R9,R10,R11);
+         PUSH2(R12,R13), PUSH1(R14);
+         sp = (ptr_t)context.R15;
+#       elif defined(MIPS)
+         PUSH4(IntAt,IntV0,IntV1,IntA0), PUSH4(IntA1,IntA2,IntA3,IntT0);
+         PUSH4(IntT1,IntT2,IntT3,IntT4), PUSH4(IntT5,IntT6,IntT7,IntS0);
+         PUSH4(IntS1,IntS2,IntS3,IntS4), PUSH4(IntS5,IntS6,IntS7,IntT8);
+         PUSH4(IntT9,IntK0,IntK1,IntS8);
+         sp = (ptr_t)context.IntSp;
+#       elif defined(PPC)
+         PUSH4(Gpr0, Gpr3, Gpr4, Gpr5),  PUSH4(Gpr6, Gpr7, Gpr8, Gpr9);
+         PUSH4(Gpr10,Gpr11,Gpr12,Gpr14), PUSH4(Gpr15,Gpr16,Gpr17,Gpr18);
+         PUSH4(Gpr19,Gpr20,Gpr21,Gpr22), PUSH4(Gpr23,Gpr24,Gpr25,Gpr26);
+         PUSH4(Gpr27,Gpr28,Gpr29,Gpr30), PUSH1(Gpr31);
+         sp = (ptr_t)context.Gpr1;
+#       elif defined(ALPHA)
+         PUSH4(IntV0,IntT0,IntT1,IntT2), PUSH4(IntT3,IntT4,IntT5,IntT6);
+         PUSH4(IntT7,IntS0,IntS1,IntS2), PUSH4(IntS3,IntS4,IntS5,IntFp);
+         PUSH4(IntA0,IntA1,IntA2,IntA3), PUSH4(IntA4,IntA5,IntT8,IntT9);
+         PUSH4(IntT10,IntT11,IntT12,IntAt);
+         sp = (ptr_t)context.IntSp;
 #       else
-#      ifdef MIPS
-         if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack
-             || thread_table[i].context.IntSp < (DWORD)bottom)
-             ABORT("Thread stack pointer out of range");
-         GC_push_one ((word) thread_table[i].context.IntAt);
-         GC_push_one ((word) thread_table[i].context.IntV0);
-         GC_push_one ((word) thread_table[i].context.IntV1);
-         GC_push_one ((word) thread_table[i].context.IntA0);
-         GC_push_one ((word) thread_table[i].context.IntA1);
-         GC_push_one ((word) thread_table[i].context.IntA2);
-         GC_push_one ((word) thread_table[i].context.IntA3);
-         GC_push_one ((word) thread_table[i].context.IntT0);
-         GC_push_one ((word) thread_table[i].context.IntT1);
-         GC_push_one ((word) thread_table[i].context.IntT2);
-         GC_push_one ((word) thread_table[i].context.IntT3);
-         GC_push_one ((word) thread_table[i].context.IntT4);
-         GC_push_one ((word) thread_table[i].context.IntT5);
-         GC_push_one ((word) thread_table[i].context.IntT6);
-         GC_push_one ((word) thread_table[i].context.IntT7);
-         GC_push_one ((word) thread_table[i].context.IntS0);
-         GC_push_one ((word) thread_table[i].context.IntS1);
-         GC_push_one ((word) thread_table[i].context.IntS2);
-         GC_push_one ((word) thread_table[i].context.IntS3);
-         GC_push_one ((word) thread_table[i].context.IntS4);
-         GC_push_one ((word) thread_table[i].context.IntS5);
-         GC_push_one ((word) thread_table[i].context.IntS6);
-         GC_push_one ((word) thread_table[i].context.IntS7);
-         GC_push_one ((word) thread_table[i].context.IntT8);
-         GC_push_one ((word) thread_table[i].context.IntT9);
-         GC_push_one ((word) thread_table[i].context.IntK0);
-         GC_push_one ((word) thread_table[i].context.IntK1);
-         GC_push_one ((word) thread_table[i].context.IntS8);
-         GC_push_all_stack((char *) thread_table[i].context.IntSp,
-                           thread_table[i].stack);
-#      else
-#      ifdef PPC
-         if (thread_table[i].context.Gpr1 >= (DWORD)thread_table[i].stack
-             || thread_table[i].context.Gpr1 < (DWORD)bottom)
-             ABORT("Thread stack pointer out of range");
-         GC_push_one ((word) thread_table[i].context.Gpr0);
-         /* Gpr1 is stack pointer */
-         /* Gpr2 is global pointer */
-         GC_push_one ((word) thread_table[i].context.Gpr3);
-         GC_push_one ((word) thread_table[i].context.Gpr4);
-         GC_push_one ((word) thread_table[i].context.Gpr5);
-         GC_push_one ((word) thread_table[i].context.Gpr6);
-         GC_push_one ((word) thread_table[i].context.Gpr7);
-         GC_push_one ((word) thread_table[i].context.Gpr8);
-         GC_push_one ((word) thread_table[i].context.Gpr9);
-         GC_push_one ((word) thread_table[i].context.Gpr10);
-         GC_push_one ((word) thread_table[i].context.Gpr11);
-         GC_push_one ((word) thread_table[i].context.Gpr12);
-         /* Gpr13 is reserved for the kernel */
-         GC_push_one ((word) thread_table[i].context.Gpr14);
-         GC_push_one ((word) thread_table[i].context.Gpr15);
-         GC_push_one ((word) thread_table[i].context.Gpr16);
-         GC_push_one ((word) thread_table[i].context.Gpr17);
-         GC_push_one ((word) thread_table[i].context.Gpr18);
-         GC_push_one ((word) thread_table[i].context.Gpr19);
-         GC_push_one ((word) thread_table[i].context.Gpr20);
-         GC_push_one ((word) thread_table[i].context.Gpr21);
-         GC_push_one ((word) thread_table[i].context.Gpr22);
-         GC_push_one ((word) thread_table[i].context.Gpr23);
-         GC_push_one ((word) thread_table[i].context.Gpr24);
-         GC_push_one ((word) thread_table[i].context.Gpr25);
-         GC_push_one ((word) thread_table[i].context.Gpr26);
-         GC_push_one ((word) thread_table[i].context.Gpr27);
-         GC_push_one ((word) thread_table[i].context.Gpr28);
-         GC_push_one ((word) thread_table[i].context.Gpr29);
-         GC_push_one ((word) thread_table[i].context.Gpr30);
-         GC_push_one ((word) thread_table[i].context.Gpr31);
-         GC_push_all_stack((char *) thread_table[i].context.Gpr1,
-                           thread_table[i].stack);
-#      else
-#      ifdef ALPHA
-         if (thread_table[i].context.IntSp >= (DWORD)thread_table[i].stack
-             || thread_table[i].context.IntSp < (DWORD)bottom)
-             ABORT("Thread stack pointer out of range");
-         GC_push_one ((word) thread_table[i].context.IntV0);
-         GC_push_one ((word) thread_table[i].context.IntT0);
-         GC_push_one ((word) thread_table[i].context.IntT1);
-         GC_push_one ((word) thread_table[i].context.IntT2);
-         GC_push_one ((word) thread_table[i].context.IntT3);
-         GC_push_one ((word) thread_table[i].context.IntT4);
-         GC_push_one ((word) thread_table[i].context.IntT5);
-         GC_push_one ((word) thread_table[i].context.IntT6);
-         GC_push_one ((word) thread_table[i].context.IntT7);
-         GC_push_one ((word) thread_table[i].context.IntS0);
-         GC_push_one ((word) thread_table[i].context.IntS1);
-         GC_push_one ((word) thread_table[i].context.IntS2);
-         GC_push_one ((word) thread_table[i].context.IntS3);
-         GC_push_one ((word) thread_table[i].context.IntS4);
-         GC_push_one ((word) thread_table[i].context.IntS5);
-         GC_push_one ((word) thread_table[i].context.IntFp);
-         GC_push_one ((word) thread_table[i].context.IntA0);
-         GC_push_one ((word) thread_table[i].context.IntA1);
-         GC_push_one ((word) thread_table[i].context.IntA2);
-         GC_push_one ((word) thread_table[i].context.IntA3);
-         GC_push_one ((word) thread_table[i].context.IntA4);
-         GC_push_one ((word) thread_table[i].context.IntA5);
-         GC_push_one ((word) thread_table[i].context.IntT8);
-         GC_push_one ((word) thread_table[i].context.IntT9);
-         GC_push_one ((word) thread_table[i].context.IntT10);
-         GC_push_one ((word) thread_table[i].context.IntT11);
-         GC_push_one ((word) thread_table[i].context.IntT12);
-         GC_push_one ((word) thread_table[i].context.IntAt);
-         GC_push_all_stack((char *) thread_table[i].context.IntSp,
-                           thread_table[i].stack);
-#      else
-             --> architecture not supported
-#      endif /* !ALPHA */
-#      endif /* !PPC */
-#      endif /* !MIPS */
-#      endif /* !SHx */
-#      endif /* !ARM32 */
-#      endif /* !I386 */
+#         error "architecture is not supported"
+#       endif
+      }
+
+      stack_min = GC_get_stack_min(thread->stack_base);
+
+      if (sp >= stack_min && sp < thread->stack_base)
+        GC_push_all_stack(sp, thread->stack_base);
+      else {
+        WARN("Thread stack pointer 0x%lx out of range, pushing everything\n",
+            (unsigned long)sp);
+        GC_push_all_stack(stack_min, thread->stack_base);
       }
     }
+  }
+  if (!found_me) ABORT("Collecting from unknown thread.");
 }
 
 void GC_get_next_stack(char *start, char **lo, char **hi)
@@ -314,9 +407,10 @@ void GC_get_next_stack(char *start, char **lo, char **hi)
     int i;
 #   define ADDR_LIMIT (char *)(-1L)
     char * current_min = ADDR_LIMIT;
-
-    for (i = 0; i < MAX_THREADS; i++) {
-       char * s = (char *)thread_table[i].stack;
+    LONG my_max = GC_get_max_thread_index();
+  
+    for (i = 0; i <= my_max; i++) {
+       char * s = (char *)thread_table[i].stack_base;
 
        if (0 != s && s > start && s < current_min) {
            current_min = s;
@@ -327,13 +421,17 @@ void GC_get_next_stack(char *start, char **lo, char **hi)
        *lo = ADDR_LIMIT;
        return;
     }
-    *lo = GC_get_lo_stack_addr(current_min);
+    *lo = GC_get_stack_min(current_min);
     if (*lo < start) *lo = start;
 }
 
-#if !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))
+#if !defined(CYGWIN32)
+
+#if !defined(MSWINCE) && defined(GC_DLL)
+
+/* We register threads from DllMain */
 
-HANDLE WINAPI GC_CreateThread(
+GC_API HANDLE WINAPI GC_CreateThread(
     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
@@ -342,87 +440,45 @@ HANDLE WINAPI GC_CreateThread(
                         lpParameter, dwCreationFlags, lpThreadId);
 }
 
-#else /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
+#else /* defined(MSWINCE) || !defined(GC_DLL))  */
+
+/* We have no DllMain to take care of new threads.  Thus we    */
+/* must properly intercept thread creation.                    */
 
 typedef struct {
-    HANDLE child_ready_h, parent_ready_h;
-    volatile struct thread_entry * entry;
     LPTHREAD_START_ROUTINE start;
     LPVOID param;
 } thread_args;
 
-DWORD WINAPI thread_start(LPVOID arg);
+static DWORD WINAPI thread_start(LPVOID arg);
 
-HANDLE WINAPI GC_CreateThread(
+GC_API HANDLE WINAPI GC_CreateThread(
     LPSECURITY_ATTRIBUTES lpThreadAttributes, 
     DWORD dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, 
     LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId )
 {
     HANDLE thread_h = NULL;
-    HANDLE child_ready_h, parent_ready_h;
 
-    int i;
-    thread_args args;
+    thread_args *args;
 
-    /* allocate thread slot */
-    LOCK();
-    for (i = 0; i != MAX_THREADS && thread_table[i].in_use; i++)
-       ;
-    if (i != MAX_THREADS) {
-       thread_table[i].in_use = TRUE;
+    if (!GC_is_initialized) GC_init();
+               /* make sure GC is initialized (i.e. main thread is attached) */
+    
+    args = GC_malloc_uncollectable(sizeof(thread_args)); 
+       /* Handed off to and deallocated by child thread.       */
+    if (0 == args) {
+       SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return NULL;
     }
-    UNLOCK();
-
-    if (i != MAX_THREADS) {
-
-       /* create unnamed unsignalled events */
-       if (child_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
-           if (parent_ready_h = CreateEvent(NULL, FALSE, FALSE, NULL)) {
-
-               /* set up thread arguments */
-               args.child_ready_h = child_ready_h;
-               args.parent_ready_h = parent_ready_h;
-               args.entry = &thread_table[i];
-               args.start = lpStartAddress;
-               args.param = lpParameter;
-
-               thread_h = CreateThread(lpThreadAttributes,
-                                       dwStackSize, thread_start,
-                                       &args,
-                                       dwCreationFlags & ~CREATE_SUSPENDED,
-                                       lpThreadId);
-
-               if (thread_h) {
-
-                   /* fill in ID and handle; tell child this is done */
-                   thread_table[i].id = *lpThreadId;
-                   thread_table[i].handle = thread_h;
-                   SetEvent (parent_ready_h);
-
-                   /* wait for child to fill in stack and copy args */
-                   WaitForSingleObject (child_ready_h, INFINITE);
-
-                   /* suspend the child if requested */
-                   if (dwCreationFlags & CREATE_SUSPENDED)
-                       SuspendThread (thread_h);
 
-                   /* let child call given function now (or when resumed) */
-                   SetEvent (parent_ready_h);
+    /* set up thread arguments */
+       args -> start = lpStartAddress;
+       args -> param = lpParameter;
 
-               } else {
-                   CloseHandle (parent_ready_h);
-               }
-           }
-       }
-
-       CloseHandle (child_ready_h);
-
-       if (thread_h == NULL)
-           thread_table[i].in_use = FALSE;
-
-    } else { /* no thread slot found */
-       SetLastError (ERROR_TOO_MANY_TCBS);
-    }
+    thread_h = CreateThread(lpThreadAttributes,
+                           dwStackSize, thread_start,
+                           args, dwCreationFlags,
+                           lpThreadId);
 
     return thread_h;
 }
@@ -430,37 +486,31 @@ HANDLE WINAPI GC_CreateThread(
 static DWORD WINAPI thread_start(LPVOID arg)
 {
     DWORD ret = 0;
-    thread_args args = *(thread_args *)arg;
+    thread_args *args = (thread_args *)arg;
 
-    /* wait for parent to fill in ID and handle */
-    WaitForSingleObject (args.parent_ready_h, INFINITE);
-    ResetEvent (args.parent_ready_h);
-
-    /* fill in stack; tell parent this is done */
-    args.entry->stack = GC_get_stack_base();
-    SetEvent (args.child_ready_h);
-
-    /* wait for parent to tell us to go (in case it needs to suspend us) */
-    WaitForSingleObject (args.parent_ready_h, INFINITE);
-    CloseHandle (args.parent_ready_h);
+    GC_new_thread();
 
     /* Clear the thread entry even if we exit with an exception.       */
     /* This is probably pointless, since an uncaught exception is      */
     /* supposed to result in the process being killed.                 */
+#ifndef __GNUC__
     __try {
-       ret = args.start (args.param);
+#endif /* __GNUC__ */
+       ret = args->start (args->param);
+#ifndef __GNUC__
     } __finally {
-       LOCK();
-       args.entry->stack = 0;
-       args.entry->in_use = FALSE;
-             /* cast away volatile qualifier */
-       BZERO((void *) &args.entry->context, sizeof(CONTEXT));
-       UNLOCK();
+#endif /* __GNUC__ */
+       GC_free(args);
+       GC_delete_thread(GetCurrentThreadId());
+#ifndef __GNUC__
     }
+#endif /* __GNUC__ */
 
     return ret;
 }
-#endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL)) */
+#endif /* !defined(MSWINCE) && !(defined(__MINGW32__) && !defined(_DLL))  */
+
+#endif /* !CYGWIN32 */
 
 #ifdef MSWINCE
 
@@ -485,7 +535,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
     DWORD thread_id;
 
     /* initialize everything */
-    InitializeCriticalSection(&GC_allocate_ml);
     GC_init();
 
     /* start the main thread */
@@ -515,104 +564,218 @@ DWORD WINAPI main_thread_start(LPVOID arg)
 
 # else /* !MSWINCE */
 
-LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info);
+/* Called by GC_init() - we hold the allocation lock.  */
+void GC_thr_init() {
+    if (GC_thr_initialized) return;
+    GC_main_thread = GetCurrentThreadId();
+    GC_thr_initialized = TRUE;
+
+    /* Add the initial thread, so we can stop it.      */
+    GC_new_thread();
+}
+
+#ifdef CYGWIN32
+
+struct start_info {
+    void *(*start_routine)(void *);
+    void *arg;
+    GC_bool detached;
+};
+
+int GC_pthread_join(pthread_t pthread_id, void **retval) {
+    int result;
+    int i;
+    GC_thread me;
+
+#   if DEBUG_CYGWIN_THREADS
+      GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n",
+                (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);
+#   endif
+
+    /* Thread being joined might not have registered itself yet. */
+    /* After the join,thread id may have been recycled.                 */
+    /* FIXME: It would be better if this worked more like       */
+    /* pthread_support.c.                                       */
+
+    while ((me = GC_lookup_thread(pthread_id)) == 0) Sleep(10);
+
+    result = pthread_join(pthread_id, retval);
+
+    GC_delete_gc_thread(me);
+
+#   if DEBUG_CYGWIN_THREADS
+      GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n",
+                (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);
+#   endif
+
+    return result;
+}
+
+/* Cygwin-pthreads calls CreateThread internally, but it's not
+ * easily interceptible by us..
+ *   so intercept pthread_create instead
+ */
+int
+GC_pthread_create(pthread_t *new_thread,
+                 const pthread_attr_t *attr,
+                  void *(*start_routine)(void *), void *arg) {
+    int result;
+    struct start_info * si;
+
+    if (!GC_is_initialized) GC_init();
+               /* make sure GC is initialized (i.e. main thread is attached) */
+    
+    /* This is otherwise saved only in an area mmapped by the thread */
+    /* library, which isn't visible to the collector.           */
+    si = GC_malloc_uncollectable(sizeof(struct start_info)); 
+    if (0 == si) return(EAGAIN);
+
+    si -> start_routine = start_routine;
+    si -> arg = arg;
+    if (attr != 0 &&
+        pthread_attr_getdetachstate(attr, &si->detached)
+       == PTHREAD_CREATE_DETACHED) {
+      si->detached = TRUE;
+    }
+
+#   if DEBUG_CYGWIN_THREADS
+      GC_printf2("About to create a thread from 0x%x(0x%x)\n",
+                (int)pthread_self(), GetCurrentThreadId);
+#   endif
+    result = pthread_create(new_thread, attr, GC_start_routine, si); 
+
+    if (result) { /* failure */
+       GC_free(si);
+    } 
+
+    return(result);
+}
+
+void * GC_start_routine(void * arg)
+{
+    struct start_info * si = arg;
+    void * result;
+    void *(*start)(void *);
+    void *start_arg;
+    pthread_t pthread_id;
+    GC_thread me;
+    GC_bool detached;
+    int i;
+
+#   if DEBUG_CYGWIN_THREADS
+      GC_printf2("thread 0x%x(0x%x) starting...\n",(int)pthread_self(),
+                                                  GetCurrentThreadId());
+#   endif
+
+    /* If a GC occurs before the thread is registered, that GC will    */
+    /* ignore this thread.  That's fine, since it will block trying to  */
+    /* acquire the allocation lock, and won't yet hold interesting     */
+    /* pointers.                                                       */
+    LOCK();
+    /* We register the thread here instead of in the parent, so that   */
+    /* we don't need to hold the allocation lock during pthread_create. */
+    me = GC_new_thread();
+    UNLOCK();
+
+    start = si -> start_routine;
+    start_arg = si -> arg;
+    if (si-> detached) me -> flags |= DETACHED;
+    me -> pthread_id = pthread_id = pthread_self();
+
+    GC_free(si); /* was allocated uncollectable */
+
+    pthread_cleanup_push(GC_thread_exit_proc, (void *)me);
+    result = (*start)(start_arg);
+    me -> status = result;
+    pthread_cleanup_pop(0);
+
+#   if DEBUG_CYGWIN_THREADS
+      GC_printf2("thread 0x%x(0x%x) returned from start routine.\n",
+                (int)pthread_self(),GetCurrentThreadId());
+#   endif
+
+    return(result);
+}
+
+void GC_thread_exit_proc(void *arg)
+{
+    GC_thread me = (GC_thread)arg;
+    int i;
+
+#   if DEBUG_CYGWIN_THREADS
+      GC_printf2("thread 0x%x(0x%x) called pthread_exit().\n",
+                (int)pthread_self(),GetCurrentThreadId());
+#   endif
+
+    LOCK();
+    if (me -> flags & DETACHED) {
+      GC_delete_thread(GetCurrentThreadId());
+    } else {
+      /* deallocate it as part of join */
+      me -> flags |= FINISHED;
+    }
+    UNLOCK();
+}
+
+/* nothing required here... */
+int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) {
+  return pthread_sigmask(how, set, oset);
+}
+
+int GC_pthread_detach(pthread_t thread)
+{
+    int result;
+    GC_thread thread_gc_id;
+    
+    LOCK();
+    thread_gc_id = GC_lookup_thread(thread);
+    UNLOCK();
+    result = pthread_detach(thread);
+    if (result == 0) {
+      LOCK();
+      thread_gc_id -> flags |= DETACHED;
+      /* Here the pthread thread id may have been recycled. */
+      if (thread_gc_id -> flags & FINISHED) {
+        GC_delete_gc_thread(thread_gc_id);
+      }
+      UNLOCK();
+    }
+    return result;
+}
+
+#else /* !CYGWIN32 */
 
 /*
- * This isn't generally safe, since DllMain is not premptible.
- * If another thread holds the lock while this runs we're in trouble.
+ * We avoid acquiring locks here, since this doesn't seem to be preemptable.
  * Pontus Rydin suggests wrapping the thread start routine instead.
  */
+#ifdef GC_DLL
 BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
 {
   switch (reason) {
   case DLL_PROCESS_ATTACH:
-    InitializeCriticalSection(&GC_allocate_ml);
     GC_init(); /* Force initialization before thread attach.   */
     /* fall through */
   case DLL_THREAD_ATTACH:
-    {
-      int i;
-      /* It appears to be unsafe to acquire a lock here, since this    */
-      /* code is apparently not preeemptible on some systems.          */
-      /* (This is based on complaints, not on Microsoft's official     */
-      /* documentation, which says this should perform "only simple    */
-      /* inititalization tasks".)                                      */
-      /* Hence we make do with nonblocking synchronization.            */
-
-      /* The following should be a noop according to the win32 */
-      /* documentation.  There is empirical evidence that it   */
-      /* isn't.                - HB                                    */
-#     ifdef MPROTECT_VDB
-       if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler);
-#     endif
-
-      for (i = 0;
-                              /* cast away volatile qualifier */
-          InterlockedExchange((LPLONG) &thread_table[i].in_use, 1) != 0;
-          i++) {
-       /* Compare-and-swap would make this cleaner, but that's not     */
-       /* supported before Windows 98 and NT 4.0.  In Windows 2000,    */
-       /* InterlockedExchange is supposed to be replaced by            */
-       /* InterlockedExchangePointer, but that's not really what I     */
-       /* want here.                                                   */
-       if (i == MAX_THREADS - 1)
-         ABORT("too many threads");
-      }
-      thread_table[i].id = GetCurrentThreadId();
-      if (!DuplicateHandle(GetCurrentProcess(),
-                          GetCurrentThread(),
-                          GetCurrentProcess(),
-                          /* cast away volatile qualifier */
-                          (HANDLE *) &thread_table[i].handle,
-                          0,
-                          0,
-                          DUPLICATE_SAME_ACCESS)) {
-       DWORD last_error = GetLastError();
-       GC_printf1("Last error code: %lx\n", last_error);
-       ABORT("DuplicateHandle failed");
-      }
-      thread_table[i].stack = GC_get_stack_base();
-      /* If this thread is being created while we are trying to stop   */
-      /* the world, wait here.  Hopefully this can't happen on any     */
-      /* systems that don't allow us to block here.                    */
-      while (GC_please_stop) Sleep(20);
-    }
+    GC_ASSERT(GC_thr_initialized);
+    if (GC_main_thread != GetCurrentThreadId()) {
+        GC_new_thread();
+    } /* o.w. we already did it during GC_thr_init(), called by GC_init() */
     break;
+
   case DLL_THREAD_DETACH:
-    {
-      int i;
-      DWORD thread_id = GetCurrentThreadId();
-      LOCK();
-      for (i = 0;
-           i < MAX_THREADS &&
-          (thread_table[i].stack == 0 || thread_table[i].id != thread_id);
-          i++) {}
-      if (i >= MAX_THREADS) {
-         WARN("thread %ld not found on detach", (GC_word)thread_id);
-      } else {
-          thread_table[i].stack = 0;
-          thread_table[i].in_use = FALSE;
-          CloseHandle(thread_table[i].handle);
-           /* cast away volatile qualifier */
-          BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));
-      }
-      UNLOCK();
-    }
+    GC_delete_thread(GetCurrentThreadId());
     break;
+
   case DLL_PROCESS_DETACH:
     {
       int i;
 
       LOCK();
-      for (i = 0; i < MAX_THREADS; ++i)
+      for (i = 0; i <= GC_get_max_thread_index(); ++i)
       {
           if (thread_table[i].in_use)
-          {
-              thread_table[i].stack = 0;
-              thread_table[i].in_use = FALSE;
-              CloseHandle(thread_table[i].handle);
-              BZERO((void *) &thread_table[i].context, sizeof(CONTEXT));
-          }
+           GC_delete_gc_thread(thread_table + i);
       }
       UNLOCK();
 
@@ -624,6 +787,8 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)
   }
   return TRUE;
 }
+#endif /* GC_DLL */
+#endif /* !CYGWIN32 */
 
 # endif /* !MSWINCE */