Merge pull request #2246 from BrzVlad/feature-concurrent-evacuation
authorVlad Brezae <brezaevlad@gmail.com>
Mon, 7 Dec 2015 22:09:17 +0000 (17:09 -0500)
committerVlad Brezae <brezaevlad@gmail.com>
Mon, 7 Dec 2015 22:09:17 +0000 (17:09 -0500)
Feature evacuation with concurrent sgen

1  2 
mono/metadata/boehm-gc.c
mono/metadata/gc-internals.h
mono/metadata/gc.c
mono/metadata/sgen-client-mono.h
mono/metadata/sgen-mono.c
mono/sgen/sgen-gc.c
mono/sgen/sgen-gc.h
mono/sgen/sgen-marksweep.c

diff --combined mono/metadata/boehm-gc.c
index 0401c5cf2d777df2b013c8ec612d2a7842e17156,c511bff8118e6eb7d8988fe349551cdf229c559a..a29e83ed57b15d986ac30ed6c8ec943e078d6feb
@@@ -29,7 -29,7 +29,7 @@@
  #include <mono/utils/mono-threads.h>
  #include <mono/utils/dtrace.h>
  #include <mono/utils/gc_wrapper.h>
 -#include <mono/utils/mono-mutex.h>
 +#include <mono/utils/mono-os-mutex.h>
  #include <mono/utils/mono-counters.h>
  
  #if HAVE_BOEHM_GC
@@@ -64,8 -64,8 +64,8 @@@ static MonoGCFinalizerCallbacks fin_cal
  /* GC Handles */
  
  static mono_mutex_t handle_section;
 -#define lock_handles(handles) mono_mutex_lock (&handle_section)
 -#define unlock_handles(handles) mono_mutex_unlock (&handle_section)
 +#define lock_handles(handles) mono_os_mutex_lock (&handle_section)
 +#define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
  
  typedef struct {
        guint32  *bitmap;
@@@ -196,7 -196,6 +196,7 @@@ mono_gc_base_init (void
        GC_finalizer_notifier = mono_gc_finalize_notify;
  
        GC_init_gcj_malloc (5, NULL);
 +      GC_allow_register_threads ();
  
        if ((env = g_getenv ("MONO_GC_PARAMS"))) {
                char **ptr, **opts = g_strsplit (env, ",", -1);
        cb.mono_method_is_critical = (gpointer)mono_runtime_is_critical_method;
  
        mono_threads_init (&cb, sizeof (MonoThreadInfo));
 -      mono_mutex_init (&mono_gc_lock);
 -      mono_mutex_init_recursive (&handle_section);
 +      mono_os_mutex_init (&mono_gc_lock);
 +      mono_os_mutex_init_recursive (&handle_section);
  
        mono_thread_info_attach (&dummy);
  
@@@ -371,6 -370,8 +371,6 @@@ mono_gc_is_gc_thread (void
        return GC_thread_is_registered ();
  }
  
 -extern int GC_thread_register_foreign (void *base_addr);
 -
  gboolean
  mono_gc_register_thread (void *baseptr)
  {
  static void*
  boehm_thread_register (MonoThreadInfo* info, void *baseptr)
  {
 -      if (mono_gc_is_gc_thread())
 -              return info;
 -#if !defined(HOST_WIN32)
 -      return GC_thread_register_foreign (baseptr) ? info : NULL;
 -#else
 -      return NULL;
 -#endif
 +      struct GC_stack_base sb;
 +      int res;
 +
 +      /* TODO: use GC_get_stack_base instead of baseptr. */
 +      sb.mem_base = baseptr;
 +      res = GC_register_my_thread (&sb);
 +      if (res == GC_UNIMPLEMENTED)
 +          return NULL; /* Cannot happen with GC v7+. */
 +      return info;
  }
  
  static void
@@@ -417,7 -416,7 +417,7 @@@ mono_gc_walk_heap (int flags, MonoGCRef
  static gint64 gc_start_time;
  
  static void
 -on_gc_notification (GCEventType event)
 +on_gc_notification (GC_EventType event)
  {
        MonoGCEvent e = (MonoGCEvent)event;
  
@@@ -496,7 -495,7 +496,7 @@@ on_gc_heap_resize (size_t new_size
  void
  mono_gc_enable_events (void)
  {
 -      GC_notify_event = on_gc_notification;
 +      GC_set_on_collection_event (on_gc_notification);
        GC_on_heap_resize = on_gc_heap_resize;
  }
  
@@@ -806,7 -805,7 +806,7 @@@ mono_gc_get_suspend_signal (void
  int
  mono_gc_get_restart_signal (void)
  {
 -      return GC_get_restart_signal ();
 +      return GC_get_thr_restart_signal ();
  }
  
  #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
@@@ -925,17 -924,11 +925,17 @@@ create_allocator (int atype, int tls_ke
        mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
        mono_mb_emit_i4 (mb, tls_key);
        if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
 -              mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
 +              mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
 +                                      + G_STRUCT_OFFSET (struct thread_local_freelists,
 +                                                         ptrfree_freelists));
        else if (atype == ATYPE_NORMAL)
 -              mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, normal_freelists));
 +              mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
 +                                      + G_STRUCT_OFFSET (struct thread_local_freelists,
 +                                                         normal_freelists));
        else if (atype == ATYPE_GCJ)
 -              mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, gcj_freelists));
 +              mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
 +                                      + G_STRUCT_OFFSET (struct thread_local_freelists,
 +                                                         gcj_freelists));
        else
                g_assert_not_reached ();
        mono_mb_emit_byte (mb, MONO_CEE_ADD);
@@@ -1148,7 -1141,7 +1148,7 @@@ mono_gc_get_managed_allocator_by_type (
                return res;
  
        res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD, slowpath);
 -      mono_mutex_lock (&mono_gc_lock);
 +      mono_os_mutex_lock (&mono_gc_lock);
        if (cache [atype]) {
                mono_free_method (res);
                res = cache [atype];
                mono_memory_barrier ();
                cache [atype] = res;
        }
 -      mono_mutex_unlock (&mono_gc_lock);
 +      mono_os_mutex_unlock (&mono_gc_lock);
        return res;
  }
  
@@@ -1427,24 -1420,18 +1427,19 @@@ mono_gc_make_root_descr_user (MonoGCRoo
        return NULL;
  }
  
- gboolean
- mono_gc_set_allow_synchronous_major (gboolean flag)
- {
-       return flag;
- }
  /* Toggleref support */
  
  void
  mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
  {
 -      GC_toggleref_add ((GC_PTR)object, (int)strong_ref);
 +      if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
 +          g_error ("GC_toggleref_add failed\n");
  }
  
  void
  mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
  {
 -      GC_toggleref_register_callback ((int (*) (GC_PTR obj)) proccess_toggleref);
 +      GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
  }
  
  /* Test support code */
@@@ -1493,7 -1480,7 +1488,7 @@@ mono_gc_register_finalizer_callbacks (M
  
        fin_callbacks = *callbacks;
  
 -      GC_set_finalizer_notify_proc ((void (*) (GC_PTR))fin_notifier);
 +      GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
  }
  
  #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
index b4ded9b5772bdfe5903425bf8274f25a45e2111a,3c53082930ee14b3395250630c211826b56833fe..bc9edcbe3ad1a1fb289d37dd8adc04e5fd2cb0e3
@@@ -17,8 -17,8 +17,8 @@@
  #include <mono/sgen/gc-internal-agnostic.h>
  #include <mono/utils/gc_wrapper.h>
  
 -#define mono_domain_finalizers_lock(domain) mono_mutex_lock (&(domain)->finalizable_objects_hash_lock);
 -#define mono_domain_finalizers_unlock(domain) mono_mutex_unlock (&(domain)->finalizable_objects_hash_lock);
 +#define mono_domain_finalizers_lock(domain) mono_os_mutex_lock (&(domain)->finalizable_objects_hash_lock);
 +#define mono_domain_finalizers_unlock(domain) mono_os_mutex_unlock (&(domain)->finalizable_objects_hash_lock);
  
  /* Register a memory area as a conservatively scanned GC root */
  #define MONO_GC_REGISTER_ROOT_PINNING(x,src,msg) mono_gc_register_root ((char*)&(x), sizeof(x), MONO_GC_DESCRIPTOR_NULL, (src), (msg))
@@@ -80,7 -80,6 +80,6 @@@ gpointer    ves_icall_System_GCHandle_G
  void        ves_icall_System_GC_register_ephemeron_array (MonoObject *array);
  MonoObject  *ves_icall_System_GC_get_ephemeron_tombstone (void);
  
- MonoBoolean ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag);
  
  extern void mono_gc_init (void);
  extern void mono_gc_base_init (void);
@@@ -111,9 -110,6 +110,6 @@@ void mono_gchandle_set_target (guint32 
  /*Ephemeron functionality. Sgen only*/
  gboolean    mono_gc_ephemeron_array_add (MonoObject *obj);
  
- /* To disable synchronous, evacuating collections - concurrent SGen only */
- gboolean    mono_gc_set_allow_synchronous_major (gboolean flag);
  MonoBoolean
  mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle);
  
diff --combined mono/metadata/gc.c
index a82c420111495eaecc0c7f5775fb246f31ecfea2,61fa63033c93fb0c3f0fad8e1c93388afdc22bd1..deca78d4e4ec2f64f50b47dde0042364e110d3db
  #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
  #include <mono/metadata/attach.h>
  #include <mono/metadata/console-io.h>
 -#include <mono/utils/mono-semaphore.h>
 +#include <mono/utils/mono-os-semaphore.h>
  #include <mono/utils/mono-memory-model.h>
  #include <mono/utils/mono-counters.h>
  #include <mono/utils/mono-time.h>
  #include <mono/utils/dtrace.h>
  #include <mono/utils/mono-threads.h>
  #include <mono/utils/atomic.h>
 +#include <mono/utils/mono-coop-semaphore.h>
  
  #ifndef HOST_WIN32
  #include <pthread.h>
@@@ -57,17 -56,17 +57,17 @@@ gboolean log_finalizers = FALSE
  gboolean mono_do_not_finalize = FALSE;
  gchar **mono_do_not_finalize_class_names = NULL;
  
 -#define mono_finalizer_lock() mono_mutex_lock (&finalizer_mutex)
 -#define mono_finalizer_unlock() mono_mutex_unlock (&finalizer_mutex)
 -static mono_mutex_t finalizer_mutex;
 -static mono_mutex_t reference_queue_mutex;
 +#define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
 +#define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
 +static MonoCoopMutex finalizer_mutex;
 +static MonoCoopMutex reference_queue_mutex;
  
  static GSList *domains_to_finalize= NULL;
  static MonoMList *threads_to_finalize = NULL;
  
  static gboolean finalizer_thread_exited;
  /* Uses finalizer_mutex */
 -static mono_cond_t exited_cond;
 +static MonoCoopCond exited_cond;
  
  static MonoInternalThread *gc_thread;
  
@@@ -335,8 -334,11 +335,8 @@@ object_register_finalizer (MonoObject *
         * end up running them while or after the domain is being cleared, so
         * the objects will not be valid anymore.
         */
 -      if (!mono_domain_is_unloading (domain)) {
 -              MONO_TRY_BLOCKING;
 +      if (!mono_domain_is_unloading (domain))
                mono_gc_register_for_finalization (obj, callback);
 -              MONO_FINISH_TRY_BLOCKING;
 -      }
  #endif
  }
  
@@@ -538,6 -540,10 +538,6 @@@ ves_icall_System_GC_get_ephemeron_tombs
        return mono_domain_get ()->ephemeron_tombstone;
  }
  
 -#define mono_allocator_lock() mono_mutex_lock (&allocator_section)
 -#define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
 -static mono_mutex_t allocator_section;
 -
  MonoObject *
  ves_icall_System_GCHandle_GetTarget (guint32 handle)
  {
@@@ -601,19 -607,16 +601,13 @@@ ves_icall_System_GCHandle_GetAddrOfPinn
        return NULL;
  }
  
- MonoBoolean
- ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
- {
-       return mono_gc_set_allow_synchronous_major (flag);
- }
  MonoBoolean
  mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
  {
        return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
  }
  
 -#ifdef MONO_HAS_SEMAPHORES
 -static MonoSemType finalizer_sem;
 -#endif
 -static HANDLE finalizer_event;
 +static MonoCoopSem finalizer_sem;
  static volatile gboolean finished=FALSE;
  
  void
@@@ -626,7 -629,11 +620,7 @@@ mono_gc_finalize_notify (void
        if (mono_gc_is_null ())
                return;
  
 -#ifdef MONO_HAS_SEMAPHORES
 -      MONO_SEM_POST (&finalizer_sem);
 -#else
 -      SetEvent (finalizer_event);
 -#endif
 +      mono_coop_sem_post (&finalizer_sem);
  }
  
  #ifdef HAVE_BOEHM_GC
@@@ -711,13 -718,18 +705,13 @@@ finalizer_thread (gpointer unused
  
                g_assert (mono_domain_get () == mono_get_root_domain ());
                mono_gc_set_skip_thread (TRUE);
 -              MONO_PREPARE_BLOCKING;
  
                if (wait) {
 -              /* An alertable wait is required so this thread can be suspended on windows */
 -#ifdef MONO_HAS_SEMAPHORES
 -                      MONO_SEM_WAIT_ALERTABLE (&finalizer_sem, TRUE);
 -#else
 -                      WaitForSingleObjectEx (finalizer_event, INFINITE, TRUE);
 -#endif
 +                      /* An alertable wait is required so this thread can be suspended on windows */
 +                      mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
                }
                wait = TRUE;
 -              MONO_FINISH_BLOCKING;
 +
                mono_gc_set_skip_thread (FALSE);
  
                mono_threads_perform_thread_dump ();
  
                reference_queue_proccess_all ();
  
 -#ifdef MONO_HAS_SEMAPHORES
                /* Avoid posting the pending done event until there are pending finalizers */
 -              if (MONO_SEM_TIMEDWAIT (&finalizer_sem, 0) == 0)
 +              if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
                        /* Don't wait again at the start of the loop */
                        wait = FALSE;
 -              else
 -                      SetEvent (pending_done_event);
 -#else
 +              } else {
                        SetEvent (pending_done_event);
 -#endif
 +              }
        }
  
        mono_finalizer_lock ();
        finalizer_thread_exited = TRUE;
 -      mono_cond_signal (&exited_cond);
 +      mono_coop_cond_signal (&exited_cond);
        mono_finalizer_unlock ();
  
        return 0;
@@@ -778,8 -793,10 +772,8 @@@ mono_gc_init_finalizer_thread (void
  void
  mono_gc_init (void)
  {
 -      mono_mutex_init_recursive (&allocator_section);
 -
 -      mono_mutex_init_recursive (&finalizer_mutex);
 -      mono_mutex_init_recursive (&reference_queue_mutex);
 +      mono_coop_mutex_init_recursive (&finalizer_mutex);
 +      mono_coop_mutex_init_recursive (&reference_queue_mutex);
  
        mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.minor_gc_count);
        mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_UINT, &gc_stats.major_gc_count);
                gc_disabled = TRUE;
                return;
        }
 -      
 -      finalizer_event = CreateEvent (NULL, FALSE, FALSE, NULL);
 -      g_assert (finalizer_event);
 +
        pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
        g_assert (pending_done_event);
 -      mono_cond_init (&exited_cond, 0);
 -#ifdef MONO_HAS_SEMAPHORES
 -      MONO_SEM_INIT (&finalizer_sem, 0);
 -#endif
 +      mono_coop_cond_init (&exited_cond);
 +      mono_coop_sem_init (&finalizer_sem, 0);
  
  #ifndef LAZY_GC_THREAD_CREATION
        mono_gc_init_finalizer_thread ();
@@@ -832,10 -853,12 +826,10 @@@ mono_gc_cleanup (void
                                        break;
                                else
                                        timeout = end_ticks - current_ticks;
 -                              MONO_PREPARE_BLOCKING;
                                mono_finalizer_lock ();
                                if (!finalizer_thread_exited)
 -                                      mono_cond_timedwait_ms (&exited_cond, &finalizer_mutex, timeout);
 +                                      mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout);
                                mono_finalizer_unlock ();
 -                              MONO_FINISH_BLOCKING;
                        }
  
                        if (!finalizer_thread_exited) {
  
        mono_reference_queue_cleanup ();
  
 -      mono_mutex_destroy (&allocator_section);
 -      mono_mutex_destroy (&finalizer_mutex);
 -      mono_mutex_destroy (&reference_queue_mutex);
 +      mono_coop_mutex_destroy (&finalizer_mutex);
 +      mono_coop_mutex_destroy (&reference_queue_mutex);
  }
  
  gboolean
@@@ -977,7 -1001,7 +971,7 @@@ reference_queue_proccess_all (void
                reference_queue_proccess (queue);
  
  restart:
 -      mono_mutex_lock (&reference_queue_mutex);
 +      mono_coop_mutex_lock (&reference_queue_mutex);
        for (iter = &ref_queues; *iter;) {
                queue = *iter;
                if (!queue->should_be_deleted) {
                        continue;
                }
                if (queue->queue) {
 -                      mono_mutex_unlock (&reference_queue_mutex);
 +                      mono_coop_mutex_unlock (&reference_queue_mutex);
                        reference_queue_proccess (queue);
                        goto restart;
                }
                *iter = queue->next;
                g_free (queue);
        }
 -      mono_mutex_unlock (&reference_queue_mutex);
 +      mono_coop_mutex_unlock (&reference_queue_mutex);
  }
  
  static void
@@@ -1046,10 -1070,10 +1040,10 @@@ mono_gc_reference_queue_new (mono_refer
        MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
        res->callback = callback;
  
 -      mono_mutex_lock (&reference_queue_mutex);
 +      mono_coop_mutex_lock (&reference_queue_mutex);
        res->next = ref_queues;
        ref_queues = res;
 -      mono_mutex_unlock (&reference_queue_mutex);
 +      mono_coop_mutex_unlock (&reference_queue_mutex);
  
        return res;
  }
index 7dcf6ce4009ead67e36860bd8e9e90a5159187ca,0ba15a0b37b45ee6983eec7e7f2cf5b57a2673e3..d1e92469b744e4f4d865a6e70e05f39199c663c2
@@@ -90,7 -90,7 +90,7 @@@ struct _SgenClientThreadInfo 
  #include "utils/mono-counters.h"
  #include "utils/mono-logger-internals.h"
  #include "utils/mono-time.h"
 -#include "utils/mono-semaphore.h"
 +#include "utils/mono-os-semaphore.h"
  #include "metadata/sgen-bridge-internals.h"
  
  extern void mono_sgen_register_moved_object (void *obj, void *destination);
@@@ -114,7 -114,7 +114,7 @@@ sgen_mono_array_size (GCVTable vtable, 
        else
                element_size = vtable->klass->sizes.element_size;
  
 -      size_without_bounds = size = sizeof (MonoArray) + element_size * mono_array_length_fast (array);
 +      size_without_bounds = size = MONO_SIZEOF_MONO_ARRAY + element_size * mono_array_length_fast (array);
  
        if (G_UNLIKELY (array->bounds)) {
                size += sizeof (mono_array_size_t) - 1;
@@@ -560,6 -560,11 +560,11 @@@ sgen_client_binary_protocol_global_rems
  #endif
  }
  
+ static void G_GNUC_UNUSED
+ sgen_client_binary_protocol_mod_union_remset (gpointer obj, gpointer ptr, gpointer value, gpointer value_vtable)
+ {
+ }
  static void G_GNUC_UNUSED
  sgen_client_binary_protocol_ptr_update (gpointer ptr, gpointer old_value, gpointer new_value, gpointer vtable, size_t size)
  {
@@@ -694,9 -699,9 +699,9 @@@ extern MonoNativeTlsKey thread_info_key
  
  typedef MonoSemType SgenSemaphore;
  
 -#define SGEN_SEMAPHORE_INIT(sem,initial)      MONO_SEM_INIT ((sem), (initial))
 -#define SGEN_SEMAPHORE_POST(sem)              MONO_SEM_POST ((sem))
 -#define SGEN_SEMAPHORE_WAIT(sem)              MONO_SEM_WAIT ((sem))
 +#define SGEN_SEMAPHORE_INIT(sem,initial)      mono_os_sem_init ((sem), (initial))
 +#define SGEN_SEMAPHORE_POST(sem)              mono_os_sem_post ((sem))
 +#define SGEN_SEMAPHORE_WAIT(sem)              mono_os_sem_wait ((sem), MONO_SEM_FLAGS_NONE)
  
  gboolean sgen_has_critical_method (void);
  gboolean sgen_is_critical_method (MonoMethod *method);
index a133f91030f05fa31fe6f79c734dfb0b29211203,3ab15956480da2a21429c17f9ab42a642bb52a36..456fe45c7ee19efb176f231af0837f2e035419ae
@@@ -376,7 -376,7 +376,7 @@@ get_array_fill_vtable (void
  
                klass.element_class = mono_defaults.byte_class;
                klass.rank = 1;
 -              klass.instance_size = sizeof (MonoArray);
 +              klass.instance_size = MONO_SIZEOF_MONO_ARRAY;
                klass.sizes.element_size = 1;
                klass.name = "array_filler_type";
  
@@@ -395,7 -395,7 +395,7 @@@ sgen_client_array_fill_range (char *sta
  {
        MonoArray *o;
  
 -      if (size < sizeof (MonoArray)) {
 +      if (size < MONO_SIZEOF_MONO_ARRAY) {
                memset (start, 0, size);
                return FALSE;
        }
        /* Mark this as not a real object */
        o->obj.synchronisation = GINT_TO_POINTER (-1);
        o->bounds = NULL;
 -      o->max_length = (mono_array_size_t)(size - sizeof (MonoArray));
 +      o->max_length = (mono_array_size_t)(size - MONO_SIZEOF_MONO_ARRAY);
  
        return TRUE;
  }
  void
  sgen_client_zero_array_fill_header (void *p, size_t size)
  {
 -      if (size >= sizeof (MonoArray)) {
 -              memset (p, 0, sizeof (MonoArray));
 +      if (size >= MONO_SIZEOF_MONO_ARRAY) {
 +              memset (p, 0, MONO_SIZEOF_MONO_ARRAY);
        } else {
 -              static guint8 zeros [sizeof (MonoArray)];
 +              static guint8 zeros [MONO_SIZEOF_MONO_ARRAY];
  
                SGEN_ASSERT (0, !memcmp (p, zeros, size), "TLAB segment must be zeroed out.");
        }
@@@ -1185,7 -1185,7 +1185,7 @@@ create_allocator (int atype, gboolean s
                mono_mb_emit_ldarg (mb, 1);
                mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
                /* + sizeof (MonoArray) */
 -              mono_mb_emit_icon (mb, sizeof (MonoArray));
 +              mono_mb_emit_icon (mb, MONO_SIZEOF_MONO_ARRAY);
                mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
                mono_mb_emit_stloc (mb, size_var);
  
@@@ -2687,12 -2687,6 +2687,6 @@@ sgen_client_ensure_weak_gchandles_acces
                mono_gc_wait_for_bridge_processing ();
  }
  
- gboolean
- mono_gc_set_allow_synchronous_major (gboolean flag)
- {
-       return sgen_set_allow_synchronous_major (flag);
- }
  void*
  mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
  {
diff --combined mono/sgen/sgen-gc.c
index d559a9285ee4fdea5299d0b674adaa01531c5a67,4ffceb8390e31e9e2f761cd0c9a0b0d3612e0423..7189811c50e98cacd2faa9ecbe9d3db45fae72a4
@@@ -239,12 -239,6 +239,6 @@@ static gboolean do_concurrent_checks = 
     each collection */
  static gboolean do_scan_starts_check = FALSE;
  
- /*
-  * If the major collector is concurrent and this is FALSE, we will
-  * never initiate a synchronous major collection, unless requested via
-  * GC.Collect().
-  */
- static gboolean allow_synchronous_major = TRUE;
  static gboolean disable_minor_collections = FALSE;
  static gboolean disable_major_collections = FALSE;
  static gboolean do_verify_nursery = FALSE;
@@@ -346,7 -340,7 +340,7 @@@ nursery_canaries_enabled (void
   * ########  Global data.
   * ######################################################################
   */
 -LOCK_DECLARE (gc_mutex);
 +MonoCoopMutex gc_mutex;
  gboolean sgen_try_free_some_memory;
  
  #define SCAN_START_SIZE       SGEN_SCAN_START_SIZE
@@@ -359,7 -353,7 +353,7 @@@ GCMemSection *nursery_section = NULL
  static volatile mword lowest_heap_address = ~(mword)0;
  static volatile mword highest_heap_address = 0;
  
 -LOCK_DECLARE (sgen_interruption_mutex);
 +MonoCoopMutex sgen_interruption_mutex;
  
  int current_collection_generation = -1;
  static volatile gboolean concurrent_collection_in_progress = FALSE;
@@@ -2244,17 -2238,6 +2238,6 @@@ sgen_perform_collection (size_t request
                goto done;
        }
  
-       /*
-        * If we've been asked to do a major collection, and the major collector wants to
-        * run synchronously (to evacuate), we set the flag to do that.
-        */
-       if (generation_to_collect == GENERATION_OLD &&
-                       allow_synchronous_major &&
-                       major_collector.want_synchronous_collection &&
-                       *major_collector.want_synchronous_collection) {
-               wait_to_finish = TRUE;
-       }
        SGEN_ASSERT (0, !concurrent_collection_in_progress, "Why did this not get handled above?");
  
        /*
@@@ -2726,16 -2709,6 +2709,6 @@@ sgen_gc_get_used_size (void
        return tot;
  }
  
- gboolean
- sgen_set_allow_synchronous_major (gboolean flag)
- {
-       if (!major_collector.is_concurrent)
-               return flag;
-       allow_synchronous_major = flag;
-       return TRUE;
- }
  void
  sgen_env_var_error (const char *env_var, const char *fallback, const char *description_format, ...)
  {
@@@ -2807,11 -2780,11 +2780,11 @@@ sgen_gc_init (void
        mono_thread_smr_init ();
  #endif
  
 -      LOCK_INIT (gc_mutex);
 +      mono_coop_mutex_init (&gc_mutex);
  
        gc_debug_file = stderr;
  
 -      LOCK_INIT (sgen_interruption_mutex);
 +      mono_coop_mutex_init (&sgen_interruption_mutex);
  
        if ((env = g_getenv (MONO_GC_PARAMS_NAME))) {
                opts = g_strsplit (env, ",", -1);
                                }
                                continue;
                        }
-                       if (g_str_has_prefix (opt, "allow-synchronous-major=")) {
-                               if (!major_collector.is_concurrent) {
-                                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`allow-synchronous-major` is only valid for the concurrent major collector.");
-                                       continue;
-                               }
-                               opt = strchr (opt, '=') + 1;
-                               if (!strcmp (opt, "yes")) {
-                                       allow_synchronous_major = TRUE;
-                               } else if (!strcmp (opt, "no")) {
-                                       allow_synchronous_major = FALSE;
-                               } else {
-                                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`allow-synchronous-major` must be either `yes' or `no'.");
-                                       continue;
-                               }
-                       }
  
                        if (!strcmp (opt, "cementing")) {
                                cement_enabled = TRUE;
                        fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
                        fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
                        fprintf (stderr, "  [no-]cementing\n");
-                       if (major_collector.is_concurrent)
-                               fprintf (stderr, "  allow-synchronous-major=FLAG (where FLAG is `yes' or `no')\n");
                        if (major_collector.print_gc_param_usage)
                                major_collector.print_gc_param_usage ();
                        if (sgen_minor_collector.print_gc_param_usage)
@@@ -3191,7 -3145,7 +3145,7 @@@ sgen_get_nursery_clear_policy (void
  void
  sgen_gc_lock (void)
  {
 -      LOCK_GC;
 +      mono_coop_mutex_lock (&gc_mutex);
  }
  
  void
@@@ -3199,7 -3153,7 +3153,7 @@@ sgen_gc_unlock (void
  {
        gboolean try_free = sgen_try_free_some_memory;
        sgen_try_free_some_memory = FALSE;
 -      mono_mutex_unlock (&gc_mutex);
 +      mono_coop_mutex_unlock (&gc_mutex);
        if (try_free)
                mono_thread_hazardous_try_free_some ();
  }
diff --combined mono/sgen/sgen-gc.h
index 0b5d182ff4a4290444afa3d34372c103af041aa0,d47a978a61922eeb3818c65ad7e5ce0b74ab66e1..089ba85a1fbebfb5cee6aa5f7ee5dfc0945dcc19
@@@ -39,8 -39,7 +39,8 @@@ typedef struct _SgenThreadInfo SgenThre
  #include <stdint.h>
  #include "mono/utils/mono-compiler.h"
  #include "mono/utils/atomic.h"
 -#include "mono/utils/mono-mutex.h"
 +#include "mono/utils/mono-os-mutex.h"
 +#include "mono/utils/mono-coop-mutex.h"
  #include "mono/sgen/sgen-conf.h"
  #include "mono/sgen/sgen-hash-table.h"
  #include "mono/sgen/sgen-protocol.h"
@@@ -90,14 -89,23 +90,14 @@@ struct _GCMemSection 
  #define LOCK_DECLARE(name) mono_mutex_t name
  /* if changing LOCK_INIT to something that isn't idempotent, look at
     its use in mono_gc_base_init in sgen-gc.c */
 -#define LOCK_INIT(name)       mono_mutex_init (&(name))
 -#define LOCK_GC do {                                          \
 -              MONO_TRY_BLOCKING       \
 -              mono_mutex_lock (&gc_mutex);                    \
 -              MONO_FINISH_TRY_BLOCKING        \
 -      } while (0)
 +#define LOCK_INIT(name)       mono_os_mutex_init (&(name))
 +#define LOCK_GC do { sgen_gc_lock (); } while (0)
  #define UNLOCK_GC do { sgen_gc_unlock (); } while (0)
  
 -extern LOCK_DECLARE (sgen_interruption_mutex);
 -
 -#define LOCK_INTERRUPTION do {        \
 -      MONO_TRY_BLOCKING       \
 -      mono_mutex_lock (&sgen_interruption_mutex);     \
 -      MONO_FINISH_TRY_BLOCKING        \
 -} while (0)
 +extern MonoCoopMutex sgen_interruption_mutex;
  
 -#define UNLOCK_INTERRUPTION mono_mutex_unlock (&sgen_interruption_mutex)
 +#define LOCK_INTERRUPTION mono_coop_mutex_lock (&sgen_interruption_mutex)
 +#define UNLOCK_INTERRUPTION mono_coop_mutex_unlock (&sgen_interruption_mutex)
  
  /* FIXME: Use InterlockedAdd & InterlockedAdd64 to reduce the CAS cost. */
  #define SGEN_CAS      InterlockedCompareExchange
@@@ -599,13 -607,6 +599,6 @@@ struct _SgenMajorCollector 
        gboolean supports_cardtable;
        gboolean sweeps_lazily;
  
-       /*
-        * This is set to TRUE by the sweep if the next major
-        * collection should be synchronous (for evacuation).  For
-        * non-concurrent collectors, this should be NULL.
-        */
-       gboolean *want_synchronous_collection;
        void* (*alloc_heap) (mword nursery_size, mword nursery_align, int nursery_bits);
        gboolean (*is_object_live) (GCObject *obj);
        GCObject* (*alloc_small_pinned_obj) (GCVTable vtable, size_t size, gboolean has_references);
@@@ -958,7 -959,7 +951,7 @@@ extern guint32 tlab_size
  extern NurseryClearPolicy nursery_clear_policy;
  extern gboolean sgen_try_free_some_memory;
  
 -extern LOCK_DECLARE (gc_mutex);
 +extern MonoCoopMutex gc_mutex;
  
  /* Nursery helpers. */
  
index 780e4adff1ee62e73eef9eb02e94dd9a76de237e,9cbf2e8eda0215219a6ab9c61a97158674574d37..5b344b6d6580c068ac38a6f2a0321337c909290d
@@@ -170,8 -170,6 +170,6 @@@ static int fast_block_obj_size_indexes 
  
  static gboolean *evacuate_block_obj_sizes;
  static float evacuation_threshold = 0.666f;
- static float concurrent_evacuation_threshold = 0.666f;
- static gboolean want_evacuation = FALSE;
  
  static gboolean lazy_sweep = FALSE;
  
@@@ -245,7 -243,6 +243,6 @@@ static MSBlockInfo * volatile *free_blo
  static guint64 stat_major_blocks_alloced = 0;
  static guint64 stat_major_blocks_freed = 0;
  static guint64 stat_major_blocks_lazy_swept = 0;
- static guint64 stat_major_objects_evacuated = 0;
  
  #if SIZEOF_VOID_P != 8
  static guint64 stat_major_blocks_freed_ideal = 0;
@@@ -271,9 -268,9 +268,9 @@@ add_scanned_object (void *ptr
        if (!binary_protocol_is_enabled ())
                return;
  
 -      mono_mutex_lock (&scanned_objects_list_lock);
 +      mono_os_mutex_lock (&scanned_objects_list_lock);
        sgen_pointer_queue_add (&scanned_objects_list, ptr);
 -      mono_mutex_unlock (&scanned_objects_list_lock);
 +      mono_os_mutex_unlock (&scanned_objects_list_lock);
  }
  #endif
  
@@@ -533,10 -530,11 +530,11 @@@ ms_alloc_block (int size_index, gboolea
         * Blocks that are to-space are not evacuated from.  During an major collection
         * blocks are allocated for two reasons: evacuating objects from the nursery and
         * evacuating them from major blocks marked for evacuation.  In both cases we don't
-        * want further evacuation.
+        * want further evacuation. We also don't want to evacuate objects allocated during
+        * the concurrent mark since it would add pointless stress on the finishing pause.
         */
-       info->is_to_space = (sgen_get_current_collection_generation () == GENERATION_OLD);
-       info->state = (info->is_to_space || sgen_concurrent_collection_in_progress ()) ? BLOCK_STATE_MARKING : BLOCK_STATE_SWEPT;
+       info->is_to_space = (sgen_get_current_collection_generation () == GENERATION_OLD) || sgen_concurrent_collection_in_progress ();
+       info->state = info->is_to_space ? BLOCK_STATE_MARKING : BLOCK_STATE_SWEPT;
        SGEN_ASSERT (6, !sweep_in_progress () || info->state == BLOCK_STATE_SWEPT, "How do we add a new block to be swept while sweeping?");
        info->cardtable_mod_union = NULL;
  
@@@ -1067,7 -1065,7 +1065,7 @@@ major_get_cardtable_mod_union_for_refer
   * Mark the mod-union card for `ptr`, which must be a reference within the object `obj`.
   */
  static void
- mark_mod_union_card (GCObject *obj, void **ptr)
+ mark_mod_union_card (GCObject *obj, void **ptr, GCObject *value_obj)
  {
        int type = sgen_obj_get_descriptor (obj) & DESC_TYPE_MASK;
        if (sgen_safe_object_is_small (obj, type)) {
        } else {
                sgen_los_mark_mod_union_card (obj, ptr);
        }
+       binary_protocol_mod_union_remset (obj, ptr, value_obj, SGEN_LOAD_VTABLE (value_obj));
+ }
+ static inline gboolean
+ major_block_is_evacuating (MSBlockInfo *block)
+ {
+       if (evacuate_block_obj_sizes [block->obj_size_index] &&
+                       !block->has_pinned &&
+                       !block->is_to_space)
+               return TRUE;
+       return FALSE;
  }
  
  #define LOAD_VTABLE   SGEN_LOAD_VTABLE
@@@ -1166,8 -1176,6 +1176,6 @@@ static guint64 stat_drain_prefetch_fill
  static guint64 stat_drain_loops;
  #endif
  
- static void major_scan_object_with_evacuation (GCObject *start, mword desc, SgenGrayQueue *queue);
  #define COPY_OR_MARK_FUNCTION_NAME    major_copy_or_mark_object_no_evacuation
  #define SCAN_OBJECT_FUNCTION_NAME     major_scan_object_no_evacuation
  #define DRAIN_GRAY_STACK_FUNCTION_NAME        drain_gray_stack_no_evacuation
@@@ -1221,7 -1229,7 +1229,7 @@@ major_copy_or_mark_object_concurrent_ca
  static void
  major_copy_or_mark_object_concurrent_finish_canonical (GCObject **ptr, SgenGrayQueue *queue)
  {
-       major_copy_or_mark_object_no_evacuation (ptr, *ptr, queue);
+       major_copy_or_mark_object_with_evacuation (ptr, *ptr, queue);
  }
  
  static void
@@@ -1610,8 -1618,6 +1618,6 @@@ sweep_job_func (void *thread_data_untyp
  static void
  sweep_finish (void)
  {
-       mword total_evacuate_heap = 0;
-       mword total_evacuate_saved = 0;
        int i;
  
        for (i = 0; i < num_block_obj_sizes; ++i) {
                } else {
                        evacuate_block_obj_sizes [i] = FALSE;
                }
-               {
-                       mword total_bytes = block_obj_sizes [i] * sweep_slots_available [i];
-                       total_evacuate_heap += total_bytes;
-                       if (evacuate_block_obj_sizes [i])
-                               total_evacuate_saved += total_bytes - block_obj_sizes [i] * sweep_slots_used [i];
-               }
        }
  
-       want_evacuation = (float)total_evacuate_saved / (float)total_evacuate_heap > (1 - concurrent_evacuation_threshold);
        set_sweep_state (SWEEP_STATE_SWEPT, SWEEP_STATE_COMPACTING);
  }
  
@@@ -2411,7 -2409,6 +2409,6 @@@ sgen_marksweep_init_internal (SgenMajor
        mono_counters_register ("# major blocks allocated", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_alloced);
        mono_counters_register ("# major blocks freed", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed);
        mono_counters_register ("# major blocks lazy swept", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_lazy_swept);
-       mono_counters_register ("# major objects evacuated", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_objects_evacuated);
  #if SIZEOF_VOID_P != 8
        mono_counters_register ("# major blocks freed ideally", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed_ideal);
        mono_counters_register ("# major blocks freed less ideally", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed_less_ideal);
        concurrent_mark = is_concurrent;
        collector->is_concurrent = is_concurrent;
        collector->needs_thread_pool = is_concurrent || concurrent_sweep;
-       if (is_concurrent)
-               collector->want_synchronous_collection = &want_evacuation;
-       else
-               collector->want_synchronous_collection = NULL;
        collector->get_and_reset_num_major_objects_marked = major_get_and_reset_num_major_objects_marked;
        collector->supports_cardtable = TRUE;
  
                collector->major_ops_concurrent_start.drain_gray_stack = drain_gray_stack_concurrent;
  
                collector->major_ops_concurrent_finish.copy_or_mark_object = major_copy_or_mark_object_concurrent_finish_canonical;
-               collector->major_ops_concurrent_finish.scan_object = major_scan_object_no_evacuation;
+               collector->major_ops_concurrent_finish.scan_object = major_scan_object_with_evacuation;
                collector->major_ops_concurrent_finish.scan_vtype = major_scan_vtype_concurrent_finish;
-               collector->major_ops_concurrent_finish.drain_gray_stack = drain_gray_stack_no_evacuation;
+               collector->major_ops_concurrent_finish.drain_gray_stack = drain_gray_stack;
        }
  
  #ifdef HEAVY_STATISTICS
        mono_counters_register ("Optimized copy major", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major);
        mono_counters_register ("Optimized copy major small fast", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major_small_fast);
        mono_counters_register ("Optimized copy major small slow", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major_small_slow);
+       mono_counters_register ("Optimized copy major small evacuate", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major_small_evacuate);
        mono_counters_register ("Optimized copy major large", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major_large);
        mono_counters_register ("Optimized major scan", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_major_scan);
        mono_counters_register ("Optimized major scan no refs", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_major_scan_no_refs);
  #endif
  
  #ifdef SGEN_HEAVY_BINARY_PROTOCOL
 -      mono_mutex_init (&scanned_objects_list_lock);
 +      mono_os_mutex_init (&scanned_objects_list_lock);
  #endif
  
        SGEN_ASSERT (0, SGEN_MAX_SMALL_OBJ_SIZE <= MS_BLOCK_FREE / 2, "MAX_SMALL_OBJ_SIZE must be at most MS_BLOCK_FREE / 2");