tags:

views:

72

answers:

4
A: 

Wait on (possibly with zero timeout) overlapped.Handle. It will be set whether the operation is completed or cancelled.

Ben Voigt
@Ben, http://msdn.microsoft.com/en-us/library/aa363791%28VS.85%29.aspx says "CancelIO cancels only outstanding I/O on the handle, it does not change the state of the handle; this means that you cannot rely on the state of the handle because you cannot know whether the operation was completed successfully or canceled."
Gili
@Gili: The file HANDLE is different from the event HANDLE in the overlapped structure. That text refers to the file HANDLE.
Ben Voigt
A: 
Alex Farber
Is it possible to abort the read and continue using the handle without closing it?
Gili
You don't need to abort the read operation, just stop waiting for overlapped event. For example, you can wait for overlapped event with timeout. After timeout expired and no data is read, you can do everything you need, file handle is valid.
Alex Farber
Imagine I am reading/writing to sockets. Thread 1 doesn't write anything but tries reading with a timeout. Once the timeout occurs, Thread 2 writes to the socket and tries reading the response. If I never cancel Thread 1's operation, it'll "steal" bytes that should go to Thread 2.
Gili
Cancelling overlapped operation is done by setting appropriate event. As you can see from my sample, after executing ReadFile (which returns immediately) reading thread waits on WiatForMultipleObjects for overlapped event and any other events you need. By setting some additional event, you can stop this waiting, which is equal to cancelling read operation.
Alex Farber
Just because WaitForMultipleObjects() returns doesn't mean the underlying file-system stops reading. Thread 1 will still "steal" bytes from Thread 2.
Gili
@Gili: In case of file system (or any seekable stream), no it won't steal anything. Thread 2 will get the bytes at the offset it requested.
Ben Voigt
@Ben, I should have mentioned: I'm reading from a serial port.
Gili
A: 

If you're already using overlapped operations, why do you need to cancel I/O at all? The entire concept of 'cancelling' an in-flight I/O operation is really race-prone, and totally subject to the underlying device stack you're trying to write to; really the only time you'd want to do this is to unblock another thread who is waiting on the completion of that I/O.

Paul Betts
Imagine I am reading/writing to sockets. Thread 1 doesn't write anything but tries reading with a timeout. Once the timeout occurs, Thread 2 writes to the socket and tries reading the response. If I never cancel Thread 1's operation, it'll "steal" bytes that should go to Thread 2.
Gili
Who's waiting, it's an async operation - just have a way for the completion routine to ignore the result if you don't want it. I guess I see your point about in-flight unwanted I/Os getting in the way of future I/Os, but I still don't think that's a huge concern (Edit: Whoops, the above comment changed while I wrote my response)
Paul Betts
@Gili Hmmm, I think I see what you're saying now. Though I'd argue that designing your app to have thread affinity like that is a bad idea - it's better to assume completion callbacks come back on an *arbitrary* thread, then dispatch the result to the right place. You have a pretty motivating example though in general.
Paul Betts
Furthermore, consider the fact that the buffer passed into ReadFile() must remain valid for the duration of the read operation. I am providing an API on top of ReadFile(). This means that from the end-user's point of view, he needs to keep the buffer valid outside the scope of a read operation (i.e. he thinks the read is over when he cancels it). It's not clear to him when it is safe for him to reclaim the buffer.
Gili
The way that kernel drivers handle this is by state machines, you have the "Waiting for response X" state, then you handle and interpret whatever response you get back regardless of what thread you're in.
Paul Betts
@Gili: Your socket protocol is broken, not just in the presence of CancelIo. Even if CancelIo worked according to whatever behavior you preferred, Thread 2 could still read the block that Thread 1 was looking for, because it could have been delayed by TCP retransmits, etc.
Ben Voigt
@Ben, I guess I gave a bad example. I am reading from a serial port.
Gili
A: 

CancelIo() works fine. I misunderstood my code.

Upon further investigation it turns out that the code was invoking CancelIo() followed by ReadFile() with a timeout INFINITE. The completion port was never getting notified of the read because the remote end was never sending anything. In other words, CancelIo() did not cancel subsequent operations.

I found some eye-opening documentation here:

Be careful when coding for asynchronous I/O because the system reserves the right to make an operation synchronous if it needs to. Therefore, it is best if you write the program to correctly handle an I/O operation that may be completed either synchronously or asynchronously. The sample code demonstrates this consideration.

It turns out that device drivers may choose to treat an asynchronous operation in a synchronous manner if the data being read is already cached by the device driver. Upon further investigation, I discovered that when CancelIo() was being invoked before ReadFile() it would sometimes cause the latter to return synchronously. I have no idea why the completion port was never getting notified of ReadFile() after a CancelIo() but I can no longer reproduce this problem.

The completion port is signaled regardless of whether ReadFile() is synchronous or asynchronous.

Gili
Which incidentally is covered by the advice in my answer. Synchronous completion of I/O will still cause `overlapped.Handle` to be signaled.
Ben Voigt