MsgWaitForMultipleObjects иногда возвращает WAIT_FAILED без значения GetLastError - PullRequest
1 голос
/ 15 октября 2010

У меня есть поток, который создает COM-объекты, для которых требуется однопоточная квартира.

Изначально основная функция этого потока помещала его в цикл WaitForMultipleObjects.Очевидно, это проблема, потому что она не позволяет насосу сообщений COM выполнять свою работу.

Я заменил это на MsgWaitForMultipleObjects в качестве решения, но теперь я сталкиваюсь с проблемой: иногда MsgWaitForMultipleObjects (часто) возвращает WAIT_FAILED, но не устанавливает ошибку.

Код обрабатывает возвращаемое значение WAIT_FAILED, просто продолжая и пытаясь вызвать MsgWaitForMultipleObjects снова.Вызов MsgWaitForMultipleObjects может возвращать WAIT_FAILED несколько раз (максимум, что я видел, это 9), но затем он неожиданно работает без проблем.

Код написан так, чтобы он мог потенциально зайти в бесконечный цикл, если бы функция возвращала WAIT_FAILED по уважительной причине.Я знаю, что должен это исправить, но в настоящий момент я считаю, что это «обходной путь», потому что вызов MsgWaitForMultipleObjects в конечном итоге завершится успешно.

Этот код тестируется в Windows 7, Vista и XP (все32-битная, Windows 7 32-битная и 64-битная).

Кто-нибудь знает, почему это происходит?

Соответствующий код:

bool run = true;
while (run)
{
    DWORD ret = MsgWaitForMultipleObjects(2, events, FALSE, INFINITE, 
        QS_ALLINPUT);

    switch (ret)
    {
        case WAIT_OBJECT_0:
        {
            ResetEvent(events[0]);
            ProcessWorkQueue();
            break;
        }

        case WAIT_OBJECT_0 + 1:
        {
            run = false;
            break;
        }

        case WAIT_OBJECT_0 + 2:
        {
            MSG msg;
            while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
                DispatchMessage(&msg);
            break;
        }

        case WAIT_FAILED:
        {
            Logger::Output(L"Wait failed in Notify::Run(), error is " 
                + boost::lexical_cast<std::wstring>(GetLastError()));
        }
    }
}

Примервывод будет:

Wait failed in Notify::Run(), error is 0
Wait failed in Notify::Run(), error is 0
Wait failed in Notify::Run(), error is 0
Wait failed in Notify::Run(), error is 0
Wait failed in Notify::Run(), error is 0
Wait failed in Notify::Run(), error is 0
Wait failed in Notify::Run(), error is 0
Wait failed in Notify::Run(), error is 0
Wait failed in Notify::Run(), error is 0
// At this point, the wait succeeds

Я считаю, что возвращаемое значение WAIT_FAILED происходит только после того, как ожидание было прервано сообщением.

1 Ответ

2 голосов
/ 16 октября 2010

Этого не должно быть, и я точно не могу объяснить , почему так и происходит.Однако у меня есть несколько указателей.

Во-первых, вы не будете звонить TranslateMessage() до DispatchMessage() в свой почтовый насос.Это плохое джиу, и вы не хотите, чтобы плохое джиу было где-то рядом с MsgWaitForMultipleObjects().

Вы также можете попробовать явно вызвать MsgWaitForMultipleObjectsEx(), на случай, если у вас не возникнет та же проблема:

DWORD ret = MsgWaitForMultipleObjectsEx(2, events, INFINITE, QS_ALLINPUT, 0);

Наконец, это может быть надуманным, но подумайте, что происходит после возврата MsgWaitForMultipleObjects() и до вызова GetLastError().Не обращая внимания на присваивание ret, я вижу неявный вызов конструктора std::wstring.

Можете ли вы гарантировать, что у конструктора std::wstring нет побочного эффекта, который очищает последнюю ошибку потокакод?Я уверен, что не могу, поэтому я бы переместил вызов GetLastError() в хорошее старомодное атомарное присваивание переменной DWORD в первой строке оператора case.

...