Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disposing NamedPipeServerStream does not cancel WaitForConnectionAsync #40674

Closed
jaredpar opened this issue Aug 11, 2020 · 2 comments · Fixed by #52825
Closed

Disposing NamedPipeServerStream does not cancel WaitForConnectionAsync #40674

jaredpar opened this issue Aug 11, 2020 · 2 comments · Fixed by #52825
Milestone

Comments

@jaredpar
Copy link
Member

Calling Dispose an a NamedPipeServerStream which is currently inside WaitForConnectionAsync does not cancel the underlying listen event when there are multiple NamedPipeServerStream instances active on the same named pipe. This can result in future NamedPipeClientStream.Connect calls going to the disposed NamedPipeServerStream instead of the non-disposed ones.

Consider the example below. This will start two NamedPipeServerStream instances on a single pipe name, then it will dispose the first one and start a client connection to the pipe. The connection will succeed but it connects to the disposed NamedPipeServerStream, not the active one. The stream can then be used without error.

Example code:

using System;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var pipeName = Guid.NewGuid().ToString();
        using var server1 = CreateServer();
        using var server2 = CreateServer();

        var wait1 = server1.WaitForConnectionAsync();
        server1.Dispose();

        var waitTask = Task.Run(async () =>
        {
            await server2.WaitForConnectionAsync();
            Console.WriteLine("Server 2 got a connection");
        });

        var client = new NamedPipeClientStream(pipeName);
        await client.ConnectAsync();
        Console.WriteLine("Client connected");
        await wait1;
        Console.WriteLine(wait1.Status);

        // Can still use server1 even though it's disposed
        await server1.WriteAsync(new byte[]{ 42 }, 0, 1);
        var buffer = new byte[1];
        await client.ReadAsync(buffer, 0, 1);
        Console.WriteLine(buffer[0]); // Prints 42
       
        Console.WriteLine("At the end");

        NamedPipeServerStream CreateServer() => new NamedPipeServerStream(
            pipeName,
            PipeDirection.InOut,
            maxNumberOfServerInstances: 10,
            PipeTransmissionMode.Byte,
            PipeOptions.Asynchronous);
    }
}

When run on Unix this code will output:

Client connected
RanToCompletion
42
At the end

When run on Windows this code will output:

Server 2 got a connection
Client connected
Unhandled exception. System.IO.IOException: The pipe has been ended.
   at System.IO.Pipes.ConnectionCompletionSource.HandleError(Int32 errorCode)
   at System.IO.Pipes.PipeCompletionSource`1.CompleteCallback(Int32 resultState)
   at System.IO.Pipes.PipeCompletionSource`1.AsyncCallback(UInt32 errorCode, UInt32 numBytes)   at System.IO.Pipes.ConnectionCompletionSource.AsyncCallback(UInt32 errorCode, UInt32 numBytes)
   at System.IO.Pipes.PipeCompletionSource`1.<>c.<.ctor>b__11_0(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOverlapped)
   at System.Threading.ThreadPoolBoundHandleOverlapped.CompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pNativeOverlapped)
--- End of stack trace from previous location ---
   at Program.Main(String[] args) in P:\temp\console\Program.cs:line 26
   at Program.<Main>(String[] args)
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-System.IO untriaged New issue has not been triaged by the area owner labels Aug 11, 2020
@carlossanlop carlossanlop added this to the Future milestone Aug 14, 2020
@carlossanlop carlossanlop removed the untriaged New issue has not been triaged by the area owner label Aug 14, 2020
@manandre
Copy link
Contributor

Hello,
I have reproduced your issue on a local dev setup with .NET 5.0.6 and .NET 6.0.0-preview.3.
I am working on a fix proposal. I will open a PR against the main branch, if needed we will see whether it can be easily backported.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label May 16, 2021
@wegylexy
Copy link
Contributor

On Windows, only when the pipe server stream is opened with PipeOptions.Asynchronous, disposing of it causes WaitForConnectionAsync() to fault with an IOException with HResult == -2147024787. Otherwise, WaitForConnectionAsync() never completes until a client connects.

@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jul 18, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Aug 17, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants