Skip to content

Commit 5a71f3b

Browse files
committed
Fixed node removal logic
1 parent eb8e34f commit 5a71f3b

File tree

2 files changed

+61
-5
lines changed

2 files changed

+61
-5
lines changed

src/DotNext.Threading/Threading/QueuedSynchronizer.Queue.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,25 @@ private protected WaitQueueVisitor GetWaitQueue(ref LinkedValueTaskCompletionSou
3737
return detachedQueue.First;
3838
}
3939

40-
private void ReturnNode(WaitNode node)
40+
private void ReturnNode<TStrategy>(TStrategy strategy)
41+
where TStrategy : struct, IRemovalStrategy
4142
{
43+
var returnToPool = strategy.Node.TryReset(out _);
4244
LinkedValueTaskCompletionSource<bool>? suspendedCallers;
4345
lock (SyncRoot)
4446
{
45-
suspendedCallers = node.NeedsRemoval && waitQueue.Remove(node) && node.DrainOnReturn
47+
suspendedCallers = strategy.Remove(ref waitQueue)
4648
? DrainWaitQueue()
4749
: null;
4850

49-
pool.Return(node);
51+
if (returnToPool)
52+
pool.Return(strategy.Node);
5053
}
5154

5255
suspendedCallers?.Unwind();
5356
}
57+
58+
5459

5560
private protected TNode? Acquire<T, TBuilder, TNode>(ref TBuilder builder, bool acquired)
5661
where T : struct, IEquatable<T>
@@ -268,4 +273,11 @@ public void Add(LinkedValueTaskCompletionSource<bool> node)
268273
return result;
269274
}
270275
}
276+
277+
private interface IRemovalStrategy
278+
{
279+
bool Remove(ref WaitQueue queue);
280+
281+
LinkedValueTaskCompletionSource<bool> Node { get; }
282+
}
271283
}

src/DotNext.Threading/Threading/QueuedSynchronizer.WaitNode.cs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Diagnostics;
2+
using System.Runtime.InteropServices;
23
using DotNext.Patterns;
34

45
namespace DotNext.Threading;
@@ -41,12 +42,55 @@ internal bool DrainOnReturn
4142

4243
protected sealed override void AfterConsumed()
4344
{
44-
if (owner is { } ownerCopy && TryReset(out _))
45-
ownerCopy.ReturnNode(this);
45+
if (owner is null)
46+
{
47+
// nothing to do
48+
}
49+
else if (!NeedsRemoval)
50+
{
51+
owner.ReturnNode<DoNotRemoveStrategy>(new(this));
52+
}
53+
else if (DrainOnReturn)
54+
{
55+
owner.ReturnNode<RemoveAndDrainStrategy>(new(this));
56+
}
57+
else
58+
{
59+
owner.ReturnNode<RemoveStrategy>(new(this));
60+
}
4661
}
4762

4863
protected sealed override Result<bool> OnTimeout()
4964
=> (flags & WaitNodeFlags.ThrowOnTimeout) is not 0 ? base.OnTimeout() : false;
65+
66+
[StructLayout(LayoutKind.Auto)]
67+
private readonly struct DoNotRemoveStrategy(WaitNode node) : IRemovalStrategy
68+
{
69+
bool IRemovalStrategy.Remove(ref WaitQueue queue) => false;
70+
71+
LinkedValueTaskCompletionSource<bool> IRemovalStrategy.Node => node;
72+
}
73+
74+
[StructLayout(LayoutKind.Auto)]
75+
private readonly struct RemoveStrategy(WaitNode node) : IRemovalStrategy
76+
{
77+
bool IRemovalStrategy.Remove(ref WaitQueue queue)
78+
{
79+
queue.Remove(node);
80+
return false;
81+
}
82+
83+
LinkedValueTaskCompletionSource<bool> IRemovalStrategy.Node => node;
84+
}
85+
86+
[StructLayout(LayoutKind.Auto)]
87+
private readonly struct RemoveAndDrainStrategy(WaitNode node) : IRemovalStrategy
88+
{
89+
bool IRemovalStrategy.Remove(ref WaitQueue queue)
90+
=> queue.Remove(node);
91+
92+
LinkedValueTaskCompletionSource<bool> IRemovalStrategy.Node => node;
93+
}
5094
}
5195

5296
[Flags]

0 commit comments

Comments
 (0)