/* 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
#ifdef USE_COMPILER_TLS
- __thread
+ __thread MONO_TLS_FAST
#endif
GC_key_t GC_thread_key;
/* 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_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);
+ 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;
}
}
+/* Similar to GC_local_gcj_malloc, but the size is in words, and we don't */
+/* adjust it. The size is assumed to be such that it can be */
+/* allocated as a small object. */
+void * GC_local_gcj_fast_malloc(size_t lw, void * ptr_to_struct_containing_descr)
+{
+ ptr_t * my_fl = ((GC_thread)GC_getspecific(GC_thread_key))
+ -> gcj_freelists + lw;
+ ptr_t my_entry = *my_fl;
+
+ GC_ASSERT(GC_gcj_malloc_initialized);
+
+ if (EXPECT((word)my_entry >= HBLKSIZE, 1)) {
+ GC_PTR result = (GC_PTR)my_entry;
+ GC_ASSERT(!GC_incremental);
+ /* We assert that any concurrent marker will stop us. */
+ /* Thus it is impossible for a mark procedure to see the */
+ /* allocation of the next object, but to see this object */
+ /* still containing a free list pointer. Otherwise the */
+ /* marker might find a random "mark descriptor". */
+ *(volatile ptr_t *)my_fl = obj_link(my_entry);
+ /* We must update the freelist before we store the pointer. */
+ /* Otherwise a GC at this point would see a corrupted */
+ /* free list. */
+ /* A memory barrier is probably never needed, since the */
+ /* action of stopping this thread will cause prior writes */
+ /* to complete. */
+ GC_ASSERT(((void * volatile *)result)[1] == 0);
+ *(void * volatile *)result = ptr_to_struct_containing_descr;
+ return result;
+ } else if ((word)my_entry - 1 < DIRECT_GRANULES) {
+ if (!GC_incremental) *my_fl = my_entry + lw + 1;
+ /* In the incremental case, we always have to take this */
+ /* path. Thus we leave the counter alone. */
+ return GC_gcj_fast_malloc(lw, ptr_to_struct_containing_descr);
+ } else {
+ GC_generic_malloc_many(BYTES_FROM_INDEX(lw), GC_gcj_kind, my_fl);
+ if (*my_fl == 0) return GC_oom_fn(BYTES_FROM_INDEX(lw));
+ return GC_local_gcj_fast_malloc(lw, ptr_to_struct_containing_descr);
+ }
+}
+
#endif /* GC_GCJ_SUPPORT */
# else /* !THREAD_LOCAL_ALLOC && !DBG_HDRS_ALL */
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 */
return(p);
}
+int GC_thread_is_registered (void)
+{
+ void *ptr;
+
+ LOCK();
+ ptr = (void *)GC_lookup_thread(pthread_self());
+ UNLOCK();
+
+ return ptr ? 1 : 0;
+}
+
#ifdef HANDLE_FORK
/* Remove all entries from the GC_threads table, except the */
/* one for the current thread. We need to do this in the child */
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, */
/* from /proc, but the hook to do so isn't there yet. */
# endif /* IA64 */
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;
+
+ GC_printf1( "GC_thread_register_foreign %p\n", &si );
+
+ 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());