Open danmoseley opened 5 years ago
@poizan42 @JeremyKuhne although I was unable to find any statement in Win32 API documentation, my understanding is that it's not possible to bind a duplicate handle to a second IOCP port, therefore async operations on clientStream2
will always fail on Windows.
This is a known behavior for sockets (see dotnet/runtime#1760).
Triage: It would be useful to try doing straight P/Invokes and confirm if handle duplication can be done as described.
While working on #58381 I wanted to use NamedPipeServerStream
and NamedPipeClientStream
to write some tests for non-seekable files (pipes in this case).
Example:
async Task<(SafeFileHandle readHandle, SafeFileHandle writeHandle)> GetNamedPipeHandlesAsync()
{
string name = FileSystemTest.GetNamedPipeServerStreamName();
var server = new NamedPipeServerStream(name, PipeDirection.In, -1, PipeTransmissionMode.Byte, PipeOptions);
var client = new NamedPipeClientStream(".", name, PipeDirection.Out, PipeOptions);
await Task.WhenAll(server.WaitForConnectionAsync(), client.ConnectAsync());
bool isAsync = (PipeOptions & PipeOptions.Asynchronous) != 0;
return (GetFileHandle(server, isAsync), GetFileHandle(client, isAsync));
}
private static SafeFileHandle GetFileHandle(PipeStream pipeStream, bool isAsync)
{
var serverHandle = new SafeFileHandle(pipeStream.SafePipeHandle.DangerousGetHandle(), ownsHandle: true);
pipeStream.SafePipeHandle.SetHandleAsInvalid();
return serverHandle;
}
The problem is that when I use PipeOptions.Asynchronous
and wait for the pipes to be connected, the async pipe handle gets bound to Thread Pool:
and when I create a copy of it and try to re-use for async IO, I hit this condition:
Minimal repro case using FileStream
and .NET 5:
I've used reflection as un ugly workaround hack:
ThreadPoolBoundHandle threadPoolBinding = (ThreadPoolBoundHandle)typeof(PipeStream).GetField("_threadPoolBinding", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance).GetValue(pipeStream);
typeof(SafeFileHandle).GetProperty("ThreadPoolBinding", Reflection.BindingFlags.NonPublic | Reflection.BindingFlags.Instance).GetSetMethod(true).Invoke(serverHandle, new object[] { threadPoolBinding });
We should take a look whether it's possible to just get ThreadPoolBoundHandle
that is assigned to given handle.
I also just had this issue because I am trying to attach a serial port handle to the FileStream.
I explicitly need the ThreadPoolBoundHandle
to be able to receive the serial communication events but the SafeFileHandle
already creates and hides it, therefore I can write to the serial port but I never get any event telling me how many bytes are available to read.
At the very end this means abandoning FileStream
and being stuck in a lot of boilerplate code to read/write as it was a C app.
From @poizan42 on June 13, 2018 4:6
When I run the following code I get a System.ArgumentException: ''handle' has already been bound to the thread pool, or was not opened for asynchronous I/O.'
This seems weird since it is a copy of the handle bound and it is definitely opened for async I/O. From a glance it looks like the error ultimately comes from CreateIoCompletionPort, but that is weird because the documentation implies that you can use DuplicateHandle to share a handle registered to an IO completion port:
Is the framework doing something funky here, or is the reality more complicated than the documentation for CreateIoCompletionPort would lead you to think?
This is tested on dotnet core 2.1.30 and Windows 10.0.17686.1003.
Copied from original issue: dotnet/coreclr#18450