Skip to content

Commit ba0901f

Browse files
committed
Added timeout support
1 parent c85a50a commit ba0901f

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

src/DotNext.Tests/Threading/CancellationTokenMultiplexerTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,17 @@ public static void ExtraListOverflow()
7171
using var scope = multiplexer.Combine(tokens);
7272
True(scope.Token.IsCancellationRequested);
7373
}
74+
75+
[Fact]
76+
public static async Task TimeOut()
77+
{
78+
using var cts = new CancellationTokenSource();
79+
var multiplexer = new CancellationTokenMultiplexer();
80+
81+
await using var scope = multiplexer.Combine(TimeSpan.FromMilliseconds(1), [cts.Token]);
82+
await scope.Token.WaitAsync();
83+
84+
Equal(scope.Token, scope.CancellationOrigin);
85+
NotEqual(scope.Token, cts.Token);
86+
}
7487
}

src/DotNext.Threading/Threading/CancellationTokenMultiplexer.Scope.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ internal Scope(CancellationTokenMultiplexer multiplexer, ReadOnlySpan<Cancellati
2626
source = multiplexer.Rent(tokens);
2727
}
2828

29+
internal Scope(CancellationTokenMultiplexer multiplexer, TimeSpan timeout, ReadOnlySpan<CancellationToken> tokens)
30+
{
31+
multiplexerOrToken = new(multiplexer);
32+
source = multiplexer.Rent(tokens);
33+
source.CancelAfter(timeout);
34+
}
35+
2936
internal Scope(CancellationToken token)
3037
=> multiplexerOrToken = InlineToken(token);
3138

@@ -86,13 +93,13 @@ private static async ValueTask ReturnAsync(CancellationTokenMultiplexer multiple
8693
private static void Return(CancellationTokenMultiplexer multiplexer, PooledCancellationTokenSource source)
8794
{
8895
source.Reset();
89-
if (source.IsCancellationRequested)
96+
if (source.TryReset())
9097
{
91-
source.Dispose();
98+
multiplexer.Return(source);
9299
}
93100
else
94101
{
95-
multiplexer.Return(source);
102+
source.Dispose();
96103
}
97104
}
98105
}

src/DotNext.Threading/Threading/CancellationTokenMultiplexer.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,24 @@ public Scope Combine(ReadOnlySpan<CancellationToken> tokens) // TODO: use params
6363
return scope;
6464
}
6565

66+
/// <summary>
67+
/// Combines the multiple tokens and the timeout.
68+
/// </summary>
69+
/// <remarks>
70+
/// The cancellation triggered by the timeout can be detected by comparing <see cref="Scope.Token"/> with <see cref="Scope.CancellationOrigin"/>.
71+
/// </remarks>
72+
/// <param name="timeout">The timeout that could trigger the cancellation.</param>
73+
/// <param name="tokens">The tokens to be combined.</param>
74+
/// <returns>The scope that represents the multiplexed token.</returns>
75+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="timeout"/> is negative or too large.</exception>
76+
public Scope Combine(TimeSpan timeout, ReadOnlySpan<CancellationToken> tokens) => timeout.Ticks switch
77+
{
78+
0L => new(new CancellationToken(canceled: true)),
79+
Timeout.InfiniteTicks => Combine(tokens),
80+
< 0L or > Timeout.MaxTimeoutParameterTicks => throw new ArgumentOutOfRangeException(nameof(timeout)),
81+
_ => new(this, timeout, tokens)
82+
};
83+
6684
private void Return(PooledCancellationTokenSource source)
6785
{
6886
// try to increment the counter

0 commit comments

Comments
 (0)