3 * Simple generational GC.
5 * Copyright 2011 Novell, Inc (http://www.novell.com)
6 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
7 * Copyright 2001-2003 Ximian, Inc
8 * Copyright 2003-2010 Novell, Inc.
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
19 #include "sgen/sgen-gc.h"
20 #include "sgen-bridge-internals.h"
21 #include "sgen/sgen-hash-table.h"
22 #include "sgen/sgen-qsort.h"
23 #include "utils/mono-logger-internals.h"
26 BRIDGE_PROCESSOR_INVALID,
29 BRIDGE_PROCESSOR_TARJAN,
30 BRIDGE_PROCESSOR_DEFAULT = BRIDGE_PROCESSOR_TARJAN
31 } BridgeProcessorSelection;
33 // Bridge processor type pending / in use
34 static BridgeProcessorSelection bridge_processor_selection = BRIDGE_PROCESSOR_DEFAULT;
35 // Most recently requested callbacks
36 static MonoGCBridgeCallbacks pending_bridge_callbacks;
37 // Configuration to be passed to bridge processor after init
38 static SgenBridgeProcessorConfig bridge_processor_config;
39 // Currently-in-use callbacks
40 MonoGCBridgeCallbacks bridge_callbacks;
42 // Bridge processor state
43 static SgenBridgeProcessor bridge_processor;
44 // This is used for a special debug feature
45 static SgenBridgeProcessor compare_to_bridge_processor;
47 volatile gboolean bridge_processing_in_progress = FALSE;
49 // FIXME: The current usage pattern for this function is unsafe. Bridge processing could start immediately after unlock
51 * mono_gc_wait_for_bridge_processing:
54 mono_gc_wait_for_bridge_processing (void)
56 if (!bridge_processing_in_progress)
59 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE waiting for bridge processing to finish");
66 * mono_gc_register_bridge_callbacks:
69 mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks *callbacks)
71 if (callbacks->bridge_version != SGEN_BRIDGE_VERSION)
72 g_error ("Invalid bridge callback version. Expected %d but got %d\n", SGEN_BRIDGE_VERSION, callbacks->bridge_version);
74 // Defer assigning to bridge_callbacks until we have the gc lock.
75 // Note: This line is unsafe if we are on a separate thread from the one the runtime was initialized on.
76 pending_bridge_callbacks = *callbacks;
78 // If sgen has started, will assign bridge callbacks and init bridge
82 static BridgeProcessorSelection
83 bridge_processor_name (const char *name)
85 if (!strcmp ("old", name)) {
86 return BRIDGE_PROCESSOR_OLD;
87 } else if (!strcmp ("new", name)) {
88 return BRIDGE_PROCESSOR_NEW;
89 } else if (!strcmp ("tarjan", name)) {
90 return BRIDGE_PROCESSOR_TARJAN;
92 return BRIDGE_PROCESSOR_INVALID;
97 bridge_processor_started (void)
99 return bridge_processor.reset_data != NULL;
102 // Initialize a single bridge processor
104 init_bridge_processor (SgenBridgeProcessor *processor, BridgeProcessorSelection selection)
106 memset (processor, 0, sizeof (SgenBridgeProcessor));
109 case BRIDGE_PROCESSOR_OLD:
110 sgen_old_bridge_init (processor);
112 case BRIDGE_PROCESSOR_NEW:
113 sgen_new_bridge_init (processor);
115 case BRIDGE_PROCESSOR_TARJAN:
116 sgen_tarjan_bridge_init (processor);
119 g_assert_not_reached ();
124 * Initializing the sgen bridge consists of setting the bridge callbacks,
125 * and initializing the bridge processor. Init should follow these rules:
127 * - Init happens only after sgen is initialized (because we don't
128 * know which bridge processor to initialize until then, and also
129 * to allow bridge processor init to interact with sgen if it wants)
131 * - Init happens only after mono_gc_register_bridge_callbacks is called
133 * - Init should not happen concurrently with a GC (because a GC will
134 * call sgen_need_bridge_processing at various times)
136 * - Initializing the bridge processor should happen only once
138 * We call sgen_init_bridge when the callbacks are set, and also when sgen
139 * is done initing. Actual initialization then only occurs if it is ready.
142 sgen_init_bridge (void)
144 if (sgen_gc_initialized ()) {
145 // This lock is not initialized until the GC is
148 bridge_callbacks = pending_bridge_callbacks;
150 // If a bridge was registered but there is no bridge processor yet
151 if (bridge_callbacks.cross_references && !bridge_processor_started ()) {
152 init_bridge_processor (&bridge_processor, bridge_processor_selection);
154 if (bridge_processor.set_config)
155 bridge_processor.set_config (&bridge_processor_config);
157 // Config is no longer needed so free its memory
158 free (bridge_processor_config.dump_prefix);
159 bridge_processor_config.dump_prefix = NULL;
167 sgen_set_bridge_implementation (const char *name)
169 BridgeProcessorSelection selection = bridge_processor_name (name);
171 if (selection == BRIDGE_PROCESSOR_INVALID)
172 g_warning ("Invalid value for bridge processor implementation, valid values are: 'new', 'old' and 'tarjan'.");
173 else if (bridge_processor_started ())
174 g_warning ("Cannot set bridge processor implementation once bridge has already started");
176 bridge_processor_selection = selection;
180 sgen_is_bridge_object (GCObject *obj)
182 if ((obj->vtable->gc_bits & SGEN_GC_BIT_BRIDGE_OBJECT) != SGEN_GC_BIT_BRIDGE_OBJECT)
184 return bridge_callbacks.is_bridge_object (obj);
188 sgen_need_bridge_processing (void)
190 return bridge_callbacks.cross_references != NULL;
194 compare_bridge_processors (void)
196 return compare_to_bridge_processor.reset_data != NULL;
199 /* Dispatch wrappers */
201 sgen_bridge_reset_data (void)
203 bridge_processor.reset_data ();
204 if (compare_bridge_processors ())
205 compare_to_bridge_processor.reset_data ();
209 sgen_bridge_processing_stw_step (void)
212 * bridge_processing_in_progress must be set with the world
213 * stopped. If not there would be race conditions.
215 bridge_processing_in_progress = TRUE;
217 bridge_processor.processing_stw_step ();
218 if (compare_bridge_processors ())
219 compare_to_bridge_processor.processing_stw_step ();
223 is_bridge_object_dead (GCObject *obj, void *data)
225 SgenHashTable *table = (SgenHashTable *)data;
226 unsigned char *value = (unsigned char *)sgen_hash_table_lookup (table, obj);
233 null_weak_links_to_dead_objects (SgenBridgeProcessor *processor, int generation)
236 int num_sccs = processor->num_sccs;
237 MonoGCBridgeSCC **api_sccs = processor->api_sccs;
238 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);
240 for (i = 0; i < num_sccs; ++i) {
241 unsigned char alive = api_sccs [i]->is_alive ? 1 : 0;
242 for (j = 0; j < api_sccs [i]->num_objs; ++j) {
243 /* Build hash table for nulling weak links. */
244 sgen_hash_table_replace (&alive_hash, api_sccs [i]->objs [j], &alive, NULL);
246 /* Release for finalization those objects we no longer care. */
247 if (!api_sccs [i]->is_alive)
248 sgen_mark_bridge_object (api_sccs [i]->objs [j]);
252 /* Null weak links to dead objects. */
253 sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, FALSE);
254 sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, TRUE);
255 if (generation == GENERATION_OLD) {
256 sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, FALSE);
257 sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, TRUE);
260 sgen_hash_table_clean (&alive_hash);
264 free_callback_data (SgenBridgeProcessor *processor)
267 int num_sccs = processor->num_sccs;
268 int num_xrefs = processor->num_xrefs;
269 MonoGCBridgeSCC **api_sccs = processor->api_sccs;
270 MonoGCBridgeXRef *api_xrefs = processor->api_xrefs;
272 for (i = 0; i < num_sccs; ++i) {
273 sgen_free_internal_dynamic (api_sccs [i],
274 sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * api_sccs [i]->num_objs,
275 INTERNAL_MEM_BRIDGE_DATA);
277 sgen_free_internal_dynamic (api_sccs, sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA);
279 sgen_free_internal_dynamic (api_xrefs, sizeof (MonoGCBridgeXRef) * num_xrefs, INTERNAL_MEM_BRIDGE_DATA);
281 processor->num_sccs = 0;
282 processor->api_sccs = NULL;
283 processor->num_xrefs = 0;
284 processor->api_xrefs = NULL;
288 compare_xrefs (const void *a_ptr, const void *b_ptr)
290 const MonoGCBridgeXRef *a = (const MonoGCBridgeXRef *)a_ptr;
291 const MonoGCBridgeXRef *b = (const MonoGCBridgeXRef *)b_ptr;
293 if (a->src_scc_index < b->src_scc_index)
295 if (a->src_scc_index > b->src_scc_index)
298 if (a->dst_scc_index < b->dst_scc_index)
300 if (a->dst_scc_index > b->dst_scc_index)
308 dump_processor_state (SgenBridgeProcessor *p)
313 printf ("SCCS %d\n", p->num_sccs);
314 for (i = 0; i < p->num_sccs; ++i) {
316 MonoGCBridgeSCC *scc = p->api_sccs [i];
317 printf ("\tSCC %d:", i);
318 for (j = 0; j < scc->num_objs; ++j) {
319 MonoObject *obj = scc->objs [j];
325 printf ("XREFS %d\n", p->num_xrefs);
326 for (i = 0; i < p->num_xrefs; ++i)
327 printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
329 printf ("-------\n");
334 sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcessor *b)
337 SgenHashTable obj_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), mono_aligned_addr_hash, NULL);
338 SgenHashTable b_scc_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), g_direct_hash, NULL);
339 MonoGCBridgeXRef *a_xrefs, *b_xrefs;
340 size_t xrefs_alloc_size;
342 // dump_processor_state (a);
343 // dump_processor_state (b);
345 if (a->num_sccs != b->num_sccs)
346 g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs);
347 if (a->num_xrefs != b->num_xrefs)
348 g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs);
351 * First we build a hash of each object in `a` to its respective SCC index within
352 * `a`. Along the way we also assert that no object is more than one SCC.
354 for (i = 0; i < a->num_sccs; ++i) {
356 MonoGCBridgeSCC *scc = a->api_sccs [i];
358 g_assert (scc->num_objs > 0);
360 for (j = 0; j < scc->num_objs; ++j) {
361 GCObject *obj = scc->objs [j];
362 gboolean new_entry = sgen_hash_table_replace (&obj_to_a_scc, obj, &i, NULL);
363 g_assert (new_entry);
368 * Now we check whether each of the objects in `b` are in `a`, and whether the SCCs
369 * of `b` contain the same sets of objects as those of `a`.
371 * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC
374 for (i = 0; i < b->num_sccs; ++i) {
375 MonoGCBridgeSCC *scc = b->api_sccs [i];
376 MonoGCBridgeSCC *a_scc;
377 int *a_scc_index_ptr;
382 g_assert (scc->num_objs > 0);
383 a_scc_index_ptr = (int *)sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [0]);
384 g_assert (a_scc_index_ptr);
385 a_scc_index = *a_scc_index_ptr;
387 //g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i);
389 a_scc = a->api_sccs [a_scc_index];
390 g_assert (a_scc->num_objs == scc->num_objs);
392 for (j = 1; j < scc->num_objs; ++j) {
393 a_scc_index_ptr = (int *)sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [j]);
394 g_assert (a_scc_index_ptr);
395 g_assert (*a_scc_index_ptr == a_scc_index);
398 new_entry = sgen_hash_table_replace (&b_scc_to_a_scc, GINT_TO_POINTER (i), &a_scc_index, NULL);
399 g_assert (new_entry);
403 * Finally, check that we have the same xrefs. We do this by making copies of both
404 * xref arrays, and replacing the SCC indexes in the copy for `b` with the
405 * corresponding indexes in `a`. Then we sort both arrays and assert that they're
408 * At the same time, check that no xref is self-referential and that there are no
412 xrefs_alloc_size = a->num_xrefs * sizeof (MonoGCBridgeXRef);
413 a_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
414 b_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
416 memcpy (a_xrefs, a->api_xrefs, xrefs_alloc_size);
417 for (i = 0; i < b->num_xrefs; ++i) {
418 MonoGCBridgeXRef *xref = &b->api_xrefs [i];
421 g_assert (xref->src_scc_index != xref->dst_scc_index);
423 scc_index_ptr = (int *)sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->src_scc_index));
424 g_assert (scc_index_ptr);
425 b_xrefs [i].src_scc_index = *scc_index_ptr;
427 scc_index_ptr = (int *)sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->dst_scc_index));
428 g_assert (scc_index_ptr);
429 b_xrefs [i].dst_scc_index = *scc_index_ptr;
432 qsort (a_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
433 qsort (b_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
435 for (i = 0; i < a->num_xrefs; ++i) {
436 g_assert (a_xrefs [i].src_scc_index == b_xrefs [i].src_scc_index);
437 g_assert (a_xrefs [i].dst_scc_index == b_xrefs [i].dst_scc_index);
440 sgen_hash_table_clean (&obj_to_a_scc);
441 sgen_hash_table_clean (&b_scc_to_a_scc);
442 sgen_free_internal_dynamic (a_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
443 sgen_free_internal_dynamic (b_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
449 sgen_bridge_processing_finish (int generation)
451 bridge_processor.processing_build_callback_data (generation);
452 if (compare_bridge_processors ())
453 compare_to_bridge_processor.processing_build_callback_data (generation);
455 if (bridge_processor.num_sccs == 0) {
456 g_assert (bridge_processor.num_xrefs == 0);
460 bridge_callbacks.cross_references (bridge_processor.num_sccs, bridge_processor.api_sccs,
461 bridge_processor.num_xrefs, bridge_processor.api_xrefs);
463 if (compare_bridge_processors ())
464 sgen_compare_bridge_processor_results (&bridge_processor, &compare_to_bridge_processor);
466 null_weak_links_to_dead_objects (&bridge_processor, generation);
468 free_callback_data (&bridge_processor);
469 if (compare_bridge_processors ())
470 free_callback_data (&compare_to_bridge_processor);
473 bridge_processor.processing_after_callback (generation);
474 if (compare_bridge_processors ())
475 compare_to_bridge_processor.processing_after_callback (generation);
477 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE: Complete, was running for %.2fms", mono_time_since_last_stw () / 10000.0f);
479 bridge_processing_in_progress = FALSE;
482 MonoGCBridgeObjectKind
483 sgen_bridge_class_kind (MonoClass *klass)
485 return bridge_processor.class_kind (klass);
489 sgen_bridge_register_finalized_object (GCObject *obj)
491 bridge_processor.register_finalized_object (obj);
492 if (compare_bridge_processors ())
493 compare_to_bridge_processor.register_finalized_object (obj);
497 sgen_bridge_describe_pointer (GCObject *obj)
499 if (bridge_processor.describe_pointer)
500 bridge_processor.describe_pointer (obj);
504 set_dump_prefix (const char *prefix)
506 if (bridge_processor_config.dump_prefix)
507 free (bridge_processor_config.dump_prefix);
508 bridge_processor_config.dump_prefix = strdup (prefix);
511 /* Test support code */
512 static const char *bridge_class;
514 static MonoGCBridgeObjectKind
515 bridge_test_bridge_class_kind (MonoClass *klass)
517 if (!strcmp (bridge_class, klass->name))
518 return GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS;
519 return GC_BRIDGE_TRANSPARENT_CLASS;
523 bridge_test_is_bridge_object (MonoObject *object)
529 bridge_test_cross_reference (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
532 for (i = 0; i < num_sccs; ++i) {
534 // g_print ("--- SCC %d\n", i);
535 for (j = 0; j < sccs [i]->num_objs; ++j) {
536 // g_print (" %s\n", sgen_safe_name (sccs [i]->objs [j]));
537 if (i & 1) /*retain half of the bridged objects */
538 sccs [i]->is_alive = TRUE;
541 for (i = 0; i < num_xrefs; ++i) {
542 g_assert (xrefs [i].src_scc_index >= 0 && xrefs [i].src_scc_index < num_sccs);
543 g_assert (xrefs [i].dst_scc_index >= 0 && xrefs [i].dst_scc_index < num_sccs);
544 // g_print ("%d -> %d\n", xrefs [i].src_scc_index, xrefs [i].dst_scc_index);
548 static MonoClassField *mono_bridge_test_field;
558 test_scc (MonoGCBridgeSCC *scc, int i)
560 int status = BRIDGE_DEAD;
561 mono_field_get_value (scc->objs [i], mono_bridge_test_field, &status);
566 mark_scc (MonoGCBridgeSCC *scc, int value)
569 for (i = 0; i < scc->num_objs; ++i) {
570 if (!test_scc (scc, i)) {
572 mono_field_set_value (scc->objs [i], mono_bridge_test_field, &status);
578 bridge_test_cross_reference2 (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
583 if (!mono_bridge_test_field) {
584 mono_bridge_test_field = mono_class_get_field_from_name (mono_object_get_class (sccs[0]->objs [0]), "__test");
585 g_assert (mono_bridge_test_field);
588 /*We mark all objects in a scc with live objects as reachable by scc*/
589 for (i = 0; i < num_sccs; ++i) {
591 gboolean live = FALSE;
592 for (j = 0; j < sccs [i]->num_objs; ++j) {
593 if (test_scc (sccs [i], j)) {
600 for (j = 0; j < sccs [i]->num_objs; ++j) {
601 if (!test_scc (sccs [i], j)) {
602 int status = BRIDGE_SAME_SCC;
603 mono_field_set_value (sccs [i]->objs [j], mono_bridge_test_field, &status);
608 /*Now we mark the transitive closure of reachable objects from the xrefs*/
612 /* Mark all objects that are brought to life due to xrefs*/
613 for (i = 0; i < num_xrefs; ++i) {
614 MonoGCBridgeXRef ref = xrefs [i];
615 if (test_scc (sccs [ref.src_scc_index], 0) && !test_scc (sccs [ref.dst_scc_index], 0)) {
617 mark_scc (sccs [ref.dst_scc_index], BRIDGE_XREF);
622 /* keep everything in memory, all we want to do is test persistence */
623 for (i = 0; i < num_sccs; ++i)
624 sccs [i]->is_alive = TRUE;
627 /* This bridge keeps all peers with __test > 0 */
629 bridge_test_positive_status (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
633 if (!mono_bridge_test_field) {
634 mono_bridge_test_field = mono_class_get_field_from_name (mono_object_get_class (sccs[0]->objs [0]), "__test");
635 g_assert (mono_bridge_test_field);
638 /*We mark all objects in a scc with live objects as reachable by scc*/
639 for (i = 0; i < num_sccs; ++i) {
641 for (j = 0; j < sccs [i]->num_objs; ++j) {
642 if (test_scc (sccs [i], j)) {
643 sccs [i]->is_alive = TRUE;
652 register_test_bridge_callbacks (const char *bridge_class_name)
654 MonoGCBridgeCallbacks callbacks;
655 callbacks.bridge_version = SGEN_BRIDGE_VERSION;
656 callbacks.bridge_class_kind = bridge_test_bridge_class_kind;
657 callbacks.is_bridge_object = bridge_test_is_bridge_object;
659 switch (bridge_class_name [0]) {
661 bridge_class = bridge_class_name + 1;
662 callbacks.cross_references = bridge_test_cross_reference2;
665 bridge_class = bridge_class_name + 1;
666 callbacks.cross_references = bridge_test_positive_status;
669 bridge_class = bridge_class_name;
670 callbacks.cross_references = bridge_test_cross_reference;
672 mono_gc_register_bridge_callbacks (&callbacks);
676 sgen_bridge_handle_gc_param (const char *opt)
678 g_assert (!bridge_processor_started ());
680 if (!strcmp (opt, "bridge-require-precise-merge")) {
681 bridge_processor_config.scc_precise_merge = TRUE;
690 sgen_bridge_handle_gc_debug (const char *opt)
692 g_assert (!bridge_processor_started ());
694 if (g_str_has_prefix (opt, "bridge=")) {
695 opt = strchr (opt, '=') + 1;
696 register_test_bridge_callbacks (g_strdup (opt));
697 } else if (!strcmp (opt, "enable-bridge-accounting")) {
698 bridge_processor_config.accounting = TRUE;
699 } else if (g_str_has_prefix (opt, "bridge-dump=")) {
700 char *prefix = strchr (opt, '=') + 1;
701 set_dump_prefix(prefix);
702 } else if (g_str_has_prefix (opt, "bridge-compare-to=")) {
703 const char *name = strchr (opt, '=') + 1;
704 BridgeProcessorSelection selection = bridge_processor_name (name);
706 if (selection != BRIDGE_PROCESSOR_INVALID) {
707 // Compare processor doesn't get config
708 init_bridge_processor (&compare_to_bridge_processor, selection);
710 g_warning ("Invalid bridge implementation to compare against - ignoring.");
719 sgen_bridge_print_gc_debug_usage (void)
721 fprintf (stderr, " bridge=<class-name>\n");
722 fprintf (stderr, " enable-bridge-accounting\n");
723 fprintf (stderr, " bridge-dump=<filename-prefix>\n");
724 fprintf (stderr, " bridge-compare-to=<implementation>\n");