[corlib] Avoid unbounded resizes of ephemeron arrays
authorVlad Brezae <brezaevlad@gmail.com>
Fri, 6 Jan 2017 00:46:29 +0000 (02:46 +0200)
committerVlad Brezae <brezaevlad@gmail.com>
Wed, 11 Jan 2017 11:21:59 +0000 (13:21 +0200)
We compute the size of the array based on how many live ephemerons it stores, and not based on the previous size of the array which doesn't indicate anything.

Fixes leak from #47388.

mcs/class/corlib/System.Runtime.CompilerServices/ConditionalWeakTable.cs

index 8574472acb11f694c3b854866c76ed54070c7f2a..6d212bd210e12c978f9727275ce2f36b2332594f 100644 (file)
@@ -72,12 +72,28 @@ namespace System.Runtime.CompilerServices
                {
                }
 
+               private void RecomputeSize ()
+               {
+                       size = 0;
+                       for (int i = 0; i < data.Length; i++) {
+                               if (data [i].key != null)
+                                       size++;
+                       }
+               }
+
                /*LOCKING: _lock must be held*/
-               void Rehash () {
-                       uint newSize = (uint)HashHelpers.GetPrime ((data.Length << 1) | 1);
-                       //Console.WriteLine ("--- resizing from {0} to {1}", data.Length, newSize);
+               private void Rehash ()
+               {
+                       // Size doesn't track elements that die without being removed. Before attempting
+                       // to rehash we traverse the array to see how many entries are left alive. We
+                       // rehash the array into a new one which has a capacity relative to the number of
+                       // live entries.
+                       RecomputeSize ();
+
+                       uint newLength = (uint)HashHelpers.GetPrime (((int)(size / LOAD_FACTOR) << 1) | 1);
+                       //Console.WriteLine ("--- resizing from {0} to {1}", data.Length, newLength);
 
-                       Ephemeron[] tmp = new Ephemeron [newSize];
+                       Ephemeron[] tmp = new Ephemeron [newLength];
                        GC.register_ephemeron_array (tmp);
                        size = 0;