@@ -181,28 +181,14 @@ public override void Write(byte[] buffer, int offset, int count)
181181
182182 public override async Task < int > ReadAsync ( byte [ ] buffer , int offset , int count , CancellationToken cancellationToken )
183183 {
184- var linkedCancellationToken = GetLinkedCancellationToken ( _streamCancellationToken , cancellationToken ) ;
185- return await _pipeReaderStream . ReadAsync ( buffer . AsMemory ( offset , count ) , linkedCancellationToken ) ;
184+ using var linkedCts = ValueLinkedCancellationTokenSource . Create ( _streamCancellationToken , cancellationToken ) ;
185+ return await _pipeReaderStream . ReadAsync ( buffer . AsMemory ( offset , count ) , linkedCts . Token ) ;
186186 }
187187
188188 public override async ValueTask < int > ReadAsync ( Memory < byte > buffer , CancellationToken cancellationToken = default )
189189 {
190- var linkedCancellationToken = GetLinkedCancellationToken ( _streamCancellationToken , cancellationToken ) ;
191- return await _pipeReaderStream . ReadAsync ( buffer , linkedCancellationToken ) ;
192- }
193-
194- private static CancellationToken GetLinkedCancellationToken ( CancellationToken a , CancellationToken b )
195- {
196- if ( a . CanBeCanceled && b . CanBeCanceled )
197- {
198- return CancellationTokenSource . CreateLinkedTokenSource ( a , b ) . Token ;
199- }
200- else if ( a . CanBeCanceled )
201- {
202- return a ;
203- }
204-
205- return b ;
190+ using var linkedCts = ValueLinkedCancellationTokenSource . Create ( _streamCancellationToken , cancellationToken ) ;
191+ return await _pipeReaderStream . ReadAsync ( buffer , linkedCts . Token ) ;
206192 }
207193
208194 private async Task ThrowOnTimeout ( )
@@ -243,4 +229,45 @@ protected override void Dispose(bool disposing)
243229
244230 _disposed = true ;
245231 }
232+
233+ // A helper for creating and disposing linked CancellationTokenSources
234+ // without allocating, when possible.
235+ // Internal for testing.
236+ internal readonly struct ValueLinkedCancellationTokenSource : IDisposable
237+ {
238+ private readonly CancellationTokenSource ? _linkedCts ;
239+
240+ public readonly CancellationToken Token ;
241+
242+ // For testing.
243+ internal bool HasLinkedCancellationTokenSource => _linkedCts is not null ;
244+
245+ public static ValueLinkedCancellationTokenSource Create (
246+ CancellationToken token1 , CancellationToken token2 )
247+ {
248+ if ( ! token1 . CanBeCanceled )
249+ {
250+ return new ( linkedCts : null , token2 ) ;
251+ }
252+
253+ if ( ! token2 . CanBeCanceled )
254+ {
255+ return new ( linkedCts : null , token1 ) ;
256+ }
257+
258+ var linkedCts = CancellationTokenSource . CreateLinkedTokenSource ( token1 , token2 ) ;
259+ return new ( linkedCts , linkedCts . Token ) ;
260+ }
261+
262+ private ValueLinkedCancellationTokenSource ( CancellationTokenSource ? linkedCts , CancellationToken token )
263+ {
264+ _linkedCts = linkedCts ;
265+ Token = token ;
266+ }
267+
268+ public void Dispose ( )
269+ {
270+ _linkedCts ? . Dispose ( ) ;
271+ }
272+ }
246273}
0 commit comments