Merge pull request #3473 from ntherning/fix-thread-double-close-handle-on-appdomain...
[mono.git] / mono / metadata / sgen-bridge.c
1 /*
2  * sgen-bridge.c: Simple generational GC.
3  *
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.
8  *
9  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10  */
11
12 #include "config.h"
13
14 #ifdef HAVE_SGEN_GC
15
16 #include <stdlib.h>
17
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"
23
24 typedef enum {
25         BRIDGE_PROCESSOR_INVALID,
26         BRIDGE_PROCESSOR_OLD,
27         BRIDGE_PROCESSOR_NEW,
28         BRIDGE_PROCESSOR_TARJAN,
29         BRIDGE_PROCESSOR_DEFAULT = BRIDGE_PROCESSOR_TARJAN
30 } BridgeProcessorSelection;
31
32 // Bridge processor type pending / in use
33 static BridgeProcessorSelection bridge_processor_selection = BRIDGE_PROCESSOR_DEFAULT;
34 // Most recently requested callbacks
35 static MonoGCBridgeCallbacks pending_bridge_callbacks;
36 // Configuration to be passed to bridge processor after init
37 static SgenBridgeProcessorConfig bridge_processor_config;
38 // Currently-in-use callbacks
39 MonoGCBridgeCallbacks bridge_callbacks;
40
41 // Bridge processor state
42 static SgenBridgeProcessor bridge_processor;
43 // This is used for a special debug feature
44 static SgenBridgeProcessor compare_to_bridge_processor;
45
46 volatile gboolean bridge_processing_in_progress = FALSE;
47
48 // FIXME: The current usage pattern for this function is unsafe. Bridge processing could start immediately after unlock
49 void
50 mono_gc_wait_for_bridge_processing (void)
51 {
52         if (!bridge_processing_in_progress)
53                 return;
54
55         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE waiting for bridge processing to finish");
56
57         sgen_gc_lock ();
58         sgen_gc_unlock ();
59 }
60
61 void
62 mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks *callbacks)
63 {
64         if (callbacks->bridge_version != SGEN_BRIDGE_VERSION)
65                 g_error ("Invalid bridge callback version. Expected %d but got %d\n", SGEN_BRIDGE_VERSION, callbacks->bridge_version);
66
67         // Defer assigning to bridge_callbacks until we have the gc lock.
68         // Note: This line is unsafe if we are on a separate thread from the one the runtime was initialized on.
69         pending_bridge_callbacks = *callbacks;
70
71         // If sgen has started, will assign bridge callbacks and init bridge
72         sgen_init_bridge ();
73 }
74
75 static BridgeProcessorSelection
76 bridge_processor_name (const char *name)
77 {
78         if (!strcmp ("old", name)) {
79                 return BRIDGE_PROCESSOR_OLD;
80         } else if (!strcmp ("new", name)) {
81                 return BRIDGE_PROCESSOR_NEW;
82         } else if (!strcmp ("tarjan", name)) {
83                 return BRIDGE_PROCESSOR_TARJAN;
84         } else {
85                 return BRIDGE_PROCESSOR_INVALID;
86         }
87 }
88
89 static gboolean
90 bridge_processor_started ()
91 {
92         return bridge_processor.reset_data != NULL;
93 }
94
95 // Initialize a single bridge processor
96 static void
97 init_bridge_processor (SgenBridgeProcessor *processor, BridgeProcessorSelection selection)
98 {
99         memset (processor, 0, sizeof (SgenBridgeProcessor));
100
101         switch (selection) {
102                 case BRIDGE_PROCESSOR_OLD:
103                         sgen_old_bridge_init (processor);
104                         break;
105                 case BRIDGE_PROCESSOR_NEW:
106                         sgen_new_bridge_init (processor);
107                         break;
108                 case BRIDGE_PROCESSOR_TARJAN:
109                         sgen_tarjan_bridge_init (processor);
110                         break;
111                 default:
112                         g_assert_not_reached ();
113         }
114 }
115
116 /*
117  * Initializing the sgen bridge consists of setting the bridge callbacks,
118  * and initializing the bridge processor. Init should follow these rules:
119  *
120  *   - Init happens only after sgen is initialized (because we don't
121  *     know which bridge processor to initialize until then, and also
122  *     to allow bridge processor init to interact with sgen if it wants)
123  *
124  *   - Init happens only after mono_gc_register_bridge_callbacks is called
125  *
126  *   - Init should not happen concurrently with a GC (because a GC will
127  *     call sgen_need_bridge_processing at various times)
128  *
129  *   - Initializing the bridge processor should happen only once
130  *
131  * We call sgen_init_bridge when the callbacks are set, and also when sgen
132  * is done initing. Actual initialization then only occurs if it is ready.
133  */
134 void
135 sgen_init_bridge ()
136 {
137         if (sgen_gc_initialized ()) {
138                 // This lock is not initialized until the GC is
139                 sgen_gc_lock ();
140
141                 bridge_callbacks = pending_bridge_callbacks;
142
143                 // If a bridge was registered but there is no bridge processor yet
144                 if (bridge_callbacks.cross_references && !bridge_processor_started ()) {
145                         init_bridge_processor (&bridge_processor, bridge_processor_selection);
146
147                         if (bridge_processor.set_config)
148                                 bridge_processor.set_config (&bridge_processor_config);
149
150                         // Config is no longer needed so free its memory
151                         free (bridge_processor_config.dump_prefix);
152                         bridge_processor_config.dump_prefix = NULL;
153                 }
154
155                 sgen_gc_unlock ();
156         }
157 }
158
159 void
160 sgen_set_bridge_implementation (const char *name)
161 {
162         BridgeProcessorSelection selection = bridge_processor_name (name);
163
164         if (selection == BRIDGE_PROCESSOR_INVALID)
165                 g_warning ("Invalid value for bridge processor implementation, valid values are: 'new', 'old' and 'tarjan'.");
166         else if (bridge_processor_started ())
167                 g_warning ("Cannot set bridge processor implementation once bridge has already started");
168         else
169                 bridge_processor_selection = selection;
170 }
171
172 gboolean
173 sgen_is_bridge_object (GCObject *obj)
174 {
175         if ((obj->vtable->gc_bits & SGEN_GC_BIT_BRIDGE_OBJECT) != SGEN_GC_BIT_BRIDGE_OBJECT)
176                 return FALSE;
177         return bridge_callbacks.is_bridge_object (obj);
178 }
179
180 gboolean
181 sgen_need_bridge_processing (void)
182 {
183         return bridge_callbacks.cross_references != NULL;
184 }
185
186 static gboolean
187 compare_bridge_processors (void)
188 {
189         return compare_to_bridge_processor.reset_data != NULL;
190 }
191
192 /* Dispatch wrappers */
193 void
194 sgen_bridge_reset_data (void)
195 {
196         bridge_processor.reset_data ();
197         if (compare_bridge_processors ())
198                 compare_to_bridge_processor.reset_data ();
199 }
200
201 void
202 sgen_bridge_processing_stw_step (void)
203 {
204         /*
205          * bridge_processing_in_progress must be set with the world
206          * stopped.  If not there would be race conditions.
207          */
208         bridge_processing_in_progress = TRUE;
209
210         bridge_processor.processing_stw_step ();
211         if (compare_bridge_processors ())
212                 compare_to_bridge_processor.processing_stw_step ();
213 }
214
215 static gboolean
216 is_bridge_object_dead (GCObject *obj, void *data)
217 {
218         SgenHashTable *table = (SgenHashTable *)data;
219         unsigned char *value = (unsigned char *)sgen_hash_table_lookup (table, obj);
220         if (!value)
221                 return FALSE;
222         return !*value;
223 }
224
225 static void
226 null_weak_links_to_dead_objects (SgenBridgeProcessor *processor, int generation)
227 {
228         int i, j;
229         int num_sccs = processor->num_sccs;
230         MonoGCBridgeSCC **api_sccs = processor->api_sccs;
231         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);
232
233         for (i = 0; i < num_sccs; ++i) {
234                 unsigned char alive = api_sccs [i]->is_alive ? 1 : 0;
235                 for (j = 0; j < api_sccs [i]->num_objs; ++j) {
236                         /* Build hash table for nulling weak links. */
237                         sgen_hash_table_replace (&alive_hash, api_sccs [i]->objs [j], &alive, NULL);
238
239                         /* Release for finalization those objects we no longer care. */
240                         if (!api_sccs [i]->is_alive)
241                                 sgen_mark_bridge_object (api_sccs [i]->objs [j]);
242                 }
243         }
244
245         /* Null weak links to dead objects. */
246         sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, FALSE);
247         sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, TRUE);
248         if (generation == GENERATION_OLD) {
249                 sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, FALSE);
250                 sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, TRUE);
251         }
252
253         sgen_hash_table_clean (&alive_hash);
254 }
255
256 static void
257 free_callback_data (SgenBridgeProcessor *processor)
258 {
259         int i;
260         int num_sccs = processor->num_sccs;
261         int num_xrefs = processor->num_xrefs;
262         MonoGCBridgeSCC **api_sccs = processor->api_sccs;
263         MonoGCBridgeXRef *api_xrefs = processor->api_xrefs;
264
265         for (i = 0; i < num_sccs; ++i) {
266                 sgen_free_internal_dynamic (api_sccs [i],
267                                 sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * api_sccs [i]->num_objs,
268                                 INTERNAL_MEM_BRIDGE_DATA);
269         }
270         sgen_free_internal_dynamic (api_sccs, sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA);
271
272         sgen_free_internal_dynamic (api_xrefs, sizeof (MonoGCBridgeXRef) * num_xrefs, INTERNAL_MEM_BRIDGE_DATA);
273
274         processor->num_sccs = 0;
275         processor->api_sccs = NULL;
276         processor->num_xrefs = 0;
277         processor->api_xrefs = NULL;
278 }
279
280 static int
281 compare_xrefs (const void *a_ptr, const void *b_ptr)
282 {
283         const MonoGCBridgeXRef *a = (const MonoGCBridgeXRef *)a_ptr;
284         const MonoGCBridgeXRef *b = (const MonoGCBridgeXRef *)b_ptr;
285
286         if (a->src_scc_index < b->src_scc_index)
287                 return -1;
288         if (a->src_scc_index > b->src_scc_index)
289                 return 1;
290
291         if (a->dst_scc_index < b->dst_scc_index)
292                 return -1;
293         if (a->dst_scc_index > b->dst_scc_index)
294                 return 1;
295
296         return 0;
297 }
298
299 /*
300 static void
301 dump_processor_state (SgenBridgeProcessor *p)
302 {
303         int i;
304
305         printf ("------\n");
306         printf ("SCCS %d\n", p->num_sccs);
307         for (i = 0; i < p->num_sccs; ++i) {
308                 int j;
309                 MonoGCBridgeSCC *scc = p->api_sccs [i];
310                 printf ("\tSCC %d:", i);
311                 for (j = 0; j < scc->num_objs; ++j) {
312                         MonoObject *obj = scc->objs [j];
313                         printf (" %p", obj);
314                 }
315                 printf ("\n");
316         }
317
318         printf ("XREFS %d\n", p->num_xrefs);
319         for (i = 0; i < p->num_xrefs; ++i)
320                 printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
321
322         printf ("-------\n");
323 }
324 */
325
326 static gboolean
327 sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcessor *b)
328 {
329         int i;
330         SgenHashTable obj_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), mono_aligned_addr_hash, NULL);
331         SgenHashTable b_scc_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), g_direct_hash, NULL);
332         MonoGCBridgeXRef *a_xrefs, *b_xrefs;
333         size_t xrefs_alloc_size;
334
335         // dump_processor_state (a);
336         // dump_processor_state (b);
337
338         if (a->num_sccs != b->num_sccs)
339                 g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs);
340         if (a->num_xrefs != b->num_xrefs)
341                 g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs);
342
343         /*
344          * First we build a hash of each object in `a` to its respective SCC index within
345          * `a`.  Along the way we also assert that no object is more than one SCC.
346          */
347         for (i = 0; i < a->num_sccs; ++i) {
348                 int j;
349                 MonoGCBridgeSCC *scc = a->api_sccs [i];
350
351                 g_assert (scc->num_objs > 0);
352
353                 for (j = 0; j < scc->num_objs; ++j) {
354                         GCObject *obj = scc->objs [j];
355                         gboolean new_entry = sgen_hash_table_replace (&obj_to_a_scc, obj, &i, NULL);
356                         g_assert (new_entry);
357                 }
358         }
359
360         /*
361          * Now we check whether each of the objects in `b` are in `a`, and whether the SCCs
362          * of `b` contain the same sets of objects as those of `a`.
363          *
364          * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC
365          * indexes.
366          */
367         for (i = 0; i < b->num_sccs; ++i) {
368                 MonoGCBridgeSCC *scc = b->api_sccs [i];
369                 MonoGCBridgeSCC *a_scc;
370                 int *a_scc_index_ptr;
371                 int a_scc_index;
372                 int j;
373                 gboolean new_entry;
374
375                 g_assert (scc->num_objs > 0);
376                 a_scc_index_ptr = (int *)sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [0]);
377                 g_assert (a_scc_index_ptr);
378                 a_scc_index = *a_scc_index_ptr;
379
380                 //g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i);
381
382                 a_scc = a->api_sccs [a_scc_index];
383                 g_assert (a_scc->num_objs == scc->num_objs);
384
385                 for (j = 1; j < scc->num_objs; ++j) {
386                         a_scc_index_ptr = (int *)sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [j]);
387                         g_assert (a_scc_index_ptr);
388                         g_assert (*a_scc_index_ptr == a_scc_index);
389                 }
390
391                 new_entry = sgen_hash_table_replace (&b_scc_to_a_scc, GINT_TO_POINTER (i), &a_scc_index, NULL);
392                 g_assert (new_entry);
393         }
394
395         /*
396          * Finally, check that we have the same xrefs.  We do this by making copies of both
397          * xref arrays, and replacing the SCC indexes in the copy for `b` with the
398          * corresponding indexes in `a`.  Then we sort both arrays and assert that they're
399          * the same.
400          *
401          * At the same time, check that no xref is self-referential and that there are no
402          * duplicate ones.
403          */
404
405         xrefs_alloc_size = a->num_xrefs * sizeof (MonoGCBridgeXRef);
406         a_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
407         b_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
408
409         memcpy (a_xrefs, a->api_xrefs, xrefs_alloc_size);
410         for (i = 0; i < b->num_xrefs; ++i) {
411                 MonoGCBridgeXRef *xref = &b->api_xrefs [i];
412                 int *scc_index_ptr;
413
414                 g_assert (xref->src_scc_index != xref->dst_scc_index);
415
416                 scc_index_ptr = (int *)sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->src_scc_index));
417                 g_assert (scc_index_ptr);
418                 b_xrefs [i].src_scc_index = *scc_index_ptr;
419
420                 scc_index_ptr = (int *)sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->dst_scc_index));
421                 g_assert (scc_index_ptr);
422                 b_xrefs [i].dst_scc_index = *scc_index_ptr;
423         }
424
425         qsort (a_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
426         qsort (b_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
427
428         for (i = 0; i < a->num_xrefs; ++i) {
429                 g_assert (a_xrefs [i].src_scc_index == b_xrefs [i].src_scc_index);
430                 g_assert (a_xrefs [i].dst_scc_index == b_xrefs [i].dst_scc_index);
431         }
432
433         sgen_hash_table_clean (&obj_to_a_scc);
434         sgen_hash_table_clean (&b_scc_to_a_scc);
435         sgen_free_internal_dynamic (a_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
436         sgen_free_internal_dynamic (b_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
437
438         return TRUE;
439 }
440
441 void
442 sgen_bridge_processing_finish (int generation)
443 {
444         bridge_processor.processing_build_callback_data (generation);
445         if (compare_bridge_processors ())
446                 compare_to_bridge_processor.processing_build_callback_data (generation);
447
448         if (bridge_processor.num_sccs == 0) {
449                 g_assert (bridge_processor.num_xrefs == 0);
450                 goto after_callback;
451         }
452
453         bridge_callbacks.cross_references (bridge_processor.num_sccs, bridge_processor.api_sccs,
454                         bridge_processor.num_xrefs, bridge_processor.api_xrefs);
455
456         if (compare_bridge_processors ())
457                 sgen_compare_bridge_processor_results (&bridge_processor, &compare_to_bridge_processor);
458
459         null_weak_links_to_dead_objects (&bridge_processor, generation);
460
461         free_callback_data (&bridge_processor);
462         if (compare_bridge_processors ())
463                 free_callback_data (&compare_to_bridge_processor);
464
465  after_callback:
466         bridge_processor.processing_after_callback (generation);
467         if (compare_bridge_processors ())
468                 compare_to_bridge_processor.processing_after_callback (generation);
469
470         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE: Complete, was running for %.2fms", mono_time_since_last_stw () / 10000.0f);
471
472         bridge_processing_in_progress = FALSE;
473 }
474
475 MonoGCBridgeObjectKind
476 sgen_bridge_class_kind (MonoClass *klass)
477 {
478         return bridge_processor.class_kind (klass);
479 }
480
481 void
482 sgen_bridge_register_finalized_object (GCObject *obj)
483 {
484         bridge_processor.register_finalized_object (obj);
485         if (compare_bridge_processors ())
486                 compare_to_bridge_processor.register_finalized_object (obj);
487 }
488
489 void
490 sgen_bridge_describe_pointer (GCObject *obj)
491 {
492         if (bridge_processor.describe_pointer)
493                 bridge_processor.describe_pointer (obj);
494 }
495
496 static void
497 set_dump_prefix (const char *prefix)
498 {
499         if (bridge_processor_config.dump_prefix)
500                 free (bridge_processor_config.dump_prefix);
501         bridge_processor_config.dump_prefix = strdup (prefix);
502 }
503
504 /* Test support code */
505 static const char *bridge_class;
506
507 static MonoGCBridgeObjectKind
508 bridge_test_bridge_class_kind (MonoClass *klass)
509 {
510         if (!strcmp (bridge_class, klass->name))
511                 return GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS;
512         return GC_BRIDGE_TRANSPARENT_CLASS;
513 }
514
515 static gboolean
516 bridge_test_is_bridge_object (MonoObject *object)
517 {
518         return TRUE;
519 }
520
521 static void
522 bridge_test_cross_reference (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
523 {
524         int i;
525         for (i = 0; i < num_sccs; ++i) {
526                 int j;
527         //      g_print ("--- SCC %d\n", i);
528                 for (j = 0; j < sccs [i]->num_objs; ++j) {
529         //              g_print ("  %s\n", sgen_safe_name (sccs [i]->objs [j]));
530                         if (i & 1) /*retain half of the bridged objects */
531                                 sccs [i]->is_alive = TRUE;
532                 }
533         }
534         for (i = 0; i < num_xrefs; ++i) {
535                 g_assert (xrefs [i].src_scc_index >= 0 && xrefs [i].src_scc_index < num_sccs);
536                 g_assert (xrefs [i].dst_scc_index >= 0 && xrefs [i].dst_scc_index < num_sccs);
537         //      g_print ("%d -> %d\n", xrefs [i].src_scc_index, xrefs [i].dst_scc_index);
538         }
539 }
540
541 static MonoClassField *mono_bridge_test_field;
542
543 enum {
544         BRIDGE_DEAD,
545         BRIDGE_ROOT,
546         BRIDGE_SAME_SCC,
547         BRIDGE_XREF,
548 };
549
550 static gboolean
551 test_scc (MonoGCBridgeSCC *scc, int i)
552 {
553         int status = BRIDGE_DEAD;
554         mono_field_get_value (scc->objs [i], mono_bridge_test_field, &status);
555         return status > 0;
556 }
557
558 static void
559 mark_scc (MonoGCBridgeSCC *scc, int value)
560 {
561         int i;
562         for (i = 0; i < scc->num_objs; ++i) {
563                 if (!test_scc (scc, i)) {
564                         int status = value;
565                         mono_field_set_value (scc->objs [i], mono_bridge_test_field, &status);
566                 }
567         }
568 }
569
570 static void
571 bridge_test_cross_reference2 (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
572 {
573         int i;
574         gboolean modified;
575
576         if (!mono_bridge_test_field) {
577                 mono_bridge_test_field = mono_class_get_field_from_name (mono_object_get_class (sccs[0]->objs [0]), "__test");
578                 g_assert (mono_bridge_test_field);
579         }
580
581         /*We mark all objects in a scc with live objects as reachable by scc*/
582         for (i = 0; i < num_sccs; ++i) {
583                 int j;
584                 gboolean live = FALSE;
585                 for (j = 0; j < sccs [i]->num_objs; ++j) {
586                         if (test_scc (sccs [i], j)) {
587                                 live = TRUE;
588                                 break;
589                         }
590                 }
591                 if (!live)
592                         continue;
593                 for (j = 0; j < sccs [i]->num_objs; ++j) {
594                         if (!test_scc (sccs [i], j)) {
595                                 int status = BRIDGE_SAME_SCC;
596                                 mono_field_set_value (sccs [i]->objs [j], mono_bridge_test_field, &status);
597                         }
598                 }
599         }
600
601         /*Now we mark the transitive closure of reachable objects from the xrefs*/
602         modified = TRUE;
603         while (modified) {
604                 modified = FALSE;
605                 /* Mark all objects that are brought to life due to xrefs*/
606                 for (i = 0; i < num_xrefs; ++i) {
607                         MonoGCBridgeXRef ref = xrefs [i];
608                         if (test_scc (sccs [ref.src_scc_index], 0) && !test_scc (sccs [ref.dst_scc_index], 0)) {
609                                 modified = TRUE;
610                                 mark_scc (sccs [ref.dst_scc_index], BRIDGE_XREF);
611                         }
612                 }
613         }
614
615         /* keep everything in memory, all we want to do is test persistence */
616         for (i = 0; i < num_sccs; ++i)
617                 sccs [i]->is_alive = TRUE;
618 }
619
620 /* This bridge keeps all peers with __test > 0 */
621 static void
622 bridge_test_positive_status (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
623 {
624         int i;
625
626         if (!mono_bridge_test_field) {
627                 mono_bridge_test_field = mono_class_get_field_from_name (mono_object_get_class (sccs[0]->objs [0]), "__test");
628                 g_assert (mono_bridge_test_field);
629         }
630
631         /*We mark all objects in a scc with live objects as reachable by scc*/
632         for (i = 0; i < num_sccs; ++i) {
633                 int j;
634                 for (j = 0; j < sccs [i]->num_objs; ++j) {
635                         if (test_scc (sccs [i], j)) {
636                                 sccs [i]->is_alive = TRUE;
637                                 break;
638                         }
639                 }
640         }
641 }
642
643
644 static void
645 register_test_bridge_callbacks (const char *bridge_class_name)
646 {
647         MonoGCBridgeCallbacks callbacks;
648         callbacks.bridge_version = SGEN_BRIDGE_VERSION;
649         callbacks.bridge_class_kind = bridge_test_bridge_class_kind;
650         callbacks.is_bridge_object = bridge_test_is_bridge_object;
651
652         switch (bridge_class_name [0]) {
653         case '2':
654                 bridge_class = bridge_class_name + 1;
655                 callbacks.cross_references = bridge_test_cross_reference2;
656                 break;
657         case '3':
658                 bridge_class = bridge_class_name + 1;
659                 callbacks.cross_references = bridge_test_positive_status;
660                 break;
661         default:
662                 bridge_class = bridge_class_name;
663                 callbacks.cross_references = bridge_test_cross_reference;
664         }
665         mono_gc_register_bridge_callbacks (&callbacks);
666 }
667
668 gboolean
669 sgen_bridge_handle_gc_param (const char *opt)
670 {
671         g_assert (!bridge_processor_started ());
672
673         if (!strcmp (opt, "bridge-require-precise-merge")) {
674                 bridge_processor_config.scc_precise_merge = TRUE;
675         } else {
676                 return FALSE;
677         }
678
679         return TRUE;
680 }
681
682 gboolean
683 sgen_bridge_handle_gc_debug (const char *opt)
684 {
685         g_assert (!bridge_processor_started ());
686
687         if (g_str_has_prefix (opt, "bridge=")) {
688                 opt = strchr (opt, '=') + 1;
689                 register_test_bridge_callbacks (g_strdup (opt));
690         } else if (!strcmp (opt, "enable-bridge-accounting")) {
691                 bridge_processor_config.accounting = TRUE;
692         } else if (g_str_has_prefix (opt, "bridge-dump=")) {
693                 char *prefix = strchr (opt, '=') + 1;
694                 set_dump_prefix(prefix);
695         } else if (g_str_has_prefix (opt, "bridge-compare-to=")) {
696                 const char *name = strchr (opt, '=') + 1;
697                 BridgeProcessorSelection selection = bridge_processor_name (name);
698
699                 if (selection != BRIDGE_PROCESSOR_INVALID) {
700                         // Compare processor doesn't get config
701                         init_bridge_processor (&compare_to_bridge_processor, selection);
702                 } else {
703                         g_warning ("Invalid bridge implementation to compare against - ignoring.");
704                 }
705         } else {
706                 return FALSE;
707         }
708         return TRUE;
709 }
710
711 void
712 sgen_bridge_print_gc_debug_usage (void)
713 {
714         fprintf (stderr, "  bridge=<class-name>\n");
715         fprintf (stderr, "  enable-bridge-accounting\n");
716         fprintf (stderr, "  bridge-dump=<filename-prefix>\n");
717         fprintf (stderr, "  bridge-compare-to=<implementation>\n");
718 }
719
720 #endif