Search code examples
.netthreadpoolexecutioncontext

Is ExecutionContext cleared after the thread is returned to thread pool?


It is written in the docs:

When the thread pool reuses a thread, it does not clear the data in thread local storage or in fields that are marked with the ThreadStaticAttribute attribute. Therefore, when a method examines thread local storage or fields that are marked with the ThreadStaticAttribute attribute, the values it finds might be left over from an earlier use of the thread pool thread.

Yet it is said nothing about execution context. How can I check whether ExecutionContext is cleared?


Solution

  • In short - yes.

    1. SynchronizationContext is not captured.
    2. LogicalCallContext is cleared.

    _

    using System.Linq;
    using System.Runtime.Remoting.Messaging;
    using System.Threading;
    using System.Threading.Tasks;
    using NUnit.Framework;
    
    namespace Tests
    {
        [TestFixture]
        public class ExecutionContextFlowTest
        {
            [Test]
            public async Task Execute()
            {
                try
                {
                    ThreadPool.SetMinThreads(2, 100);
                    ThreadPool.SetMaxThreads(2, 100);
    
                    var asyncLocal = new AsyncLocal<bool>();
    
                    await Task.Run(
                        () =>
                            {
                                asyncLocal.Value = true;
                                CallContext.LogicalSetData("test", true);
                            });
    
                    await Task.WhenAll(
                        Enumerable.Range(0, 10).Select(
                            _ => Task.Run(
                                () =>
                                    {
                                        Assert.That(asyncLocal.Value, Is.False);
                                        Assert.That(CallContext.LogicalGetData("test"), Is.Null);
                                    })).ToArray());
                }
                finally
                {
                    ThreadPool.SetMinThreads(10, 100);
                    ThreadPool.SetMaxThreads(100, 100);
                }
            }
        }
    }
    

    Though I'm not sure about SecurityContext.

    [ThreadStatic] and ThreadLocal are not controlled by ExecutionContext (and not cleared).

    Also in .NET sources I found a cleanup which replaces ExecutionContext with previous (empty) ones after item execution is completed.