[Pkg-mono-svn-commits] [mono] 01/01: Fix race conditions in finalizer/weak link staging. (cherry picked from commit aef4b77ea79aa0a4c06e10bd5842da9df0d10973)
Jo Shields
directhex at alioth.debian.org
Tue Aug 13 10:22:07 UTC 2013
This is an automated email from the git hooks/post-receive script.
directhex pushed a commit to branch master-experimental-patches/fix-sgen-race-condition
in repository mono.
commit e19428c4f71f089a4f058676fec4f2618a92a07e
Author: Mark Probst <mark.probst at gmail.com>
Date: Thu Jul 11 13:25:42 2013 -0700
Fix race conditions in finalizer/weak link staging.
(cherry picked from commit aef4b77ea79aa0a4c06e10bd5842da9df0d10973)
---
mono/metadata/sgen-fin-weak-hash.c | 322 ++++++++++++++++++++++++++++++------
mono/metadata/sgen-gc.c | 1 +
mono/metadata/sgen-gc.h | 2 +
mono/metadata/sgen-protocol.c | 18 +-
mono/metadata/sgen-protocol.h | 26 ++-
tools/sgen/sgen-grep-binprot.c | 26 ++-
6 files changed, 339 insertions(+), 56 deletions(-)
diff --git a/mono/metadata/sgen-fin-weak-hash.c b/mono/metadata/sgen-fin-weak-hash.c
index b6983fb..bb350a9 100644
--- a/mono/metadata/sgen-fin-weak-hash.c
+++ b/mono/metadata/sgen-fin-weak-hash.c
@@ -31,6 +31,7 @@
#include "metadata/sgen-gray.h"
#include "metadata/sgen-protocol.h"
#include "utils/dtrace.h"
+#include "utils/mono-counters.h"
#define ptr_in_nursery sgen_ptr_in_nursery
@@ -230,12 +231,66 @@ register_for_finalization (MonoObject *obj, void *user_data, int generation)
}
}
+/*
+ * We're using (mostly) non-locking staging queues for finalizers and weak links to speed
+ * up registering them. Otherwise we'd have to take the GC lock.
+ *
+ * The queues are arrays of `StageEntry`, plus a `next_entry` index. Threads add entries to
+ * the queue via `add_stage_entry()` in a linear fashion until it fills up, in which case
+ * `process_stage_entries()` is called to drain it. A garbage collection will also drain
+ * the queues via the same function. That implies that `add_stage_entry()`, since it
+ * doesn't take a lock, must be able to run concurrently with `process_stage_entries()`,
+ * though it doesn't have to make progress while the queue is drained. In fact, once it
+ * detects that the queue is being drained, it blocks until the draining is done.
+ *
+ * The protocol must guarantee that entries in the queue are causally ordered, otherwise two
+ * entries for the same location might get switched, resulting in the earlier one being
+ * committed and the later one ignored.
+ *
+ * `next_entry` is the index of the next entry to be filled, or `-1` if the queue is
+ * currently being drained. Each entry has a state:
+ *
+ * `STAGE_ENTRY_FREE`: The entry is free. Its data fields must be `NULL`.
+ *
+ * `STAGE_ENTRY_BUSY`: The entry is currently being filled in.
+ *
+ * `STAGE_ENTRY_USED`: The entry is completely filled in and must be processed in the next
+ * draining round.
+ *
+ * `STAGE_ENTRY_INVALID`: The entry was busy during queue draining and therefore
+ * invalidated. Entries that are `BUSY` can obviously not be processed during a drain, but
+ * we can't leave them in place because new entries might be inserted before them, including
+ * from the same thread, violating causality. An alternative would be not to reset
+ * `next_entry` to `0` after a drain, but to the index of the last `BUSY` entry plus one,
+ * but that can potentially waste the whole queue.
+ *
+ * State transitions:
+ *
+ * | from | to | filler? | drainer? |
+ * +---------+---------+---------+----------+
+ * | FREE | BUSY | X | |
+ * | BUSY | FREE | X | |
+ * | BUSY | USED | X | |
+ * | BUSY | INVALID | | X |
+ * | USED | FREE | | X |
+ * | INVALID | FREE | X | |
+ *
+ * `next_entry` can be incremented either by the filler thread that set the corresponding
+ * entry to `BUSY`, or by another filler thread that's trying to get a `FREE` slot. If that
+ * other thread wasn't allowed to increment, it would block on the first filler thread.
+ *
+ * An entry's state, once it's set from `FREE` to `BUSY` by a filler thread, can only be
+ * changed by that same thread or by the drained. The drainer can only set a `BUSY` thread
+ * to `INVALID`, so it needs to be set to `FREE` again by the original filler thread.
+ */
+
#define STAGE_ENTRY_FREE 0
#define STAGE_ENTRY_BUSY 1
#define STAGE_ENTRY_USED 2
+#define STAGE_ENTRY_INVALID 3
typedef struct {
- gint32 state;
+ volatile gint32 state;
MonoObject *obj;
void *user_data;
} StageEntry;
@@ -245,75 +300,217 @@ typedef struct {
static volatile gint32 next_fin_stage_entry = 0;
static StageEntry fin_stage_entries [NUM_FIN_STAGE_ENTRIES];
+/*
+ * This is used to lock the stage when processing is forced, i.e. when it's triggered by a
+ * garbage collection. In that case, the world is already stopped and there's only one
+ * thread operating on the queue.
+ */
+static void
+lock_stage_for_processing (volatile gint32 *next_entry)
+{
+ *next_entry = -1;
+}
+
+/*
+ * When processing is triggered by an overflow, we don't want to take the GC lock
+ * immediately, and then set `next_index` to `-1`, because another thread might have drained
+ * the queue in the mean time. Instead, we make sure the overflow is still there, we
+ * atomically set `next_index`, and only once that happened do we take the GC lock.
+ */
+static gboolean
+try_lock_stage_for_processing (int num_entries, volatile gint32 *next_entry)
+{
+ gint32 old = *next_entry;
+ if (old < num_entries)
+ return FALSE;
+ return InterlockedCompareExchange (next_entry, -1, old) == old;
+}
+
/* LOCKING: requires that the GC lock is held */
static void
-process_stage_entries (int num_entries, volatile gint32 *next_entry, StageEntry *entries, void (*process_func) (MonoObject*, void*))
+process_stage_entries (int num_entries, volatile gint32 *next_entry, StageEntry *entries, void (*process_func) (MonoObject*, void*, int))
{
int i;
- int num_registered = 0;
- int num_busy = 0;
+
+ /*
+ * This can happen if after setting `next_index` to `-1` in
+ * `try_lock_stage_for_processing()`, a GC was triggered, which then drained the
+ * queue and reset `next_entry`.
+ *
+ * We have the GC lock now, so if it's still `-1`, we can't be interrupted by a GC.
+ */
+ if (*next_entry != -1)
+ return;
for (i = 0; i < num_entries; ++i) {
- gint32 state = entries [i].state;
+ gint32 state;
- if (state == STAGE_ENTRY_BUSY)
- ++num_busy;
+ retry:
+ state = entries [i].state;
- if (state != STAGE_ENTRY_USED ||
- InterlockedCompareExchange (&entries [i].state, STAGE_ENTRY_BUSY, STAGE_ENTRY_USED) != STAGE_ENTRY_USED) {
+ switch (state) {
+ case STAGE_ENTRY_FREE:
+ case STAGE_ENTRY_INVALID:
+ continue;
+ case STAGE_ENTRY_BUSY:
+ /* BUSY -> INVALID */
+ /*
+ * This must be done atomically, because the filler thread can set
+ * the entry to `USED`, in which case we must process it, so we must
+ * detect that eventuality.
+ */
+ if (InterlockedCompareExchange (&entries [i].state, STAGE_ENTRY_INVALID, STAGE_ENTRY_BUSY) != STAGE_ENTRY_BUSY)
+ goto retry;
continue;
+ case STAGE_ENTRY_USED:
+ break;
+ default:
+ SGEN_ASSERT (0, FALSE, "Invalid stage entry state");
+ break;
}
- process_func (entries [i].obj, entries [i].user_data);
+ /* state is USED */
+
+ process_func (entries [i].obj, entries [i].user_data, i);
entries [i].obj = NULL;
entries [i].user_data = NULL;
mono_memory_write_barrier ();
+ /* USED -> FREE */
+ /*
+ * This transition only happens here, so we don't have to do it atomically.
+ */
entries [i].state = STAGE_ENTRY_FREE;
-
- ++num_registered;
}
- *next_entry = 0;
+ mono_memory_write_barrier ();
- /* g_print ("stage busy %d reg %d\n", num_busy, num_registered); */
+ *next_entry = 0;
}
-static gboolean
+#ifdef HEAVY_STATISTICS
+static long long stat_overflow_abort = 0;
+static long long stat_wait_for_processing = 0;
+static long long stat_increment_other_thread = 0;
+static long long stat_index_decremented = 0;
+static long long stat_entry_invalidated = 0;
+static long long stat_success = 0;
+#endif
+
+static int
add_stage_entry (int num_entries, volatile gint32 *next_entry, StageEntry *entries, MonoObject *obj, void *user_data)
{
- gint32 index;
-
- do {
- do {
- index = *next_entry;
- if (index >= num_entries)
- return FALSE;
- } while (InterlockedCompareExchange (next_entry, index + 1, index) != index);
-
+ gint32 index, new_next_entry, old_next_entry;
+ gint32 previous_state;
+
+ retry:
+ for (;;) {
+ index = *next_entry;
+ if (index >= num_entries) {
+ HEAVY_STAT (++stat_overflow_abort);
+ return -1;
+ }
+ if (index < 0) {
+ /*
+ * Backed-off waiting is way more efficient than even using a
+ * dedicated lock for this.
+ */
+ while ((index = *next_entry) < 0) {
+ /*
+ * This seems like a good value. Determined by timing
+ * sgen-weakref-stress.exe.
+ */
+ g_usleep (200);
+ HEAVY_STAT (++stat_wait_for_processing);
+ }
+ continue;
+ }
+ /* FREE -> BUSY */
+ if (entries [index].state != STAGE_ENTRY_FREE ||
+ InterlockedCompareExchange (&entries [index].state, STAGE_ENTRY_BUSY, STAGE_ENTRY_FREE) != STAGE_ENTRY_FREE) {
+ /*
+ * If we can't get the entry it must be because another thread got
+ * it first. We don't want to wait for that thread to increment
+ * `next_entry`, so we try to do it ourselves. Whether we succeed
+ * or not, we start over.
+ */
+ if (*next_entry == index) {
+ InterlockedCompareExchange (next_entry, index + 1, index);
+ //g_print ("tried increment for other thread\n");
+ HEAVY_STAT (++stat_increment_other_thread);
+ }
+ continue;
+ }
+ /* state is BUSY now */
+ mono_memory_write_barrier ();
/*
- * We don't need a write barrier here. *next_entry is just a
- * help for finding an index, its value is irrelevant for
- * correctness.
+ * Incrementing `next_entry` must happen after setting the state to `BUSY`.
+ * If it were the other way around, it would be possible that after a filler
+ * incremented the index, other threads fill up the queue, the queue is
+ * drained, the original filler finally fills in the slot, but `next_entry`
+ * ends up at the start of the queue, and new entries are written in the
+ * queue in front of, not behind, the original filler's entry.
+ *
+ * We don't actually require that the CAS succeeds, but we do require that
+ * the value of `next_entry` is not lower than our index. Since the drainer
+ * sets it to `-1`, that also takes care of the case that the drainer is
+ * currently running.
*/
- } while (entries [index].state != STAGE_ENTRY_FREE ||
- InterlockedCompareExchange (&entries [index].state, STAGE_ENTRY_BUSY, STAGE_ENTRY_FREE) != STAGE_ENTRY_FREE);
+ old_next_entry = InterlockedCompareExchange (next_entry, index + 1, index);
+ if (old_next_entry < index) {
+ /* BUSY -> FREE */
+ /* INVALID -> FREE */
+ /*
+ * The state might still be `BUSY`, or the drainer could have set it
+ * to `INVALID`. In either case, there's no point in CASing. Set
+ * it to `FREE` and start over.
+ */
+ entries [index].state = STAGE_ENTRY_FREE;
+ HEAVY_STAT (++stat_index_decremented);
+ continue;
+ }
+ break;
+ }
+
+ SGEN_ASSERT (0, index >= 0 && index < num_entries, "Invalid index");
entries [index].obj = obj;
entries [index].user_data = user_data;
mono_memory_write_barrier ();
- entries [index].state = STAGE_ENTRY_USED;
+ new_next_entry = *next_entry;
+ mono_memory_read_barrier ();
+ /* BUSY -> USED */
+ /*
+ * A `BUSY` entry will either still be `BUSY` or the drainer will have set it to
+ * `INVALID`. In the former case, we set it to `USED` and we're finished. In the
+ * latter case, we reset it to `FREE` and start over.
+ */
+ previous_state = InterlockedCompareExchange (&entries [index].state, STAGE_ENTRY_USED, STAGE_ENTRY_BUSY);
+ if (previous_state == STAGE_ENTRY_BUSY) {
+ SGEN_ASSERT (0, new_next_entry >= index || new_next_entry < 0, "Invalid next entry index - as long as we're busy, other thread can only increment or invalidate it");
+ HEAVY_STAT (++stat_success);
+ return index;
+ }
+
+ SGEN_ASSERT (0, previous_state == STAGE_ENTRY_INVALID, "Invalid state transition - other thread can only make busy state invalid");
+ entries [index].obj = NULL;
+ entries [index].user_data = NULL;
+ mono_memory_write_barrier ();
+ /* INVALID -> FREE */
+ entries [index].state = STAGE_ENTRY_FREE;
- return TRUE;
+ HEAVY_STAT (++stat_entry_invalidated);
+
+ goto retry;
}
/* LOCKING: requires that the GC lock is held */
static void
-process_fin_stage_entry (MonoObject *obj, void *user_data)
+process_fin_stage_entry (MonoObject *obj, void *user_data, int index)
{
if (ptr_in_nursery (obj))
register_for_finalization (obj, user_data, GENERATION_NURSERY);
@@ -325,16 +522,19 @@ process_fin_stage_entry (MonoObject *obj, void *user_data)
void
sgen_process_fin_stage_entries (void)
{
+ lock_stage_for_processing (&next_fin_stage_entry);
process_stage_entries (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry, fin_stage_entries, process_fin_stage_entry);
}
void
mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
{
- while (!add_stage_entry (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry, fin_stage_entries, obj, user_data)) {
- LOCK_GC;
- sgen_process_fin_stage_entries ();
- UNLOCK_GC;
+ while (add_stage_entry (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry, fin_stage_entries, obj, user_data) == -1) {
+ if (try_lock_stage_for_processing (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry)) {
+ LOCK_GC;
+ process_stage_entries (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry, fin_stage_entries, process_fin_stage_entry);
+ UNLOCK_GC;
+ }
}
}
@@ -478,7 +678,7 @@ sgen_null_link_in_range (int generation, gboolean before_finalization, ScanCopyC
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);
+ 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;
@@ -500,14 +700,14 @@ sgen_null_link_in_range (int generation, gboolean before_finalization, ScanCopyC
g_assert (copy);
*link = HIDE_POINTER (copy, track);
add_or_remove_disappearing_link ((MonoObject*)copy, link, GENERATION_OLD);
- binary_protocol_dislink_update (link, copy, track);
+ 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);
+ binary_protocol_dislink_update (link, copy, track, 0);
SGEN_LOG (5, "Updated dislink at %p to %p", link, DISLINK_OBJECT (link));
}
}
@@ -530,7 +730,7 @@ sgen_null_links_for_domain (MonoDomain *domain, int generation)
if (*link) {
*link = NULL;
- binary_protocol_dislink_update (link, NULL, 0);
+ binary_protocol_dislink_update (link, NULL, 0, 0);
free = FALSE;
/*
* This can happen if finalizers are not ran, i.e. Environment.Exit ()
@@ -563,7 +763,7 @@ sgen_null_links_with_predicate (int generation, WeakLinkAlivePredicateFunc predi
if (!is_alive) {
*link = NULL;
- binary_protocol_dislink_update (link, NULL, 0);
+ 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 (TRUE);
continue;
@@ -592,10 +792,13 @@ sgen_remove_finalizers_for_domain (MonoDomain *domain, int generation)
/* LOCKING: requires that the GC lock is held */
static void
-process_dislink_stage_entry (MonoObject *obj, void *_link)
+process_dislink_stage_entry (MonoObject *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) {
@@ -615,6 +818,7 @@ static StageEntry dislink_stage_entries [NUM_DISLINK_STAGE_ENTRIES];
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);
}
@@ -640,25 +844,43 @@ sgen_register_disappearing_link (MonoObject *obj, void **link, gboolean track, g
else
*link = NULL;
- binary_protocol_dislink_update (link, obj, track);
-
#if 1
if (in_gc) {
- process_dislink_stage_entry (obj, link);
+ binary_protocol_dislink_update (link, obj, track, 0);
+ process_dislink_stage_entry (obj, link, -1);
} else {
- while (!add_stage_entry (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, obj, link)) {
- LOCK_GC;
- sgen_process_dislink_stage_entries ();
- UNLOCK_GC;
+ 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;
- process_dislink_stage_entry (obj, link);
+ 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)
+{
+#ifdef HEAVY_STATISTICS
+ mono_counters_register ("FinWeak Successes", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_success);
+ mono_counters_register ("FinWeak Overflow aborts", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_overflow_abort);
+ mono_counters_register ("FinWeak Wait for processing", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_wait_for_processing);
+ mono_counters_register ("FinWeak Increment other thread", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_increment_other_thread);
+ mono_counters_register ("FinWeak Index decremented", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_index_decremented);
+ mono_counters_register ("FinWeak Entry invalidated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_entry_invalidated);
+#endif
+}
+
#endif /* HAVE_SGEN_GC */
diff --git a/mono/metadata/sgen-gc.c b/mono/metadata/sgen-gc.c
index d59f06d..516cb5e 100644
--- a/mono/metadata/sgen-gc.c
+++ b/mono/metadata/sgen-gc.c
@@ -4865,6 +4865,7 @@ mono_gc_base_init (void)
init_stats ();
sgen_init_internal_allocator ();
sgen_init_nursery_allocator ();
+ sgen_init_fin_weak_hash ();
sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
diff --git a/mono/metadata/sgen-gc.h b/mono/metadata/sgen-gc.h
index 8628764..deba1d9 100644
--- a/mono/metadata/sgen-gc.h
+++ b/mono/metadata/sgen-gc.h
@@ -768,6 +768,8 @@ const char* sgen_safe_name (void* obj) MONO_INTERNAL;
gboolean sgen_object_is_live (void *obj) MONO_INTERNAL;
+void sgen_init_fin_weak_hash (void) MONO_INTERNAL;
+
gboolean sgen_need_bridge_processing (void) MONO_INTERNAL;
void sgen_bridge_reset_data (void) MONO_INTERNAL;
void sgen_bridge_processing_stw_step (void) MONO_INTERNAL;
diff --git a/mono/metadata/sgen-protocol.c b/mono/metadata/sgen-protocol.c
index 51a9ccf..4e363a6 100644
--- a/mono/metadata/sgen-protocol.c
+++ b/mono/metadata/sgen-protocol.c
@@ -368,12 +368,26 @@ binary_protocol_cement_reset (void)
}
void
-binary_protocol_dislink_update (gpointer link, gpointer obj, int track)
+binary_protocol_dislink_update (gpointer link, gpointer obj, int track, int staged)
{
- SGenProtocolDislinkUpdate entry = { link, obj, track };
+ SGenProtocolDislinkUpdate entry = { link, obj, track, staged };
protocol_entry (SGEN_PROTOCOL_DISLINK_UPDATE, &entry, sizeof (SGenProtocolDislinkUpdate));
}
+void
+binary_protocol_dislink_update_staged (gpointer link, gpointer obj, int track, int index)
+{
+ SGenProtocolDislinkUpdateStaged entry = { link, obj, track, index };
+ protocol_entry (SGEN_PROTOCOL_DISLINK_UPDATE_STAGED, &entry, sizeof (SGenProtocolDislinkUpdateStaged));
+}
+
+void
+binary_protocol_dislink_process_staged (gpointer link, gpointer obj, int index)
+{
+ SGenProtocolDislinkProcessStaged entry = { link, obj, index };
+ protocol_entry (SGEN_PROTOCOL_DISLINK_PROCESS_STAGED, &entry, sizeof (SGenProtocolDislinkProcessStaged));
+}
+
#endif
#endif /* HAVE_SGEN_GC */
diff --git a/mono/metadata/sgen-protocol.h b/mono/metadata/sgen-protocol.h
index 7b9dff6..658a68c 100644
--- a/mono/metadata/sgen-protocol.h
+++ b/mono/metadata/sgen-protocol.h
@@ -49,7 +49,9 @@ enum {
SGEN_PROTOCOL_CARD_SCAN,
SGEN_PROTOCOL_CEMENT,
SGEN_PROTOCOL_CEMENT_RESET,
- SGEN_PROTOCOL_DISLINK_UPDATE
+ SGEN_PROTOCOL_DISLINK_UPDATE,
+ SGEN_PROTOCOL_DISLINK_UPDATE_STAGED,
+ SGEN_PROTOCOL_DISLINK_PROCESS_STAGED
};
typedef struct {
@@ -167,8 +169,22 @@ typedef struct {
gpointer link;
gpointer obj;
int track;
+ int staged;
} SGenProtocolDislinkUpdate;
+typedef struct {
+ gpointer link;
+ gpointer obj;
+ int track;
+ int index;
+} SGenProtocolDislinkUpdateStaged;
+
+typedef struct {
+ gpointer link;
+ gpointer obj;
+ int index;
+} SGenProtocolDislinkProcessStaged;
+
/* missing: finalizers, dislinks, roots, non-store wbarriers */
void binary_protocol_init (const char *filename) MONO_INTERNAL;
@@ -201,7 +217,9 @@ void binary_protocol_missing_remset (gpointer obj, gpointer obj_vtable, int offs
void binary_protocol_card_scan (gpointer start, int size) MONO_INTERNAL;
void binary_protocol_cement (gpointer ptr, gpointer vtable, int size) MONO_INTERNAL;
void binary_protocol_cement_reset (void) MONO_INTERNAL;
-void binary_protocol_dislink_update (gpointer link, gpointer obj, int track) MONO_INTERNAL;
+void binary_protocol_dislink_update (gpointer link, gpointer obj, int track, int staged) MONO_INTERNAL;
+void binary_protocol_dislink_update_staged (gpointer link, gpointer obj, int track, int index) MONO_INTERNAL;
+void binary_protocol_dislink_process_staged (gpointer link, gpointer obj, int index) MONO_INTERNAL;
#else
@@ -232,6 +250,8 @@ void binary_protocol_dislink_update (gpointer link, gpointer obj, int track) MON
#define binary_protocol_card_scan(start, size)
#define binary_protocol_cement(ptr, vtable, size)
#define binary_protocol_cement_reset()
-#define binary_protocol_dislink_update(link,obj,track)
+#define binary_protocol_dislink_update(link,obj,track,staged)
+#define binary_protocol_dislink_update_staged(link,obj,track,index)
+#define binary_protocol_dislink_process_staged(link,obj,index)
#endif
diff --git a/tools/sgen/sgen-grep-binprot.c b/tools/sgen/sgen-grep-binprot.c
index 3dfda20..2ba9325 100644
--- a/tools/sgen/sgen-grep-binprot.c
+++ b/tools/sgen/sgen-grep-binprot.c
@@ -44,6 +44,8 @@ read_entry (FILE *in, void **data)
case SGEN_PROTOCOL_CEMENT: size = sizeof (SGenProtocolCement); break;
case SGEN_PROTOCOL_CEMENT_RESET: size = 0; break;
case SGEN_PROTOCOL_DISLINK_UPDATE: size = sizeof (SGenProtocolDislinkUpdate); break;
+ case SGEN_PROTOCOL_DISLINK_UPDATE_STAGED: size = sizeof (SGenProtocolDislinkUpdateStaged); break;
+ case SGEN_PROTOCOL_DISLINK_PROCESS_STAGED: size = sizeof (SGenProtocolDislinkProcessStaged); break;
default: assert (0);
}
@@ -185,13 +187,27 @@ print_entry (int type, void *data)
}
case SGEN_PROTOCOL_DISLINK_UPDATE: {
SGenProtocolDislinkUpdate *entry = data;
- printf ("dislink_update link %p obj %p", entry->link, entry->obj);
+ printf ("dislink_update link %p obj %p staged %d", entry->link, entry->obj, entry->staged);
if (entry->obj)
printf (" track %d\n", entry->track);
else
printf ("\n");
break;
}
+ case SGEN_PROTOCOL_DISLINK_UPDATE_STAGED: {
+ SGenProtocolDislinkUpdateStaged *entry = data;
+ printf ("dislink_update_staged link %p obj %p index %d", entry->link, entry->obj, entry->index);
+ if (entry->obj)
+ printf (" track %d\n", entry->track);
+ else
+ printf ("\n");
+ break;
+ }
+ case SGEN_PROTOCOL_DISLINK_PROCESS_STAGED: {
+ SGenProtocolDislinkProcessStaged *entry = data;
+ printf ("dislink_process_staged link %p obj %p index %d\n", entry->link, entry->obj, entry->index);
+ break;
+ }
default:
assert (0);
}
@@ -280,6 +296,14 @@ is_match (gpointer ptr, int type, void *data)
SGenProtocolDislinkUpdate *entry = data;
return ptr == entry->obj || ptr == entry->link;
}
+ case SGEN_PROTOCOL_DISLINK_UPDATE_STAGED: {
+ SGenProtocolDislinkUpdateStaged *entry = data;
+ return ptr == entry->obj || ptr == entry->link;
+ }
+ case SGEN_PROTOCOL_DISLINK_PROCESS_STAGED: {
+ SGenProtocolDislinkProcessStaged *entry = data;
+ return ptr == entry->obj || ptr == entry->link;
+ }
default:
assert (0);
}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mono/packages/mono.git
More information about the Pkg-mono-svn-commits
mailing list