Keep objects returned by sdb alive during suspensions to avoid ObjectCollectedExcepti...
authorZoltan Varga <vargaz@gmail.com>
Sat, 22 Dec 2012 04:31:33 +0000 (05:31 +0100)
committerZoltan Varga <vargaz@gmail.com>
Sat, 22 Dec 2012 04:31:45 +0000 (05:31 +0100)
mcs/class/Mono.Debugger.Soft/Test/dtest-app.cs
mcs/class/Mono.Debugger.Soft/Test/dtest.cs
mono/mini/debugger-agent.c

index cdf7fc6097e79680b4fb11b2bd966621983d96b1..e5618b965795cd6c65a8459565c853853450b890 100644 (file)
@@ -241,6 +241,7 @@ public class Tests : TestsBase
                user ();
                type_load ();
                regress ();
+               gc_suspend ();
                if (args.Length > 0 && args [0] == "domain-test")
                        /* This takes a lot of time, so execute it conditionally */
                        domains ();
@@ -248,7 +249,7 @@ public class Tests : TestsBase
                        ref_emit ();
                if (args.Length > 0 && args [0] == "frames-in-native")
                        frames_in_native ();
-               if (args.Length >0 && args [0] == "invoke-single-threaded")
+               if (args.Length > 0 && args [0] == "invoke-single-threaded")
                        new Tests ().invoke_single_threaded ();
                return 3;
        }
@@ -1056,6 +1057,37 @@ public class Tests : TestsBase
        [MethodImplAttribute (MethodImplOptions.NoInlining)]
        public static void regress_2755_3 (int sum) {
        }
+
+       static object gc_suspend_field;
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       static unsafe void set_gc_suspend_field () {
+               set_gc_suspend_field_2 ();
+               // Clear stack
+               int* buffer = stackalloc int [4096];
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       static void set_gc_suspend_field_2 () {
+               gc_suspend_field = new object ();
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       static void gc_suspend_1 () {
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static void gc_suspend_invoke () {
+               gc_suspend_field = null;
+               GC.Collect ();
+               GC.WaitForPendingFinalizers ();
+       }
+
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       public static void gc_suspend () {
+               set_gc_suspend_field ();
+               gc_suspend_1 ();
+       }
 }
 
 class TypeLoadClass {
index ead271375b165287671f02994238cced868b8315..7cd6236e0ed2ff05b81ca88c0e4c17600f3a94fa 100644 (file)
@@ -3104,6 +3104,28 @@ public class DebuggerTests
                vm = null;
        }
 #endif
+
+       [Test]
+       public void GCWhileSuspended () {
+               // Check that objects are kept alive during suspensions
+               Event e = run_until ("gc_suspend_1");
+
+               MethodMirror m = entry_point.DeclaringType.GetMethod ("gc_suspend_invoke");
+
+               var o = entry_point.DeclaringType.GetValue (entry_point.DeclaringType.GetField ("gc_suspend_field")) as ObjectMirror;
+               //Console.WriteLine (o);
+
+               StackFrame frame = e.Thread.GetFrames () [0];
+               TypeMirror t = frame.Method.DeclaringType;
+               for (int i = 0; i < 10; ++i)
+                       t.InvokeMethod (e.Thread, m, new Value [] { });
+
+               // This throws an exception if the object is collected
+               long addr = o.Address;
+
+               var o2 = entry_point.DeclaringType.GetValue (entry_point.DeclaringType.GetField ("gc_suspend_field")) as ObjectMirror;
+               Assert.IsNull (o2);
+       }
 }
 
-}
\ No newline at end of file
+}
index 080b88263217d9892cb76d2dc749f9df5197fd0d..dc44c7f60d3cfa4e108eb8ff72389f4ad4a21cfd 100644 (file)
@@ -676,6 +676,9 @@ static gboolean protocol_version_set;
 /* A hash table containing all active domains */
 static GHashTable *domains;
 
+/* The number of times the runtime is suspended */
+static gint32 suspend_count;
+
 static void transport_init (void);
 static void transport_connect (const char *address);
 static gboolean transport_handshake (void);
@@ -1813,6 +1816,7 @@ objrefs_cleanup (void)
 }
 
 static GHashTable *obj_to_objref;
+static MonoGHashTable *suspended_objs;
 
 /*
  * Return an ObjRef for OBJ.
@@ -1829,8 +1833,18 @@ get_objref (MonoObject *obj)
 
        mono_loader_lock ();
 
-       if (!obj_to_objref)
+       if (!obj_to_objref) {
                obj_to_objref = g_hash_table_new (NULL, NULL);
+               suspended_objs = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_GC);
+               MONO_GC_REGISTER_ROOT_FIXED (suspended_objs);
+       }
+
+       if (suspend_count) {
+               /*
+                * Have to keep object refs created during suspensions alive for the duration of the suspension, so GCs during invokes don't collect them.
+                */
+               mono_g_hash_table_insert (suspended_objs, obj, NULL);
+       }
        
        /* FIXME: The tables can grow indefinitely */
 
@@ -1877,6 +1891,20 @@ get_objref (MonoObject *obj)
        return ref;
 }
 
+static gboolean
+true_pred (gpointer key, gpointer value, gpointer user_data)
+{
+       return TRUE;
+}
+
+static void
+clear_suspended_objs (void)
+{
+       mono_loader_lock ();
+       mono_g_hash_table_foreach_remove (suspended_objs, true_pred, NULL);
+       mono_loader_unlock ();
+}
+
 static inline int
 get_objid (MonoObject *obj)
 {
@@ -2283,9 +2311,6 @@ save_thread_context (MonoContext *ctx)
                mono_thread_state_init_from_current (&tls->context);
 }
 
-/* The number of times the runtime is suspended */
-static gint32 suspend_count;
-
 /* Number of threads suspended */
 /* 
  * If this is equal to the size of thread_to_tls, the runtime is considered
@@ -6310,6 +6335,7 @@ vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
                if (suspend_count == 0)
                        return ERR_NOT_SUSPENDED;
                resume_vm ();
+               clear_suspended_objs ();
                break;
        case CMD_VM_DISPOSE:
                /* Clear all event requests */