Search code examples
.netipcnamed-pipes

Why does reading from pipe block the process?


I have two processes ( both .NET Framework applications) and i am trying to communicate using Named Pipes The client connects to the pipe , but when it tries to ReadAsync the message sent by the server, it starts waiting indifinetly , even though the server has already sent a message !!

What surprises me is that if i close the Server app the Client finally continues to the next line of code (after the ReadAsync line), having read 0 bytes.

Is there anything i must to on the server after WriteAsync-ing the payload? Do i need to flush or anything ?

Server

static async Task Main(string[] args) {

         var server = new NamedPipeServerStream(
             "somePipe",
              PipeDirection.InOut, 
              2, 
              PipeTransmissionMode.Message
              );

         await server.WaitForConnectionAsync();

         using (StreamReader reader = new StreamReader(server)) {
              using (StreamWriter writer = new StreamWriter(server)) {

                 await writer.WriteAsync("Hello from server"); 

                 char[] buffer = new char[20];
                 var read = await reader.ReadAsync(buffer, 0, buffer.Length);
                }
         }
}

Client

 static async Task Main() {

    NamedPipeClientStream client = new NamedPipeClientStream(
             ".",
             "somePipe",
             PipeDirection.InOut,
             PipeOptions.Asynchronous);

    await client.ConnectAsync();

    try {
    using (StreamReader reader = new StreamReader(client)) {
        using (StreamWriter writer = new StreamWriter(client)) {

          var buffer = new char[20];
          int readChars = await reader.ReadAsync(buffer, 0,buffer.Length); //starts waiting indifinetly , until i close the Server

          await writer.WriteAsync("From Client");

        }
     }
     } catch (Exception ex) {

       throw;
     }
}

Update It seems using the NamedPipeServerStream directly to write instead of the StreamWriter on the server side makes the client get the data !

Server Changes

byte[] data=Encoding.UTF8.GetBytes("Hello from server");
server.WriteAsync(data,0,data.Length);

P.S However using again the server to ReadAsync blocks the server.So there is a problem when wrapping the NamedPipeServerStream and ClientStream into StreamReader and StreamWriter.


Solution

  • This blocking/lock issue is caused by the code that's generated undercovers by the async/await syntactic sugar. If you don't add ConfigureAwait(false), the compiler generates something that's cool for a UI application (UI is a general term, could be ASP.NET - not core - , WPF, Winforms, etc.), but that can be deadly otherwise.

    A lot has beew written on this matter:

    Don't Block on Async Code

    quoted:

    There are two best practices [...] that avoid this situation:

    • In your “library” async methods, use ConfigureAwait(false) wherever possible.
    • Don’t block on Tasks; use async all the way down.

    Parallel Computing - It's All About the SynchronizationContext

    Best practice to call ConfigureAwait for all server-side code

    Should I call ConfigureAwait(false) on every awaited operation

    So, the simplest solution is to add ConfigureAwait(false) everywhere in your code that is not known to run in the UI, or use async everywhere (never block on any task ever in your whole code, really everywhere, including in code you don't own).

    IMHO, this is ridiculous... I personally use a Visual Studio extension that yells at me, if I forget to add it behind every async call :-)

    Note there are some alternatives, for example: An alternative to ConfigureAwait(false) everywhere