Merge pull request #1457 from evincarofautumn/weak-refs
authorMark Probst <mark.probst@gmail.com>
Tue, 8 Sep 2015 20:24:14 +0000 (13:24 -0700)
committerMark Probst <mark.probst@gmail.com>
Tue, 8 Sep 2015 20:24:14 +0000 (13:24 -0700)
Refactor weak-reference handling code in SGen

21 files changed:
mono/metadata/boehm-gc.c
mono/metadata/gc-internal.h
mono/metadata/gc.c
mono/metadata/marshal.c
mono/metadata/monitor.c
mono/metadata/monitor.h
mono/metadata/reflection.c
mono/metadata/sgen-bridge-internal.h
mono/metadata/sgen-bridge.c
mono/metadata/sgen-client-mono.h
mono/metadata/sgen-mono.c
mono/mini/mini-exceptions.c
mono/mini/mini-runtime.c
mono/sgen/Makefile.am
mono/sgen/gc-internal-agnostic.h
mono/sgen/sgen-client.h
mono/sgen/sgen-fin-weak-hash.c
mono/sgen/sgen-gc.c
mono/sgen/sgen-gc.h
mono/sgen/sgen-gchandles.c [new file with mode: 0644]
mono/sgen/sgen-protocol-def.h

index 2fb0ec330f7288da57c42884fc8f6b4ced209171..540c2b94cf647416e3ff835678b41e4d44454795 100644 (file)
@@ -61,6 +61,34 @@ register_test_toggleref_callback (void);
 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
 static MonoGCFinalizerCallbacks fin_callbacks;
 
+/* 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)
+
+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;
+} HandleData;
+
+#define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
+
+/* weak and weak-track arrays will be allocated in malloc memory 
+ */
+static HandleData gc_handles [] = {
+       EMPTY_HANDLE_DATA (HANDLE_WEAK),
+       EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
+       EMPTY_HANDLE_DATA (HANDLE_NORMAL),
+       EMPTY_HANDLE_DATA (HANDLE_PINNED)
+};
+
 static void
 mono_gc_warning (char *msg, GC_word arg)
 {
@@ -210,10 +238,15 @@ mono_gc_base_init (void)
 
        mono_threads_init (&cb, sizeof (MonoThreadInfo));
        mono_mutex_init (&mono_gc_lock);
+       mono_mutex_init_recursive (&handle_section);
 
        mono_thread_info_attach (&dummy);
 
        mono_gc_enable_events ();
+
+       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");
+
        gc_initialized = TRUE;
 }
 
@@ -493,7 +526,7 @@ mono_gc_deregister_root (char* addr)
 #endif
 }
 
-void
+static void
 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
 {
        /* libgc requires that we use HIDE_POINTER... */
@@ -504,7 +537,7 @@ mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
                GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
 }
 
-void
+static void
 mono_gc_weak_link_remove (void **link_addr, gboolean track)
 {
        if (track)
@@ -521,7 +554,7 @@ reveal_link (gpointer link_addr)
        return REVEAL_POINTER (*link_a);
 }
 
-MonoObject*
+static MonoObject *
 mono_gc_weak_link_get (void **link_addr)
 {
        MonoObject *obj = GC_call_with_alloc_lock (reveal_link, link_addr);
@@ -1456,10 +1489,381 @@ mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
        GC_set_finalizer_notify_proc ((void (*) (GC_PTR))fin_notifier);
 }
 
+#define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
+
+static inline gboolean
+slot_occupied (HandleData *handles, guint slot) {
+       return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
+}
+
+static inline void
+vacate_slot (HandleData *handles, guint slot) {
+       handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
+}
+
+static inline void
+occupy_slot (HandleData *handles, guint slot) {
+       handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
+}
+
+static int
+find_first_unset (guint32 bitmap)
+{
+       int i;
+       for (i = 0; i < 32; ++i) {
+               if (!(bitmap & (1 << i)))
+                       return i;
+       }
+       return -1;
+}
+
+static void
+handle_data_alloc_entries (HandleData *handles)
+{
+       handles->size = 32;
+       if (MONO_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, NULL, 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]);
+       }
+       return -1;
+}
+
+static gint
+handle_data_first_unset (HandleData *handles)
+{
+       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]);
+       }
+       return -1;
+}
+
+/* Returns the index of the current slot in the bitmap. */
+static void
+handle_data_grow (HandleData *handles, gboolean track)
+{
+       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 (MONO_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, NULL, 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;
+       }
+       handles->slot_hint = handles->size / BITMAP_SIZE;
+       handles->size = new_size;
+}
+
+static guint32
+alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
+{
+       gint slot, i;
+       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 (MONO_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;
+       }
+
+#ifndef DISABLE_PERFCOUNTERS
+       mono_perfcounters->gc_num_handles++;
+#endif
+       unlock_handles (handles);
+       res = MONO_GC_HANDLE (slot, handles->type);
+       mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
+       return res;
+}
+
+/**
+ * mono_gchandle_new:
+ * @obj: managed object to get a handle for
+ * @pinned: whether the object should be pinned
+ *
+ * This returns a handle that wraps the object, this is used to keep a
+ * reference to a managed object from the unmanaged world and preventing the
+ * object from being disposed.
+ * 
+ * If @pinned is false the address of the object can not be obtained, if it is
+ * true the address of the object can be obtained.  This will also pin the
+ * object so it will not be possible by a moving garbage collector to move the
+ * object. 
+ * 
+ * Returns: a handle that can be used to access the object from
+ * unmanaged code.
+ */
+guint32
+mono_gchandle_new (MonoObject *obj, gboolean pinned)
+{
+       return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
+}
+
+/**
+ * mono_gchandle_new_weakref:
+ * @obj: managed object to get a handle for
+ * @pinned: whether the object should be pinned
+ *
+ * This returns a weak handle that wraps the object, this is used to
+ * keep a reference to a managed object from the unmanaged world.
+ * Unlike the mono_gchandle_new the object can be reclaimed by the
+ * garbage collector.  In this case the value of the GCHandle will be
+ * set to zero.
+ * 
+ * If @pinned is false the address of the object can not be obtained, if it is
+ * true the address of the object can be obtained.  This will also pin the
+ * object so it will not be possible by a moving garbage collector to move the
+ * object. 
+ * 
+ * Returns: a handle that can be used to access the object from
+ * unmanaged code.
+ */
+guint32
+mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
+{
+       return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
+}
+
+/**
+ * mono_gchandle_get_target:
+ * @gchandle: a GCHandle's handle.
+ *
+ * The handle was previously created by calling mono_gchandle_new or
+ * mono_gchandle_new_weakref. 
+ *
+ * Returns a pointer to the MonoObject represented by the handle or
+ * NULL for a collected object if using a weakref handle.
+ */
+MonoObject*
+mono_gchandle_get_target (guint32 gchandle)
+{
+       guint slot = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_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 (MONO_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;
+}
+
+void
+mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
+{
+       guint slot = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       HandleData *handles = &gc_handles [type];
+       MonoObject *old_obj = NULL;
+
+       g_assert (type < HANDLE_TYPE_MAX);
+       lock_handles (handles);
+       if (slot < handles->size && slot_occupied (handles, slot)) {
+               if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
+                       old_obj = handles->entries [slot];
+                       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? */
+       }
+       /*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);
+}
+
+/**
+ * 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.
+ */
 gboolean
 mono_gc_is_null (void)
 {
        return FALSE;
 }
 
+gboolean
+mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
+{
+       guint slot = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_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 (MONO_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;
+}
+
+/**
+ * mono_gchandle_free:
+ * @gchandle: a GCHandle's handle.
+ *
+ * Frees the @gchandle handle.  If there are no outstanding
+ * references, the garbage collector can reclaim the memory of the
+ * object wrapped. 
+ */
+void
+mono_gchandle_free (guint32 gchandle)
+{
+       guint slot = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_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 (MONO_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);
+       } 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
+ *
+ * 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)
+{
+       guint type;
+
+       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))
+                               continue;
+                       if (MONO_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);
+                               }
+                       } else {
+                               if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
+                                       vacate_slot (handles, slot);
+                                       handles->entries [slot] = NULL;
+                               }
+                       }
+               }
+               unlock_handles (handles);
+       }
+
+}
+
 #endif /* no Boehm GC */
index caac7de930123d346f7e011ba10463ffbecf4972..3b599f1b57e9d30fa8a0bc5e7c5e95670af0a615 100644 (file)
@@ -11,6 +11,7 @@
 #define __MONO_METADATA_GC_INTERNAL_H__
 
 #include <glib.h>
+#include <mono/utils/gc_wrapper.h>
 #include <mono/metadata/object-internals.h>
 #include <mono/metadata/threads-types.h>
 #include <mono/sgen/gc-internal-agnostic.h>
@@ -84,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);
 
 /*
@@ -107,9 +107,11 @@ 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);
+
+void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
 
 /*Ephemeron functionality. Sgen only*/
 gboolean    mono_gc_ephemeron_array_add (MonoObject *obj);
index 00018597956a45b08b6a6413bb03be17d0107d87..bc34095900d92a7fb98dcbc8de82fbbfc9f78937 100644 (file)
@@ -25,6 +25,7 @@
 #include <mono/metadata/threads-types.h>
 #include <mono/metadata/threadpool-ms.h>
 #include <mono/sgen/sgen-conf.h>
+#include <mono/sgen/sgen-gc.h>
 #include <mono/utils/mono-logger-internal.h>
 #include <mono/metadata/gc-internal.h>
 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
@@ -70,8 +71,6 @@ static MonoInternalThread *gc_thread;
 
 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
 
-static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj);
-
 static void reference_queue_proccess_all (void);
 static void mono_reference_queue_cleanup (void);
 static void reference_queue_clear_for_domain (MonoDomain *domain);
@@ -529,16 +528,6 @@ ves_icall_System_GC_get_ephemeron_tombstone (void)
 #define mono_allocator_lock() mono_mutex_lock (&allocator_section)
 #define mono_allocator_unlock() mono_mutex_unlock (&allocator_section)
 static mono_mutex_t allocator_section;
-static mono_mutex_t handle_section;
-
-typedef enum {
-       HANDLE_WEAK,
-       HANDLE_WEAK_TRACK,
-       HANDLE_NORMAL,
-       HANDLE_PINNED
-} HandleType;
-
-static HandleType mono_gchandle_get_type (guint32 gchandle);
 
 MonoObject *
 ves_icall_System_GCHandle_GetTarget (guint32 handle)
@@ -583,7 +572,7 @@ ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
 {
        MonoObject *obj;
 
-       if (mono_gchandle_get_type (handle) != HANDLE_PINNED)
+       if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
                return (gpointer)-2;
        obj = mono_gchandle_get_target (handle);
        if (obj) {
@@ -609,385 +598,6 @@ ves_icall_Mono_Runtime_SetGCAllowSynchronousMajor (MonoBoolean flag)
        return mono_gc_set_allow_synchronous_major (flag);
 }
 
-typedef struct {
-       guint32  *bitmap;
-       gpointer *entries;
-       guint32   size;
-       guint8    type;
-       guint     slot_hint : 24; /* starting slot for search */
-       /* 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;
-} HandleData;
-
-/* weak and weak-track arrays will be allocated in malloc memory 
- */
-static HandleData gc_handles [] = {
-       {NULL, NULL, 0, HANDLE_WEAK, 0},
-       {NULL, NULL, 0, HANDLE_WEAK_TRACK, 0},
-       {NULL, NULL, 0, HANDLE_NORMAL, 0},
-       {NULL, NULL, 0, HANDLE_PINNED, 0}
-};
-
-#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)
-
-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)
-{
-#ifdef HAVE_SGEN_GC
-       if (pinned)
-               return MONO_GC_DESCRIPTOR_NULL;
-#endif
-       return mono_gc_make_root_descr_all_refs (numbits);
-}
-
-static guint32
-alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
-{
-       gint slot, i;
-       guint32 res;
-       lock_handles (handles);
-       if (!handles->size) {
-               handles->size = 32;
-               if (handles->type > HANDLE_WEAK_TRACK) {
-                       handles->entries = mono_gc_alloc_fixed (sizeof (gpointer) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED), MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
-               } else {
-                       handles->entries = g_malloc0 (sizeof (gpointer) * handles->size);
-                       handles->domain_ids = g_malloc0 (sizeof (guint16) * handles->size);
-               }
-               handles->bitmap = g_malloc0 (handles->size / 8);
-       }
-       i = -1;
-       for (slot = handles->slot_hint; slot < handles->size / 32; ++slot) {
-               if (handles->bitmap [slot] != 0xffffffff) {
-                       i = find_first_unset (handles->bitmap [slot]);
-                       handles->slot_hint = slot;
-                       break;
-               }
-       }
-       if (i == -1 && handles->slot_hint != 0) {
-               for (slot = 0; slot < handles->slot_hint; ++slot) {
-                       if (handles->bitmap [slot] != 0xffffffff) {
-                               i = find_first_unset (handles->bitmap [slot]);
-                               handles->slot_hint = slot;
-                               break;
-                       }
-               }
-       }
-       if (i == -1) {
-               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 / 8);
-               memcpy (new_bitmap, handles->bitmap, handles->size / 8);
-               g_free (handles->bitmap);
-               handles->bitmap = new_bitmap;
-
-               /* resize and copy the entries */
-               if (handles->type > HANDLE_WEAK_TRACK) {
-                       gpointer *entries;
-
-                       entries = mono_gc_alloc_fixed (sizeof (gpointer) * 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 (gpointer) * handles->size);
-
-                       mono_gc_free_fixed (handles->entries);
-                       handles->entries = entries;
-               } else {
-                       gpointer *entries;
-                       guint16 *domain_ids;
-                       domain_ids = g_malloc0 (sizeof (guint16) * new_size);
-                       entries = g_malloc0 (sizeof (gpointer) * new_size);
-                       memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * 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;
-               }
-
-               /* set i and slot to the next free position */
-               i = 0;
-               slot = (handles->size + 1) / 32;
-               handles->slot_hint = handles->size + 1;
-               handles->size = new_size;
-       }
-       handles->bitmap [slot] |= 1 << i;
-       slot = slot * 32 + i;
-       handles->entries [slot] = NULL;
-       if (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;
-               if (obj)
-                       mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
-       } else {
-               handles->entries [slot] = obj;
-       }
-
-#ifndef DISABLE_PERFCOUNTERS
-       mono_perfcounters->gc_num_handles++;
-#endif
-       unlock_handles (handles);
-       /*g_print ("allocated entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
-       res = (slot << 3) | (handles->type + 1);
-       mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
-       return res;
-}
-
-/**
- * mono_gchandle_new:
- * @obj: managed object to get a handle for
- * @pinned: whether the object should be pinned
- *
- * This returns a handle that wraps the object, this is used to keep a
- * reference to a managed object from the unmanaged world and preventing the
- * object from being disposed.
- * 
- * If @pinned is false the address of the object can not be obtained, if it is
- * true the address of the object can be obtained.  This will also pin the
- * object so it will not be possible by a moving garbage collector to move the
- * object. 
- * 
- * Returns: a handle that can be used to access the object from
- * unmanaged code.
- */
-guint32
-mono_gchandle_new (MonoObject *obj, gboolean pinned)
-{
-       return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
-}
-
-/**
- * mono_gchandle_new_weakref:
- * @obj: managed object to get a handle for
- * @pinned: whether the object should be pinned
- *
- * This returns a weak handle that wraps the object, this is used to
- * keep a reference to a managed object from the unmanaged world.
- * Unlike the mono_gchandle_new the object can be reclaimed by the
- * garbage collector.  In this case the value of the GCHandle will be
- * set to zero.
- * 
- * If @pinned is false the address of the object can not be obtained, if it is
- * true the address of the object can be obtained.  This will also pin the
- * object so it will not be possible by a moving garbage collector to move the
- * object. 
- * 
- * Returns: a handle that can be used to access the object from
- * unmanaged code.
- */
-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);
-
-       return handle;
-}
-
-static HandleType
-mono_gchandle_get_type (guint32 gchandle)
-{
-       guint type = (gchandle & 7) - 1;
-
-       return type;
-}
-
-/**
- * mono_gchandle_get_target:
- * @gchandle: a GCHandle's handle.
- *
- * The handle was previously created by calling mono_gchandle_new or
- * mono_gchandle_new_weakref. 
- *
- * Returns a pointer to the MonoObject represented by the handle or
- * NULL for a collected object if using a weakref handle.
- */
-MonoObject*
-mono_gchandle_get_target (guint32 gchandle)
-{
-       guint slot = gchandle >> 3;
-       guint type = (gchandle & 7) - 1;
-       HandleData *handles = &gc_handles [type];
-       MonoObject *obj = NULL;
-       if (type > 3)
-               return NULL;
-       lock_handles (handles);
-       if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
-               if (handles->type <= HANDLE_WEAK_TRACK) {
-                       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;
-}
-
-static void
-mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
-{
-       guint slot = gchandle >> 3;
-       guint type = (gchandle & 7) - 1;
-       HandleData *handles = &gc_handles [type];
-
-       if (type > 3)
-               return;
-       lock_handles (handles);
-       if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
-               if (handles->type <= HANDLE_WEAK_TRACK) {
-                       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? */
-       }
-       /*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);
-}
-
-/**
- * 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.
- */
-gboolean
-mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
-{
-       guint slot = gchandle >> 3;
-       guint type = (gchandle & 7) - 1;
-       HandleData *handles = &gc_handles [type];
-       gboolean result = FALSE;
-       if (type > 3)
-               return FALSE;
-       lock_handles (handles);
-       if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
-               if (handles->type <= HANDLE_WEAK_TRACK) {
-                       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;
-}
-
-/**
- * mono_gchandle_free:
- * @gchandle: a GCHandle's handle.
- *
- * Frees the @gchandle handle.  If there are no outstanding
- * references, the garbage collector can reclaim the memory of the
- * object wrapped. 
- */
-void
-mono_gchandle_free (guint32 gchandle)
-{
-       guint slot = gchandle >> 3;
-       guint type = (gchandle & 7) - 1;
-       HandleData *handles = &gc_handles [type];
-       if (type > 3)
-               return;
-
-       lock_handles (handles);
-       if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) {
-               if (handles->type <= HANDLE_WEAK_TRACK) {
-                       if (handles->entries [slot])
-                               mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
-               } else {
-                       handles->entries [slot] = NULL;
-               }
-               handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
-       } 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
- *
- * 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)
-{
-       guint type;
-
-       for (type = 0; type < 3; ++type) {
-               guint slot;
-               HandleData *handles = &gc_handles [type];
-               lock_handles (handles);
-               for (slot = 0; slot < handles->size; ++slot) {
-                       if (!(handles->bitmap [slot / 32] & (1 << (slot % 32))))
-                               continue;
-                       if (type <= HANDLE_WEAK_TRACK) {
-                               if (domain->domain_id == handles->domain_ids [slot]) {
-                                       handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
-                                       if (handles->entries [slot])
-                                               mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
-                               }
-                       } else {
-                               if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
-                                       handles->bitmap [slot / 32] &= ~(1 << (slot % 32));
-                                       handles->entries [slot] = NULL;
-                               }
-                       }
-               }
-               unlock_handles (handles);
-       }
-
-}
-
 MonoBoolean
 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
 {
@@ -1174,15 +784,11 @@ mono_gc_init_finalizer_thread (void)
 void
 mono_gc_init (void)
 {
-       mono_mutex_init_recursive (&handle_section);
        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);
@@ -1290,17 +896,6 @@ mono_gc_cleanup (void)
        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)
-{
-       mono_mutex_destroy (&handle_section);
-}
-
 gboolean
 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
 {
@@ -1377,13 +972,8 @@ reference_queue_proccess (MonoReferenceQueue *queue)
        RefQueueEntry **iter = &queue->queue;
        RefQueueEntry *entry;
        while ((entry = *iter)) {
-#ifdef HAVE_SGEN_GC
-               if (queue->should_be_deleted || !mono_gc_weak_link_get (&entry->dis_link)) {
-                       mono_gc_weak_link_remove (&entry->dis_link, TRUE);
-#else
                if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
                        mono_gchandle_free ((guint32)entry->gchandle);
-#endif
                        ref_list_remove_element (iter, entry);
                        queue->callback (entry->user_data);
                        g_free (entry);
@@ -1438,11 +1028,7 @@ reference_queue_clear_for_domain (MonoDomain *domain)
                RefQueueEntry *entry;
                while ((entry = *iter)) {
                        if (entry->domain == domain) {
-#ifdef HAVE_SGEN_GC
-                               mono_gc_weak_link_remove (&entry->dis_link, TRUE);
-#else
                                mono_gchandle_free ((guint32)entry->gchandle);
-#endif
                                ref_list_remove_element (iter, entry);
                                queue->callback (entry->user_data);
                                g_free (entry);
@@ -1506,12 +1092,8 @@ mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *u
        entry->user_data = user_data;
        entry->domain = mono_object_domain (obj);
 
-#ifdef HAVE_SGEN_GC
-       mono_gc_weak_link_add (&entry->dis_link, obj, TRUE);
-#else
        entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
        mono_object_register_finalizer (obj);
-#endif
 
        ref_list_push (&queue->queue, entry);
        return TRUE;
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 0d9001be1a05514840d263c456affd595d3765e7..151c1d382c91033269361ce8ff1fcdbb2da617f2 100644 (file)
@@ -331,7 +331,7 @@ mono_locks_dump (gboolean include_untaken)
                                        to_recycle++;
                        } else {
                                if (!monitor_is_on_freelist (mon->data)) {
-                                       MonoObject *holder = mono_gc_weak_link_get (&mon->data);
+                                       MonoObject *holder = (MonoObject *)mono_gchandle_get_target ((guint32)mon->data);
                                        if (mon_status_get_owner (mon->status)) {
                                                g_print ("Lock %p in object %p held by thread %d, nest level: %d\n",
                                                        mon, holder, mon_status_get_owner (mon->status), mon->nest);
@@ -387,7 +387,7 @@ mon_new (gsize id)
                new = NULL;
                for (marray = monitor_allocated; marray; marray = marray->next) {
                        for (i = 0; i < marray->num_monitors; ++i) {
-                               if (marray->monitors [i].data == NULL) {
+                               if (mono_gchandle_get_target ((guint32)marray->monitors [i].data) == NULL) {
                                        new = &marray->monitors [i];
                                        if (new->wait_list) {
                                                /* Orphaned events left by aborted threads */
@@ -397,7 +397,7 @@ mon_new (gsize id)
                                                        new->wait_list = g_slist_remove (new->wait_list, new->wait_list->data);
                                                }
                                        }
-                                       mono_gc_weak_link_remove (&new->data, TRUE);
+                                       mono_gchandle_free ((guint32)new->data);
                                        new->data = monitor_freelist;
                                        monitor_freelist = new;
                                }
@@ -454,7 +454,7 @@ alloc_mon (MonoObject *obj, gint32 id)
 
        mono_monitor_allocator_lock ();
        mon = mon_new (id);
-       mono_gc_weak_link_add (&mon->data, obj, TRUE);
+       mon->data = (void *)(size_t)mono_gchandle_new_weakref (obj, TRUE);
        mono_monitor_allocator_unlock ();
 
        return mon;
@@ -465,7 +465,7 @@ static void
 discard_mon (MonoThreadsSync *mon)
 {
        mono_monitor_allocator_lock ();
-       mono_gc_weak_link_remove (&mon->data, TRUE);
+       mono_gchandle_free ((guint32)mon->data);
        mon_finalize (mon);
        mono_monitor_allocator_unlock ();
 }
@@ -1036,8 +1036,8 @@ mono_monitor_exit (MonoObject *obj)
                mono_monitor_exit_flat (obj, lw);
 }
 
-void**
-mono_monitor_get_object_monitor_weak_link (MonoObject *object)
+guint32
+mono_monitor_get_object_monitor_gchandle (MonoObject *object)
 {
        LockWord lw;
 
@@ -1045,10 +1045,9 @@ mono_monitor_get_object_monitor_weak_link (MonoObject *object)
 
        if (lock_word_is_inflated (lw)) {
                MonoThreadsSync *mon = lock_word_get_inflated_lock (lw);
-               if (mon->data)
-                       return &mon->data;
+               return (guint32)mon->data;
        }
-       return NULL;
+       return 0;
 }
 
 /*
index cc00f2044dc08d8638a7e1b0dfe4b4505ed18db1..bd796254034eac6f4fee232c63554231442536c3 100644 (file)
@@ -106,7 +106,7 @@ MONO_API void mono_locks_dump (gboolean include_untaken);
 void mono_monitor_init (void);
 void mono_monitor_cleanup (void);
 
-void** mono_monitor_get_object_monitor_weak_link (MonoObject *object);
+guint32 mono_monitor_get_object_monitor_gchandle (MonoObject *object);
 
 void mono_monitor_threads_sync_members_offset (int *status_offset, int *nest_offset);
 #define MONO_THREADS_SYNC_MEMBER_OFFSET(o)     ((o)>>8)
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 47f4f94db5c2fd6c7faaaf703771972a1415fc76..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)
@@ -179,9 +179,12 @@ null_weak_links_to_dead_objects (SgenBridgeProcessor *processor, int generation)
        }
 
        /* Null weak links to dead objects. */
-       sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY);
-       if (generation == GENERATION_OLD)
-               sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD);
+       sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, FALSE);
+       sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, TRUE);
+       if (generation == GENERATION_OLD) {
+               sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, FALSE);
+               sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, TRUE);
+       }
 
        sgen_hash_table_clean (&alive_hash);
 }
index ede6e48ba408d971d305f1b682051554faea5561..e986ae0c2ed5ba451d3c3b34204e27ae687405fa 100644 (file)
@@ -571,7 +571,12 @@ sgen_client_binary_protocol_cleanup (gpointer ptr, gpointer vtable, size_t size)
 }
 
 static void G_GNUC_UNUSED
-sgen_client_binary_protocol_dislink_update (gpointer link, gpointer obj, gboolean track, gboolean staged)
+sgen_client_binary_protocol_dislink_add (gpointer link, gpointer obj, gboolean track)
+{
+}
+
+static void G_GNUC_UNUSED
+sgen_client_binary_protocol_dislink_update (gpointer link, gpointer obj, gboolean track)
 {
 #ifdef ENABLE_DTRACE
        if (MONO_GC_WEAK_UPDATE_ENABLED ()) {
@@ -587,12 +592,7 @@ sgen_client_binary_protocol_dislink_update (gpointer link, gpointer obj, gboolea
 }
 
 static void G_GNUC_UNUSED
-sgen_client_binary_protocol_dislink_update_staged (gpointer link, gpointer obj, gboolean track, int index)
-{
-}
-
-static void G_GNUC_UNUSED
-sgen_client_binary_protocol_dislink_process_staged (gpointer link, gpointer obj, int index)
+sgen_client_binary_protocol_dislink_remove (gpointer link, gboolean track)
 {
 }
 
@@ -706,5 +706,6 @@ gboolean sgen_is_managed_allocator (MonoMethod *method);
 gboolean sgen_has_managed_allocator (void);
 
 void sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type);
+void sgen_null_links_for_domain (MonoDomain *domain);
 
 #endif
index 4e388d1557a28e2e0be4fb58ba73089c4d660627..1b9b7ca17bad409ba948b513a25d8a6ab1376765 100644 (file)
@@ -784,9 +784,9 @@ clear_domain_process_object (GCObject *obj, MonoDomain *domain)
        remove = need_remove_object_for_domain (obj, domain);
 
        if (remove && obj->synchronisation) {
-               void **dislink = mono_monitor_get_object_monitor_weak_link (obj);
+               guint32 dislink = mono_monitor_get_object_monitor_gchandle (obj);
                if (dislink)
-                       sgen_register_disappearing_link (NULL, dislink, FALSE, TRUE);
+                       mono_gchandle_free (dislink);
        }
 
        return remove;
@@ -849,7 +849,6 @@ mono_gc_clear_domain (MonoDomain * domain)
        major_collector.finish_sweeping ();
 
        sgen_process_fin_stage_entries ();
-       sgen_process_dislink_stage_entries ();
 
        sgen_clear_nursery_fragments ();
 
@@ -862,9 +861,7 @@ mono_gc_clear_domain (MonoDomain * domain)
        /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
        to memory returned to the OS.*/
        null_ephemerons_for_domain (domain);
-
-       for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
-               sgen_null_links_if (object_in_domain_predicate, domain, i);
+       sgen_null_links_for_domain (domain);
 
        for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
                sgen_remove_finalizers_if (object_in_domain_predicate, domain, i);
@@ -2569,21 +2566,137 @@ 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);
+}
+
+void
+mono_gc_weak_link_unregister (volatile gpointer *link_addr, gboolean track)
 {
-       sgen_register_disappearing_link (obj, link_addr, track, FALSE);
+       binary_protocol_dislink_remove ((gpointer)link_addr, track);
 }
 
 void
-mono_gc_weak_link_remove (void **link_addr, gboolean track)
+mono_gc_ensure_weak_links_accessible (void)
+{
+       /*
+        * 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 ();
+}
+
+gpointer
+sgen_client_default_metadata (void)
+{
+       return mono_domain_get ();
+}
+
+gpointer
+sgen_client_metadata_for_object (GCObject *obj)
 {
-       sgen_register_disappearing_link (NULL, link_addr, track, FALSE);
+       return mono_object_domain (obj);
 }
 
-MonoObject*
-mono_gc_weak_link_get (void **link_addr)
+/**
+ * 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.
+ */
+gboolean
+mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
 {
-       return sgen_weak_link_get (link_addr);
+       MonoDomain *gchandle_domain = sgen_gchandle_get_metadata (gchandle);
+       return domain->domain_id == gchandle_domain->domain_id;
+}
+
+/**
+ * mono_gchandle_free_domain:
+ * @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 *unloading)
+{
+}
+
+static gpointer
+null_link_if_in_domain (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user)
+{
+       MonoDomain *unloading_domain = user;
+       MonoDomain *obj_domain;
+       gboolean is_weak = MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type);
+       if (MONO_GC_HANDLE_IS_OBJECT_POINTER (hidden)) {
+               MonoObject *obj = MONO_GC_REVEAL_POINTER (hidden, is_weak);
+               obj_domain = mono_object_domain (obj);
+       } else {
+               obj_domain = MONO_GC_REVEAL_POINTER (hidden, is_weak);
+       }
+       if (unloading_domain->domain_id == obj_domain->domain_id)
+               return NULL;
+       return hidden;
+}
+
+void
+sgen_null_links_for_domain (MonoDomain *domain)
+{
+       guint type;
+       for (type = HANDLE_TYPE_MIN; type < HANDLE_TYPE_MAX; ++type)
+               sgen_gchandle_iterate (type, GENERATION_OLD, null_link_if_in_domain, domain);
+}
+
+void
+mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
+{
+       sgen_gchandle_set_target (gchandle, obj);
+}
+
+void
+sgen_client_gchandle_created (int handle_type, GCObject *obj, guint32 handle)
+{
+#ifndef DISABLE_PERFCOUNTERS
+       mono_perfcounters->gc_num_handles++;
+#endif
+       mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handle_type, handle, obj);
+}
+
+void
+sgen_client_gchandle_destroyed (int handle_type, guint32 handle)
+{
+#ifndef DISABLE_PERFCOUNTERS
+       mono_perfcounters->gc_num_handles--;
+#endif
+       mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handle_type, handle, NULL);
+}
+
+void
+sgen_client_ensure_weak_gchandles_accessible (void)
+{
+       /*
+        * 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.
+        */
+       if (G_UNLIKELY (bridge_processing_in_progress))
+               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 32673bfaf07c5ca2efe61fba17f07b4e0b2b093d..7816c06d9f962e2d37b8b94d3281168726b2e31f 100644 (file)
@@ -32,6 +32,7 @@ monosgen_sources = \
        sgen-fin-weak-hash.c \
        sgen-gc.c \
        sgen-gc.h \
+       sgen-gchandles.c \
        sgen-gray.c \
        sgen-gray.h \
        sgen-hash-table.c \
index b7e4b6708c0681b3dcad2d0d611912c9e40d252c..e3c695119e40f7ac64972c2ae00b321802e3e7ab 100644 (file)
 #include "mono/sgen/sgen-conf.h"
 #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))
+
+#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_METADATA_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_METADATA_POINTER(slot) (MONO_GC_HANDLE_TAG (slot) == MONO_GC_HANDLE_OCCUPIED_MASK)
+
+typedef enum {
+       HANDLE_TYPE_MIN = 0,
+       HANDLE_WEAK = HANDLE_TYPE_MIN,
+       HANDLE_WEAK_TRACK,
+       HANDLE_NORMAL,
+       HANDLE_PINNED,
+       HANDLE_TYPE_MAX
+} GCHandleType;
+
+#define GC_HANDLE_TYPE_IS_WEAK(x) ((x) <= HANDLE_WEAK_TRACK)
+
+#define MONO_GC_HANDLE_TYPE_SHIFT (3)
+#define MONO_GC_HANDLE_TYPE_MASK ((1 << MONO_GC_HANDLE_TYPE_SHIFT) - 1)
+#define MONO_GC_HANDLE_TYPE(x) (((x) & MONO_GC_HANDLE_TYPE_MASK) - 1)
+#define MONO_GC_HANDLE_SLOT(x) ((x) >> MONO_GC_HANDLE_TYPE_SHIFT)
+#define MONO_GC_HANDLE_TYPE_IS_WEAK(x) ((x) <= HANDLE_WEAK_TRACK)
+#define MONO_GC_HANDLE(slot, type) (((slot) << MONO_GC_HANDLE_TYPE_SHIFT) | (((type) & MONO_GC_HANDLE_TYPE_MASK) + 1))
+
 typedef struct {
        guint minor_gc_count;
        guint major_gc_count;
index 15ff659f715e42ff5b6070425188447d9d500c95..5c803b50ae59a397cd1314702b19f3895091ea79 100644 (file)
@@ -87,6 +87,19 @@ gboolean sgen_client_mark_ephemerons (ScanCopyContext ctx);
  */
 void sgen_client_clear_unreachable_ephemerons (ScanCopyContext ctx);
 
+/*
+ * May return NULL.  Must be an aligned pointer.
+ */
+gpointer sgen_client_default_metadata (void);
+gpointer sgen_client_metadata_for_object (GCObject *obj);
+
+/*
+ * No action required.
+ */
+void sgen_client_gchandle_created (int handle_type, GCObject *obj, guint32 handle);
+void sgen_client_gchandle_destroyed (int handle_type, guint32 handle);
+void sgen_client_ensure_weak_gchandles_accessible (void);
+
 /*
  * This is called for objects that are larger than one card.  If it's possible to scan only
  * parts of the object based on which cards are marked, do so and return TRUE.  Otherwise,
index a0994b9a1be1c9d03f2fd0285c0713ab514f4e41..9641594483eec65cc456942dfaf37e235916ea16 100644 (file)
@@ -32,6 +32,7 @@
 #include "mono/sgen/sgen-protocol.h"
 #include "mono/sgen/sgen-pointer-queue.h"
 #include "mono/sgen/sgen-client.h"
+#include "mono/sgen/gc-internal-agnostic.h"
 #include "mono/utils/mono-membar.h"
 
 #define ptr_in_nursery sgen_ptr_in_nursery
@@ -40,9 +41,6 @@ typedef SgenGrayQueue GrayQueue;
 
 static int no_finalize = 0;
 
-#define DISLINK_OBJECT(l)      (REVEAL_POINTER (*(void**)(l)))
-#define DISLINK_TRACK(l)       ((~(size_t)(*(void**)(l))) & 1)
-
 /*
  * The finalizable hash has the object as the key, the 
  * disappearing_link hash, has the link address as key.
@@ -629,149 +627,6 @@ sgen_gather_finalizers_if (SgenObjectPredicateFunc predicate, void *user_data, G
        return result;
 }
 
-static SgenHashTable minor_disappearing_link_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_DISLINK_TABLE, INTERNAL_MEM_DISLINK, 0, sgen_aligned_addr_hash, NULL);
-static SgenHashTable major_disappearing_link_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_DISLINK_TABLE, INTERNAL_MEM_DISLINK, 0, sgen_aligned_addr_hash, NULL);
-
-static SgenHashTable*
-get_dislink_hash_table (int generation)
-{
-       switch (generation) {
-       case GENERATION_NURSERY: return &minor_disappearing_link_hash;
-       case GENERATION_OLD: return &major_disappearing_link_hash;
-       default: g_assert_not_reached ();
-       }
-}
-
-/* LOCKING: assumes the GC lock is held */
-static void
-add_or_remove_disappearing_link (GCObject *obj, void **link, int generation)
-{
-       SgenHashTable *hash_table = get_dislink_hash_table (generation);
-
-       if (!obj) {
-               if (sgen_hash_table_remove (hash_table, link, NULL)) {
-                       SGEN_LOG (5, "Removed dislink %p (%d) from %s table",
-                                       link, hash_table->num_entries, sgen_generation_name (generation));
-               }
-               return;
-       }
-
-       sgen_hash_table_replace (hash_table, link, NULL, NULL);
-       SGEN_LOG (5, "Added dislink for object: %p (%s) at %p to %s table",
-                       obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE_UNCHECKED (obj)), link, sgen_generation_name (generation));
-}
-
-/* LOCKING: requires that the GC lock is held */
-void
-sgen_null_link_in_range (int generation, gboolean before_finalization, ScanCopyContext ctx)
-{
-       CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
-       GrayQueue *queue = ctx.queue;
-       void **link;
-       gpointer dummy G_GNUC_UNUSED;
-       SgenHashTable *hash = get_dislink_hash_table (generation);
-
-       SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
-               GCObject *object;
-               gboolean track;
-
-               /*
-               We null a weak link before unregistering it, so it's possible that a thread is
-               suspended right in between setting the content to null and staging the unregister.
-
-               The rest of this code cannot handle null links as DISLINK_OBJECT (NULL) produces an invalid address.
-
-               We should simply skip the entry as the staged removal will take place during the next GC.
-               */
-               if (!*link) {
-                       SGEN_LOG (5, "Dislink %p was externally nullified", link);
-                       continue;
-               }
-
-               track = DISLINK_TRACK (link);
-               /*
-                * Tracked references are processed after
-                * finalization handling whereas standard weak
-                * references are processed before.  If an
-                * object is still not marked after finalization
-                * handling it means that it either doesn't have
-                * a finalizer or the finalizer has already run,
-                * so we must null a tracking reference.
-                */
-               if (track != before_finalization) {
-                       object = DISLINK_OBJECT (link);
-                       /*
-                       We should guard against a null object been hidden. This can sometimes happen.
-                       */
-                       if (!object) {
-                               SGEN_LOG (5, "Dislink %p with a hidden null object", link);
-                               continue;
-                       }
-
-                       if (!major_collector.is_object_live (object)) {
-                               if (sgen_gc_is_object_ready_for_finalization (object)) {
-                                       *link = NULL;
-                                       binary_protocol_dislink_update (link, NULL, 0, 0);
-                                       SGEN_LOG (5, "Dislink nullified at %p to GCed object %p", link, object);
-                                       SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
-                                       continue;
-                               } else {
-                                       GCObject *copy = object;
-                                       copy_func (&copy, queue);
-
-                                       /* Update pointer if it's moved.  If the object
-                                        * has been moved out of the nursery, we need to
-                                        * remove the link from the minor hash table to
-                                        * the major one.
-                                        *
-                                        * FIXME: what if an object is moved earlier?
-                                        */
-
-                                       if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
-                                               SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
-
-                                               g_assert (copy);
-                                               *link = HIDE_POINTER (copy, track);
-                                               add_or_remove_disappearing_link (copy, link, GENERATION_OLD);
-                                               binary_protocol_dislink_update (link, copy, track, 0);
-
-                                               SGEN_LOG (5, "Upgraded dislink at %p to major because object %p moved to %p", link, object, copy);
-
-                                               continue;
-                                       } else {
-                                               *link = HIDE_POINTER (copy, track);
-                                               binary_protocol_dislink_update (link, copy, track, 0);
-                                               SGEN_LOG (5, "Updated dislink at %p to %p", link, DISLINK_OBJECT (link));
-                                       }
-                               }
-                       }
-               }
-       } SGEN_HASH_TABLE_FOREACH_END;
-}
-
-/* LOCKING: requires that the GC lock is held */
-void
-sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation)
-{
-       void **link;
-       gpointer dummy G_GNUC_UNUSED;
-       SgenHashTable *hash = get_dislink_hash_table (generation);
-       SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
-               char *object = DISLINK_OBJECT (link);
-
-               if (!*link)
-                       continue;
-
-               if (predicate ((GCObject*)object, data)) {
-                       *link = NULL;
-                       binary_protocol_dislink_update (link, NULL, 0, 0);
-                       SGEN_LOG (5, "Dislink nullified by predicate at %p to GCed object %p", link, object);
-                       SGEN_HASH_TABLE_FOREACH_REMOVE (FALSE /* TRUE */);
-                       continue;
-               }
-       } SGEN_HASH_TABLE_FOREACH_END;
-}
-
 void
 sgen_remove_finalizers_if (SgenObjectPredicateFunc predicate, void *user_data, int generation)
 {
@@ -789,72 +644,6 @@ sgen_remove_finalizers_if (SgenObjectPredicateFunc predicate, void *user_data, i
        } SGEN_HASH_TABLE_FOREACH_END;  
 }
 
-/* LOCKING: requires that the GC lock is held */
-static void
-process_dislink_stage_entry (GCObject *obj, void *_link, int index)
-{
-       void **link = _link;
-
-       if (index >= 0)
-               binary_protocol_dislink_process_staged (link, obj, index);
-
-       add_or_remove_disappearing_link (NULL, link, GENERATION_NURSERY);
-       add_or_remove_disappearing_link (NULL, link, GENERATION_OLD);
-       if (obj) {
-               if (ptr_in_nursery (obj))
-                       add_or_remove_disappearing_link (obj, link, GENERATION_NURSERY);
-               else
-                       add_or_remove_disappearing_link (obj, link, GENERATION_OLD);
-       }
-}
-
-#define NUM_DISLINK_STAGE_ENTRIES      1024
-
-static volatile gint32 next_dislink_stage_entry = 0;
-static StageEntry dislink_stage_entries [NUM_DISLINK_STAGE_ENTRIES];
-
-/* LOCKING: requires that the GC lock is held */
-void
-sgen_process_dislink_stage_entries (void)
-{
-       lock_stage_for_processing (&next_dislink_stage_entry);
-       process_stage_entries (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, process_dislink_stage_entry);
-}
-
-void
-sgen_register_disappearing_link (GCObject *obj, void **link, gboolean track, gboolean in_gc)
-{
-       if (obj)
-               *link = HIDE_POINTER (obj, track);
-       else
-               *link = NULL;
-
-#if 1
-       if (in_gc) {
-               binary_protocol_dislink_update (link, obj, track, 0);
-               process_dislink_stage_entry (obj, link, -1);
-       } else {
-               int index;
-               binary_protocol_dislink_update (link, obj, track, 1);
-               while ((index = add_stage_entry (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, obj, link)) == -1) {
-                       if (try_lock_stage_for_processing (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry)) {
-                               LOCK_GC;
-                               process_stage_entries (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, process_dislink_stage_entry);
-                               UNLOCK_GC;
-                       }
-               }
-               binary_protocol_dislink_update_staged (link, obj, track, index);
-       }
-#else
-       if (!in_gc)
-               LOCK_GC;
-       binary_protocol_dislink_update (link, obj, track, 0);
-       process_dislink_stage_entry (obj, link, -1);
-       if (!in_gc)
-               UNLOCK_GC;
-#endif
-}
-
 void
 sgen_init_fin_weak_hash (void)
 {
index 1a6d5195e9ad8d2e1c46baf083d74e9e8e8be824..b3ee2ca50129ca82dc98efe8f5d0fe460fd11cec 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);
+       sgen_null_link_in_range (generation, ctx, FALSE);
        if (generation == GENERATION_OLD)
-               sgen_null_link_in_range (GENERATION_NURSERY, TRUE, ctx);
+               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);
+               sgen_null_link_in_range (generation, ctx, TRUE);
                if (generation == GENERATION_OLD)
-                       sgen_null_link_in_range (GENERATION_NURSERY, FALSE, ctx);
+                       sgen_null_link_in_range (GENERATION_NURSERY, ctx, TRUE);
                if (sgen_gray_object_queue_is_empty (queue))
                        break;
                sgen_drain_gray_stack (-1, ctx);
@@ -1537,7 +1537,6 @@ collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
                sgen_check_consistency ();
 
        sgen_process_fin_stage_entries ();
-       sgen_process_dislink_stage_entries ();
 
        /* pin from pinned handles */
        sgen_init_pinning ();
@@ -1719,7 +1718,6 @@ major_copy_or_mark_from_roots (size_t *old_next_pin_slot, CopyOrMarkFromRootsMod
        }
 
        sgen_process_fin_stage_entries ();
-       sgen_process_dislink_stage_entries ();
 
        TV_GETTIME (atv);
        sgen_init_pinning ();
@@ -2746,48 +2744,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)
 {
@@ -2899,6 +2855,7 @@ sgen_gc_init (void)
        sgen_init_descriptors ();
        sgen_init_gray_queues ();
        sgen_init_allocator ();
+       sgen_init_gchandles ();
 
        sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
        sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
@@ -3238,6 +3195,8 @@ sgen_gc_init (void)
 
        sgen_card_table_init (&remset);
 
+       sgen_register_root (NULL, 0, sgen_make_user_root_descriptor (sgen_mark_normal_gc_handles), ROOT_TYPE_NORMAL, MONO_ROOT_SOURCE_GC_HANDLE, "normal gc handles");
+
        gc_initialized = 1;
 }
 
index 5594db16c7fe25f566c6f76037a29ef6af6b975d..4cb4edf8bafe8ed5956e2a40c4f4cd72d332fad9 100644 (file)
@@ -43,6 +43,7 @@ typedef struct _SgenThreadInfo SgenThreadInfo;
 #include "mono/sgen/sgen-conf.h"
 #include "mono/sgen/sgen-hash-table.h"
 #include "mono/sgen/sgen-protocol.h"
+#include "mono/sgen/gc-internal-agnostic.h"
 
 /* The method used to clear the nursery */
 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
@@ -172,14 +173,6 @@ sgen_aligned_addr_hash (gconstpointer ptr)
        return GPOINTER_TO_UINT (ptr) >> 3;
 }
 
-/*
- * The link pointer is hidden by negating each bit.  We use the lowest
- * bit of the link (before negation) to store whether it needs
- * resurrection tracking.
- */
-#define HIDE_POINTER(p,t)      ((gpointer)(~((size_t)(p)|((t)?1:0))))
-#define REVEAL_POINTER(p)      ((gpointer)((~(size_t)(p))&~3L))
-
 #define SGEN_PTR_IN_NURSERY(p,bits,start,end)  (((mword)(p) & ~((1 << (bits)) - 1)) == (mword)(start))
 
 #ifdef USER_CONFIG
@@ -786,7 +779,7 @@ void sgen_collect_bridge_objects (int generation, ScanCopyContext ctx);
 
 typedef gboolean (*SgenObjectPredicateFunc) (GCObject *obj, void *user_data);
 
-void sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation);
+void sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation, gboolean track);
 
 gboolean sgen_gc_is_object_ready_for_finalization (GCObject *object);
 void sgen_gc_lock (void);
@@ -796,7 +789,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);
+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);
@@ -804,7 +797,6 @@ void sgen_object_register_for_finalization (GCObject *obj, void *user_data);
 int sgen_gather_finalizers_if (SgenObjectPredicateFunc predicate, void *user_data, GCObject **out_array, int out_size);
 void sgen_remove_finalizers_if (SgenObjectPredicateFunc predicate, void *user_data, int generation);
 
-void sgen_process_dislink_stage_entries (void);
 void sgen_register_disappearing_link (GCObject *obj, void **link, gboolean track, gboolean in_gc);
 
 GCObject* sgen_weak_link_get (void **link_addr);
@@ -948,6 +940,19 @@ sgen_is_object_alive_for_current_gen (GCObject *object)
 
 int sgen_gc_invoke_finalizers (void);
 
+/* GC handles */
+
+void sgen_init_gchandles (void);
+
+void sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation, gboolean track);
+
+typedef gpointer (*SgenGCHandleIterateCallback) (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user);
+
+void sgen_gchandle_iterate (GCHandleType handle_type, int max_generation, SgenGCHandleIterateCallback callback, gpointer user);
+void sgen_gchandle_set_target (guint32 gchandle, GCObject *obj);
+void sgen_mark_normal_gc_handles (void *addr, SgenUserMarkFunc mark_func, void *gc_data);
+gpointer sgen_gchandle_get_metadata (guint32 gchandle);
+
 /* Other globals */
 
 extern GCMemSection *nursery_section;
@@ -1012,23 +1017,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"
@@ -1075,6 +1063,22 @@ gboolean nursery_canaries_enabled (void);
                                                g_warning ("CORRUPT CANARY:\naddr->%p\ntype->%s\nexcepted->'%s'\nfound->'%s'\n", (char*) addr, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE ((addr))), CANARY_STRING, canary_copy); \
                                } }
 
+/*
+ * 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
+}
+
 #endif /* HAVE_SGEN_GC */
 
 #endif /* __MONO_SGENGC_H__ */
diff --git a/mono/sgen/sgen-gchandles.c b/mono/sgen/sgen-gchandles.c
new file mode 100644 (file)
index 0000000..367c2c3
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * sgen-gchandles.c: SGen GC handles.
+ *
+ * Copyright (C) 2015 Xamarin Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License 2.0 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License 2.0 along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "config.h"
+#ifdef HAVE_SGEN_GC
+
+#include "mono/sgen/sgen-gc.h"
+#include "mono/sgen/sgen-client.h"
+#include "mono/utils/mono-membar.h"
+
+#ifdef HEAVY_STATISTICS
+static volatile guint32 stat_gc_handles_allocated = 0;
+static volatile guint32 stat_gc_handles_max_allocated = 0;
+#endif
+
+#define BUCKETS (32 - MONO_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 metadata pointer--this allows us to
+ * retrieve the domain ID of an expired weak reference in Mono.
+ *
+ * Finally, 'slot_hint' denotes the position of the last allocation, so that the
+ * whole array needn't be searched on every allocation.
+ */
+
+typedef struct {
+       volatile gpointer *volatile entries [BUCKETS];
+       volatile guint32 capacity;
+       volatile guint32 slot_hint;
+       volatile guint32 max_index;
+       guint8 type;
+} HandleData;
+
+static inline guint
+bucket_size (guint index)
+{
+       return 1 << (index + MIN_BUCKET_BITS);
+}
+
+/* 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
+bucketize (guint index, guint *bucket, guint *offset)
+{
+       *bucket = index_bucket (index);
+       *offset = index - bucket_size (*bucket) + MIN_BUCKET_SIZE;
+}
+
+static void
+protocol_gchandle_update (int handle_type, gpointer link, gpointer old_value, gpointer new_value)
+{
+       gboolean old = MONO_GC_HANDLE_IS_OBJECT_POINTER (old_value);
+       gboolean new = MONO_GC_HANDLE_IS_OBJECT_POINTER (new_value);
+       gboolean track = handle_type == HANDLE_WEAK_TRACK;
+
+       if (!MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type))
+               return;
+
+       if (!old && new)
+               binary_protocol_dislink_add (link, MONO_GC_REVEAL_POINTER (new_value, TRUE), track);
+       else if (old && !new)
+               binary_protocol_dislink_remove (link, track);
+       else if (old && new && old_value != new_value)
+               binary_protocol_dislink_update (link, MONO_GC_REVEAL_POINTER (new_value, TRUE), track);
+}
+
+/* Returns the new value in the slot, or NULL if the CAS failed. */
+static inline gpointer
+try_set_slot (volatile gpointer *slot, GCObject *obj, gpointer old, GCHandleType type)
+{
+       gpointer new;
+       if (obj)
+               new = MONO_GC_HANDLE_OBJECT_POINTER (obj, GC_HANDLE_TYPE_IS_WEAK (type));
+       else
+               new = MONO_GC_HANDLE_METADATA_POINTER (sgen_client_default_metadata (), GC_HANDLE_TYPE_IS_WEAK (type));
+       SGEN_ASSERT (0, new, "Why is the occupied bit not set?");
+       if (InterlockedCompareExchangePointer (slot, new, old) == old) {
+               protocol_gchandle_update (type, (gpointer)slot, old, new);
+               return new;
+       }
+       return NULL;
+}
+
+/* Try to claim a slot by setting its occupied bit. */
+static inline gboolean
+try_occupy_slot (HandleData *handles, guint bucket, guint offset, GCObject *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) != NULL;
+}
+
+static HandleData gc_handles [] = {
+       { { NULL }, 0, 0, 0, (HANDLE_WEAK) },
+       { { NULL }, 0, 0, 0, (HANDLE_WEAK_TRACK) },
+       { { NULL }, 0, 0, 0, (HANDLE_NORMAL) },
+       { { NULL }, 0, 0, 0, (HANDLE_PINNED) }
+};
+
+static HandleData *
+gc_handles_for_type (GCHandleType type)
+{
+       g_assert (type < HANDLE_TYPE_MAX);
+       return &gc_handles [type];
+}
+
+/* This assumes that the world is stopped. */
+void
+sgen_mark_normal_gc_handles (void *addr, SgenUserMarkFunc mark_func, void *gc_data)
+{
+       HandleData *handles = gc_handles_for_type (HANDLE_NORMAL);
+       size_t bucket, offset;
+       const guint max_bucket = index_bucket (handles->capacity);
+       guint32 index = 0;
+       const guint32 max_index = handles->max_index;
+       for (bucket = 0; bucket < max_bucket; ++bucket) {
+               volatile gpointer *entries = handles->entries [bucket];
+               for (offset = 0; offset < bucket_size (bucket); ++offset, ++index) {
+                       volatile gpointer *entry;
+                       gpointer hidden, revealed;
+                       /* No need to iterate beyond the largest index ever allocated. */
+                       if (index > max_index)
+                               return;
+                       entry = &entries [offset];
+                       hidden = *entry;
+                       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);
+               }
+       }
+}
+
+static guint
+handle_data_find_unset (HandleData *handles, guint32 begin, guint32 end)
+{
+       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;
+}
+
+/* Adds a bucket if necessary and possible. */
+static void
+handle_data_grow (HandleData *handles, guint32 old_capacity)
+{
+       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);
+       if (handles->type == HANDLE_PINNED)
+               sgen_register_root ((char *)entries, new_bucket_size, SGEN_DESCRIPTOR_NULL, ROOT_TYPE_PINNED, MONO_ROOT_SOURCE_GC_HANDLE, "pinned gc handles");
+       /* The zeroing of the newly allocated bucket must be complete before storing
+        * the new bucket pointer.
+        */
+       mono_memory_write_barrier ();
+       if (InterlockedCompareExchangePointer ((volatile gpointer *)&handles->entries [new_bucket], entries, NULL) == NULL) {
+               /* It must not be the case that we succeeded in setting the bucket
+                * pointer, while someone else succeeded in changing the capacity.
+                */
+               if (InterlockedCompareExchange ((volatile gint32 *)&handles->capacity, new_capacity, old_capacity) != old_capacity)
+                       g_assert_not_reached ();
+               handles->slot_hint = old_capacity;
+               return;
+       }
+       /* Someone beat us to the allocation. */
+       if (handles->type == HANDLE_PINNED)
+               sgen_deregister_root ((char *)entries);
+       g_free (entries);
+}
+
+static guint32
+alloc_handle (HandleData *handles, GCObject *obj, gboolean track)
+{
+       guint index;
+       guint32 res;
+       guint bucket, offset;
+       guint32 capacity;
+       guint32 slot_hint;
+       guint32 max_index;
+       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 a GC happens shortly after a new bucket is allocated, the entire
+        * bucket could be scanned even though it's mostly empty. To avoid this, we
+        * track the maximum index seen so far, so that we can skip the empty slots.
+        */
+       do {
+               max_index = handles->max_index;
+               if (index <= max_index)
+                       break;
+       } while (!InterlockedCompareExchange ((volatile gint32 *)&handles->max_index, index, max_index));
+#ifdef HEAVY_STATISTICS
+       InterlockedIncrement ((volatile gint32 *)&stat_gc_handles_allocated);
+       if (stat_gc_handles_allocated > stat_gc_handles_max_allocated)
+               stat_gc_handles_max_allocated = stat_gc_handles_allocated;
+#endif
+       /* Ensure that a GC handle cannot be given to another thread without the slot having been set. */
+       mono_memory_write_barrier ();
+       res = MONO_GC_HANDLE (index, handles->type);
+       sgen_client_gchandle_created (handles->type, obj, res);
+       return res;
+}
+
+static gboolean
+object_older_than (GCObject *object, int generation)
+{
+       return generation == GENERATION_NURSERY && !sgen_ptr_in_nursery (object);
+}
+
+/*
+ * Maps a function over all GC handles.
+ * This assumes that the world is stopped!
+ */
+void
+sgen_gchandle_iterate (GCHandleType handle_type, int max_generation, SgenGCHandleIterateCallback callback, gpointer user)
+{
+       HandleData *handle_data = gc_handles_for_type (handle_type);
+       size_t bucket, offset;
+       guint max_bucket = index_bucket (handle_data->capacity);
+       guint32 index = 0;
+       guint32 max_index = handle_data->max_index;
+       /* 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, ++index) {
+                       gpointer hidden;
+                       gpointer result;
+                       /* Table must contain no garbage pointers. */
+                       gboolean occupied;
+                       /* No need to iterate beyond the largest index ever allocated. */
+                       if (index > max_index)
+                                       return;
+                       hidden = entries [offset];
+                       occupied = MONO_GC_HANDLE_OCCUPIED (hidden);
+                       g_assert (hidden ? occupied : !occupied);
+                       if (!occupied)
+                               continue;
+                       result = callback (hidden, handle_type, max_generation, user);
+                       if (result)
+                               SGEN_ASSERT (0, MONO_GC_HANDLE_OCCUPIED (result), "Why did the callback return an unoccupied entry?");
+                       else
+                               HEAVY_STAT (InterlockedDecrement ((volatile gint32 *)&stat_gc_handles_allocated));
+                       protocol_gchandle_update (handle_type, (gpointer)&entries [offset], hidden, result);
+                       entries [offset] = result;
+               }
+       }
+}
+
+/**
+ * mono_gchandle_new:
+ * @obj: managed object to get a handle for
+ * @pinned: whether the object should be pinned
+ *
+ * This returns a handle that wraps the object, this is used to keep a
+ * reference to a managed object from the unmanaged world and preventing the
+ * object from being disposed.
+ * 
+ * If @pinned is false the address of the object can not be obtained, if it is
+ * true the address of the object can be obtained.  This will also pin the
+ * object so it will not be possible by a moving garbage collector to move the
+ * object. 
+ * 
+ * Returns: a handle that can be used to access the object from
+ * unmanaged code.
+ */
+guint32
+mono_gchandle_new (GCObject *obj, gboolean pinned)
+{
+       return alloc_handle (gc_handles_for_type (pinned ? HANDLE_PINNED : HANDLE_NORMAL), obj, FALSE);
+}
+
+/**
+ * mono_gchandle_new_weakref:
+ * @obj: managed object to get a handle for
+ * @pinned: whether the object should be pinned
+ *
+ * This returns a weak handle that wraps the object, this is used to
+ * keep a reference to a managed object from the unmanaged world.
+ * Unlike the mono_gchandle_new the object can be reclaimed by the
+ * garbage collector.  In this case the value of the GCHandle will be
+ * set to zero.
+ * 
+ * If @pinned is false the address of the object can not be obtained, if it is
+ * true the address of the object can be obtained.  This will also pin the
+ * object so it will not be possible by a moving garbage collector to move the
+ * object. 
+ * 
+ * Returns: a handle that can be used to access the object from
+ * unmanaged code.
+ */
+guint32
+mono_gchandle_new_weakref (GCObject *obj, gboolean track_resurrection)
+{
+       return alloc_handle (gc_handles_for_type (track_resurrection ? HANDLE_WEAK_TRACK : HANDLE_WEAK), obj, track_resurrection);
+}
+
+static GCObject *
+link_get (volatile gpointer *link_addr, gboolean is_weak)
+{
+       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 && MONO_GC_HANDLE_IS_OBJECT_POINTER (ptr))
+               obj = (GCObject *)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.
+        */
+       sgen_dummy_use (obj);
+       mono_memory_barrier ();
+
+       if (is_weak)
+               sgen_client_ensure_weak_gchandles_accessible ();
+
+       if ((void*)*link_addr_volatile != ptr)
+               goto retry;
+
+       return obj;
+}
+
+/**
+ * mono_gchandle_get_target:
+ * @gchandle: a GCHandle's handle.
+ *
+ * The handle was previously created by calling mono_gchandle_new or
+ * mono_gchandle_new_weakref. 
+ *
+ * Returns a pointer to the MonoObject represented by the handle or
+ * NULL for a collected object if using a weakref handle.
+ */
+GCObject*
+mono_gchandle_get_target (guint32 gchandle)
+{
+       guint index = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       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], MONO_GC_HANDLE_TYPE_IS_WEAK (type));
+}
+
+void
+sgen_gchandle_set_target (guint32 gchandle, GCObject *obj)
+{
+       guint index = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       HandleData *handles = gc_handles_for_type (type);
+       guint bucket, offset;
+       gpointer slot;
+
+       g_assert (index < handles->capacity);
+       bucketize (index, &bucket, &offset);
+
+       do {
+               slot = handles->entries [bucket] [offset];
+               SGEN_ASSERT (0, MONO_GC_HANDLE_OCCUPIED (slot), "Why are we setting the target on an unoccupied slot?");
+       } while (!try_set_slot (&handles->entries [bucket] [offset], obj, slot, handles->type));
+}
+
+static gpointer
+mono_gchandle_slot_metadata (volatile gpointer *slot_addr, gboolean is_weak)
+{
+       gpointer slot;
+       gpointer metadata;
+retry:
+       slot = *slot_addr;
+       if (!MONO_GC_HANDLE_OCCUPIED (slot))
+               return NULL;
+       if (MONO_GC_HANDLE_IS_OBJECT_POINTER (slot)) {
+               GCObject *obj = MONO_GC_REVEAL_POINTER (slot, is_weak);
+               /* See note [dummy use]. */
+               sgen_dummy_use (obj);
+               /*
+                * FIXME: The compiler could technically not carry a reference to obj around
+                * at this point and recompute it later, in which case we would still use
+                * it.
+                */
+               if (*slot_addr != slot)
+                       goto retry;
+               return sgen_client_metadata_for_object (obj);
+       }
+       metadata = MONO_GC_REVEAL_POINTER (slot, is_weak);
+       /* See note [dummy use]. */
+       sgen_dummy_use (metadata);
+       if (*slot_addr != slot)
+               goto retry;
+       return metadata;
+}
+
+gpointer
+sgen_gchandle_get_metadata (guint32 gchandle)
+{
+       guint index = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_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_metadata (&handles->entries [bucket] [offset], MONO_GC_HANDLE_TYPE_IS_WEAK (type));
+}
+
+/**
+ * mono_gchandle_free:
+ * @gchandle: a GCHandle's handle.
+ *
+ * Frees the @gchandle handle.  If there are no outstanding
+ * references, the garbage collector can reclaim the memory of the
+ * object wrapped. 
+ */
+void
+mono_gchandle_free (guint32 gchandle)
+{
+       guint index = MONO_GC_HANDLE_SLOT (gchandle);
+       guint type = MONO_GC_HANDLE_TYPE (gchandle);
+       HandleData *handles = gc_handles_for_type (type);
+       guint bucket, offset;
+       gpointer slot;
+       bucketize (index, &bucket, &offset);
+       slot = handles->entries [bucket] [offset];
+       if (index < handles->capacity && MONO_GC_HANDLE_OCCUPIED (slot)) {
+               handles->entries [bucket] [offset] = NULL;
+               protocol_gchandle_update (handles->type, (gpointer)&handles->entries [bucket] [offset], slot, NULL);
+               HEAVY_STAT (InterlockedDecrement ((volatile gint32 *)&stat_gc_handles_allocated));
+       } else {
+               /* print a warning? */
+       }
+       sgen_client_gchandle_destroyed (handles->type, gchandle);
+}
+
+/*
+ * Returns whether to remove the link from its hash.
+ */
+static gpointer
+null_link_if_necessary (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user)
+{
+       const gboolean is_weak = GC_HANDLE_TYPE_IS_WEAK (handle_type);
+       ScanCopyContext *ctx = (ScanCopyContext *)user;
+       GCObject *obj;
+       GCObject *copy;
+
+       if (!MONO_GC_HANDLE_VALID (hidden))
+               return hidden;
+
+       obj = MONO_GC_REVEAL_POINTER (hidden, MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type));
+       SGEN_ASSERT (0, obj, "Why is the hidden pointer NULL?");
+
+       if (object_older_than (obj, max_generation))
+               return hidden;
+
+       if (major_collector.is_object_live (obj))
+               return hidden;
+
+       /* 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))
+               return MONO_GC_HANDLE_METADATA_POINTER (sgen_client_metadata_for_object (obj), is_weak);
+
+       copy = obj;
+       ctx->ops->copy_or_mark_object (&copy, ctx->queue);
+       SGEN_ASSERT (0, copy, "Why couldn't we copy the object?");
+       /* 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, ScanCopyContext ctx, gboolean track)
+{
+       sgen_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if_necessary, &ctx);
+}
+
+typedef struct {
+       SgenObjectPredicateFunc predicate;
+       gpointer data;
+} WeakLinkAlivePredicateClosure;
+
+static gpointer
+null_link_if (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user)
+{
+       WeakLinkAlivePredicateClosure *closure = (WeakLinkAlivePredicateClosure *)user;
+       GCObject *obj;
+
+       if (!MONO_GC_HANDLE_VALID (hidden))
+               return hidden;
+
+       obj = MONO_GC_REVEAL_POINTER (hidden, MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type));
+       SGEN_ASSERT (0, obj, "Why is the hidden pointer NULL?");
+
+       if (object_older_than (obj, max_generation))
+               return hidden;
+
+       if (closure->predicate (obj, closure->data))
+               return NULL;
+
+       return hidden;
+}
+
+/* LOCKING: requires that the GC lock is held */
+void
+sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation, gboolean track)
+{
+       WeakLinkAlivePredicateClosure closure = { predicate, data };
+       sgen_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if, &closure);
+}
+
+void
+sgen_init_gchandles (void)
+{
+#ifdef HEAVY_STATISTICS
+       mono_counters_register ("GC handles allocated", MONO_COUNTER_GC | MONO_COUNTER_UINT, (void *)&stat_gc_handles_allocated);
+       mono_counters_register ("max GC handles allocated", MONO_COUNTER_GC | MONO_COUNTER_UINT, (void *)&stat_gc_handles_max_allocated);
+#endif
+}
+
+#endif
index ca1e053e64ba01fef649be738e5875dc384f8320..9f8f9bcd6eb9e82cf87c94235d7d3732dd6e6150 100644 (file)
@@ -307,24 +307,24 @@ MATCH_INDEX (BINARY_PROTOCOL_MATCH)
 IS_VTABLE_MATCH (FALSE)
 END_PROTOCOL_ENTRY
 
-BEGIN_PROTOCOL_ENTRY_HEAVY4 (binary_protocol_dislink_update, TYPE_POINTER, link, TYPE_POINTER, obj, TYPE_BOOL, track, TYPE_BOOL, staged)
-CUSTOM_PRINT(entry->obj ? printf ("link %p obj %p staged %d track %d", entry->link, entry->obj, entry->staged, entry->track) : printf ("link %p obj %p staged %d", entry->link, entry->obj, entry->staged))
+BEGIN_PROTOCOL_ENTRY_HEAVY3 (binary_protocol_dislink_add, TYPE_POINTER, link, TYPE_POINTER, obj, TYPE_BOOL, track)
+DEFAULT_PRINT ()
 IS_ALWAYS_MATCH (FALSE)
 MATCH_INDEX (ptr == entry->link ? 0 : ptr == entry->obj ? 1 : BINARY_PROTOCOL_NO_MATCH)
 IS_VTABLE_MATCH (FALSE)
 END_PROTOCOL_ENTRY_HEAVY
 
-BEGIN_PROTOCOL_ENTRY_HEAVY4 (binary_protocol_dislink_update_staged, TYPE_POINTER, link, TYPE_POINTER, obj, TYPE_BOOL, track, TYPE_INT, index)
-CUSTOM_PRINT(entry->obj ? printf ("link %p obj %p index %d track %d", entry->link, entry->obj, entry->index, entry->track) : printf ("link %p obj %p index %d", entry->link, entry->obj, entry->index))
+BEGIN_PROTOCOL_ENTRY_HEAVY3 (binary_protocol_dislink_update, TYPE_POINTER, link, TYPE_POINTER, obj, TYPE_BOOL, track)
+CUSTOM_PRINT(entry->obj ? printf ("link %p obj %p track %d", entry->link, entry->obj, entry->track) : printf ("link %p obj %p", entry->link, entry->obj))
 IS_ALWAYS_MATCH (FALSE)
 MATCH_INDEX (ptr == entry->link ? 0 : ptr == entry->obj ? 1 : BINARY_PROTOCOL_NO_MATCH)
 IS_VTABLE_MATCH (FALSE)
 END_PROTOCOL_ENTRY_HEAVY
 
-BEGIN_PROTOCOL_ENTRY_HEAVY3 (binary_protocol_dislink_process_staged, TYPE_POINTER, link, TYPE_POINTER, obj, TYPE_INT, index)
+BEGIN_PROTOCOL_ENTRY_HEAVY2 (binary_protocol_dislink_remove, TYPE_POINTER, link, TYPE_BOOL, track)
 DEFAULT_PRINT ()
 IS_ALWAYS_MATCH (FALSE)
-MATCH_INDEX (ptr == entry->link ? 0 : ptr == entry->obj ? 1 : BINARY_PROTOCOL_NO_MATCH)
+MATCH_INDEX (ptr == entry->link ? 0 : BINARY_PROTOCOL_NO_MATCH)
 IS_VTABLE_MATCH (FALSE)
 END_PROTOCOL_ENTRY_HEAVY