Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormonojenkins <jo.shields+jenkins@xamarin.com>2018-09-19 19:16:35 +0300
committerLudovic Henry <luhenry@microsoft.com>2018-09-19 19:16:35 +0300
commitadbb8f72c8b44c3f69ca1fd074d286efc7acbbdd (patch)
treef39f1a56f0d335e3697a5ad96df71ede305d5a99
parent1b18f39e46eac0c38438bc8cfdaef8a4bc8afc18 (diff)
[2018-06] Implement IEnumerable for ConditionalWeakTable (#10657)
* Implement IEnumerable for ConditionalWeakTable * Update ConditionalWeakTable.cs
-rw-r--r--mcs/class/corlib/System.Runtime.CompilerServices/ConditionalWeakTable.cs102
-rw-r--r--mcs/class/corlib/Test/System.Runtime.CompilerServices/ConditionalWeakTableTest.cs15
2 files changed, 116 insertions, 1 deletions
diff --git a/mcs/class/corlib/System.Runtime.CompilerServices/ConditionalWeakTable.cs b/mcs/class/corlib/System.Runtime.CompilerServices/ConditionalWeakTable.cs
index 17474df4f97..aa37ecc4249 100644
--- a/mcs/class/corlib/System.Runtime.CompilerServices/ConditionalWeakTable.cs
+++ b/mcs/class/corlib/System.Runtime.CompilerServices/ConditionalWeakTable.cs
@@ -31,6 +31,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
namespace System.Runtime.CompilerServices
{
@@ -375,11 +377,109 @@ namespace System.Runtime.CompilerServices
}
}
+ // IEnumerable implementation was copied from CoreCLR
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator ()
{
- throw new NotImplementedException ();
+ lock (_lock)
+ {
+ return size == 0 ?
+ ((IEnumerable<KeyValuePair<TKey, TValue>>)Array.Empty<KeyValuePair<TKey, TValue>>()).GetEnumerator() :
+ new Enumerator(this);
+ }
}
IEnumerator IEnumerable.GetEnumerator () => ((IEnumerable<KeyValuePair<TKey, TValue>>)this).GetEnumerator ();
+
+ /// <summary>Provides an enumerator for the table.</summary>
+ private sealed class Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
+ {
+ // The enumerator would ideally hold a reference to the Container and the end index within that
+ // container. However, the safety of the CWT depends on the only reference to the Container being
+ // from the CWT itself; the Container then employs a two-phase finalization scheme, where the first
+ // phase nulls out that parent CWT's reference, guaranteeing that the second time it's finalized there
+ // can be no other existing references to it in use that would allow for concurrent usage of the
+ // native handles with finalization. We would break that if we allowed this Enumerator to hold a
+ // reference to the Container. Instead, the Enumerator holds a reference to the CWT rather than to
+ // the Container, and it maintains the CWT._activeEnumeratorRefCount field to track whether there
+ // are outstanding enumerators that have yet to be disposed/finalized. If there aren't any, the CWT
+ // behaves as it normally does. If there are, certain operations are affected, in particular resizes.
+ // Normally when the CWT is resized, it enumerates the contents of the table looking for indices that
+ // contain entries which have been collected or removed, and it frees those up, effectively moving
+ // down all subsequent entries in the container (not in the existing container, but in a replacement).
+ // This, however, would cause the enumerator's understanding of indices to break. So, as long as
+ // there is any outstanding enumerator, no compaction is performed.
+
+ private ConditionalWeakTable<TKey, TValue> _table; // parent table, set to null when disposed
+ private int _currentIndex = -1; // the current index into the container
+ private KeyValuePair<TKey, TValue> _current; // the current entry set by MoveNext and returned from Current
+
+ public Enumerator(ConditionalWeakTable<TKey, TValue> table)
+ {
+ // Store a reference to the parent table and increase its active enumerator count.
+ _table = table;
+ _currentIndex = -1;
+ }
+
+ ~Enumerator() { Dispose(); }
+
+ public void Dispose()
+ {
+ // Use an interlocked operation to ensure that only one thread can get access to
+ // the _table for disposal and thus only decrement the ref count once.
+ ConditionalWeakTable<TKey, TValue> table = Interlocked.Exchange(ref _table, null);
+ if (table != null)
+ {
+ // Ensure we don't keep the last current alive unnecessarily
+ _current = default;
+
+ // Finalization is purely to decrement the ref count. We can suppress it now.
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ public bool MoveNext()
+ {
+ // Start by getting the current table. If it's already been disposed, it will be null.
+ ConditionalWeakTable<TKey, TValue> table = _table;
+ if (table != null)
+ {
+ // Once have the table, we need to lock to synchronize with other operations on
+ // the table, like adding.
+ lock (table._lock)
+ {
+ var tombstone = GC.EPHEMERON_TOMBSTONE;
+ while (_currentIndex < table.data.Length - 1)
+ {
+ _currentIndex++;
+ var currentDataItem = table.data[_currentIndex];
+ if (currentDataItem.key != null && currentDataItem.key != tombstone)
+ {
+ _current = new KeyValuePair<TKey, TValue>((TKey)currentDataItem.key, (TValue)currentDataItem.value);
+ return true;
+ }
+ }
+ }
+ }
+
+ // Nothing more to enumerate.
+ return false;
+ }
+
+ public KeyValuePair<TKey, TValue> Current
+ {
+ get
+ {
+ if (_currentIndex < 0)
+ {
+ ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumOpCantHappen();
+ }
+ return _current;
+ }
+ }
+
+ object IEnumerator.Current => Current;
+
+ public void Reset() { }
+ }
}
}
diff --git a/mcs/class/corlib/Test/System.Runtime.CompilerServices/ConditionalWeakTableTest.cs b/mcs/class/corlib/Test/System.Runtime.CompilerServices/ConditionalWeakTableTest.cs
index 1db16372c72..a016888ca22 100644
--- a/mcs/class/corlib/Test/System.Runtime.CompilerServices/ConditionalWeakTableTest.cs
+++ b/mcs/class/corlib/Test/System.Runtime.CompilerServices/ConditionalWeakTableTest.cs
@@ -29,6 +29,7 @@
using NUnit.Framework;
using System;
+using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -486,6 +487,20 @@ namespace MonoTests.System.Runtime.CompilerServices {
Assert.IsTrue (table.Remove (key), "#2-" + i + "-k-" + key);
}
}
+
+ [Test]
+ public void ConditionalWeakTableEnumerable()
+ {
+ var cwt = new ConditionalWeakTable<string, string>();
+ Assert.AreEqual(0, cwt.ToArray().Length);
+ cwt.Add("test1", "foo1");
+ cwt.Add("test2", "foo2");
+ Assert.AreEqual(2, cwt.ToArray().Length);
+ cwt.Remove("test1");
+ Assert.AreEqual(1, cwt.ToArray().Length);
+ cwt.Remove("test2");
+ Assert.AreEqual(0, cwt.ToArray().Length);
+ }
}
}