diff --git a/Recyclable.Collections/Parallel/IndexOfSynchronizationContext.cs b/Recyclable.Collections/Parallel/IndexOfSynchronizationContext.cs index 4a53190..99fcd77 100644 --- a/Recyclable.Collections/Parallel/IndexOfSynchronizationContext.cs +++ b/Recyclable.Collections/Parallel/IndexOfSynchronizationContext.cs @@ -3,6 +3,10 @@ namespace Recyclable.Collections.Parallel public sealed class IndexOfSynchronizationContext { private readonly SpinWait _participantsSpinWait = new(); + private readonly SpinLock _participantsSpinLock = new(false); + private readonly SpinLock _indexOfSpinLock = new(false); + private bool _indexOfLockTaken; + internal bool _isItemFound; internal int _participants; @@ -10,12 +14,29 @@ namespace Recyclable.Collections.Parallel public void AddParticipant() { - _ = Interlocked.Increment(ref _participants); + bool lockTaken = false; + _participantsSpinLock.Enter(ref lockTaken); + _participants++; + if (lockTaken) + { + _participantsSpinLock.Exit(false); + } } public void AddParticipants(int newParticipants) { - _ = Interlocked.Add(ref _participants, newParticipants); + bool lockTaken = false; + _participantsSpinLock.Enter(ref lockTaken); + _participants += newParticipants; + if (lockTaken) + { + _participantsSpinLock.Exit(false); + } + } + + public void Lock() + { + _indexOfSpinLock.Enter(ref _indexOfLockTaken); } public void RemoveParticipants(int participantsToRemove) @@ -25,7 +46,13 @@ namespace Recyclable.Collections.Parallel ThrowNotEnoughParticipants(); } - _ = Interlocked.Add(ref _participants, -participantsToRemove); + bool lockTaken = false; + _participantsSpinLock.Enter(ref lockTaken); + _participants -= participantsToRemove; + if (lockTaken) + { + _participantsSpinLock.Exit(false); + } } public void RemoveParticipant() @@ -35,7 +62,13 @@ namespace Recyclable.Collections.Parallel ThrowNoMoreParticipants(); } - _ = Interlocked.Decrement(ref _participants); + bool lockTaken = false; + _participantsSpinLock.Enter(ref lockTaken); + _participants--; + if (lockTaken) + { + _participantsSpinLock.Exit(false); + } } public void SignalAndWait() @@ -45,13 +78,28 @@ namespace Recyclable.Collections.Parallel ThrowNoMoreParticipants(); } - _ = Interlocked.Decrement(ref _participants); + bool lockTaken = false; + _participantsSpinLock.Enter(ref lockTaken); + _participants--; + if (lockTaken) + { + _participantsSpinLock.Exit(false); + } + while (_participants > 0) { _participantsSpinWait.SpinOnce(); } } + public void Unlock() + { + if (_indexOfLockTaken) + { + _indexOfSpinLock.Exit(false); + } + } + private static void ThrowNotEnoughParticipants() => throw new InvalidOperationException("Participant cannot be removed, because there are not enough participants."); private static void ThrowNoMoreParticipants() => throw new InvalidOperationException("Participant cannot be removed, because there are no more participants."); diff --git a/Recyclable.Collections/RecyclableLongList.IndexOfHelpers.cs b/Recyclable.Collections/RecyclableLongList.IndexOfHelpers.cs index d84126d..62d367b 100644 --- a/Recyclable.Collections/RecyclableLongList.IndexOfHelpers.cs +++ b/Recyclable.Collections/RecyclableLongList.IndexOfHelpers.cs @@ -25,7 +25,9 @@ namespace Recyclable.Collections else if (index >= 0) { context._isItemFound = true; - _ = Interlocked.Exchange(ref context.FoundItemIndex, (blockIndex << list._blockSizePow2BitShift) + index); + context.Lock(); + context.FoundItemIndex = ((long)blockIndex << list._blockSizePow2BitShift) + index; + context.Unlock(); return; } @@ -37,7 +39,9 @@ namespace Recyclable.Collections else if (index >= 0) { context._isItemFound = true; - _ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 1) << list._blockSizePow2BitShift) + index); + context.Lock(); + context.FoundItemIndex = ((long)(blockIndex + 1) << list._blockSizePow2BitShift) + index; + context.Unlock(); return; } @@ -49,7 +53,9 @@ namespace Recyclable.Collections else if (index >= 0) { context._isItemFound = true; - _ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 2) << list._blockSizePow2BitShift) + index); + context.Lock(); + context.FoundItemIndex = ((long)(blockIndex + 2) << list._blockSizePow2BitShift) + index; + context.Unlock(); return; } @@ -57,7 +63,9 @@ namespace Recyclable.Collections if (!context._isItemFound && index >= 0) { context._isItemFound = true; - _ = Interlocked.Exchange(ref context.FoundItemIndex, ((blockIndex + 3) << list._blockSizePow2BitShift) + index); + context.Lock(); + context.FoundItemIndex = ((long)(blockIndex + 3) << list._blockSizePow2BitShift) + index; + context.Unlock(); } } catch (Exception e) @@ -87,7 +95,9 @@ namespace Recyclable.Collections if (!context._isItemFound && index >= 0) { context._isItemFound = true; - _ = Interlocked.Exchange(ref context.FoundItemIndex, (blockIndex << list._blockSizePow2BitShift) + index); + context.Lock(); + context.FoundItemIndex = ((long)blockIndex << list._blockSizePow2BitShift) + index; + context.Unlock(); } } catch (Exception e) @@ -176,7 +186,9 @@ namespace Recyclable.Collections if (!context._isItemFound && itemIndex >= 0) { context._isItemFound = true; - _ = Interlocked.Exchange(ref context.FoundItemIndex, ((long)blockIndex << list._blockSizePow2BitShift) + itemIndex); + context.Lock(); + context.FoundItemIndex = ((long)blockIndex << list._blockSizePow2BitShift) + itemIndex; + context.Unlock(); } } @@ -249,16 +261,9 @@ namespace Recyclable.Collections } } - //if (blockIndex == list._lastBlockWithData) - //{ - itemIndex = list._nextItemIndex != 0 - ? Array.IndexOf(memoryBlocksSpan[list._lastBlockWithData], item, 0, list._nextItemIndex) - : Array.IndexOf(memoryBlocksSpan[list._lastBlockWithData], item, 0, list._blockSize); - //} - //else - //{ - // return RecyclableDefaults.ItemNotFoundIndexLong; - //} + itemIndex = list._nextItemIndex != 0 + ? Array.IndexOf(memoryBlocksSpan[list._lastBlockWithData], item, 0, list._nextItemIndex) + : Array.IndexOf(memoryBlocksSpan[list._lastBlockWithData], item, 0, list._blockSize); return itemIndex >= 0 ? itemIndex + ((long)list._lastBlockWithData << list._blockSizePow2BitShift) : RecyclableDefaults.ItemNotFoundIndexLong; }