Many developers are familiar with Thread Local Storage (TLS) as a mechanism allowing you to associate a piece of data with a thread. I usually discourage people from using TLS because I recommend architecting applications to perform their operations using thread pool threads. TLS doesn’t work with the thread pool because different thread pool threads all take part in a since workflow or operation sequence. However, sometimes people would like to associate some data with a workflow that multiple threads participate in. The .NET Framework has a little-known facility that allows you to associate data with a “logical” thread-of-execution. This facility is called logical
call context and it allows data to flow to other threads, AppDomains, and even to threads in other processes.

While the logical call context facility was originally created for use by the .NET Framework’s remoting infrastructure it can certainly be used without using remoting. Here’s how it works, every managed thread has a hashtable object associated with it. For this hastable, the keys are String objects and the values are can refer to any object whose type is marked with the [Serializable] custom attribute. The content of this hashtable is automatically serialized/deserialized when a logical thread of execution crosses a physical thread, appdomain, or process boundary.

To manipulate a thread’s logical call context hashtable, you call the static LogicalSetData and LogicalGetData methods defined on the CallContext class (defined in the System.Runtime.Remoting.Messaging namespace). You delete a key from the hashtable by calling FreeNamedDataSlot. The class looks like this:

[Serializable]
public sealed class CallContext : ISerializable {
public static void LogicalSetData(String name, Object data);
public static Object LogicalGetData(String name);
public static void FreeNamedDataSlot(String name);

// For remoting only
public static void SetHeaders(Header[] headers);
public static Header[] GetHeaders();
}

Here is a small sample application that demonstrates the call context mechanism:

public static class Program {
   private const String c_CCDataName = “CCData”;

   private static void Main() {
      // Set an item in the thread’s call context
      CallContext.LogicalSetData(c_CCDataName, “Data=” + DateTime.Now);

      // Get the item in the thread’s call context
      GetCallContext();

      // Show that call context flows to another thread
      WaitCallback wc = na => GetCallContext();
      wc.EndInvoke(wc.BeginInvoke(nullnullnull));

      // Show that call context flows to another AppDomain
      AppDomain ad = AppDomain.CreateDomain(“Other AppDomain”);
      ad.DoCallBack(GetCallContext);
      AppDomain.Unload(ad);

// Remove the key to prevent (de)serialization of its value
// from this point on improving performance

      CallContext.FreeNamedDataSlot(c_CCDataName);

      // Show no data due to the key being removed from the hashtable
      GetCallContext(); 
   }

   private static void GetCallContext() {
      // Get the item in the thread’s call context
      Console.WriteLine(“AppDomain={0}, Thread ID={1}, Data={2}”,
         AppDomain.CurrentDomain.FriendlyName,
         Thread.CurrentThread.ManagedThreadId,
         CallContext.LogicalGetData(c_CCDataName));
   }

}

 

When I compile and run this code, I get the following output:

AppDomain=04a-Contexts.exe, Thread ID=1, Data=Data=9/27/2010 2:47:55 PM
AppDomain=04a-Contexts.exe, Thread ID=3, Data=Data=9/27/2010 2:47:55 PM
AppDomain=Other AppDomain, Thread ID=1, Data=Data=9/27/2010 2:47:55 PM
AppDomain=04a-Contexts.exe, Thread ID=1, Data=

Since the logical call context mechanism serializes and deserializes objects in a hashtable, it is a pretty expensive mechanism. That is, it takes time to serialize & deserialize objects and, of course, deserializing objects creates objects in the managed heap which must ultimately be garbage collected. So, in my own applications, I try to avoid the call context mechanism just like I try to avoid TLS. However, there are occasions where call context can fill a need and save the day.

  • wds_admin

    If i remember correctly FreeNamedDataSlot only clears the data from the current thread. This means that the data is still present in the thread with the id 3. It’s nice that the data automatically flows with you but on the other hand you end up with polluted ThreadPool threads unless you clean them up yourself. IMO the framework should automatically clean ThreadPool threads after they have been used and returned to the pool but unfortunately that’s not the case…

  • Interesting Finds: September 28, 2010

  • Logical call context (LCC) is a fantastic mechanism to flow data up and down a logical execution path. However it must be pointed out that when executing under ASP.NET, the LCC falls apart. This is because an ASP.NET request, when under load, can become “thread agile”: in other words, ASP.NET will suspend a thread, and move it to another thread without context or TLS for that matter. ASP.NET does move items in the HttpContext.Current.Items dictionary from one thread to the next. So, proper code to keep the LCC concept invariant across ASP.NET and non-ASP.NET application must be cognizant of the execution context. I have created the solution, fully unit tested, and been vetted in production for years; see the end of my comment for the complete code. I use the code below to flow a connection/transaction to mimic the System.Transactions.TransactionScope for ADO.NET and even LINQ to SQL applications without the use of MSDTC (assuming single provider/connection). The code and other goodies can be had at:

    http://code.google.com/p/softwareishardwork/

    *** begin code snippet ***

    /*
    Copyright ©2002-2010 Daniel Bullington
    Distributed under the MIT license: http://www.opensource.org/licenses/mit-license.php
    */

    using System.Runtime.Remoting.Messaging;
    using System.Web;

    namespace SoftwareIsHardwork.Core
    {
    ///
    /// Manages execution path storage of objects in a manner which is safe in
    /// standard executables and libraries and ASP.NET code.
    ///
    public static class ExecutionPathStorage
    {
    #region Properties/Indexers/Events

    ///
    /// Gets a value indicating if the current application domain is running under ASP.NET.
    ///
    public static bool IsInHttpContext
    {
    get
    {
    return (object)HttpContext.Current != null;
    }
    }

    #endregion

    #region Methods/Operators

    private static object AspNetGetValue(string key)
    {
    return HttpContext.Current.Items[key];
    }

    private static void AspNetRemoveValue(string key)
    {
    HttpContext.Current.Items.Remove(key);
    }

    private static void AspNetSetValue(string key, object value)
    {
    HttpContext.Current.Items[key] = value;
    }

    private static object CallCtxGetValue(string key)
    {
    return CallContext.GetData(key);
    }

    private static void CallCtxRemoveValue(string key)
    {
    CallContext.FreeNamedDataSlot(key);
    }

    private static void CallCtxSetValue(string key, object value)
    {
    CallContext.SetData(key, value);
    }

    public static object GetValue(string key)
    {
    if (IsInHttpContext)
    return AspNetGetValue(key);
    else
    return CallCtxGetValue(key);
    }

    public static void RemoveValue(string key)
    {
    if (IsInHttpContext)
    AspNetRemoveValue(key);
    else
    CallCtxRemoveValue(key);
    }

    public static void SetValue(string key, object value)
    {
    if (IsInHttpContext)
    AspNetSetValue(key, value);
    else
    CallCtxSetValue(key, value);
    }

    #endregion
    }
    }

    *** end code snippet ***

  • wds_admin

    If i remember correctly FreeNamedDataSlot only clears the data from the current thread. This means that the data is still present in the thread with the id 3. It’s nice that the data automatically flows with you but on the other hand you end up with polluted ThreadPool threads unless you clean them up yourself. IMO the framework should automatically clean ThreadPool threads after they have been used and returned to the pool but unfortunately that’s not the case…

  • wds_admin

    good read

  • wds_admin

    Logical Call Context doesn’t work when I want to set some data from a class which is a channel sink.

    public class ServerSink : BaseChannelSinkWithProperties, IServerChannelSink
    {

    public ServerProcessing ProcessMessage(…)
    {
    CallContext.LogicalSetData(“TRACE”, “true”);

    }
    }

    When I get CallContext.LogicalGetData(“TRACE”) returns null.

  • wds_admin

    Hello, Jeffrey.
    My name is Antony Norko, I’m from Belarus (west of Russia).

    I’m a 4th year ASP.NET C# developer and I have a little proposition for you. How about a Russian-translated version of your blog? I can translate it. I suppose it will help russian dev community for better understanding of your blog’s content. How you found my idea?

    If you are interested – please contact me via e-mail: anton.norko(at)gmail.com

    Thanks.

  • Jeff, this was exactly I was looking for. I thought… 🙂 Here is the problem. I used this approach to share some data across a multi-threaded application and it worked great. But now the application needs to make a remote call to an external service. The call is failing because my ILogicalThreadAffinative implemenation and the data it contains is not serializable, nor should it be. I don’t want it going over the wire on the remote call and even if it did, the remote app does not have access to the assemblies contain the types so they couldn’t be deserialized anyway.

    Any suggestions on the right way to solve this problem?

  • wds_admin

    Hello Jeffrey,

    Multi threading is used in many situations to improve the performance of the application. When the above mentioned Logical Call Context is used, the performance is hurt because it using serialization and de serialization. In such a case can we create a thread and use it to execute a workflow instead of using threadpool thread and logical call context?

    Regards

    Ram

  • wds_admin

    Richter, I have 2 problems : http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/37801713-75d0-4772-a610-9e11b78d89c6

    1、I wrote a winform that with no border and title. I draw the border and title in the client area using some nice pictures. In order to make this window can be moveresizedoubleclick, I override the WndProc method, and handle some msg in it.

    Every thing is go on except that I cannot handle double-click msg. When it has a MaximizeBox, I want it can be double-clicked to be Maximized, when not has MaximizeBox, it just can be moved.

    My code :

    Point p = new Point((int)m.LParam);
    p.X -= this.Left;
    p.Y -= this.Top;

    // In the title area
    if (p.X >= 5 && p.X = 5 && p.Y

  • wds_admin

    How different are constructs:
    WaitCallback wc1 = delegate(object na) { GetCallContext(); };
    and
    WaitCallback wc2 = new WaitCallback(GetCallContext); //error

    Why wc1 is compile time OK despite signature
    public delegate void WaitCallback(object state);

  • wds_admin

    Hi Jeffrey,

    Here is a piece of code snippet, it is about logical call context flow. Please take a little time to check it.

    class Program
    {
    static void Main(string[] args)
    {
    Console.WriteLine(“Main Thread id : {0}”, Thread.CurrentThread.ManagedThreadId);
    CallContext.LogicalSetData(“LogicalData”,”data”);
    new FileDialogPermission(FileDialogPermissionAccess.OpenSave).Deny();

    using (AsyncFlowControl afc = ExecutionContext.SuppressFlow())
    {
    WaitCallback wc = new WaitCallback(PrintLogicData);
    ThreadPool.QueueUserWorkItem(wc);
    wc.EndInvoke(wc.BeginInvoke(null, null, null));
    }
    Console.ReadLine();
    }

    static void PrintLogicData(object state)
    {
    Console.WriteLine(“Thread id : {0}”,Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine(“Is current thread in thread pool? {0}”,Thread.CurrentThread.IsThreadPoolThread);
    Console.WriteLine(“Logical Data : {0}”, CallContext.LogicalGetData(“LogicalData”));
    try
    {
    new FileDialogPermission(FileDialogPermissionAccess.OpenSave).Demand();
    Console.WriteLine(“Successfully demanded {0}”, Environment.NewLine);
    }
    catch (Exception)
    {
    Console.WriteLine(“Demand for FileDialogPermission failed {0}”, Environment.NewLine);
    }
    }
    }

    Result :

    Main Thread id : 9
    Thread id : 6
    Is current thread in thread pool? True
    Logical Data:
    Successfully demanded

    Thread id : 10
    Is current thread in thread pool? True
    Logical Data: data
    Successfully demanded

    Above program I suppress execution context flow from main thread to the other thread, so the other thread couldn’t be affected by the execution context of the main thread. And the thread pool’s behavior satisfied our prospects (allowed to demand for filedialog and couldn’t be able to visit main thread’s call context data), but the result of delegate’s async invoke looked like a bit strange. (allowed to demand for filedialog and could be able to visit main thread’s call context data) The delegate async invoke also used thread of threadpool to execute.(The output is the evidence) So how to explain ? Looking forward to your reply. Thanks a lot!

  • Good to see a taelnt at work. I can’t match that.

  • wds_admin

    Do you have sample code that sets the CallContext of a newly spawned process?

  • wds_admin

    I noticed several typos in this nice blog. Below are the excerpts.

    TLS doesn’t work with the thread pool because different thread pool threads all take part in a since workflow or operation sequence.

    For this hastable, the keys are String objects and the values are can refer to any object whose type is marked with the [Serializable] custom attribute.