Skip to content

Commit

Permalink
Do not propagate a CancellationToken from a method arg to a long-live…
Browse files Browse the repository at this point in the history
…d state machine

This fixes #414, which is a hang that occurs when the `CancellationToken` passed to `MultiplexingStream.CreateAsync` is cancelled after `CreateAsync` has completed.
  • Loading branch information
AArnott committed Nov 2, 2021
1 parent 6b4e3c3 commit 9b437ac
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 1 deletion.
19 changes: 19 additions & 0 deletions src/Nerdbank.Streams.Tests/MultiplexingStreamTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,25 @@ public async Task CreateChannelAsync_CanceledBeforeAcceptance()
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => channel1Task).WithCancellation(this.TimeoutToken);
}

[Fact]
public async Task CreateAsync_CancellationToken()
{
var cts = new CancellationTokenSource();
var streamPair = FullDuplexStream.CreatePair();
Task<MultiplexingStream> mx1Task = MultiplexingStream.CreateAsync(streamPair.Item1, new MultiplexingStream.Options { ProtocolMajorVersion = this.ProtocolMajorVersion }, cts.Token);
Task<MultiplexingStream> mx2Task = MultiplexingStream.CreateAsync(streamPair.Item2, new MultiplexingStream.Options { ProtocolMajorVersion = this.ProtocolMajorVersion }, CancellationToken.None);
MultiplexingStream mx1 = await mx1Task;
MultiplexingStream mx2 = await mx2Task;

// At this point the cancellation token really shouldn't have any effect on mx1 now that the connection is established.
cts.Cancel();

Task<MultiplexingStream.Channel> ch1Task = mx1.OfferChannelAsync(string.Empty, this.TimeoutToken);
Task<MultiplexingStream.Channel> ch2Task = mx2.AcceptChannelAsync(string.Empty, this.TimeoutToken);

await Task.WhenAll(ch1Task, ch2Task).WithCancellation(this.TimeoutToken);
}

[Fact]
public async Task CreateChannelAsync()
{
Expand Down
4 changes: 3 additions & 1 deletion src/Nerdbank.Streams/MultiplexingStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ public static async Task<MultiplexingStream> CreateAsync(Stream stream, Options?
throw new NotSupportedException(Strings.SeededChannelsRequireV3Protocol);
}

var streamWriter = stream.UsePipeWriter(cancellationToken: cancellationToken);
// Do NOT specify our own cancellationToken parameter in UsePipeWriter, since this PipeWriter
// must outlive this method and therefore should not be canceled later if that token is eventually canceled.
var streamWriter = stream.UsePipeWriter(cancellationToken: CancellationToken.None);

var formatter = options.ProtocolMajorVersion switch
{
Expand Down

0 comments on commit 9b437ac

Please sign in to comment.