Ready… cancel… wait for it! (part 2)


A customer had a question about I/O cancellation. They have a pending Read­File­Ex call with a completion procedure. They then cancel the I/O with Cancel­Io­Ex and wait for the completion by passing TRUE as the bWait parameter to Get­Overlapped­Result.

Assuming both return success, can I assume that my completion procedure will not be called after GetOverlappedResult returns? It appears that GetOverlappedResult waits non-alertably for the I/O to complete, so I'm assuming it just eats the APC if there was one. But if an APC had been posted just before I called CancelIoEx, will it also cancel that APC?

Get­Overlapped­Result does not magically revoke completion callbacks. Why should it?

Recall that completion is not the same as success. Completion means that the I/O subsystem has closed the books on the I/O operation. The underlying operation may have completed successfully or it may have failed (and cancellation is just one of the many possible reasons for failure). Either way, the completion procedure signed up to be notified when the I/O completes, and therefore it will be called to be informed of the completion due to cancellation.

Besides, as the customer noted, there is a race condition if the Cancel­Io­Ex call is made just after the I/O completed, in which case it didn't get cancelled after all.

This answers our question from last time, namely, how our fix for the cancellation code was incomplete. If the I/O had been issued with a completion routine (or equivalently, if it had been issued against an I/O completion port), then the code frees the OVERLAPPED structure before the completion routine runs. The kernel doesn't care that you did that (the kernel is finished with the OVERLAPPED structure), but your completion routine is probably not going to be happy that it was given a pointer to freed memory as its lpOverlapped parameter.

You have to delay freeing the OVERLAPPED structure until the completion routine executes. Typically, this is done by allocating the OVERLAPPED structure on the heap rather than the stack, and making it the completion routine's responsibility to free the memory as its final act.

Comments (9)
  1. Joshua says:

    Don't be discouraged by the lack of comments.

  2. Kyle says:

    @Joshua: Agreed.  Apparently silence around here is a perverse form of approval?

    Anyway, while best practice would be heap allocate the OVERLAPPED structure, stack allocation isn't out of the question.  The problem with stack allocation, of course, is that the function where the OVERLAPPED struct is stack-allocated must not return until after the cancellation is complete.  As proposed in comments to the previous article, using an alertable wait on hEvent in a loop along with the completion routing signaling the hEvent member would be sufficient.  Certainly, however, heap allocation works better with less complicated code.

  3. Alex Grigoriev says:

    Um… GetOverlappedResult doesn't support IOs issued by ReadFileEx/WriteFileEx AT ALL. The whole point is moot.

    Semantics of ReadFileEx doesn't involve setting any event you can wait for or GetOverlappedResult can check for. Doesn't even involve setting the signalled state of FILE_OBJECT.

  4. Actually says:

    Funny how when Raymond posts anything remotely involved, the old "look at me, I'm smarter than Micro$oft crowd" goes mute. Very indicative of a fundamental inability to comprehend the post by the general nitpicker population here. But don't worry you'll get at least 40 posts when Raymond has a spelling error. LOL

  5. Kyle says:

    @Alex Grigoriev

    You are correct that GetOverlappedResult() is unusable, but WaitForSingleObjectEx() could be used in conjunction with manually firing the hEvent member of the OVERLAPPED structure.  Just because ReadFileEx() doesn't fire hEvent doesn't mean that the completion function couldn't fire hEvent itself.  Once the completion function finishes (having fired hEvent), WaitForSingleObjectEx() would return (because of the APC completion), at which point the loop would restart and then WaitForSingleObjectEx() would return immediately because hEvent had been fired.

    As I said previously, it's messy, but workable.

  6. Scott says:

    Are you using some unicode characters in your formatting? I noticed that when I copied Cancel­Io­Ex into MSDN search (curious about the function) it only returned a match for this post. If I paste into notepad I see "Cancel-Io-Ex". Manually typing in the function name displayed the expected page of course.

  7. Andreas says:

    @Actually: This is apparently called a bike-shed discussion: http://www.unixguide.net/…/16.19.shtml

  8. yekop says:

    @Scott: It appears to be a 0x00AD "soft hyphen": blogs.msdn.com/…/736881.aspx

    (sorry if this double-posts, nothing happened when I clicked "post" the first time..)

  9. Scott says:

    @yekop That explains the "Sometimes rendered as a hyphen" behavior. I was wondering why the Bing search didn't ignore it if it was a non-printable character.

    Such articles make me glad I never became a Unicode guru.

Comments are closed.

Skip to main content