Win32 Asynchronous Pipes Question

I have code that already handles reading the pipe in a loop correctly and that code has to happen at a particular time in my program. I would like to avoid reorganizing the actual reading process, but I still need to be able to get signals whenever a pipe becomes ready. From what I can tell after ten minutes of browsing around the only way to do this is to call ReadFile and pass it an OVERLAPPED struct. This way I can get it to signal an event and I can call WaitOnMultipleObjects to know when something is ready on the pipe.

However, as far as I can tell, this means it will also interfere with the existing and somewhat intricate read process. I was hoping I could just ReadFile with a size of zero or something, but the docs don't say anything about it, can anyone out there help me?
What do you mean by "it will also interfere with the existing and somewhat intricate read process"?
If you want to use WaitOnMultipleObjects function then you either wait for any handle to get signaled, or wait for all of them get signaled. And you can either block infinitely, return immediately (basically a boolean check), or wait approximate time.

Something like this (if you want to wait for any of events and want to block):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
HANDLE events[] = { overlapped1.hEvent, oeverlapped2.hEvent };
DWORD ret = WaitOnMultipleObjects(2, events, FALSE, INFINITE);
if (ret == WAIT_OBJECT_0)
{
   // do something with overlapped1
}
else if (ret == WAIT_OBJECT_0 + 1)
{
   // do something with overlapped2
}


Or if can have code that will periodically check the result of handle with WaitForSingleObject, useful for sticking it in game loop:
1
2
3
4
5
if (WaitForSingleObject(overlapped1.hEvent, 0) == WAIT_OBJECT_0) // will not block
{
   // do something with overlapped1
}
// else it nothing happened with overlapped1, so skip it and check it later


Edited by Mārtiņš Možeiko on
It's not the WaitForMultipleObjects that will interfere with how existing read code works. It's the fact that I have to call ReadFile to even set up the event that I will be watching for a signal on. I don't know if there's a way to call ReadFile or something else to set up that event without reading one byte out of the file and therefore NOT reading it during the read loop I already have elsewhere.
But you will need to read anyway, no?
Then why not start reading, and then wait until its completed. Then process received data and issue next read call. Something like this:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// issue read
ReadFile(handle, buffer, count, NULL, &overlapped);

while (running)
{
    HANDLE events[] = { overlapped.hEvent, other handles... };
    DWORD ret = WaitOnMultipleObjects(ArraySize(events), events, FALSE, INFINITE);
    if (ret == WAIT_OBJECT_0)
    {
       DWORD read;
       GetOverlappedResult(handle, &overlapped, &read, TRUE);
       // process what was read
       ProcessReadBuffer(buffer, read);
       // issue next read call
       ReadFile(handle, buffer, count, NULL, &overlapped);
    }
    else ...

    // whatever
    // ...
}

Yes but the point is that the reading code is already written and cannot happen anywhere near when the event first happens, and is somewhat intricately related to another system. If I have to I can just read one byte from the pipe to create the event for me to wait on, and then when I reach the point where I am actually ready for input I will have a special case that lumps that first byte in with the rest of the first chunk that comes out.

BUt this is just complexity I would like to avoid by leaving the pipe itself untouched when the signal comes in, so that all the other code can remain simple.

Edited by Allen Webster on
You'll need to explain more. What is this other place? How does this other place decide when to read pipe?
What's the point of waiting for this event if other place is responsible for reading? Why not wait on the event in this other place?

In my previous example you don't need to ProcessReadBuffer and call next ReadFile when the event is signaled. You can simply set some boolean or whatever to indicate that data is available. And then process data in this other place (and issue next ReadFile call). Just make buffer/count available to that other place.

There is PeekNamedPipe call that would allow you to check if something is available in pipe. It's not blocking call, but it might help you if you can call it periodically and can afford to not sleep.
Yeah it's pretty hard to explain. The other location DOES already use Peek to see if there is anything to read.

The architecture of the system is basically like you have a big "update" function that, if it ran every frame, the program would be correct. But a lot of the time it does not need to run, so I have one spot in the main loop that is in charge of waiting. The hope is to set it up so that it wakes up whenever it needs to, but not more than that.

It looks like to get the effect I want I would have to switch a named pipe and then I could ReadFile with a size of 0.
So do you basically have one producer for the pipe and multiple consumers?
Well if you are using Peek, the just set boolean if WaitOnMultipleObjects got even for pipe, and in this other place check if boolean is set.

What is condition to wake up? When some handle is signaled or windows message is received? Then you can use MsgWaitForMultipleObjects function.
As far as I know, yes, you should be able to do a read of size 0. This is actually how IOCP normally works if you're doing the crazy "thread per connection" model in networking and so on. I've never actually tried a size-0 read without the IOCP (eg., just using hEvent), but I would assume it should work as well. It's at least worth trying ;)

The one thing to be aware of here, however, is that if there is data in the pipe at the time of the ReadFile, at least with IOCPs, Windows may elect not to post to the IOCP and instead return that the read completed immediately. I assume the same would be true of the hEvent model. So you will probably need to check the return status of the ReadFile and when you detect that the read 0 completed immediately, raise the event yourself.

- Casey