Saturday, December 05, 2009


Leaky Abstractions Redux, Part II

Last time I discussed why Winsock's connect function shouldn't be called by a thread running in the I/O component of the system thread pool. To recap, the implementation of connect does an alertable wait, causing APC work items that are enqueued to that thread to execute. This causes a cyclical effect where connect and the work items invoke each other repeatedly.

Another thread pool no-no is to call GetQueuedCompletionStatus from a thread in the default component. Instead of using the APC queue, the default component uses an I/O completion port as its queuing mechanism. This is actually very handy because I/O handles can be bound to the system thread pool using BindIoCompletionCallback. This allows asynchronous I/O completion events to be posted directly to the system thread pool.

I like to think of I/O completion ports as thread-safe queues provided by the OS that possess two unique properties:

  • Integration with the scheduler: When a thread that is associated with an I/O completion port blocks, the scheduler and the port cooperate so that another thread that is blocked on the port can be woken up and provided with an I/O completion packet.
  • Integration with the I/O subsystem: I/O completion packets can be posted directly to the port without any user-mode intervention.
Unfortunately only one I/O completion port can be associated with a thread at any given time. Because the default component of the system thread pool uses an I/O completion port for its queue, all WT_EXECUTEDEFAULT threads are already associated with a port. This means that if one of those threads blocks, the pool's port will know about it.

One associates a thread with an I/O completion port by calling GetQueuedCompletionStatus. The calling thread becomes associated with the port whose handle was passed in as the first parameter. This action overwrites any previous associations -- including the association that was made with the thread pool's port! This impairs the thread pool's ability to detect when one of its threads has blocked because the scheduler is no longer aware of the pool's completion port. In the best case, the thread pool will run less efficiently. In the worst case, a deadlock is possible.


Release 7.0; Copyright © 1996-2012 Aaron Klotz. All Rights Reserved.