Move thread management from sgen to utils. Move smr from MonoInternalThread to thread...
[mono.git] / mono / metadata / sgen-gc.c
index c88c049276c6b3b3f5dc42c97d5640022f173e93..f0a263fe0a436db4c8993cf56aee3a4603d03c98 100644 (file)
 #ifdef __MACH__
 #define _XOPEN_SOURCE
 #endif
+
+#include "metadata/sgen-gc.h"
 #include "metadata/metadata-internals.h"
 #include "metadata/class-internals.h"
 #include "metadata/gc-internal.h"
 #include "metadata/object-internals.h"
 #include "metadata/threads.h"
-#include "metadata/sgen-gc.h"
 #include "metadata/sgen-cardtable.h"
 #include "metadata/sgen-protocol.h"
 #include "metadata/sgen-archdep.h"
@@ -256,6 +257,8 @@ static gboolean conservative_stack_mark = FALSE;
 /* If set, do a plausibility check on the scan_starts before and after
    each collection */
 static gboolean do_scan_starts_check = FALSE;
+static gboolean disable_minor_collections = FALSE;
+static gboolean disable_major_collections = FALSE;
 
 #ifdef HEAVY_STATISTICS
 static long long stat_objects_alloced = 0;
@@ -859,7 +862,7 @@ static void check_scan_starts (void);
 static void check_for_xdomain_refs (void);
 static void dump_heap (const char *type, int num, const char *reason);
 
-void mono_gc_scan_for_specific_ref (MonoObject *key);
+void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise);
 
 static void init_stats (void);
 
@@ -1174,7 +1177,7 @@ check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
                        o, o->vtable->klass->name_space, o->vtable->klass->name,
                        offset, field ? field->name : "",
                        ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
-       mono_gc_scan_for_specific_ref (o);
+       mono_gc_scan_for_specific_ref (o, TRUE);
        if (str)
                g_free (str);
 }
@@ -1190,6 +1193,8 @@ scan_object_for_xdomain_refs (char *start, mword size, void *data)
        #include "sgen-scan-object.h"
 }
 
+static gboolean scan_object_for_specific_ref_precise = TRUE;
+
 #undef HANDLE_PTR
 #define HANDLE_PTR(ptr,obj) do {               \
        if ((MonoObject*)*(ptr) == key) {       \
@@ -1206,7 +1211,19 @@ scan_object_for_specific_ref (char *start, MonoObject *key)
        if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
                start = forwarded;
 
-       #include "sgen-scan-object.h"
+       if (scan_object_for_specific_ref_precise) {
+               #include "sgen-scan-object.h"
+       } else {
+               mword *words = (mword*)start;
+               size_t size = safe_object_get_size ((MonoObject*)start);
+               int i;
+               for (i = 0; i < size / sizeof (mword); ++i) {
+                       if (words [i] == (mword)key) {
+                               g_print ("found possible ref to %p in object %p (%s) at offset %td\n",
+                                               key, start, safe_name (start), i * sizeof (mword));
+                       }
+               }
+       }
 }
 
 void
@@ -1317,11 +1334,13 @@ scan_roots_for_specific_ref (MonoObject *key, int root_type)
 }
 
 void
-mono_gc_scan_for_specific_ref (MonoObject *key)
+mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise)
 {
        RootRecord *root;
        int i;
 
+       scan_object_for_specific_ref_precise = precise;
+
        mono_sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
                        (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
 
@@ -2210,6 +2229,19 @@ alloc_fragment (void)
        frag->next = NULL;
        return frag;
 }
+static void
+add_fragment (char *start, char *end)
+{
+       Fragment *fragment;
+
+       fragment = alloc_fragment ();
+       fragment->fragment_start = start;
+       fragment->fragment_limit = start;
+       fragment->fragment_end = end;
+       fragment->next = nursery_fragments;
+       nursery_fragments = fragment;
+}
 
 /* size must be a power of 2 */
 void*
@@ -2242,7 +2274,6 @@ alloc_nursery (void)
        GCMemSection *section;
        char *data;
        int scan_starts;
-       Fragment *frag;
        int alloc_size;
 
        if (nursery_section)
@@ -2265,7 +2296,6 @@ alloc_nursery (void)
        nursery_start = data;
        nursery_end = nursery_start + nursery_size;
        mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
-       nursery_next = nursery_start;
        DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %lu, total: %lu\n", data, data + alloc_size, (unsigned long)nursery_size, (unsigned long)total_alloc));
        section->data = section->next_data = data;
        section->size = alloc_size;
@@ -2279,12 +2309,7 @@ alloc_nursery (void)
        nursery_section = section;
 
        /* Setup the single first large fragment */
-       frag = alloc_fragment ();
-       frag->fragment_start = nursery_start;
-       frag->fragment_limit = nursery_start;
-       frag->fragment_end = nursery_end;
-       nursery_frag_real_end = nursery_end;
-       /* FIXME: frag here is lost */
+       add_fragment (nursery_start, nursery_end);
 }
 
 void*
@@ -2433,7 +2458,6 @@ static mword fragment_total = 0;
 static void
 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
 {
-       Fragment *fragment;
        DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
        binary_protocol_empty (frag_start, frag_size);
        /* Not worth dealing with smaller fragments: need to tune */
@@ -2442,12 +2466,7 @@ add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
                if (nursery_clear_policy == CLEAR_AT_GC)
                        memset (frag_start, 0, frag_size);
 
-               fragment = alloc_fragment ();
-               fragment->fragment_start = frag_start;
-               fragment->fragment_limit = frag_start;
-               fragment->fragment_end = frag_end;
-               fragment->next = nursery_fragments;
-               nursery_fragments = fragment;
+               add_fragment (frag_start, frag_end);
                fragment_total += frag_size;
        } else {
                /* Clear unused fragments, pinning depends on this */
@@ -3021,6 +3040,40 @@ job_scan_from_remsets (WorkerData *worker_data, void *job_data_untyped)
        scan_from_remsets (job_data->heap_start, job_data->heap_end, job_gray_queue (worker_data));
 }
 
+typedef struct
+{
+       CopyOrMarkObjectFunc func;
+       char *heap_start;
+       char *heap_end;
+       int root_type;
+} ScanFromRegisteredRootsJobData;
+
+static void
+job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
+{
+       ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
+
+       scan_from_registered_roots (job_data->func,
+                       job_data->heap_start, job_data->heap_end,
+                       job_data->root_type,
+                       job_gray_queue (worker_data));
+}
+
+typedef struct
+{
+       char *heap_start;
+       char *heap_end;
+} ScanThreadDataJobData;
+
+static void
+job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
+{
+       ScanThreadDataJobData *job_data = job_data_untyped;
+
+       scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
+                       job_gray_queue (worker_data));
+}
+
 /*
  * Collect objects in the nursery.  Returns whether to trigger a major
  * collection.
@@ -3032,11 +3085,16 @@ collect_nursery (size_t requested_size)
        size_t max_garbage_amount;
        char *orig_nursery_next;
        ScanFromRemsetsJobData sfrjd;
+       ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
+       ScanThreadDataJobData stdjd;
        TV_DECLARE (all_atv);
        TV_DECLARE (all_btv);
        TV_DECLARE (atv);
        TV_DECLARE (btv);
 
+       if (disable_minor_collections)
+               return TRUE;
+
        mono_perfcounters->gc_collections0++;
 
        current_collection_generation = GENERATION_NURSERY;
@@ -3138,13 +3196,28 @@ collect_nursery (size_t requested_size)
                report_finalizer_roots ();
        TV_GETTIME (atv);
        time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
+
        /* registered roots, this includes static fields */
-       scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_NORMAL, WORKERS_DISTRIBUTE_GRAY_QUEUE);
-       scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+       scrrjd_normal.func = major_collector.copy_object;
+       scrrjd_normal.heap_start = nursery_start;
+       scrrjd_normal.heap_end = nursery_next;
+       scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
+       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
+
+       scrrjd_wbarrier.func = major_collector.copy_object;
+       scrrjd_wbarrier.heap_start = nursery_start;
+       scrrjd_wbarrier.heap_end = nursery_next;
+       scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
+       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
+
        TV_GETTIME (btv);
        time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
+
        /* thread data */
-       scan_thread_data (nursery_start, nursery_next, TRUE, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+       stdjd.heap_start = nursery_start;
+       stdjd.heap_end = nursery_next;
+       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
+
        TV_GETTIME (atv);
        time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
        btv = atv;
@@ -3229,39 +3302,6 @@ collect_nursery (size_t requested_size)
        return needs_major;
 }
 
-typedef struct
-{
-       char *heap_start;
-       char *heap_end;
-       int root_type;
-} ScanFromRegisteredRootsJobData;
-
-static void
-job_scan_from_registered_roots (WorkerData *worker_data, void *job_data_untyped)
-{
-       ScanFromRegisteredRootsJobData *job_data = job_data_untyped;
-
-       scan_from_registered_roots (major_collector.copy_or_mark_object,
-                       job_data->heap_start, job_data->heap_end,
-                       job_data->root_type,
-                       job_gray_queue (worker_data));
-}
-
-typedef struct
-{
-       char *heap_start;
-       char *heap_end;
-} ScanThreadDataJobData;
-
-static void
-job_scan_thread_data (WorkerData *worker_data, void *job_data_untyped)
-{
-       ScanThreadDataJobData *job_data = job_data_untyped;
-
-       scan_thread_data (job_data->heap_start, job_data->heap_end, TRUE,
-                       job_gray_queue (worker_data));
-}
-
 typedef struct
 {
        FinalizeEntry *list;
@@ -3410,11 +3450,13 @@ major_do_collection (const char *reason)
        time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
 
        /* registered roots, this includes static fields */
+       scrrjd_normal.func = major_collector.copy_or_mark_object;
        scrrjd_normal.heap_start = heap_start;
        scrrjd_normal.heap_end = heap_end;
        scrrjd_normal.root_type = ROOT_TYPE_NORMAL;
        workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_normal);
 
+       scrrjd_wbarrier.func = major_collector.copy_or_mark_object;
        scrrjd_wbarrier.heap_start = heap_start;
        scrrjd_wbarrier.heap_end = heap_end;
        scrrjd_wbarrier.root_type = ROOT_TYPE_WBARRIER;
@@ -3569,7 +3611,7 @@ major_do_collection (const char *reason)
 static void
 major_collection (const char *reason)
 {
-       if (g_getenv ("MONO_GC_NO_MAJOR")) {
+       if (disable_major_collections) {
                collect_nursery (0);
                return;
        }
@@ -5095,11 +5137,6 @@ mono_gc_deregister_root (char* addr)
  * ######################################################################
  */
 
-/* FIXME: handle large/small config */
-#define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
-
-SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
-
 #if USE_SIGNAL_BASED_START_STOP_WORLD
 
 static MonoSemType suspend_ack_semaphore;
@@ -5114,20 +5151,6 @@ static MonoContext cur_thread_ctx = {0};
 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
 #endif
 
-
-SgenThreadInfo*
-mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
-{
-       unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
-       SgenThreadInfo *info;
-
-       info = thread_table [hash];
-       while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
-               info = info->next;
-       }
-       return info;
-}
-
 static void
 update_current_thread_stack (void *start)
 {
@@ -5135,7 +5158,7 @@ update_current_thread_stack (void *start)
 #ifndef USE_MONO_CTX
        void *ptr = cur_thread_regs;
 #endif
-       SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+       SgenThreadInfo *info = mono_thread_info_current ();
        
        info->stack_start = align_pointer (&stack_guard);
        g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
@@ -5171,6 +5194,9 @@ is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
 void
 mono_sgen_wait_for_suspend_ack (int count)
 {
+#if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
+               /* mach thread_resume is synchronous so we dont need to wait for them */
+#else
        int i, result;
 
        for (i = 0; i < count; ++i) {
@@ -5180,31 +5206,29 @@ mono_sgen_wait_for_suspend_ack (int count)
                        }
                }
        }
+#endif
 }
 
 static int
 restart_threads_until_none_in_managed_allocator (void)
 {
        SgenThreadInfo *info;
-       int result, num_threads_died = 0;
+       int num_threads_died = 0;
        int sleep_duration = -1;
 
        for (;;) {
                int restart_count = 0, restarted_count = 0;
                /* restart all threads that stopped in the
                   allocator */
-               FOREACH_THREAD (info) {
+               FOREACH_THREAD_SAFE (info) {
+                       gboolean result;
                        if (info->skip)
                                continue;
                        if (!info->stack_start || info->in_critical_region ||
                                        is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
-                               binary_protocol_thread_restart ((gpointer)info->id);
-#if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
-                               result = thread_resume (pthread_mach_thread_np (info->id));
-#else
-                               result = pthread_kill (info->id, restart_signal_num);
-#endif
-                               if (result == 0) {
+                               binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
+                               result = mono_sgen_resume_thread (info);
+                               if (result) {
                                        ++restart_count;
                                } else {
                                        info->skip = 1;
@@ -5218,17 +5242,13 @@ restart_threads_until_none_in_managed_allocator (void)
                                info->stopped_ip = NULL;
                                info->stopped_domain = NULL;
                        }
-               } END_FOREACH_THREAD
+               } END_FOREACH_THREAD_SAFE
                /* if no threads were restarted, we're done */
                if (restart_count == 0)
                        break;
 
-#if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
-               /* mach thread_resume is synchronous so we dont need to wait for them */
-#else
                /* wait for the threads to signal their restart */
                mono_sgen_wait_for_suspend_ack (restart_count);
-#endif
 
                if (sleep_duration < 0) {
                        sched_yield ();
@@ -5240,14 +5260,12 @@ restart_threads_until_none_in_managed_allocator (void)
 
                /* stop them again */
                FOREACH_THREAD (info) {
+                       gboolean result;
                        if (info->skip || info->stopped_ip == NULL)
                                continue;
-#if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
-                       result = thread_suspend (pthread_mach_thread_np (info->id));
-#else
-                       result = pthread_kill (info->id, suspend_signal_num);
-#endif
-                       if (result == 0) {
+                       result = mono_sgen_suspend_thread (info);
+
+                       if (result) {
                                ++restarted_count;
                        } else {
                                info->skip = 1;
@@ -5255,13 +5273,9 @@ restart_threads_until_none_in_managed_allocator (void)
                } END_FOREACH_THREAD
                /* some threads might have died */
                num_threads_died += restart_count - restarted_count;
-#if defined(__MACH__) && MONO_MACH_ARCH_SUPPORTED
-               /* mach thread_resume is synchronous so we dont need to wait for them */
-#else
                /* wait for the threads to signal their suspension
                   again */
                mono_sgen_wait_for_suspend_ack (restart_count);
-#endif
        }
 
        return num_threads_died;
@@ -5272,7 +5286,6 @@ static void
 suspend_handler (int sig, siginfo_t *siginfo, void *context)
 {
        SgenThreadInfo *info;
-       pthread_t id;
        int stop_count;
        int old_errno = errno;
 #ifdef USE_MONO_CTX
@@ -5282,8 +5295,7 @@ suspend_handler (int sig, siginfo_t *siginfo, void *context)
 #endif
        gpointer stack_start;
 
-       id = pthread_self ();
-       info = mono_sgen_thread_info_lookup (id);
+       info = mono_thread_info_current ();
        info->stopped_domain = mono_domain_get ();
        info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
        stop_count = global_stop_count;
@@ -5317,7 +5329,7 @@ suspend_handler (int sig, siginfo_t *siginfo, void *context)
        if (gc_callbacks.thread_suspend_func)
                gc_callbacks.thread_suspend_func (info->runtime_data, context);
 
-       DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
+       DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
        /* notify the waiting thread */
        MONO_SEM_POST (suspend_ack_semaphore_ptr);
        info->stop_count = stop_count;
@@ -5328,7 +5340,7 @@ suspend_handler (int sig, siginfo_t *siginfo, void *context)
                sigsuspend (&suspend_signal_mask);
        } while (info->signal != restart_signal_num);
 
-       DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
+       DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
        /* notify the waiting thread */
        MONO_SEM_POST (suspend_ack_semaphore_ptr);
 
@@ -5341,9 +5353,9 @@ restart_handler (int sig)
        SgenThreadInfo *info;
        int old_errno = errno;
 
-       info = mono_sgen_thread_info_lookup (pthread_self ());
+       info = mono_thread_info_current ();
        info->signal = restart_signal_num;
-       DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
+       DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
 
        errno = old_errno;
 }
@@ -5375,7 +5387,7 @@ stop_world (int generation)
        update_current_thread_stack (&count);
 
        global_stop_count++;
-       DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()), (gpointer)ARCH_GET_THREAD ()));
+       DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ()));
        TV_GETTIME (stop_world_time);
        count = mono_sgen_thread_handshake (suspend_signal_num);
        count -= restart_threads_until_none_in_managed_allocator ();
@@ -5514,7 +5526,7 @@ find_pinning_ref_from_thread (char *obj, size_t size)
                        continue;
                while (start < (char**)info->stack_end) {
                        if (*start >= obj && *start < endobj) {
-                               DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, (gpointer)info->id, start, info->stack_start, info->stack_end));
+                               DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, (gpointer)mono_thread_info_get_tid (info), start, info->stack_start, info->stack_end));
                        }
                        start++;
                }
@@ -5527,7 +5539,7 @@ find_pinning_ref_from_thread (char *obj, size_t size)
 #endif
 
                        if (w >= (mword)obj && w < (mword)obj + size)
-                               DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in saved reg %d of thread %p (id %p)\n", obj, j, info, (gpointer)info->id));
+                               DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in saved reg %d of thread %p (id %p)\n", obj, j, info, (gpointer)mono_thread_info_get_tid (info)));
                } END_FOREACH_THREAD
        }
 }
@@ -5536,7 +5548,7 @@ static gboolean
 ptr_on_stack (void *ptr)
 {
        gpointer stack_start = &stack_start;
-       SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+       SgenThreadInfo *info = mono_thread_info_current ();
 
        if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
                return TRUE;
@@ -5887,20 +5899,14 @@ clear_tlabs (void)
        } END_FOREACH_THREAD
 }
 
-/* LOCKING: assumes the GC lock is held */
-static SgenThreadInfo*
-gc_register_current_thread (void *addr)
+static void*
+sgen_thread_register (SgenThreadInfo* info, void *addr)
 {
-       int hash;
-       SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
 #ifndef HAVE_KW_THREAD
        SgenThreadInfo *__thread_info__ = info;
 #endif
 
-       if (!info)
-               return NULL;
-
-       memset (info, 0, sizeof (SgenThreadInfo));
+       LOCK_GC;
 #ifndef HAVE_KW_THREAD
        info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
 
@@ -5910,7 +5916,6 @@ gc_register_current_thread (void *addr)
        thread_info = info;
 #endif
 
-       info->id = ARCH_GET_THREAD ();
        info->stop_count = -1;
        info->skip = 0;
        info->signal = 0;
@@ -5929,13 +5934,17 @@ gc_register_current_thread (void *addr)
        info->stopped_regs = NULL;
 #endif
 
-       binary_protocol_thread_register ((gpointer)info->id);
+       binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
 
 #ifdef HAVE_KW_THREAD
        tlab_next_addr = &tlab_next;
        store_remset_buffer_index_addr = &store_remset_buffer_index;
 #endif
 
+#if defined(__MACH__)
+       info->mach_port = mach_thread_self ();
+#endif
+
        /* try to get it with attributes first */
 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
        {
@@ -5965,11 +5974,6 @@ gc_register_current_thread (void *addr)
        stack_end = info->stack_end;
 #endif
 
-       /* hash into the table */
-       hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
-       info->next = thread_table [hash];
-       thread_table [hash] = info;
-
        info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
        pthread_setspecific (remembered_set_key, info->remset);
 #ifdef HAVE_KW_THREAD
@@ -5979,11 +5983,12 @@ gc_register_current_thread (void *addr)
        STORE_REMSET_BUFFER = mono_sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
        STORE_REMSET_BUFFER_INDEX = 0;
 
-       DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
+       DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p)\n", info, (gpointer)mono_thread_info_get_tid (info)));
 
        if (gc_callbacks.thread_attach_func)
                info->runtime_data = gc_callbacks.thread_attach_func ();
 
+       UNLOCK_GC;
        return info;
 }
 
@@ -5997,29 +6002,27 @@ add_generic_store_remset_from_buffer (gpointer *buffer)
 }
 
 static void
-unregister_current_thread (void)
+sgen_thread_unregister (SgenThreadInfo *p)
 {
-       int hash;
-       SgenThreadInfo *prev = NULL;
-       SgenThreadInfo *p;
        RememberedSet *rset;
-       ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
+
+       /* If a delegate is passed to native code and invoked on a thread we dont
+        * know about, the jit will register it with mono_jit_thread_attach, but
+        * we have no way of knowing when that thread goes away.  SGen has a TSD
+        * so we assume that if the domain is still registered, we can detach
+        * the thread
+        */
+       if (mono_domain_get ())
+               mono_thread_detach (mono_thread_current ());
+
+       LOCK_GC;
 
        binary_protocol_thread_unregister ((gpointer)id);
+       DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)mono_thread_info_get_tid (p)));
 
-       hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
-       p = thread_table [hash];
-       assert (p);
-       DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
-       while (!ARCH_THREAD_EQUALS (p->id, id)) {
-               prev = p;
-               p = p->next;
-       }
-       if (prev == NULL) {
-               thread_table [hash] = p->next;
-       } else {
-               prev->next = p->next;
-       }
+#if defined(__MACH__)
+       mach_port_deallocate (current_task (), p->mach_port);
+#endif
 
        if (gc_callbacks.thread_detach_func) {
                gc_callbacks.thread_detach_func (p->runtime_data);
@@ -6039,49 +6042,31 @@ unregister_current_thread (void)
        if (*p->store_remset_buffer_index_addr)
                add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
        mono_sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
-       free (p);
-}
-
-static void
-unregister_thread (void *k)
-{
-       /* If a delegate is passed to native code and invoked on a thread we dont
-        * know about, the jit will register it with mono_jit_thead_attach, but
-        * we have no way of knowing when that thread goes away.  SGen has a TSD
-        * so we assume that if the domain is still registered, we can detach
-        * the thread
-        */
-       if (mono_domain_get ())
-               mono_thread_detach (mono_thread_current ());
-
-       LOCK_GC;
-       unregister_current_thread ();
        UNLOCK_GC;
 }
 
-gboolean
-mono_gc_register_thread (void *baseptr)
-{
-       SgenThreadInfo *info;
 
+static void
+sgen_thread_attach (SgenThreadInfo *info)
+{
        LOCK_GC;
+       /*this is odd, can we get attached before the gc is inited?*/
        init_stats ();
-       info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
-       if (info == NULL) {
-               info = gc_register_current_thread (baseptr);
-       } else {
-               /* The main thread might get registered before callbacks are set */
-               if (gc_callbacks.thread_attach_func && !info->runtime_data)
-                       info->runtime_data = gc_callbacks.thread_attach_func ();
-       }
        UNLOCK_GC;
+       
+       if (gc_callbacks.thread_attach_func && !info->runtime_data)
+               info->runtime_data = gc_callbacks.thread_attach_func ();
 
        /* Need a better place to initialize this */
        if (!array_fill_vtable && mono_get_root_domain ()) {
                array_fill_vtable = mono_class_vtable (mono_get_root_domain (), mono_array_class_get (mono_defaults.byte_class, 1));
        }
-
-       return info != NULL;
+       
+}
+gboolean
+mono_gc_register_thread (void *baseptr)
+{
+       return mono_thread_info_attach (baseptr) != NULL;
 }
 
 /*
@@ -6096,7 +6081,7 @@ mono_gc_set_stack_end (void *stack_end)
        SgenThreadInfo *info;
 
        LOCK_GC;
-       info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+       info = mono_thread_info_current ();
        if (info) {
                g_assert (stack_end < info->stack_end);
                info->stack_end = stack_end;
@@ -6106,62 +6091,11 @@ mono_gc_set_stack_end (void *stack_end)
 
 #if USE_PTHREAD_INTERCEPT
 
-typedef struct {
-       void *(*start_routine) (void *);
-       void *arg;
-       int flags;
-       MonoSemType registered;
-} SgenThreadStartInfo;
-
-static void*
-gc_start_thread (void *arg)
-{
-       SgenThreadStartInfo *start_info = arg;
-       SgenThreadInfo* info;
-       void *t_arg = start_info->arg;
-       void *(*start_func) (void*) = start_info->start_routine;
-       void *result;
-       int post_result;
-
-       LOCK_GC;
-       info = gc_register_current_thread (&result);
-       UNLOCK_GC;
-       post_result = MONO_SEM_POST (&(start_info->registered));
-       g_assert (!post_result);
-       result = start_func (t_arg);
-       g_assert (!mono_domain_get ());
-       /*
-        * this is done by the pthread key dtor
-       LOCK_GC;
-       unregister_current_thread ();
-       UNLOCK_GC;
-       */
-
-       return result;
-}
 
 int
 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
 {
-       SgenThreadStartInfo *start_info;
-       int result;
-
-       start_info = malloc (sizeof (SgenThreadStartInfo));
-       if (!start_info)
-               return ENOMEM;
-       MONO_SEM_INIT (&(start_info->registered), 0);
-       start_info->arg = arg;
-       start_info->start_routine = start_routine;
-
-       result = pthread_create (new_thread, attr, gc_start_thread, start_info);
-       if (result == 0) {
-               while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
-                       /*if (EINTR != errno) ABORT("sem_wait failed"); */
-               }
-       }
-       MONO_SEM_DESTROY (&(start_info->registered));
-       free (start_info);
-       return result;
+       return mono_threads_pthread_create (new_thread, attr, start_routine, arg);
 }
 
 int
@@ -6250,7 +6184,7 @@ mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* val
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
 #ifdef HAVE_KW_THREAD
-               mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+               mono_thread_info_current ()->remset = rs;
 #endif
                *(rs->store_next++) = (mword)field_ptr;
                *(void**)field_ptr = value;
@@ -6288,7 +6222,7 @@ mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* va
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
 #ifdef HAVE_KW_THREAD
-               mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+               mono_thread_info_current ()->remset = rs;
 #endif
                *(rs->store_next++) = (mword)slot_ptr;
                *(void**)slot_ptr = value;
@@ -6351,7 +6285,7 @@ mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
 #ifdef HAVE_KW_THREAD
-               mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+               mono_thread_info_current ()->remset = rs;
 #endif
                *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
                *(rs->store_next++) = count;
@@ -6540,9 +6474,8 @@ mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *
                rs = alloc_remset (rs->end_set - rs->data, (void*)1);
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
-#ifdef HAVE_KW_THREAD
-               mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
-#endif
+
+               mono_thread_info_current ()->remset = rs;
                *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
                *(rs->store_next++) = (mword)klass->gc_descr;
                *(rs->store_next++) = (mword)count;
@@ -6582,9 +6515,9 @@ mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
        rs = alloc_remset (rs->end_set - rs->data, (void*)1);
        rs->next = REMEMBERED_SET;
        REMEMBERED_SET = rs;
-#ifdef HAVE_KW_THREAD
-       mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
-#endif
+
+       mono_thread_info_current ()->remset = rs;
+
        *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
        UNLOCK_GC;
 }
@@ -7122,12 +7055,17 @@ mono_gc_make_root_descr_all_refs (int numbits)
 {
        gsize *gc_bitmap;
        void *descr;
+       int num_bytes = numbits / 8;
 
        if (numbits < 32 && all_ref_root_descrs [numbits])
                return all_ref_root_descrs [numbits];
 
-       gc_bitmap = g_malloc0 (ALIGN_TO (numbits, 8) + 1);
-       memset (gc_bitmap, 0xff, numbits / 8);
+       gc_bitmap = g_malloc0 (ALIGN_TO (ALIGN_TO (numbits, 8) + 1, sizeof (gsize)));
+       memset (gc_bitmap, 0xff, num_bytes);
+       if (numbits < ((sizeof (*gc_bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) 
+               gc_bitmap[0] = GUINT64_TO_LE(gc_bitmap[0]);
+       else if (numbits && num_bytes % (sizeof (*gc_bitmap)))
+               gc_bitmap[num_bytes / 8] = GUINT64_TO_LE(gc_bitmap [num_bytes / 8]);
        if (numbits % 8)
                gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
        descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
@@ -7187,7 +7125,7 @@ mono_gc_is_gc_thread (void)
 {
        gboolean result;
        LOCK_GC;
-        result = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
+       result = mono_thread_info_current () != NULL;
        UNLOCK_GC;
        return result;
 }
@@ -7195,6 +7133,7 @@ mono_gc_is_gc_thread (void)
 void
 mono_gc_base_init (void)
 {
+       MonoThreadInfoCallbacks cb;
        char *env;
        char **opts, **ptr;
        char *major_collector_opt = NULL;
@@ -7215,6 +7154,11 @@ mono_gc_base_init (void)
        pagesize = mono_pagesize ();
        gc_debug_file = stdout;
 
+       cb.thread_register = sgen_thread_register;
+       cb.thread_unregister = sgen_thread_unregister;
+       cb.thread_attach = sgen_thread_attach;
+       mono_threads_init (&cb, sizeof (SgenThreadInfo));
+
        LOCK_INIT (interruption_mutex);
        LOCK_INIT (global_remset_mutex);
        LOCK_INIT (pin_queue_mutex);
@@ -7421,6 +7365,10 @@ mono_gc_base_init (void)
                                nursery_clear_policy = CLEAR_AT_GC;
                        } else if (!strcmp (opt, "check-scan-starts")) {
                                do_scan_starts_check = TRUE;
+                       } else if (!strcmp (opt, "disable-minor")) {
+                               disable_minor_collections = TRUE;
+                       } else if (!strcmp (opt, "disable-major")) {
+                               disable_major_collections = TRUE;
                        } else if (g_str_has_prefix (opt, "heap-dump=")) {
                                char *filename = strchr (opt, '=') + 1;
                                nursery_clear_policy = CLEAR_AT_GC;
@@ -7435,7 +7383,13 @@ mono_gc_base_init (void)
                        } else {
                                fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
                                fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
-                               fprintf (stderr, "Valid options are: collect-before-allocs[=<n>], check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
+                               fprintf (stderr, "Valid options are:\n");
+                               fprintf (stderr, "  collect-before-allocs[=<n>]\n");
+                               fprintf (stderr, "  check-at-minor-collections\n");
+                               fprintf (stderr, "  disable-minor\n");
+                               fprintf (stderr, "  disable-major\n");
+                               fprintf (stderr, "  xdomain-checks\n");
+                               fprintf (stderr, "  clear-at-gc\n");
                                exit (1);
                        }
                }
@@ -7466,7 +7420,7 @@ mono_gc_base_init (void)
        global_remset = alloc_remset (1024, NULL);
        global_remset->next = NULL;
 
-       pthread_key_create (&remembered_set_key, unregister_thread);
+       pthread_key_create (&remembered_set_key, NULL);
 
 #ifndef HAVE_KW_THREAD
        pthread_key_create (&thread_info_key, NULL);
@@ -7477,7 +7431,7 @@ mono_gc_base_init (void)
 
        gc_initialized = TRUE;
        UNLOCK_GC;
-       mono_gc_register_thread (&sinfo);
+       mono_thread_info_attach (&sinfo);
 }
 
 int