-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Closed
Description
When creating named pipes on .NET or .NET Core, unless I use PipeOptions.Asynchronous, async I/O operations done later end up hanging the I/O operations. Even if I pass in a CancellationToken, these async I/O methods do not cancel when demanded.
IMO the async flag should be simply an optimization -- not something that if set incorrectly causes a hang.
But even if a hang is acceptable, surely the CancellationToken should be honored, no?
Minimal repro: XUnitTestProject5.zip
You'll see the test passes, but if you change PipeOptions used in the repro to None, the test hangs.
Source code:
using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
#nullable enable
public class UnitTest1
{
private readonly CancellationToken TimeoutToken = new CancellationTokenSource(5000).Token;
private readonly string PipeName = Guid.NewGuid().ToString();
private const PipeOptions Options = PipeOptions.Asynchronous; // FAILS when set to None
private const PipeDirection Direction = PipeDirection.InOut;
[Fact]
public async Task Test1()
{
var serverTask = RunServer();
using var clientPipe = new NamedPipeClientStream(".", PipeName, Direction, Options);
await clientPipe.ConnectAsync(this.TimeoutToken);
Task readTask = ReadBlockAsync(clientPipe, 3, this.TimeoutToken);
await clientPipe.WriteAsync(new byte[] { 1, 2, 3 }, this.TimeoutToken);
await clientPipe.FlushAsync(this.TimeoutToken);
await Task.WhenAll(readTask, serverTask);
}
private async Task RunServer()
{
using var serverPipe = new NamedPipeServerStream(PipeName, Direction, maxNumberOfServerInstances: 1, PipeTransmissionMode.Byte, Options);
await serverPipe.WaitForConnectionAsync(this.TimeoutToken);
Task readTask = ReadBlockAsync(serverPipe, 3, this.TimeoutToken);
await serverPipe.WriteAsync(new byte[] { 4, 5, 6 }, this.TimeoutToken);
await serverPipe.FlushAsync(this.TimeoutToken);
await readTask;
}
private static async Task<ReadOnlyMemory<byte>> ReadBlockAsync(Stream stream, int length, CancellationToken cancellationToken)
{
byte[] buffer = new byte[length];
int bytesRead = 0;
while (bytesRead < length)
{
int bytesJustRead = await stream.ReadAsync(buffer.AsMemory(bytesRead), cancellationToken);
if (bytesJustRead == 0)
{
throw new Exception("Lost connection.");
}
bytesRead += bytesJustRead;
}
return buffer;
}
}noseratio