2 * sgen-bridge.c: Simple generational GC.
4 * Copyright 2011 Novell, Inc (http://www.novell.com)
5 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
6 * Copyright 2001-2003 Ximian, Inc
7 * Copyright 2003-2010 Novell, Inc.
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
18 #include "sgen/sgen-gc.h"
19 #include "sgen-bridge-internals.h"
20 #include "sgen/sgen-hash-table.h"
21 #include "sgen/sgen-qsort.h"
22 #include "utils/mono-logger-internals.h"
24 MonoGCBridgeCallbacks bridge_callbacks;
25 static SgenBridgeProcessor bridge_processor;
26 static SgenBridgeProcessor compare_to_bridge_processor;
28 volatile gboolean bridge_processing_in_progress = FALSE;
31 mono_gc_wait_for_bridge_processing (void)
33 if (!bridge_processing_in_progress)
36 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE waiting for bridge processing to finish");
44 mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks *callbacks)
46 if (callbacks->bridge_version != SGEN_BRIDGE_VERSION)
47 g_error ("Invalid bridge callback version. Expected %d but got %d\n", SGEN_BRIDGE_VERSION, callbacks->bridge_version);
49 bridge_callbacks = *callbacks;
51 // If callbacks are still uninitialized, initialize defaults
52 if (!bridge_processor.reset_data)
53 sgen_tarjan_bridge_init (&bridge_processor);
57 init_bridge_processor (SgenBridgeProcessor *processor, const char *name)
59 if (!strcmp ("old", name)) {
60 memset (processor, 0, sizeof (SgenBridgeProcessor));
61 sgen_old_bridge_init (processor);
62 } else if (!strcmp ("new", name)) {
63 memset (processor, 0, sizeof (SgenBridgeProcessor));
64 sgen_new_bridge_init (processor);
65 } else if (!strcmp ("tarjan", name)) {
66 memset (processor, 0, sizeof (SgenBridgeProcessor));
67 sgen_tarjan_bridge_init (processor);
75 sgen_set_bridge_implementation (const char *name)
77 if (!init_bridge_processor (&bridge_processor, name))
78 g_warning ("Invalid value for bridge implementation, valid values are: 'new', 'old' and 'tarjan'.");
82 sgen_is_bridge_object (GCObject *obj)
84 if ((obj->vtable->gc_bits & SGEN_GC_BIT_BRIDGE_OBJECT) != SGEN_GC_BIT_BRIDGE_OBJECT)
86 return bridge_callbacks.is_bridge_object (obj);
90 sgen_need_bridge_processing (void)
92 return bridge_callbacks.cross_references != NULL;
96 compare_bridge_processors (void)
98 return compare_to_bridge_processor.reset_data != NULL;
101 /* Dispatch wrappers */
103 sgen_bridge_reset_data (void)
105 bridge_processor.reset_data ();
106 if (compare_bridge_processors ())
107 compare_to_bridge_processor.reset_data ();
111 sgen_bridge_processing_stw_step (void)
114 * bridge_processing_in_progress must be set with the world
115 * stopped. If not there would be race conditions.
117 bridge_processing_in_progress = TRUE;
119 bridge_processor.processing_stw_step ();
120 if (compare_bridge_processors ())
121 compare_to_bridge_processor.processing_stw_step ();
125 is_bridge_object_dead (GCObject *obj, void *data)
127 SgenHashTable *table = (SgenHashTable *)data;
128 unsigned char *value = (unsigned char *)sgen_hash_table_lookup (table, obj);
135 null_weak_links_to_dead_objects (SgenBridgeProcessor *processor, int generation)
138 int num_sccs = processor->num_sccs;
139 MonoGCBridgeSCC **api_sccs = processor->api_sccs;
140 SgenHashTable alive_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_ALIVE_HASH_TABLE, INTERNAL_MEM_BRIDGE_ALIVE_HASH_TABLE_ENTRY, 1, mono_aligned_addr_hash, NULL);
142 for (i = 0; i < num_sccs; ++i) {
143 unsigned char alive = api_sccs [i]->is_alive ? 1 : 0;
144 for (j = 0; j < api_sccs [i]->num_objs; ++j) {
145 /* Build hash table for nulling weak links. */
146 sgen_hash_table_replace (&alive_hash, api_sccs [i]->objs [j], &alive, NULL);
148 /* Release for finalization those objects we no longer care. */
149 if (!api_sccs [i]->is_alive)
150 sgen_mark_bridge_object (api_sccs [i]->objs [j]);
154 /* Null weak links to dead objects. */
155 sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, FALSE);
156 sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, TRUE);
157 if (generation == GENERATION_OLD) {
158 sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, FALSE);
159 sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, TRUE);
162 sgen_hash_table_clean (&alive_hash);
166 free_callback_data (SgenBridgeProcessor *processor)
169 int num_sccs = processor->num_sccs;
170 int num_xrefs = processor->num_xrefs;
171 MonoGCBridgeSCC **api_sccs = processor->api_sccs;
172 MonoGCBridgeXRef *api_xrefs = processor->api_xrefs;
174 for (i = 0; i < num_sccs; ++i) {
175 sgen_free_internal_dynamic (api_sccs [i],
176 sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * api_sccs [i]->num_objs,
177 INTERNAL_MEM_BRIDGE_DATA);
179 sgen_free_internal_dynamic (api_sccs, sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA);
181 sgen_free_internal_dynamic (api_xrefs, sizeof (MonoGCBridgeXRef) * num_xrefs, INTERNAL_MEM_BRIDGE_DATA);
183 processor->num_sccs = 0;
184 processor->api_sccs = NULL;
185 processor->num_xrefs = 0;
186 processor->api_xrefs = NULL;
190 compare_xrefs (const void *a_ptr, const void *b_ptr)
192 const MonoGCBridgeXRef *a = (const MonoGCBridgeXRef *)a_ptr;
193 const MonoGCBridgeXRef *b = (const MonoGCBridgeXRef *)b_ptr;
195 if (a->src_scc_index < b->src_scc_index)
197 if (a->src_scc_index > b->src_scc_index)
200 if (a->dst_scc_index < b->dst_scc_index)
202 if (a->dst_scc_index > b->dst_scc_index)
210 dump_processor_state (SgenBridgeProcessor *p)
215 printf ("SCCS %d\n", p->num_sccs);
216 for (i = 0; i < p->num_sccs; ++i) {
218 MonoGCBridgeSCC *scc = p->api_sccs [i];
219 printf ("\tSCC %d:", i);
220 for (j = 0; j < scc->num_objs; ++j) {
221 MonoObject *obj = scc->objs [j];
227 printf ("XREFS %d\n", p->num_xrefs);
228 for (i = 0; i < p->num_xrefs; ++i)
229 printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
231 printf ("-------\n");
236 sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcessor *b)
239 SgenHashTable obj_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), mono_aligned_addr_hash, NULL);
240 SgenHashTable b_scc_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), g_direct_hash, NULL);
241 MonoGCBridgeXRef *a_xrefs, *b_xrefs;
242 size_t xrefs_alloc_size;
244 // dump_processor_state (a);
245 // dump_processor_state (b);
247 if (a->num_sccs != b->num_sccs)
248 g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs);
249 if (a->num_xrefs != b->num_xrefs)
250 g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs);
253 * First we build a hash of each object in `a` to its respective SCC index within
254 * `a`. Along the way we also assert that no object is more than one SCC.
256 for (i = 0; i < a->num_sccs; ++i) {
258 MonoGCBridgeSCC *scc = a->api_sccs [i];
260 g_assert (scc->num_objs > 0);
262 for (j = 0; j < scc->num_objs; ++j) {
263 GCObject *obj = scc->objs [j];
264 gboolean new_entry = sgen_hash_table_replace (&obj_to_a_scc, obj, &i, NULL);
265 g_assert (new_entry);
270 * Now we check whether each of the objects in `b` are in `a`, and whether the SCCs
271 * of `b` contain the same sets of objects as those of `a`.
273 * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC
276 for (i = 0; i < b->num_sccs; ++i) {
277 MonoGCBridgeSCC *scc = b->api_sccs [i];
278 MonoGCBridgeSCC *a_scc;
279 int *a_scc_index_ptr;
284 g_assert (scc->num_objs > 0);
285 a_scc_index_ptr = (int *)sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [0]);
286 g_assert (a_scc_index_ptr);
287 a_scc_index = *a_scc_index_ptr;
289 //g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i);
291 a_scc = a->api_sccs [a_scc_index];
292 g_assert (a_scc->num_objs == scc->num_objs);
294 for (j = 1; j < scc->num_objs; ++j) {
295 a_scc_index_ptr = (int *)sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [j]);
296 g_assert (a_scc_index_ptr);
297 g_assert (*a_scc_index_ptr == a_scc_index);
300 new_entry = sgen_hash_table_replace (&b_scc_to_a_scc, GINT_TO_POINTER (i), &a_scc_index, NULL);
301 g_assert (new_entry);
305 * Finally, check that we have the same xrefs. We do this by making copies of both
306 * xref arrays, and replacing the SCC indexes in the copy for `b` with the
307 * corresponding indexes in `a`. Then we sort both arrays and assert that they're
310 * At the same time, check that no xref is self-referential and that there are no
314 xrefs_alloc_size = a->num_xrefs * sizeof (MonoGCBridgeXRef);
315 a_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
316 b_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
318 memcpy (a_xrefs, a->api_xrefs, xrefs_alloc_size);
319 for (i = 0; i < b->num_xrefs; ++i) {
320 MonoGCBridgeXRef *xref = &b->api_xrefs [i];
323 g_assert (xref->src_scc_index != xref->dst_scc_index);
325 scc_index_ptr = (int *)sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->src_scc_index));
326 g_assert (scc_index_ptr);
327 b_xrefs [i].src_scc_index = *scc_index_ptr;
329 scc_index_ptr = (int *)sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->dst_scc_index));
330 g_assert (scc_index_ptr);
331 b_xrefs [i].dst_scc_index = *scc_index_ptr;
334 qsort (a_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
335 qsort (b_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
337 for (i = 0; i < a->num_xrefs; ++i) {
338 g_assert (a_xrefs [i].src_scc_index == b_xrefs [i].src_scc_index);
339 g_assert (a_xrefs [i].dst_scc_index == b_xrefs [i].dst_scc_index);
342 sgen_hash_table_clean (&obj_to_a_scc);
343 sgen_hash_table_clean (&b_scc_to_a_scc);
344 sgen_free_internal_dynamic (a_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
345 sgen_free_internal_dynamic (b_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
351 sgen_bridge_processing_finish (int generation)
353 bridge_processor.processing_build_callback_data (generation);
354 if (compare_bridge_processors ())
355 compare_to_bridge_processor.processing_build_callback_data (generation);
357 if (bridge_processor.num_sccs == 0) {
358 g_assert (bridge_processor.num_xrefs == 0);
362 bridge_callbacks.cross_references (bridge_processor.num_sccs, bridge_processor.api_sccs,
363 bridge_processor.num_xrefs, bridge_processor.api_xrefs);
365 if (compare_bridge_processors ())
366 sgen_compare_bridge_processor_results (&bridge_processor, &compare_to_bridge_processor);
368 null_weak_links_to_dead_objects (&bridge_processor, generation);
370 free_callback_data (&bridge_processor);
371 if (compare_bridge_processors ())
372 free_callback_data (&compare_to_bridge_processor);
375 bridge_processor.processing_after_callback (generation);
376 if (compare_bridge_processors ())
377 compare_to_bridge_processor.processing_after_callback (generation);
379 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE: Complete, was running for %.2fms", mono_time_since_last_stw () / 10000.0f);
381 bridge_processing_in_progress = FALSE;
384 MonoGCBridgeObjectKind
385 sgen_bridge_class_kind (MonoClass *klass)
387 return bridge_processor.class_kind (klass);
391 sgen_bridge_register_finalized_object (GCObject *obj)
393 bridge_processor.register_finalized_object (obj);
394 if (compare_bridge_processors ())
395 compare_to_bridge_processor.register_finalized_object (obj);
399 sgen_bridge_describe_pointer (GCObject *obj)
401 if (bridge_processor.describe_pointer)
402 bridge_processor.describe_pointer (obj);
406 set_dump_prefix (const char *prefix)
408 if (!bridge_processor.set_dump_prefix) {
409 fprintf (stderr, "Warning: Bridge implementation does not support dumping - ignoring.\n");
413 bridge_processor.set_dump_prefix (prefix);
416 /* Test support code */
417 static const char *bridge_class;
419 static MonoGCBridgeObjectKind
420 bridge_test_bridge_class_kind (MonoClass *klass)
422 if (!strcmp (bridge_class, klass->name))
423 return GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS;
424 return GC_BRIDGE_TRANSPARENT_CLASS;
428 bridge_test_is_bridge_object (MonoObject *object)
434 bridge_test_cross_reference (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
437 for (i = 0; i < num_sccs; ++i) {
439 // g_print ("--- SCC %d\n", i);
440 for (j = 0; j < sccs [i]->num_objs; ++j) {
441 // g_print (" %s\n", sgen_safe_name (sccs [i]->objs [j]));
442 if (i & 1) /*retain half of the bridged objects */
443 sccs [i]->is_alive = TRUE;
446 for (i = 0; i < num_xrefs; ++i) {
447 g_assert (xrefs [i].src_scc_index >= 0 && xrefs [i].src_scc_index < num_sccs);
448 g_assert (xrefs [i].dst_scc_index >= 0 && xrefs [i].dst_scc_index < num_sccs);
449 // g_print ("%d -> %d\n", xrefs [i].src_scc_index, xrefs [i].dst_scc_index);
453 static MonoClassField *mono_bridge_test_field;
463 test_scc (MonoGCBridgeSCC *scc, int i)
465 int status = BRIDGE_DEAD;
466 mono_field_get_value (scc->objs [i], mono_bridge_test_field, &status);
471 mark_scc (MonoGCBridgeSCC *scc, int value)
474 for (i = 0; i < scc->num_objs; ++i) {
475 if (!test_scc (scc, i)) {
477 mono_field_set_value (scc->objs [i], mono_bridge_test_field, &status);
483 bridge_test_cross_reference2 (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
488 if (!mono_bridge_test_field) {
489 mono_bridge_test_field = mono_class_get_field_from_name (mono_object_get_class (sccs[0]->objs [0]), "__test");
490 g_assert (mono_bridge_test_field);
493 /*We mark all objects in a scc with live objects as reachable by scc*/
494 for (i = 0; i < num_sccs; ++i) {
496 gboolean live = FALSE;
497 for (j = 0; j < sccs [i]->num_objs; ++j) {
498 if (test_scc (sccs [i], j)) {
505 for (j = 0; j < sccs [i]->num_objs; ++j) {
506 if (!test_scc (sccs [i], j)) {
507 int status = BRIDGE_SAME_SCC;
508 mono_field_set_value (sccs [i]->objs [j], mono_bridge_test_field, &status);
513 /*Now we mark the transitive closure of reachable objects from the xrefs*/
517 /* Mark all objects that are brought to life due to xrefs*/
518 for (i = 0; i < num_xrefs; ++i) {
519 MonoGCBridgeXRef ref = xrefs [i];
520 if (test_scc (sccs [ref.src_scc_index], 0) && !test_scc (sccs [ref.dst_scc_index], 0)) {
522 mark_scc (sccs [ref.dst_scc_index], BRIDGE_XREF);
527 /* keep everything in memory, all we want to do is test persistence */
528 for (i = 0; i < num_sccs; ++i)
529 sccs [i]->is_alive = TRUE;
532 /* This bridge keeps all peers with __test > 0 */
534 bridge_test_positive_status (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
538 if (!mono_bridge_test_field) {
539 mono_bridge_test_field = mono_class_get_field_from_name (mono_object_get_class (sccs[0]->objs [0]), "__test");
540 g_assert (mono_bridge_test_field);
543 /*We mark all objects in a scc with live objects as reachable by scc*/
544 for (i = 0; i < num_sccs; ++i) {
546 for (j = 0; j < sccs [i]->num_objs; ++j) {
547 if (test_scc (sccs [i], j)) {
548 sccs [i]->is_alive = TRUE;
557 register_test_bridge_callbacks (const char *bridge_class_name)
559 MonoGCBridgeCallbacks callbacks;
560 callbacks.bridge_version = SGEN_BRIDGE_VERSION;
561 callbacks.bridge_class_kind = bridge_test_bridge_class_kind;
562 callbacks.is_bridge_object = bridge_test_is_bridge_object;
564 switch (bridge_class_name [0]) {
566 bridge_class = bridge_class_name + 1;
567 callbacks.cross_references = bridge_test_cross_reference2;
570 bridge_class = bridge_class_name + 1;
571 callbacks.cross_references = bridge_test_positive_status;
574 bridge_class = bridge_class_name;
575 callbacks.cross_references = bridge_test_cross_reference;
577 mono_gc_register_bridge_callbacks (&callbacks);
581 sgen_bridge_handle_gc_debug (const char *opt)
583 if (g_str_has_prefix (opt, "bridge=")) {
584 opt = strchr (opt, '=') + 1;
585 register_test_bridge_callbacks (g_strdup (opt));
586 } else if (!strcmp (opt, "enable-bridge-accounting")) {
587 bridge_processor.enable_accounting ();
588 } else if (g_str_has_prefix (opt, "bridge-dump=")) {
589 char *prefix = strchr (opt, '=') + 1;
590 set_dump_prefix (prefix);
591 } else if (g_str_has_prefix (opt, "bridge-compare-to=")) {
592 const char *name = strchr (opt, '=') + 1;
593 if (init_bridge_processor (&compare_to_bridge_processor, name)) {
594 if (compare_to_bridge_processor.reset_data == bridge_processor.reset_data) {
595 g_warning ("Cannot compare bridge implementation to itself - ignoring.");
596 memset (&compare_to_bridge_processor, 0, sizeof (SgenBridgeProcessor));
599 g_warning ("Invalid bridge implementation to compare against - ignoring.");
608 sgen_bridge_print_gc_debug_usage (void)
610 fprintf (stderr, " bridge=<class-name>\n");
611 fprintf (stderr, " enable-bridge-accounting\n");
612 fprintf (stderr, " bridge-dump=<filename-prefix>\n");
613 fprintf (stderr, " bridge-compare-to=<implementation>\n");