1+ using System . Diagnostics ;
2+ using System . Runtime . CompilerServices ;
13using System . Runtime . InteropServices ;
24
35namespace DotNext . Threading ;
46
7+ using Runtime ;
8+
59partial class CancellationTokenMultiplexer
610{
711 /// <summary>
@@ -10,44 +14,90 @@ partial class CancellationTokenMultiplexer
1014 [ StructLayout ( LayoutKind . Auto ) ]
1115 public readonly struct Scope : IMultiplexedCancellationTokenSource , IDisposable , IAsyncDisposable
1216 {
13- private readonly CancellationTokenMultiplexer multiplexer ;
14- private readonly PooledCancellationTokenSource source ;
17+ // CancellationToken is just a wrapper over CancellationTokenSource.
18+ // For optimization purposes, if only one token is passed to the scope, we can inline the underlying CTS
19+ // to this structure.
20+ private readonly ValueTuple < object > multiplexerOrToken ;
21+ private readonly PooledCancellationTokenSource ? source ;
1522
1623 internal Scope ( CancellationTokenMultiplexer multiplexer , ReadOnlySpan < CancellationToken > tokens )
1724 {
18- this . multiplexer = multiplexer ;
19- source = multiplexer . Rent ( ) ;
20-
21- foreach ( var token in tokens )
25+ switch ( tokens )
2226 {
23- source . Add ( token ) ;
27+ case [ ] :
28+ source = null ;
29+ multiplexerOrToken = InlineToken ( new ( canceled : false ) ) ;
30+ break ;
31+ case [ var token ] :
32+ source = null ;
33+ multiplexerOrToken = InlineToken ( token ) ;
34+ break ;
35+ case [ var token1 , var token2 ] :
36+ source = null ;
37+ if ( ! token1 . CanBeCanceled || token1 == token2 )
38+ {
39+ multiplexerOrToken = InlineToken ( token2 ) ;
40+ }
41+ else if ( ! token2 . CanBeCanceled )
42+ {
43+ multiplexerOrToken = InlineToken ( token1 ) ;
44+ }
45+ else
46+ {
47+ goto default ;
48+ }
49+
50+ break ;
51+ default :
52+ multiplexerOrToken = new ( multiplexer ) ;
53+ source = multiplexer . Rent ( tokens ) ;
54+ break ;
2455 }
2556 }
2657
58+ private static ValueTuple < object > InlineToken ( CancellationToken token )
59+ => CanInlineToken ? Unsafe . BitCast < CancellationToken , ValueTuple < object > > ( token ) : new ( token ) ;
60+
61+ private static CancellationToken GetToken ( ValueTuple < object > value )
62+ => CanInlineToken ? Unsafe . BitCast < ValueTuple < object > , CancellationToken > ( value ) : ( CancellationToken ) value . Item1 ;
63+
64+ // This property checks whether the reinterpret cast CancellationToken => CancellationTokenSource
65+ // is safe. If not, just box the token.
66+ private static bool CanInlineToken => Intrinsics . AreCompatible < CancellationToken , ValueTuple < object > > ( )
67+ && RuntimeHelpers . IsReferenceOrContainsReferences < CancellationToken > ( ) ;
68+
2769 /// <summary>
2870 /// Gets the cancellation token that can be canceled by any of the multiplexed tokens.
2971 /// </summary>
30- public CancellationToken Token => source . Token ;
72+ public CancellationToken Token => source ? . Token ?? GetToken ( multiplexerOrToken ) ;
3173
3274 /// <summary>
3375 /// Gets the cancellation origin if <see cref="Token"/> is in canceled state.
3476 /// </summary>
35- public CancellationToken CancellationOrigin => source . CancellationOrigin ;
77+ public CancellationToken CancellationOrigin => source ? . Token ?? GetToken ( multiplexerOrToken ) ;
3678
3779 /// <inheritdoc/>
3880 public void Dispose ( )
3981 {
40- for ( var i = 0 ; i < source . Count ; i ++ )
82+ if ( source is not null )
4183 {
42- source [ i ] . Dispose ( ) ;
43- }
84+ Debug . Assert ( multiplexerOrToken . Item1 is CancellationTokenMultiplexer ) ;
4485
45- // now we sure that no one can cancel the source concurrently
46- Return ( multiplexer , source ) ;
86+ for ( var i = 0 ; i < source . Count ; i ++ )
87+ {
88+ source [ i ] . Dispose ( ) ;
89+ }
90+
91+ // now we sure that no one can cancel the source concurrently
92+ Return ( Unsafe . As < CancellationTokenMultiplexer > ( multiplexerOrToken . Item1 ) , source ) ;
93+ }
4794 }
4895
4996 /// <inheritdoc/>
50- public ValueTask DisposeAsync ( ) => ReturnAsync ( multiplexer , source ) ;
97+ public ValueTask DisposeAsync ( )
98+ => source is not null
99+ ? ReturnAsync ( Unsafe . As < CancellationTokenMultiplexer > ( multiplexerOrToken . Item1 ) , source )
100+ : ValueTask . CompletedTask ;
51101
52102 private static async ValueTask ReturnAsync ( CancellationTokenMultiplexer multiplexer , PooledCancellationTokenSource source )
53103 {
0 commit comments