GetQueuedCompletionStatus no puede sacar de la cola IO de IOCP si el subproceso que emitió originalmente la IO está bloqueando en ReadFile en Windows 8

Mi aplicación dejó de funcionar después de cambiar a Windows 8. Pasé horas para solucionar el problema, descubrí que IOCP se comporta de manera diferente entre Windows 8 y versiones anteriores. Extraigo el código necesario para demostrar y reproducir el problema.

SOCKET sListen;

DWORD WINAPI WorkerProc(LPVOID lpParam)
{
    ULONG_PTR dwKey;
    DWORD dwTrans;
    LPOVERLAPPED lpol;
    while(true)
    {
        GetQueuedCompletionStatus((HANDLE)lpParam, &dwTrans, &dwKey, (LPOVERLAPPED*)&lpol, WSA_INFINITE);
        printf("dequeued an IO\n");
    }
}
DWORD WINAPI StartProc(LPVOID lpParam)
{
    WSADATA WsaData;
    if (WSAStartup(0x202,&WsaData)!=0) return 1;
    sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    SOCKADDR_IN si;
    ZeroMemory(&si,sizeof(si));
    si.sin_family = AF_INET;
    si.sin_port = ntohs(1999);
    si.sin_addr.S_un.S_addr = INADDR_ANY;
    if(bind(sListen, (sockaddr*)&si, sizeof(si)) == SOCKET_ERROR) return 1;
    listen(sListen, SOMAXCONN);
    HANDLE hCompletion = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
    CreateIoCompletionPort((HANDLE)sListen, hCompletion, (DWORD)0, 0);
    CreateThread(NULL, 0, WorkerProc, hCompletion, 0, NULL);
    return 0;
}
DWORD WINAPI AcceptProc(LPVOID lpParam)
{
    DWORD dwBytes;
    LPOVERLAPPED pol=(LPOVERLAPPED)malloc(sizeof(OVERLAPPED));
    ZeroMemory(pol,sizeof(OVERLAPPED));
    SOCKET sClient = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    BOOL b = AcceptEx(sListen, 
        sClient,
        malloc ((sizeof(sockaddr_in) + 16) * 2), 
        0,
        sizeof(sockaddr_in) + 16, 
        sizeof(sockaddr_in) + 16, 
        &dwBytes, 
        pol);
    if(!b && WSAGetLastError() != WSA_IO_PENDING)   return 1;
    HANDLE hPipe=CreateNamedPipeA("\\\\.\\pipe\\testpipe",PIPE_ACCESS_DUPLEX,PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,PIPE_UNLIMITED_INSTANCES,4096,4096,999999999,NULL);
    BYTE chBuf[1024]; 
    DWORD  cbRead; 
    CreateFileA("\\\\.\\pipe\\testpipe", GENERIC_READ |GENERIC_WRITE,  0,NULL, OPEN_EXISTING, 0, NULL);
    ReadFile(hPipe,chBuf,1024, &cbRead,NULL);
    return 0;
}

int main()
{
    printf ("Starting server on port 1999...");
    WaitForSingleObject(CreateThread(NULL, 0, StartProc, NULL, 0, NULL),INFINITE);
    CreateThread(NULL, 0,AcceptProc, NULL, 0, NULL);
    printf ("done\n");
    Sleep(10000000);
    return 0;
}

Este programa escucha en el puerto 1999 y emite un protocolo asíncrono luego lee un canal de bloqueo. He probado este programa en Windows 7, 8, XP, 2003, 2008, después de "telnet 127.0.0.1 1999", "encolado una IO \ n" se imprimirá en la consola, excepto en Windows 8.

El punto es el subproceso que originalmente emitió la operación asíncrona, no debe bloquearse en ReadFile o GetQueuedCompletionStatus nunca eliminará esa IO hasta que ReadFile vuelva en Windows 8.

También probé utilizando "scanf" en lugar de leer el canal, los resultados son los mismos ya que "scanf" llamará a ReadFile para leer la consola eventualmente. No sé si ReadFile es la única función afectada o puede haber otras funciones.

Lo que se me ocurre es usar un subproceso dedicado para emitir operaciones asíncronas, y toda la lógica empresarial se comunica con ese subproceso dedicado para realizar aceptar / enviar / recibir. Pero una capa adicional significa una sobrecarga adicional, ¿hay alguna forma de lograr el mismo rendimiento que las versiones anteriores de Windows en Windows 8?

Respuestas a la pregunta(1)

Su respuesta a la pregunta