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