Search code examples
c#.netmultithreadingclr

Does ThreadPool.RegisterWaitForSingleObject block the current thread or a thread-pool thread?


From reading the documentation of the ThreadPool.RegisterWaitForSingleObject method, it is not clear whether:

  1. It blocks the current thread while waiting on the EventWaitHandle and then commissions the WaitOrTimerCallback on a thread pool thread, or

  2. It commissions a thread pool thread to wait on the wait handle and then on that same thread execute the WaitOrTimerCallback once the wait handle has been signaled.

  3. It blocks the current thread and when the wait handle is signaled, it calls the WaitOrTimerCallback on the current thread. But this would be the equivalent functionality of WaitHandle.WaitOne(). Also, it would not involve the thread pool at all.

Which of the three does it do?


Solution

  • It is none of the above, 2) is closest. The exact details are pretty convoluted, most of the code is buried in the CLR and it has changed across .NET versions. You can have a look-see at the current version in the CoreCLR source, I'll give the 10,000 feet view.

    Key is that it doesn't block, the work is done by a dedicated unmanaged thread. Called the "wait thread" in the source code, it uses the WaitForMultipleObjects() winapi function to wait on all registered waits. If there is none (left) it just sleeps. The thread is woken up from either by a QueueUserApc() if the wait list changes so it can resume waiting with an updated list.

    Once one of the wait objects get signaled, it uses ThreadPool.QueueUserWorkItem() to invoke the callback delegate target on a threadpool thread. If the executeOnlyOnce argument was true then the wait handle is removed from the list. And it quickly resumes waiting with WFMO. The thread never ends.

    The executeOnlyOnce argument is important btw, hilarity ensues if you pass false and you use a ManualResetEvent. The thread explosion triggered by the MRE's Set() method is an interesting artifact to observe :) You can see the wait thread in the debugger's Debug > Windows > Threads when you enable unmanaged debugging. It doesn't have an interesting name however.