I'm communicating with another process via named pipes. The pipe server is implemented in C# and the client is written in C. The server is a WPF application.
I need to create a NamedPipeServerStream
and wait (synchronously) up to 1 second for the client to connect. And then I need to know whether the client connected.
As NamedPipeServerStream
's only way to cancel/timeout a wait for the client to connect is via its asynchronous WaitForConnectionAsync
method - which takes a CancellationToken
- I've implemented what I believe is a synchronous wait like so:
public bool WaitOneSecondForClientConnect()
{
bool result = false;
try
{
result = WaitForConnectionAsyncSyncWrapper().Result;
}
catch (AggregateException e)
{
log.Write("Error waiting for pipe client connect: " + e.InnerException.Message);
}
return result;
}
private async Task<bool> WaitForConnectionAsyncSyncWrapper()
{
CancellationTokenSource cts = new CancellationTokenSource(1000);
await pipe.WaitForConnectionAsync(cts.Token);
return pipe.IsConnected;
}
The pipe is defined like so: NamedPipeServerStream(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1, 1);
The WaitOneSecondForClientConnect()
function runs on the UI thread.
What should make it synchronous is accessing the async WaitForConnectionAsyncSyncWrapper()
function's Result
property on the Task<bool>
it returns. In order to access the Result, the async function must have fully returned, and it can't execute the return pipe.IsConnected
line until the function is resumed after await pipe.WaitForConnectionAsync(cts.Token);
has completed. At least that's my understanding.
So the problem: although the client program says it's opened my server's pipe (which has already been created of course before the code above executes), WaitOneSecondForClientConnect()
never returns. If I break into the server, it's on this line: result = WaitForConnectionAsyncSyncWrapper().Result;
.
So I guess it's waiting for the Task's Result to be available, which should be the value of pipe.IsConnected
if the client connected within 1 second, or it should throw an AggregateException
when I access it if the await
has completed because the token has been cancelled (after 1 second). But it's just hanging completely.
On the other hand, if I cancel the token before it starts e.g. by putting a Thread.Sleep(2000);
right before calling await pipe.WaitForConnectionAsync(cts.Token);
, then the connection is cancelled successfully (I think it doesn't even try to start because the token is already cancelled) - accessing the Result
property throws an AggregateException
, etc...
A few things to note.
- If I replace the content of
WaitOneSecondForClientConnect()
with a standard synchronouspipe.WaitForConnection();
, it works every time - i.e. the client connects, and the function returns. - In a test program I wrote to initially get this async/sync stuff working, connecting in this synchronous-asynchronous way works every time. It's a console program as opposed to my real program which is WPF. The relevant code of the test program is listed below.
- The code posted above has actually worked a couple of times, and failed maybe 30-40 times.
- If my client doesn't open my pipe, my "real" code still hangs, whereas my test code waits the specified time period and then prints "Connection failed." as expected (see below) - which is exactly the behaviour that should be happening in my real code.
The test code that works:
var pipe = new NamedPipeServerStream("SemiUsefulPipe_" + pid.ToString() + "ctest", PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1, 1);
// ... dll containing pipe client is injected in client process at this point.
try
{
var result = ConnectAsync(pipe).Result;
}
catch (AggregateException)
{
Console.WriteLine("Connection failed.");
}
...
private static async Task<bool> ConnectAsync(NamedPipeServerStream pipe)
{
CancellationTokenSource cts = new CancellationTokenSource(1000);
await pipe.WaitForConnectionAsync(cts.Token);
return pipe.IsConnected;
}