Windows System Programming Third Edition [Electronic resources]

Johnson M. Hart

نسخه متنی -صفحه : 291/ 155
نمايش فراداده

  • 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
    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;
    }
    

    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