[sgen] Split up concurrent sweep from worker logic
[mono.git] / mono / metadata / sgen-mono.c
index af0dd65c2906a8fc94c25c2592b5e11a37c265a6..a67fc067263ca69b8714b0bb056b9c2fe4028e3d 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * sgen-mono.c: SGen features specific to Mono.
+/**
+ * \file
+ * SGen features specific to Mono.
  *
  * Copyright (C) 2014 Xamarin Inc
  *
@@ -16,7 +17,7 @@
 #include "sgen/sgen-client.h"
 #include "sgen/sgen-cardtable.h"
 #include "sgen/sgen-pinning.h"
-#include "sgen/sgen-thread-pool.h"
+#include "sgen/sgen-workers.h"
 #include "metadata/marshal.h"
 #include "metadata/method-builder.h"
 #include "metadata/abi-details.h"
@@ -28,7 +29,6 @@
 #include "utils/mono-memory-model.h"
 #include "utils/mono-logger-internals.h"
 #include "utils/mono-threads-coop.h"
-#include "sgen/sgen-thread-pool.h"
 #include "utils/mono-threads.h"
 #include "metadata/w32handle.h"
 
@@ -128,7 +128,7 @@ mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *
 /**
  * mono_gc_wbarrier_object_copy:
  *
- * Write barrier to call when obj is the result of a clone or copy of an object.
+ * Write barrier to call when \p obj is the result of a clone or copy of an object.
  */
 void
 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
@@ -153,6 +153,9 @@ mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
        sgen_get_remset ()->wbarrier_object_copy (obj, src);
 }
 
+/**
+ * mono_gc_wbarrier_set_arrayref:
+ */
 void
 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
 {
@@ -168,6 +171,9 @@ mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* va
        sgen_get_remset ()->wbarrier_set_field ((GCObject*)arr, slot_ptr, value);
 }
 
+/**
+ * mono_gc_wbarrier_set_field:
+ */
 void
 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
 {
@@ -520,12 +526,18 @@ sgen_client_run_finalize (MonoObject *obj)
        mono_gc_run_finalize (obj, NULL);
 }
 
+/**
+ * mono_gc_invoke_finalizers:
+ */
 int
 mono_gc_invoke_finalizers (void)
 {
        return sgen_gc_invoke_finalizers ();
 }
 
+/**
+ * mono_gc_pending_finalizers:
+ */
 MonoBoolean
 mono_gc_pending_finalizers (void)
 {
@@ -557,12 +569,11 @@ object_in_domain_predicate (MonoObject *obj, void *user_data)
 
 /**
  * mono_gc_finalizers_for_domain:
- * @domain: the unloading appdomain
- * @out_array: output array
- * @out_size: size of output array
- *
- * Enqueue for finalization all objects that belong to the unloading appdomain @domain
- * @suspend is used for early termination of the enqueuing process.
+ * \param domain the unloading appdomain
+ * \param out_array output array
+ * \param out_size size of output array
+ * Enqueue for finalization all objects that belong to the unloading appdomain \p domain.
+ * \p suspend is used for early termination of the enqueuing process.
  */
 void
 mono_gc_finalize_domain (MonoDomain *domain)
@@ -979,6 +990,9 @@ mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
        return obj;
 }
 
+/**
+ * mono_gc_alloc_fixed:
+ */
 void*
 mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
 {
@@ -993,6 +1007,9 @@ mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource sourc
        return res;
 }
 
+/**
+ * mono_gc_free_fixed:
+ */
 void
 mono_gc_free_fixed (void* addr)
 {
@@ -1052,7 +1069,7 @@ create_allocator (int atype, ManagedAllocatorVariant variant)
 {
        int p_var, size_var, real_size_var, thread_var G_GNUC_UNUSED;
        gboolean slowpath = variant == MANAGED_ALLOCATOR_SLOW_PATH;
-       guint32 slowpath_branch, max_size_branch;
+       guint32 fastpath_branch, max_size_branch, no_oom_branch;
        MonoMethodBuilder *mb;
        MonoMethod *res;
        MonoMethodSignature *csig;
@@ -1319,7 +1336,7 @@ create_allocator (int atype, ManagedAllocatorVariant variant)
        /* if (G_LIKELY (new_next < tlab_temp_end)) */
        mono_mb_emit_ldloc (mb, new_next_var);
        EMIT_TLS_ACCESS_TEMP_END (mb, thread_var);
-       slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
+       fastpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
 
        /* Slowpath */
        if (atype != ATYPE_SMALL)
@@ -1354,10 +1371,17 @@ create_allocator (int atype, ManagedAllocatorVariant variant)
        } else {
                g_assert_not_reached ();
        }
+
+       /* if (ret == NULL) throw OOM; */
+       mono_mb_emit_byte (mb, CEE_DUP);
+       no_oom_branch = mono_mb_emit_branch (mb, CEE_BRTRUE);
+       mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
+
+       mono_mb_patch_branch (mb, no_oom_branch);
        mono_mb_emit_byte (mb, CEE_RET);
 
        /* Fastpath */
-       mono_mb_patch_short_branch (mb, slowpath_branch);
+       mono_mb_patch_short_branch (mb, fastpath_branch);
 
        /* FIXME: Memory barrier */
 
@@ -1567,71 +1591,10 @@ sgen_has_managed_allocator (void)
        return FALSE;
 }
 
-/*
- * Cardtable scanning
- */
-
-#define MWORD_MASK (sizeof (mword) - 1)
-
-static inline int
-find_card_offset (mword card)
-{
-/*XXX Use assembly as this generates some pretty bad code */
-#if defined(__i386__) && defined(__GNUC__)
-       return  (__builtin_ffs (card) - 1) / 8;
-#elif defined(__x86_64__) && defined(__GNUC__)
-       return (__builtin_ffsll (card) - 1) / 8;
-#elif defined(__s390x__)
-       return (__builtin_ffsll (GUINT64_TO_LE(card)) - 1) / 8;
-#else
-       int i;
-       guint8 *ptr = (guint8 *) &card;
-       for (i = 0; i < sizeof (mword); ++i) {
-               if (ptr[i])
-                       return i;
-       }
-       return 0;
-#endif
-}
-
-static guint8*
-find_next_card (guint8 *card_data, guint8 *end)
-{
-       mword *cards, *cards_end;
-       mword card;
-
-       while ((((mword)card_data) & MWORD_MASK) && card_data < end) {
-               if (*card_data)
-                       return card_data;
-               ++card_data;
-       }
-
-       if (card_data == end)
-               return end;
-
-       cards = (mword*)card_data;
-       cards_end = (mword*)((mword)end & ~MWORD_MASK);
-       while (cards < cards_end) {
-               card = *cards;
-               if (card)
-                       return (guint8*)cards + find_card_offset (card);
-               ++cards;
-       }
-
-       card_data = (guint8*)cards_end;
-       while (card_data < end) {
-               if (*card_data)
-                       return card_data;
-               ++card_data;
-       }
-
-       return end;
-}
-
 #define ARRAY_OBJ_INDEX(ptr,array,elem_size) (((char*)(ptr) - ((char*)(array) + G_STRUCT_OFFSET (MonoArray, vector))) / (elem_size))
 
 gboolean
-sgen_client_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, ScanCopyContext ctx)
+sgen_client_cardtable_scan_object (GCObject *obj, guint8 *cards, ScanCopyContext ctx)
 {
        MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
        MonoClass *klass = vt->klass;
@@ -1684,8 +1647,8 @@ sgen_client_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *
 LOOP_HEAD:
 #endif
 
-               card_data = find_next_card (card_data, card_data_end);
-               for (; card_data < card_data_end; card_data = find_next_card (card_data + 1, card_data_end)) {
+               card_data = sgen_find_next_card (card_data, card_data_end);
+               for (; card_data < card_data_end; card_data = sgen_find_next_card (card_data + 1, card_data_end)) {
                        size_t index;
                        size_t idx = (card_data - card_base) + extra_idx;
                        char *start = (char*)(obj_start + idx * CARD_SIZE_IN_BYTES);
@@ -2001,6 +1964,15 @@ precisely_report_roots_from (GCRootReport *report, void** start_root, void** end
                }
                break;
        }
+       case ROOT_DESC_VECTOR: {
+               void **p;
+
+               for (p = start_root; p < end_root; p++) {
+                       if (*p)
+                               add_profile_gc_root (report, *p, MONO_PROFILE_GC_ROOT_OTHER, 0);
+               }
+               break;
+       }
        case ROOT_DESC_USER: {
                MonoGCRootMarkFunc marker = (MonoGCRootMarkFunc)sgen_get_user_descriptor_func (desc);
                root_report = report;
@@ -2097,7 +2069,7 @@ mono_sgen_register_moved_object (void *obj, void *destination)
         * lock-free data structure for the queue as multiple threads will be
         * adding to it at the same time.
         */
-       if (sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ())) {
+       if (sgen_workers_is_worker_thread (mono_native_thread_id_get ())) {
                sgen_pointer_queue_add (&moved_objects_queue, obj);
                sgen_pointer_queue_add (&moved_objects_queue, destination);
        } else {
@@ -2177,22 +2149,20 @@ walk_references (GCObject *start, size_t size, void *data)
 
 /**
  * mono_gc_walk_heap:
- * @flags: flags for future use
- * @callback: a function pointer called for each object in the heap
- * @data: a user data pointer that is passed to callback
- *
- * This function can be used to iterate over all the live objects in the heap:
- * for each object, @callback is invoked, providing info about the object's
+ * \param flags flags for future use
+ * \param callback a function pointer called for each object in the heap
+ * \param data a user data pointer that is passed to callback
+ * This function can be used to iterate over all the live objects in the heap;
+ * for each object, \p callback is invoked, providing info about the object's
  * location in memory, its class, its size and the objects it references.
- * For each referenced object it's offset from the object address is
+ * For each referenced object its offset from the object address is
  * reported in the offsets array.
  * The object references may be buffered, so the callback may be invoked
  * multiple times for the same object: in all but the first call, the size
  * argument will be zero.
- * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
+ * Note that this function can be only called in the \c MONO_GC_EVENT_PRE_START_WORLD
  * profiler event handler.
- *
- * Returns: a non-zero value if the GC doesn't support heap walking
+ * \returns a non-zero value if the GC doesn't support heap walking
  */
 int
 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
@@ -2301,6 +2271,14 @@ mono_gc_set_skip_thread (gboolean skip)
        LOCK_GC;
        info->client_info.gc_disabled = skip;
        UNLOCK_GC;
+
+       if (skip) {
+               /* If we skip scanning a thread with a non-empty handle stack, we may move an
+                * object but fail to update the reference in the handle.
+                */
+               HandleStack *stack = info->client_info.info.handle_stack;
+               g_assert (stack == NULL || mono_handle_stack_is_empty (stack));
+       }
 }
 
 static gboolean
@@ -2329,12 +2307,18 @@ sgen_thread_detach (SgenThreadInfo *p)
                mono_thread_detach_internal (mono_thread_internal_current ());
 }
 
+/**
+ * mono_gc_register_thread:
+ */
 gboolean
 mono_gc_register_thread (void *baseptr)
 {
        return mono_thread_info_attach (baseptr) != NULL;
 }
 
+/**
+ * mono_gc_is_gc_thread:
+ */
 gboolean
 mono_gc_is_gc_thread (void)
 {
@@ -2369,6 +2353,22 @@ mono_gc_scan_object (void *obj, void *gc_data)
        return obj;
 }
 
+typedef struct {
+       void **start_nursery;
+       void **end_nursery;
+} PinHandleStackInteriorPtrData;
+
+/* Called when we're scanning the handle stack imprecisely and we encounter a pointer into the
+   middle of an object.
+ */
+static void
+pin_handle_stack_interior_ptrs (void **ptr_slot, void *user_data)
+{
+       PinHandleStackInteriorPtrData *ud = (PinHandleStackInteriorPtrData *)user_data;
+       sgen_conservatively_pin_objects_from (ptr_slot, ptr_slot+1, ud->start_nursery, ud->end_nursery, PIN_TYPE_STACK);
+}
+
+
 /*
  * Mark from thread stacks and registers.
  */
@@ -2398,8 +2398,17 @@ sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean p
 
                binary_protocol_scan_stack ((gpointer)mono_thread_info_get_tid (info), info->client_info.stack_start, info->client_info.stack_end, skip_reason);
 
-               if (skip_reason)
+               if (skip_reason) {
+                       if (precise) {
+                               /* If we skip a thread with a non-empty handle stack and then it
+                                * resumes running we may potentially move an object but fail to
+                                * update the reference in the handle.
+                                */
+                               HandleStack *stack = info->client_info.info.handle_stack;
+                               g_assert (stack == NULL || mono_handle_stack_is_empty (stack));
+                       }
                        continue;
+               }
 
                g_assert (info->client_info.stack_start);
                g_assert (info->client_info.stack_end);
@@ -2449,8 +2458,21 @@ sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean p
                                }
                        }
                }
-               if (precise && info->client_info.info.handle_stack) {
-                       mono_handle_stack_scan ((HandleStack*)info->client_info.info.handle_stack, (GcScanFunc)ctx.ops->copy_or_mark_object, ctx.queue);
+               if (info->client_info.info.handle_stack) {
+                       /*
+                         Make two passes over the handle stack.  On the imprecise pass, pin all
+                         objects where the handle points into the interior of the object. On the
+                         precise pass, copy or mark all the objects that have handles to the
+                         beginning of the object.
+                       */
+                       if (precise)
+                               mono_handle_stack_scan ((HandleStack*)info->client_info.info.handle_stack, (GcScanFunc)ctx.ops->copy_or_mark_object, ctx.queue, precise);
+                       else {
+                               PinHandleStackInteriorPtrData ud = { .start_nursery = start_nursery,
+                                                                    .end_nursery = end_nursery,
+                               };
+                               mono_handle_stack_scan ((HandleStack*)info->client_info.info.handle_stack, pin_handle_stack_interior_ptrs, &ud, precise);
+                       }
                }
        } FOREACH_THREAD_END
 }
@@ -2505,7 +2527,13 @@ mono_gc_deregister_root (char* addr)
 int
 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
 {
-       return pthread_create (new_thread, attr, start_routine, arg);
+       int res;
+
+       mono_threads_join_lock ();
+       res = pthread_create (new_thread, attr, start_routine, arg);
+       mono_threads_join_unlock ();
+
+       return res;
 }
 #endif
 
@@ -2655,10 +2683,9 @@ sgen_client_metadata_for_object (GCObject *obj)
 
 /**
  * mono_gchandle_is_in_domain:
- * @gchandle: a GCHandle's handle.
- * @domain: An application domain.
- *
- * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
+ * \param gchandle a GCHandle's handle.
+ * \param domain An application domain.
+ * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
  */
 gboolean
 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
@@ -2669,7 +2696,7 @@ mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
 
 /**
  * mono_gchandle_free_domain:
- * @unloading: domain that is unloading
+ * \param unloading domain that is unloading
  *
  * Function used internally to cleanup any GC handle for objects belonging
  * to the specified domain during appdomain unload.
@@ -2963,6 +2990,9 @@ sgen_client_describe_invalid_pointer (GCObject *ptr)
 
 static gboolean gc_inited;
 
+/**
+ * mono_gc_base_init:
+ */
 void
 mono_gc_base_init (void)
 {
@@ -2993,7 +3023,9 @@ mono_gc_base_init (void)
 void
 mono_gc_base_cleanup (void)
 {
-       sgen_thread_pool_shutdown ();
+       sgen_thread_pool_shutdown (major_collector.get_sweep_pool ());
+
+       sgen_workers_shutdown ();
 
        // We should have consumed any outstanding moves.
        g_assert (sgen_pointer_queue_is_empty (&moved_objects_queue));