Async I/O and I/O completion ports
These days, the testers in Indigo are working on their TDSs (test design specifications). I own testing an area for which I needed to read up PostQueuedCompletionStatus; which lead to reading about async I/O and I/O completion ports. Pretty interesting stuff. And anytime I explain what I understood, I find that it stays with me longer! Now that I am blogging, this is an excellent place to try and relate some of the key points.
For those of you who have used the CLR thread pool know thats its recommended to be used for short-lived work items. For I/O intensive situations, the CLR thread pool doesnt have native support (there are Win32 APIs that let you use the I/O completion ports).
I/O completion ports are the way in which an app uses a pool of threads to process asynchronous I/O requests. These threads are kernel threads and their only job in life is to process I/O requests. Applications that process many concurrent asynchronous I/O requests can do so more quickly and efficiently by using I/O completion ports than by creating threads at the time of the I/O request. When you create a completion port, you can specify a concurrency value which limits the #threads associated with the port. Typically this number = #processors.
The CreateIoCompletionPort function lets you associate a file handle with an I/O completion port. When the async I/O completes, an I/O completion packet is queued to the port. A thread can use the GetQueuedCompletionStatus to wait for a packet to be queued; instead of directly waiting for the async I/O to be completed. Threads that block are released in LIFO order. The most efficient case is when no waits in the queue can be satisfied because the port has reached its concurrency limit - because in this case, when a running thread calls GetQueuedCompletionStatus, it picks up the queued packet immediately, and no context switch happens. The running thread continuously picks up packets and other threads are not able to run. The PostQueuedCompletionStatus function allows your app to queue its own I/O packets to the port without starting an async I/O. This is the way you would notify worker threads of some events that your app is aware of.
Read this article for a good example: https://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/reading_asynchronously_with_io_completion_ports.asp
Comments
- Anonymous
December 14, 2003
Are you saying CLR does not use
CreateIoCompletionPort /PostQueuedCompletionStatus/... for asynch I/O on win2003 ? - Anonymous
December 15, 2003
Yes the CLR thread pool threads (QueueUserWorkItem) do not use PostQueuedCompletionStatus. I think this excerpt clarifies that....
The full article is at: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconThreadPooling.asp
There are several scenarios in which it is appropriate to create and manage your own threads instead of using the ThreadPool. You should do so:
.....
If you have a task that might run a long time (and therefore block other tasks).
..... - Anonymous
January 24, 2004
On all NT platforms, I/O completion ports are available for CLR applications to use. You can do this through the three functions in System.Threading.Thread:
1) Overlapped.Pack
2) Overlapped.Unpack
3) ThreadPool.BindHandle.
Normally, you dont need to call anyone of these functions, unless you are implementing a managed wrapper for a native NT object (eg: namedpipe). Some of the classes that ship in the framework (eg: Socket) automatically call these api's for you.
On NT, CLR threadpool will automatically start a CompletionPort thread, which calls GetQueuedCompletionStatus, and calls your I/o callback. So, for eg: System.Net.Sockets on NT automatically uses completion ports. So does System.IO.FileStream, and other native I/O related api's in the framework.
On 9x platforms, the System.Net.Sockets.Socket class defaults to Overlapped I/O. - Anonymous
March 17, 2004
I'm using the Overlapped.Pack, Overlapped.Unpack and ThreadPool.BindHandle methods to do async I/O on named pipes. This seems to work, but it seems that it should fail given what I know about the native APIs.
If I call BindIoCompletionCallback on a handle, the thread that initiates the I/O operation is not supposed to terminate before the I/O operation completes. This could be accomplished by queuing the operation that initiates the I/O to an I/O thread, thus ensuring that the calling thread never terminates before the I/O operation completes.
The problem is that ThreadPool.QueueUserWorkItem doesn't allow you to specify that the item should be processed on an I/O thread (as the native QueueUserWorkItem does).
Since this idiom is used by my code and the .NET Framework's async I/O implementation, and both seem to work, I have to assume that something is handling under the covers in the CLR that handles this properly. The lack of documentation or explanation makes me a bit nervous. - Anonymous
March 17, 2004
The comment has been removed - Anonymous
April 27, 2004
I thought that Windows 9x systems could not support Overlapped I/O, as it required Winsock 2? is this correct?