Click Here to Install Silverlight*
United StatesChange|All Microsoft Sites
MSDN
|Developer Centers|Library|Downloads|Code Center|Subscriptions|MSDN Worldwide
Search for


Advanced Search
MSDN Home > MSJ > January 1997
January 1997


Code for this article: Win320197.exe (13KB)
Jeffrey Richter wrote Advanced Windows, Third Edition (Microsoft Press, 1997) and Windows 95: A Developer's Guide (M&T Books, 1995). Jeff is a consultant and teaches Win32-based programming seminars. He can be reached at v-jeffrr@microsoft.com.

Let me start this column with a bug fix for my program that demonstrated how to delete an executable while it was running (MSJ January 1996). Reader Gord Umphrey pointed out this bug to me and also sent in the correction for it.
In my source code file, RFOR.C, search for the if statement on the first line shown below and insert the lines in italics.

 if (pszWinInit != NULL) {
 
    // Make sure a zero-character terminates the string.
pszWinInit[dwFileSize] = '\0'; // Search for the [Rename] section in the file. LPSTR pszRenameSecInFile = strstr(pszWinInit, szRenameSec); • • •
This additional line fixes a problem that could cause access violations when strstr is called. The problem is that the memory-mapped string may not be terminated with a zero character, and therefore strstr doesn't know when it has reached the end of the string. The solution is to force a terminating zero character at the end of the string.

Q I need to solve a complex thread synchronization problem. I'm aware that the WaitForMultipleObjects API allows my thread to wait for either a single object or for all of the specified objects to be signaled. WaitForMultipleObjects almost fills my needs, but what I really want is a function that allows me to express a richer waiting criteria.
For example, let's say that I have three kernel objects: a mutex, a semaphore, and an event. I want my thread to sleep until either the mutex and the semaphore are both signaled or until the mutex and the event are both signaled. As far as I can tell, WaitForMultipleObjects won't let me accomplish this. How can I get this behavior?

Lauren Calia
Columbia, MD

A You are correct: Win32 does not offer an API that allows you to wait on multiple Boolean expressions. But with a little creative use of the existing Win32 APIs, you can create your own WaitForMultipleExpressions function that does what you want. I created my own WaitForMultipleExpressions function that works on Windows® 95 and Windows NT® 4.0 or later.
My WaitForMultipleExpressions function has the following prototype:


 DWORD WINAPI WaitForMultipleExpressions(DWORD cExpObjects, 
                                         CONST HANDLE* pchExpObjects,
                                         DWORD dwTimeOut);
To call this function, you must first allocate an array of HANDLEs and initialize all of the array entries. The cExpObjects parameter indicates the number of entries in the array pointed to by the pchExpObjects parameter. This array contains multiple sets of kernel object handles, with each set separated by a NULL handle entry. WaitForMultipleExpressions treats objects within a single set as being ANDed together and treats the individual sets as being ORed together. So a call to WaitForMultipleExpressions suspends the calling thread until all the objects within a single set are signaled at the same time.
Here's an example. Suppose that we are working with four kernel objects:
Object
Handle Value
Mutex
0x1111
Semaphore
0x2222
Event
0x3333
Process
0x4444

Initializing the array of handles as they appear in Figure 1 instructs WaitForMultipleExpressions to suspend the calling thread until either the mutex AND the process are signaled, OR the semaphore AND the event AND the process are signaled, OR the mutex and the semaphore are signaled. You may recall that WaitForMultipleObjects does not allow you to indicate a kernel object more than once when you call it. When calling WaitForMultipleExpressions you may not indicate a single kernel object more than once within a single set, but different sets may refer to a single kernel object. Also, WaitForMultipleObjects does not allow you to pass an array of handles that exceeds 64 (MAXIMUM_ WAIT_OBJECTS) entries. With WaitForMultipleExpressions, the handle array may be much larger than 64 entries; however, you must not have more than 64 sets, and each set can contain no more than 63 handles.
Figure 2 shows WaitForMultipleExpressions' possible return values. If an expression does become true, WaitForMultipleExpressions returns the WAIT_OBJECT_0-based index of that expression. Using the example above, if the mutex and the process object become signaled, WaitForMultipleExpressions returns an index of 2.
That's all there is to using WaitForMultipleExpressions. To test my function, I wrote the program, TWFME, which can be downloaded from www.msj.com. When you run it, the dialog box in Figure 3 is displayed.

Figure 3 WaitForMultipleExpressions
Figure 3 WaitForMultipleExpressions

If you don't change any of the settings and press the Wait For Multiple Expressions button, the application creates three event kernel objects that are all initially not signaled, and places one entry in the multicolumn, multiselection listbox for each kernel object. Then the test application parses the expression field and constructs the array of handles. I've chosen four kernel objects and an expression that coincides with my earlier examples.
Since I've specified a timeout of 30000 milliseconds, you now have 30 seconds to signal and unsignal the event objects by toggling entries in the listbox on and off. As soon as you toggle enough entries to satisfy one of the expressions, WaitForMultipleExpressions returns and indicates which expression was satisfied at the bottom of the dialog box. If no expressions are satisfied within the 30 seconds, the word "Timeout" appears.
Now I'll discuss how I implemented WaitForMultipleExpressions. This was not an easy function to implement, and there are definitely some overhead issues to be concerned with when you're using it.
As you know, Win32 offers the WaitForMultipleObjects API that allows a thread to wait on a single AND expression:

 DWORD WaitForMultipleObjects(DWORD cObjects, 
                              CONST HANDLE* pchObjects, 
                              BOOL fWaitAll, DWORD dwTimeout);
To extend this functionality to include OR expressions, I must spawn multiple threads: one thread for each ORed expression. Each of these individual threads waits on a single AND expression using WaitForMultipleObjectsEx. (I use WaitForMultipleObjectsEx instead of the more common WaitForMultipleObjects for reasons that I'll discuss later). When one of the expressions comes true, one of the spawned threads wakes up and terminates.
The thread that called WaitForMultipleExpressions (which is the same thread that spawned all the OR threads) needs to wait until one of the OR expressions comes true. It does this by calling the Win32 WaitForMultipleObjects API. The number of threads spawned (OR expressions) is passed for the cObjects parameter, and the phObjects parameter points to an array containing the list of spawned thread handles. For the fWaitAll parameter, FALSE is passed so that the main thread will wake up as soon as any of the expressions come true. Finally, the dwTimeout value passed to WaitForMultipleExpressions is passed to WaitForMultipleObjects.
If none of the expressions comes true in the specified time, WAIT_TIMEOUT is returned from WaitForMultipleObjects, and WAIT_TIMEOUT will be returned from WaitForMultipleExpressions as well. If an expression does come true, WaitForMultipleObjects returns the index that indicates which thread terminated. Since each thread is a separate expression, this index also indicates which expression came true. The same index will be returned from WaitForMultipleExpressions.
That's the executive summary explaining how WaitForMultipleExpressions works. However, there are three small details that need to be addressed.
First, I don't want it to be possible for multiple OR threads to wake up from their call to WaitForMultipleObjects simultaneously, because successfully waiting on some kernel objects causes the object to alter its state. For example, waiting on a semaphore causes its count to decrement by one. WaitForMultipleExpressions waits for just one expression to come true, so I must prevent an object from altering its state more than once.
The solution to this problem is actually quite simple. Before I spawn the OR threads, I create a semaphore object of my own with an initial count of 1. Then each OR thread's call to WaitForMultipleObjects includes the handle to this semaphore, along with the other handles in the expression. This explains why each set can specify no more than 63 handles. For an OR thread to wake up, all the objects that it's waiting on must be signaled—this includes my special semaphore. Since I gave my semaphore an initial count of 1, I'm guaranteed that no more than one OR thread will ever wake up; therefore, no other objects will accidentally have their states altered.
The second detail that needs to be addressed is how to force a waiting thread to stop waiting in order to clean up properly. Adding the semaphore guarantees that no more than one thread wakes up, but after I know which expression came true, I need to force the remaining threads to wake up so that they can terminate cleanly, freeing their stack. Calling TerminateThread should always be avoided, so another mechanism is required.
After thinking a while, I remembered that waiting threads are forced to wake up if they are in an altertable state when an entry enters their Asynchronous Procedure Call (APC) queue. I'm aware that most programmers aren't familiar with the APC queue, so I'll give some background information here.
Whenever a thread is created, the system also creates a queue and associates it with the thread. This queue is called the APC queue. The thread's APC queue works similarly to the window message queue. However, the APC queue is implemented by the low-level guts of the kernel, whereas the window message queue is implemented at a much higher level—the user-interface components of the system. Because the APC queue is implemented by the kernel, it's more efficient and operations involving it execute much quicker.
When performing asynchronous I/O, it's possible to have completed I/O notifications queue to the issuing thread's APC queue (for more information about this, see my book, Advanced Windows, Third Ed., Microsoft Press
®, 1996). Each queued notification includes the address of a callback routine that will be called to get the complete I/O notification. As I/O requests complete, they are simply queued to your thread's APC queue—the callback routine is not immediately called because your thread may be busy doing something else and cannot be interrupted. To process entries in your thread's APC queue, the thread must put itself in an alertable state. This means that your thread has reached a position in its execution where it can handle being interrupted. There are five Win32 APIs (see Figure 4) that place a thread into an alertable state.
The last argument to the first four APIs is a Boolean value indicating whether the calling thread should place itself into an alertable state or not. For MsgWaitForMultipleObjectEx, you must use the MWMO_ALERTABLE flag to have the thread enter an alertable state. If you're familiar with the Sleep, WaitForSingleObject, and WaitForMultipleObjects APIs, you know that, internally, these non-Ex APIs call their Ex counterparts, always passing FALSE for the fAlertable parameter.
When you call one of the five APIs above and place your thread in an alertable state, the system first checks your thread's APC queue. If there is at least one entry in the queue, the system does not put your thread to sleep; instead, the system pulls the entry from the APC queue and your thread calls the callback routine. When the callback routine returns to the system, the system checks for more entries in the APC queue. If more entries exist, they are processed. If no more entries exist, your call to the alertable API returns. If there are any entries in your thread's APC queue when you call any of these APIs, your thread never sleeps!
The only time that these APIs suspend your thread is if there are no entries in your thread's APC queue at the time when you call the API. Now, while your thread is suspended, it will wake up if the kernel objects that you're waiting on become signaled or if an APC entry appears in your thread's queue. Since your thread is in an alertable state, the system wakes your thread as soon as an APC entry appears. The system empties the queue (by calling the callback routines), and the APIs immediately return to the caller—your thread does not go back to sleep waiting for kernel objects to become signaled.
The return value from these five APIs indicates why they have returned. If the return value is WAIT_IO_COMPLETION, you know that the thread is continuing to execute because at least one entry was processed from the thread's APC queue. If the return value is anything else, it indicates that the thread woke up because the sleep period expired, the specified kernel objects became signaled, or a mutex was abandoned.
Windows 95 and Windows NT 4.0 introduce a new API that allows you to manually queue an entry to a thread's APC queue:

 DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, DWORD dwData);
The first parameter is a pointer to an APC function and must have the following prototype:

 VOID WINAPI APCFunc(DWORD dwParam);
The second parameter is the handle of the thread that you wish to queue the entry for. Note that this can be any thread in the system. If hThread identifies a thread in a different process's address space, then pfnAPC must specify the memory address of a function that is in the address space of the target thread. The last parameter to QueueUserAPC, dwData, is a 32-bit value that simply gets passed to the callback function.
Even though QueueUserAPC is prototyped as returning a DWORD, it actually returns a BOOL indicating success or failure. By the way, QueueUserAPC is undocumented in Windows 95, but it does exist and is exported from Kernel32.DLL. Although it is not in the Windows 95 documentation, it is in the Windows NT 4.0 documentation, and I have found it to work fine under Windows 95.
My implementation of WaitForMultipleExpressions uses QueueUserAPC to force waiting threads to wake up. After my main thread's call to WaitForMultipleObjects returns, I queue an APC event to each of the still-waiting OR threads:

 // Break all the waiting expression threads out of 
 // their wait state so that they can terminate cleanly.
 for (dwExpNum = 0; dwExpNum < dwNumExps; dwExpNum++) {
    if ((WAIT_TIMEOUT == dwWaitRet) || 
        (dwExpNum != (dwWaitRet - WAIT_OBJECT_0))) {
       QueueUserAPC(WFME_ExpressionAPC, 
          ahThreads[dwExpNum], 0);
    }
 }
Because I really don't have anything to do and I just want the thread to stop waiting, the callback function, WFME_ExpressionAPC, looks like this:

 // This is the APC callback routine function
 VOID WINAPI WFME_ExpressionAPC (DWORD dwData) {
     // This function intentionally left blank
 }
The third and last detail has to do with handling timeouts correctly. If none of the expressions comes true while waiting, the main thread's call to WaitForMultipleObjects returns with a value of WAIT_TIMEOUT. If this happens, I want to prevent any of the expressions from coming true and potentially causing objects to alter their state. The code in Figure 5 shows how I accomplish this.
I prevent the other expressions from coming true by waiting on the semaphore. This will decrement the semaphore's count to zero and none of the OR threads will be able to wake up. But it's possible that an expression came true somewhere after the main thread's call to WaitForMultipleObjects and its call to WaitForSingleObject. This is why I check the return value of calling WaitForSingleObject. If it returns WAIT_OBJECT_0, the main thread got the semaphore and none of the expressions can come true. If WAIT_TIMEOUT is returned, then an expression did come true before the main thread got the semaphore. To determine which expression came true, the main thread calls WaitForMultipleObjects again with a timeout of INFINITE, which is OK because I know that an OR thread got the semaphore and is about to terminate.
At this point, I must force the OR threads to wake up so that they exit cleanly. The loop that calls QueueUserAPC (in the code fragment on the previous page) does this.

Have a question about programming in Win32? Send your questions via email to Jeffrey Richter: v-jeffrr@microsoft.com.

From the January 1997 issue of Microsoft Systems Journal. Get it at your local newsstand, or better yet, subscribe.

© 1997 Microsoft Corporation. All rights reserved. Legal Notices.

© 2014 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy & Cookies
Microsoft