GetQueuedCompletionStatusEx behavior when alerted

896 views Asked by At

I'm using this function with fAlertable set to TRUE, because I use user alerts as a general thread interrupt mechanism in my framework. According to the comment on the MSDN page for this function,

When the call enters an alertable wait state and a User APC is available to dispatch the return value of the GetQueuedCompletionStatusEx is TRUE, ulEntriesRemoved is non-zero and your 'pack' of OVERLAPPED_ENTRYs is untouched. So if you ensure that all your OVERLAPPED_ENTRYs have a known unique value set for their lpOverlapped members, before you call the API , you can more easily detect whether a removed entry is an APC or an I/O completion packet by checking lpOverlapped for that magic value.

Why would I need to check all of the OVERLAPPED_ENTRYs rather than just the first one? Wouldn't GetQueuedCompletionStatusEx() always fill the output array in order, and thus I should only check the first one rather than loop over the whole array (or at least up to ulEntriesRemoved)? Moreover, given that this function dequeues multiple completion port entries simultaneously, is it guaranteed that it will never ever return both an alert and one or more completion entries? I'm especially concerned about this possibility given the note in the comment that ulEntriesRemoved will be nonzero during alert. Even though the comment on the page further mentions that "User APCs will not get dispatched if there are any I/O completion packets in the queue.", perhaps this user only tested in regards to any completion entries only queued up before the user alert.

Say the system queues in some mix of both user alerts to the thread and completion port entries, then the thread runs GetQueuedCompletionStatusEx(). From the documentation, I cannot tell for sure whether the function dequeues all the completion entries in this call, then one user alert per each subsequent call (assuming no more completion entries are queued). It would seem that's the most likely case given the documentation, but I don't want to make an assumption that may turn out to be wrong...

1

There are 1 answers

1
UltimaWeapon On BEST ANSWER

AFAIK, GetQueuedCompletionStatusEx return FALSE when User APC got executed. And GetLastError() return WAIT_IO_COMPLETION. Thus, it will never dequeue any completion packets when User APC got executed.

This is my code that work perfectly for me.

if (!::GetQueuedCompletionStatusEx(m_hIoCompletionPort, CompletionPackets, uCompletionPacketsToDequeue, &uCompletionPacketsRemoved, dwTimeToSleep, TRUE))
{
    DWORD dwError = ::GetLastError();

    if (dwError == WAIT_IO_COMPLETION)
    {
        // User Terminate Instance.
        ATLASSERT(m_blTerminating);
        hrResult = S_OK;
        break;
    }
    else if (dwError != ERROR_TIMEOUT)
    {
        hrResult = HRESULT_FROM_WIN32(dwError);
        break;
    }

    // One or more Timer has elapsed.
    ATLASSERT(m_Timers.GetCount() != 0);
    uCompletionPacketsRemoved = 0;
}