Queuing Asynchronous Procedure Calls
One thread (the boss) queues an APC to a target thread using QueueUserAPC.
DWORD QueueUserAPC (
PAPCFUNC pfnAPC,
HANDLE hThread,
DWORD dwData)
hThread is the handle of the target thread. dwData is an argument value that will be passed to the APC function when it is executed, and the value could represent a termination code or convey other information to the function.THReeStageCancel.c, in the main function (compare to Program 10-5), replaces TerminateThread calls with QueueUserAPC calls, as follows.
// TerminateThread (transmitter_th, 0); Replace with APC
// TerminateThread (receiver_th, 0); Replace with APC
tstatus = QueueUserAPC
(ShutDownTransmitter, transmitter_th, 1);
if (tstatus == 0) ReportError (
"Failed queuing APC for transmitter", 8, FALSE);
tstatus = QueueUserAPC
(ShutDownReceiver, receiver_th, 2);
if (tstatus == 0) ReportError (...);
The QueueUserAPC return value is nonzero for success or zero for failure. GetLastError(), however, does not return a useful value, so the ReportError call does not request an error message (the last argument is FALSE).pfnAPC is a pointer to the actual function that the target thread will execute, such as the following example used in ThreeStageCancel.c.
/* APC to shut down the receiver. */
void WINAPI ShutDownReceiver (DWORD n)
{
printf ("In ShutDownReceiver. %d\n", n);
/* Free any resource (none in this example). */
return;
}
ShutDownTransmitter is identical, other than the message text. It's not immediately clear why this function, which does nothing, can cause the target receiver thread to shut down. The next section will explain the process.
APCs and Missed Signals
A kernel mode APC (used in asynchronous I/O) can momentarily move a waiting thread out of its wait state, potentially causing a missed PulseEvent signal. Some documentation warns against PulseEvent for this reason, even though this chapter has demonstrated its usefulness. This risk is not an issue in our examples, which do not use kernel mode APCs. Furthermore, using SignalObjectAndWait and testing its return value is sufficient protection against this sort of missed signal. Finally, should there be a situation where a missed signal could occur, simply include a finite time-out period on the appropriate wait calls.
