Providing a small amount of additional control over what happens when an asynchronous I/O completes synchronously
SetFileCompletionNotificationModes function gives you some control over how the system behaves when an overlapped I/O completes synchronously.
SUCCESS flag specifies that a completion is not queued to the associated I/O completion port in the case the call completes synchronously. In that case, you can reclaim the
OVERLAPPED structure immediately, since that is the last you will ever know about the I/O. Normally, there would be a completion queued to the completion port, but since you suppressed even that, there’s nothing more going on.
The other flag is more confusing. The
HANDLE flag specifies that the I/O Manager “does not set the event for the file object.”
The phrase “the event for the file object” was written by someone wearing kernel-colored glasses.
There is a secret event inside every file object. You cannot access this event directly, but you can observe it by calling a function like
WaitForSingleObject and passing it a file handle. When you ask to wait on a file handle, the kernel waits on the secret event hiding inside the file object.
This secret event is reset when the file operation starts, and it is set when the file operation completes. This secret event is internally how the kernel implements I/O to synchronous file handles: It issues an asynchronous I/O operation, and then waits on the file object.
You too can use this secret event by issuing an asynchronous I/O operation with
hEvent = nullptr, and then waiting directly on the file handle. This is also why the
GetOverlappedResult function takes a handle as its first parameter: It waits on the handle if the
OVERLAPPED structure didn’t have an event handle in it.
Of course, if you choose to rely on the secret kernel event, it is your own responsibility to make sure you don’t have more than one I/O operation outstanding at a time, because there is only one secret event. If you mistakenly have two I/O operations outstanding, then the first one to complete will set the secret event, and the second one to complete will set the already-set event, which has no effect.
The documentation for the
HANDLE flag is talking about the secret event. It says that if you enable this feature on a handle, then the secret event will not be set if the operation returns success (indicating synchronous completion) or if it returns
PENDING (indicating that the operation is continuing asynchronously).
It’s hard for me to come up with a scenario where you would even need to do something like this. The secret event is not something that is widely used, and given the fact that you have to exercise special care to ensure you have at most one outstanding operation at a time, using this secret event seems to be more trouble than it’s worth.
Bonus chatter: The documentation continues: “If an explicit event is provided for the request, it is still signaled.” The explicit event being referred to here is the event passed in the
hEvent member. That event is always set, regardless of whether the operation completed synchronously or asynchronously, and regardless of whether the
HANDLE feature was enabled on the file handle.
Well then, can I safely use this “secret event” for one of several simultaneous I/O requests?
For example, InterlockedIncrement/Decrement’ing the “pending requests” counter and using the “secret event” only when counter was just incremented from zero.
It’s not like there is one secret event for process, or for user. One secret event is hiding behind every file handle! You can use tons of these, only be careful not to perform two operations on the same file.
Oops, it was unclear, I meant “one of several simultaneous I/O requests on certain file handle”. Is it safe to do simultaneously one operation with hEvent = 0, and several others with explicit hEvent’s?