From 34805b4bd13638aa410b3f0e1c17fb2b327f0598 Mon Sep 17 00:00:00 2001 From: "Javad Rahnama (SIMBA TECHNOLOGIES INC)" Date: Tue, 12 Mar 2024 14:34:53 -0700 Subject: [PATCH 1/4] Merge common code base for DBReferenceCollection --- .../src/Microsoft.Data.SqlClient.csproj | 6 +- .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +- .../ProviderBase/DbReferenceCollection.cs | 280 ++++++++++++++++++ 3 files changed, 286 insertions(+), 4 deletions(-) create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs 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/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs new file mode 100644 index 0000000000..f95cda974e --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs @@ -0,0 +1,280 @@ +// 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 + { + #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 _refInfo; // information about the reference + private WeakReference _weakReference; // the reference itself. + + public void SetTNewTarget(int refInfo, object target) + { + Debug.Assert(!TryGetTarget(out object _), "Entry already has a valid target"); + Debug.Assert(refInfo != 0, "Bad reference info"); + Debug.Assert(target != null, "Invalid target"); + + if (_weakReference == null) + { + _weakReference = new WeakReference(target, false); + } + else + { + _weakReference.SetTarget(target); + } + _refInfo = refInfo; + } + + public void RemoveTarget() + { + _refInfo = 0; + _weakReference.SetTarget(null); + } + + public readonly int RefInfo => _refInfo; + + public readonly bool TryGetTarget(out object target) + { + target = null; + return _refInfo != 0 && _weakReference.TryGetTarget(out target); + } + } + + protected DbReferenceCollection() + { + _items = new CollectionEntry[DefaultCollectionSize]; + _itemLock = new object(); + _estimatedCount = 0; + _lastItemIndex = 0; + } + + abstract public void Add(object value, int refInfo); + + protected void AddItem(object value, int refInfo) + { + Debug.Assert(value != null && 0 != refInfo, "AddItem with null value or 0 reference info"); + bool itemAdded = false; + + lock (_itemLock) + { + // Try to find a free spot + for (int i = 0; i <= _lastItemIndex; ++i) + { + if (_items[i].RefInfo == 0) + { + _items[i].SetTNewTarget(refInfo, 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].SetTNewTarget(refInfo, 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].SetTNewTarget(refInfo, 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].SetTNewTarget(refInfo, value); + } + + _estimatedCount++; + } + } + + internal T FindItem(int refInfo, Func filterMethod) where T : class + { + bool lockObtained = false; + try + { + TryEnterItemLock(ref lockObtained); + if (lockObtained) + { + if (_estimatedCount > 0) + { + for (int counter = 0; counter <= _lastItemIndex; counter++) + { + // Check reference info (should be easiest and quickest) + if (_items[counter].RefInfo == refInfo) + { + 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 (_estimatedCount > 0) + { + for (int index = 0; index <= _lastItemIndex; ++index) + { + if (_items[index].TryGetTarget(out object value)) + { + NotifyItem(message, _items[index].RefInfo, value); + _items[index].RemoveTarget(); + } + Debug.Assert(!_items[index].TryGetTarget(out object _), "Unexpected target after notifying"); + } + _estimatedCount = 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 refInfo, 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 (_estimatedCount > 0) + { + for (int index = 0; index <= _lastItemIndex; ++index) + { + if (_items[index].TryGetTarget(out object target) && value == target) + { + _items[index].RemoveTarget(); + _estimatedCount--; + 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); + } + } + } +} From 2c6a671261b9328acd4edf7253964ca311f2748c Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Fri, 22 Mar 2024 16:27:43 -0700 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> --- .../src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs index f95cda974e..904b2d28d4 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs @@ -2,7 +2,6 @@ // 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; @@ -42,7 +41,7 @@ private struct CollectionEntry private int _refInfo; // information about the reference private WeakReference _weakReference; // the reference itself. - public void SetTNewTarget(int refInfo, object target) + public void SetNewTarget(int refInfo, object target) { Debug.Assert(!TryGetTarget(out object _), "Entry already has a valid target"); Debug.Assert(refInfo != 0, "Bad reference info"); From 35c9753b9a99f8f3d09e375f75552b58cc324e8c Mon Sep 17 00:00:00 2001 From: Javad Rahnama Date: Mon, 25 Mar 2024 11:23:12 -0700 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: DavoudEshtehari <61173489+DavoudEshtehari@users.noreply.github.com> --- .../Microsoft/Data/ProviderBase/DbReferenceCollection.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs index 904b2d28d4..35356bfa12 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs @@ -95,7 +95,7 @@ protected void AddItem(object value, int refInfo) { if (_items[i].RefInfo == 0) { - _items[i].SetTNewTarget(refInfo, value); + _items[i].SetNewTarget(refInfo, value); Debug.Assert(_items[i].TryGetTarget(out object _), "missing expected target"); itemAdded = true; break; @@ -106,7 +106,7 @@ protected void AddItem(object value, int refInfo) if ((!itemAdded) && (_lastItemIndex + 1 < _items.Length)) { _lastItemIndex++; - _items[_lastItemIndex].SetTNewTarget(refInfo, value); + _items[_lastItemIndex].SetNewTarget(refInfo, value); itemAdded = true; } @@ -117,7 +117,7 @@ protected void AddItem(object value, int refInfo) { if (!_items[i].TryGetTarget(out object _)) { - _items[i].SetTNewTarget(refInfo, value); + _items[i].SetNewTarget(refInfo, value); Debug.Assert(_items[i].TryGetTarget(out object _), "missing expected target"); itemAdded = true; break; @@ -130,7 +130,7 @@ protected void AddItem(object value, int refInfo) { Array.Resize(ref _items, _items.Length * 2); _lastItemIndex++; - _items[_lastItemIndex].SetTNewTarget(refInfo, value); + _items[_lastItemIndex].SetNewTarget(refInfo, value); } _estimatedCount++; From 04eb9dbf8c59e246dc6f0dcfd84a77b80e87a86d Mon Sep 17 00:00:00 2001 From: "Javad Rahnama (SIMBA TECHNOLOGIES INC)" Date: Thu, 28 Mar 2024 11:19:14 -0700 Subject: [PATCH 4/4] Removing DbReferenceCollection files from netcore and netfx --- .../ProviderBase/DbReferenceCollection.cs | 263 ----------------- .../ProviderBase/DbReferenceCollection.cs | 265 ------------------ 2 files changed, 528 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Common/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs 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/netfx/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs deleted file mode 100644 index 1e69bcaba9..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/ProviderBase/DbReferenceCollection.cs +++ /dev/null @@ -1,265 +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. - -namespace Microsoft.Data.ProviderBase -{ - - using System; - using System.Diagnostics; - using System.Threading; - - 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) - { - // Loop through the items - 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); - } - } - } -} -