Waiting for all handles with MsgWaitForMultipleObjects is a bug waiting to happen


The MsgWaitForMultipleObjects and MsgWaitForMultipleObjectsEx functions allow you to specify whether you want to want for any or all of the handles (either by passing bWaitAll = TRUE or by passing dwFlags = MWMO_WAITALL, accordingly). But you never want to wait for all handles.

Waiting for all handles means that the call does not return unless all the handles are signalled and a window message meeting your wake criteria has arrived. Since you are typically waiting for messages out of a sense of obligation (keeping the UI thread responsive) rather than one of expectation, even if all the handles you pass become signalled, your program will still stall until a window message arrives. (And if you thought you were being a good UI citizen by using MsgWaitForMultipleObjectsEx, you aren’t actually helping any because waiting for all objects means that the call will not return even if a message is ready, since it’s also waiting for those handles you passed.) Functions which are built on top of the MsgWaitForMultipleObjectsEx function such as MsgWaitForMultipleObjects and CoWaitForMultipleHandles suffer from the same problem.

The reason for this can be gleaned from the MsgWaitForMultipleObjectsEx documentation; you just have to put on your thinking cap. Notice that if a message arrives when you are waiting for any handle, the return value is WAIT_OBJECT_0 + cHandles. Notice also that the maximum number of objects you can wait on is MAXIMUM_WAIT_OBJECTS - 1. Obviously, what’s happening under the covers is that the MsgWaitForMultipleObjectsEx function creates a handle that will be signalled when the message queue reaches one of the states you requested in the wake mask, adds that handle to the end of the array you passed in, and then passes the whole thing to the WaitForMultipleObjectsEx function. (Note that the getting access to that internal handle won’t be of any use to you, the application, since you don’t know how to tell the window manager what wait states should result in the event being set.)

(Larry Osterman reminded me that he covered the same topic a while back. So now you get to see it twice.)

Comments (14)
  1. Neil says:

    If you never want to wait for all handles, then why was the option ever specified in the first place?

  2. Good Point says:

    The latest documentation is pretty clear. Under dwWakeMask it says "Input types for which an input event object handle will be added to the array of object handles"

    Now, how does one go about creating an "input event object handle"?

  3. oldnewthing says:

    Neil: Probably because somebody said, "We should expose all the functionality of WaitForMultipleObjects – otherwise I’m sure somebody will complain that we hid the functionality for some evil purpose."

    GP: You don’t create the event object handle; the window manager does.

  4. Jerry Pisk says:

    Waiting on a UI thread is a problem no mater what you do. If you need to wait for something then fire off a thread to do that. UI thread should be handling UI events, not waiting for some background processing to finish.

  5. BryanK says:

    Jerry — so what do you do when you have to wait until the thread that you fired off finishes?

    (Not just a thread to wait for something else, but think about a thread doing a bunch of CPU intensive work.)

  6. Pavel Lebedinsky says:

    what do you do when you have to wait until the thread that you fired off finishes?

    The worker thread could post a "finished" message to the UI thread.

  7. BryanK says:

    Oh, yeah, that would be an option, wouldn’t it. Heh. So much for that then.

  8. bmm6o says:

    BrianK: I agree. The docs as quoted by Larry are pretty clear. I could see how a casual reader could miss it though, especially as it seems much more useful for the function to return when all passed in handles are signalled OR there is a new message.

  9. Tim Lesher says:

    BrianK: not to split hairs, but the post says "The reason for this can be gleaned from the MsgWaitForMultipleObjectsEx documentation; you just have to put on your thinking cap."

    It doesn’t say "the fact that this happens can be gleaned…". If it did, I’d agree with you. No, you don’t need to think about the implementation. But Raymond’s saying that if you care about the implementation, with a tiny bit of thought you can figure it out.

    Nothing personal, but I always get a bad feeling from programmers who automatically trust the documentation and assume it tells them everything they need to know, and that they never need to think about the implementation or read between the lines.

    Or maybe I’ve just been programming Windows too long…

  10. BryanK says:

    Er, hang on a minute…

    > The reason for this can be gleaned from the

    > MsgWaitForMultipleObjectsEx documentation; you

    > just have to put on your thinking cap.

    I think you just have to read carefully. Note the documentation’s description of bWaitAll (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/msgwaitformultipleobjects.asp):

    ——–

    bWaitAll

    If this parameter is TRUE, the function returns when the states of all objects in the pHandles array have been set to signaled *and an input event has been received*.

    ——–

    (Emphasis mine.)

    This is explicit enough to tell me that it will wait until a message comes in *AND* all the handles are signaled. I don’t need to think about the implementation, I just need to read the description of the parameter and notice the difference between it and WaitForMultipleObjects.

    Larry, if you’re reading this, your post has the same issue. You even quote the MSDN doc page on MsgWaitForMultipleObjects (the text I’ve quoted above), and you say that it’s "the same as WaitForMultipleObjects" — but it isn’t. The WaitForMultipleObjects page says:

    ——–

    bWaitAll

    If this parameter is TRUE, the function returns when the state of all objects in the lpHandles array is signaled.

    ——–

    Perhaps this used to be an issue in the documentation or something, I don’t know. But it’s not an issue anymore — if people are still doing this, it has to be because they either can’t or won’t read the docs, not because there’s some deep thinking that they have to do.

    (Unless I misunderstand what an "input event" is. That’s always possible.)

  11. Chris Smith says:

    I can think of a reason to use MsgWaitForMultipleObjects with bWaitAll set to true: if you are waiting for a non-gui message, like a socket message from WSAAsyncSelect, or a background timer message.

    Your thread could be waiting for an event from an overlapped file operation, a mutex lock, and a socket select (delivered by msg) to be ready at the same time.

  12. Gideon says:

    Even with bWaitAll=FALSE there is (or was) a well-known problem with starvation. The kernel checks the event array sequentially. So basically if Handle[n] is constantly tripping it can starve all the handles > n.

    I noticed it when writing an NT network server application. The lowest numbered socket got serviced while the higher number sockets timed out. Investigation revealed that the select() Winsock API was calling MsgWaitForMultipleObjects().

    I dunno if they fixed it since NT.

  13. BryanK says:

    I was trying to say that I’m not so sure anything has to be "gleaned" — it’s pretty obvious just reading the bWaitAll docs.

  14. Mark Steward says:

    Gideon: surely by definition select returns all sockets that are ready? I admittedly don’t have experience of early NT, but it seems a major bug if it only returns one. And if simply doesn’t bother to check the entire set *after* calling MsgWaitForMultipleObjects, the next call to select would catch any others.

    But anyway, MsgWaitForMultipleObjects is documented as only returning the first handle in the array. "If multiple objects become signaled, the function returns the index of the first handle in the array whose object was signaled." I think this is something you have to program around :)

Comments are closed.