* Copyright (c) 1994 by Xerox Corporation. All rights reserved.
* Copyright (c) 1996 by Silicon Graphics. All rights reserved.
* Copyright (c) 1998 by Fergus Henderson. All rights reserved.
- * Copyright (c) 2000-2001 by Hewlett-Packard Company. All rights reserved.
+ * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
/*#define DEBUG_THREADS 1*/
/*#define GC_ASSERTIONS*/
+#include "gc_config.h"
+
+#ifdef GC_PTHREAD_SYM_VERSION
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#endif
+
+# include "gc.h"
# include "private/pthread_support.h"
# if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
- && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS) \
- && !defined(GC_AIX_THREADS)
+ && !defined(GC_WIN32_THREADS)
# if defined(GC_HPUX_THREADS) && !defined(USE_PTHREAD_SPECIFIC) \
- && !defined(USE_HPUX_TLS)
-# define USE_HPUX_TLS
+ && !defined(USE_COMPILER_TLS)
+# ifdef __GNUC__
+# define USE_PTHREAD_SPECIFIC
+ /* Empirically, as of gcc 3.3, USE_COMPILER_TLS doesn't work. */
+# else
+# define USE_COMPILER_TLS
+# endif
+# endif
+
+# if defined USE_HPUX_TLS
+ --> Macro replaced by USE_COMPILER_TLS
# endif
# if (defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) || \
- defined(GC_DARWIN_THREADS)) && !defined(USE_PTHREAD_SPECIFIC)
+ defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS)) \
+ && !defined(USE_PTHREAD_SPECIFIC)
# define USE_PTHREAD_SPECIFIC
# endif
# endif
# ifdef THREAD_LOCAL_ALLOC
-# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_HPUX_TLS)
+# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_COMPILER_TLS)
# include "private/specific.h"
# endif
# if defined(USE_PTHREAD_SPECIFIC)
# define GC_key_create pthread_key_create
typedef pthread_key_t GC_key_t;
# endif
-# if defined(USE_HPUX_TLS)
+# if defined(USE_COMPILER_TLS)
# define GC_getspecific(x) (x)
# define GC_setspecific(key, v) ((key) = (v), 0)
# define GC_key_create(key, d) 0
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
+# include <signal.h>
#if defined(GC_DARWIN_THREADS)
# include "private/darwin_semaphore.h"
# include <semaphore.h>
#endif /* !GC_DARWIN_THREADS */
-#if defined(GC_DARWIN_THREADS)
+#if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS)
# include <sys/sysctl.h>
#endif /* GC_DARWIN_THREADS */
/* We don't really support thread-local allocation with DBG_HDRS_ALL */
-#ifdef USE_HPUX_TLS
+#ifdef USE_COMPILER_TLS
__thread
#endif
GC_key_t GC_thread_key;
#endif /* !PARALLEL_MARK */
-/* Defining INSTALL_LOOPING_SEGV_HANDLER causes SIGSEGV and SIGBUS to */
-/* result in an infinite loop in a signal handler. This can be very */
-/* useful for debugging, since (as of RH7) gdb still seems to have */
-/* serious problems with threads. */
-#ifdef INSTALL_LOOPING_SEGV_HANDLER
-void GC_looping_handler(int sig)
-{
- GC_printf3("Signal %ld in thread %lx, pid %ld\n",
- sig, pthread_self(), getpid());
- for (;;);
-}
-#endif
-
GC_bool GC_thr_initialized = FALSE;
volatile GC_thread GC_threads[THREAD_TABLE_SZ];
} else {
prev -> next = p -> next;
}
- GC_INTERNAL_FREE(p);
+
+ if (p != &first_thread)
+ GC_INTERNAL_FREE(p);
}
/* If a thread has been joined, but we have not yet */
GC_INTERNAL_FREE(p);
}
-/* Return a GC_thread corresponding to a given thread_t. */
+/* Return a GC_thread corresponding to a given pthread_t. */
/* Returns 0 if it's not there. */
/* Caller holds allocation lock or otherwise inhibits */
/* updates. */
while (GC_incremental && GC_collection_in_progress()
&& (wait_for_all || old_gc_no == GC_gc_no)) {
ENTER_GC();
+ GC_in_thread_creation = TRUE;
GC_collect_a_little_inner(1);
+ GC_in_thread_creation = FALSE;
EXIT_GC();
UNLOCK();
sched_yield();
/* We hold the allocation lock. */
void GC_thr_init()
{
-# ifndef GC_DARWIN_THREADS
- int dummy;
-# endif
+# ifndef GC_DARWIN_THREADS
+ int dummy;
+# endif
GC_thread t;
if (GC_thr_initialized) return;
# if defined(GC_HPUX_THREADS)
GC_nprocs = pthread_num_processors_np();
# endif
-# if defined(GC_OSF1_THREADS)
+# if defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS) \
+ || defined(GC_SOLARIS_PTHREADS)
GC_nprocs = sysconf(_SC_NPROCESSORS_ONLN);
if (GC_nprocs <= 0) GC_nprocs = 1;
# endif
-# if defined(GC_FREEBSD_THREADS)
- GC_nprocs = 1;
+# if defined(GC_IRIX_THREADS)
+ GC_nprocs = sysconf(_SC_NPROC_ONLN);
+ if (GC_nprocs <= 0) GC_nprocs = 1;
# endif
-# if defined(GC_DARWIN_THREADS)
+# if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS)
int ncpus = 1;
size_t len = sizeof(ncpus);
sysctl((int[2]) {CTL_HW, HW_NCPU}, 2, &ncpus, &len, NULL, 0);
/* Disable true incremental collection, but generational is OK. */
GC_time_limit = GC_TIME_UNLIMITED;
}
+ /* If we are using a parallel marker, actually start helper threads. */
+ if (GC_parallel) start_mark_threads();
# endif
}
/* GC_init() calls us back, so set flag first. */
if (!GC_is_initialized) GC_init();
- /* If we are using a parallel marker, start the helper threads. */
-# ifdef PARALLEL_MARK
- if (GC_parallel) start_mark_threads();
-# endif
/* Initialize thread local free lists if used. */
# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
LOCK();
me -> flags |= FINISHED;
}
# if defined(THREAD_LOCAL_ALLOC) && !defined(USE_PTHREAD_SPECIFIC) \
- && !defined(USE_HPUX_TLS) && !defined(DBG_HDRS_ALL)
+ && !defined(USE_COMPILER_TLS) && !defined(DBG_HDRS_ALL)
GC_remove_specific(GC_thread_key);
# endif
+ /* The following may run the GC from "nonexistent" thread. */
GC_wait_for_gc_completion(FALSE);
UNLOCK();
}
return result;
}
+GC_bool GC_in_thread_creation = FALSE;
+
+GC_PTR GC_get_thread_stack_base()
+{
+# ifdef HAVE_PTHREAD_GETATTR_NP
+ pthread_t my_pthread;
+ pthread_attr_t attr;
+ ptr_t stack_addr;
+ size_t stack_size;
+
+ my_pthread = pthread_self();
+ if (pthread_getattr_np (my_pthread, &attr) != 0)
+ {
+# ifdef DEBUG_THREADS
+ GC_printf1("Can not determine stack base for attached thread");
+# endif
+ return 0;
+ }
+ pthread_attr_getstack (&attr, (void **) &stack_addr, &stack_size);
+ pthread_attr_destroy (&attr);
+
+# ifdef DEBUG_THREADS
+ GC_printf1("attached thread stack address: 0x%x\n", stack_addr);
+# endif
+
+# ifdef STACK_GROWS_DOWN
+ return stack_addr + stack_size;
+# else
+ return stack_addr;
+# endif
+
+# else
+# ifdef DEBUG_THREADS
+ GC_printf1("Can not determine stack base for attached thread");
+# endif
+ return 0;
+# endif
+}
+
+void GC_register_my_thread()
+{
+ GC_thread me;
+ pthread_t my_pthread;
+
+ my_pthread = pthread_self();
+# ifdef DEBUG_THREADS
+ GC_printf1("Attaching thread 0x%lx\n", my_pthread);
+ GC_printf1("pid = %ld\n", (long) getpid());
+# endif
+
+ /* Check to ensure this thread isn't attached already. */
+ LOCK();
+ me = GC_lookup_thread (my_pthread);
+ UNLOCK();
+ if (me != 0)
+ {
+# ifdef DEBUG_THREADS
+ GC_printf1("Attempt to re-attach known thread 0x%lx\n", my_pthread);
+# endif
+ return;
+ }
+
+ LOCK();
+ GC_in_thread_creation = TRUE;
+ me = GC_new_thread(my_pthread);
+ GC_in_thread_creation = FALSE;
+
+ me -> flags |= DETACHED;
+
+#ifdef GC_DARWIN_THREADS
+ me -> stop_info.mach_thread = mach_thread_self();
+#else
+ me -> stack_end = GC_get_thread_stack_base();
+ if (me -> stack_end == 0)
+ GC_abort("Can not determine stack base for attached thread");
+
+# ifdef STACK_GROWS_DOWN
+ me -> stop_info.stack_ptr = me -> stack_end - 0x10;
+# else
+ me -> stop_info.stack_ptr = me -> stack_end + 0x10;
+# endif
+#endif
+
+# ifdef IA64
+ me -> backing_store_end = (ptr_t)
+ (GC_save_regs_in_stack() & ~(GC_page_size - 1));
+ /* This is also < 100% convincing. We should also read this */
+ /* from /proc, but the hook to do so isn't there yet. */
+# endif /* IA64 */
+
+# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
+ GC_init_thread_local(me);
+# endif
+ UNLOCK();
+}
+
+void GC_unregister_my_thread()
+{
+ pthread_t my_pthread;
+
+ my_pthread = pthread_self();
+
+# ifdef DEBUG_THREADS
+ GC_printf1("Detaching thread 0x%lx\n", my_pthread);
+# endif
+
+ GC_thread_exit_proc (0);
+}
+
void * GC_start_routine(void * arg)
{
int dummy;
GC_printf1("sp = 0x%lx\n", (long) &arg);
# endif
LOCK();
+ GC_in_thread_creation = TRUE;
me = GC_new_thread(my_pthread);
+ GC_in_thread_creation = FALSE;
#ifdef GC_DARWIN_THREADS
me -> stop_info.mach_thread = mach_thread_self();
#else
if (!GC_thr_initialized) GC_thr_init();
# ifdef GC_ASSERTIONS
{
- int stack_size;
+ size_t stack_size;
if (NULL == attr) {
pthread_attr_t my_attr;
pthread_attr_init(&my_attr);
} else {
pthread_attr_getstacksize(attr, &stack_size);
}
- GC_ASSERT(stack_size >= (8*HBLKSIZE*sizeof(word)));
+# ifdef PARALLEL_MARK
+ GC_ASSERT(stack_size >= (8*HBLKSIZE*sizeof(word)));
+# else
+ /* FreeBSD-5.3/Alpha: default pthread stack is 64K, */
+ /* HBLKSIZE=8192, sizeof(word)=8 */
+ GC_ASSERT(stack_size >= 65536);
+# endif
/* Our threads may need to do some work for the GC. */
/* Ridiculously small threads won't work, and they */
/* probably wouldn't work anyway. */
void GC_pause()
{
int i;
-# ifndef __GNUC__
- volatile word dummy = 0;
-# endif
+# if !defined(__GNUC__) || defined(__INTEL_COMPILER)
+ volatile word dummy = 0;
+# endif
for (i = 0; i < 10; ++i) {
-# ifdef __GNUC__
+# if defined(__GNUC__) && !defined(__INTEL_COMPILER)
__asm__ __volatile__ (" " : : : "memory");
# else
/* Something that's unlikely to be optimized away. */
}
}
-#define SPIN_MAX 1024 /* Maximum number of calls to GC_pause before */
+#define SPIN_MAX 128 /* Maximum number of calls to GC_pause before */
/* give up. */
VOLATILE GC_bool GC_collecting = 0;
/* yield by calling pthread_mutex_lock(); it never makes sense to */
/* explicitly sleep. */
+#define LOCK_STATS
+#ifdef LOCK_STATS
+ unsigned long GC_spin_count = 0;
+ unsigned long GC_block_count = 0;
+ unsigned long GC_unlocked_count = 0;
+#endif
+
void GC_generic_lock(pthread_mutex_t * lock)
{
#ifndef NO_PTHREAD_TRYLOCK
unsigned pause_length = 1;
unsigned i;
- if (0 == pthread_mutex_trylock(lock)) return;
+ if (0 == pthread_mutex_trylock(lock)) {
+# ifdef LOCK_STATS
+ ++GC_unlocked_count;
+# endif
+ return;
+ }
for (; pause_length <= SPIN_MAX; pause_length <<= 1) {
for (i = 0; i < pause_length; ++i) {
GC_pause();
}
switch(pthread_mutex_trylock(lock)) {
case 0:
+# ifdef LOCK_STATS
+ ++GC_spin_count;
+# endif
return;
case EBUSY:
break;
}
}
#endif /* !NO_PTHREAD_TRYLOCK */
+# ifdef LOCK_STATS
+ ++GC_block_count;
+# endif
pthread_mutex_lock(lock);
}