diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs deleted file mode 100644 index e7efdad252..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs +++ /dev/null @@ -1,263 +0,0 @@ -// 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. - - -using System; -using System.Diagnostics; -using System.Threading; - -namespace Microsoft.Data.ProviderBase -{ - internal abstract class DbReferenceCollection - { - private struct CollectionEntry - { - private int _tag; // information about the reference - private WeakReference _weak; // the reference itself. - - public void NewTarget(int tag, object target) - { - Debug.Assert(!TryGetTarget(out object _) , "Entry already has a valid target"); - Debug.Assert(tag != 0, "Bad tag"); - Debug.Assert(target != null, "Invalid target"); - - if (_weak == null) - { - _weak = new WeakReference(target, false); - } - else - { - _weak.SetTarget(target); - } - _tag = tag; - } - - public void RemoveTarget() - { - _tag = 0; - _weak.SetTarget(null); - } - - public int Tag => _tag; - - public bool TryGetTarget(out object target) - { - target = null; - return _tag != 0 && _weak.TryGetTarget(out target); - } - } - - private const int LockPollTime = 100; // Time to wait (in ms) between attempting to get the _itemLock - private const int DefaultCollectionSize = 20; // Default size for the collection, and the amount to grow every time the collection is full - private CollectionEntry[] _items; // The collection of items we are keeping track of - private readonly object _itemLock; // Used to synchronize access to the _items collection - private int _optimisticCount; // (#ItemsAdded - #ItemsRemoved) - This estimates the number of items that we *should* have (but doesn't take into account item targets being GC'd) - private int _lastItemIndex; // Location of the last item in _items - private volatile bool _isNotifying; // Indicates that the collection is currently being notified (and, therefore, about to be cleared) - - protected DbReferenceCollection() - { - _items = new CollectionEntry[DefaultCollectionSize]; - _itemLock = new object(); - _optimisticCount = 0; - _lastItemIndex = 0; - } - - abstract public void Add(object value, int tag); - - protected void AddItem(object value, int tag) - { - Debug.Assert(null != value && 0 != tag, "AddItem with null value or 0 tag"); - bool itemAdded = false; - - lock (_itemLock) - { - // Try to find a free spot - for (int i = 0; i <= _lastItemIndex; ++i) - { - if (_items[i].Tag == 0) - { - _items[i].NewTarget(tag, value); - Debug.Assert(_items[i].TryGetTarget(out object _), "missing expected target"); - itemAdded = true; - break; - } - } - - // No free spots, can we just add on to the end? - if ((!itemAdded) && (_lastItemIndex + 1 < _items.Length)) - { - _lastItemIndex++; - _items[_lastItemIndex].NewTarget(tag, value); - itemAdded = true; - } - - // If no free spots and no space at the end, try to find a dead item - if (!itemAdded) - { - for (int i = 0; i <= _lastItemIndex; ++i) - { - if (!_items[i].TryGetTarget(out object _)) - { - _items[i].NewTarget(tag, value); - Debug.Assert(_items[i].TryGetTarget(out object _), "missing expected target"); - itemAdded = true; - break; - } - } - } - - // If nothing was free, then resize and add to the end - if (!itemAdded) - { - Array.Resize(ref _items, _items.Length * 2); - _lastItemIndex++; - _items[_lastItemIndex].NewTarget(tag, value); - } - - _optimisticCount++; - } - } - - internal T FindItem(int tag, Func filterMethod) where T : class - { - bool lockObtained = false; - try - { - TryEnterItemLock(ref lockObtained); - if (lockObtained) - { - if (_optimisticCount > 0) - { - for (int counter = 0; counter <= _lastItemIndex; counter++) - { - // Check tag (should be easiest and quickest) - if (_items[counter].Tag == tag) - { - if (_items[counter].TryGetTarget(out object value)) - { - // Make sure the item has the correct type and passes the filtering - if (value is T tempItem && filterMethod(tempItem)) - { - return tempItem; - } - } - } - } - } - } - } - finally - { - ExitItemLockIfNeeded(lockObtained); - } - - // If we got to here, then no item was found, so return null - return null; - } - - public void Notify(int message) - { - bool lockObtained = false; - try - { - TryEnterItemLock(ref lockObtained); - if (lockObtained) - { - try - { - _isNotifying = true; - - // Loop through each live item and notify it - if (_optimisticCount > 0) - { - for (int index = 0; index <= _lastItemIndex; ++index) - { - if (_items[index].TryGetTarget(out object value)) - { - NotifyItem(message, _items[index].Tag, value); - _items[index].RemoveTarget(); - } - Debug.Assert(!_items[index].TryGetTarget(out object _), "Unexpected target after notifying"); - } - _optimisticCount = 0; - } - - // Shrink collection (if needed) - if (_items.Length > 100) - { - _lastItemIndex = 0; - _items = new CollectionEntry[DefaultCollectionSize]; - } - } - finally - { - _isNotifying = false; - } - } - } - finally - { - ExitItemLockIfNeeded(lockObtained); - } - } - - abstract protected void NotifyItem(int message, int tag, object value); - - abstract public void Remove(object value); - - protected void RemoveItem(object value) - { - Debug.Assert(null != value, "RemoveItem with null"); - - bool lockObtained = false; - try - { - TryEnterItemLock(ref lockObtained); - - if (lockObtained) - { - // Find the value, and then remove the target from our collection - if (_optimisticCount > 0) - { - for (int index = 0; index <= _lastItemIndex; ++index) - { - if (_items[index].TryGetTarget(out object target) && value == target) - { - _items[index].RemoveTarget(); - _optimisticCount--; - break; - } - } - } - } - } - finally - { - ExitItemLockIfNeeded(lockObtained); - } - } - - // This is polling lock that will abandon getting the lock if _isNotifying is set to true - private void TryEnterItemLock(ref bool lockObtained) - { - // Assume that we couldn't take the lock - lockObtained = false; - // Keep trying to take the lock until either we've taken it, or the collection is being notified - while ((!_isNotifying) && (!lockObtained)) - { - Monitor.TryEnter(_itemLock, LockPollTime, ref lockObtained); - } - } - - private void ExitItemLockIfNeeded(bool lockObtained) - { - if (lockObtained) - { - Monitor.Exit(_itemLock); - } - } - } -} - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index eb15ff1e1a..48ab208ceb 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -519,6 +519,9 @@ Common\System\Diagnostics\CodeAnalysis.cs + + Microsoft\Data\ProviderBase\DbReferenceCollection.cs + @@ -616,9 +619,6 @@ Common\Microsoft\Data\ProviderBase\DbConnectionInternal.cs - - Common\Microsoft\Data\ProviderBase\DbReferenceCollection.cs - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 0a0757731b..917c401e90 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -610,6 +610,9 @@ Resources\ResDescriptionAttribute.cs + + Microsoft\Data\ProviderBase\DbReferenceCollection.cs + @@ -631,7 +634,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs similarity index 69% rename from src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs index 1e69bcaba9..35356bfa12 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs @@ -2,74 +2,90 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Diagnostics; +using System.Threading; + namespace Microsoft.Data.ProviderBase { - - using System; - using System.Diagnostics; - using System.Threading; - internal abstract class DbReferenceCollection { + #region Constants + // Time to wait (in ms) between attempting to get the _itemLock + private const int LockPollTime = 100; + + // Default size for the collection, and the amount to grow every time the collection is full + private const int DefaultCollectionSize = 20; + #endregion + + #region Fields + // The collection of items we are keeping track of + private CollectionEntry[] _items; + + // Used to synchronize access to the _items collection + private readonly object _itemLock; + + // (#ItemsAdded - #ItemsRemoved) - This estimates the number of items that we should have + // (but doesn't take into account item targets being GC'd) + private int _estimatedCount; + + // Location of the last item in _items + private int _lastItemIndex; + + // Indicates that the collection is currently being notified (and, therefore, about to be cleared) + private volatile bool _isNotifying; + #endregion private struct CollectionEntry { - private int _tag; // information about the reference - private WeakReference _weak; // the reference itself. + private int _refInfo; // information about the reference + private WeakReference _weakReference; // the reference itself. - public void NewTarget(int tag, object target) + public void SetNewTarget(int refInfo, object target) { Debug.Assert(!TryGetTarget(out object _), "Entry already has a valid target"); - Debug.Assert(tag != 0, "Bad tag"); + Debug.Assert(refInfo != 0, "Bad reference info"); Debug.Assert(target != null, "Invalid target"); - if (_weak == null) + if (_weakReference == null) { - _weak = new WeakReference(target, false); + _weakReference = new WeakReference(target, false); } else { - _weak.SetTarget(target); + _weakReference.SetTarget(target); } - _tag = tag; + _refInfo = refInfo; } public void RemoveTarget() { - _tag = 0; - _weak.SetTarget(null); + _refInfo = 0; + _weakReference.SetTarget(null); } - public int Tag => _tag; + public readonly int RefInfo => _refInfo; - public bool TryGetTarget(out object target) + public readonly bool TryGetTarget(out object target) { target = null; - return _tag != 0 && _weak.TryGetTarget(out target); + return _refInfo != 0 && _weakReference.TryGetTarget(out target); } } - private const int LockPollTime = 100; // Time to wait (in ms) between attempting to get the _itemLock - private const int DefaultCollectionSize = 20; // Default size for the collection, and the amount to grow every time the collection is full - private CollectionEntry[] _items; // The collection of items we are keeping track of - private readonly object _itemLock; // Used to synchronize access to the _items collection - private int _optimisticCount; // (#ItemsAdded - #ItemsRemoved) - This estimates the number of items that we *should* have (but doesn't take into account item targets being GC'd) - private int _lastItemIndex; // Location of the last item in _items - private volatile bool _isNotifying; // Indicates that the collection is currently being notified (and, therefore, about to be cleared) - protected DbReferenceCollection() { _items = new CollectionEntry[DefaultCollectionSize]; _itemLock = new object(); - _optimisticCount = 0; + _estimatedCount = 0; _lastItemIndex = 0; } - abstract public void Add(object value, int tag); + abstract public void Add(object value, int refInfo); - protected void AddItem(object value, int tag) + protected void AddItem(object value, int refInfo) { - Debug.Assert(null != value && 0 != tag, "AddItem with null value or 0 tag"); + Debug.Assert(value != null && 0 != refInfo, "AddItem with null value or 0 reference info"); bool itemAdded = false; lock (_itemLock) @@ -77,9 +93,9 @@ protected void AddItem(object value, int tag) // Try to find a free spot for (int i = 0; i <= _lastItemIndex; ++i) { - if (_items[i].Tag == 0) + if (_items[i].RefInfo == 0) { - _items[i].NewTarget(tag, value); + _items[i].SetNewTarget(refInfo, value); Debug.Assert(_items[i].TryGetTarget(out object _), "missing expected target"); itemAdded = true; break; @@ -90,7 +106,7 @@ protected void AddItem(object value, int tag) if ((!itemAdded) && (_lastItemIndex + 1 < _items.Length)) { _lastItemIndex++; - _items[_lastItemIndex].NewTarget(tag, value); + _items[_lastItemIndex].SetNewTarget(refInfo, value); itemAdded = true; } @@ -101,7 +117,7 @@ protected void AddItem(object value, int tag) { if (!_items[i].TryGetTarget(out object _)) { - _items[i].NewTarget(tag, value); + _items[i].SetNewTarget(refInfo, value); Debug.Assert(_items[i].TryGetTarget(out object _), "missing expected target"); itemAdded = true; break; @@ -114,14 +130,14 @@ protected void AddItem(object value, int tag) { Array.Resize(ref _items, _items.Length * 2); _lastItemIndex++; - _items[_lastItemIndex].NewTarget(tag, value); + _items[_lastItemIndex].SetNewTarget(refInfo, value); } - _optimisticCount++; + _estimatedCount++; } } - internal T FindItem(int tag, Func filterMethod) where T : class + internal T FindItem(int refInfo, Func filterMethod) where T : class { bool lockObtained = false; try @@ -129,13 +145,12 @@ internal T FindItem(int tag, Func filterMethod) where T : class TryEnterItemLock(ref lockObtained); if (lockObtained) { - if (_optimisticCount > 0) + if (_estimatedCount > 0) { - // Loop through the items for (int counter = 0; counter <= _lastItemIndex; counter++) { - // Check tag (should be easiest and quickest) - if (_items[counter].Tag == tag) + // Check reference info (should be easiest and quickest) + if (_items[counter].RefInfo == refInfo) { if (_items[counter].TryGetTarget(out object value)) { @@ -172,18 +187,18 @@ public void Notify(int message) _isNotifying = true; // Loop through each live item and notify it - if (_optimisticCount > 0) + if (_estimatedCount > 0) { for (int index = 0; index <= _lastItemIndex; ++index) { if (_items[index].TryGetTarget(out object value)) { - NotifyItem(message, _items[index].Tag, value); + NotifyItem(message, _items[index].RefInfo, value); _items[index].RemoveTarget(); } Debug.Assert(!_items[index].TryGetTarget(out object _), "Unexpected target after notifying"); } - _optimisticCount = 0; + _estimatedCount = 0; } // Shrink collection (if needed) @@ -205,7 +220,7 @@ public void Notify(int message) } } - abstract protected void NotifyItem(int message, int tag, object value); + abstract protected void NotifyItem(int message, int refInfo, object value); abstract public void Remove(object value); @@ -221,14 +236,14 @@ protected void RemoveItem(object value) if (lockObtained) { // Find the value, and then remove the target from our collection - if (_optimisticCount > 0) + if (_estimatedCount > 0) { for (int index = 0; index <= _lastItemIndex; ++index) { if (_items[index].TryGetTarget(out object target) && value == target) { _items[index].RemoveTarget(); - _optimisticCount--; + _estimatedCount--; break; } } @@ -262,4 +277,3 @@ private void ExitItemLockIfNeeded(bool lockObtained) } } } -