Fix sporadic failures in MonoTests.System.Net.HttpRequestStreamTest on x64 Windows.
[mono.git] / mono / metadata / sgen-bridge.c
index e015d88cdf32a5794102d0654e2f837abe1de405..9d7809a211a4a550269488fa7dc2b18be7bb0e33 100644 (file)
@@ -1,40 +1,13 @@
-/*
- * sgen-bridge.c: Simple generational GC.
+/**
+ * \file
+ * Simple generational GC.
  *
  * Copyright 2011 Novell, Inc (http://www.novell.com)
  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
- *
- * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
- * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
- *
- * Permission is hereby granted to use or copy this program
- * for any purpose,  provided the above notices are retained on all copies.
- * Permission to modify the code and to distribute modified code is granted,
- * provided the above notices are retained, and a notice that the code was
- * modified is included with the above copyright notice.
- *
- *
  * Copyright 2001-2003 Ximian, Inc
  * Copyright 2003-2010 Novell, Inc.
  *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
 #include "config.h"
 
 #include <stdlib.h>
 
-#include "sgen-gc.h"
-#include "sgen-bridge-internal.h"
-#include "sgen-hash-table.h"
-#include "sgen-qsort.h"
-#include "utils/mono-logger-internal.h"
-
+#include "sgen/sgen-gc.h"
+#include "sgen-bridge-internals.h"
+#include "sgen/sgen-hash-table.h"
+#include "sgen/sgen-qsort.h"
+#include "utils/mono-logger-internals.h"
+
+typedef enum {
+       BRIDGE_PROCESSOR_INVALID,
+       BRIDGE_PROCESSOR_OLD,
+       BRIDGE_PROCESSOR_NEW,
+       BRIDGE_PROCESSOR_TARJAN,
+       BRIDGE_PROCESSOR_DEFAULT = BRIDGE_PROCESSOR_TARJAN
+} BridgeProcessorSelection;
+
+// Bridge processor type pending / in use
+static BridgeProcessorSelection bridge_processor_selection = BRIDGE_PROCESSOR_DEFAULT;
+// Most recently requested callbacks
+static MonoGCBridgeCallbacks pending_bridge_callbacks;
+// Configuration to be passed to bridge processor after init
+static SgenBridgeProcessorConfig bridge_processor_config;
+// Currently-in-use callbacks
 MonoGCBridgeCallbacks bridge_callbacks;
+
+// Bridge processor state
 static SgenBridgeProcessor bridge_processor;
+// This is used for a special debug feature
 static SgenBridgeProcessor compare_to_bridge_processor;
 
-gboolean bridge_processing_in_progress = FALSE;
+volatile gboolean bridge_processing_in_progress = FALSE;
 
+// FIXME: The current usage pattern for this function is unsafe. Bridge processing could start immediately after unlock
+/**
+ * mono_gc_wait_for_bridge_processing:
+ */
 void
 mono_gc_wait_for_bridge_processing (void)
 {
@@ -67,42 +62,118 @@ mono_gc_wait_for_bridge_processing (void)
        sgen_gc_unlock ();
 }
 
-
+/**
+ * mono_gc_register_bridge_callbacks:
+ */
 void
 mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks *callbacks)
 {
        if (callbacks->bridge_version != SGEN_BRIDGE_VERSION)
                g_error ("Invalid bridge callback version. Expected %d but got %d\n", SGEN_BRIDGE_VERSION, callbacks->bridge_version);
 
-       bridge_callbacks = *callbacks;
+       // Defer assigning to bridge_callbacks until we have the gc lock.
+       // Note: This line is unsafe if we are on a separate thread from the one the runtime was initialized on.
+       pending_bridge_callbacks = *callbacks;
 
-       if (!bridge_processor.reset_data)
-               sgen_old_bridge_init (&bridge_processor);
+       // If sgen has started, will assign bridge callbacks and init bridge
+       sgen_init_bridge ();
 }
 
-static gboolean
-init_bridge_processor (SgenBridgeProcessor *processor, const char *name)
+static BridgeProcessorSelection
+bridge_processor_name (const char *name)
 {
        if (!strcmp ("old", name)) {
-               memset (processor, 0, sizeof (SgenBridgeProcessor));
-               sgen_old_bridge_init (processor);
+               return BRIDGE_PROCESSOR_OLD;
        } else if (!strcmp ("new", name)) {
-               memset (processor, 0, sizeof (SgenBridgeProcessor));
-               sgen_new_bridge_init (processor);
+               return BRIDGE_PROCESSOR_NEW;
        } else if (!strcmp ("tarjan", name)) {
-               memset (processor, 0, sizeof (SgenBridgeProcessor));
-               sgen_tarjan_bridge_init (processor);
+               return BRIDGE_PROCESSOR_TARJAN;
        } else {
-               return FALSE;
+               return BRIDGE_PROCESSOR_INVALID;
+       }
+}
+
+static gboolean
+bridge_processor_started (void)
+{
+       return bridge_processor.reset_data != NULL;
+}
+
+// Initialize a single bridge processor
+static void
+init_bridge_processor (SgenBridgeProcessor *processor, BridgeProcessorSelection selection)
+{
+       memset (processor, 0, sizeof (SgenBridgeProcessor));
+
+       switch (selection) {
+               case BRIDGE_PROCESSOR_OLD:
+                       sgen_old_bridge_init (processor);
+                       break;
+               case BRIDGE_PROCESSOR_NEW:
+                       sgen_new_bridge_init (processor);
+                       break;
+               case BRIDGE_PROCESSOR_TARJAN:
+                       sgen_tarjan_bridge_init (processor);
+                       break;
+               default:
+                       g_assert_not_reached ();
+       }
+}
+
+/*
+ * Initializing the sgen bridge consists of setting the bridge callbacks,
+ * and initializing the bridge processor. Init should follow these rules:
+ *
+ *   - Init happens only after sgen is initialized (because we don't
+ *     know which bridge processor to initialize until then, and also
+ *     to allow bridge processor init to interact with sgen if it wants)
+ *
+ *   - Init happens only after mono_gc_register_bridge_callbacks is called
+ *
+ *   - Init should not happen concurrently with a GC (because a GC will
+ *     call sgen_need_bridge_processing at various times)
+ *
+ *   - Initializing the bridge processor should happen only once
+ *
+ * We call sgen_init_bridge when the callbacks are set, and also when sgen
+ * is done initing. Actual initialization then only occurs if it is ready.
+ */
+void
+sgen_init_bridge (void)
+{
+       if (sgen_gc_initialized ()) {
+               // This lock is not initialized until the GC is
+               sgen_gc_lock ();
+
+               bridge_callbacks = pending_bridge_callbacks;
+
+               // If a bridge was registered but there is no bridge processor yet
+               if (bridge_callbacks.cross_references && !bridge_processor_started ()) {
+                       init_bridge_processor (&bridge_processor, bridge_processor_selection);
+
+                       if (bridge_processor.set_config)
+                               bridge_processor.set_config (&bridge_processor_config);
+
+                       // Config is no longer needed so free its memory
+                       free (bridge_processor_config.dump_prefix);
+                       bridge_processor_config.dump_prefix = NULL;
+               }
+
+               sgen_gc_unlock ();
        }
-       return TRUE;
 }
 
 void
 sgen_set_bridge_implementation (const char *name)
 {
-       if (!init_bridge_processor (&bridge_processor, name))
-               g_warning ("Invalid value for bridge implementation, valid values are: 'new' and 'old'.");
+       BridgeProcessorSelection selection = bridge_processor_name (name);
+
+       if (selection == BRIDGE_PROCESSOR_INVALID)
+               g_warning ("Invalid value for bridge processor implementation, valid values are: 'new', 'old' and 'tarjan'.");
+       else if (bridge_processor_started ())
+               g_warning ("Cannot set bridge processor implementation once bridge has already started");
+       else
+               bridge_processor_selection = selection;
 }
 
 gboolean
@@ -151,8 +222,8 @@ sgen_bridge_processing_stw_step (void)
 static gboolean
 is_bridge_object_dead (GCObject *obj, void *data)
 {
-       SgenHashTable *table = data;
-       unsigned char *value = sgen_hash_table_lookup (table, obj);
+       SgenHashTable *table = (SgenHashTable *)data;
+       unsigned char *value = (unsigned char *)sgen_hash_table_lookup (table, obj);
        if (!value)
                return FALSE;
        return !*value;
@@ -179,9 +250,12 @@ null_weak_links_to_dead_objects (SgenBridgeProcessor *processor, int generation)
        }
 
        /* Null weak links to dead objects. */
-       sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY);
-       if (generation == GENERATION_OLD)
-               sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD);
+       sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, FALSE);
+       sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_NURSERY, TRUE);
+       if (generation == GENERATION_OLD) {
+               sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, FALSE);
+               sgen_null_links_if (is_bridge_object_dead, &alive_hash, GENERATION_OLD, TRUE);
+       }
 
        sgen_hash_table_clean (&alive_hash);
 }
@@ -210,6 +284,167 @@ free_callback_data (SgenBridgeProcessor *processor)
        processor->api_xrefs = NULL;
 }
 
+static int
+compare_xrefs (const void *a_ptr, const void *b_ptr)
+{
+       const MonoGCBridgeXRef *a = (const MonoGCBridgeXRef *)a_ptr;
+       const MonoGCBridgeXRef *b = (const MonoGCBridgeXRef *)b_ptr;
+
+       if (a->src_scc_index < b->src_scc_index)
+               return -1;
+       if (a->src_scc_index > b->src_scc_index)
+               return 1;
+
+       if (a->dst_scc_index < b->dst_scc_index)
+               return -1;
+       if (a->dst_scc_index > b->dst_scc_index)
+               return 1;
+
+       return 0;
+}
+
+/*
+static void
+dump_processor_state (SgenBridgeProcessor *p)
+{
+       int i;
+
+       printf ("------\n");
+       printf ("SCCS %d\n", p->num_sccs);
+       for (i = 0; i < p->num_sccs; ++i) {
+               int j;
+               MonoGCBridgeSCC *scc = p->api_sccs [i];
+               printf ("\tSCC %d:", i);
+               for (j = 0; j < scc->num_objs; ++j) {
+                       MonoObject *obj = scc->objs [j];
+                       printf (" %p", obj);
+               }
+               printf ("\n");
+       }
+
+       printf ("XREFS %d\n", p->num_xrefs);
+       for (i = 0; i < p->num_xrefs; ++i)
+               printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
+
+       printf ("-------\n");
+}
+*/
+
+static gboolean
+sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcessor *b)
+{
+       int i;
+       SgenHashTable obj_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), mono_aligned_addr_hash, NULL);
+       SgenHashTable b_scc_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), g_direct_hash, NULL);
+       MonoGCBridgeXRef *a_xrefs, *b_xrefs;
+       size_t xrefs_alloc_size;
+
+       // dump_processor_state (a);
+       // dump_processor_state (b);
+
+       if (a->num_sccs != b->num_sccs)
+               g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs);
+       if (a->num_xrefs != b->num_xrefs)
+               g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs);
+
+       /*
+        * First we build a hash of each object in `a` to its respective SCC index within
+        * `a`.  Along the way we also assert that no object is more than one SCC.
+        */
+       for (i = 0; i < a->num_sccs; ++i) {
+               int j;
+               MonoGCBridgeSCC *scc = a->api_sccs [i];
+
+               g_assert (scc->num_objs > 0);
+
+               for (j = 0; j < scc->num_objs; ++j) {
+                       GCObject *obj = scc->objs [j];
+                       gboolean new_entry = sgen_hash_table_replace (&obj_to_a_scc, obj, &i, NULL);
+                       g_assert (new_entry);
+               }
+       }
+
+       /*
+        * Now we check whether each of the objects in `b` are in `a`, and whether the SCCs
+        * of `b` contain the same sets of objects as those of `a`.
+        *
+        * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC
+        * indexes.
+        */
+       for (i = 0; i < b->num_sccs; ++i) {
+               MonoGCBridgeSCC *scc = b->api_sccs [i];
+               MonoGCBridgeSCC *a_scc;
+               int *a_scc_index_ptr;
+               int a_scc_index;
+               int j;
+               gboolean new_entry;
+
+               g_assert (scc->num_objs > 0);
+               a_scc_index_ptr = (int *)sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [0]);
+               g_assert (a_scc_index_ptr);
+               a_scc_index = *a_scc_index_ptr;
+
+               //g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i);
+
+               a_scc = a->api_sccs [a_scc_index];
+               g_assert (a_scc->num_objs == scc->num_objs);
+
+               for (j = 1; j < scc->num_objs; ++j) {
+                       a_scc_index_ptr = (int *)sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [j]);
+                       g_assert (a_scc_index_ptr);
+                       g_assert (*a_scc_index_ptr == a_scc_index);
+               }
+
+               new_entry = sgen_hash_table_replace (&b_scc_to_a_scc, GINT_TO_POINTER (i), &a_scc_index, NULL);
+               g_assert (new_entry);
+       }
+
+       /*
+        * Finally, check that we have the same xrefs.  We do this by making copies of both
+        * xref arrays, and replacing the SCC indexes in the copy for `b` with the
+        * corresponding indexes in `a`.  Then we sort both arrays and assert that they're
+        * the same.
+        *
+        * At the same time, check that no xref is self-referential and that there are no
+        * duplicate ones.
+        */
+
+       xrefs_alloc_size = a->num_xrefs * sizeof (MonoGCBridgeXRef);
+       a_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
+       b_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
+
+       memcpy (a_xrefs, a->api_xrefs, xrefs_alloc_size);
+       for (i = 0; i < b->num_xrefs; ++i) {
+               MonoGCBridgeXRef *xref = &b->api_xrefs [i];
+               int *scc_index_ptr;
+
+               g_assert (xref->src_scc_index != xref->dst_scc_index);
+
+               scc_index_ptr = (int *)sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->src_scc_index));
+               g_assert (scc_index_ptr);
+               b_xrefs [i].src_scc_index = *scc_index_ptr;
+
+               scc_index_ptr = (int *)sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->dst_scc_index));
+               g_assert (scc_index_ptr);
+               b_xrefs [i].dst_scc_index = *scc_index_ptr;
+       }
+
+       qsort (a_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
+       qsort (b_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
+
+       for (i = 0; i < a->num_xrefs; ++i) {
+               g_assert (a_xrefs [i].src_scc_index == b_xrefs [i].src_scc_index);
+               g_assert (a_xrefs [i].dst_scc_index == b_xrefs [i].dst_scc_index);
+       }
+
+       sgen_hash_table_clean (&obj_to_a_scc);
+       sgen_hash_table_clean (&b_scc_to_a_scc);
+       sgen_free_internal_dynamic (a_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
+       sgen_free_internal_dynamic (b_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
+
+       return TRUE;
+}
+
 void
 sgen_bridge_processing_finish (int generation)
 {
@@ -239,13 +474,15 @@ sgen_bridge_processing_finish (int generation)
        if (compare_bridge_processors ())
                compare_to_bridge_processor.processing_after_callback (generation);
 
+       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_BRIDGE: Complete, was running for %.2fms", mono_time_since_last_stw () / 10000.0f);
+
        bridge_processing_in_progress = FALSE;
 }
 
 MonoGCBridgeObjectKind
-sgen_bridge_class_kind (MonoClass *class)
+sgen_bridge_class_kind (MonoClass *klass)
 {
-       return bridge_processor.class_kind (class);
+       return bridge_processor.class_kind (klass);
 }
 
 void
@@ -266,21 +503,18 @@ sgen_bridge_describe_pointer (GCObject *obj)
 static void
 set_dump_prefix (const char *prefix)
 {
-       if (!bridge_processor.set_dump_prefix) {
-               fprintf (stderr, "Warning: Bridge implementation does not support dumping - ignoring.\n");
-               return;
-       }
-
-       bridge_processor.set_dump_prefix (prefix);
+       if (bridge_processor_config.dump_prefix)
+               free (bridge_processor_config.dump_prefix);
+       bridge_processor_config.dump_prefix = strdup (prefix);
 }
 
 /* Test support code */
 static const char *bridge_class;
 
 static MonoGCBridgeObjectKind
-bridge_test_bridge_class_kind (MonoClass *class)
+bridge_test_bridge_class_kind (MonoClass *klass)
 {
-       if (!strcmp (bridge_class, class->name))
+       if (!strcmp (bridge_class, klass->name))
                return GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS;
        return GC_BRIDGE_TRANSPARENT_CLASS;
 }
@@ -390,6 +624,30 @@ bridge_test_cross_reference2 (int num_sccs, MonoGCBridgeSCC **sccs, int num_xref
                sccs [i]->is_alive = TRUE;
 }
 
+/* This bridge keeps all peers with __test > 0 */
+static void
+bridge_test_positive_status (int num_sccs, MonoGCBridgeSCC **sccs, int num_xrefs, MonoGCBridgeXRef *xrefs)
+{
+       int i;
+
+       if (!mono_bridge_test_field) {
+               mono_bridge_test_field = mono_class_get_field_from_name (mono_object_get_class (sccs[0]->objs [0]), "__test");
+               g_assert (mono_bridge_test_field);
+       }
+
+       /*We mark all objects in a scc with live objects as reachable by scc*/
+       for (i = 0; i < num_sccs; ++i) {
+               int j;
+               for (j = 0; j < sccs [i]->num_objs; ++j) {
+                       if (test_scc (sccs [i], j)) {
+                               sccs [i]->is_alive = TRUE;
+                               break;
+                       }
+               }
+       }
+}
+
+
 static void
 register_test_bridge_callbacks (const char *bridge_class_name)
 {
@@ -397,29 +655,57 @@ register_test_bridge_callbacks (const char *bridge_class_name)
        callbacks.bridge_version = SGEN_BRIDGE_VERSION;
        callbacks.bridge_class_kind = bridge_test_bridge_class_kind;
        callbacks.is_bridge_object = bridge_test_is_bridge_object;
-       callbacks.cross_references = bridge_class_name[0] == '2' ? bridge_test_cross_reference2 : bridge_test_cross_reference;
+
+       switch (bridge_class_name [0]) {
+       case '2':
+               bridge_class = bridge_class_name + 1;
+               callbacks.cross_references = bridge_test_cross_reference2;
+               break;
+       case '3':
+               bridge_class = bridge_class_name + 1;
+               callbacks.cross_references = bridge_test_positive_status;
+               break;
+       default:
+               bridge_class = bridge_class_name;
+               callbacks.cross_references = bridge_test_cross_reference;
+       }
        mono_gc_register_bridge_callbacks (&callbacks);
-       bridge_class = bridge_class_name + (bridge_class_name[0] == '2' ? 1 : 0);
+}
+
+gboolean
+sgen_bridge_handle_gc_param (const char *opt)
+{
+       g_assert (!bridge_processor_started ());
+
+       if (!strcmp (opt, "bridge-require-precise-merge")) {
+               bridge_processor_config.scc_precise_merge = TRUE;
+       } else {
+               return FALSE;
+       }
+
+       return TRUE;
 }
 
 gboolean
 sgen_bridge_handle_gc_debug (const char *opt)
 {
+       g_assert (!bridge_processor_started ());
+
        if (g_str_has_prefix (opt, "bridge=")) {
                opt = strchr (opt, '=') + 1;
                register_test_bridge_callbacks (g_strdup (opt));
        } else if (!strcmp (opt, "enable-bridge-accounting")) {
-               bridge_processor.enable_accounting ();
+               bridge_processor_config.accounting = TRUE;
        } else if (g_str_has_prefix (opt, "bridge-dump=")) {
                char *prefix = strchr (opt, '=') + 1;
-               set_dump_prefix (prefix);
+               set_dump_prefix(prefix);
        } else if (g_str_has_prefix (opt, "bridge-compare-to=")) {
                const char *name = strchr (opt, '=') + 1;
-               if (init_bridge_processor (&compare_to_bridge_processor, name)) {
-                       if (compare_to_bridge_processor.reset_data == bridge_processor.reset_data) {
-                               g_warning ("Cannot compare bridge implementation to itself - ignoring.");
-                               memset (&compare_to_bridge_processor, 0, sizeof (SgenBridgeProcessor));
-                       }
+               BridgeProcessorSelection selection = bridge_processor_name (name);
+
+               if (selection != BRIDGE_PROCESSOR_INVALID) {
+                       // Compare processor doesn't get config
+                       init_bridge_processor (&compare_to_bridge_processor, selection);
                } else {
                        g_warning ("Invalid bridge implementation to compare against - ignoring.");
                }