David,
The WDK 6000 says under KeWaitForSingleObject():
If the WaitMode parameter is UserMode, the kernel stack can be swapped out
during the wait. Consequently, a caller must never attempt to pass
parameters on the stack when calling KeWaitForSingleObject using the
UserMode argument. If you allocate the event on the stack, you must set the
WaitMode parameter to KernelMode.
Actually, I prefer not to take MSDN too literally, and try my best to understand *why* it works this way (although, to be honest, sometimes it leads me to the wrong conclusions). Therefore, let’s do a bit of analysis.
Let’s say you have allocated an event on the stack, and wait on it. Who may possibly have an access to it, apart from your thread??? Who is going to signal it then???
Therefore, the only way you can use this event is to specify it as an argument in IoBuildxxx call in order to build IRP, and then to wait on it after having sent IRP to the target device. When the request completes, IoCompleteRequest() will issue a special kernel-mode APC to the thread, and APC routine will set the event. However, what happens if you have entered the waiting state at APC_LEVEL??? At this point APC delivery to the thread will be disabled, so that IoCompleteRequest(), apparently, will have to signal the event itself, and IoCompleteRequest() for asynchronously completed request normally runs at DPC level. At this point it event’s (and, hence, the target thread’s stack) constant presence in RAM becomes a requirement.
However, when you wait on, say, timer, why should your stack be always present on RAM, especially if you wait for extended periods of time, even if you wait in the kernel mode???
Therefore, I believe that by specifying "KernelMode’ as WaitMode parameter, you just tell the system
to do double-checking, because your stack’s presence in RAM *may* be required - you don’t give it
the precise directive to keep in in RAM all the time (when you specify ‘UserMode’, you tell the system that your stack’s presence in RAM is just not needed). In addition to that, it tells the system how possible thread termination should be handled - a thread that waits in the kernel mode cannot get terminated until the wait is satisfied, but the one that waits in the user mode can be terminated
while in the waiting state…
Anton Bassov