Alertable Wait States
The last SignalObjectAndWait parameter, bAlertable, has been FALSE in previous examples. By using trUE instead, we indicate that the wait is a so-called alertable wait and the thread enters an alertable wait state. The behavior is as follows.
- If one or more APCs are queued to the thread (as a QueueUserAPC target thread) before either hObjectToWaitOn (normally an event) is signaled or the time-out expires, then the APCs are executed (there is no guaranteed order) and SignalObjectAndWait returns with a return value of WAIT_IO_COMPLETION.
- If an APC is never queued, then SignalObjectAndWait behaves in the normal way; that is, it waits for the object to be signaled or the time-out period to expire.
Alterable wait states will be used again with asynchronous I/O (Chapter 14); the name WAIT_IO_COMPLETION comes from this usage. A thread can also enter an alertable wait state with other alertable wait functions such as WaitForSingleObjectEx, WaitForMultipleObjectsEx, and SleepEx, and these functions will be useful when performing asynchronous I/O.q_get and q_put (see Program 10-4) can now be modified to perform an orderly shutdown after an APC is performed, even though the APC function does not do anything other than print a message and return. All that is required is to enter an alertable wait state and to test the SignalObjectAndWait return value, as shown by the following modified version of q_get (see QueueObjCancel.c on the Web site).
Program 10-6. q_get Modified for Cancellation
The APC routine could be either ShutDownReceiver or ShutDownTransmitter, as the receiver and transmitter threads use both q_get and q_put. If it were necessary for the shutdown functions to know which thread they are executed from, use different APC argument values for the third QueueUserAPC arguments in the code segment preceding
DWORD q_put (queue_t *q, PVOID msg, DWORD msize, DWORD MaxWait)
{
BOOL Cancelled = FALSE;
if (q_destroyed(q)) return 1;
WaitForSingleObject (q->q_guard, INFINITE);
while (q_full (q) && !Cancelled) {
if (SignalObjectAndWait(q->q_guard, q->q_nf, INFINITE, TRUE)
== WAIT_IO_COMPLETION) {
Cancelled = TRUE;
continue;
}
WaitForSingleObject (q->q_guard, INFINITE);
}
/* Put the message in the queue. */
if (!Cancelled) {
q_remove (q, msg, msize);
/* Signal that queue is not full as we've removed a message. */
PulseEvent (q->q_nf);
ReleaseMutex (q->q_guard);
}
return Cancelled ? WAIT_TIMEOUT : 0;
}
