Merge branch 'master' of github.com:mono/mono
authorNeale Ferguson <neale@sinenomine.net>
Thu, 17 Mar 2011 16:15:10 +0000 (12:15 -0400)
committerNeale Ferguson <neale@sinenomine.net>
Thu, 17 Mar 2011 16:15:10 +0000 (12:15 -0400)
1  2 
mono/metadata/decimal.c
mono/metadata/sgen-cardtable.c
mono/metadata/sgen-gc.c
mono/metadata/sgen-marksweep.c

diff --combined mono/metadata/decimal.c
index befc0ef27baf68c3f4bd2f1db207868dd7ccb5f9,0d0f32893cac6b186a090f98d1a0075d4ba998c9..984279f70dd8f999a0d06339696e3332fce5dc55
@@@ -578,15 -578,6 +578,15 @@@ my_g_bit_nth_msf (gsize mask
        if (_BitScanReverse64 (&bIndex, mask))
                return bIndex;
        return -1;
 +#elif defined(__s390x__) && defined(__NOT_YET)
 +      guint64 r;
 +
 +      __asm__("\tlrvgr\t%1,%1\n"
 +              "\tflogr\t%0,%1\n"
 +              "\tjz\t0f\n"
 +              "\tlghi\t%0,-1\n"
 +              "0:\n"
 +              : "=r" (r) : "r" (mask) : "cc");
  #else
        int i;
  
@@@ -945,7 -936,11 +945,11 @@@ gint32 mono_double2decimal(/*[Out]*/dec
      PRECONDITION(digits <= 15);
  
      sign = ((*p & LIT_GUINT64_HIGHBIT) != 0) ? 1 : 0;
+     // Exponent
      k = ((guint16)((*p) >> 52)) & 0x7FF;
+     // 1-bit followed by the fraction component from the float
      alo = (*p & LIT_GUINT64(0xFFFFFFFFFFFFF)) | LIT_GUINT64(0x10000000000000);
      ahi = 0;
  
      }
  
      scale = 0;
-     rc = rescale128(&alo, &ahi, &scale, -texp, 0, DECIMAL_MAX_SCALE, 0);
+     rc = rescale128(&alo, &ahi, &scale, -texp, 0, DECIMAL_MAX_SCALE, 1);
      if (rc != DECIMAL_SUCCESS) return rc;
  
      sigDigits = calcDigits(alo, ahi);
index 7a26ae77f2fc62409aa4f58ac7d32c7e02956bc2,b4e53c03b5443e8da29ee51f889550ae10175be5..fc63424227011ba5c41f5c98c57ed289caf20c24
@@@ -325,16 -325,14 +325,16 @@@ find_card_offset (mword card
        return  (__builtin_ffs (card) - 1) / 8;
  #elif defined(__x86_64__) && defined(__GNUC__)
        return (__builtin_ffsll (card) - 1) / 8;
 +#elif defined(__s390x__)
 +      return (__builtin_ffsll (card) - 1) / 8;
  #else
        // FIXME:
        g_assert_not_reached ();
        /*
        int i;
 -      guint8 *ptr = &card;
 +      guint8 *ptr = (guint *) &card;
        for (i = 0; i < sizeof (mword); ++i) {
 -              if (card [i])
 +              if (ptr[i])
                        return i;
        }
        */
@@@ -456,7 -454,7 +456,7 @@@ LOOP_HEAD
                                                major_collector.copy_object ((void**)elem, queue);
                                                new = *(gpointer*)elem;
                                                if (G_UNLIKELY (ptr_in_nursery (new)))
-                                                       mono_sgen_add_to_global_remset (elem);
+                                                       mono_sgen_add_to_global_remset (queue->allocator, elem);
                                        }
                                }
                        }
diff --combined mono/metadata/sgen-gc.c
index 97c5cd6cfad11cd0332eb68c738069f38545946b,7a96028467d556b5fdbce033b9d97df46d62f403..6fad8d90b1d5207fe955c39df61e9e8368e7cff7
@@@ -256,6 -256,8 +256,8 @@@ static gboolean conservative_stack_mar
  /* 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;
@@@ -440,13 -442,14 +442,14 @@@ static gpointer global_remset_cache [2]
   */
  #define DEFAULT_REMSET_SIZE 1024
  static RememberedSet* alloc_remset (int size, gpointer id);
+ static RememberedSet* alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id);
  
  #define object_is_forwarded   SGEN_OBJECT_IS_FORWARDED
  #define object_is_pinned      SGEN_OBJECT_IS_PINNED
  #define pin_object            SGEN_PIN_OBJECT
  #define unpin_object          SGEN_UNPIN_OBJECT
  
- #define ptr_in_nursery(p)     (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_real_end))
+ #define ptr_in_nursery(p)     (SGEN_PTR_IN_NURSERY ((p), DEFAULT_NURSERY_BITS, nursery_start, nursery_end))
  
  #define LOAD_VTABLE   SGEN_LOAD_VTABLE
  
@@@ -647,7 -650,7 +650,7 @@@ add_profile_gc_root (GCRootReport *repo
  /* 
   * The current allocation cursors
   * We allocate objects in the nursery.
-  * The nursery is the area between nursery_start and nursery_real_end.
+  * The nursery is the area between nursery_start and nursery_end.
   * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
   * from nursery fragments.
   * tlab_next is the pointer to the space inside the TLAB where the next object will 
   * MAX(nursery_last_pinned_end, nursery_frag_real_end)
   */
  static char *nursery_start = NULL;
+ static char *nursery_next = NULL;
+ static char *nursery_frag_real_end = NULL;
+ static char *nursery_end = NULL;
+ static char *nursery_last_pinned_end = NULL;
  
  #ifdef HAVE_KW_THREAD
  #define TLAB_ACCESS_INIT
@@@ -708,10 -715,6 +715,6 @@@ static __thread char **tlab_next_addr
  static __thread char *stack_end;
  static __thread long *store_remset_buffer_index_addr;
  #endif
- static char *nursery_next = NULL;
- static char *nursery_frag_real_end = NULL;
- static char *nursery_real_end = NULL;
- static char *nursery_last_pinned_end = NULL;
  
  /* The size of a TLAB */
  /* The bigger the value, the less often we have to go to the slow path to allocate a new 
@@@ -820,7 -823,8 +823,8 @@@ typedef char* (*ScanObjectFunc) (char*
  /* forward declarations */
  static int stop_world (int generation);
  static int restart_world (int generation);
- static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
+ static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue);
+ static void scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
  static void scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue);
  static void scan_from_registered_roots (CopyOrMarkObjectFunc copy_func, char *addr_start, char *addr_end, int root_type, GrayQueue *queue);
  static void scan_finalizer_entries (CopyOrMarkObjectFunc copy_func, FinalizeEntry *list, GrayQueue *queue);
@@@ -832,10 -836,10 +836,10 @@@ static void finalize_in_range (CopyOrMa
  static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
  static void null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue);
  static void null_links_for_domain (MonoDomain *domain, int generation);
- static gboolean search_fragment_for_size (size_t size);
- static int search_fragment_for_size_range (size_t desired_size, size_t minimum_size);
+ static gboolean alloc_fragment_for_size (size_t size);
+ static int alloc_fragment_for_size_range (size_t desired_size, size_t minimum_size);
  static void clear_nursery_fragments (char *next);
- static void pin_from_roots (void *start_nursery, void *end_nursery);
+ static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
  static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, GrayQueue *queue);
  static void optimize_pin_queue (int start_slot);
  static void clear_remsets (void);
@@@ -1644,10 -1648,10 +1648,10 @@@ global_remset_location_was_not_added (g
   * lock must be held.  For serial collectors that is not necessary.
   */
  void
- mono_sgen_add_to_global_remset (gpointer ptr)
+ mono_sgen_add_to_global_remset (SgenInternalAllocator *alc, gpointer ptr)
  {
        RememberedSet *rs;
-       gboolean lock;
+       gboolean lock = major_collector.is_parallel;
  
        if (use_cardtable) {
                sgen_card_table_mark_address ((mword)ptr);
  
        g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
  
-       lock = (current_collection_generation == GENERATION_OLD && major_collector.is_parallel);
        if (lock)
                LOCK_GLOBAL_REMSET;
  
                *(global_remset->store_next++) = (mword)ptr;
                goto done;
        }
-       rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
+       rs = alloc_global_remset (alc, global_remset->end_set - global_remset->data, NULL);
        rs->next = global_remset;
        global_remset = rs;
        *(global_remset->store_next++) = (mword)ptr;
@@@ -2057,7 -2060,7 +2060,7 @@@ find_pinning_reference (char *obj, size
   * conservatively scanned.
   */
  static void
- pin_from_roots (void *start_nursery, void *end_nursery)
+ pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue)
  {
        RootRecord *root;
        int i;
         * *) the _last_ managed stack frame
         * *) pointers slots in managed frames
         */
-       scan_thread_data (start_nursery, end_nursery, FALSE);
+       scan_thread_data (start_nursery, end_nursery, FALSE, queue);
  
        evacuate_pin_staging_area ();
  }
  
- static CopyOrMarkObjectFunc user_copy_or_mark_func;
- static GrayQueue *user_copy_or_mark_queue;
+ typedef struct {
+       CopyOrMarkObjectFunc func;
+       GrayQueue *queue;
+ } UserCopyOrMarkData;
+ static pthread_key_t user_copy_or_mark_key;
+ static void
+ init_user_copy_or_mark_key (void)
+ {
+       pthread_key_create (&user_copy_or_mark_key, NULL);
+ }
+ static void
+ set_user_copy_or_mark_data (UserCopyOrMarkData *data)
+ {
+       static pthread_once_t init_control = PTHREAD_ONCE_INIT;
+       pthread_once (&init_control, init_user_copy_or_mark_key);
+       pthread_setspecific (user_copy_or_mark_key, data);
+ }
  
  static void
  single_arg_user_copy_or_mark (void **obj)
  {
-       user_copy_or_mark_func (obj, user_copy_or_mark_queue);
+       UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
+       data->func (obj, data->queue);
  }
  
  /*
@@@ -2136,12 -2160,11 +2160,11 @@@ precisely_scan_objects_from (CopyOrMark
                break;
        }
        case ROOT_DESC_USER: {
+               UserCopyOrMarkData data = { copy_func, queue };
                MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
-               user_copy_or_mark_func = copy_func;
-               user_copy_or_mark_queue = queue;
+               set_user_copy_or_mark_data (&data);
                marker (start_root, single_arg_user_copy_or_mark);
-               user_copy_or_mark_func = NULL;
-               user_copy_or_mark_queue = NULL;
+               set_user_copy_or_mark_data (NULL);
                break;
        }
        case ROOT_DESC_RUN_LEN:
@@@ -2189,6 -2212,19 +2212,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*
@@@ -2221,7 -2257,6 +2257,6 @@@ alloc_nursery (void
        GCMemSection *section;
        char *data;
        int scan_starts;
-       Fragment *frag;
        int alloc_size;
  
        if (nursery_section)
        data = major_collector.alloc_heap (alloc_size, 0, DEFAULT_NURSERY_BITS);
  #endif
        nursery_start = data;
-       nursery_real_end = nursery_start + nursery_size;
-       mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
-       nursery_next = nursery_start;
+       nursery_end = nursery_start + nursery_size;
+       mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
        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;
-       section->end_data = nursery_real_end;
+       section->end_data = nursery_end;
        scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
        section->scan_starts = mono_sgen_alloc_internal_dynamic (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
        section->num_scan_start = scan_starts;
        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_real_end;
-       nursery_frag_real_end = nursery_real_end;
-       /* FIXME: frag here is lost */
+       add_fragment (nursery_start, nursery_end);
  }
  
  void*
@@@ -2412,7 -2441,6 +2441,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 */
                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 */
@@@ -2554,7 -2577,7 +2577,7 @@@ finish_gray_stack (char *start_addr, ch
                fin_ready = num_ready_finalizers;
                finalize_in_range (copy_func, start_addr, end_addr, generation, queue);
                if (generation == GENERATION_OLD)
-                       finalize_in_range (copy_func, nursery_start, nursery_real_end, GENERATION_NURSERY, queue);
+                       finalize_in_range (copy_func, nursery_start, nursery_end, GENERATION_NURSERY, queue);
  
                if (fin_ready != num_ready_finalizers) {
                        ++num_loops;
@@@ -2652,7 -2675,7 +2675,7 @@@ build_nursery_fragments (void **start, 
                frag_start = (char*)start [i] + frag_size;
        }
        nursery_last_pinned_end = frag_start;
-       frag_end = nursery_real_end;
+       frag_end = nursery_end;
        frag_size = frag_end - frag_start;
        if (frag_size)
                add_nursery_frag (frag_size, frag_start, frag_end);
@@@ -2980,6 -3003,60 +3003,60 @@@ mono_sgen_need_major_collection (mword 
        return need_major_collection (space_needed);
  }
  
+ static GrayQueue*
+ job_gray_queue (WorkerData *worker_data)
+ {
+       return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
+ }
+ typedef struct
+ {
+       char *heap_start;
+       char *heap_end;
+ } ScanFromRemsetsJobData;
+ static void
+ job_scan_from_remsets (WorkerData *worker_data, void *job_data_untyped)
+ {
+       ScanFromRemsetsJobData *job_data = 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.
@@@ -2990,11 -3067,17 +3067,17 @@@ collect_nursery (size_t requested_size
        gboolean needs_major;
        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;
        orig_nursery_next = nursery_next;
        nursery_next = MAX (nursery_next, nursery_last_pinned_end);
        /* FIXME: optimize later to use the higher address where an object can be present */
-       nursery_next = MAX (nursery_next, nursery_real_end);
+       nursery_next = MAX (nursery_next, nursery_end);
  
        DEBUG (1, fprintf (gc_debug_file, "Start nursery collection %d %p-%p, size: %d\n", num_minor_gcs, nursery_start, nursery_next, (int)(nursery_next - nursery_start)));
        max_garbage_amount = nursery_next - nursery_start;
        try_calculate_minor_collection_allowance (FALSE);
  
        gray_object_queue_init (&gray_queue, mono_sgen_get_unmanaged_allocator ());
+       workers_init_distribute_gray_queue ();
  
        num_minor_gcs++;
        mono_stats.minor_gc_count ++;
        /* pin from pinned handles */
        init_pinning ();
        mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
-       pin_from_roots (nursery_start, nursery_next);
+       pin_from_roots (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
        /* identify pinned objects */
        optimize_pin_queue (0);
-       next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, &gray_queue);
+       next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
        nursery_section->pin_queue_start = pin_queue;
        nursery_section->pin_queue_num_entries = next_pin_slot;
        TV_GETTIME (atv);
        if (consistency_check_at_minor_collection)
                check_consistency ();
  
-       /* 
-        * walk all the roots and copy the young objects to the old generation,
-        * starting from to_space
+       workers_start_all_workers ();
+       /*
+        * Walk all the roots and copy the young objects to the old
+        * generation, starting from to_space.
+        *
+        * The global remsets must be processed before the workers start
+        * marking because they might add global remsets.
         */
+       scan_from_global_remsets (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+       workers_start_marking ();
+       sfrjd.heap_start = nursery_start;
+       sfrjd.heap_end = nursery_next;
+       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_remsets, &sfrjd);
  
-       scan_from_remsets (nursery_start, nursery_next, &gray_queue);
        /* we don't have complete write barrier yet, so we scan all the old generation sections */
        TV_GETTIME (btv);
        time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
        if (use_cardtable) {
                atv = btv;
                card_tables_collect_stats (TRUE);
-               scan_from_card_tables (nursery_start, nursery_next, &gray_queue);
+               scan_from_card_tables (nursery_start, nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
                TV_GETTIME (btv);
                time_minor_scan_card_table += TV_ELAPSED_MS (atv, btv);
        }
  
-       drain_gray_stack (&gray_queue, -1);
+       if (!major_collector.is_parallel)
+               drain_gray_stack (&gray_queue, -1);
  
        if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
                report_registered_roots ();
                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, &gray_queue);
-       scan_from_registered_roots (major_collector.copy_object, nursery_start, nursery_next, ROOT_TYPE_WBARRIER, &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);
+       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;
  
+       if (major_collector.is_parallel) {
+               while (!gray_object_queue_is_empty (WORKERS_DISTRIBUTE_GRAY_QUEUE)) {
+                       workers_distribute_gray_queue_sections ();
+                       usleep (1000);
+               }
+       }
+       workers_join ();
+       if (major_collector.is_parallel)
+               g_assert (gray_object_queue_is_empty (&gray_queue));
        finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY, &gray_queue);
        TV_GETTIME (atv);
        time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
        mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
  
+       /*
+        * The (single-threaded) finalization code might have done
+        * some copying/marking so we can only reset the GC thread's
+        * worker data here instead of earlier when we joined the
+        * workers.
+        */
+       if (major_collector.reset_worker_data)
+               major_collector.reset_worker_data (workers_gc_thread_data.major_collector_data);
        if (objects_pinned) {
                evacuate_pin_staging_area ();
                optimize_pin_queue (0);
        return needs_major;
  }
  
+ typedef struct
+ {
+       FinalizeEntry *list;
+ } ScanFinalizerEntriesJobData;
+ static void
+ job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
+ {
+       ScanFinalizerEntriesJobData *job_data = job_data_untyped;
+       scan_finalizer_entries (major_collector.copy_or_mark_object,
+                       job_data->list,
+                       job_gray_queue (worker_data));
+ }
  static void
  major_do_collection (const char *reason)
  {
        char *heap_start = NULL;
        char *heap_end = (char*)-1;
        int old_next_pin_slot;
+       ScanFromRegisteredRootsJobData scrrjd_normal, scrrjd_wbarrier;
+       ScanThreadDataJobData stdjd;
+       ScanFinalizerEntriesJobData sfejd_fin_ready, sfejd_critical_fin;
  
        mono_perfcounters->gc_collections1++;
  
        TV_GETTIME (btv);
        time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
  
-       nursery_section->next_data = nursery_real_end;
+       nursery_section->next_data = nursery_end;
        /* we should also coalesce scanning from sections close to each other
         * and deal with pointers outside of the sections later.
         */
        TV_GETTIME (atv);
        init_pinning ();
        DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
-       pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
+       pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
        optimize_pin_queue (0);
  
        /*
  #endif
  
        workers_start_all_workers ();
+       workers_start_marking ();
  
        if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
                report_registered_roots ();
        time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
  
        /* registered roots, this includes static fields */
-       scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_NORMAL, WORKERS_DISTRIBUTE_GRAY_QUEUE);
-       scan_from_registered_roots (major_collector.copy_or_mark_object, heap_start, heap_end, ROOT_TYPE_WBARRIER, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+       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;
+       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_from_registered_roots, &scrrjd_wbarrier);
        TV_GETTIME (btv);
        time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
  
        /* Threads */
-       /* FIXME: This is the wrong place for this, because it does
-          pinning */
-       scan_thread_data (heap_start, heap_end, TRUE);
+       stdjd.heap_start = heap_start;
+       stdjd.heap_end = heap_end;
+       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_thread_data, &stdjd);
        TV_GETTIME (atv);
        time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
  
  
        if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
                report_finalizer_roots ();
        /* scan the list of objects ready for finalization */
-       scan_finalizer_entries (major_collector.copy_or_mark_object, fin_ready_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
-       scan_finalizer_entries (major_collector.copy_or_mark_object, critical_fin_list, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+       sfejd_fin_ready.list = fin_ready_list;
+       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_fin_ready);
+       sfejd_critical_fin.list = critical_fin_list;
+       workers_enqueue_job (workers_distribute_gray_queue.allocator, job_scan_finalizer_entries, &sfejd_critical_fin);
        TV_GETTIME (atv);
        time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
        DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
        }
  
        reset_heap_boundaries ();
-       mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_real_end);
+       mono_sgen_update_heap_boundaries ((mword)nursery_start, (mword)nursery_end);
  
        /* sweep the big objects list */
        prevbo = NULL;
  static void
  major_collection (const char *reason)
  {
-       if (g_getenv ("MONO_GC_NO_MAJOR")) {
+       if (disable_major_collections) {
                collect_nursery (0);
                return;
        }
@@@ -3472,7 -3638,7 +3638,7 @@@ minor_collect_or_expand_inner (size_t s
                DEBUG (2, fprintf (gc_debug_file, "Heap size: %lu, LOS size: %lu\n", (unsigned long)total_alloc, (unsigned long)los_memory_usage));
                restart_world (0);
                /* this also sets the proper pointers for the next allocation */
-               if (!search_fragment_for_size (size)) {
+               if (!alloc_fragment_for_size (size)) {
                        int i;
                        /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
                        DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
@@@ -3568,12 -3734,13 +3734,13 @@@ setup_fragment (Fragment *frag, Fragmen
        fragment_freelist = frag;
  }
  
- /* check if we have a suitable fragment in nursery_fragments to be able to allocate
-  * an object of size @size
-  * Return FALSE if not found (which means we need a collection)
+ /*
+  * Allocate a new nursery fragment able to hold an object of size @size.
+  * nursery_next and nursery_frag_real_end are set to the boundaries of the fragment.
+  * Return TRUE if found, FALSE otherwise.
   */
  static gboolean
search_fragment_for_size (size_t size)
alloc_fragment_for_size (size_t size)
  {
        Fragment *frag, *prev;
        DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
  }
  
  /*
-  * Same as search_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
+  * Same as alloc_fragment_for_size but if search for @desired_size fails, try to satisfy @minimum_size.
   * This improves nursery usage.
   */
  static int
search_fragment_for_size_range (size_t desired_size, size_t minimum_size)
alloc_fragment_for_size_range (size_t desired_size, size_t minimum_size)
  {
        Fragment *frag, *prev, *min_prev;
        DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, desired size: %zd minimum size %zd\n", nursery_frag_real_end, desired_size, minimum_size));
@@@ -3692,7 -3859,7 +3859,7 @@@ mono_gc_alloc_obj_nolock (MonoVTable *v
                        collect_nursery (0);
                        restart_world (0);
                        mono_profiler_gc_event (MONO_GC_EVENT_END, 0);
-                       if (!degraded_mode && !search_fragment_for_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
+                       if (!degraded_mode && !alloc_fragment_for_size (size) && size <= MAX_SMALL_OBJ_SIZE) {
                                // FIXME:
                                g_assert_not_reached ();
                        }
                        if (size > tlab_size) {
                                /* Allocate directly from the nursery */
                                if (nursery_next + size >= nursery_frag_real_end) {
-                                       if (!search_fragment_for_size (size)) {
+                                       if (!alloc_fragment_for_size (size)) {
                                                minor_collect_or_expand_inner (size);
                                                if (degraded_mode) {
                                                        p = alloc_degraded (vtable, size);
                                        if (available_in_nursery > MAX_NURSERY_TLAB_WASTE && available_in_nursery > size) {
                                                alloc_size = available_in_nursery;
                                        } else {
-                                               alloc_size = search_fragment_for_size_range (tlab_size, size);
+                                               alloc_size = alloc_fragment_for_size_range (tlab_size, size);
                                                if (!alloc_size) {
                                                        alloc_size = tlab_size;
                                                        minor_collect_or_expand_inner (tlab_size);
@@@ -4039,6 -4206,9 +4206,9 @@@ mono_gc_alloc_mature (MonoVTable *vtabl
        res = alloc_degraded (vtable, size);
        *res = vtable;
        UNLOCK_GC;
+       if (G_UNLIKELY (vtable->klass->has_finalize))
+               mono_object_register_finalizer ((MonoObject*)res);
        return res;
  }
  
@@@ -4299,11 -4469,11 +4469,11 @@@ clear_unreachable_ephemerons (CopyOrMar
                        if (was_promoted) {
                                if (ptr_in_nursery (key)) {/*key was not promoted*/
                                        DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to key %p\n", key));
-                                       mono_sgen_add_to_global_remset (&cur->key);
+                                       mono_sgen_add_to_global_remset (queue->allocator, &cur->key);
                                }
                                if (ptr_in_nursery (cur->value)) {/*value was not promoted*/
                                        DEBUG (5, fprintf (gc_debug_file, "\tAdded remset to value %p\n", cur->value));
-                                       mono_sgen_add_to_global_remset (&cur->value);
+                                       mono_sgen_add_to_global_remset (queue->allocator, &cur->value);
                                }
                        }
                }
@@@ -4969,6 -5139,15 +5139,15 @@@ static MonoContext cur_thread_ctx = {0}
  static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
  #endif
  
+ SgenThreadInfo*
+ mono_sgen_thread_info_current (void)
+ {
+ #ifdef HAVE_KW_THREAD
+       return thread_info;
+ #else
+       return pthread_getspecific (thread_info_key);
+ #endif
+ }
  
  SgenThreadInfo*
  mono_sgen_thread_info_lookup (ARCH_THREAD_TYPE id)
@@@ -4987,12 -5166,10 +5166,10 @@@ static voi
  update_current_thread_stack (void *start)
  {
        int stack_guard = 0;
- #ifdef USE_MONO_CTX
-       void *ptr = NULL;
- #else
+ #ifndef USE_MONO_CTX
        void *ptr = cur_thread_regs;
  #endif
-       SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+       SgenThreadInfo *info = mono_sgen_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);
@@@ -5129,7 -5306,6 +5306,6 @@@ static voi
  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
  #endif
        gpointer stack_start;
  
-       id = pthread_self ();
-       info = mono_sgen_thread_info_lookup (id);
+       info = mono_sgen_thread_info_current ();
        info->stopped_domain = mono_domain_get ();
        info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
        stop_count = global_stop_count;
@@@ -5198,7 -5373,7 +5373,7 @@@ restart_handler (int sig
        SgenThreadInfo *info;
        int old_errno = errno;
  
-       info = mono_sgen_thread_info_lookup (pthread_self ());
+       info = mono_sgen_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 ()));
  
@@@ -5232,7 -5407,7 +5407,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_sgen_thread_info_current (), (gpointer)ARCH_GET_THREAD ()));
        TV_GETTIME (stop_world_time);
        count = mono_sgen_thread_handshake (suspend_signal_num);
        count -= restart_threads_until_none_in_managed_allocator ();
@@@ -5311,10 -5486,12 +5486,12 @@@ mono_gc_conservatively_scan_area (void 
  void*
  mono_gc_scan_object (void *obj)
  {
+       UserCopyOrMarkData *data = pthread_getspecific (user_copy_or_mark_key);
        if (current_collection_generation == GENERATION_NURSERY)
-               major_collector.copy_object (&obj, &gray_queue);
+               major_collector.copy_object (&obj, data->queue);
        else
-               major_collector.copy_or_mark_object (&obj, &gray_queue);
+               major_collector.copy_or_mark_object (&obj, data->queue);
        return obj;
  }
  
   * Mark from thread stacks and registers.
   */
  static void
- scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
+ scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, GrayQueue *queue)
  {
        SgenThreadInfo *info;
  
                        continue;
                }
                DEBUG (3, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %td, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, next_pin_slot));
-               if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
+               if (gc_callbacks.thread_mark_func && !conservative_stack_mark) {
+                       UserCopyOrMarkData data = { NULL, queue };
+                       set_user_copy_or_mark_data (&data);
                        gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
-               else if (!precise)
+                       set_user_copy_or_mark_data (NULL);
+               } else if (!precise) {
                        conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
+               }
  
  #ifdef USE_MONO_CTX
                if (!precise)
@@@ -5387,7 -5568,7 +5568,7 @@@ static gboolea
  ptr_on_stack (void *ptr)
  {
        gpointer stack_start = &stack_start;
-       SgenThreadInfo *info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+       SgenThreadInfo *info = mono_sgen_thread_info_current ();
  
        if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
                return TRUE;
@@@ -5423,7 -5604,7 +5604,7 @@@ handle_remset (mword *p, void *start_nu
                                 * becomes part of the global remset, which can grow very large.
                                 */
                                DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
-                               mono_sgen_add_to_global_remset (ptr);
+                               mono_sgen_add_to_global_remset (queue->allocator, ptr);
                        }
                } else {
                        DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
                        major_collector.copy_object (ptr, queue);
                        DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
                        if (!global && *ptr >= start_nursery && *ptr < end_nursery)
-                               mono_sgen_add_to_global_remset (ptr);
+                               mono_sgen_add_to_global_remset (queue->allocator, ptr);
                        ++ptr;
                }
                return p + 2;
@@@ -5567,18 -5748,11 +5748,11 @@@ remset_byte_size (RememberedSet *remset
  }
  
  static void
- scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
+ scan_from_global_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
  {
-       int i;
-       SgenThreadInfo *info;
        RememberedSet *remset;
-       GenericStoreRememberedSet *store_remset;
        mword *p, *next_p, *store_pos;
  
- #ifdef HEAVY_STATISTICS
-       remset_stats ();
- #endif
        /* the global one */
        for (remset = global_remset; remset; remset = remset->next) {
                DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %td\n", remset->data, remset->store_next, remset->store_next - remset->data));
                /* Truncate the remset */
                remset->store_next = store_pos;
        }
+ }
+ static void
+ scan_from_remsets (void *start_nursery, void *end_nursery, GrayQueue *queue)
+ {
+       int i;
+       SgenThreadInfo *info;
+       RememberedSet *remset;
+       GenericStoreRememberedSet *store_remset;
+       mword *p;
+ #ifdef HEAVY_STATISTICS
+       remset_stats ();
+ #endif
  
        /* the generic store ones */
        store_remset = generic_store_remsets;
@@@ -5681,7 -5869,8 +5869,8 @@@ clear_remsets (void
                remset->next = NULL;
                if (remset != global_remset) {
                        DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
-                       mono_sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
+                       mono_sgen_free_internal_dynamic_delayed (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET,
+                                       mono_sgen_get_unmanaged_allocator ());
                }
        }
        /* the generic store ones */
@@@ -5779,6 -5968,10 +5968,10 @@@ gc_register_current_thread (void *addr
        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)
        {
@@@ -5864,6 -6057,10 +6057,10 @@@ unregister_current_thread (void
                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);
                p->runtime_data = NULL;
@@@ -5909,7 -6106,7 +6106,7 @@@ mono_gc_register_thread (void *baseptr
  
        LOCK_GC;
        init_stats ();
-       info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+       info = mono_sgen_thread_info_current ();
        if (info == NULL) {
                info = gc_register_current_thread (baseptr);
        } else {
@@@ -5939,7 -6136,7 +6136,7 @@@ mono_gc_set_stack_end (void *stack_end
        SgenThreadInfo *info;
  
        LOCK_GC;
-       info = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ());
+       info = mono_sgen_thread_info_current ();
        if (info) {
                g_assert (stack_end < info->stack_end);
                info->stack_end = stack_end;
@@@ -6046,6 -6243,17 +6243,17 @@@ alloc_remset (int size, gpointer id) 
        return res;
  }
  
+ static RememberedSet*
+ alloc_global_remset (SgenInternalAllocator *alc, int size, gpointer id)
+ {
+       RememberedSet* res = mono_sgen_alloc_internal_full (alc, sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
+       res->store_next = res->data;
+       res->end_set = res->data + size;
+       res->next = NULL;
+       DEBUG (4, fprintf (gc_debug_file, "Allocated global remset size %d at %p for %p\n", size, res->data, id));
+       return res;
+ }
  /*
   * Note: the write barriers first do the needed GC work and then do the actual store:
   * this way the value is visible to the conservative GC scan after the write barrier
@@@ -6082,7 -6290,7 +6290,7 @@@ mono_gc_wbarrier_set_field (MonoObject 
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
  #ifdef HAVE_KW_THREAD
-               mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+               mono_sgen_thread_info_current ()->remset = rs;
  #endif
                *(rs->store_next++) = (mword)field_ptr;
                *(void**)field_ptr = value;
@@@ -6120,7 -6328,7 +6328,7 @@@ mono_gc_wbarrier_set_arrayref (MonoArra
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
  #ifdef HAVE_KW_THREAD
-               mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+               mono_sgen_thread_info_current ()->remset = rs;
  #endif
                *(rs->store_next++) = (mword)slot_ptr;
                *(void**)slot_ptr = value;
@@@ -6183,7 -6391,7 +6391,7 @@@ mono_gc_wbarrier_arrayref_copy (gpointe
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
  #ifdef HAVE_KW_THREAD
-               mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+               mono_sgen_thread_info_current ()->remset = rs;
  #endif
                *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
                *(rs->store_next++) = count;
@@@ -6373,7 -6581,7 +6581,7 @@@ mono_gc_wbarrier_value_copy (gpointer d
                rs->next = REMEMBERED_SET;
                REMEMBERED_SET = rs;
  #ifdef HAVE_KW_THREAD
-               mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+               mono_sgen_thread_info_current ()->remset = rs;
  #endif
                *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
                *(rs->store_next++) = (mword)klass->gc_descr;
@@@ -6415,7 -6623,7 +6623,7 @@@ mono_gc_wbarrier_object_copy (MonoObjec
        rs->next = REMEMBERED_SET;
        REMEMBERED_SET = rs;
  #ifdef HAVE_KW_THREAD
-       mono_sgen_thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
+       mono_sgen_thread_info_current ()->remset = rs;
  #endif
        *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
        UNLOCK_GC;
@@@ -6960,8 -7168,6 +7168,8 @@@ mono_gc_make_root_descr_all_refs (int n
  
        gc_bitmap = g_malloc0 (ALIGN_TO (numbits, 8) + 1);
        memset (gc_bitmap, 0xff, numbits / 8);
 +      if (numbits < ((sizeof (*gc_bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) 
 +              gc_bitmap[0] = GUINT64_TO_LE(gc_bitmap[0]);
        if (numbits % 8)
                gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
        descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
@@@ -7021,7 -7227,7 +7229,7 @@@ mono_gc_is_gc_thread (void
  {
        gboolean result;
        LOCK_GC;
-         result = mono_sgen_thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
+         result = mono_sgen_thread_info_current () != NULL;
        UNLOCK_GC;
        return result;
  }
@@@ -7255,6 -7461,10 +7463,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;
                        } 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);
                        }
                }
@@@ -7799,9 -8015,9 +8017,9 @@@ mono_gc_get_write_barrier (void
                mono_mb_emit_ptr (mb, (gpointer) nursery_start);
                label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
  
-               // if (ptr >= nursery_real_end)) goto continue;
+               // if (ptr >= nursery_end)) goto continue;
                mono_mb_emit_ldarg (mb, 0);
-               mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
+               mono_mb_emit_ptr (mb, (gpointer) nursery_end);
                label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
  
                // Otherwise return
  
                // if (*ptr >= nursery_end) return;
                mono_mb_emit_ldloc (mb, dereferenced_var);
-               mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
+               mono_mb_emit_ptr (mb, (gpointer) nursery_end);
                label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
  
  #endif 
index c4b60807b399e1e87708d41261128c2ce25e09d5,3a0d3af5dab4c75c8814f1ecc5b2780a5d2e805c..ec771bfeefb0ea387a4de552f5c7e4077898a6b7
@@@ -1009,6 -1009,32 +1009,32 @@@ major_dump_heap (FILE *heap_dump_file
                }                                                       \
        } while (0)
  
+ #ifdef SGEN_PARALLEL_MARK
+ static void
+ pin_or_update_par (void **ptr, void *obj, MonoVTable *vt, SgenGrayQueue *queue)
+ {
+       for (;;) {
+               mword vtable_word;
+               if (SGEN_CAS_PTR (obj, (void*)((mword)vt | SGEN_PINNED_BIT), vt) == vt) {
+                       mono_sgen_pin_object (obj, queue);
+                       break;
+               }
+               vtable_word = *(mword*)obj;
+               /*someone else forwarded it, update the pointer and bail out*/
+               if (vtable_word & SGEN_FORWARDED_BIT) {
+                       *ptr = (void*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
+                       break;
+               }
+               /*someone pinned it, nothing to do.*/
+               if (vtable_word & SGEN_PINNED_BIT)
+                       break;
+       }
+ }
+ #endif
  #include "sgen-major-copy-object.h"
  
  #ifdef SGEN_PARALLEL_MARK
@@@ -1055,26 -1081,18 +1081,18 @@@ major_copy_or_mark_object (void **ptr, 
                                evacuate_block_obj_sizes [size_index] = FALSE;
                        }
  
-                       do {
-                               if (SGEN_CAS_PTR (obj, (void*)((mword)vt | SGEN_PINNED_BIT), vt) == vt) {
-                                       mono_sgen_pin_object (obj, queue);
-                                       break;
-                               }
-                               vtable_word = *(mword*)obj;
-                               /*someone else forwarded it, update the pointer and bail out*/
-                               if (vtable_word & SGEN_FORWARDED_BIT) {
-                                       *ptr = (void*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
-                                       break;
-                               }
-                               /*someone pinned it, nothing to do.*/
-                               if (vtable_word & SGEN_PINNED_BIT)
-                                       break;
-                       } while (TRUE);
+                       pin_or_update_par (ptr, obj, vt, queue);
                        return;
                }
  
+               /*
+                * We do this before the CAS because we want to make
+                * sure that if another thread sees the destination
+                * pointer the VTable is already in place.  Not doing
+                * this can crash binary protocols.
+                */
+               *(MonoVTable**)destination = vt;
                if (SGEN_CAS_PTR (obj, (void*)((mword)destination | SGEN_FORWARDED_BIT), vt) == vt) {
                        gboolean was_marked;
  
@@@ -1764,8 -1782,6 +1782,8 @@@ initial_skip_card (guint8 *card_data
        return card_data + i * 4 +  (__builtin_ffs (card) - 1) / 8;
  #elif defined(__x86_64__) && defined(__GNUC__)
        return card_data + i * 8 +  (__builtin_ffsll (card) - 1) / 8;
 +#elif defined(__s390x__) && defined(__GNUC__)
 +      return card_data + i * 8 +  (__builtin_ffsll (card) - 1) / 8;
  #else
        for (i = i * SIZEOF_VOID_P; i < CARDS_PER_BLOCK; ++i) {
                if (card_data [i])