diff options
| -rw-r--r-- | boehm-gc/ChangeLog | 9 | ||||
| -rw-r--r-- | boehm-gc/configure.in | 4 | ||||
| -rw-r--r-- | boehm-gc/include/private/gc_priv.h | 14 | ||||
| -rw-r--r-- | boehm-gc/mach_dep.c | 6 | ||||
| -rw-r--r-- | boehm-gc/misc.c | 6 | ||||
| -rw-r--r-- | boehm-gc/win32_threads.c | 788 | 
6 files changed, 387 insertions, 440 deletions
| diff --git a/boehm-gc/ChangeLog b/boehm-gc/ChangeLog index ec1d28fa..f38b218 100644 --- a/boehm-gc/ChangeLog +++ b/boehm-gc/ChangeLog @@ -1,3 +1,12 @@ +2003-10-03  Hans Boehm  <Hans.Boehm@hp.com> + +	* configure.in: Remove NO_GETENV definition for win32. +	* mach_dep.c (GC_generic_push_regs): Prevent tail call optimization. +	* misc.c (GC_init_inner): Call GC_thr_init for win32. +	  (GC_set_warn_proc): Add assertion. +	* win32_threads.c: Import 6.3alpha2 version. +	* include/private/gc_priv.h: Add support for EMPTY_GETENV_RESULTS. +  2003-09-29  Rainer Orth  <ro@TechFak.Uni-Bielefeld.DE>  	* configure.in: Remove wildcard from Solaris 8-9/Intel and Solaris diff --git a/boehm-gc/configure.in b/boehm-gc/configure.in index 96da3c31ed..85d0eeb 100644 --- a/boehm-gc/configure.in +++ b/boehm-gc/configure.in @@ -137,8 +137,8 @@ case "$THREADS" in      ;;   win32)      AC_DEFINE(GC_WIN32_THREADS) -    dnl Wine getenv may not return NULL for missing entry -    AC_DEFINE(NO_GETENV) +    dnl Old wine getenv may not return NULL for missing entry. +    dnl Define EMPTY_GETENV_RESULTS here to work around the bug.      ;;   dgux386)      THREADS=dgux386 diff --git a/boehm-gc/include/private/gc_priv.h b/boehm-gc/include/private/gc_priv.h index ffa398b..5e7625f 100644 --- a/boehm-gc/include/private/gc_priv.h +++ b/boehm-gc/include/private/gc_priv.h @@ -448,7 +448,19 @@ extern GC_warn_proc GC_current_warn_proc;  /* Get environment entry */  #if !defined(NO_GETENV) -#   define GETENV(name) getenv(name) +#   if defined(EMPTY_GETENV_RESULTS) +	/* Workaround for a reputed Wine bug.	*/ +	static inline char * fixed_getenv(const char *name) +	{ +	  char * tmp = getenv(name); +	  if (tmp == 0 || strlen(tmp) == 0) +	    return 0; +	  return tmp; +	} +#       define GETENV(name) fixed_getenv(name) +#   else +#       define GETENV(name) getenv(name) +#   endif  #else  #   define GETENV(name) 0  #endif diff --git a/boehm-gc/mach_dep.c b/boehm-gc/mach_dep.c index 3dc5f0b..2c321fa 100644 --- a/boehm-gc/mach_dep.c +++ b/boehm-gc/mach_dep.c @@ -405,6 +405,8 @@ void GC_generic_push_regs(cold_gc_frame)  ptr_t cold_gc_frame;  {  	{ +	    word dummy; +  #	    ifdef HAVE_BUILTIN_UNWIND_INIT  	      /* This was suggested by Richard Henderson as the way to	*/  	      /* force callee-save registers and register windows onto	*/ @@ -448,6 +450,10 @@ ptr_t cold_gc_frame;  	      }  #           endif  	    GC_push_current_stack(cold_gc_frame); +	    /* Strongly discourage the compiler from treating the above	*/ +	    /* as a tail-call, since that would pop the register 	*/ +	    /* contents before we get a chance to look at them.		*/ +	    GC_noop1((word)(&dummy));  	}  }  #endif /* USE_GENERIC_PUSH_REGS */ diff --git a/boehm-gc/misc.c b/boehm-gc/misc.c index 814fa41a..12d6d2e 100644 --- a/boehm-gc/misc.c +++ b/boehm-gc/misc.c @@ -638,7 +638,8 @@ void GC_init_inner()  #   if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__)  	GC_init_netbsd_elf();  #   endif -#   if defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) +#   if defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) \ +       || defined(GC_WIN32_THREADS)          GC_thr_init();  #   endif  #   ifdef GC_SOLARIS_THREADS @@ -1002,6 +1003,9 @@ GC_warn_proc GC_current_warn_proc = GC_default_warn_proc;  {      GC_warn_proc result; +#   ifdef GC_WIN32_THREADS +      GC_ASSERT(GC_is_initialized); +#   endif      LOCK();      result = GC_current_warn_proc;      GC_current_warn_proc = p; diff --git a/boehm-gc/win32_threads.c b/boehm-gc/win32_threads.c index 10cfef9..7a1dbe2 100644 --- a/boehm-gc/win32_threads.c +++ b/boehm-gc/win32_threads.c @@ -14,36 +14,205 @@  # define DEBUG_CYGWIN_THREADS 0 -  GC_bool GC_thr_initialized = FALSE;    void * GC_start_routine(void * arg);    void GC_thread_exit_proc(void *arg);  #endif +/* 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 64 +# define MAX_THREADS 256 +    /* FIXME:							*/ +    /* Things may get quite slow for large numbers of threads,	*/ +    /* since we look them up with sequential search.		*/  #endif -struct thread_entry { -  LONG in_use; +GC_bool GC_thr_initialized = FALSE; + +DWORD GC_main_thread = 0; + +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_psuh_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))  { @@ -52,8 +221,12 @@ void GC_push_thread_structures GC_PROTO((void))      /* no private structures we need to preserve.			*/  # ifdef CYGWIN32    { int i; /* pthreads may keep a pointer in the thread exit value */ -    for (i = 0; i < MAX_THREADS; i++) -      if (thread_table[i].in_use) GC_push_all((ptr_t)&(thread_table[i].status),(ptr_t)(&(thread_table[i].status)+1)); +    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  } @@ -63,13 +236,11 @@ void GC_stop_world()    DWORD thread_id = GetCurrentThreadId();    int i; -#ifdef CYGWIN32    if (!GC_thr_initialized) ABORT("GC_stop_world() called before GC_thr_init()"); -#endif    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 */ @@ -84,13 +255,12 @@ void GC_stop_world()  	DWORD exitCode;   	if (GetExitCodeThread(thread_table[i].handle,&exitCode) &&              exitCode != STILL_ACTIVE) { -          thread_table[i].stack = 0; /* prevent stack from being pushed */ +          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));  #         endif  	  continue;  	} @@ -105,8 +275,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"); @@ -122,9 +294,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 @@ -135,10 +309,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; @@ -155,197 +329,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 -	  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); -	  if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack -	      || thread_table[i].context.Esp < (DWORD)bottom) { -	      WARN("Thread stack pointer 0x%lx out of range, pushing everything", -		   thread_table[i].context.Esp); -	      GC_push_all_stack((char *) bottom, thread_table[i].stack); -	  } else { -	      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) @@ -353,9 +406,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; @@ -366,7 +420,7 @@ 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;  } @@ -391,8 +445,6 @@ GC_API HANDLE WINAPI GC_CreateThread(  /* 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; @@ -405,80 +457,27 @@ GC_API HANDLE WINAPI GC_CreateThread(      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; -		    if (!DuplicateHandle(GetCurrentProcess(), -				 	 thread_h, -					 GetCurrentProcess(), -			 		 (PHANDLE) &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"); -		    } -		    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); - -		} else { -		    CloseHandle (parent_ready_h); -		} -	    } -	} - -	CloseHandle (child_ready_h); - -	if (thread_h == NULL) -	    thread_table[i].in_use = FALSE; +    /* set up thread arguments */ +    	args -> start = lpStartAddress; +    	args -> param = lpParameter; -    } else { /* no thread slot found */ -	SetLastError (ERROR_TOO_MANY_TCBS); -    } +    thread_h = CreateThread(lpThreadAttributes, +    			    dwStackSize, thread_start, +    			    args, dwCreationFlags, +    			    lpThreadId);      return thread_h;  } @@ -486,19 +485,9 @@ GC_API HANDLE WINAPI GC_CreateThread(  static DWORD WINAPI thread_start(LPVOID arg)  {      DWORD ret = 0; -    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); +    thread_args *args = (thread_args *)arg; -    /* 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	*/ @@ -506,16 +495,12 @@ static DWORD WINAPI thread_start(LPVOID arg)  #ifndef __GNUC__      __try {  #endif /* __GNUC__ */ -	ret = args.start (args.param); +	ret = args->start (args->param);  #ifndef __GNUC__      } __finally {  #endif /* __GNUC__ */ -	LOCK(); -	args.entry->stack = 0; -	args.entry->in_use = FALSE; -	      /* cast away volatile qualifier */ -	BZERO((void *) &args.entry->context, sizeof(CONTEXT)); -	UNLOCK(); +	GC_free(args); +	GC_delete_thread(GetCurrentThreadId());  #ifndef __GNUC__      }  #endif /* __GNUC__ */ @@ -549,7 +534,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,      DWORD thread_id;      /* initialize everything */ -    InitializeCriticalSection(&GC_allocate_ml);      GC_init();      /* start the main thread */ @@ -579,126 +563,44 @@ DWORD WINAPI main_thread_start(LPVOID arg)  # else /* !MSWINCE */ -LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info); - -/* threadAttach/threadDetach routines used by both CYGWIN and DLL - * implementation, since both recieve explicit notification on thread - * creation/destruction. - */ -static void threadAttach() { -  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					*/ -# if defined(MPROTECT_VDB) -   if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); -# endif -                /* cast away volatile qualifier */ -  for (i = 0; InterlockedExchange((LONG*)&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(); -# 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 = GC_get_stack_base(); -  if (thread_table[i].stack == NULL)  -    ABORT("Failed to find stack base in threadAttach"); -  /* 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); -} - -static void threadDetach(DWORD thread_id) { -  int i; - -  LOCK(); -  for (i = 0; -       i < MAX_THREADS && -       (!thread_table[i].in_use || 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(); -} - -#ifdef CYGWIN32 -  /* 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; -#if 0 -    /* this might already be handled in GC_init... */ -    InitializeCriticalSection(&GC_allocate_ml); -#endif -      /* Add the initial thread, so we can stop it.	*/ -    threadAttach(); +    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); +      GC_printf3("thread 0x%x(0x%x) is joining thread 0x%x.\n", +		 (int)pthread_self(), GetCurrentThreadId(), (int)pthread_id);  #   endif -    /* Can't do any table lookups here, because thread being joined  -       might not have registered itself yet */ +    /* 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); -    LOCK(); -    for (i = 0; !thread_table[i].in_use || thread_table[i].pthread_id != pthread_id; -         i++) { -      if (i == MAX_THREADS - 1) { -        GC_printf1("Failed to find thread 0x%x in pthread_join()\n", pthread_id); -        ABORT("thread not found on detach"); -      } -    } -    UNLOCK(); -    threadDetach(thread_table[i].id); +    GC_delete_gc_thread(me);  #   if DEBUG_CYGWIN_THREADS        GC_printf3("thread 0x%x(0x%x) completed join with thread 0x%x.\n", @@ -729,10 +631,15 @@ GC_pthread_create(pthread_t *new_thread,      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); +      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);  @@ -750,6 +657,8 @@ void * GC_start_routine(void * arg)      void *(*start)(void *);      void *start_arg;      pthread_t pthread_id; +    GC_thread me; +    GC_bool detached;      int i;  #   if DEBUG_CYGWIN_THREADS @@ -764,17 +673,19 @@ void * GC_start_routine(void * arg)      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. */ -    threadAttach(); +    me = GC_new_thread();      UNLOCK();      start = si -> start_routine;      start_arg = si -> arg; -    pthread_id = pthread_self(); +    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, pthread_id); +    pthread_cleanup_push(GC_thread_exit_proc, (void *)me);      result = (*start)(start_arg); +    me -> status = result;      pthread_cleanup_pop(0);  #   if DEBUG_CYGWIN_THREADS @@ -782,20 +693,12 @@ void * GC_start_routine(void * arg)  		 (int)pthread_self(),GetCurrentThreadId());  #   endif -    LOCK(); -    for (i = 0; thread_table[i].pthread_id != pthread_id; i++) { -      if (i == MAX_THREADS - 1) -        ABORT("thread not found on exit"); -    } -    thread_table[i].status = result; -    UNLOCK(); -      return(result);  }  void GC_thread_exit_proc(void *arg)  { -    pthread_t pthread_id = (pthread_t)arg; +    GC_thread me = (GC_thread)arg;      int i;  #   if DEBUG_CYGWIN_THREADS @@ -804,25 +707,41 @@ void GC_thread_exit_proc(void *arg)  #   endif      LOCK(); -    for (i = 0; thread_table[i].pthread_id != pthread_id; i++) { -      if (i == MAX_THREADS - 1) -        ABORT("thread not found on exit"); +    if (me -> flags & DETACHED) { +      GC_delete_thread(GetCurrentThreadId()); +    } else { +      /* deallocate it as part of join */ +      me -> flags |= FINISHED;      }      UNLOCK(); - -#if 0 -    /* TODO: we need a way to get the exit value after a pthread_exit so we can stash it safely away */ -    thread_table[i].status = ??? -#endif  }  /* 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) { -  return pthread_detach(thread); + +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 */  /* @@ -834,15 +753,17 @@ 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: -    threadAttach(); +    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: -    threadDetach(GetCurrentThreadId()); +    GC_delete_thread(GetCurrentThreadId());      break;    case DLL_PROCESS_DETACH: @@ -850,15 +771,10 @@ BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved)        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(); | 
