#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)
{
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;
}
#endif
}
-void
+static void
mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
{
/* libgc requires that we use HIDE_POINTER... */
GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
}
-void
+static void
mono_gc_weak_link_remove (void **link_addr, gboolean track)
{
if (track)
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);
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 */
#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>
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);
/*
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);
#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 () */
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);
#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)
{
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) {
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)
{
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);
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)
{
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);
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);
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;
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);
}
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);
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 */
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;
}
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;
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 ();
}
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;
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;
}
/*
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)
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);
MonoClass *klass;
MonoDomain *domain;
GSList *l;
- void *dis_link;
int i;
if (mono_runtime_is_shutting_down ())
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);
}
#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);
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)
}
/* 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);
}
}
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 ()) {
}
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)
{
}
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
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;
major_collector.finish_sweeping ();
sgen_process_fin_stage_entries ();
- sgen_process_dislink_stage_entries ();
sgen_clear_nursery_fragments ();
/*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);
}
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
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);
}
#ifndef MONO_CROSS_COMPILE
mono_domain_free (domain, TRUE);
- mono_gc_mutex_cleanup ();
#endif
#ifdef ENABLE_LLVM
sgen-fin-weak-hash.c \
sgen-gc.c \
sgen-gc.h \
+ sgen-gchandles.c \
sgen-gray.c \
sgen-gray.h \
sgen-hash-table.c \
#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;
*/
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,
#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
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.
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 (©, 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)
{
} 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)
{
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
*/
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);
sgen_check_consistency ();
sgen_process_fin_stage_entries ();
- sgen_process_dislink_stage_entries ();
/* pin from pinned handles */
sgen_init_pinning ();
}
sgen_process_fin_stage_entries ();
- sgen_process_dislink_stage_entries ();
TV_GETTIME (atv);
sgen_init_pinning ();
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)
{
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));
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;
}
#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.
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
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);
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);
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);
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;
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"
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__ */
--- /dev/null
+/*
+ * 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 (©, 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
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