[sgen] Make GC handles table lock-free.
authorJon Purdy <evincarofautumn@gmail.com>
Wed, 7 Jan 2015 01:44:19 +0000 (17:44 -0800)
committerJon Purdy <evincarofautumn@gmail.com>
Thu, 3 Sep 2015 23:07:05 +0000 (16:07 -0700)
The previous code was hard to make lock-free because it was trying to
coordinate three separate buffers: one of GC handles, one of a
free-bitmap, and one used to get the domain ID for an expired weak
reference. Now all of this information is stored in a single tagged
pointer per entry.

 * Make the GC lock non-recursive again.

 * Correctly null links for domain unloading.

 * Ensure that non-weak handles are registered as roots using the
   correct descriptors.

15 files changed:
mono/metadata/boehm-gc.c
mono/metadata/gc-internal.h
mono/metadata/gc.c
mono/metadata/marshal.c
mono/metadata/reflection.c
mono/metadata/sgen-bridge-internal.h
mono/metadata/sgen-bridge.c
mono/metadata/sgen-mono.c
mono/mini/mini-exceptions.c
mono/mini/mini-runtime.c
mono/sgen/gc-internal-agnostic.h
mono/sgen/sgen-cardtable.c
mono/sgen/sgen-fin-weak-hash.c
mono/sgen/sgen-gc.c
mono/sgen/sgen-gc.h

index 52b701c3e50849086d126a04c509f0593857452e..92fc5d43f14e10c64300af3725a0378a5713b78f 100644 (file)
@@ -494,40 +494,26 @@ mono_gc_deregister_root (char* addr)
 }
 
 void
-mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
+mono_gc_weak_link_register (volatile gpointer *link_addr, MonoObject *obj, gboolean track)
 {
-       /* libgc requires that we use HIDE_POINTER... */
-       *link_addr = (void*)HIDE_POINTER (obj);
        if (track)
-               GC_REGISTER_LONG_LINK (link_addr, obj);
+               GC_REGISTER_LONG_LINK ((gpointer *)link_addr, obj);
        else
-               GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
+               GC_GENERAL_REGISTER_DISAPPEARING_LINK ((gpointer *)link_addr, obj);
 }
 
 void
-mono_gc_weak_link_remove (void **link_addr, gboolean track)
+mono_gc_weak_link_unregister (volatile gpointer *link_addr, gboolean track)
 {
        if (track)
-               GC_unregister_long_link (link_addr);
+               GC_unregister_long_link ((gpointer *)link_addr);
        else
-               GC_unregister_disappearing_link (link_addr);
-       *link_addr = NULL;
+               GC_unregister_disappearing_link ((gpointer *)link_addr);
 }
 
-static gpointer
-reveal_link (gpointer link_addr)
-{
-       void **link_a = link_addr;
-       return REVEAL_POINTER (*link_a);
-}
-
-MonoObject*
-mono_gc_weak_link_get (void **link_addr)
+void
+mono_gc_ensure_weak_links_accessible (void)
 {
-       MonoObject *obj = GC_call_with_alloc_lock (reveal_link, link_addr);
-       if (obj == (MonoObject *) -1)
-               return NULL;
-       return obj;
 }
 
 void*
index 0b977306b40910835a56a8172ae10388f88f1152..c1c8885f81814a9eb53578c0196092bdeb952497 100644 (file)
@@ -85,7 +85,6 @@ MonoBoolean ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
 extern void mono_gc_init (void);
 extern void mono_gc_base_init (void);
 extern void mono_gc_cleanup (void);
-extern void mono_gc_mutex_cleanup (void);
 extern void mono_gc_base_cleanup (void);
 
 /*
@@ -108,9 +107,9 @@ void     mono_gc_enable_events (void);
 void     mono_gc_enable_alloc_events (void);
 
 /* disappearing link functionality */
-void        mono_gc_weak_link_add    (void **link_addr, MonoObject *obj, gboolean track);
-void        mono_gc_weak_link_remove (void **link_addr, gboolean track);
-MonoObject *mono_gc_weak_link_get    (void **link_addr);
+void mono_gc_weak_link_register (volatile gpointer *link_addr, MonoObject *obj, gboolean track);
+void mono_gc_weak_link_unregister (volatile gpointer *link_addr, gboolean track);
+void mono_gc_ensure_weak_links_accessible (void);
 
 /*Ephemeron functionality. Sgen only*/
 gboolean    mono_gc_ephemeron_array_add (MonoObject *obj);
index 4d4998a2f5b11506117dcae3a586fab655cd9f29..57562ea11d41002da3d59a0377c2b6ad90f92d7f 100644 (file)
@@ -531,16 +531,11 @@ ves_icall_System_GC_get_ephemeron_tombstone (void)
 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
 static mono_mutex_t allocator_section;
 
-#ifndef HAVE_SGEN_GC
-static mono_mutex_t handle_section;
-#endif
-
 #define GC_HANDLE_TYPE_SHIFT (3)
 #define GC_HANDLE_TYPE_MASK ((1 << GC_HANDLE_TYPE_SHIFT) - 1)
 #define GC_HANDLE_TYPE(x) (((x) & GC_HANDLE_TYPE_MASK) - 1)
-#define GC_HANDLE_SLOT(x) ((x) >> GC_HANDLE_TYPE_SHIFT)
-#define GC_HANDLE_TYPE_IS_WEAK(x) ((x) <= HANDLE_WEAK_TRACK)
-#define GC_HANDLE_TAG(slot, type) (((slot) << GC_HANDLE_TYPE_SHIFT) | (((type) & GC_HANDLE_TYPE_MASK) + 1))
+#define GC_HANDLE_INDEX(x) ((x) >> GC_HANDLE_TYPE_SHIFT)
+#define GC_HANDLE(index, type) (((index) << GC_HANDLE_TYPE_SHIFT) | (((type) & GC_HANDLE_TYPE_MASK) + 1))
 
 MonoObject *
 ves_icall_System_GCHandle_GetTarget (guint32 handle)
@@ -611,35 +606,96 @@ ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
        return mono_gc_set_allow_synchronous_major (flag);
 }
 
+#define BUCKETS (32 - GC_HANDLE_TYPE_SHIFT)
+#define MIN_BUCKET_BITS (5)
+#define MIN_BUCKET_SIZE (1 << MIN_BUCKET_BITS)
+
+/*
+ * A table of GC handle data, implementing a simple lock-free bitmap allocator.
+ *
+ * 'entries' is an array of pointers to buckets of increasing size. The first
+ * bucket has size 'MIN_BUCKET_SIZE', and each bucket is twice the size of the
+ * previous, i.e.:
+ *
+ *           |-------|-- MIN_BUCKET_SIZE
+ *    [0] -> xxxxxxxx
+ *    [1] -> xxxxxxxxxxxxxxxx
+ *    [2] -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ *    ...
+ *
+ * The size of the spine, 'BUCKETS', is chosen so that the maximum number of
+ * entries is no less than the maximum index value of a GC handle.
+ *
+ * Each entry in a bucket is a pointer with two tag bits: if
+ * 'GC_HANDLE_OCCUPIED' returns true for a slot, then the slot is occupied; if
+ * so, then 'GC_HANDLE_VALID' gives whether the entry refers to a valid (1) or
+ * NULL (0) object reference. If the reference is valid, then the pointer is an
+ * object pointer. If the reference is NULL, and 'GC_HANDLE_TYPE_IS_WEAK' is
+ * true for 'type', then the pointer is a domain pointer--this allows us to
+ * retrieve the domain ID of an expired weak reference.
+ *
+ * Finally, 'slot_hint' denotes the position of the last allocation, so that the
+ * whole array needn't be searched on every allocation.
+ */
+
 typedef struct {
-       guint32  *bitmap;
-       gpointer *entries;
-       guint32   size;
-       guint8    type;
-       guint     slot_hint : 24; /* starting slot for search in bitmap */
-       /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
-       /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
-       guint16  *domain_ids;
+       volatile gpointer *volatile entries [BUCKETS];
+       volatile guint32 capacity;
+       volatile guint32 slot_hint;
+       guint8 type;
 } HandleData;
 
-#define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
+static inline guint
+bucket_size (guint index)
+{
+       return 1 << (index + MIN_BUCKET_BITS);
+}
 
-static inline gboolean
-slot_occupied (HandleData *handles, guint slot) {
-       return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
+/* Computes floor(log2(index + MIN_BUCKET_SIZE)) - 1, giving the index
+ * of the bucket containing a slot.
+ */
+static inline guint
+index_bucket (guint index)
+{
+#ifdef __GNUC__
+       return CHAR_BIT * sizeof (index) - __builtin_clz (index + MIN_BUCKET_SIZE) - 1 - MIN_BUCKET_BITS;
+#else
+       guint count = 0;
+       index += MIN_BUCKET_SIZE;
+       while (index) {
+               ++count;
+               index >>= 1;
+       }
+       return count - 1 - MIN_BUCKET_BITS;
+#endif
 }
 
 static inline void
-vacate_slot (HandleData *handles, guint slot) {
-       handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
+bucketize (guint index, guint *bucket, guint *offset)
+{
+       *bucket = index_bucket (index);
+       *offset = index - bucket_size (*bucket) + MIN_BUCKET_SIZE;
 }
 
-static inline void
-occupy_slot (HandleData *handles, guint slot) {
-       handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
+static inline gboolean
+try_set_slot (volatile gpointer *slot, MonoObject *obj, gpointer old, GCHandleType type)
+{
+    if (obj)
+               return InterlockedCompareExchangePointer (slot, MONO_GC_HANDLE_OBJECT_POINTER (obj, GC_HANDLE_TYPE_IS_WEAK (type)), old) == old;
+    return InterlockedCompareExchangePointer (slot, MONO_GC_HANDLE_DOMAIN_POINTER (mono_domain_get (), GC_HANDLE_TYPE_IS_WEAK (type)), old) == old;
+}
+
+/* Try to claim a slot by setting its occupied bit. */
+static inline gboolean
+try_occupy_slot (HandleData *handles, guint bucket, guint offset, MonoObject *obj, gboolean track)
+{
+       volatile gpointer *link_addr = &(handles->entries [bucket] [offset]);
+       if (MONO_GC_HANDLE_OCCUPIED (*link_addr))
+               return FALSE;
+       return try_set_slot (link_addr, obj, NULL, handles->type);
 }
 
-#define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0}
+#define EMPTY_HANDLE_DATA(type) { { NULL }, 0, 0, (type) }
 
 /* weak and weak-track arrays will be allocated in malloc memory 
  */
@@ -650,175 +706,156 @@ static HandleData gc_handles [] = {
        EMPTY_HANDLE_DATA (HANDLE_PINNED)
 };
 
-
-#ifdef HAVE_SGEN_GC
-#define lock_handles(handles) sgen_gc_lock ()
-#define unlock_handles(handles) sgen_gc_unlock ()
-#else
-#define lock_handles(handles) do {     \
-       MONO_TRY_BLOCKING;      \
-       mono_mutex_lock (&handle_section);      \
-       MONO_FINISH_TRY_BLOCKING;       \
-} while (0)
-#define unlock_handles(handles) mono_mutex_unlock (&handle_section)
-#endif
-
-static int
-find_first_unset (guint32 bitmap)
-{
-       int i;
-       for (i = 0; i < 32; ++i) {
-               if (!(bitmap & (1 << i)))
-                       return i;
-       }
-       return -1;
-}
-
-static MonoGCDescriptor
-make_root_descr_all_refs (int numbits, gboolean pinned)
+static HandleData *
+gc_handles_for_type (GCHandleType type)
 {
-#ifdef HAVE_SGEN_GC
-       if (pinned)
-               return MONO_GC_DESCRIPTOR_NULL;
-#endif
-       return mono_gc_make_root_descr_all_refs (numbits);
+       g_assert (type < HANDLE_TYPE_MAX);
+       return &gc_handles [type];
 }
 
 static void
-handle_data_alloc_entries (HandleData *handles)
+mark_gc_handles (void *addr, MonoGCMarkFunc mark_func, void *gc_data)
 {
-       handles->size = 32;
-       if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
-               handles->entries = g_malloc0 (sizeof (*handles->entries) * handles->size);
-               handles->domain_ids = g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
-       } else {
-               handles->entries = mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED), MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
-       }
-       handles->bitmap = g_malloc0 (handles->size / CHAR_BIT);
-}
-
-static gint
-handle_data_next_unset (HandleData *handles)
-{
-       gint slot;
-       for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
-               if (handles->bitmap [slot] == 0xffffffff)
-                       continue;
-               handles->slot_hint = slot;
-               return find_first_unset (handles->bitmap [slot]);
+       HandleData *handles = gc_handles_for_type (HANDLE_NORMAL);
+       size_t bucket, offset;
+       const guint max_bucket = index_bucket (handles->capacity);
+       for (bucket = 0; bucket < max_bucket; ++bucket) {
+               volatile gpointer *entries = handles->entries [bucket];
+               for (offset = 0; offset < bucket_size (bucket); ++offset) {
+                       volatile gpointer *entry = &entries [offset];
+                       gpointer hidden = *entry;
+                       gpointer revealed = MONO_GC_REVEAL_POINTER (hidden, FALSE);
+                       if (!MONO_GC_HANDLE_IS_OBJECT_POINTER (hidden))
+                               continue;
+                       mark_func ((MonoObject **)&revealed, gc_data);
+                       g_assert (revealed);
+                       *entry = MONO_GC_HANDLE_OBJECT_POINTER (revealed, FALSE);
+               }
        }
-       return -1;
 }
 
-static gint
-handle_data_first_unset (HandleData *handles)
+static guint
+handle_data_find_unset (HandleData *handles, guint32 begin, guint32 end)
 {
-       gint slot;
-       for (slot = 0; slot < handles->slot_hint; ++slot) {
-               if (handles->bitmap [slot] == 0xffffffff)
-                       continue;
-               handles->slot_hint = slot;
-               return find_first_unset (handles->bitmap [slot]);
+       guint index;
+       gint delta = begin < end ? +1 : -1;
+       for (index = begin; index < end; index += delta) {
+               guint bucket, offset;
+               volatile gpointer *entries;
+               bucketize (index, &bucket, &offset);
+               entries = handles->entries [bucket];
+               g_assert (entries);
+               if (!MONO_GC_HANDLE_OCCUPIED (entries [offset]))
+                       return index;
        }
        return -1;
 }
 
-/* Returns the index of the current slot in the bitmap. */
+/* Adds a bucket if necessary and possible. */
 static void
-handle_data_grow (HandleData *handles, gboolean track)
+handle_data_grow (HandleData *handles, guint32 old_capacity)
 {
-       guint32 *new_bitmap;
-       guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
-
-       /* resize and copy the bitmap */
-       new_bitmap = g_malloc0 (new_size / CHAR_BIT);
-       memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
-       g_free (handles->bitmap);
-       handles->bitmap = new_bitmap;
-
-       /* resize and copy the entries */
-       if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
-               gpointer *entries;
-               guint16 *domain_ids;
-               gint i;
-               domain_ids = g_malloc0 (sizeof (*handles->domain_ids) * new_size);
-               entries = g_malloc0 (sizeof (*handles->entries) * new_size);
-               memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
-               for (i = 0; i < handles->size; ++i) {
-                       MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
-                       if (obj) {
-                               mono_gc_weak_link_add (&(entries [i]), obj, track);
-                               mono_gc_weak_link_remove (&(handles->entries [i]), track);
-                       } else {
-                               g_assert (!handles->entries [i]);
-                       }
-               }
-               g_free (handles->entries);
-               g_free (handles->domain_ids);
-               handles->entries = entries;
-               handles->domain_ids = domain_ids;
-       } else {
-               gpointer *entries;
-               entries = mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED), MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
-               mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
-               mono_gc_free_fixed (handles->entries);
-               handles->entries = entries;
+       const guint new_bucket = index_bucket (old_capacity);
+       const guint32 growth = bucket_size (new_bucket);
+       const guint32 new_capacity = old_capacity + growth;
+       gpointer *entries;
+       const size_t new_bucket_size = sizeof (**handles->entries) * growth;
+       if (handles->capacity >= new_capacity)
+               return;
+       entries = g_malloc0 (new_bucket_size);
+#ifdef HAVE_BOEHM_GC
+       if (!GC_HANDLE_TYPE_IS_WEAK (handles->type))
+               mono_gc_register_root ((char *)entries, new_bucket_size, handles->type == HANDLE_PINNED ? NULL : mono_gc_make_root_descr_all_refs (new_bucket_size * CHAR_BIT), MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
+#endif
+#ifdef HAVE_SGEN_GC
+       if (handles->type == HANDLE_PINNED)
+               mono_gc_register_root ((char *)entries, new_bucket_size, MONO_GC_DESCRIPTOR_NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
+#endif
+       if (InterlockedCompareExchangePointer ((volatile gpointer *)&handles->entries [new_bucket], entries, NULL) == NULL) {
+               if (InterlockedCompareExchange ((volatile gint32 *)&handles->capacity, new_capacity, old_capacity) != old_capacity)
+                       g_assert_not_reached ();
+               handles->slot_hint = old_capacity;
+               mono_memory_write_barrier ();
+               return;
        }
-       handles->slot_hint = handles->size / BITMAP_SIZE;
-       handles->size = new_size;
+       /* Someone beat us to the allocation. */
+#ifdef HAVE_BOEHM_GC
+       mono_gc_deregister_root ((char *)entries);
+#endif
+       g_free (entries);
 }
 
 static guint32
 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
 {
-       gint slot, i;
+       guint index;
        guint32 res;
-       lock_handles (handles);
-       if (!handles->size)
-               handle_data_alloc_entries (handles);
-       i = handle_data_next_unset (handles);
-       if (i == -1 && handles->slot_hint != 0)
-               i = handle_data_first_unset (handles);
-       if (i == -1) {
-               handle_data_grow (handles, track);
-               i = 0;
-       }
-       slot = handles->slot_hint * BITMAP_SIZE + i;
-       occupy_slot (handles, slot);
-       handles->entries [slot] = NULL;
-       if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
-               /*FIXME, what to use when obj == null?*/
-               handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
-               if (obj)
-                       mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
-       } else {
-               handles->entries [slot] = obj;
+       guint bucket, offset;
+       guint32 capacity;
+       guint32 slot_hint;
+       if (!handles->capacity)
+               handle_data_grow (handles, 0);
+retry:
+       capacity = handles->capacity;
+       slot_hint = handles->slot_hint;
+       index = handle_data_find_unset (handles, slot_hint, capacity);
+       if (index == -1)
+               index = handle_data_find_unset (handles, 0, slot_hint);
+       if (index == -1) {
+               handle_data_grow (handles, capacity);
+               goto retry;
        }
-
+       handles->slot_hint = index;
+       bucketize (index, &bucket, &offset);
+       if (!try_occupy_slot (handles, bucket, offset, obj, track))
+               goto retry;
+       if (obj && GC_HANDLE_TYPE_IS_WEAK (handles->type))
+               mono_gc_weak_link_register (&handles->entries [bucket] [offset], obj, track);
+       /* Ensure that a GC handle cannot be given to another thread without the slot having been set. */
+       mono_memory_write_barrier ();
 #ifndef DISABLE_PERFCOUNTERS
        mono_perfcounters->gc_num_handles++;
 #endif
-       unlock_handles (handles);
-       res = GC_HANDLE_TAG (slot, handles->type);
+       res = GC_HANDLE (index, handles->type);
        mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
        return res;
 }
 
+/*
+ * Maps a function over all GC handles.
+ * This assumes that the world is stopped!
+ */
 void
-mono_gchandle_iterate (GCHandleType handle_type, int max_generation, gpointer callback(gpointer *, GCHandleType, gpointer), gpointer user)
+mono_gchandle_iterate (GCHandleType handle_type, int max_generation, gpointer callback(gpointer, GCHandleType, gpointer), gpointer user)
 {
-       HandleData *handle_data = &gc_handles [handle_type];
-       size_t i;
-       lock_handles (handle_data);
-       for (i = 0; i < handle_data->size; ++i) {
-               gpointer *entry = &handle_data->entries [i];
-               /* Table must contain no garbage pointers. */
-               g_assert (*entry ? slot_occupied (handle_data, i) : TRUE);
-               if (!*entry || !slot_occupied (handle_data, i) || mono_gc_object_older_than (REVEAL_POINTER (*entry), max_generation))
-                       continue;
-               *entry = callback (entry, handle_type, user);
+       HandleData *handle_data = gc_handles_for_type (handle_type);
+       size_t bucket, offset;
+       guint max_bucket = index_bucket (handle_data->capacity);
+       /* If a new bucket has been allocated, but the capacity has not yet been
+        * increased, nothing can yet have been allocated in the bucket because the
+        * world is stopped, so we shouldn't miss any handles during iteration.
+        */
+       for (bucket = 0; bucket < max_bucket; ++bucket) {
+               volatile gpointer *entries = handle_data->entries [bucket];
+               for (offset = 0; offset < bucket_size (bucket); ++offset) {
+                       gpointer hidden = entries [offset];
+                       gpointer revealed;
+                       gpointer result;
+                       /* Table must contain no garbage pointers. */
+                       gboolean occupied = MONO_GC_HANDLE_OCCUPIED (hidden);
+                       g_assert (hidden ? occupied : !occupied);
+                       if (!occupied || !MONO_GC_HANDLE_VALID (hidden))
+                               continue;
+                       revealed = MONO_GC_REVEAL_POINTER (hidden, GC_HANDLE_TYPE_IS_WEAK (handle_type));
+                       g_assert (revealed);
+                       if (mono_gc_object_older_than (revealed, max_generation))
+                               continue;
+                       result = callback (revealed, handle_type, user);
+                       if (result)
+                               g_assert (MONO_GC_HANDLE_OCCUPIED (result));
+                       entries [offset] = result;
+               }
        }
-       unlock_handles (handle_data);
 }
 
 /**
@@ -841,7 +878,7 @@ mono_gchandle_iterate (GCHandleType handle_type, int max_generation, gpointer ca
 guint32
 mono_gchandle_new (MonoObject *obj, gboolean pinned)
 {
-       return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
+       return alloc_handle (gc_handles_for_type (pinned ? HANDLE_PINNED : HANDLE_NORMAL), obj, FALSE);
 }
 
 /**
@@ -866,11 +903,53 @@ mono_gchandle_new (MonoObject *obj, gboolean pinned)
 guint32
 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
 {
-       guint32 handle = alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
+       guint32 handle = alloc_handle (gc_handles_for_type (track_resurrection ? HANDLE_WEAK_TRACK : HANDLE_WEAK), obj, track_resurrection);
 
        return handle;
 }
 
+static MonoObject *
+link_get (volatile gpointer *link_addr, gboolean is_weak)
+{
+       void *volatile *link_addr_volatile;
+       void *ptr;
+       MonoObject *obj;
+retry:
+       link_addr_volatile = link_addr;
+       ptr = (void*)*link_addr_volatile;
+       /*
+        * At this point we have a hidden pointer.  If the GC runs
+        * here, it will not recognize the hidden pointer as a
+        * reference, and if the object behind it is not referenced
+        * elsewhere, it will be freed.  Once the world is restarted
+        * we reveal the pointer, giving us a pointer to a freed
+        * object.  To make sure we don't return it, we load the
+        * hidden pointer again.  If it's still the same, we can be
+        * sure the object reference is valid.
+        */
+       if (ptr && MONO_GC_HANDLE_IS_OBJECT_POINTER (ptr))
+               obj = (MonoObject *)MONO_GC_REVEAL_POINTER (ptr, is_weak);
+       else
+               return NULL;
+
+       /* Note [dummy use]:
+        *
+        * If a GC happens here, obj needs to be on the stack or in a
+        * register, so we need to prevent this from being reordered
+        * wrt the check.
+        */
+       mono_gc_dummy_use (obj);
+       mono_memory_barrier ();
+
+       if (is_weak)
+               mono_gc_ensure_weak_links_accessible ();
+
+       if ((void*)*link_addr_volatile != ptr)
+               goto retry;
+
+       return obj;
+}
+
 /**
  * mono_gchandle_get_target:
  * @gchandle: a GCHandle's handle.
@@ -884,53 +963,74 @@ mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
 MonoObject*
 mono_gchandle_get_target (guint32 gchandle)
 {
-       guint slot = GC_HANDLE_SLOT (gchandle);
+       guint index = GC_HANDLE_INDEX (gchandle);
        guint type = GC_HANDLE_TYPE (gchandle);
-       HandleData *handles = &gc_handles [type];
-       MonoObject *obj = NULL;
-       if (type >= HANDLE_TYPE_MAX)
-               return NULL;
-
-       lock_handles (handles);
-       if (slot < handles->size && slot_occupied (handles, slot)) {
-               if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
-                       obj = mono_gc_weak_link_get (&handles->entries [slot]);
-               } else {
-                       obj = handles->entries [slot];
-               }
-       } else {
-               /* print a warning? */
-       }
-       unlock_handles (handles);
-       /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
-       return obj;
+       HandleData *handles = gc_handles_for_type (type);
+       guint bucket, offset;
+       g_assert (index < handles->capacity);
+       bucketize (index, &bucket, &offset);
+       return link_get (&handles->entries [bucket] [offset], GC_HANDLE_TYPE_IS_WEAK (type));
 }
 
 static void
 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
 {
-       guint slot = GC_HANDLE_SLOT (gchandle);
+       guint index = GC_HANDLE_INDEX (gchandle);
        guint type = GC_HANDLE_TYPE (gchandle);
-       HandleData *handles = &gc_handles [type];
+       HandleData *handles = gc_handles_for_type (type);
+       gboolean track = handles->type == HANDLE_WEAK_TRACK;
+       guint bucket, offset;
+       gpointer slot;
+
+       g_assert (index < handles->capacity);
+       bucketize (index, &bucket, &offset);
+
+retry:
+       slot = handles->entries [bucket] [offset];
+       g_assert (MONO_GC_HANDLE_OCCUPIED (slot));
+       if (!try_set_slot (&handles->entries [bucket] [offset], obj, slot, GC_HANDLE_TYPE_IS_WEAK (handles->type)))
+               goto retry;
+       if (MONO_GC_HANDLE_IS_OBJECT_POINTER (slot))
+               mono_gc_weak_link_unregister (&handles->entries [bucket] [offset], track);
+       if (obj)
+               mono_gc_weak_link_register (&handles->entries [bucket] [offset], obj, track);
+}
 
-       g_assert (type < HANDLE_TYPE_MAX);
-       lock_handles (handles);
-       if (slot < handles->size && slot_occupied (handles, slot)) {
-               if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
-                       if (handles->entries [slot])
-                               mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
-                       if (obj)
-                               mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
-                       /*FIXME, what to use when obj == null?*/
-                       handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
-               } else {
-                       handles->entries [slot] = obj;
-               }
-       } else {
-               /* print a warning? */
+static MonoDomain *
+mono_gchandle_slot_domain (volatile gpointer *slot_addr, gboolean is_weak)
+{
+       gpointer slot;
+       MonoDomain *domain;
+retry:
+       slot = *slot_addr;
+       if (!MONO_GC_HANDLE_OCCUPIED (slot))
+               return NULL;
+       if (MONO_GC_HANDLE_IS_OBJECT_POINTER (slot)) {
+               MonoObject *obj = MONO_GC_REVEAL_POINTER (slot, is_weak);
+               /* See note [dummy use]. */
+               mono_gc_dummy_use (obj);
+               if (*slot_addr != slot)
+                       goto retry;
+               return mono_object_domain (obj);
        }
-       /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
-       unlock_handles (handles);
+       domain = MONO_GC_REVEAL_POINTER (slot, is_weak);
+       /* See note [dummy use]. */
+       mono_gc_dummy_use (domain);
+       if (*slot_addr != slot)
+               goto retry;
+       return domain;
+}
+
+static MonoDomain *
+gchandle_domain (guint32 gchandle) {
+       guint index = GC_HANDLE_INDEX (gchandle);
+       guint type = GC_HANDLE_TYPE (gchandle);
+       HandleData *handles = gc_handles_for_type (type);
+       guint bucket, offset;
+       if (index >= handles->capacity)
+               return NULL;
+       bucketize (index, &bucket, &offset);
+       return mono_gchandle_slot_domain (&handles->entries [bucket] [offset], GC_HANDLE_TYPE_IS_WEAK (type));
 }
 
 /**
@@ -943,31 +1043,7 @@ mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
 gboolean
 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
 {
-       guint slot = GC_HANDLE_SLOT (gchandle);
-       guint type = GC_HANDLE_TYPE (gchandle);
-       HandleData *handles = &gc_handles [type];
-       gboolean result = FALSE;
-
-       if (type >= HANDLE_TYPE_MAX)
-               return FALSE;
-
-       lock_handles (handles);
-       if (slot < handles->size && slot_occupied (handles, slot)) {
-               if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
-                       result = domain->domain_id == handles->domain_ids [slot];
-               } else {
-                       MonoObject *obj;
-                       obj = handles->entries [slot];
-                       if (obj == NULL)
-                               result = TRUE;
-                       else
-                               result = domain == mono_object_domain (obj);
-               }
-       } else {
-               /* print a warning? */
-       }
-       unlock_handles (handles);
-       return result;
+       return domain->domain_id == gchandle_domain (gchandle)->domain_id;
 }
 
 /**
@@ -981,65 +1057,69 @@ mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
 void
 mono_gchandle_free (guint32 gchandle)
 {
-       guint slot = GC_HANDLE_SLOT (gchandle);
+       guint index = GC_HANDLE_INDEX (gchandle);
        guint type = GC_HANDLE_TYPE (gchandle);
-       HandleData *handles = &gc_handles [type];
-       if (type >= HANDLE_TYPE_MAX)
-               return;
-
-       lock_handles (handles);
-       if (slot < handles->size && slot_occupied (handles, slot)) {
-               if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
-                       if (handles->entries [slot])
-                               mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
-               } else {
-                       handles->entries [slot] = NULL;
-               }
-               vacate_slot (handles, slot);
+       HandleData *handles = gc_handles_for_type (type);
+       guint bucket, offset;
+       bucketize (index, &bucket, &offset);
+       if (index < handles->capacity && MONO_GC_HANDLE_OCCUPIED (handles->entries [bucket] [offset])) {
+               if (GC_HANDLE_TYPE_IS_WEAK (handles->type))
+                       mono_gc_weak_link_unregister (&handles->entries [bucket] [offset], handles->type == HANDLE_WEAK_TRACK);
+               handles->entries [bucket] [offset] = NULL;
        } else {
                /* print a warning? */
        }
 #ifndef DISABLE_PERFCOUNTERS
        mono_perfcounters->gc_num_handles--;
 #endif
-       /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
-       unlock_handles (handles);
        mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
 }
 
 /**
  * mono_gchandle_free_domain:
- * @domain: domain that is unloading
+ * @unloading: domain that is unloading
  *
  * Function used internally to cleanup any GC handle for objects belonging
  * to the specified domain during appdomain unload.
  */
 void
-mono_gchandle_free_domain (MonoDomain *domain)
+mono_gchandle_free_domain (MonoDomain *unloading)
 {
        guint type;
-
+       /* All non-pinned handle types. */
        for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
-               guint slot;
-               HandleData *handles = &gc_handles [type];
-               lock_handles (handles);
-               for (slot = 0; slot < handles->size; ++slot) {
-                       if (!slot_occupied (handles, slot))
+               const gboolean is_weak = GC_HANDLE_TYPE_IS_WEAK (type);
+               guint index;
+               HandleData *handles = gc_handles_for_type (type);
+               guint32 capacity = handles->capacity;
+               for (index = 0; index < capacity; ++index) {
+                       guint bucket, offset;
+                       gpointer slot;
+                       bucketize (index, &bucket, &offset);
+                       MonoObject *obj = NULL;
+                       MonoDomain *domain;
+                       volatile gpointer *slot_addr = &handles->entries [bucket] [offset];
+                       /* NB: This should have the same behavior as mono_gchandle_slot_domain(). */
+               retry:
+                       slot = *slot_addr;
+                       if (!MONO_GC_HANDLE_OCCUPIED (slot))
                                continue;
-                       if (GC_HANDLE_TYPE_IS_WEAK (type)) {
-                               if (domain->domain_id == handles->domain_ids [slot]) {
-                                       vacate_slot (handles, slot);
-                                       if (handles->entries [slot])
-                                               mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
-                               }
+                       if (MONO_GC_HANDLE_IS_OBJECT_POINTER (slot)) {
+                               obj = MONO_GC_REVEAL_POINTER (slot, is_weak);
+                               if (*slot_addr != slot)
+                                       goto retry;
+                               domain = mono_object_domain (obj);
                        } else {
-                               if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
-                                       vacate_slot (handles, slot);
-                                       handles->entries [slot] = NULL;
-                               }
+                               domain = MONO_GC_REVEAL_POINTER (slot, is_weak);
+                       }
+                       if (unloading->domain_id == domain->domain_id) {
+                               if (GC_HANDLE_TYPE_IS_WEAK (type) && MONO_GC_REVEAL_POINTER (slot, is_weak))
+                                       mono_gc_weak_link_unregister (&handles->entries [bucket] [offset], handles->type == HANDLE_WEAK_TRACK);
+                               *slot_addr = NULL;
                        }
+                       /* See note [dummy use]. */
+                       mono_gc_dummy_use (obj);
                }
-               unlock_handles (handles);
        }
 
 }
@@ -1230,17 +1310,11 @@ mono_gc_init_finalizer_thread (void)
 void
 mono_gc_init (void)
 {
-#ifndef HAVE_SGEN_GC
-       mono_mutex_init_recursive (&handle_section);
-#endif
        mono_mutex_init_recursive (&allocator_section);
 
        mono_mutex_init_recursive (&finalizer_mutex);
        mono_mutex_init_recursive (&reference_queue_mutex);
 
-       MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_NORMAL].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
-       MONO_GC_REGISTER_ROOT_FIXED (gc_handles [HANDLE_PINNED].entries, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
-
        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);
        mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
@@ -1249,6 +1323,10 @@ mono_gc_init (void)
 
        mono_gc_base_init ();
 
+#ifdef HAVE_SGEN_GC
+       mono_gc_register_root ((char *)&gc_handles [0], sizeof (gc_handles), mono_gc_make_root_descr_user (mark_gc_handles), MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
+#endif
+
        if (mono_gc_is_disabled ()) {
                gc_disabled = TRUE;
                return;
@@ -1343,28 +1421,11 @@ mono_gc_cleanup (void)
 
        mono_reference_queue_cleanup ();
 
-#ifndef HAVE_SGEN_GC
-       mono_mutex_destroy (&handle_section);
-#endif
-
        mono_mutex_destroy (&allocator_section);
        mono_mutex_destroy (&finalizer_mutex);
        mono_mutex_destroy (&reference_queue_mutex);
 }
 
-/**
- * mono_gc_mutex_cleanup:
- *
- * Destroy the mutexes that may still be used after the main cleanup routine.
- */
-void
-mono_gc_mutex_cleanup (void)
-{
-#ifndef HAVE_SGEN_GC
-       mono_mutex_destroy (&handle_section);
-#endif
-}
-
 gboolean
 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
 {
index 18313f98b12b96c1aa4dcdc79aa057d7f0f0d878..b9edad825d44635bc2facd6c4eb90d16b51ff5b6 100644 (file)
@@ -345,7 +345,7 @@ delegate_hash_table_remove (MonoDelegate *d)
                gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (delegate_hash_table, d->delegate_trampoline));
        g_hash_table_remove (delegate_hash_table, d->delegate_trampoline);
        mono_marshal_unlock ();
-       if (mono_gc_is_moving ())
+       if (gchandle && mono_gc_is_moving ())
                mono_gchandle_free (gchandle);
 }
 
index 5a0f8687a83dd24d22008175808339d8c48dc125..73dffddad6b08c62d04f7bfd08c49289a90124a3 100644 (file)
@@ -11891,15 +11891,14 @@ free_dynamic_method (void *dynamic_method)
        DynamicMethodReleaseData *data = dynamic_method;
        MonoDomain *domain = data->domain;
        MonoMethod *method = data->handle;
-       gpointer *dis_link;
+       guint32 dis_link;
 
        mono_domain_lock (domain);
-       dis_link = g_hash_table_lookup (domain->method_to_dyn_method, method);
+       dis_link = (guint32)(size_t)g_hash_table_lookup (domain->method_to_dyn_method, method);
        g_hash_table_remove (domain->method_to_dyn_method, method);
        mono_domain_unlock (domain);
        g_assert (dis_link);
-       mono_gc_weak_link_remove (dis_link, TRUE);
-       g_free (dis_link);
+       mono_gchandle_free (dis_link);
 
        mono_runtime_free_method (domain, method);
        g_free (data);
@@ -11916,7 +11915,6 @@ mono_reflection_create_dynamic_method (MonoReflectionDynamicMethod *mb)
        MonoClass *klass;
        MonoDomain *domain;
        GSList *l;
-       void *dis_link;
        int i;
 
        if (mono_runtime_is_shutting_down ())
@@ -12018,9 +12016,7 @@ mono_reflection_create_dynamic_method (MonoReflectionDynamicMethod *mb)
        mono_domain_lock (domain);
        if (!domain->method_to_dyn_method)
                domain->method_to_dyn_method = g_hash_table_new (NULL, NULL);
-       dis_link = g_new0 (gpointer, 1);
-       mono_gc_weak_link_add (dis_link, (MonoObject*)mb, TRUE);
-       g_hash_table_insert (domain->method_to_dyn_method, handle, dis_link);
+       g_hash_table_insert (domain->method_to_dyn_method, handle, (gpointer)(size_t)mono_gchandle_new_weakref ((MonoObject *)mb, TRUE));
        mono_domain_unlock (domain);
 }
 
index 824e5d461a289b75056d4182773bd29ae34d822c..60eb578939451a20e97b8b13faa5e02a119768fa 100644 (file)
@@ -29,7 +29,7 @@
 #include "mono/sgen/sgen-gc.h"
 #include "mono/metadata/sgen-bridge.h"
 
-extern gboolean bridge_processing_in_progress;
+extern volatile gboolean bridge_processing_in_progress;
 extern MonoGCBridgeCallbacks bridge_callbacks;
 
 gboolean sgen_need_bridge_processing (void);
index 3f2992dbb047b37f20099d6e92f611f915bf8d84..9eb5b3693034dfd541355e92dd2d375925199145 100644 (file)
@@ -53,7 +53,7 @@ MonoGCBridgeCallbacks bridge_callbacks;
 static SgenBridgeProcessor bridge_processor;
 static SgenBridgeProcessor compare_to_bridge_processor;
 
-gboolean bridge_processing_in_progress = FALSE;
+volatile gboolean bridge_processing_in_progress = FALSE;
 
 void
 mono_gc_wait_for_bridge_processing (void)
index b946b36c699f5746b830913894a3828650aa59a3..07ef1027090f98cbfb53ebc61b29045e65d37181 100644 (file)
@@ -2571,23 +2571,32 @@ mono_gc_get_los_limit (void)
 }
 
 void
-mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
+mono_gc_weak_link_register (volatile gpointer *link_addr, MonoObject *obj, gboolean track)
 {
        binary_protocol_dislink_add ((gpointer)link_addr, obj, track);
-       *link_addr = (void*)HIDE_POINTER (obj);
 }
 
 void
-mono_gc_weak_link_remove (void **link_addr, gboolean track)
+mono_gc_weak_link_unregister (volatile gpointer *link_addr, gboolean track)
 {
        binary_protocol_dislink_remove ((gpointer)link_addr, track);
-       *link_addr = NULL;
 }
 
-MonoObject*
-mono_gc_weak_link_get (void **link_addr)
+void
+mono_gc_ensure_weak_links_accessible (void)
 {
-       return sgen_weak_link_get (link_addr);
+       /*
+        * During the second bridge processing step the world is
+        * running again.  That step processes all weak links once
+        * more to null those that refer to dead objects.  Before that
+        * is completed, those links must not be followed, so we
+        * conservatively wait for bridge processing when any weak
+        * link is dereferenced.
+        */
+       /* FIXME: A GC can occur after this check fails, in which case we
+        * should wait for bridge processing but would fail to do so.
+        */
+       mono_gc_wait_for_bridge_processing ();
 }
 
 gboolean
index fd607d71f4397a9742c42d61a649a7878f220829..bc865e0d97f3aa4d7c263ccd546e0bf37a81dfa9 100644 (file)
@@ -1164,15 +1164,15 @@ setup_stack_trace (MonoException *mono_ex, GSList *dynamic_methods, MonoArray *i
                        MonoMList *list = NULL;
 
                        for (l = dynamic_methods; l; l = l->next) {
-                               gpointer *dis_link;
+                               guint32 dis_link;
                                MonoDomain *domain = mono_domain_get ();
 
                                if (domain->method_to_dyn_method) {
                                        mono_domain_lock (domain);
-                                       dis_link = g_hash_table_lookup (domain->method_to_dyn_method, l->data);
+                                       dis_link = (guint32)(size_t)g_hash_table_lookup (domain->method_to_dyn_method, l->data);
                                        mono_domain_unlock (domain);
                                        if (dis_link) {
-                                               MonoObject *o = mono_gc_weak_link_get (dis_link);
+                                               MonoObject *o = mono_gchandle_get_target (dis_link);
                                                if (o) {
                                                        list = mono_mlist_prepend (list, o);
                                                }
index 2cf9580859fbd425b7f80849c27ada1dbee6fb61..8e42c09edf0a458cc6bb3de9228c8fc5097ed1e6 100644 (file)
@@ -3443,7 +3443,6 @@ mini_cleanup (MonoDomain *domain)
 
 #ifndef MONO_CROSS_COMPILE
        mono_domain_free (domain, TRUE);
-       mono_gc_mutex_cleanup ();
 #endif
 
 #ifdef ENABLE_LLVM
index ef85b74d36c67b94e1568fa140526a8faeed7793..5ca152d22dd2cdab0642ca16f15a4041dc6ce780 100644 (file)
 #include "mono/sgen/sgen-conf.h"
 #endif
 
-#ifndef HIDE_POINTER
-#define HIDE_POINTER(p) ((gpointer)~(size_t)(p))
-#endif
+/* h indicates whether to hide or just tag.
+ * (-!!h ^ p) is used instead of (h ? ~p : p) to avoid multiple mentions of p.
+ */
+#define MONO_GC_HIDE_POINTER(p,t,h) ((gpointer)(((-(size_t)!!(h) ^ (size_t)(p)) & ~3UL) | (t & 3UL)))
+#define MONO_GC_REVEAL_POINTER(p,h) ((gpointer)((-(size_t)!!(h) ^ (size_t)(p)) & ~3UL))
 
-#ifndef REVEAL_POINTER
-#define REVEAL_POINTER(p) ((gpointer)~(size_t)(p))
-#endif
+#define MONO_GC_POINTER_TAG(p) ((size_t)(p) & 3UL)
+
+#define MONO_GC_HANDLE_OCCUPIED_MASK (1)
+#define MONO_GC_HANDLE_VALID_MASK (2)
+#define MONO_GC_HANDLE_TAG_MASK (MONO_GC_HANDLE_OCCUPIED_MASK | MONO_GC_HANDLE_VALID_MASK)
+
+#define MONO_GC_HANDLE_DOMAIN_POINTER(p,h) (MONO_GC_HIDE_POINTER ((p), MONO_GC_HANDLE_OCCUPIED_MASK, (h)))
+#define MONO_GC_HANDLE_OBJECT_POINTER(p,h) (MONO_GC_HIDE_POINTER ((p), MONO_GC_HANDLE_OCCUPIED_MASK | MONO_GC_HANDLE_VALID_MASK, (h)))
+
+#define MONO_GC_HANDLE_OCCUPIED(slot) ((size_t)(slot) & MONO_GC_HANDLE_OCCUPIED_MASK)
+#define MONO_GC_HANDLE_VALID(slot) ((size_t)(slot) & MONO_GC_HANDLE_VALID_MASK)
+
+#define MONO_GC_HANDLE_TAG(slot) ((size_t)(slot) & MONO_GC_HANDLE_TAG_MASK)
+
+#define MONO_GC_HANDLE_IS_OBJECT_POINTER(slot) (MONO_GC_HANDLE_TAG (slot) == (MONO_GC_HANDLE_OCCUPIED_MASK | MONO_GC_HANDLE_VALID_MASK))
+#define MONO_GC_HANDLE_IS_DOMAIN_POINTER(slot) (MONO_GC_HANDLE_TAG (slot) == MONO_GC_HANDLE_OCCUPIED_MASK)
 
 typedef enum {
        HANDLE_TYPE_MIN = 0,
@@ -48,7 +63,9 @@ typedef enum {
        HANDLE_TYPE_MAX
 } GCHandleType;
 
-void mono_gchandle_iterate (GCHandleType handle_type, int max_generation, gpointer callback(gpointer *, GCHandleType, gpointer), gpointer user);
+#define GC_HANDLE_TYPE_IS_WEAK(x) ((x) <= HANDLE_WEAK_TRACK)
+
+void mono_gchandle_iterate (GCHandleType handle_type, int max_generation, gpointer callback(gpointer, GCHandleType, gpointer), gpointer user);
 
 typedef struct {
        guint minor_gc_count;
@@ -99,4 +116,19 @@ void mono_gc_memmove_aligned (void *dest, const void *src, size_t size);
 
 FILE *mono_gc_get_logfile (void);
 
+/*
+ * This causes the compile to extend the liveness of 'v' till the call to dummy_use
+ */
+static inline void
+mono_gc_dummy_use (gpointer v) {
+#if defined(__GNUC__)
+       __asm__ volatile ("" : "=r"(v) : "r"(v));
+#elif defined(_MSC_VER)
+       static volatile gpointer ptr;
+       ptr = v;
+#else
+#error "Implement mono_gc_dummy_use for your compiler"
+#endif
+}
+
 #endif
index fb18def266d4bd4441db6466c5f782c4bd2b80d9..733a7e85c3e11dfca7b0e9510c811a679a5251a3 100644 (file)
@@ -80,7 +80,7 @@ sgen_card_table_wbarrier_set_field (GCObject *obj, gpointer field_ptr, GCObject*
        *(void**)field_ptr = value;
        if (need_mod_union || sgen_ptr_in_nursery (value))
                sgen_card_table_mark_address ((mword)field_ptr);
-       sgen_dummy_use (value);
+       mono_gc_dummy_use (value);
 }
 
 static void
@@ -100,7 +100,7 @@ sgen_card_table_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int
                        SGEN_UPDATE_REFERENCE_ALLOW_NULL (dest, value);
                        if (need_mod_union || sgen_ptr_in_nursery (value))
                                sgen_card_table_mark_address ((mword)dest);
-                       sgen_dummy_use (value);
+                       mono_gc_dummy_use (value);
                }
        } else {
                gpointer *end = dest + count;
@@ -109,7 +109,7 @@ sgen_card_table_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int
                        SGEN_UPDATE_REFERENCE_ALLOW_NULL (dest, value);
                        if (need_mod_union || sgen_ptr_in_nursery (value))
                                sgen_card_table_mark_address ((mword)dest);
-                       sgen_dummy_use (value);
+                       mono_gc_dummy_use (value);
                }
        }       
 }
index 5bf77c4fde3972a29581dad5846d6a264c7a8ec5..0f222a1fa556e92ed259c629a9a74a8538acff2c 100644 (file)
@@ -634,28 +634,35 @@ sgen_gather_finalizers_if (SgenObjectPredicateFunc predicate, void *user_data, G
  * Returns whether to remove the link from its hash.
  */
 static gpointer
-null_link_if_necessary (gpointer *hidden_entry, GCHandleType handle_type, gpointer user)
+null_link_if_necessary (gpointer obj_untyped, GCHandleType handle_type, gpointer user)
 {
+       GCObject *obj = obj_untyped;
+       const gboolean is_weak = GC_HANDLE_TYPE_IS_WEAK (handle_type);
        ScanCopyContext *ctx = (ScanCopyContext *)user;
-       gpointer entry = REVEAL_POINTER (*hidden_entry);
-       char *copy = entry;
-       gboolean entry_in_nursery = ptr_in_nursery (entry);
-       if (sgen_get_current_collection_generation () == GENERATION_NURSERY && !entry_in_nursery)
-               return *hidden_entry;
-       /* Clear link if object is ready for finalization. */
-       if (sgen_gc_is_object_ready_for_finalization (entry)) {
-               return NULL;
+       char *copy = (char *)obj;
+       g_assert (obj);
+       if (major_collector.is_object_live ((char *)obj))
+               return MONO_GC_HANDLE_OBJECT_POINTER (obj, is_weak);
+       /*
+       gboolean obj_in_nursery = ptr_in_nursery (obj);
+       if (sgen_get_current_collection_generation () == GENERATION_NURSERY && !obj_in_nursery)
+               return GC_HANDLE_OBJECT_POINTER (obj);
+       */
+       /* Clear link if object is ready for finalization. This check may be redundant wrt is_object_live(). */
+       if (sgen_gc_is_object_ready_for_finalization (obj)) {
+               /* binary_protocol_dislink_update (hidden_entry, entry, 0); */
+               return MONO_GC_HANDLE_DOMAIN_POINTER (mono_object_domain (obj), is_weak);
        }
        ctx->ops->copy_or_mark_object ((void **)&copy, ctx->queue);
        g_assert (copy);
-       /* Update pointer if it's moved. */
-       binary_protocol_dislink_update (hidden_entry, copy, handle_type == HANDLE_WEAK_TRACK);
-       return HIDE_POINTER (copy);
+       /* binary_protocol_dislink_update (hidden_entry, copy, handle_type == HANDLE_WEAK_TRACK); */
+       /* Update link if object was moved. */
+       return MONO_GC_HANDLE_OBJECT_POINTER (copy, is_weak);
 }
 
 /* LOCKING: requires that the GC lock is held */
 void
-sgen_null_link_in_range (int generation, gboolean before_finalization, ScanCopyContext ctx, gboolean track)
+sgen_null_link_in_range (int generation, ScanCopyContext ctx, gboolean track)
 {
        mono_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if_necessary, &ctx);
 }
@@ -666,18 +673,16 @@ typedef struct {
 } WeakLinkAlivePredicateClosure;
 
 static gpointer
-null_link_if (gpointer *hidden_entry, GCHandleType handle_type, gpointer user)
+null_link_if (gpointer obj_untyped, GCHandleType handle_type, gpointer user)
 {
+       GCObject *obj = obj_untyped;
        /* Strictly speaking, function pointers are not guaranteed to have the same size as data pointers. */
        WeakLinkAlivePredicateClosure *closure = (WeakLinkAlivePredicateClosure *)user;
-       gpointer entry = REVEAL_POINTER (*hidden_entry);
-       if (!entry)
-               return NULL;
-       if (closure->predicate ((MonoObject*)entry, closure->data)) {
-               binary_protocol_dislink_update (hidden_entry, NULL, 0);
+       if (closure->predicate (obj, closure->data)) {
+               /* binary_protocol_dislink_update (hidden_entry, NULL, 0); */
                return NULL;
        }
-       return *hidden_entry;
+       return MONO_GC_HANDLE_OBJECT_POINTER (obj, GC_HANDLE_TYPE_IS_WEAK (handle_type));
 }
 
 /* LOCKING: requires that the GC lock is held */
index 382b259e711c65e3dd65b95befcee7b7c9e47505..d1cd18d7244ec094a954488e1308b557b89e9180 100644 (file)
@@ -1138,9 +1138,9 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
        We must clear weak links that don't track resurrection before processing object ready for
        finalization so they can be cleared before that.
        */
-       sgen_null_link_in_range (generation, TRUE, ctx, FALSE);
+       sgen_null_link_in_range (generation, ctx, FALSE);
        if (generation == GENERATION_OLD)
-               sgen_null_link_in_range (GENERATION_NURSERY, TRUE, ctx, FALSE);
+               sgen_null_link_in_range (GENERATION_NURSERY, ctx, FALSE);
 
 
        /* walk the finalization queue and move also the objects that need to be
@@ -1187,9 +1187,9 @@ finish_gray_stack (int generation, ScanCopyContext ctx)
         */
        g_assert (sgen_gray_object_queue_is_empty (queue));
        for (;;) {
-               sgen_null_link_in_range (generation, FALSE, ctx, TRUE);
+               sgen_null_link_in_range (generation, ctx, TRUE);
                if (generation == GENERATION_OLD)
-                       sgen_null_link_in_range (GENERATION_NURSERY, FALSE, ctx, TRUE);
+                       sgen_null_link_in_range (GENERATION_NURSERY, ctx, TRUE);
                if (sgen_gray_object_queue_is_empty (queue))
                        break;
                sgen_drain_gray_stack (-1, ctx);
@@ -2674,7 +2674,7 @@ mono_gc_wbarrier_generic_store (gpointer ptr, GCObject* value)
        SGEN_UPDATE_REFERENCE_ALLOW_NULL (ptr, value);
        if (ptr_in_nursery (value) || concurrent_collection_in_progress)
                mono_gc_wbarrier_generic_nostore (ptr);
-       sgen_dummy_use (value);
+       mono_gc_dummy_use (value);
 }
 
 /* Same as mono_gc_wbarrier_generic_store () but performs the store
@@ -2692,7 +2692,7 @@ mono_gc_wbarrier_generic_store_atomic (gpointer ptr, GCObject *value)
        if (ptr_in_nursery (value) || concurrent_collection_in_progress)
                mono_gc_wbarrier_generic_nostore (ptr);
 
-       sgen_dummy_use (value);
+       mono_gc_dummy_use (value);
 }
 
 void
@@ -2750,48 +2750,6 @@ sgen_gc_get_used_size (void)
        return tot;
 }
 
-GCObject*
-sgen_weak_link_get (void **link_addr)
-{
-       void * volatile *link_addr_volatile;
-       void *ptr;
-       GCObject *obj;
- retry:
-       link_addr_volatile = link_addr;
-       ptr = (void*)*link_addr_volatile;
-       /*
-        * At this point we have a hidden pointer.  If the GC runs
-        * here, it will not recognize the hidden pointer as a
-        * reference, and if the object behind it is not referenced
-        * elsewhere, it will be freed.  Once the world is restarted
-        * we reveal the pointer, giving us a pointer to a freed
-        * object.  To make sure we don't return it, we load the
-        * hidden pointer again.  If it's still the same, we can be
-        * sure the object reference is valid.
-        */
-       if (ptr)
-               obj = (GCObject*) REVEAL_POINTER (ptr);
-       else
-               return NULL;
-
-       mono_memory_barrier ();
-
-       /*
-        * During the second bridge processing step the world is
-        * running again.  That step processes all weak links once
-        * more to null those that refer to dead objects.  Before that
-        * is completed, those links must not be followed, so we
-        * conservatively wait for bridge processing when any weak
-        * link is dereferenced.
-        */
-       sgen_client_bridge_wait_for_processing ();
-
-       if ((void*)*link_addr_volatile != ptr)
-               goto retry;
-
-       return obj;
-}
-
 gboolean
 sgen_set_allow_synchronous_major (gboolean flag)
 {
@@ -2873,7 +2831,7 @@ sgen_gc_init (void)
        mono_thread_smr_init ();
 #endif
 
-       mono_mutex_init_recursive (&gc_mutex);
+       LOCK_INIT (gc_mutex);
 
        gc_debug_file = stderr;
 
index 327d47f08c6964216cb6f70fc0cea6658e48f6fd..9cf9ca908ed44078b9b47a4cfd0f75d4ebcd33f3 100644 (file)
@@ -788,7 +788,7 @@ void sgen_queue_finalization_entry (GCObject *obj);
 const char* sgen_generation_name (int generation);
 
 void sgen_finalize_in_range (int generation, ScanCopyContext ctx);
-void sgen_null_link_in_range (int generation, gboolean before_finalization, ScanCopyContext ctx, gboolean track);
+void sgen_null_link_in_range (int generation, ScanCopyContext ctx, gboolean track);
 void sgen_process_fin_stage_entries (void);
 gboolean sgen_have_pending_finalizers (void);
 void sgen_object_register_for_finalization (GCObject *obj, void *user_data);
@@ -1004,23 +1004,6 @@ void sgen_debug_dump_heap (const char *type, int num, const char *reason);
 void sgen_debug_verify_nursery (gboolean do_dump_nursery_content);
 void sgen_debug_check_nursery_is_clean (void);
 
-/* Write barrier support */
-
-/*
- * This causes the compile to extend the liveness of 'v' till the call to dummy_use
- */
-static inline void
-sgen_dummy_use (gpointer v) {
-#if defined(__GNUC__)
-       __asm__ volatile ("" : "=r"(v) : "r"(v));
-#elif defined(_MSC_VER)
-       static volatile gpointer ptr;
-       ptr = v;
-#else
-#error "Implement sgen_dummy_use for your compiler"
-#endif
-}
-
 /* Environment variable parsing */
 
 #define MONO_GC_PARAMS_NAME    "MONO_GC_PARAMS"