none
How to check whether the PipeStream has data or not RRS feed

  • Question

  • In .NET, the pipe API doesn't expose any property the application can use in order to decide whether it's worth involving a Read operation or not, and I don't understand why.

    The other posts on this subject suggest using the async reading and CancellationToken for this. Raising an OperationCancelledException every few milliseconds to find out there is no data in the pipe doesn't sound very appealing. Anyway, I tried that, as well. I am writing this post because to me it seems that ReadAsync doesn't work either for this purpose. I guess this is because ReadAsync deals with unmanaged IO operations(but this example uses PipeOptions.Asynchronous). 

    Please see the code below; it's the Main of a console application. The code contains comments explaining what are the actual and the expected behaviors.

    Thank you, Ernest.

            // This is the Main of a console application
            public static void Main()
            {
                // .NET Framework 4.6.2 / Windows 10 Professional
                // This code is intended to verify if ReadAsync can be used to check the pipe doesn't have any data.
    
                string pipeName = "XYZ";
    
                // create the server (async)pipe and put it listenting mode
                var serverPipe = new System.IO.Pipes.NamedPipeServerStream(pipeName, System.IO.Pipes.PipeDirection.InOut, 1, System.IO.Pipes.PipeTransmissionMode.Byte, System.IO.Pipes.PipeOptions.Asynchronous);
                var connWaiterTask = serverPipe.WaitForConnectionAsync();
    
                // create the client pipe and connect it to server. The client intentionally doesn't send any data to the server.
                var clientPipe = new System.IO.Pipes.NamedPipeClientStream("localhost", pipeName, System.IO.Pipes.PipeDirection.InOut); //, System.IO.Pipes.PipeOptions.Asynchronous);
                clientPipe.Connect();
                System.Threading.Thread.Sleep(50);
    
                // prepare a cancellation source for ReadAsync
                System.Threading.CancellationTokenSource cts = new System.Threading.CancellationTokenSource();
                byte[] inBuffer = new byte[1];
    
                try
                {
                    // invoke ReadAsync on the server pipe and immediately cancel the passed in cancellation token.
                    var readAsyncTask = serverPipe.ReadAsync(inBuffer, 0, 1, cts.Token);
                    System.Threading.Thread.Sleep(50);
                    cts.Cancel();  // cancel ReadAsync
    
                    //clientPipe.WriteByte(1); // If the client sends some data, it unblocks the ReadAsync on server side 
    
                    int n = readAsyncTask.Result; // The token is cancelled at this time, but ReadAsync doesn't seem to be aware of this. This line blocks indefinitely if the client doesn't send any data. 
                }
                finally
                {
                    // The execution never gets here. Why?
                    Console.WriteLine("FINISH");
                }
            }

    Wednesday, April 26, 2017 9:30 PM

Answers

  • The readAsyncTask is still in WaitingForActivation state when Cancel is called. That means the task has not even been start running, so the cancel call has no effect and reading .Result will case it to wait indefinitely.

    It'll work if you modify the try...finally block of code like this:

                try
                {
                    // invoke ReadAsync on the server pipe and immediately cancel the passed in cancellation token.
                    var readAsyncTask = serverPipe.ReadAsync(inBuffer, 0, 1, cts.Token);
                    System.Threading.Thread.Sleep(50);
                    cts.Cancel();  // cancel ReadAsync
    
                    //clientPipe.WriteByte(1); // If the client sends some data, it unblocks the ReadAsync on server side 
                    readAsyncTask.Wait(cts.Token); // Makes it blocking until the result that used in next line is ready and throw exception if cancelled
                    int n = readAsyncTask.Result; // The token is cancelled at this time, but ReadAsync doesn't seem to be aware of this. This line blocks indefinitely if the client doesn't send any data. 
                }
                catch (OperationCanceledException)
                {
    
                }
                finally
                {
                    // The execution never gets here. Why?
                    Console.WriteLine("FINISH");
                }



    Thursday, April 27, 2017 3:04 AM
    Answerer

All replies

  • The readAsyncTask is still in WaitingForActivation state when Cancel is called. That means the task has not even been start running, so the cancel call has no effect and reading .Result will case it to wait indefinitely.

    It'll work if you modify the try...finally block of code like this:

                try
                {
                    // invoke ReadAsync on the server pipe and immediately cancel the passed in cancellation token.
                    var readAsyncTask = serverPipe.ReadAsync(inBuffer, 0, 1, cts.Token);
                    System.Threading.Thread.Sleep(50);
                    cts.Cancel();  // cancel ReadAsync
    
                    //clientPipe.WriteByte(1); // If the client sends some data, it unblocks the ReadAsync on server side 
                    readAsyncTask.Wait(cts.Token); // Makes it blocking until the result that used in next line is ready and throw exception if cancelled
                    int n = readAsyncTask.Result; // The token is cancelled at this time, but ReadAsync doesn't seem to be aware of this. This line blocks indefinitely if the client doesn't send any data. 
                }
                catch (OperationCanceledException)
                {
    
                }
                finally
                {
                    // The execution never gets here. Why?
                    Console.WriteLine("FINISH");
                }



    Thursday, April 27, 2017 3:04 AM
    Answerer
  • Hi Ernet.Morariu,

    Thank you for posting here.

    For your question, maybe when you get the result value of  Task<TResult>, the ReadAsync has not been canceled completly.

    Please try the following code. And invoke the method instead of cts.Cancel().

    In Main method.

         try
                {
                    // invoke ReadAsync on the server pipe and immediately cancel the passed in cancellation token.
                    var readAsyncTask = serverPipe.ReadAsync(inBuffer, 0, 1, cts.Token);
    
                    System.Threading.Thread.Sleep(50);
                    bool b = readAsyncTask.IsCanceled;
                    //cts.Cancel();  // cancel ReadAsync
                    asyncMethodInvoke(cts);
    
                    //clientPipe.WriteByte(1); // If the client sends some data, it unblocks the ReadAsync on server side 
                    //  readAsyncTask.Wait(cts.Token);
                    int n = readAsyncTask.Result; // The token is cancelled at this time, but ReadAsync doesn't seem to be aware of this. This line blocks indefinitely if the client doesn't send any data. 
    
                }
                finally
                {
                    // The execution never gets here. Why?
                    Console.WriteLine("FINISH");
                }

         public static async void asyncMethodInvoke(System.Threading.CancellationTokenSource cts)
            {
                var t = Task.Factory.StartNew(() =>
                {
                    cts.Cancel();
                });
                await t;
                Console.WriteLine("Done awaiting");      
            }

    I hope this would be helpful.

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    Thursday, April 27, 2017 8:51 AM
    Moderator
  • Hi Cheong,

    Thank you for your support. I am going to mark your reply as answer. 

    To me this seems a bug. Async methods are usually invoked with "await". The application is not supposed to use 'Task.Wait()' when using Async API. 

    Thank you,

    Ernest Morariu

    Thursday, April 27, 2017 10:44 PM
  • Hello Wendy,

    Thank you for your support. 

    The problem is the fact that the Task created by ReadAsync has no knowledge about the CancellationToken I pass in, and it doesn't matter if I cancel the token immediately or later(with a asyncMethodInvoke). 

    This is clearly a bug in the framework. 

    Thank you,

    Ernest Morariu

    Thursday, April 27, 2017 10:50 PM
  • Well, actually the way I provided is just a workaround.

    The async I/O requests in .NET framework uses I/O completion port to implement. The correct way to cancel it would be to call CancelIOEx() with the overlapped I/O structure used on the read operation to cancel it. All my code do is just to force the thread to notice the I/O is cancelled and throw the OperationCancelledException. When there is data appear afterward, the code will still process it and add it to the .Result. (As demonstrated on your code, althrough in my workaround no code would be reading it)

    This is indeed something that should be fixed in the runtime. The Stream class should save the State object to a variable instead of local variable, and ReadAsync() implementation should register the cancellation callback to the Token to do it, but seems it does not. All it does is "if the cancellation token is in cancelled state, return immediately".


    Friday, April 28, 2017 1:26 AM
    Answerer