# endif
# if (defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) || \
- defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS)) \
- && !defined(USE_PTHREAD_SPECIFIC)
+ defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS)) || \
+ defined(GC_NETBSD_THREADS) && !defined(USE_PTHREAD_SPECIFIC) || \
+ defined(GC_OPENBSD_THREADS)
# 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 <sys/sysctl.h>
#endif /* GC_DARWIN_THREADS */
+#if defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS)
+# include <sys/param.h>
+# include <sys/sysctl.h>
+#endif
+
#if defined(GC_DGUX386_THREADS)
# endif
# undef pthread_join
# undef pthread_detach
+# if defined(NACL)
+# undef pthread_exit
+# endif
# if defined(GC_OSF1_THREADS) && defined(_PTHREAD_USE_MANGLED_NAMES_) \
&& !defined(_PTHREAD_USE_PTDNAM_)
/* Restore the original mangled names on Tru64 UNIX. */
#include "mono/utils/mono-compiler.h"
static
+GC_key_t GC_thread_key;
+
#ifdef USE_COMPILER_TLS
- __thread MONO_TLS_FAST
+__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_thread(pthread_t id);
+void GC_delete_gc_thread(pthread_t id, GC_thread gct);
+void GC_destroy_thread_local(GC_thread p);
-/* Called by pthreads when the TLS entry is destroyed */
-static void GC_thread_deregister_foreign_internal (void *data)
+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" ); */
- GC_delete_thread(me->id);
+#if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
+ GC_destroy_thread_local (me);
+#endif
+ GC_delete_gc_thread(me->id, me);
UNLOCK();
}
}
int i;
if (!keys_initialized) {
- if (0 != GC_key_create(&GC_thread_key, GC_thread_deregister_foreign_internal)) {
+ if (0 != GC_key_create(&GC_thread_key, GC_thread_deregister_foreign)) {
ABORT("Failed to create key for local allocator");
}
keys_initialized = TRUE;
# ifdef GC_GCJ_SUPPORT
return_freelists(p -> gcj_freelists, GC_gcjobjfreelist);
# endif
- GC_setspecific(GC_thread_key, NULL);
}
extern GC_PTR GC_generic_malloc_many();
static struct GC_Thread_Rep first_thread;
+#ifdef NACL
+extern int nacl_thread_parked[MAX_NACL_GC_THREADS];
+extern int nacl_thread_used[MAX_NACL_GC_THREADS];
+extern int nacl_thread_parking_inited;
+extern int nacl_num_gc_threads;
+extern pthread_mutex_t nacl_thread_alloc_lock;
+extern __thread int nacl_thread_idx;
+extern __thread GC_thread nacl_gc_thread_self;
+
+void nacl_initialize_gc_thread()
+{
+ int i;
+ pthread_mutex_lock(&nacl_thread_alloc_lock);
+ if (!nacl_thread_parking_inited)
+ {
+ for (i = 0; i < MAX_NACL_GC_THREADS; i++) {
+ nacl_thread_used[i] = 0;
+ nacl_thread_parked[i] = 0;
+ }
+ nacl_thread_parking_inited = 1;
+ }
+ GC_ASSERT(nacl_num_gc_threads <= MAX_NACL_GC_THREADS);
+ for (i = 0; i < MAX_NACL_GC_THREADS; i++) {
+ if (nacl_thread_used[i] == 0) {
+ nacl_thread_used[i] = 1;
+ nacl_thread_idx = i;
+ nacl_num_gc_threads++;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&nacl_thread_alloc_lock);
+}
+
+void nacl_shutdown_gc_thread()
+{
+ pthread_mutex_lock(&nacl_thread_alloc_lock);
+ GC_ASSERT(nacl_thread_idx >= 0 && nacl_thread_idx < MAX_NACL_GC_THREADS);
+ GC_ASSERT(nacl_thread_used[nacl_thread_idx] != 0);
+ nacl_thread_used[nacl_thread_idx] = 0;
+ nacl_thread_idx = -1;
+ nacl_num_gc_threads--;
+ pthread_mutex_unlock(&nacl_thread_alloc_lock);
+}
+
+#endif /* NACL */
+
/* Add a thread to GC_threads. We assume it wasn't already there. */
/* Caller holds allocation lock. */
GC_thread GC_new_thread(pthread_t id)
}
if (result == 0) return(0);
result -> id = id;
+#ifdef PLATFORM_ANDROID
+ result -> kernel_id = gettid();
+#endif
result -> next = GC_threads[hv];
GC_threads[hv] = result;
+#ifdef NACL
+ nacl_gc_thread_self = result;
+ nacl_initialize_gc_thread();
+#endif
GC_ASSERT(result -> flags == 0 && result -> thread_blocked == 0);
return(result);
}
int hv = ((word)id) % THREAD_TABLE_SZ;
register GC_thread p = GC_threads[hv];
register GC_thread prev = 0;
+
+#ifdef NACL
+ nacl_shutdown_gc_thread();
+ nacl_gc_thread_self = NULL;
+#endif
while (!pthread_equal(p -> id, id)) {
prev = p;
} 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 */
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();
GC_nprocs = sysconf(_SC_NPROC_ONLN);
if (GC_nprocs <= 0) GC_nprocs = 1;
# endif
-# if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS)
+# if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS)
int ncpus = 1;
size_t len = sizeof(ncpus);
sysctl((int[2]) {CTL_HW, HW_NCPU}, 2, &ncpus, &len, NULL, 0);
GC_markers = atoi(markers_string);
} else {
GC_markers = GC_nprocs;
+ if (GC_markers > MAX_MARKERS)
+ GC_markers = MAX_MARKERS;
}
}
# endif
}
-#if !defined(GC_DARWIN_THREADS)
+#if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_THREADS)
+#ifndef NACL
int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, sigset_t *oset)
{
sigset_t fudged_set;
}
return(REAL_FUNC(pthread_sigmask)(how, set, oset));
}
+#endif
#endif /* !GC_DARWIN_THREADS */
/* Wrappers for functions that are likely to block for an appreciable */
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;
return result;
}
+#ifdef NACL
+/* Native Client doesn't support pthread cleanup functions, */
+/* so wrap pthread_exit and manually cleanup the thread. */
+void
+WRAP_FUNC(pthread_exit)(void *status)
+{
+ GC_thread_exit_proc(0);
+ REAL_FUNC(pthread_exit)(status);
+}
+#endif
+
int
WRAP_FUNC(pthread_detach)(pthread_t thread)
{
/* 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();
if (start) *start = si -> start_routine;
if (start_arg) *start_arg = si -> arg;
- sem_post(&(si -> registered)); /* Last action on si. */
+ if (!(si->flags & FOREIGN_THREAD))
+ sem_post(&(si -> registered)); /* Last action on si. */
/* OK to deallocate. */
# if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
LOCK();
return me != NULL;
}
-void GC_thread_deregister_foreign (void)
-{
- GC_thread me;
-
- LOCK ();
- me = GC_lookup_thread (pthread_self ());
- if (me->flags & FOREIGN_THREAD) {
- GC_delete_thread (me->id);
-#if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
- GC_destroy_thread_local (me);
-#endif
- }
- UNLOCK ();
-}
-
void * GC_start_routine(void * arg)
{
int dummy;