//
// 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 ()
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)
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 ()
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;
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;
}
}
}
//
// 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
}
}
- public bool HasInfo
- {
- get
- {
- return (_data != null && _data.Count > 0);
+ public bool HasInfo {
+ get {
+ return _data != null && _data.Count > 0;
}
}
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)
public void SetData (string name, object data)
{
- if (_data == null) _data = new Hashtable ();
+ if (_data == null)
+ _data = new Hashtable ();
_data [name] = data;
}
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; }
}
}
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.InteropServices;
+using System.Threading;
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);
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)
{
[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;
CancellationToken token;
CancellationTokenRegistration? cancellationRegistration;
+ ExecutionContext ec;
+
internal const TaskCreationOptions WorkerTaskNotSupportedOptions = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
const TaskCreationOptions MaxTaskCreationOptions =
if (token.CanBeCanceled && !ignoreCancellation)
cancellationRegistration = token.Register (l => ((Task) l).CancelReal (), this);
+
+ ec = ExecutionContext.Capture (false, true);
}
static bool HasFlag (TaskCreationOptions opt, TaskCreationOptions member)
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 ();
// 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
using System.Security;
using System.Security.Permissions;
using System.Runtime.Remoting.Messaging;
+using System.Collections.Generic;
namespace System.Threading {
[Serializable]
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;
}
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;
}
internal LogicalCallContext LogicalCallContext {
get {
if (_lcc == null)
- return new LogicalCallContext ();
+ _lcc = new LogicalCallContext ();
return _lcc;
}
set {
}
}
+ 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 {
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)
{
"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 ()
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
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,
Internal.stack_size = CheckStackSize (maxStackSize);
}
- [MonoTODO ("limited to CompressedStack support")]
public ExecutionContext ExecutionContext {
[ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
get {
_ec = new ExecutionContext ();
return _ec;
}
+ internal set {
+ _ec = value;
+ }
}
public int ManagedThreadId {
--- /dev/null
+//
+// 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");
+ }
+ }
+}
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