I am reading and learning about ThreadScheduler
and articles around Tasks and came across the function ThreadPool.UnsafeQueueUserWorkItem
used in one of the MSDN examples about own ThreadSchedulers. In the MSDN description about UnsafeQueueUserWorkItem there is a big warning that the function may be an security hole and that it "does not propagate the calling stack".
The only link is to QueueUserWorkItem
which - from the name - seems to be the "safe counterpart"? but does not mention anything about calling stacks either.
What does that exactly mean to propagate a stack? Copy it over before the work starts? Why would another thread need the stack of the calling thread anyway? I would assume that they start with a fresh and empty stack. After all, when the thread function returns, it does not continue to execute the function scheduling the Task, right?
It is an implementation detail of CAS, Code Access Security. Which can check whether a thread has sufficient rights to perform an operation. It only matters if code runs in a restricted security environment, not running with full trust or in a sandbox.
The plumbing that makes this work is complicated and I can only approximate the way it works. The ExecutionContext class is key, it determines the security context in which code runs. Things get difficult when a thread that runs with restricted rights starts another thread. Clearly that other thread needs to run with the same kind of restrictions as the original thread. CAS depends on the being able to perform stack walks to discover restrictions. That's difficult on another thread, it has its own stack.
The ExecutionContext.Capture() method performs an essential role here. It makes a copy of the context of the calling thread, including making a stack walk to create a "compressed" stack of the security attributes discovered. The new thread then runs with that captured context.
ThreadPool.UnsafeQueueUserWorkItem() skips the Capture() call. The threadpool thread will run with the default execution context.
This is an optimization, Capture() is not a cheap method. It matters in the kind of program that depends on TP threads to get stuff done in a hurry. A web server jumps to mind. Also the kind of code that uses the method, you see it used in internal methods in the System.Net namespace for example.
Clearly it is unsafe, it doesn't run with the CAS restrictions of the originating thread.