summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Toub <stoub@microsoft.com>2019-02-11 21:34:21 -0500
committerJan Kotas <jkotas@microsoft.com>2019-02-11 18:34:21 -0800
commit8e5f65c4ec87cc58ea80c7473cb2946118c6ade9 (patch)
tree9452495a5c09d9ad1cdcc4efa677d53d753fac74
parent7dbf35b6f4961299d199fabc009e114bff7800f0 (diff)
downloadcoreclr-8e5f65c4ec87cc58ea80c7473cb2946118c6ade9.tar.gz
coreclr-8e5f65c4ec87cc58ea80c7473cb2946118c6ade9.tar.bz2
coreclr-8e5f65c4ec87cc58ea80c7473cb2946118c6ade9.zip
Move ConditionalWeakTable to shared (#22531)
Leaves DependentHandle as being the runtime PAL
-rw-r--r--src/System.Private.CoreLib/System.Private.CoreLib.csproj2
-rw-r--r--src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems1
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConditionalWeakTable.cs (renamed from src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs)453
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs85
4 files changed, 209 insertions, 332 deletions
diff --git a/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
index 97e89d3820..da4fdc6210 100644
--- a/src/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -235,7 +235,7 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimePropertyInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Resources\ManifestBasedResourceGroveler.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\RtType.cs" />
- <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ConditionalWeakTable.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\DependentHandle.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ICastableHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\jithelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeFeature.CoreCLR.cs" />
diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
index 251e9e8a3a..8e327eccf0 100644
--- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
@@ -524,6 +524,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilationRelaxationsAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilerGeneratedAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\CompilerGlobalScopeAttribute.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ConditionalWeakTable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ConfiguredAsyncDisposable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ConfiguredCancelableAsyncEnumerable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\ConfiguredValueTaskAwaitable.cs" />
diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConditionalWeakTable.cs
index f4e502adc5..19039224b4 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConditionalWeakTable.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConditionalWeakTable.cs
@@ -2,106 +2,58 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-/*============================================================
-**
-**
-** Description: Compiler support for runtime-generated "object fields."
-**
-** Lets DLR and other language compilers expose the ability to
-** attach arbitrary "properties" to instanced managed objects at runtime.
-**
-** We expose this support as a dictionary whose keys are the
-** instanced objects and the values are the "properties."
-**
-** Unlike a regular dictionary, ConditionalWeakTables will not
-** keep keys alive.
-**
-**
-** Lifetimes of keys and values:
-**
-** Inserting a key and value into the dictonary will not
-** prevent the key from dying, even if the key is strongly reachable
-** from the value.
-**
-** Prior to ConditionalWeakTable, the CLR did not expose
-** the functionality needed to implement this guarantee.
-**
-** Once the key dies, the dictionary automatically removes
-** the key/value entry.
-**
-**
-** Relationship between ConditionalWeakTable and Dictionary:
-**
-** ConditionalWeakTable mirrors the form and functionality
-** of the IDictionary interface for the sake of api consistency.
-**
-** Unlike Dictionary, ConditionalWeakTable is fully thread-safe
-** and requires no additional locking to be done by callers.
-**
-** ConditionalWeakTable defines equality as Object.ReferenceEquals().
-** ConditionalWeakTable does not invoke GetHashCode() overrides.
-**
-** It is not intended to be a general purpose collection
-** and it does not formally implement IDictionary or
-** expose the full public surface area.
-**
-**
-** Thread safety guarantees:
-**
-** ConditionalWeakTable is fully thread-safe and requires no
-** additional locking to be done by callers.
-**
-**
-** OOM guarantees:
-**
-** Will not corrupt unmanaged handle table on OOM. No guarantees
-** about managed weak table consistency. Native handles reclamation
-** may be delayed until appdomain shutdown.
-===========================================================*/
-
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Runtime.InteropServices;
using System.Threading;
using Internal.Runtime.CompilerServices;
namespace System.Runtime.CompilerServices
{
- #region ConditionalWeakTable
public sealed class ConditionalWeakTable<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
where TKey : class
where TValue : class
{
- #region Fields
+ // Lifetimes of keys and values:
+ // Inserting a key and value into the dictonary will not
+ // prevent the key from dying, even if the key is strongly reachable
+ // from the value. Once the key dies, the dictionary automatically removes
+ // the key/value entry.
+ //
+ // Thread safety guarantees:
+ // ConditionalWeakTable is fully thread-safe and requires no
+ // additional locking to be done by callers.
+ //
+ // OOM guarantees:
+ // Will not corrupt unmanaged handle table on OOM. No guarantees
+ // about managed weak table consistency. Native handles reclamation
+ // may be delayed until appdomain shutdown.
+
private const int InitialCapacity = 8; // Initial length of the table. Must be a power of two.
private readonly object _lock; // This lock protects all mutation of data in the table. Readers do not take this lock.
private volatile Container _container; // The actual storage for the table; swapped out as the table grows.
private int _activeEnumeratorRefCount; // The number of outstanding enumerators on the table
- #endregion
- #region Constructors
public ConditionalWeakTable()
{
_lock = new object();
_container = new Container(this);
}
- #endregion
- #region Public Members
- //--------------------------------------------------------------------------------------------
- // key: key of the value to find. Cannot be null.
- // value: if the key is found, contains the value associated with the key upon method return.
- // if the key is not found, contains default(TValue).
- //
- // Method returns "true" if key was found, "false" otherwise.
- //
- // Note: The key may get garbaged collected during the TryGetValue operation. If so, TryGetValue
- // may at its discretion, return "false" and set "value" to the default (as if the key was not present.)
- //--------------------------------------------------------------------------------------------
+ /// <summary>Gets the value of the specified key.</summary>
+ /// <param name="key">key of the value to find. Cannot be null.</param>
+ /// <param name="value">
+ /// If the key is found, contains the value associated with the key upon method return.
+ /// If the key is not found, contains default(TValue).
+ /// </param>
+ /// <returns>Returns "true" if key was found, "false" otherwise.</returns>
+ /// <remarks>
+ /// The key may get garbaged collected during the TryGetValue operation. If so, TryGetValue
+ /// may at its discretion, return "false" and set "value" to the default (as if the key was not present.)
+ /// </remarks>
public bool TryGetValue(TKey key, out TValue value)
{
- if (key == null)
+ if (key is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
@@ -109,27 +61,25 @@ namespace System.Runtime.CompilerServices
return _container.TryGetValueWorker(key, out value);
}
- //--------------------------------------------------------------------------------------------
- // key: key to add. May not be null.
- // value: value to associate with key.
- //
- // If the key is already entered into the dictionary, this method throws an exception.
- //
- // Note: The key may get garbage collected during the Add() operation. If so, Add()
- // has the right to consider any prior entries successfully removed and add a new entry without
- // throwing an exception.
- //--------------------------------------------------------------------------------------------
+ /// <summary>Adds a key to the table.</summary>
+ /// <param name="key">key to add. May not be null.</param>
+ /// <param name="value">value to associate with key.</param>
+ /// <remarks>
+ /// If the key is already entered into the dictionary, this method throws an exception.
+ /// The key may get garbage collected during the Add() operation. If so, Add()
+ /// has the right to consider any prior entries successfully removed and add a new entry without
+ /// throwing an exception.
+ /// </remarks>
public void Add(TKey key, TValue value)
{
- if (key == null)
+ if (key is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
lock (_lock)
{
- object otherValue;
- int entryIndex = _container.FindEntry(key, out otherValue);
+ int entryIndex = _container.FindEntry(key, out _);
if (entryIndex != -1)
{
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
@@ -139,23 +89,19 @@ namespace System.Runtime.CompilerServices
}
}
- //--------------------------------------------------------------------------------------------
- // key: key to add or update. May not be null.
- // value: value to associate with key.
- //
- // If the key is already entered into the dictionary, this method will update the value associated with key.
- //--------------------------------------------------------------------------------------------
+ /// <summary>Adds the key and value if the key doesn't exist, or updates the existing key's value if it does exist.</summary>
+ /// <param name="key">key to add or update. May not be null.</param>
+ /// <param name="value">value to associate with key.</param>
public void AddOrUpdate(TKey key, TValue value)
{
- if (key == null)
+ if (key is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
lock (_lock)
{
- object otherValue;
- int entryIndex = _container.FindEntry(key, out otherValue);
+ int entryIndex = _container.FindEntry(key, out _);
// if we found a key we should just update, if no we should create a new entry.
if (entryIndex != -1)
@@ -169,18 +115,17 @@ namespace System.Runtime.CompilerServices
}
}
- //--------------------------------------------------------------------------------------------
- // key: key to remove. May not be null.
- //
- // Returns true if the key is found and removed. Returns false if the key was not in the dictionary.
- //
- // Note: The key may get garbage collected during the Remove() operation. If so,
- // Remove() will not fail or throw, however, the return value can be either true or false
- // depending on who wins the race.
- //--------------------------------------------------------------------------------------------
+ /// <summary>Removes a key and its value from the table.</summary>
+ /// <param name="key">key to remove. May not be null.</param>
+ /// <returns>true if the key is found and removed. Returns false if the key was not in the dictionary.</returns>
+ /// <remarks>
+ /// The key may get garbage collected during the Remove() operation. If so,
+ /// Remove() will not fail or throw, however, the return value can be either true or false
+ /// depending on who wins the race.
+ /// </remarks>
public bool Remove(TKey key)
{
- if (key == null)
+ if (key is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
@@ -191,9 +136,7 @@ namespace System.Runtime.CompilerServices
}
}
- //--------------------------------------------------------------------------------------------
- // Clear all the key/value pairs
- //--------------------------------------------------------------------------------------------
+ /// <summary>Clear all the key/value pairs</summary>
public void Clear()
{
lock (_lock)
@@ -220,32 +163,31 @@ namespace System.Runtime.CompilerServices
}
}
- //--------------------------------------------------------------------------------------------
- // key: key of the value to find. Cannot be null.
- // createValueCallback: callback that creates value for key. Cannot be null.
- //
- // Atomically tests if key exists in table. If so, returns corresponding value. If not,
- // invokes createValueCallback() passing it the key. The returned value is bound to the key in the table
- // and returned as the result of GetValue().
- //
- // If multiple threads try to initialize the same key, the table may invoke createValueCallback
- // multiple times with the same key. Exactly one of these calls will succeed and the returned
- // value of that call will be the one added to the table and returned by all the racing GetValue() calls.
- //
- // This rule permits the table to invoke createValueCallback outside the internal table lock
- // to prevent deadlocks.
- //--------------------------------------------------------------------------------------------
+ /// <summary>
+ /// Atomically searches for a specified key in the table and returns the corresponding value.
+ /// If the key does not exist in the table, the method invokes a callback method to create a
+ /// value that is bound to the specified key.
+ /// </summary>
+ /// <param name="key">key of the value to find. Cannot be null.</param>
+ /// <param name="createValueCallback">callback that creates value for key. Cannot be null.</param>
+ /// <returns></returns>
+ /// <remarks>
+ /// If multiple threads try to initialize the same key, the table may invoke createValueCallback
+ /// multiple times with the same key. Exactly one of these calls will succeed and the returned
+ /// value of that call will be the one added to the table and returned by all the racing GetValue() calls.
+ /// This rule permits the table to invoke createValueCallback outside the internal table lock
+ /// to prevent deadlocks.
+ /// </remarks>
public TValue GetValue(TKey key, CreateValueCallback createValueCallback)
{
// key is validated by TryGetValue
- if (createValueCallback == null)
+ if (createValueCallback is null)
{
throw new ArgumentNullException(nameof(createValueCallback));
}
- TValue existingValue;
- return TryGetValue(key, out existingValue) ?
+ return TryGetValue(key, out TValue existingValue) ?
existingValue :
GetValueLocked(key, createValueCallback);
}
@@ -259,8 +201,7 @@ namespace System.Runtime.CompilerServices
lock (_lock)
{
// Now that we've taken the lock, must recheck in case we lost a race to add the key.
- TValue existingValue;
- if (_container.TryGetValueWorker(key, out existingValue))
+ if (_container.TryGetValueWorker(key, out TValue existingValue))
{
return existingValue;
}
@@ -273,32 +214,30 @@ namespace System.Runtime.CompilerServices
}
}
- //--------------------------------------------------------------------------------------------
- // key: key of the value to find. Cannot be null.
- //
- // Helper method to call GetValue without passing a creation delegate. Uses Activator.CreateInstance
- // to create new instances as needed. If TValue does not have a default constructor, this will
- // throw.
- //--------------------------------------------------------------------------------------------
-
+ /// <summary>
+ /// Helper method to call GetValue without passing a creation delegate. Uses Activator.CreateInstance
+ /// to create new instances as needed. If TValue does not have a default constructor, this will throw.
+ /// </summary>
+ /// <param name="key">key of the value to find. Cannot be null.</param>
public TValue GetOrCreateValue(TKey key) => GetValue(key, _ => Activator.CreateInstance<TValue>());
public delegate TValue CreateValueCallback(TKey key);
- //--------------------------------------------------------------------------------------------
- // Gets an enumerator for the table. The returned enumerator will not extend the lifetime of
- // any object pairs in the table, other than the one that's Current. It will not return entries
- // that have already been collected, nor will it return entries added after the enumerator was
- // retrieved. It may not return all entries that were present when the enumerat was retrieved,
- // however, such as not returning entries that were collected or removed after the enumerator
- // was retrieved but before they were enumerated.
- //--------------------------------------------------------------------------------------------
+ /// <summary>Gets an enumerator for the table.</summary>
+ /// <remarks>
+ /// The returned enumerator will not extend the lifetime of
+ /// any object pairs in the table, other than the one that's Current. It will not return entries
+ /// that have already been collected, nor will it return entries added after the enumerator was
+ /// retrieved. It may not return all entries that were present when the enumerat was retrieved,
+ /// however, such as not returning entries that were collected or removed after the enumerator
+ /// was retrieved but before they were enumerated.
+ /// </remarks>
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
lock (_lock)
{
Container c = _container;
- return c == null || c.FirstFreeEntry == 0 ?
+ return c is null || c.FirstFreeEntry == 0 ?
((IEnumerable<KeyValuePair<TKey, TValue>>)Array.Empty<KeyValuePair<TKey, TValue>>()).GetEnumerator() :
new Enumerator(this);
}
@@ -347,7 +286,7 @@ namespace System.Runtime.CompilerServices
_currentIndex = -1;
}
- ~Enumerator() { Dispose(); }
+ ~Enumerator() => Dispose();
public void Dispose()
{
@@ -394,9 +333,7 @@ namespace System.Runtime.CompilerServices
while (_currentIndex < _maxIndexInclusive)
{
_currentIndex++;
- TKey key;
- TValue value;
- if (c.TryGetEntry(_currentIndex, out key, out value))
+ if (c.TryGetEntry(_currentIndex, out TKey key, out TValue value))
{
_current = new KeyValuePair<TKey, TValue>(key, value);
return true;
@@ -427,21 +364,13 @@ namespace System.Runtime.CompilerServices
public void Reset() { }
}
- #endregion
-
- #region Private Members
-
- //----------------------------------------------------------------------------------------
- // Worker for adding a new key/value pair.
- // Will resize the container if it is full
- //
- // Preconditions:
- // Must hold _lock.
- // Key already validated as non-null and not already in table.
- //----------------------------------------------------------------------------------------
+ /// <summary>Worker for adding a new key/value pair. Will resize the container if it is full.</summary>
+ /// <param name="key"></param>
+ /// <param name="value"></param>
private void CreateEntry(TKey key, TValue value)
{
Debug.Assert(Monitor.IsEntered(_lock));
+ Debug.Assert(key != null); // key already validated as non-null and not already in table.
Container c = _container;
if (!c.HasCapacity)
@@ -453,9 +382,6 @@ namespace System.Runtime.CompilerServices
private static bool IsPowerOfTwo(int value) => (value > 0) && ((value & (value - 1)) == 0);
- #endregion
-
- #region Private Data Members
//--------------------------------------------------------------------------------------------
// Entry can be in one of four states:
//
@@ -470,7 +396,7 @@ namespace System.Runtime.CompilerServices
// next links to next Entry in bucket.
//
// - Used with dead key (linked into a bucket list where _buckets[hashCode & (_buckets.Length - 1)] points to first entry)
- // depHnd.IsAllocated == true, depHnd.GetPrimary() == null
+ // depHnd.IsAllocated == true, depHnd.GetPrimary() is null
// hashCode == <notcare>
// next links to next Entry in bucket.
//
@@ -495,11 +421,11 @@ namespace System.Runtime.CompilerServices
public int Next; // Index of next entry, -1 if last
}
- //
- // Container holds the actual data for the table. A given instance of Container always has the same capacity. When we need
- // more capacity, we create a new Container, copy the old one into the new one, and discard the old one. This helps enable lock-free
- // reads from the table, as readers never need to deal with motion of entries due to rehashing.
- //
+ /// <summary>
+ /// Container holds the actual data for the table. A given instance of Container always has the same capacity. When we need
+ /// more capacity, we create a new Container, copy the old one into the new one, and discard the old one. This helps enable lock-free
+ /// reads from the table, as readers never need to deal with motion of entries due to rehashing.
+ /// </summary>
private sealed class Container
{
private readonly ConditionalWeakTable<TKey, TValue> _parent; // the ConditionalWeakTable with which this container is associated
@@ -548,11 +474,7 @@ namespace System.Runtime.CompilerServices
internal int FirstFreeEntry => _firstFreeEntry;
- //----------------------------------------------------------------------------------------
- // Worker for adding a new key/value pair.
- // Preconditions:
- // Container must NOT be full
- //----------------------------------------------------------------------------------------
+ /// <summary>Worker for adding a new key/value pair. Container must NOT be full.</summary>
internal void CreateEntryNoResize(TKey key, TValue value)
{
Debug.Assert(HasCapacity);
@@ -575,30 +497,24 @@ namespace System.Runtime.CompilerServices
_invalid = false;
}
- //----------------------------------------------------------------------------------------
- // Worker for finding a key/value pair
- //
- // Preconditions:
- // Must hold _lock.
- // Key already validated as non-null
- //----------------------------------------------------------------------------------------
+ /// <summary>Worker for finding a key/value pair. Must hold _lock.</summary>
internal bool TryGetValueWorker(TKey key, out TValue value)
{
- object secondary;
- int entryIndex = FindEntry(key, out secondary);
+ Debug.Assert(key != null); // Key already validated as non-null
+
+ int entryIndex = FindEntry(key, out object secondary);
value = Unsafe.As<TValue>(secondary);
return entryIndex != -1;
}
- //----------------------------------------------------------------------------------------
- // Returns -1 if not found (if key expires during FindEntry, this can be treated as "not found.")
- //
- // Preconditions:
- // Must hold _lock, or be prepared to retry the search while holding _lock.
- // Key already validated as non-null.
- //----------------------------------------------------------------------------------------
+ /// <summary>
+ /// Returns -1 if not found (if key expires during FindEntry, this can be treated as "not found.").
+ /// Must hold _lock, or be prepared to retry the search while holding _lock.
+ /// </summary>
internal int FindEntry(TKey key, out object value)
{
+ Debug.Assert(key != null); // Key already validated as non-null.
+
int hashCode = RuntimeHelpers.GetHashCode(key) & int.MaxValue;
int bucket = hashCode & (_buckets.Length - 1);
for (int entriesIndex = Volatile.Read(ref _buckets[bucket]); entriesIndex != -1; entriesIndex = _entries[entriesIndex].Next)
@@ -615,15 +531,12 @@ namespace System.Runtime.CompilerServices
return -1;
}
- //----------------------------------------------------------------------------------------
- // Gets the entry at the specified entry index.
- //----------------------------------------------------------------------------------------
+ /// <summary>Gets the entry at the specified entry index.</summary>
internal bool TryGetEntry(int index, out TKey key, out TValue value)
{
if (index < _entries.Length)
{
- object oKey, oValue;
- oKey = _entries[index].depHnd.GetPrimaryAndSecondary(out oValue);
+ object oKey = _entries[index].depHnd.GetPrimaryAndSecondary(out object oValue);
GC.KeepAlive(this); // ensure we don't get finalized while accessing DependentHandles.
if (oKey != null)
@@ -639,9 +552,7 @@ namespace System.Runtime.CompilerServices
return false;
}
- //----------------------------------------------------------------------------------------
- // Removes all of the keys in the table.
- //----------------------------------------------------------------------------------------
+ /// <summary>Removes all of the keys in the table.</summary>
internal void RemoveAllKeys()
{
for (int i = 0; i < _firstFreeEntry; i++)
@@ -650,15 +561,12 @@ namespace System.Runtime.CompilerServices
}
}
- //----------------------------------------------------------------------------------------
- // Removes the specified key from the table, if it exists.
- //----------------------------------------------------------------------------------------
+ /// <summary>Removes the specified key from the table, if it exists.</summary>
internal bool Remove(TKey key)
{
VerifyIntegrity();
- object value;
- int entryIndex = FindEntry(key, out value);
+ int entryIndex = FindEntry(key, out _);
if (entryIndex != -1)
{
RemoveIndex(entryIndex);
@@ -695,15 +603,10 @@ namespace System.Runtime.CompilerServices
_invalid = false;
}
- //----------------------------------------------------------------------------------------
- // This does two things: resize and scrub expired keys off bucket lists.
- //
- // Precondition:
- // Must hold _lock.
- //
- // Postcondition:
- // _firstEntry is less than _entries.Length on exit, that is, the table has at least one free entry.
- //----------------------------------------------------------------------------------------
+ /// <summary>Resize, and scrub expired keys off bucket lists. Must hold _lock.</summary>
+ /// <remarks>
+ /// _firstEntry is less than _entries.Length on exit, that is, the table has at least one free entry.
+ /// </remarks>
internal Container Resize()
{
Debug.Assert(!HasCapacity);
@@ -711,7 +614,7 @@ namespace System.Runtime.CompilerServices
bool hasExpiredEntries = false;
int newSize = _buckets.Length;
- if (_parent == null || _parent._activeEnumeratorRefCount == 0)
+ if (_parent is null || _parent._activeEnumeratorRefCount == 0)
{
// If any expired or removed keys exist, we won't resize.
// If there any active enumerators, though, we don't want
@@ -727,7 +630,7 @@ namespace System.Runtime.CompilerServices
break;
}
- if (entry.depHnd.IsAllocated && entry.depHnd.GetPrimary() == null)
+ if (entry.depHnd.IsAllocated && entry.depHnd.GetPrimary() is null)
{
// the entry has expired
hasExpiredEntries = true;
@@ -834,10 +737,6 @@ namespace System.Runtime.CompilerServices
return newContainer;
}
- //----------------------------------------------------------------------------------------
- // Precondition:
- // Must hold _lock.
- //----------------------------------------------------------------------------------------
private void VerifyIntegrity()
{
if (_invalid)
@@ -846,16 +745,13 @@ namespace System.Runtime.CompilerServices
}
}
- //----------------------------------------------------------------------------------------
- // Finalizer.
- //----------------------------------------------------------------------------------------
~Container()
{
// We're just freeing per-appdomain unmanaged handles here. If we're already shutting down the AD,
// don't bother. (Despite its name, Environment.HasShutdownStart also returns true if the current
// AD is finalizing.) We also skip doing anything if the container is invalid, including if someone
// the container object was allocated but its associated table never set.
- if (Environment.HasShutdownStarted || _invalid || _parent == null)
+ if (Environment.HasShutdownStarted || _invalid || _parent is null)
{
return;
}
@@ -895,7 +791,7 @@ namespace System.Runtime.CompilerServices
// to another container that replaced this one), then it should be freed.
// - If this container had the entry removed, then even if in general ownership was transferred to
// another container, removed entries are not, therefore this container must free them.
- if (_oldKeepAlive == null || entries[entriesIndex].HashCode == -1)
+ if (_oldKeepAlive is null || entries[entriesIndex].HashCode == -1)
{
entries[entriesIndex].depHnd.Free();
}
@@ -903,110 +799,5 @@ namespace System.Runtime.CompilerServices
}
}
}
- #endregion
}
- #endregion
-
- #region DependentHandle
- //=========================================================================================
- // This struct collects all operations on native DependentHandles. The DependentHandle
- // merely wraps an IntPtr so this struct serves mainly as a "managed typedef."
- //
- // DependentHandles exist in one of two states:
- //
- // IsAllocated == false
- // No actual handle is allocated underneath. Illegal to call GetPrimary
- // or GetPrimaryAndSecondary(). Ok to call Free().
- //
- // Initializing a DependentHandle using the nullary ctor creates a DependentHandle
- // that's in the !IsAllocated state.
- // (! Right now, we get this guarantee for free because (IntPtr)0 == NULL unmanaged handle.
- // ! If that assertion ever becomes false, we'll have to add an _isAllocated field
- // ! to compensate.)
- //
- //
- // IsAllocated == true
- // There's a handle allocated underneath. You must call Free() on this eventually
- // or you cause a native handle table leak.
- //
- // This struct intentionally does no self-synchronization. It's up to the caller to
- // to use DependentHandles in a thread-safe way.
- //=========================================================================================
- internal struct DependentHandle
- {
- #region Constructors
- public DependentHandle(object primary, object secondary)
- {
- // no need to check for null result: nInitialize expected to throw OOM.
- _handle = nInitialize(primary, secondary);
- }
- #endregion
-
- #region Public Members
- public bool IsAllocated => _handle != IntPtr.Zero;
-
- // Getting the secondary object is more expensive than getting the first so
- // we provide a separate primary-only accessor for those times we only want the
- // primary.
- public object GetPrimary()
- {
- return nGetPrimary(_handle);
- }
-
- public object GetPrimaryAndSecondary(out object secondary)
- {
- return nGetPrimaryAndSecondary(_handle, out secondary);
- }
-
- public void SetPrimary(object primary)
- {
- nSetPrimary(_handle, primary);
- }
-
- public void SetSecondary(object secondary)
- {
- nSetSecondary(_handle, secondary);
- }
-
- //----------------------------------------------------------------------
- // Forces dependentHandle back to non-allocated state (if not already there)
- // and frees the handle if needed.
- //----------------------------------------------------------------------
- public void Free()
- {
- if (_handle != (IntPtr)0)
- {
- IntPtr handle = _handle;
- _handle = (IntPtr)0;
- nFree(handle);
- }
- }
- #endregion
-
- #region Private Members
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern IntPtr nInitialize(object primary, object secondary);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object nGetPrimary(IntPtr dependentHandle);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern object nGetPrimaryAndSecondary(IntPtr dependentHandle, out object secondary);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void nSetPrimary(IntPtr dependentHandle, object primary);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void nSetSecondary(IntPtr dependentHandle, object secondary);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern void nFree(IntPtr dependentHandle);
- #endregion
-
- #region Private Data Member
- private IntPtr _handle;
- #endregion
-
- } // struct DependentHandle
- #endregion
}
diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs
new file mode 100644
index 0000000000..35fa0780be
--- /dev/null
+++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/DependentHandle.cs
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ //=========================================================================================
+ // This struct collects all operations on native DependentHandles. The DependentHandle
+ // merely wraps an IntPtr so this struct serves mainly as a "managed typedef."
+ //
+ // DependentHandles exist in one of two states:
+ //
+ // IsAllocated == false
+ // No actual handle is allocated underneath. Illegal to call GetPrimary
+ // or GetPrimaryAndSecondary(). Ok to call Free().
+ //
+ // Initializing a DependentHandle using the nullary ctor creates a DependentHandle
+ // that's in the !IsAllocated state.
+ // (! Right now, we get this guarantee for free because (IntPtr)0 == NULL unmanaged handle.
+ // ! If that assertion ever becomes false, we'll have to add an _isAllocated field
+ // ! to compensate.)
+ //
+ //
+ // IsAllocated == true
+ // There's a handle allocated underneath. You must call Free() on this eventually
+ // or you cause a native handle table leak.
+ //
+ // This struct intentionally does no self-synchronization. It's up to the caller to
+ // to use DependentHandles in a thread-safe way.
+ //=========================================================================================
+ internal struct DependentHandle
+ {
+ private IntPtr _handle;
+
+ public DependentHandle(object primary, object secondary) =>
+ // no need to check for null result: nInitialize expected to throw OOM.
+ _handle = nInitialize(primary, secondary);
+
+ public bool IsAllocated => _handle != IntPtr.Zero;
+
+ // Getting the secondary object is more expensive than getting the first so
+ // we provide a separate primary-only accessor for those times we only want the
+ // primary.
+ public object GetPrimary() => nGetPrimary(_handle);
+
+ public object GetPrimaryAndSecondary(out object secondary) =>
+ nGetPrimaryAndSecondary(_handle, out secondary);
+
+ public void SetPrimary(object primary) =>
+ nSetPrimary(_handle, primary);
+
+ public void SetSecondary(object secondary) =>
+ nSetSecondary(_handle, secondary);
+
+ // Forces dependentHandle back to non-allocated state (if not already there)
+ // and frees the handle if needed.
+ public void Free()
+ {
+ if (_handle != IntPtr.Zero)
+ {
+ IntPtr handle = _handle;
+ _handle = IntPtr.Zero;
+ nFree(handle);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern IntPtr nInitialize(object primary, object secondary);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern object nGetPrimary(IntPtr dependentHandle);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern object nGetPrimaryAndSecondary(IntPtr dependentHandle, out object secondary);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern void nSetPrimary(IntPtr dependentHandle, object primary);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern void nSetSecondary(IntPtr dependentHandle, object secondary);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern void nFree(IntPtr dependentHandle);
+ }
+}