tags:

views:

565

answers:

1

I want to use a thread pool to both initiate/cancel overlapped read operations -- using ReadFile() and CancelIo() respectively -- as well as handling any completion port events when read operations complete.

  1. Any thread can initiate a read operation
  2. Any thread can handle a read-complete event
  3. Only the thread that initiated a read may cancel it (this is a CancelIo() limitation)

I'm not sure how to implement this. One normally uses GetQueuedCompletionStatus() to wait on completion port events and WaitOnSingleObject() to wait on normal events but it's not clear how to mix the two. If PostQueuedCompletionStatus() would let me specify a specific thread to wake up I'd be set. Any ideas?

UPDATE: The solution must run on Windows XP. Unfortunately this rules out using CancelIoEx() or GetQueuedCompletionStatusEx().

+2  A: 

1 and 2 are easy, just use the IO Completion port.

But, as you have found 3 requires (prior to Windows V61) the same thread.

If using Windows >= V6, GetQueuedCompletionStatusEx includes an alterable option which will cause it to return is an APC is performed on the thread. So use QueueUserAPC to queue a no-op APC1 when you need that specific thread to do some other work. You will of course need some thread safe queue to provide the interrupted thread with instructions of what to cancel.

If earlier version compatibility is required then things get more difficult. Possibilities:

  • Use the timeout parameter of GetQueuedCompletionStatus](http://msdn.microsoft.com/library/aa364986) to return regularly to check for cancellations.

  • Or, possibly more practically, divide the thread pool into two groups. Threads that initiate and cancel IO. The rest of the time these threads spend waiting to be signalled to perform one of these actions. The other part of the pool waits on IO completions with GetQueuedCompletionStatus.

Neither of these is as nice, but that's always the problem with older versions: they lack functionality.

1 Use a no-op APC rather than doing work in the APC so the limitations on what can be done in an APC and its inherant issues with concurrency are bypassed. (Since an APC is executed on a thread, any locks that thread hold are held in the APC, any state protected will be arbitrarily inconsistent.)

Richard
Unfortunately both GetQueuedCompletionStatusEx() and CancelIoEx() require Windows Vista. I will update the question to note that I'm looking for solutions which are backwards compatible to Windows XP.
Gili
Oops, forgot that GQCSEx was also >=Vista...updating.
Richard
How do you notify the "initiate and cancel" thread pool that it should invoke ReadFile()? If I use WaitOnMultipleObjects() where a single Event denotes a pending ReadFile() aren't I bottle-necking all reads to a single thread?
Gili
@Gill: I would have each init/cancel thread wait on two events. "Instruction here" and a "shutting down", the instruction event would indicate that there is a message waiting on that threads command queue. That command queue is used by other threads (i.e. the completion pool) to send instructions (initiate or cancel). It does not require a single initial/cancel thread (albeit you will need to do a lot of async operations to need multiple), but the load balancing of multiple threads also needs to be created (initiate instructions could be done by any, but cancel needs to be specific).
Richard
Great answer, thanks! As a side-note, I just found out that I don't need to use completion ports because my I/O handler only spends 0.3 milliseconds (that's no typo) per read/write request. I'm going to keep it simple by using a single thread :)
Gili
KISS is *always* good, when possible.
Richard