2

In the MSDN doc for the lpOverlapped parameter of GetQueuedCompletionStatus it is said that the application can prevent completion port notification by setting the low-order bit of the hEvent member of the OVERLAPPED structure. But is it possible to resume the notifications after they were stopped?

I need to use this for monitoring network folders for changes:

When GetQueuedCompletionStatus returns FALSE and GetLastError() returns ERROR_NETNAME_DELETED, I do this (works):

di->Overlapped.hEvent = CreateEvent( NULL, FALSE, FALSE, di->lpszDirName );
reinterpret_cast<uintptr_t &>(di->Overlapped.hEvent) |= 0x1;

And when the network problem was resolved, I tried to do the reverse operation - but it DID NOT work:

reinterpret_cast<uintptr_t &>(di->Overlapped.hEvent) &= ~(0x1);

(It will be good if the solution be compatible with Windows 7)

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
neflow
  • 76
  • 3
  • the documentation is very bad and you misunderstand it. *resume completion port notification* is absolute senseless. it can not be suspended or resumed. what is said in msnd at all not related to `GetQueuedCompletionStatus` api, but for some io request - how skip queue packet to port for **this** request. and unclear at all why you try to do (you not show code) – RbMm Jan 27 '19 at 14:19
  • however from 3 lines of your codfe absolute unclear what is you try todo and how this at all related to `GetQueuedCompletionStatus` – RbMm Jan 27 '19 at 14:24

1 Answers1

2

first of all completion port notification can not be "suspended" or "resumed"

Even if you have passed the function a file handle associated with a completion port and a valid OVERLAPPED structure, an application can prevent completion port notification. This is done by specifying a valid event handle for the hEvent member of the OVERLAPPED structure, and setting its low-order bit. A valid event handle whose low-order bit is set keeps I/O completion from being queued to the completion port.

this mean the next - when we call some win32 I/O api (api which take pointer to OVERLAPPED as in/out parameter, such as ReadFile, ReadDirectoryChangesW, LockFileEx etc) and file handle (passed to this api) associated with a completion port - despite this we can prevent completion port notification for this call by event handle with low-order bit. this is for only concrete api call and not affect any another api calls. and all this unrelated to GetQueuedCompletionStatus

(strictly said we can simply pass 1 in place hEvent too. but in this case question - how we get notify about I/O complete, if api return pending status ? yes possible wait and on file handle only, call GetOverlappedResult. but this will be correct only in no any another I/O call on this file in concurent)

in any case need understand how this is internally work. all native I/O api have the next signature:

NTSTATUS NTAPI SomeIoApi(
                         _In_ HANDLE FileHandle,
                         _In_opt_ HANDLE Event,
                         _In_opt_ PIO_APC_ROUTINE ApcRoutine,
                         _In_opt_ PVOID ApcContext,
                         _Out_ PIO_STATUS_BLOCK IoStatusBlock, 
                         ...
                         );

all have this common 5 parameters at begin. for queue I/O completion as result of this call several conditions must be met. of course FileHandle must be associated with some completion port (to this port and can be packet sent). but else one mandatory condition - ApcContext must be not zero (ApcContext != 0). if this 2 condition met and device return not error status (if FILE_SKIP_COMPLETION_PORT_ON_SUCCESS set on file - must be pending status only) - when I/O complete - ApcContext pointer will be pushed to port. and then it can be removed by

NTSTATUS
NTAPI
NtRemoveIoCompletion(
    _In_ HANDLE IoCompletionHandle,
    _Out_ PVOID *KeyContext,
    _Out_ PVOID *ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_opt_ PLARGE_INTEGER Timeout
    );

or by it win32 shell GetQueuedCompletionStatus.

so solution for not sent packet to port (even is file handle associated with completion port) - set ApcContext = 0. win32 layer do this in next way (pseudo - code):

BOOL WINAPI SomeWin32Api(
                         HANDLE FileHandle,
                         LPOVERLAPPED lpOverlapped,
                         LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
             )
{

    HANDLE hEvent = lpOverlapped->hEvent;
    PVOID ApcContext = lpOverlapped;

    if ((ULONG_PTR)hEvent & 1)
    {
        reinterpret_cast<uintptr_t&>(hEvent) &= ~1;
        ApcContext = 0;
    }

    NTSTATUS status = SomeIoApi(
        FileHandle, 
        hEvent, 
        lpCompletionRoutine, // not exactly, but by sense
        ApcContext, 
        (PIO_STATUS_BLOCK)lpOverlapped,...);
}

it check low-order bit of hEvent in OVERLAPPED - if it set - pass 0 inplace ApcContext otherwise pass lpOverlapped (pointer to OVERLAPPED) as context ( ApcContext = lpOverlapped;)

note that nt layer let pass any void* pointer as ApcContext. but win32 layer always pass here pointer to OVERLAPPED structure or 0. because this and GetQueuedCompletionStatus return this pointer back as _Out_ LPOVERLAPPED *lpOverlapped (compare with NtRemoveIoCompletion - return as _Out_ PVOID *ApcContext)

anyway this trick affect only concrete single win32 I/O call, and if you late reset low-order bit in hEvent from overlapped ( reinterpret_cast<uintptr_t &>(di->Overlapped.hEvent) &= ~(0x1);) this already can not have any effect - the 0 in place ApcContext already passed.

also from general view this is rarely when we associate file handle with a completion port, but want not use it in some call. usually this is another api call. for example we can create asynchronous file handle, associate it with a completion port. and use port notifications in call WriteFile, but before begin write we can set/remove compression on file via FSCTL_SET_COMPRESSION. because file is asynchronous, the FSCTL_SET_COMPRESSION also can complete asynchronous, but we can want prevent completion port notification for this ioctl, instead wait inplace (on event) for it complete. for such situation and can be used this trick.

and in most case applications (if this not server with huge count of i/o requests) can instead manual call GetQueuedCompletionStatus, bind callback to file via BindIoCompletionCallback or CreateThreadpoolIo. as result system for you create iocp, thread pool which will be listen on this iocp (via GetQueuedCompletionStatus or NtRemoveIoCompletion) and then call your callback. this is very simplify your src code and logic


findings:

  • i almost sure (despite not view your code) that you not need at all use trick with event low-order bit
  • if you use this trick in some I/O request (say ReadDirectoryChangesW) this affect only this particular request
  • you can not change the behaviour by reset low-order bit in event handle after request is sent, or by any another way
  • you in general not need use GetQueuedCompletionStatus and self thread pool at all. instead simply call BindIoCompletionCallback for file
RbMm
  • 31,280
  • 3
  • 35
  • 56