Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

One use for Overlapped I/O

0.00/5 (No votes)
14 Jan 2004 5  
How to use overlapped I/O

Introduction

If you�re like me you�ve taken one look at the Microsoft documentation on overlapped I/O and concluded that you don�t really need it. After all, for the vast majority of the applications I�ve had to work on, I/O processing goes something like this:

OpenFile()

While (File.HasData())
{
    ReadData();
    ProcessData();
    WriteData();
}

CloseFile()

Typically you don�t need to setup I/O operations to allow processing and reading/writing to overlap. Even if you did you could achieve similar results using multithreading. So why did Microsoft provide overlapping I/O? Well here�s one use for it.

Details

Let�s use named pipes instead of real files for our I/O. Let�s further assume we�re writing a server application, which will handle one or more instances of a named pipe. (See Conclusions below for why I used a named pipe in this example).

This is one (greatly simplified and not error checked) way to write the loop. I�m assuming a global BOOL variable called gbl_bStop initially set to FALSE.

while (!gbl_bStop)
{
    HANDLE h = CreateNamedPipe();

    if (ConnectNamedPipe(h))
        _beginthread(threadProc, 0, h);
}

This loop runs until gbl_bStop is set to TRUE. It creates a named pipe and then sits inside the ConnectNamedPipe() call waiting for a client to connect. When a client connects the ConnectNamedPipe() call returns and a new thread is launched to handle this particular client/server connection. The handle returned in the CreateNamedPipe() call is passed to the thread procedure, which will presumably use the handle for read or write operations.

Typically the above loop would be running in its own thread so that your application can handle other events.

This code works fine until you want to stop the server. The main thread sets gbl_bStop to TRUE and waits in vain for the thread to terminate. The problem is that very little of the threads time is spent executing code over which you have any control. Most of the time it�s sitting inside that ConnectNamedPipe() call, deep inside the Operating System, waiting for client connections.

Aha, we can work around that. Let�s set gbl_bStop to TRUE and then make a connection (from our main thread) to the named pipe. Well that will work but it also starts up yet another thread. Ok, we can work around that by checking gbl_bStop when we come out of the ConnectNamedPipe() call to see if we should continue or exit.

Now our loop looks like this:

while (!gbl_bStop)
{
    HANDLE h = CreateNamedPipe();

    if (ConnectNamedPipe(h) && !gbl_bStop)
        _beginthread(threadProc, 0, h);
}

Not much added code but don�t forget our shutdown routine also has to create a connection to the named pipe. It�s getting somewhat complex, with code needed to shut down our application scattered all over the place.

Overlapped I/O usage

This is where overlapped I/O comes in. Let�s write our loop like this instead. I�m assuming a global EVENT object called gbl_hStopEvent and I�m still leaving out a lot of detail.

OVERLAPPED  op;
HANDLE      h,
            handleArray[2];
BOOL        bStop = FALSE;
		
memset(&op, 0, sizeof(op));
handleArray[0] = op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
handleArray[1] = gbl_hStopEvent;
		
while (bStop == FALSE)
{
    h = CreateNamedPipe(FILE_FLAG_OVERLAPPED);

    ConnectNamedPipe(h, &op);
				
    switch (WaitForMultipleObjects(2, handleArray, FALSE, INFINITE))
    {
    case WAIT_OBJECT_0:
        _beginthread(threadProc, 0, h);
        ResetEvent(handleArray[0]);
        break;

    case WAIT_OBJECT_0 + 1:
        CloseHandle(h);
        bStop = TRUE;
        break;
    }
}

CloseHandle(handleArray[0]);

The key to this is the WaitForMultipleObjects(). We create an array containing a handle to the global stop event and a handle to a local event. The local event handle is also used in the OVERLAPPED structure. We create the named pipe using overlapped I/O and then call ConnectNamedPipe(). However, because we specified overlapped I/O the Operating System returns control to us immediately and we fall into the WaitForMultipleObjects() call. If a client connects to the named pipe the OS will signal the event handle we passed in the OVERLAPPED structure and the code for WAIT_OBJECT_0 is executed. If, on the other hand, our main thread signals gbl_hStopEvent the code for WAIT_OBJECT_0 + 1 is executed and we will exit the loop entirely.

The same technique can be used for the thread procedure using the named pipe connection and for the client side code.

Conclusions

I chose named pipes for this example for simplicity (the examples require fewer lines of code compared to a socket example). But the choice of something other than a regular file on the disk was deliberate. When reading a disk file you're working with a known and predictable entity. On the large time scale (the time scale humans experience) a read from a disk file takes very little time and it's easy to determine when the operation has reached the end (end of file). Thus the pseudocode I showed at the start of the article is reasonable. But when dealing with interprocess communications across (possibly) network connections it's not possible to determine with any kind of accuracy just when the operation might end.

An obvious (and wrong) solution might be to write a tight loop polling the named pipe for new data. But, given the asynchronous nature of a named pipe data conduit you run a risk of wasting an awful lot of CPU cycles executing that tight loop.

Overlapped I/O provides an easy way to ensure your application is always responsive to user input without consuming excessive amounts of CPU time. Code that doesn't run costs the least in performance.

History

  • 19 December 2002 - First version.

  • 15 January 2004 - Added some comments about why Named Pipes were used in the pseudocode.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here