[corlib] Improve flow of ExecutionContext (few tests taken from https://github.com...
authorMarek Safar <marek.safar@gmail.com>
Thu, 6 Mar 2014 18:50:52 +0000 (19:50 +0100)
committerMarek Safar <marek.safar@gmail.com>
Thu, 6 Mar 2014 18:54:06 +0000 (19:54 +0100)
mcs/class/corlib/System.Runtime.Remoting.Messaging/CallContext.cs
mcs/class/corlib/System.Runtime.Remoting.Messaging/LogicalCallContext.cs
mcs/class/corlib/System.Runtime.Remoting.Proxies/RealProxy.cs
mcs/class/corlib/System.Runtime.Remoting/RemotingServices.cs
mcs/class/corlib/System.Threading.Tasks/Task.cs
mcs/class/corlib/System.Threading/ExecutionContext.cs
mcs/class/corlib/System.Threading/Thread.cs
mcs/class/corlib/Test/System.Runtime.Remoting.Messaging/CallContextTest.cs [new file with mode: 0644]
mcs/class/corlib/corlib_test.dll.sources

index fad4eaf4e4ebffb2cc5f9da6c7d3a4e7e48acd9c..8fe846c632e7a63069c19a3ba18085581372dd91 100644 (file)
@@ -1,18 +1,13 @@
 // 
 // System.Runtime.Remoting.Messaging.CallContext.cs
 //
-// Author: Jaime Anguiano Olarra (jaime@gnome.org)
+// Authors: Jaime Anguiano Olarra (jaime@gnome.org)
 //         Lluis Sanchez Gual (lluis@ximian.com)
+//          Marek Safar (marek.safar@gmail.com)
 //
 // (c) 2002, Jaime Anguiano Olarra
-//
-///<summary>
-///Provides several properties that come with the execution code path.
-///This class is sealed.
-///</summary>
-
-//
 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 using System;
 using System.Threading;
 using System.Collections;
+using System.Collections.Generic;
 
 namespace System.Runtime.Remoting.Messaging 
-{
-       
+{      
        [Serializable]
        [System.Runtime.InteropServices.ComVisible (true)]
        public sealed class CallContext 
        {
                [ThreadStatic] static Header [] Headers;
-               [ThreadStatic] static Hashtable logicalDatastore;
-               [ThreadStatic] static Hashtable datastore;
                [ThreadStatic] static object hostContext;
                
                private CallContext ()
@@ -59,20 +52,18 @@ namespace System.Runtime.Remoting.Messaging
                        set { hostContext = value; }
                }
 
-               // public methods
-               public static void FreeNamedDataSlot (string name) 
+               public static void FreeNamedDataSlot (string name)
                {
-                       Datastore.Remove (name);
-                       LogicalDatastore.Remove (name);
+                       ExecutionContext.FreeNamedDataSlot (name);
                }
 
                public static object GetData (string name) 
                {
-                       if (LogicalDatastore.ContainsKey (name)) {
-                               return LogicalDatastore [name];
-                       } else {
-                               return Datastore [name];
-                       }
+                       var value = LogicalGetData (name);
+                       if (value == null)
+                               Datastore.TryGetValue (name, out value);
+
+                       return value;
                }
                
                public static void SetData (string name, object data) 
@@ -80,20 +71,20 @@ namespace System.Runtime.Remoting.Messaging
                        if (data is ILogicalThreadAffinative) {
                                LogicalSetData (name, data);
                        } else {
-                               LogicalDatastore.Remove (name);
+                               LogicalContext.FreeNamedDataSlot (name);
                                Datastore [name] = data;
                        }
                }
                
                public static object LogicalGetData (string name) 
                {
-                       return LogicalDatastore [name];
+                       return LogicalContext.GetData (name);
                }
 
                public static void LogicalSetData (string name, object data) 
                {
                        Datastore.Remove (name);
-                       LogicalDatastore [name] = data;
+                       LogicalContext.SetData (name, data);
                }
 
                public static Header[] GetHeaders () 
@@ -106,34 +97,20 @@ namespace System.Runtime.Remoting.Messaging
                        Headers = headers;
                }
 
-               internal static LogicalCallContext CreateLogicalCallContext (bool createEmpty)
-               {
-                       LogicalCallContext ctx = null;
-                       if (logicalDatastore != null) {
-                               ctx = new LogicalCallContext ();
-                               foreach (DictionaryEntry entry in logicalDatastore) {
-                                       ctx.SetData ((string)entry.Key, entry.Value);
-                               }
-                       }
-
-                       if (ctx == null && createEmpty)
-                               return new LogicalCallContext ();
-                       else
-                               return ctx;
-               }
-
                internal static object SetCurrentCallContext (LogicalCallContext ctx)
                {
-                       object oldData = new object[] { datastore, logicalDatastore };
+                       object oldData = new object[] { Datastore, LogicalContext.Datastore };
 
+                       Hashtable logicalDatastore;
                        if (ctx != null && ctx.HasInfo)
                                logicalDatastore = (Hashtable) ctx.Datastore.Clone ();
                        else
                                logicalDatastore = null;
                                
+                       LogicalContext.Datastore = logicalDatastore;
                        return oldData;
                }
-               
+
                internal static void UpdateCurrentLogicalCallContext (LogicalCallContext ctx)
                {
                        Hashtable data = ctx.Datastore;
@@ -147,27 +124,25 @@ namespace System.Runtime.Remoting.Messaging
                internal static void RestoreCallContext (object oldContext)
                {
                        object[] contextArray = (object[])oldContext;
-                       datastore = (Hashtable)contextArray [0];
-                       logicalDatastore = (Hashtable)contextArray [1];
+                       ExecutionContext.DataStore = (Dictionary<string, object>)contextArray [0];
+                       LogicalContext.Datastore = (Hashtable)contextArray [1];
                }
 
-               private static Hashtable Datastore
-               {
+               static Dictionary<string, object> Datastore {
                        get {
-                               Hashtable r = datastore;
-                               if (r == null)
-                                       return datastore = new Hashtable ();
-                               return r;
+                               return ExecutionContext.DataStore;
                        }
                }
 
-               private static Hashtable LogicalDatastore
-               {
+               static LogicalCallContext LogicalContext {
+                       get {
+                               return ExecutionContext.LogicalCallContext;
+                       }
+               }
+
+               static ExecutionContext ExecutionContext {
                        get {
-                               Hashtable r = logicalDatastore;
-                               if (r == null)
-                                       return logicalDatastore = new Hashtable ();
-                               return r;
+                               return ExecutionContext.Current;
                        }
                }
        }
index 30398dfa10b40d0f58a2c3c8f75a3d80b86391fa..957fbcda19537ac3a2d9289524c7098c1a15c4a5 100644 (file)
@@ -1,15 +1,14 @@
 //
 // System.Runtime.Remoting.Messaging.LogicalCallContext.cs
 //
-// Author:
+// Authors:
 //   Dan Lewis (dihlewis@yahoo.co.uk)
 //   Lluis Sanchez Gual (lluis@ximian.com)
+//   Marek Safar (marek.safar@gmail.com)
 //
 // 2002 (C) Copyright. Ximian, Inc.
-//
-
-//
 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -58,11 +57,9 @@ namespace System.Runtime.Remoting.Messaging
                        }
                }
 
-               public bool HasInfo
-               {
-                       get
-                       {
-                               return (_data != null && _data.Count > 0);
+               public bool HasInfo {
+                       get {
+                               return _data != null && _data.Count > 0;
                        }
                }
 
@@ -74,8 +71,7 @@ namespace System.Runtime.Remoting.Messaging
 
                public object GetData (string name)
                {
-                       if (_data != null) return _data [name];
-                       else return null;
+                       return _data != null ? _data [name] : null;
                }
 
                public void GetObjectData (SerializationInfo info, StreamingContext context)
@@ -90,7 +86,8 @@ namespace System.Runtime.Remoting.Messaging
 
                public void SetData (string name, object data)
                {
-                       if (_data == null) _data = new Hashtable ();
+                       if (_data == null) 
+                               _data = new Hashtable ();
                        _data [name] = data;
                }
 
@@ -99,17 +96,14 @@ namespace System.Runtime.Remoting.Messaging
                        LogicalCallContext nc = new LogicalCallContext ();
                        nc._remotingData = (CallContextRemotingData) _remotingData.Clone ();
                        if (_data != null)
-                       {
-                               nc._data = new Hashtable ();
-                               foreach (DictionaryEntry de in _data)
-                                       nc._data [de.Key] = de.Value;
-                       }
+                               nc._data = (Hashtable) _data.Clone ();
+
                        return nc;
                }
 
-               internal Hashtable Datastore
-               {
+               internal Hashtable Datastore {
                        get { return _data; }
+                       set { _data = value; }
                }
        }
 
index d430e9ccc1d505f0815f4d5c0100f7bf8d9d60d8..a87cf9517bd20e8b515a3c890667ad7b28cc4f7f 100644 (file)
@@ -41,6 +41,7 @@ using System.Runtime.Remoting.Contexts;
 using System.Runtime.CompilerServices;
 using System.Runtime.Serialization;
 using System.Runtime.InteropServices;
+using System.Threading;
 
 namespace System.Runtime.Remoting.Proxies
 {
@@ -162,7 +163,7 @@ namespace System.Runtime.Remoting.Proxies
                                                      out object [] out_args)
                {
                        MonoMethodMessage mMsg = (MonoMethodMessage) msg;
-                       mMsg.LogicalCallContext = CallContext.CreateLogicalCallContext (true);
+                       mMsg.LogicalCallContext = ExecutionContext.CreateLogicalCallContext (true);
                        CallType call_type = mMsg.CallType;
                        bool is_remproxy = (rp is RemotingProxy);
 
index 66ef9ce0e64df1e9a4d15b7b0dc431eb4f4f19c2..804806536ffb58d4661182ae1db6dd1525514e41 100644 (file)
@@ -169,7 +169,7 @@ namespace System.Runtime.Remoting
                                                returnArgs [n++] = null; 
                                }
                                
-                               result = new ReturnMessage (rval, returnArgs, n, CallContext.CreateLogicalCallContext (true), reqMsg);
+                               result = new ReturnMessage (rval, returnArgs, n, ExecutionContext.CreateLogicalCallContext (true), reqMsg);
                        } 
                        catch (Exception e) 
                        {
@@ -770,7 +770,7 @@ namespace System.Runtime.Remoting
                [SecurityPermission (SecurityAction.Assert, SerializationFormatter = true)] // FIXME: to be reviewed
                internal static byte[] SerializeCallData (object obj)
                {
-                       LogicalCallContext ctx = CallContext.CreateLogicalCallContext (false);
+                       LogicalCallContext ctx = ExecutionContext.CreateLogicalCallContext (false);
                        if (ctx != null) {
                                CACD cad = new CACD ();
                                cad.d = obj;
index bb023257b4769978f0db08de9d5a5a4a03119811..7277fea2ce867f67fda57858be9520d590115042 100644 (file)
@@ -76,6 +76,8 @@ namespace System.Threading.Tasks
                CancellationToken token;
                CancellationTokenRegistration? cancellationRegistration;
 
+               ExecutionContext ec;
+
                internal const TaskCreationOptions WorkerTaskNotSupportedOptions = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
 
                const TaskCreationOptions MaxTaskCreationOptions =
@@ -158,6 +160,8 @@ namespace System.Threading.Tasks
 
                        if (token.CanBeCanceled && !ignoreCancellation)
                                cancellationRegistration = token.Register (l => ((Task) l).CancelReal (), this);
+
+                       ec = ExecutionContext.Capture (false, true);
                }
 
                static bool HasFlag (TaskCreationOptions opt, TaskCreationOptions member)
@@ -428,7 +432,10 @@ namespace System.Threading.Tasks
                                status = TaskStatus.Running;
                                
                                try {
-                                       InnerInvoke ();
+                                       if (ec != null)
+                                               ExecutionContext.Run (ec, l => ((Task) l).InnerInvoke (), this);
+                                       else
+                                               InnerInvoke ();
                                } catch (OperationCanceledException oce) {
                                        if (token != CancellationToken.None && oce.CancellationToken == token)
                                                CancelReal ();
index ce55788e0ae2e5a8b57b8585c489e32716857a23..5718bbfc23d4c4e5c1f9067cdebe4ca14956a5f7 100644 (file)
@@ -4,8 +4,10 @@
 // Authors:
 //     Lluis Sanchez (lluis@novell.com)
 //     Sebastien Pouliot  <sebastien@ximian.com>
+//  Marek Safar (marek.safar@gmail.com)
 //
 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -32,6 +34,7 @@ using System.Runtime.Serialization;
 using System.Security;
 using System.Security.Permissions;
 using System.Runtime.Remoting.Messaging;
+using System.Collections.Generic;
 
 namespace System.Threading {
        [Serializable]
@@ -46,17 +49,21 @@ namespace System.Threading {
                private LogicalCallContext _lcc;
                private bool _suppressFlow;
                private bool _capture;
+               Dictionary<string, object> local_data;
 
                internal ExecutionContext ()
                {
                }
 
-               internal ExecutionContext (ExecutionContext ec)
+               private ExecutionContext (ExecutionContext ec)
                {
 #if !MOBILE
                        if (ec._sc != null)
                                _sc = new SecurityContext (ec._sc);
 #endif
+                       if (ec._lcc != null)
+                               _lcc = (LogicalCallContext) ec._lcc.Clone ();
+
                        _suppressFlow = ec._suppressFlow;
                        _capture = true;
                }
@@ -69,24 +76,23 @@ namespace System.Threading {
 
                public static ExecutionContext Capture ()
                {
-                       return Capture (true);
+                       return Capture (true, false);
                }
                
-               internal static ExecutionContext Capture (bool captureSyncContext)
+               internal static ExecutionContext Capture (bool captureSyncContext, bool nullOnEmpty)
                {
-                       ExecutionContext ec = Thread.CurrentThread.ExecutionContext;
+                       ExecutionContext ec = Current;
                        if (ec.FlowSuppressed)
                                return null;
 
+                       if (nullOnEmpty && ec._sc == null && (ec._lcc == null || !ec._lcc.HasInfo))
+                               return null;
+
                        ExecutionContext capture = new ExecutionContext (ec);
 #if !MOBILE
                        if (SecurityManager.SecurityEnabled)
                                capture.SecurityContext = SecurityContext.Capture ();
 #endif
-
-#if !MONOTOUCH
-                       capture.LogicalCallContext = CallContext.CreateLogicalCallContext (false);
-#endif
                        return capture;
                }
                
@@ -122,7 +128,7 @@ namespace System.Threading {
                internal LogicalCallContext LogicalCallContext {
                        get {
                                if (_lcc == null)
-                                       return new LogicalCallContext ();
+                                       _lcc = new LogicalCallContext ();
                                return _lcc;
                        }
                        set {
@@ -130,6 +136,17 @@ namespace System.Threading {
                        }
                }
 
+               internal Dictionary<string, object> DataStore {
+                       get {
+                               if (local_data == null)
+                                       local_data = new Dictionary<string, object> ();
+                               return local_data;
+                       }
+                       set {
+                               local_data = value;
+                       }
+               }
+
 #if !MOBILE
                internal SecurityContext SecurityContext {
                        get {
@@ -146,25 +163,20 @@ namespace System.Threading {
                        set { _suppressFlow = value; }
                }
 
-               // Note: Previous to version 2.0 only the CompressedStack and (sometimes!) the WindowsIdentity
-               // were propagated to new threads. This is why ExecutionContext is internal in before NET_2_0.
-               // It also means that all newer context classes should be here (i.e. inside the #if NET_2_0).
-
                public static bool IsFlowSuppressed ()
                {
-                       return Thread.CurrentThread.ExecutionContext.FlowSuppressed;
+                       return Current.FlowSuppressed;
                }
 
                public static void RestoreFlow ()
                {
-                       ExecutionContext ec = Thread.CurrentThread.ExecutionContext;
+                       ExecutionContext ec = Current;
                        if (!ec.FlowSuppressed)
                                throw new InvalidOperationException ();
 
                        ec.FlowSuppressed = false;
                }
 
-               [MonoTODO ("only the SecurityContext is considered")]
                [SecurityPermission (SecurityAction.LinkDemand, Infrastructure = true)]
                public static void Run (ExecutionContext executionContext, ContextCallback callback, object state)
                {
@@ -173,22 +185,13 @@ namespace System.Threading {
                                        "Null ExecutionContext"));
                        }
 
-#if MOBILE
-                       callback (state);
-#else
-                       // FIXME: supporting more than one context should be done with executionContextSwitcher
-                       // and will requires a rewrite of this method
-                       var callContextCallBack = new ContextCallback (new Action<object> ((ostate) => {
-                               var originalCallContext = CallContext.CreateLogicalCallContext (true);
-                               try {
-                                       CallContext.SetCurrentCallContext (executionContext.LogicalCallContext);
-                                       callback (ostate);
-                               } finally {
-                                       CallContext.SetCurrentCallContext (originalCallContext);
-                               }
-                       }));
-                       SecurityContext.Run (executionContext.SecurityContext, callContextCallBack, state);
-#endif
+                       var prev = Current;
+                       try {
+                               Thread.CurrentThread.ExecutionContext = executionContext;
+                               callback (state);
+                       } finally {
+                               Thread.CurrentThread.ExecutionContext = prev;
+                       }
                }
 
                public static AsyncFlowControl SuppressFlow ()
@@ -197,5 +200,33 @@ namespace System.Threading {
                        t.ExecutionContext.FlowSuppressed = true;
                        return new AsyncFlowControl (t, AsyncFlowControlType.Execution);
                }
+
+               internal static LogicalCallContext CreateLogicalCallContext (bool createEmpty)
+               {
+                       var lcc = Current._lcc;
+                       if (lcc == null) {
+                               if (createEmpty)
+                                       lcc = new LogicalCallContext ();
+
+                               return lcc;
+                       }
+
+                       return (LogicalCallContext) lcc.Clone ();
+               }
+
+               internal void FreeNamedDataSlot (string name)
+               {
+                       if (_lcc != null)
+                               _lcc.FreeNamedDataSlot (name);
+
+                       if (local_data != null)
+                               local_data.Remove (name);
+               }
+
+               internal static ExecutionContext Current {
+                       get {
+                               return Thread.CurrentThread.ExecutionContext;
+                       }
+               }
        }
 }
\ No newline at end of file
index d4a8d167a34246acea4db11c40161a6f6391add7..a52d8d1258506712ba856214353c4a4468fc517b 100644 (file)
@@ -696,8 +696,7 @@ namespace System.Threading {
 
                public void Start() {
                        // propagate informations from the original thread to the new thread
-                       if (!ExecutionContext.IsFlowSuppressed ())
-                               ec_to_set = ExecutionContext.Capture ();
+                       ec_to_set = ExecutionContext.Capture (false, true);
                        Internal._serialized_principal = CurrentThread.Internal._serialized_principal;
 
                        // Thread_internal creates and starts the new thread, 
@@ -858,7 +857,6 @@ namespace System.Threading {
                        Internal.stack_size = CheckStackSize (maxStackSize);
                }
 
-               [MonoTODO ("limited to CompressedStack support")]
                public ExecutionContext ExecutionContext {
                        [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
                        get {
@@ -866,6 +864,9 @@ namespace System.Threading {
                                        _ec = new ExecutionContext ();
                                return _ec;
                        }
+                       internal set {
+                               _ec = value;
+                       }
                }
 
                public int ManagedThreadId {
diff --git a/mcs/class/corlib/Test/System.Runtime.Remoting.Messaging/CallContextTest.cs b/mcs/class/corlib/Test/System.Runtime.Remoting.Messaging/CallContextTest.cs
new file mode 100644 (file)
index 0000000..9bae6c6
--- /dev/null
@@ -0,0 +1,204 @@
+//
+// CallContexTest.cs
+//
+// Authors:
+//     Marek Safar  <marek.safar@gmail.com>
+//  Chris F Carroll <chris.carroll@unforgettable.me.uk>
+//
+// Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
+// Copyright (C) 2013 Chris F Carroll (http://cafe-encounter.net)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using NUnit.Framework;
+using System.Runtime.Remoting.Messaging;
+using System.Threading;
+#if NET_4_0
+using System.Threading.Tasks;
+#endif
+
+namespace MonoTests.System.Runtime.Remoting.Messaging
+{
+       [TestFixture]
+       public class CallContextTest
+       {
+               public class Holder : ILogicalThreadAffinative
+               {
+                       public string Value { get; set; }
+               }
+
+               const string SlotName = "Test";
+
+               [Test]
+               public void CallContextPropagation_Thread ()
+               {
+                       bool passed = false;
+                       var t = new Thread (() => {
+                               var h = CallContext.GetData (SlotName) as Holder;
+                               passed = h == null;
+                               CallContext.FreeNamedDataSlot (SlotName);
+                       });
+                       t.Start ();
+                       t.Join ();
+                       Assert.IsTrue (passed, "#1");
+
+                       var holder = new Holder {
+                               Value = "Hello World"
+                       };
+                       CallContext.SetData (SlotName, holder);
+
+                       t = new Thread (() => {
+                               var h = CallContext.GetData (SlotName) as Holder;
+                               passed = h == holder;
+                               CallContext.FreeNamedDataSlot (SlotName);
+                       });
+                       t.Start ();
+                       t.Join ();
+                       CallContext.FreeNamedDataSlot (SlotName);
+
+                       Assert.IsTrue (passed, "#2");
+               }
+
+               [Test]
+               public void CallContextPropagation_ThreadPool ()
+               {
+                       var holder = new Holder {
+                               Value = "Hello World"
+                       };
+                       CallContext.SetData (SlotName, holder);
+
+                       bool passed = false;
+                       var mre = new ManualResetEvent (false);
+                       ThreadPool.QueueUserWorkItem(x => {
+                               var h = CallContext.GetData (SlotName) as Holder;
+                               passed = h == holder;
+                               CallContext.FreeNamedDataSlot (SlotName);
+                               mre.Set ();
+                       });
+
+                       Assert.IsTrue (mre.WaitOne (3000), "#1");
+                       Assert.IsTrue (passed, "#2");
+
+                       CallContext.FreeNamedDataSlot (SlotName);
+               }
+
+               [Test]
+               public void CallContextPropagation_Not_ThreadPool ()
+               {
+                       CallContext.SetData (SlotName, "x");
+
+                       bool passed = false;
+                       var mre = new ManualResetEvent (false);
+                       ThreadPool.QueueUserWorkItem(x => {
+                               var h = (string)CallContext.GetData (SlotName);
+                               passed = h == null;
+                               CallContext.FreeNamedDataSlot (SlotName);
+                               mre.Set ();
+                       });
+
+                       Assert.IsTrue (mre.WaitOne (3000), "#1");
+                       Assert.IsTrue (passed, "#2");
+
+                       CallContext.FreeNamedDataSlot (SlotName);
+               }
+
+#if NET_4_0
+               [Test]
+               public void CallContextPropagation_Task ()
+               {
+                       var holder = new Holder {
+                               Value = "Hello World"
+                       };
+                       CallContext.SetData (SlotName, holder);
+                       
+                       bool passed = false;
+                       var t = Task.Factory.StartNew(() => {
+                               var h = CallContext.GetData (SlotName) as Holder;
+                               passed = h == holder;
+                               CallContext.FreeNamedDataSlot (SlotName);
+                       });
+
+                       Assert.IsTrue (t.Wait (3000), "#1");
+                       Assert.IsTrue (passed, "#2");
+
+                       CallContext.FreeNamedDataSlot (SlotName);
+               }
+
+               [Test]
+               public void CallContextPropagation_TaskContinuation ()
+               {
+                       string d1 = null;
+                       string d2 = null;
+                       Console.WriteLine("Current thread: {0}", Thread.CurrentThread.ManagedThreadId);
+
+                       var ct = Thread.CurrentThread.ManagedThreadId;
+                       CallContext.LogicalSetData ("d1", "logicalData");
+                       CallContext.SetData ("d2", "data2");
+                       var t = Task.Factory.StartNew (() => {
+                               }).ContinueWith (task => {
+                                       d1 = (string) CallContext.LogicalGetData ("d1");
+                                       d2 = (string) CallContext.GetData ("d2");
+                               }, TaskContinuationOptions.ExecuteSynchronously);
+
+                       Assert.IsTrue (t.Wait (3000), "#0");
+                       Assert.AreEqual ("logicalData", d1, "#1");
+                       Assert.IsNull (d2, "#2");
+
+                       CallContext.FreeNamedDataSlot ("d1");
+                       CallContext.FreeNamedDataSlot ("d2");
+               }
+#endif
+
+               [Test]
+               public void FreeNamedDataSlot_ShouldClearLogicalData ()
+               {
+                       CallContext.LogicalSetData ("slotkey", "illogical");
+                       CallContext.FreeNamedDataSlot ("slotkey");
+
+                       Assert.IsNull (CallContext.LogicalGetData ("slotkey"), "Illogical slot should be null");
+                       Assert.IsNull (CallContext.GetData ("slotkey"), "Illogical slot should be null");
+               }
+
+               [Test]
+               public void FreeNamedDataSlot_ShouldClearIllogicalData ()
+               {
+                       CallContext.SetData ("slotkey", "illogical");
+                       CallContext.FreeNamedDataSlot ("slotkey");
+
+                       Assert.IsNull (CallContext.LogicalGetData ("slotkey"), "Illogical slot should be null");
+                       Assert.IsNull (CallContext.GetData ("slotkey"), "Illogical slot should be null");
+               }
+
+               [Test]
+               public void FreeNamedDataSlot_ShouldClearBothLogicalAndIllogicalData ()
+               {
+                       CallContext.LogicalSetData ("slotkey","logical");
+                       CallContext.SetData ("slotkey", "illogical");
+                       CallContext.FreeNamedDataSlot ("slotkey");
+
+                       Assert.IsNull (CallContext.LogicalGetData ("slotkey"), "Illogical slot should be null");
+                       Assert.IsNull (CallContext.GetData ("slotkey"), "Illogical slot should be null");
+               }
+       }
+}
index 952d64a524f6501c781c4f2f6d70da5c18c0a51d..a4f73aad3c8180f5b60d913181be1d4430110dae 100644 (file)
@@ -192,6 +192,7 @@ System.Runtime.Remoting/SoapServicesTest.cs
 System.Runtime.Remoting/SynchronizationAttributeTest.cs
 System.Runtime.Remoting.Channels/ChannelServicesTest.cs
 System.Runtime.Remoting.Contexts/SynchronizationAttributeTest.cs
+System.Runtime.Remoting.Messaging/CallContextTest.cs
 System.Runtime.Remoting.Metadata.W3cXsd2001/SoapHexBinaryTest.cs
 System.Runtime.Serialization/FormatterServicesTests.cs
 System.Runtime.Serialization/ObjectIDGeneratorTests.cs