From: Jon Purdy Date: Tue, 9 Dec 2014 22:24:13 +0000 (-0800) Subject: [sgen] Remove weak ref hashes. X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=87fb34d18a733168a91df8d2358f233a2382a62d;p=mono.git [sgen] Remove weak ref hashes. These hashes were inefficiently duplicating the information in the common GC handle data tables. * Make GC lock recursive. This must be reverted when the GC handles table is made lock-free. * Refactor handle allocation and handle data manipulation to avoid duplication and magic numbers. * Use GC handles in refqueue and monitors, because the dislink machinery no longer exists. --- diff --git a/mono/metadata/boehm-gc.c b/mono/metadata/boehm-gc.c index 2fb0ec330f7..52b701c3e50 100644 --- a/mono/metadata/boehm-gc.c +++ b/mono/metadata/boehm-gc.c @@ -1462,4 +1462,9 @@ mono_gc_is_null (void) return FALSE; } +gboolean +mono_gc_object_older_than (MonoObject *object, int generation) { + return FALSE; +} + #endif /* no Boehm GC */ diff --git a/mono/metadata/gc-internal.h b/mono/metadata/gc-internal.h index caac7de9301..0b977306b40 100644 --- a/mono/metadata/gc-internal.h +++ b/mono/metadata/gc-internal.h @@ -11,6 +11,7 @@ #define __MONO_METADATA_GC_INTERNAL_H__ #include +#include #include #include #include @@ -375,5 +376,7 @@ extern gboolean log_finalizers; /* If set, do not run finalizers. */ extern gboolean do_not_finalize; +gboolean mono_gc_object_older_than (MonoObject *object, int generation); + #endif /* __MONO_METADATA_GC_INTERNAL_H__ */ diff --git a/mono/metadata/gc.c b/mono/metadata/gc.c index 00018597956..4d4998a2f5b 100644 --- a/mono/metadata/gc.c +++ b/mono/metadata/gc.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include /* for mono_delegate_free_ftnptr () */ @@ -529,16 +530,17 @@ 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; +#ifndef HAVE_SGEN_GC +static mono_mutex_t handle_section; +#endif -static HandleType mono_gchandle_get_type (guint32 gchandle); +#define GC_HANDLE_TYPE_SHIFT (3) +#define GC_HANDLE_TYPE_MASK ((1 << GC_HANDLE_TYPE_SHIFT) - 1) +#define GC_HANDLE_TYPE(x) (((x) & GC_HANDLE_TYPE_MASK) - 1) +#define GC_HANDLE_SLOT(x) ((x) >> GC_HANDLE_TYPE_SHIFT) +#define GC_HANDLE_TYPE_IS_WEAK(x) ((x) <= HANDLE_WEAK_TRACK) +#define GC_HANDLE_TAG(slot, type) (((slot) << GC_HANDLE_TYPE_SHIFT) | (((type) & GC_HANDLE_TYPE_MASK) + 1)) MonoObject * ves_icall_System_GCHandle_GetTarget (guint32 handle) @@ -583,7 +585,7 @@ ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle) { MonoObject *obj; - if (mono_gchandle_get_type (handle) != HANDLE_PINNED) + if (GC_HANDLE_TYPE (handle) != HANDLE_PINNED) return (gpointer)-2; obj = mono_gchandle_get_target (handle); if (obj) { @@ -614,28 +616,52 @@ typedef struct { gpointer *entries; guint32 size; guint8 type; - guint slot_hint : 24; /* starting slot for search */ + 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 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); +} + +#define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0} + /* 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} + EMPTY_HANDLE_DATA (HANDLE_WEAK), + EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK), + EMPTY_HANDLE_DATA (HANDLE_NORMAL), + EMPTY_HANDLE_DATA (HANDLE_PINNED) }; + +#ifdef HAVE_SGEN_GC +#define lock_handles(handles) sgen_gc_lock () +#define unlock_handles(handles) sgen_gc_unlock () +#else #define lock_handles(handles) do { \ MONO_TRY_BLOCKING; \ mono_mutex_lock (&handle_section); \ MONO_FINISH_TRY_BLOCKING; \ } while (0) - #define unlock_handles(handles) mono_mutex_unlock (&handle_section) +#endif static int find_first_unset (guint32 bitmap) @@ -658,89 +684,109 @@ make_root_descr_all_refs (int numbits, gboolean pinned) return mono_gc_make_root_descr_all_refs (numbits); } -static guint32 -alloc_handle (HandleData *handles, MonoObject *obj, gboolean track) +static void +handle_data_alloc_entries (HandleData *handles) { - 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; - } - } + handles->size = 32; + if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) { + handles->entries = g_malloc0 (sizeof (*handles->entries) * handles->size); + handles->domain_ids = g_malloc0 (sizeof (*handles->domain_ids) * handles->size); + } else { + handles->entries = mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, make_root_descr_all_refs (handles->size, handles->type == HANDLE_PINNED), MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table"); } - 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; + handles->bitmap = g_malloc0 (handles->size / CHAR_BIT); +} - /* resize and copy the entries */ - if (handles->type > HANDLE_WEAK_TRACK) { - gpointer *entries; +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; +} - 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); +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; +} - 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]); - } +/* 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 (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; } + g_free (handles->entries); + g_free (handles->domain_ids); + handles->entries = entries; + handles->domain_ids = domain_ids; + } else { + gpointer *entries; + entries = mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, make_root_descr_all_refs (new_size, handles->type == HANDLE_PINNED), MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table"); + mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size); + mono_gc_free_fixed (handles->entries); + handles->entries = entries; + } + handles->slot_hint = handles->size / BITMAP_SIZE; + handles->size = new_size; +} - /* set i and slot to the next free position */ +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->size + 1) / 32; - handles->slot_hint = handles->size + 1; - handles->size = new_size; } - handles->bitmap [slot] |= 1 << i; - slot = slot * 32 + i; + slot = handles->slot_hint * BITMAP_SIZE + i; + occupy_slot (handles, slot); handles->entries [slot] = NULL; - if (handles->type <= HANDLE_WEAK_TRACK) { + if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) { /*FIXME, what to use when obj == null?*/ handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id; if (obj) @@ -753,12 +799,28 @@ alloc_handle (HandleData *handles, MonoObject *obj, gboolean track) 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); + res = GC_HANDLE_TAG (slot, handles->type); mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj); return res; } +void +mono_gchandle_iterate (GCHandleType handle_type, int max_generation, gpointer callback(gpointer *, GCHandleType, gpointer), gpointer user) +{ + HandleData *handle_data = &gc_handles [handle_type]; + size_t i; + lock_handles (handle_data); + for (i = 0; i < handle_data->size; ++i) { + gpointer *entry = &handle_data->entries [i]; + /* Table must contain no garbage pointers. */ + g_assert (*entry ? slot_occupied (handle_data, i) : TRUE); + if (!*entry || !slot_occupied (handle_data, i) || mono_gc_object_older_than (REVEAL_POINTER (*entry), max_generation)) + continue; + *entry = callback (entry, handle_type, user); + } + unlock_handles (handle_data); +} + /** * mono_gchandle_new: * @obj: managed object to get a handle for @@ -809,14 +871,6 @@ mono_gchandle_new_weakref (MonoObject *obj, gboolean 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. @@ -830,15 +884,16 @@ mono_gchandle_get_type (guint32 gchandle) MonoObject* mono_gchandle_get_target (guint32 gchandle) { - guint slot = gchandle >> 3; - guint type = (gchandle & 7) - 1; + guint slot = GC_HANDLE_SLOT (gchandle); + guint type = GC_HANDLE_TYPE (gchandle); HandleData *handles = &gc_handles [type]; MonoObject *obj = NULL; - if (type > 3) + if (type >= HANDLE_TYPE_MAX) return NULL; + lock_handles (handles); - if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) { - if (handles->type <= HANDLE_WEAK_TRACK) { + if (slot < handles->size && slot_occupied (handles, slot)) { + if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) { obj = mono_gc_weak_link_get (&handles->entries [slot]); } else { obj = handles->entries [slot]; @@ -854,15 +909,14 @@ mono_gchandle_get_target (guint32 gchandle) static void mono_gchandle_set_target (guint32 gchandle, MonoObject *obj) { - guint slot = gchandle >> 3; - guint type = (gchandle & 7) - 1; + guint slot = GC_HANDLE_SLOT (gchandle); + guint type = GC_HANDLE_TYPE (gchandle); HandleData *handles = &gc_handles [type]; - if (type > 3) - return; + g_assert (type < HANDLE_TYPE_MAX); lock_handles (handles); - if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) { - if (handles->type <= HANDLE_WEAK_TRACK) { + if (slot < handles->size && slot_occupied (handles, slot)) { + if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) { if (handles->entries [slot]) mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK); if (obj) @@ -889,15 +943,17 @@ mono_gchandle_set_target (guint32 gchandle, MonoObject *obj) gboolean mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain) { - guint slot = gchandle >> 3; - guint type = (gchandle & 7) - 1; + guint slot = GC_HANDLE_SLOT (gchandle); + guint type = GC_HANDLE_TYPE (gchandle); HandleData *handles = &gc_handles [type]; gboolean result = FALSE; - if (type > 3) + + if (type >= HANDLE_TYPE_MAX) return FALSE; + lock_handles (handles); - if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) { - if (handles->type <= HANDLE_WEAK_TRACK) { + if (slot < handles->size && slot_occupied (handles, slot)) { + if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) { result = domain->domain_id == handles->domain_ids [slot]; } else { MonoObject *obj; @@ -925,21 +981,21 @@ mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain) void mono_gchandle_free (guint32 gchandle) { - guint slot = gchandle >> 3; - guint type = (gchandle & 7) - 1; + guint slot = GC_HANDLE_SLOT (gchandle); + guint type = GC_HANDLE_TYPE (gchandle); HandleData *handles = &gc_handles [type]; - if (type > 3) + if (type >= HANDLE_TYPE_MAX) return; lock_handles (handles); - if (slot < handles->size && (handles->bitmap [slot / 32] & (1 << (slot % 32)))) { - if (handles->type <= HANDLE_WEAK_TRACK) { + if (slot < handles->size && slot_occupied (handles, slot)) { + if (GC_HANDLE_TYPE_IS_WEAK (handles->type)) { if (handles->entries [slot]) mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK); } else { handles->entries [slot] = NULL; } - handles->bitmap [slot / 32] &= ~(1 << (slot % 32)); + vacate_slot (handles, slot); } else { /* print a warning? */ } @@ -963,22 +1019,22 @@ mono_gchandle_free_domain (MonoDomain *domain) { guint type; - for (type = 0; type < 3; ++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 (!(handles->bitmap [slot / 32] & (1 << (slot % 32)))) + if (!slot_occupied (handles, slot)) continue; - if (type <= HANDLE_WEAK_TRACK) { + if (GC_HANDLE_TYPE_IS_WEAK (type)) { if (domain->domain_id == handles->domain_ids [slot]) { - handles->bitmap [slot / 32] &= ~(1 << (slot % 32)); + 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) { - handles->bitmap [slot / 32] &= ~(1 << (slot % 32)); + vacate_slot (handles, slot); handles->entries [slot] = NULL; } } @@ -1174,7 +1230,9 @@ mono_gc_init_finalizer_thread (void) void mono_gc_init (void) { +#ifndef HAVE_SGEN_GC mono_mutex_init_recursive (&handle_section); +#endif mono_mutex_init_recursive (&allocator_section); mono_mutex_init_recursive (&finalizer_mutex); @@ -1285,6 +1343,10 @@ mono_gc_cleanup (void) mono_reference_queue_cleanup (); +#ifndef HAVE_SGEN_GC + mono_mutex_destroy (&handle_section); +#endif + mono_mutex_destroy (&allocator_section); mono_mutex_destroy (&finalizer_mutex); mono_mutex_destroy (&reference_queue_mutex); @@ -1298,7 +1360,9 @@ mono_gc_cleanup (void) void mono_gc_mutex_cleanup (void) { +#ifndef HAVE_SGEN_GC mono_mutex_destroy (&handle_section); +#endif } gboolean @@ -1377,13 +1441,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 +1497,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 +1561,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; diff --git a/mono/metadata/monitor.c b/mono/metadata/monitor.c index 0d9001be1a0..151c1d382c9 100644 --- a/mono/metadata/monitor.c +++ b/mono/metadata/monitor.c @@ -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; } /* diff --git a/mono/metadata/monitor.h b/mono/metadata/monitor.h index cc00f2044dc..bd796254034 100644 --- a/mono/metadata/monitor.h +++ b/mono/metadata/monitor.h @@ -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) diff --git a/mono/metadata/sgen-bridge.c b/mono/metadata/sgen-bridge.c index 47f4f94db5c..3f2992dbb04 100644 --- a/mono/metadata/sgen-bridge.c +++ b/mono/metadata/sgen-bridge.c @@ -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); } diff --git a/mono/metadata/sgen-client-mono.h b/mono/metadata/sgen-client-mono.h index ede6e48ba40..7b4fdc66d7a 100644 --- a/mono/metadata/sgen-client-mono.h +++ b/mono/metadata/sgen-client-mono.h @@ -571,7 +571,7 @@ 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_update (gpointer link, gpointer obj, gboolean track) { #ifdef ENABLE_DTRACE if (MONO_GC_WEAK_UPDATE_ENABLED ()) { diff --git a/mono/metadata/sgen-mono.c b/mono/metadata/sgen-mono.c index 4e388d1557a..b946b36c699 100644 --- a/mono/metadata/sgen-mono.c +++ b/mono/metadata/sgen-mono.c @@ -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; @@ -863,8 +863,10 @@ mono_gc_clear_domain (MonoDomain * domain) 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); + for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i) { + sgen_null_links_if (object_in_domain_predicate, domain, i, FALSE); + sgen_null_links_if (object_in_domain_predicate, domain, i, TRUE); + } for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i) sgen_remove_finalizers_if (object_in_domain_predicate, domain, i); @@ -2571,13 +2573,15 @@ mono_gc_get_los_limit (void) void mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track) { - sgen_register_disappearing_link (obj, link_addr, track, FALSE); + binary_protocol_dislink_add ((gpointer)link_addr, obj, track); + *link_addr = (void*)HIDE_POINTER (obj); } void mono_gc_weak_link_remove (void **link_addr, gboolean track) { - sgen_register_disappearing_link (NULL, link_addr, track, FALSE); + binary_protocol_dislink_remove ((gpointer)link_addr, track); + *link_addr = NULL; } MonoObject* @@ -2586,6 +2590,12 @@ mono_gc_weak_link_get (void **link_addr) return sgen_weak_link_get (link_addr); } +gboolean +mono_gc_object_older_than (MonoObject *object, int generation) +{ + return generation == GENERATION_NURSERY && !sgen_ptr_in_nursery (object); +} + gboolean mono_gc_set_allow_synchronous_major (gboolean flag) { diff --git a/mono/sgen/gc-internal-agnostic.h b/mono/sgen/gc-internal-agnostic.h index b7e4b6708c0..ef85b74d36c 100644 --- a/mono/sgen/gc-internal-agnostic.h +++ b/mono/sgen/gc-internal-agnostic.h @@ -31,6 +31,25 @@ #include "mono/sgen/sgen-conf.h" #endif +#ifndef HIDE_POINTER +#define HIDE_POINTER(p) ((gpointer)~(size_t)(p)) +#endif + +#ifndef REVEAL_POINTER +#define REVEAL_POINTER(p) ((gpointer)~(size_t)(p)) +#endif + +typedef enum { + HANDLE_TYPE_MIN = 0, + HANDLE_WEAK = HANDLE_TYPE_MIN, + HANDLE_WEAK_TRACK, + HANDLE_NORMAL, + HANDLE_PINNED, + HANDLE_TYPE_MAX +} GCHandleType; + +void mono_gchandle_iterate (GCHandleType handle_type, int max_generation, gpointer callback(gpointer *, GCHandleType, gpointer), gpointer user); + typedef struct { guint minor_gc_count; guint major_gc_count; diff --git a/mono/sgen/sgen-fin-weak-hash.c b/mono/sgen/sgen-fin-weak-hash.c index a0994b9a1be..5bf77c4fde3 100644 --- a/mono/sgen/sgen-fin-weak-hash.c +++ b/mono/sgen/sgen-fin-weak-hash.c @@ -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 @@ -629,147 +630,62 @@ 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) +/* + * Returns whether to remove the link from its hash. + */ +static gpointer +null_link_if_necessary (gpointer *hidden_entry, GCHandleType handle_type, gpointer user) { - 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; + ScanCopyContext *ctx = (ScanCopyContext *)user; + gpointer entry = REVEAL_POINTER (*hidden_entry); + char *copy = entry; + gboolean entry_in_nursery = ptr_in_nursery (entry); + if (sgen_get_current_collection_generation () == GENERATION_NURSERY && !entry_in_nursery) + return *hidden_entry; + /* Clear link if object is ready for finalization. */ + if (sgen_gc_is_object_ready_for_finalization (entry)) { + return NULL; } - - 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)); + ctx->ops->copy_or_mark_object ((void **)©, ctx->queue); + g_assert (copy); + /* Update pointer if it's moved. */ + binary_protocol_dislink_update (hidden_entry, copy, handle_type == HANDLE_WEAK_TRACK); + return HIDE_POINTER (copy); } /* LOCKING: requires that the GC lock is held */ void -sgen_null_link_in_range (int generation, gboolean before_finalization, ScanCopyContext ctx) +sgen_null_link_in_range (int generation, gboolean before_finalization, ScanCopyContext ctx, gboolean track) { - 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; - } + mono_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if_necessary, &ctx); +} - 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; - } +typedef struct { + SgenObjectPredicateFunc predicate; + gpointer data; +} WeakLinkAlivePredicateClosure; - 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; +static gpointer +null_link_if (gpointer *hidden_entry, GCHandleType handle_type, gpointer user) +{ + /* Strictly speaking, function pointers are not guaranteed to have the same size as data pointers. */ + WeakLinkAlivePredicateClosure *closure = (WeakLinkAlivePredicateClosure *)user; + gpointer entry = REVEAL_POINTER (*hidden_entry); + if (!entry) + return NULL; + if (closure->predicate ((MonoObject*)entry, closure->data)) { + binary_protocol_dislink_update (hidden_entry, NULL, 0); + return NULL; + } + return *hidden_entry; } /* LOCKING: requires that the GC lock is held */ void -sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation) +sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation, gboolean track) { - 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; + WeakLinkAlivePredicateClosure closure = { predicate, data }; + mono_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if, &closure); } void @@ -789,70 +705,15 @@ 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 diff --git a/mono/sgen/sgen-gc.c b/mono/sgen/sgen-gc.c index f00706d9772..382b259e711 100644 --- a/mono/sgen/sgen-gc.c +++ b/mono/sgen/sgen-gc.c @@ -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, TRUE, ctx, FALSE); if (generation == GENERATION_OLD) - sgen_null_link_in_range (GENERATION_NURSERY, TRUE, ctx); + sgen_null_link_in_range (GENERATION_NURSERY, TRUE, 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, FALSE, ctx, TRUE); if (generation == GENERATION_OLD) - sgen_null_link_in_range (GENERATION_NURSERY, FALSE, ctx); + sgen_null_link_in_range (GENERATION_NURSERY, FALSE, ctx, TRUE); if (sgen_gray_object_queue_is_empty (queue)) break; sgen_drain_gray_stack (-1, ctx); @@ -2873,7 +2873,7 @@ sgen_gc_init (void) mono_thread_smr_init (); #endif - LOCK_INIT (gc_mutex); + mono_mutex_init_recursive (&gc_mutex); gc_debug_file = stderr; diff --git a/mono/sgen/sgen-gc.h b/mono/sgen/sgen-gc.h index 5594db16c7f..327d47f08c6 100644 --- a/mono/sgen/sgen-gc.h +++ b/mono/sgen/sgen-gc.h @@ -172,14 +172,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 +778,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 +788,7 @@ void sgen_queue_finalization_entry (GCObject *obj); const char* sgen_generation_name (int generation); void sgen_finalize_in_range (int generation, ScanCopyContext ctx); -void sgen_null_link_in_range (int generation, gboolean before_finalization, ScanCopyContext ctx); +void sgen_null_link_in_range (int generation, gboolean before_finalization, 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); diff --git a/mono/sgen/sgen-protocol-def.h b/mono/sgen/sgen-protocol-def.h index ca1e053e64b..fec52341131 100644 --- a/mono/sgen/sgen-protocol-def.h +++ b/mono/sgen/sgen-protocol-def.h @@ -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_INT, 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