What’s the point of passing a never-signaled event to MsgWaitForMultipleObjects?

In the Quake source code, there is this variable tevent whose usage is rather strange.

 49 static HANDLE   tevent;

It is initialized at program startup to a newly-created unsignaled event.

660     tevent = CreateEvent(NULL, FALSE, FALSE, NULL);
662     if (!tevent)
663         Sys_Error ("Couldn't create event");

and it is cleaned up a program shutdown:

267         if (tevent)
268             CloseHandle (tevent);

and the only use of it is in this call to Msg­Wait­For­Multiple­Objects:

535         MsgWaitForMultipleObjects(1, &tevent, FALSE, time, QS_ALLINPUT);

In true angry developer fashion, this is in a function with the banner

520 =================================================================
524 =================================================================

Anyway, when the bWaitAll parameter is FALSE, the Msg­Wait­For­Multiple­Objects function waits for one of three things to happen:

  • One of the handles is signaled,
  • The queue is in a state specified by the filter, or
  • The timeout elapses.

Since the code never signals the event, the first case neve occurs, so the only things that will cause Msg­Wait­For­Multiple­Objects to return are the second or third cases.

The dummy event is not actually necessary.

MsgWaitForMultipleObjects(0, NULL, FALSE, time, QS_ALLINPUT); 

If bWaitAll is TRUE, then the Msg­Wait­For­Multiple­Objects function waits for one of two things to happen:

  • All of the handles is signaled and the queue is in a state specified by the filter, or
  • The timeout elapses.

If you pass no handles, then the first part of the first case is vacuously satisfied (due to the magic properties of the empty set), so the things that will cause the function to return are either that the queue is in a required state or the timeout elapses.

The fact that the handle count can be any value up to MAXIMUM_WAIT_OBJECTS minus one gives you some insight into the internal implementation of the Msg­Wait­For­Multiple­Objects function: It takes the handle array you pass, and adds another handle that is signaled when the queue is in the desired state. It then calls the Wait­For­Multiple­Objects with the same bWaitAll parameter. That explains why passing bWaitAll = TRUE requires all the handles to be signaled and the queue to be in the requested state.

If you don't want to rely on the magical properties of the empty set, you could instead use a handle that you already know will never be signaled: You can use Get­Current­Process() or Get­Current­Thread(). The current process pseudohandle and current thread pseudohandle will become signaled when the process or thread terminates, but this is code running on that thread in that process. The thread cannot outlive itself.

Bonus chatter 2: Here's why I'm in the Quake credits.

Comments (6)
  1. Rising says:

    Raymond, in the statement
    MsgWaitForMultipleObjects(0, NULL, FALSE, time, QS_ALLINPUT);
    did you mean to pass TRUE instead of FALSE for the fWaitAll argument?

    1. uffa8 says:

      in this call no difference between TRUE (WaitAll) and FALSE(WaitAny), because in this case internally called `KeWaitForMultipleObjects` with single event (related to thread input). when object count (in call `KeWaitForMultipleObjects` ) is 1 – WaitAll == WaitAny by sense

  2. SpecLad says:

    I wish there was a way to get this hidden input event handle directly. It seems very inelegant to have to use a separate function when you want to wait for both objects and window messages.

    1. RP (MSFT) says:

      Without any special knowledge of the internals, I’d guess that the event doesn’t even exist – or if it does exist, it’s never signaled – except when MsgWaitForMultipleObjects is executing. The specific conditions that would cause it to be signaled are unknown until you specify them in the arguments to that function.

  3. Henke37 says:

    I take it that the special internal handle is for an object that is not part of the documented api and is subject to change at will.

  4. Gee Law says:

    I don’t really understand why people will have problem understanding the bWaitAll argument in MsgWaitForMultipleObjects (as suggested by Larry Osterman’s post). The docs clearly say true = all and input, false = one or input. The only quirk for people unfamiliar with mathematical logic would be the empty set case, where all means none (automatically qualified) and one is never satisfied, in which case the effect of passing true/false will be the same — wait for the input event.

Comments are closed.

Skip to main content