WaitForSingleObject() на порте завершения?

Сегодня я узнал, что могу вызвать CreateIoCompletionPort(), а затем передать возвращенный HANDLE в WaitForSingleObject():

#include <Windows.h>

int main()
{
    HANDLE h = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0);
    auto bRes = PostQueuedCompletionStatus(h, 1, 2, 0);
    if (!bRes) {
        abort();
    }

    auto dwRes = WaitForSingleObject(h, INFINITE);
    if (dwRes != WAIT_OBJECT_0){
        abort();
    }

    LPOVERLAPPED pOvr;
    DWORD cb;
    ULONG_PTR key;
    bRes = GetQueuedCompletionStatus(
        h, &cb, &key, &pOvr, INFINITE); // <-- returns 1, 2, nullptr

    if (!bRes) {
        abort();
    }

    dwRes = WaitForSingleObject(h, INFINITE); // <-- blocks here
    return 0;
}

Он работает, как и ожидалось, на моем компьютере с Windows 10.

Известно ли такое поведение, является ли оно законным или задокументировано? Я ничего не смог найти об этом.


person Sergei Vorobiev    schedule 20.09.2016    source источник
comment
dwRes = WaitForSingleObject(h, INFINITE); // <-- blocks here - действительно заблокировать? Уверен ? просто интересуюсь   -  person RbMm    schedule 21.09.2016
comment
Да. Аварийный дамп в состоянии ожидания и исходный код здесь: 1drv.ms/f/s!AtyOp6RRifUTuC2CAVzWdFrvZu6w   -  person Sergei Vorobiev    schedule 21.09.2016
comment
да, в win10 действительно ждите заголовка диспетчера KQUEUE. а скажем в win8.1 - нет.   -  person RbMm    schedule 21.09.2016
comment
ага, Server 2016 TP5 тоже не ждет, по крайней мере, в отладчике ядра — второе ожидание завершается с WAIT_OBJECT_0.   -  person Sergei Vorobiev    schedule 21.09.2016
comment
i быстрый тест - теперь в win10 (сборка 1607) IOCP работает как NotificationEvent (даже ручной сброс) - после PostQueuedCompletionStatus все потоки, ожидающие iocp, пробуждаются, но только один удаляет пакет   -  person RbMm    schedule 21.09.2016


Ответы (2)


Если вы читали WaitForSingleObject() документацию, ввод-вывод Порт завершения НЕ является разрешенным типом дескриптора:

Функция WaitForSingleObject может ожидать следующие объекты:

  • Уведомление об изменении
  • Консольный ввод
  • Мероприятие
  • Уведомление о ресурсах памяти
  • Мьютекс
  • Процесс
  • семафор
  • Нить
  • Таймер ожидания

Чтобы дождаться прибытия события завершения на порт, вы должны передать дескриптор GetQueuedCompletionStatus() самому и позволить ему заблокироваться до прибытия события или истечения времени ожидания.

#include <Windows.h>

int main()
{
    HANDLE h = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0);
    auto bRes = PostQueuedCompletionStatus(h, 1, 2, 0);
    if (!bRes) {
        abort();
    }

    LPOVERLAPPED pOvr;
    DWORD cb;
    ULONG_PTR key;
    bRes = GetQueuedCompletionStatus(
        h, &cb, &key, &pOvr, INFINITE); // <-- returns 1, 2, nullptr

    if (!bRes) {
        abort();
    }

    bRes = GetQueuedCompletionStatus(
        h, &cb, &key, &pOvr, INFINITE); // <-- blocks here

    return 0;
}
person Remy Lebeau    schedule 20.09.2016
comment
Спасибо за быстрый ответ :-) Функция WaitForSingleObject() также может ожидать дескриптор файла, хотя это явно не упоминается в документации по функции WaitForSingleObject() (см. blogs.msdn.microsoft.com/oldnewthing/20121012-00/?p=6343, дескриптор файла является ожидаемым объектом) . Я полностью осведомлен о правильном использовании портов завершения. Интересно, является ли порт завершения также ожидаемым объектом. - person Sergei Vorobiev; 20.09.2016
comment
В документации для GetOverlappedResult() упоминается файл с именем pipe и дескриптор коммуникационного устройства также являются ожидаемыми дескрипторами, но не рекомендуют ожидать их напрямую. Но в документации по портам завершения ввода-вывода ничего нет. говорит, что дескриптор порта завершения ввода-вывода можно ожидать напрямую. Если что-то не задокументировано, не полагайтесь на это. - person Remy Lebeau; 21.09.2016

Резюме:

  • не делай этого. См. Ответ Реми Лебо и MSDN о том, как правильно использовать порты завершения.
  • такое злоупотребление портами завершения не только недокументировано, но и ненадежно. Поведение зависит от сборки Windows 10.
  • порт завершения, вероятно, является допустимым объектом для ожидания в ядре, поскольку KQUEUE, который поддерживает порт завершения, имеет DISPATCHER_HEADER.
person Sergei Vorobiev    schedule 20.09.2016