1 /**
2  * \file
3  * Simple generational GC.
4  *
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.
9  *
10  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11  */
12 
13 #include "config.h"
14 
15 #ifdef HAVE_SGEN_GC
16 
17 #include <stdlib.h>
18 
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"
24 
25 typedef enum {
26 	BRIDGE_PROCESSOR_INVALID,
27 	BRIDGE_PROCESSOR_OLD,
28 	BRIDGE_PROCESSOR_NEW,
29 	BRIDGE_PROCESSOR_TARJAN,
30 	BRIDGE_PROCESSOR_DEFAULT = BRIDGE_PROCESSOR_TARJAN
31 } BridgeProcessorSelection;
32 
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;
41 
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;
46 
47 volatile gboolean bridge_processing_in_progress = FALSE;
48 
49 // FIXME: The current usage pattern for this function is unsafe. Bridge processing could start immediately after unlock
50 /**
51  * mono_gc_wait_for_bridge_processing:
52  */
53 void
mono_gc_wait_for_bridge_processing(void)54 mono_gc_wait_for_bridge_processing (void)
55 {
56 	if (!bridge_processing_in_progress)
57 		return;
58 
59 	mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE waiting for bridge processing to finish");
60 
61 	sgen_gc_lock ();
62 	sgen_gc_unlock ();
63 }
64 
65 /**
66  * mono_gc_register_bridge_callbacks:
67  */
68 void
mono_gc_register_bridge_callbacks(MonoGCBridgeCallbacks * callbacks)69 mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks *callbacks)
70 {
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);
73 
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;
77 
78 	// If sgen has started, will assign bridge callbacks and init bridge
79 	sgen_init_bridge ();
80 }
81 
82 static BridgeProcessorSelection
bridge_processor_name(const char * name)83 bridge_processor_name (const char *name)
84 {
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;
91 	} else {
92 		return BRIDGE_PROCESSOR_INVALID;
93 	}
94 }
95 
96 static gboolean
bridge_processor_started(void)97 bridge_processor_started (void)
98 {
99 	return bridge_processor.reset_data != NULL;
100 }
101 
102 // Initialize a single bridge processor
103 static void
init_bridge_processor(SgenBridgeProcessor * processor,BridgeProcessorSelection selection)104 init_bridge_processor (SgenBridgeProcessor *processor, BridgeProcessorSelection selection)
105 {
106 	memset (processor, 0, sizeof (SgenBridgeProcessor));
107 
108 	switch (selection) {
109 		case BRIDGE_PROCESSOR_OLD:
110 			sgen_old_bridge_init (processor);
111 			break;
112 		case BRIDGE_PROCESSOR_NEW:
113 			sgen_new_bridge_init (processor);
114 			break;
115 		case BRIDGE_PROCESSOR_TARJAN:
116 			sgen_tarjan_bridge_init (processor);
117 			break;
118 		default:
119 			g_assert_not_reached ();
120 	}
121 }
122 
123 /*
124  * Initializing the sgen bridge consists of setting the bridge callbacks,
125  * and initializing the bridge processor. Init should follow these rules:
126  *
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)
130  *
131  *   - Init happens only after mono_gc_register_bridge_callbacks is called
132  *
133  *   - Init should not happen concurrently with a GC (because a GC will
134  *     call sgen_need_bridge_processing at various times)
135  *
136  *   - Initializing the bridge processor should happen only once
137  *
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.
140  */
141 void
sgen_init_bridge(void)142 sgen_init_bridge (void)
143 {
144 	if (sgen_gc_initialized ()) {
145 		// This lock is not initialized until the GC is
146 		sgen_gc_lock ();
147 
148 		bridge_callbacks = pending_bridge_callbacks;
149 
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);
153 
154 			if (bridge_processor.set_config)
155 				bridge_processor.set_config (&bridge_processor_config);
156 
157 			// Config is no longer needed so free its memory
158 			free (bridge_processor_config.dump_prefix);
159 			bridge_processor_config.dump_prefix = NULL;
160 		}
161 
162 		sgen_gc_unlock ();
163 	}
164 }
165 
166 void
sgen_set_bridge_implementation(const char * name)167 sgen_set_bridge_implementation (const char *name)
168 {
169 	BridgeProcessorSelection selection = bridge_processor_name (name);
170 
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");
175 	else
176 		bridge_processor_selection = selection;
177 }
178 
179 gboolean
sgen_is_bridge_object(GCObject * obj)180 sgen_is_bridge_object (GCObject *obj)
181 {
182 	if ((obj->vtable->gc_bits & SGEN_GC_BIT_BRIDGE_OBJECT) != SGEN_GC_BIT_BRIDGE_OBJECT)
183 		return FALSE;
184 	return bridge_callbacks.is_bridge_object (obj);
185 }
186 
187 gboolean
sgen_need_bridge_processing(void)188 sgen_need_bridge_processing (void)
189 {
190 	return bridge_callbacks.cross_references != NULL;
191 }
192 
193 static gboolean
compare_bridge_processors(void)194 compare_bridge_processors (void)
195 {
196 	return compare_to_bridge_processor.reset_data != NULL;
197 }
198 
199 /* Dispatch wrappers */
200 void
sgen_bridge_reset_data(void)201 sgen_bridge_reset_data (void)
202 {
203 	bridge_processor.reset_data ();
204 	if (compare_bridge_processors ())
205 		compare_to_bridge_processor.reset_data ();
206 }
207 
208 void
sgen_bridge_processing_stw_step(void)209 sgen_bridge_processing_stw_step (void)
210 {
211 	/*
212 	 * bridge_processing_in_progress must be set with the world
213 	 * stopped.  If not there would be race conditions.
214 	 */
215 	bridge_processing_in_progress = TRUE;
216 
217 	bridge_processor.processing_stw_step ();
218 	if (compare_bridge_processors ())
219 		compare_to_bridge_processor.processing_stw_step ();
220 }
221 
222 static gboolean
is_bridge_object_dead(GCObject * obj,void * data)223 is_bridge_object_dead (GCObject *obj, void *data)
224 {
225 	SgenHashTable *table = (SgenHashTable *)data;
226 	unsigned char *value = (unsigned char *)sgen_hash_table_lookup (table, obj);
227 	if (!value)
228 		return FALSE;
229 	return !*value;
230 }
231 
232 static void
null_weak_links_to_dead_objects(SgenBridgeProcessor * processor,int generation)233 null_weak_links_to_dead_objects (SgenBridgeProcessor *processor, int generation)
234 {
235 	int i, j;
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);
239 
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);
245 
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]);
249 		}
250 	}
251 
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);
258 	}
259 
260 	sgen_hash_table_clean (&alive_hash);
261 }
262 
263 static void
free_callback_data(SgenBridgeProcessor * processor)264 free_callback_data (SgenBridgeProcessor *processor)
265 {
266 	int i;
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;
271 
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);
276 	}
277 	sgen_free_internal_dynamic (api_sccs, sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA);
278 
279 	sgen_free_internal_dynamic (api_xrefs, sizeof (MonoGCBridgeXRef) * num_xrefs, INTERNAL_MEM_BRIDGE_DATA);
280 
281 	processor->num_sccs = 0;
282 	processor->api_sccs = NULL;
283 	processor->num_xrefs = 0;
284 	processor->api_xrefs = NULL;
285 }
286 
287 static int
compare_xrefs(const void * a_ptr,const void * b_ptr)288 compare_xrefs (const void *a_ptr, const void *b_ptr)
289 {
290 	const MonoGCBridgeXRef *a = (const MonoGCBridgeXRef *)a_ptr;
291 	const MonoGCBridgeXRef *b = (const MonoGCBridgeXRef *)b_ptr;
292 
293 	if (a->src_scc_index < b->src_scc_index)
294 		return -1;
295 	if (a->src_scc_index > b->src_scc_index)
296 		return 1;
297 
298 	if (a->dst_scc_index < b->dst_scc_index)
299 		return -1;
300 	if (a->dst_scc_index > b->dst_scc_index)
301 		return 1;
302 
303 	return 0;
304 }
305 
306 /*
307 static void
308 dump_processor_state (SgenBridgeProcessor *p)
309 {
310 	int i;
311 
312 	printf ("------\n");
313 	printf ("SCCS %d\n", p->num_sccs);
314 	for (i = 0; i < p->num_sccs; ++i) {
315 		int j;
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];
320 			printf (" %p", obj);
321 		}
322 		printf ("\n");
323 	}
324 
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);
328 
329 	printf ("-------\n");
330 }
331 */
332 
333 static gboolean
sgen_compare_bridge_processor_results(SgenBridgeProcessor * a,SgenBridgeProcessor * b)334 sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcessor *b)
335 {
336 	int i;
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;
341 
342 	// dump_processor_state (a);
343 	// dump_processor_state (b);
344 
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);
349 
350 	/*
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.
353 	 */
354 	for (i = 0; i < a->num_sccs; ++i) {
355 		int j;
356 		MonoGCBridgeSCC *scc = a->api_sccs [i];
357 
358 		g_assert (scc->num_objs > 0);
359 
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);
364 		}
365 	}
366 
367 	/*
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`.
370 	 *
371 	 * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC
372 	 * indexes.
373 	 */
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;
378 		int a_scc_index;
379 		int j;
380 		gboolean new_entry;
381 
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;
386 
387 		//g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i);
388 
389 		a_scc = a->api_sccs [a_scc_index];
390 		g_assert (a_scc->num_objs == scc->num_objs);
391 
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);
396 		}
397 
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);
400 	}
401 
402 	/*
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
406 	 * the same.
407 	 *
408 	 * At the same time, check that no xref is self-referential and that there are no
409 	 * duplicate ones.
410 	 */
411 
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);
415 
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];
419 		int *scc_index_ptr;
420 
421 		g_assert (xref->src_scc_index != xref->dst_scc_index);
422 
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;
426 
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;
430 	}
431 
432 	qsort (a_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
433 	qsort (b_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
434 
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);
438 	}
439 
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);
444 
445 	return TRUE;
446 }
447 
448 void
sgen_bridge_processing_finish(int generation)449 sgen_bridge_processing_finish (int generation)
450 {
451 	bridge_processor.processing_build_callback_data (generation);
452 	if (compare_bridge_processors ())
453 		compare_to_bridge_processor.processing_build_callback_data (generation);
454 
455 	if (bridge_processor.num_sccs == 0) {
456 		g_assert (bridge_processor.num_xrefs == 0);
457 		goto after_callback;
458 	}
459 
460 	bridge_callbacks.cross_references (bridge_processor.num_sccs, bridge_processor.api_sccs,
461 			bridge_processor.num_xrefs, bridge_processor.api_xrefs);
462 
463 	if (compare_bridge_processors ())
464 		sgen_compare_bridge_processor_results (&bridge_processor, &compare_to_bridge_processor);
465 
466 	null_weak_links_to_dead_objects (&bridge_processor, generation);
467 
468 	free_callback_data (&bridge_processor);
469 	if (compare_bridge_processors ())
470 		free_callback_data (&compare_to_bridge_processor);
471 
472  after_callback:
473 	bridge_processor.processing_after_callback (generation);
474 	if (compare_bridge_processors ())
475 		compare_to_bridge_processor.processing_after_callback (generation);
476 
477 	mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE: Complete, was running for %.2fms", mono_time_since_last_stw () / 10000.0f);
478 
479 	bridge_processing_in_progress = FALSE;
480 }
481 
482 MonoGCBridgeObjectKind
sgen_bridge_class_kind(MonoClass * klass)483 sgen_bridge_class_kind (MonoClass *klass)
484 {
485 	return bridge_processor.class_kind (klass);
486 }
487 
488 void
sgen_bridge_register_finalized_object(GCObject * obj)489 sgen_bridge_register_finalized_object (GCObject *obj)
490 {
491 	bridge_processor.register_finalized_object (obj);
492 	if (compare_bridge_processors ())
493 		compare_to_bridge_processor.register_finalized_object (obj);
494 }
495 
496 void
sgen_bridge_describe_pointer(GCObject * obj)497 sgen_bridge_describe_pointer (GCObject *obj)
498 {
499 	if (bridge_processor.describe_pointer)
500 		bridge_processor.describe_pointer (obj);
501 }
502 
503 static void
set_dump_prefix(const char * prefix)504 set_dump_prefix (const char *prefix)
505 {
506 	if (bridge_processor_config.dump_prefix)
507 		free (bridge_processor_config.dump_prefix);
508 	bridge_processor_config.dump_prefix = strdup (prefix);
509 }
510 
511 /* Test support code */
512 static const char *bridge_class;
513 
514 static MonoGCBridgeObjectKind
bridge_test_bridge_class_kind(MonoClass * klass)515 bridge_test_bridge_class_kind (MonoClass *klass)
516 {
517 	if (!strcmp (bridge_class, klass->name))
518 		return GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS;
519 	return GC_BRIDGE_TRANSPARENT_CLASS;
520 }
521 
522 static gboolean
bridge_test_is_bridge_object(MonoObject * object)523 bridge_test_is_bridge_object (MonoObject *object)
524 {
525 	return TRUE;
526 }
527 
528 static void
bridge_test_cross_reference(int num_sccs,MonoGCBridgeSCC ** sccs,int num_xrefs,MonoGCBridgeXRef * xrefs)529 bridge_test_cross_reference (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
530 {
531 	int i;
532 	for (i = 0; i < num_sccs; ++i) {
533 		int j;
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;
539 		}
540 	}
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);
545 	}
546 }
547 
548 static MonoClassField *mono_bridge_test_field;
549 
550 enum {
551 	BRIDGE_DEAD,
552 	BRIDGE_ROOT,
553 	BRIDGE_SAME_SCC,
554 	BRIDGE_XREF,
555 };
556 
557 static gboolean
test_scc(MonoGCBridgeSCC * scc,int i)558 test_scc (MonoGCBridgeSCC *scc, int i)
559 {
560 	int status = BRIDGE_DEAD;
561 	mono_field_get_value (scc->objs [i], mono_bridge_test_field, &status);
562 	return status > 0;
563 }
564 
565 static void
mark_scc(MonoGCBridgeSCC * scc,int value)566 mark_scc (MonoGCBridgeSCC *scc, int value)
567 {
568 	int i;
569 	for (i = 0; i < scc->num_objs; ++i) {
570 		if (!test_scc (scc, i)) {
571 			int status = value;
572 			mono_field_set_value (scc->objs [i], mono_bridge_test_field, &status);
573 		}
574 	}
575 }
576 
577 static void
bridge_test_cross_reference2(int num_sccs,MonoGCBridgeSCC ** sccs,int num_xrefs,MonoGCBridgeXRef * xrefs)578 bridge_test_cross_reference2 (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
579 {
580 	int i;
581 	gboolean modified;
582 
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);
586 	}
587 
588 	/*We mark all objects in a scc with live objects as reachable by scc*/
589 	for (i = 0; i < num_sccs; ++i) {
590 		int j;
591 		gboolean live = FALSE;
592 		for (j = 0; j < sccs [i]->num_objs; ++j) {
593 			if (test_scc (sccs [i], j)) {
594 				live = TRUE;
595 				break;
596 			}
597 		}
598 		if (!live)
599 			continue;
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);
604 			}
605 		}
606 	}
607 
608 	/*Now we mark the transitive closure of reachable objects from the xrefs*/
609 	modified = TRUE;
610 	while (modified) {
611 		modified = FALSE;
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)) {
616 				modified = TRUE;
617 				mark_scc (sccs [ref.dst_scc_index], BRIDGE_XREF);
618 			}
619 		}
620 	}
621 
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;
625 }
626 
627 /* This bridge keeps all peers with __test > 0 */
628 static void
bridge_test_positive_status(int num_sccs,MonoGCBridgeSCC ** sccs,int num_xrefs,MonoGCBridgeXRef * xrefs)629 bridge_test_positive_status (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
630 {
631 	int i;
632 
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);
636 	}
637 
638 	/*We mark all objects in a scc with live objects as reachable by scc*/
639 	for (i = 0; i < num_sccs; ++i) {
640 		int j;
641 		for (j = 0; j < sccs [i]->num_objs; ++j) {
642 			if (test_scc (sccs [i], j)) {
643 				sccs [i]->is_alive = TRUE;
644 				break;
645 			}
646 		}
647 	}
648 }
649 
650 
651 static void
register_test_bridge_callbacks(const char * bridge_class_name)652 register_test_bridge_callbacks (const char *bridge_class_name)
653 {
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;
658 
659 	switch (bridge_class_name [0]) {
660 	case '2':
661 		bridge_class = bridge_class_name + 1;
662 		callbacks.cross_references = bridge_test_cross_reference2;
663 		break;
664 	case '3':
665 		bridge_class = bridge_class_name + 1;
666 		callbacks.cross_references = bridge_test_positive_status;
667 		break;
668 	default:
669 		bridge_class = bridge_class_name;
670 		callbacks.cross_references = bridge_test_cross_reference;
671 	}
672 	mono_gc_register_bridge_callbacks (&callbacks);
673 }
674 
675 gboolean
sgen_bridge_handle_gc_param(const char * opt)676 sgen_bridge_handle_gc_param (const char *opt)
677 {
678 	g_assert (!bridge_processor_started ());
679 
680 	if (!strcmp (opt, "bridge-require-precise-merge")) {
681 		bridge_processor_config.scc_precise_merge = TRUE;
682 	} else {
683 		return FALSE;
684 	}
685 
686 	return TRUE;
687 }
688 
689 gboolean
sgen_bridge_handle_gc_debug(const char * opt)690 sgen_bridge_handle_gc_debug (const char *opt)
691 {
692 	g_assert (!bridge_processor_started ());
693 
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);
705 
706 		if (selection != BRIDGE_PROCESSOR_INVALID) {
707 			// Compare processor doesn't get config
708 			init_bridge_processor (&compare_to_bridge_processor, selection);
709 		} else {
710 			g_warning ("Invalid bridge implementation to compare against - ignoring.");
711 		}
712 	} else {
713 		return FALSE;
714 	}
715 	return TRUE;
716 }
717 
718 void
sgen_bridge_print_gc_debug_usage(void)719 sgen_bridge_print_gc_debug_usage (void)
720 {
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");
725 }
726 
727 #endif
728