[sgen] Fix GC handle table resize for SGen. Fixes #10127.
authorMark Probst <mark.probst@gmail.com>
Thu, 14 Feb 2013 17:39:02 +0000 (09:39 -0800)
committerMark Probst <mark.probst@gmail.com>
Thu, 14 Feb 2013 22:40:10 +0000 (14:40 -0800)
SGen doesn't honor mono_gc_disable(), so the code was racy.

mono/metadata/gc.c
mono/tests/Makefile.am
mono/tests/bug-10127.cs [new file with mode: 0644]

index 796ff3698cb2fc67cfd67218ee755703622514c4..6a41811ec83274a9929ebc8b520e880584568a52 100644 (file)
@@ -681,26 +681,21 @@ alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
                        gpointer *entries;
                        guint16 *domain_ids;
                        domain_ids = g_malloc0 (sizeof (guint16) * new_size);
-                       entries = g_malloc (sizeof (gpointer) * new_size);
-                       /* we disable GC because we could lose some disappearing link updates */
-                       mono_gc_disable ();
-                       mono_gc_memmove (entries, handles->entries, sizeof (gpointer) * handles->size);
-                       mono_gc_bzero (entries + handles->size, sizeof (gpointer) * handles->size);
+                       entries = g_malloc0 (sizeof (gpointer) * new_size);
                        memcpy (domain_ids, handles->domain_ids, sizeof (guint16) * handles->size);
                        for (i = 0; i < handles->size; ++i) {
                                MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
-                               if (handles->entries [i])
-                                       mono_gc_weak_link_remove (&(handles->entries [i]), track);
-                               /*g_print ("reg/unreg entry %d of type %d at %p to object %p (%p), was: %p\n", i, handles->type, &(entries [i]), obj, entries [i], handles->entries [i]);*/
                                if (obj) {
                                        mono_gc_weak_link_add (&(entries [i]), obj, track);
+                                       mono_gc_weak_link_remove (&(handles->entries [i]), track);
+                               } else {
+                                       g_assert (!handles->entries [i]);
                                }
                        }
                        g_free (handles->entries);
                        g_free (handles->domain_ids);
                        handles->entries = entries;
                        handles->domain_ids = domain_ids;
-                       mono_gc_enable ();
                }
 
                /* set i and slot to the next free position */
index 8a4be3ca320d68541212e1c955d51f5d503e9028..32eb869b20070fdae51789351c70c8527b727ecd 100644 (file)
@@ -386,7 +386,8 @@ BASE_TEST_CS_SRC=           \
        async-with-cb-throws.cs \
        appdomain-unload-doesnot-raise-pending-events.cs        \
        bug-6148.cs     \
-       assembly_append_ordering.cs
+       assembly_append_ordering.cs     \
+       bug-10127.cs
 
 TEST_CS_SRC_DIST=      \
        $(BASE_TEST_CS_SRC)     \
diff --git a/mono/tests/bug-10127.cs b/mono/tests/bug-10127.cs
new file mode 100644 (file)
index 0000000..4662ff6
--- /dev/null
@@ -0,0 +1,111 @@
+using System;
+using System.Threading;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace WeakReferenceTest
+{
+       public static class Cache {
+               static GCHandle[] table = new GCHandle[1024 * 1024];
+
+               public static T Probe<T>(int hc) where T : class
+               {
+                       int index = hc & (table.Length - 1);
+                       lock (table)
+                       {
+                               var wr = table[index];
+                               if (!wr.IsAllocated)
+                                       return null;
+                               return wr.Target as T;
+                       }
+               }
+
+               public static T Add<T>(T obj, int hc) where T : class 
+               {
+                       int index = hc & (table.Length - 1);
+                       lock (table)
+                       {
+                               table[index] = GCHandle.Alloc (obj, GCHandleType.Weak);
+                       }
+                       return obj;
+               }
+
+       }
+
+       public class Tester {
+               static readonly int seed = unchecked(DateTime.Now.Ticks.GetHashCode());
+
+               Random rand = new Random(seed);
+
+               bool alive;
+               Thread thread;
+
+               public void Start()
+               {
+                       alive = true;
+                       thread = new Thread(new ThreadStart(Work));
+                       thread.Start();
+               }
+
+               void Work()
+               {
+                       do {
+
+                               var item = rand.Next ();
+                               var probed = Cache.Probe<object>(item.GetHashCode());
+
+                               if (probed == null) {
+                                       Cache.Add<object>(item, item.GetHashCode());
+                               }
+
+                               if (rand.NextDouble() <= 0.1) {
+                                       GC.Collect();
+                               }
+
+                       } while (alive);
+               }
+
+               public void Stop ()
+               {
+                       alive = false;
+               }
+
+       }
+
+       static class RandHelper {
+               public static string RandString(this Random rand, int len)
+               {
+                       char[] table = new char[len];
+                       for (int idx = 0; idx < len; idx++) {
+                               table[idx] = (char) ('a' + idx);
+                       }
+                       return new string(table, 0, len);
+               }
+       }
+
+       class MainClass
+       {
+               public static void Main (string[] args)
+               {
+                       Console.WriteLine("Starting cache testers");
+                       List<Tester> testers = new List<Tester>();
+                       for (int count = 0; count < 10; count++) {
+                               testers.Add(new Tester());
+                       }
+
+                       foreach (var tester in testers) {
+                               tester.Start();
+                       }
+
+                       for (int i = 0; i < 4; ++i)
+                       {
+                               Thread.Sleep(TimeSpan.FromSeconds(1));
+                       }
+
+                       foreach (var tester in testers)
+                       {
+                               tester.Stop ();
+                       }
+               }
+       }
+}