* 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.
# 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_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(GC_NETBSD_THREADS) && !defined(USE_PTHREAD_SPECIFIC)
# define USE_PTHREAD_SPECIFIC
# endif
# if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_COMPILER_TLS)
# include "private/specific.h"
# endif
+
+/* Note that these macros should be used only to get/set the GC_thread pointer.
+ * We need to use both tls and pthread because we use the pthread_create function hook to
+ * free the data for foreign threads. When that doesn't happen, libgc could have old
+ * pthread_t that get reused...
+ */
# if defined(USE_PTHREAD_SPECIFIC)
# define GC_getspecific pthread_getspecific
# define GC_setspecific pthread_setspecific
typedef pthread_key_t GC_key_t;
# endif
# 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
- typedef void * GC_key_t;
+/* Note sles9 gcc on powerpc gets confused by the define to set GC_thread_tls and pthread_setspecific
+ * so we actually use a static inline function decalred below that is equivalent to:
+ * define GC_setspecific(key, v) (GC_thread_tls = (v), pthread_setspecific ((key), (v)))
+ */
+# define GC_getspecific(x) (GC_thread_tls)
+# define GC_key_create pthread_key_create
+ typedef pthread_key_t GC_key_t;
# endif
# endif
# include <stdlib.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 */
+#if defined(GC_NETBSD_THREADS)
+# include <sys/param.h>
+# include <sys/sysctl.h>
+#endif
+
#if defined(GC_DGUX386_THREADS)
/* We don't really support thread-local allocation with DBG_HDRS_ALL */
+/* work around a dlopen issue (bug #75390), undefs to avoid warnings with redefinitions */
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#include "mono/utils/mono-compiler.h"
+
static
+GC_key_t GC_thread_key;
+
#ifdef USE_COMPILER_TLS
- __thread
+__thread MONO_TLS_FAST void* GC_thread_tls;
+
+/*
+ * gcc errors out with /tmp/ccdPMFuq.s:2994: Error: symbol `.LTLS4' is already defined
+ * if the inline is added on powerpc
+ */
+#if !defined(__ppc__) && !defined(__powerpc__)
+inline
+#endif
+static int GC_setspecific (GC_key_t key, void *value) {
+ GC_thread_tls = value;
+ return pthread_setspecific (key, value);
+}
#endif
-GC_key_t GC_thread_key;
static GC_bool keys_initialized;
+#ifdef MONO_DEBUGGER_SUPPORTED
+#include "include/libgc-mono-debugger.h"
+#endif
+
/* Recover the contents of the freelist array fl into the global one gfl.*/
/* Note that the indexing scheme differs, in that gfl has finer size */
/* resolution, even if not all entries are used. */
/* we arrange for those to fault asap.) */
static ptr_t size_zero_object = (ptr_t)(&size_zero_object);
+void GC_delete_gc_thread(pthread_t id, GC_thread gct);
+void GC_destroy_thread_local(GC_thread p);
+
+void GC_thread_deregister_foreign (void *data)
+{
+ GC_thread me = (GC_thread)data;
+ /* GC_fprintf1( "\n\n\n\n --- Deregister %x ---\n\n\n\n\n", me->flags ); */
+ if (me -> flags & FOREIGN_THREAD) {
+ LOCK();
+ /* GC_fprintf0( "\n\n\n\n --- FOO ---\n\n\n\n\n" ); */
+#if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
+ GC_destroy_thread_local (me);
+#endif
+ GC_delete_gc_thread(me->id, me);
+ UNLOCK();
+ }
+}
+
/* Each thread structure must be initialized. */
/* This call must be made from the new thread. */
/* Caller holds allocation lock. */
int i;
if (!keys_initialized) {
- if (0 != GC_key_create(&GC_thread_key, 0)) {
+ if (0 != GC_key_create(&GC_thread_key, GC_thread_deregister_foreign)) {
ABORT("Failed to create key for local allocator");
}
keys_initialized = TRUE;
volatile GC_thread GC_threads[THREAD_TABLE_SZ];
+/*
+ * gcc-3.3.6 miscompiles the &GC_thread_key+sizeof(&GC_thread_key) expression so
+ * put it into a separate function.
+ */
+# if defined(__GNUC__) && defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
+static __attribute__((noinline)) unsigned char* get_gc_thread_key_addr GC_PROTO((void))
+{
+ return (unsigned char*)&GC_thread_key;
+}
+
+void GC_push_thread_structures GC_PROTO((void))
+{
+ GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
+# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
+ GC_push_all((ptr_t)get_gc_thread_key_addr(),
+ (ptr_t)(get_gc_thread_key_addr())+sizeof(&GC_thread_key));
+# endif
+}
+
+#else
+
void GC_push_thread_structures GC_PROTO((void))
{
GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
# endif
}
+#endif
+
#ifdef THREAD_LOCAL_ALLOC
/* We must explicitly mark ptrfree and gcj free lists, since the free */
/* list links wouldn't otherwise be found. We also set them in the */
} else {
prev -> next = p -> next;
}
+#ifdef MONO_DEBUGGER_SUPPORTED
+ if (gc_thread_vtable && gc_thread_vtable->thread_exited)
+ gc_thread_vtable->thread_exited (id, &p->stop_info.stack_ptr);
+#endif
+
+#ifdef GC_DARWIN_THREADS
+ mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread);
+#endif
+
GC_INTERNAL_FREE(p);
}
} else {
prev -> next = p -> next;
}
+
+#ifdef GC_DARWIN_THREADS
+ mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread);
+#endif
+
GC_INTERNAL_FREE(p);
}
GC_destroy_thread_local(p);
}
# endif /* THREAD_LOCAL_ALLOC */
- if (p != &first_thread) GC_INTERNAL_FREE(p);
+ if (p != &first_thread) GC_INTERNAL_FREE(p);
}
}
GC_threads[hv] = me;
}
+ GC_INTERNAL_FREE(p);
}
#endif /* HANDLE_FORK */
/* 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;
t -> stop_info.stack_ptr = (ptr_t)(&dummy);
# endif
t -> flags = DETACHED | MAIN_THREAD;
+#ifdef MONO_DEBUGGER_SUPPORTED
+ if (gc_thread_vtable && gc_thread_vtable->thread_created)
+# ifdef GC_DARWIN_THREADS
+ gc_thread_vtable->thread_created (mach_thread_self (), &t->stop_info.stack_ptr);
+# else
+ gc_thread_vtable->thread_created (pthread_self (), &t->stop_info.stack_ptr);
+# endif
+#endif
GC_stop_init();
# 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)
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) || defined(GC_NETBSD_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 = GC_lookup_thread(pthread_self());
GC_destroy_thread_local(me);
if (me -> flags & DETACHED) {
+# ifdef THREAD_LOCAL_ALLOC
+ /* NULL out the tls key to prevent the dtor function from being called */
+ if (0 != GC_setspecific(GC_thread_key, NULL))
+ ABORT("Failed to set thread specific allocation pointers");
+#endif
GC_delete_thread(pthread_self());
} else {
me -> flags |= FINISHED;
GC_bool GC_in_thread_creation = FALSE;
-void * GC_start_routine(void * arg)
+typedef void *(*ThreadStartFn)(void *);
+void * GC_start_routine_head(void * arg, void *base_addr,
+ ThreadStartFn *start, void **start_arg )
{
- int dummy;
struct start_info * si = arg;
void * result;
GC_thread me;
pthread_t my_pthread;
- void *(*start)(void *);
- void *start_arg;
my_pthread = pthread_self();
# ifdef DEBUG_THREADS
/* one for the main thread. There is a strong argument that that's */
/* a kernel bug, but a pervasive one. */
# ifdef STACK_GROWS_DOWN
- me -> stack_end = (ptr_t)(((word)(&dummy) + (GC_page_size - 1))
+ me -> stack_end = (ptr_t)(((word)(base_addr) + (GC_page_size - 1))
& ~(GC_page_size - 1));
# ifndef GC_DARWIN_THREADS
me -> stop_info.stack_ptr = me -> stack_end - 0x10;
/* Needs to be plausible, since an asynchronous stack mark */
/* should not crash. */
# else
- me -> stack_end = (ptr_t)((word)(&dummy) & ~(GC_page_size - 1));
+ me -> stack_end = (ptr_t)((word)(base_addr) & ~(GC_page_size - 1));
me -> stop_info.stack_ptr = me -> stack_end + 0x10;
# endif
/* This is dubious, since we may be more than a page into the stack, */
/* This is also < 100% convincing. We should also read this */
/* from /proc, but the hook to do so isn't there yet. */
# endif /* IA64 */
+#ifdef MONO_DEBUGGER_SUPPORTED
+ if (gc_thread_vtable && gc_thread_vtable->thread_created)
+# ifdef GC_DARWIN_THREADS
+ gc_thread_vtable->thread_created (mach_thread_self(), &me->stop_info.stack_ptr);
+# else
+ gc_thread_vtable->thread_created (my_pthread, &me->stop_info.stack_ptr);
+# endif
+#endif
UNLOCK();
- start = si -> start_routine;
-# ifdef DEBUG_THREADS
- GC_printf1("start_routine = 0x%lx\n", start);
-# endif
- start_arg = si -> arg;
+
+ if (start) *start = si -> start_routine;
+ if (start_arg) *start_arg = si -> arg;
+
sem_post(&(si -> registered)); /* Last action on si. */
/* OK to deallocate. */
- pthread_cleanup_push(GC_thread_exit_proc, 0);
# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
LOCK();
GC_init_thread_local(me);
UNLOCK();
# endif
+
+ return me;
+}
+
+int GC_thread_register_foreign (void *base_addr)
+{
+ struct start_info si = { 0, }; /* stacked for legibility & locking */
+ GC_thread me;
+
+# ifdef DEBUG_THREADS
+ GC_printf1( "GC_thread_register_foreign %p\n", &si );
+# endif
+
+ si.flags = FOREIGN_THREAD;
+
+ if (!parallel_initialized) GC_init_parallel();
+ LOCK();
+ if (!GC_thr_initialized) GC_thr_init();
+
+ UNLOCK();
+
+ me = GC_start_routine_head(&si, base_addr, NULL, NULL);
+
+ return me != NULL;
+}
+
+void * GC_start_routine(void * arg)
+{
+ int dummy;
+ struct start_info * si = arg;
+ void * result;
+ GC_thread me;
+ ThreadStartFn start;
+ void *start_arg;
+
+ me = GC_start_routine_head (arg, &dummy, &start, &start_arg);
+
+ pthread_cleanup_push(GC_thread_exit_proc, 0);
+# ifdef DEBUG_THREADS
+ GC_printf1("start_routine = 0x%lx\n", start);
+# endif
result = (*start)(start_arg);
#if DEBUG_THREADS
GC_printf1("Finishing thread 0x%x\n", pthread_self());
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. */