Faulty Message Loop


The following code is an example of a message loop that will result in a hang.  Library or COM developers may be tempted to use this type of loop to pump messages while waiting for a particular message to be posted (i.e., waiting for background activity to complete), but the wrong approach is used:

// Bad message loop - do not use!
while (GetState() != dwDesiredState)
{
    DWORD dwWakeReason = MsgWaitForMultipleObjects(
        0, 0, FALSE, 5000, QS_POSTMESSAGE);
    if (WAIT_OBJECT_0 == dwWakeReason)
    {
        while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE) == TRUE)
        {
            if (WM_QUIT == msg.message)
            {
                PostQuitMessage((int)msg.wParam);
                SetState(dwDesiredState);
            }

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
}

This message loop is only pulling some of the messages off of the message loop.  User input is being ignored, and a hang will result!  The reason is the dwWakeMask parameter to MsgWaitForMultipleObjects - it is only looking for messages posted to the message queue.


The really bad news is that this message loop will periodically process a message from the thread's message queue, thereby bypassing the Windows mechanism for detecting stale message queues.  Users will see an unresponsive window, but they will not get the ghosting/frosting experience.  This is among the worst user experiences possible for all classes of hangs because the user has no recourse through the application's user interface.  Even though ghosting/frosting is not a pleasant experience, at least the OS will allow the user some degree of control over their application.


If the call to PeekMessage() includes a flag for PM_NOYIELD, the application will then experience a hang that results in ghosting/frosting.  Of course, the problem of the hang still exists, but at least the user can choose to stop waiting on the application.


The solution is to use a less restrictive wake mask in the call to MsgWaitForMultipleObjects, such as QS_ALLINPUT.  Even though this particular piece of code may only be interested in a small class of events to awaken a thread, it needs to be well behaved with all other code that resides in the process.


Comments (0)

Skip to main content