Search code examples
cwinsock2overlapped-io

What is the purpose of WSA_WAIT_EVENT_0 in overlapped IO?


All my experience in networking has been on linux so I'm an absolute beginner at windows networking. This is probably a stupid question but I can't seem to find the answer anywhere. Consider the following code snippet:

    DWORD Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);
    WSAResetEvent( EventArray[Index - WSA_WAIT_EVENT_0]);

Every time an event is selected from the EventArray WSA_WAIT_EVENT_0 is subtracted from the index but WSA_WAIT_EVENT_0 is defined in winsock2.h as being equal to zero.

Why is code cluttered with this seemingly needless subtraction? Obviously the compiler will optimize it out but still don't understand why it's there.


Solution

  • The fact that WSA_WAIT_EVENT_0 is defined as 0 is irrelevant (it is just an alias for WAIT_OBJECT_0 from the WaitFor(Single|Multiple)Object(s)() API, which is also defined as 0 - WSAWaitForMultipleEvents() is itself just a wrapper for WaitForMultipleObjectsEx(), though Microsoft reserves the right to change the implementation in the future without breaking existing user code).

    WSAWaitForMultipleEvents() can operate on multiple events at a time, and its return value will be one of the following possibilities:

    WSA_WAIT_EVENT_0 .. (WSA_WAIT_EVENT_0 + cEvents - 1)
    

    A specific event object was signaled.

    WSA_WAIT_IO_COMPLETION
    

    One or more alertable I/O completion routines were executed.

    WSA_WAIT_TIMEOUT
    

    A timeout occurred.

    WSA_WAIT_FAILED
    

    The function failed.

    Typically, code should be looking at the return value and act accordingly, eg:

    DWORD ReturnValue = WSAWaitForMultipleEvents(...);
    if ((ReturnValue >= WSA_WAIT_EVENT_0) && (ReturnValue < (WSA_WAIT_EVENT_0 + EventTotal))
    {
        DWORD Index = ReturnValue - WSA_WAIT_EVENT_0;
        // handle event at Index as needed...
    }
    else if (ReturnValue == WSA_WAIT_IO_COMPLETION)
    {
        // handle I/O as needed...
    }
    else if (RetunValue == WSA_WAIT_TIMEOUT)
    {
        // handle timeout as needed...
    }
    else
    {
        // handle error as needed...
    }
    

    Which can be simplified given the fact that bAlertable is FALSE (no I/O routines can be called) and dwTimeout is WSA_INFINITE (no timeout can elapse), so there are only 2 possible outcomes - an event is signaled or an error occurred:

    DWORD ReturnValue = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);
    if (ReturnValue != WSA_WAIT_FAILED)
    {
        DWORD Index = ReturnValue - WSA_WAIT_EVENT_0;
        WSAResetEvent(EventArray[Index]);
    }
    else
    {
        // handle error as needed...
    }