Click here to Skip to main content
Click here to Skip to main content

One use for Overlapped I/O

, 14 Jan 2004
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Rob Manderson

United States United States
I've been programming for 35 years - started in machine language on the National Semiconductor SC/MP chip, moved via the 8080 to the Z80 - graduated through HP Rocky Mountain Basic and HPL - then to C and C++ and now C#.
 
I used (30 or so years ago when I worked for Hewlett Packard) to repair HP Oscilloscopes and Spectrum Analysers - for a while there I was the one repairing DC to daylight SpecAns in the Asia Pacific area.
 
Afterward I was the fourth team member added to the Australia Post EPOS project at Unisys Australia. We grew to become an A$400 million project. I wrote a few device drivers for the project under Microsoft OS/2 v 1.3 - did hardware qualification and was part of the rollout team dealing directly with the customer.
 
Born and bred in Melbourne Australia, now living in Scottsdale Arizona USA, became a US Citizen on September 29th, 2006.
 
I work for a medical insurance broker, learning how to create ASP.NET websites in VB.Net and C#. It's all good.
 
Oh, I'm also a Kentucky Colonel. http://www.kycolonels.org

Comments and Discussions

 
GeneralMy vote of 5 PinmemberAndyLau00429-Aug-13 18:14 
GeneralThanks! PinmemberDuojiao Yan8-Dec-11 20:44 
GeneralHere's another way... PinmemberPaul Sanders (AlpineSoft)2-Mar-08 7:21 
GeneralRe: Here's another way... Pinmemberjjshean20-Nov-09 9:21 
Questionwhich windows versions support it ?? Pinmemberpratibhap6-Feb-08 1:49 
AnswerRe: which windows versions support it ?? PinprotectorRob Manderson6-Feb-08 3:46 
GeneralGood paper around a bad desing Pinmemberdarbal20-Jan-04 2:21 
GeneralRe: Good paper around a bad desing Pinmemberedsela20-Jan-04 3:32 
GeneralRe: Good paper around a bad desing Pinmemberarmentage21-Jan-04 7:10 
GeneralExcellent ! But i have some problems. Pinmemberguit444-May-03 22:00 
GeneralRe: Excellent ! But i have some problems. Pinmemberignacio14-Feb-05 10:25 
Generalalso for sockets PinmemberForogar2-Apr-03 3:34 
GeneralThanks very much PinmemberPaul Farry17-Mar-03 12:10 
GeneralRe: Thanks very much PinmemberRob Manderson17-Mar-03 12:38 
GeneralRe: Thanks very much PinmemberPaul Farry17-Mar-03 18:55 
GeneralHelped Me PinmemberRenjith R23-Jan-03 7:37 
Generala good read... Pinmemberadrian cooper20-Dec-02 5:14 
GeneralNice PinmemberGiles19-Dec-02 22:58 
GeneralFinally.. Overlapped I/O explained PinmemberJubjub19-Dec-02 12:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 15 Jan 2004
Article Copyright 2002 by Rob Manderson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid