Skip to content

Commit

Permalink
Allow NetworkStream.WriteAsync calls to be canceled asynchronously (d…
Browse files Browse the repository at this point in the history
…otnet/corefx#40839)

In 3.0 we added the necessary support for this, and for Socket.WriteAsync calls we're appropriately passing the CancellationToken through, but we missed doing so for NetworkStream.WriteAsync, which takes a slightly different code path through AwaitableSocketAsyncEventArgs.

Commit migrated from dotnet/corefx@0a5b928
  • Loading branch information
stephentoub authored Sep 5, 2019
1 parent 1260cf7 commit 2d68185
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ internal ValueTask SendAsyncForNetworkStream(ReadOnlyMemory<byte> buffer, Socket
saea.SetBuffer(MemoryMarshal.AsMemory(buffer));
saea.SocketFlags = socketFlags;
saea.WrapExceptionsInIOExceptions = true;
return saea.SendAsyncForNetworkStream(this);
return saea.SendAsyncForNetworkStream(this, cancellationToken);
}
else
{
Expand Down Expand Up @@ -929,12 +929,13 @@ public ValueTask<int> SendAsync(Socket socket, CancellationToken cancellationTok
new ValueTask<int>(Task.FromException<int>(CreateException(error)));
}

public ValueTask SendAsyncForNetworkStream(Socket socket)
public ValueTask SendAsyncForNetworkStream(Socket socket, CancellationToken cancellationToken)
{
Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use");

if (socket.SendAsync(this))
if (socket.SendAsync(this, cancellationToken))
{
_cancellationToken = cancellationToken;
return new ValueTask(this, _token);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,37 @@ await RunWithConnectedNetworkStreamsAsync(async (server, client) =>
});
}

[Fact]
public async Task WriteAsync_CancelPendingWrite_SucceedsOrThrowsOperationCanceled()
{
await RunWithConnectedNetworkStreamsAsync(async (server, client) =>
{
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => client.WriteAsync(new byte[1], 0, 1, new CancellationToken(true)));
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => { await client.WriteAsync(new Memory<byte>(new byte[1]), new CancellationToken(true)); });

byte[] hugeBuffer = new byte[100_000_000];
Exception e;

var cts = new CancellationTokenSource();
Task t = client.WriteAsync(hugeBuffer, 0, hugeBuffer.Length, cts.Token);
cts.Cancel();
e = await Record.ExceptionAsync(async () => await t);
if (e != null)
{
Assert.IsAssignableFrom<OperationCanceledException>(e);
}

cts = new CancellationTokenSource();
ValueTask vt = client.WriteAsync(new Memory<byte>(hugeBuffer), cts.Token);
cts.Cancel();
e = await Record.ExceptionAsync(async () => await vt);
if (e != null)
{
Assert.IsAssignableFrom<OperationCanceledException>(e);
}
});
}

private sealed class CustomSynchronizationContext : SynchronizationContext
{
public override void Post(SendOrPostCallback d, object state)
Expand Down

0 comments on commit 2d68185

Please sign in to comment.