3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <OWNER>[....]</OWNER>
7 /*============================================================
9 ** Class: SynchronizationContext
12 ** Purpose: Capture synchronization semantics for asynchronous callbacks
15 ===========================================================*/
17 namespace System.Threading
20 using Microsoft.Win32.SafeHandles;
22 using System.Security.Permissions;
23 using System.Runtime.InteropServices;
24 using System.Runtime.CompilerServices;
25 #if FEATURE_CORRUPTING_EXCEPTIONS
26 using System.Runtime.ExceptionServices;
27 #endif // FEATURE_CORRUPTING_EXCEPTIONS
29 using System.Runtime.Versioning;
30 using System.Runtime.ConstrainedExecution;
31 using System.Reflection;
32 using System.Security;
33 using System.Diagnostics.Contracts;
34 using System.Diagnostics.CodeAnalysis;
37 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
39 enum SynchronizationContextProperties
42 RequireWaitNotification = 0x1
46 #if FEATURE_COMINTEROP && FEATURE_APPX
48 // This is implemented in System.Runtime.WindowsRuntime, allowing us to ask that assembly for a WinRT-specific SyncCtx.
49 // I'd like this to be an interface, or at least an abstract class - but neither seems to play nice with FriendAccessAllowed.
53 internal class WinRTSynchronizationContextFactoryBase
56 public virtual SynchronizationContext Create(object coreDispatcher) {return null;}
58 #endif //FEATURE_COMINTEROP
61 [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags =SecurityPermissionFlag.ControlPolicy|SecurityPermissionFlag.ControlEvidence)]
63 public class SynchronizationContext
65 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
66 SynchronizationContextProperties _props = SynchronizationContextProperties.None;
69 public SynchronizationContext()
73 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
75 static Type s_cachedPreparedType1;
76 static Type s_cachedPreparedType2;
77 static Type s_cachedPreparedType3;
78 static Type s_cachedPreparedType4;
79 static Type s_cachedPreparedType5;
81 // protected so that only the derived [....] context class can enable these flags
82 [System.Security.SecuritySafeCritical] // auto-generated
83 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "We never dereference s_cachedPreparedType*, so ordering is unimportant")]
84 protected void SetWaitNotificationRequired()
87 // Prepare the method so that it can be called in a reliable fashion when a wait is needed.
88 // This will obviously only make the Wait reliable if the Wait method is itself reliable. The only thing
89 // preparing the method here does is to ensure there is no failure point before the method execution begins.
91 // Preparing the method in this way is quite expensive, but only needs to be done once per type, per AppDomain.
92 // So we keep track of a few types we've already prepared in this AD. It is uncommon to have more than
93 // a few SynchronizationContext implementations, so we only cache the first five we encounter; this lets
94 // our cache be much faster than a more general cache might be. This is important, because this
95 // is a *very* hot code path for many WPF and [....] apps.
97 Type type = this.GetType();
98 if (s_cachedPreparedType1 != type &&
99 s_cachedPreparedType2 != type &&
100 s_cachedPreparedType3 != type &&
101 s_cachedPreparedType4 != type &&
102 s_cachedPreparedType5 != type)
104 RuntimeHelpers.PrepareDelegate(new WaitDelegate(this.Wait));
106 if (s_cachedPreparedType1 == null) s_cachedPreparedType1 = type;
107 else if (s_cachedPreparedType2 == null) s_cachedPreparedType2 = type;
108 else if (s_cachedPreparedType3 == null) s_cachedPreparedType3 = type;
109 else if (s_cachedPreparedType4 == null) s_cachedPreparedType4 = type;
110 else if (s_cachedPreparedType5 == null) s_cachedPreparedType5 = type;
113 _props |= SynchronizationContextProperties.RequireWaitNotification;
116 public bool IsWaitNotificationRequired()
118 return ((_props & SynchronizationContextProperties.RequireWaitNotification) != 0);
123 public virtual void Send(SendOrPostCallback d, Object state)
128 public virtual void Post(SendOrPostCallback d, Object state)
130 ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
135 /// Optional override for subclasses, for responding to notification that operation is starting.
137 public virtual void OperationStarted()
142 /// Optional override for subclasses, for responding to notification that operation has completed.
144 public virtual void OperationCompleted()
148 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
149 // Method called when the CLR does a wait operation
150 [System.Security.SecurityCritical] // auto-generated_required
151 [CLSCompliant(false)]
153 public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
155 if (waitHandles == null)
157 throw new ArgumentNullException("waitHandles");
159 Contract.EndContractBlock();
160 return WaitHelper(waitHandles, waitAll, millisecondsTimeout);
163 // Static helper to which the above method can delegate to in order to get the default
165 [System.Security.SecurityCritical] // auto-generated_required
166 [CLSCompliant(false)]
168 [ResourceExposure(ResourceScope.None)]
169 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
171 protected static int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
173 throw new NotImplementedException ();
176 [MethodImplAttribute(MethodImplOptions.InternalCall)]
177 protected static extern int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
181 // set SynchronizationContext on the current thread
182 [System.Security.SecurityCritical] // auto-generated_required
184 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
186 public static void SetSynchronizationContext(SynchronizationContext syncContext)
188 ExecutionContext ec = Thread.CurrentThread.GetMutableExecutionContext();
189 ec.SynchronizationContext = syncContext;
190 ec.SynchronizationContextNoFlow = syncContext;
195 // This is a framework-internal method for Jolt's use. The problem is that SynchronizationContexts set inside of a reverse p/invoke
196 // into an AppDomain are not persisted in that AppDomain; the next time the same thread calls into the same AppDomain,
197 // the [....] context will be null. For Silverlight, this means that it's impossible to persist a [....] context on the UI thread,
198 // since Jolt is constantly transitioning in and out of each control's AppDomain on that thread.
200 // So for Jolt we will track a special thread-static context, which *will* persist across calls from Jolt, and if the thread does not
201 // have a [....] context set in its execution context we'll use the thread-static context instead.
203 // This will break any future work that requires SynchronizationContext.Current to be in [....] with the value
204 // stored in a thread's ExecutionContext (wait notifications being one such example). If that becomes a problem, we will
205 // need to rework this mechanism (which is one reason it's not being exposed publically).
209 private static SynchronizationContext s_threadStaticContext;
211 #if FEATURE_LEGACYNETCF
213 // NetCF had a bug where SynchronizationContext.SetThreadStaticContext would set the SyncContext for every thread in the process.
214 // This was because they stored the value in a regular static field (NetCF has no support for ThreadStatic fields). This was fixed in
215 // Mango, but some apps built against pre-Mango WP7 do depend on the broken behavior. So for those apps we need an AppDomain-wide static
216 // to hold whatever context was last set on any thread.
218 private static SynchronizationContext s_appDomainStaticContext;
221 [System.Security.SecurityCritical]
222 #if FEATURE_LEGACYNETCF
223 public static void SetThreadStaticContext(SynchronizationContext syncContext)
225 internal static void SetThreadStaticContext(SynchronizationContext syncContext)
228 #if FEATURE_LEGACYNETCF
230 // If this is a pre-Mango Windows Phone app, we need to set the SC for *all* threads to match the old NetCF behavior.
232 if (CompatibilitySwitches.IsAppEarlierThanWindowsPhoneMango)
233 s_appDomainStaticContext = syncContext;
236 s_threadStaticContext = syncContext;
240 // Get the current SynchronizationContext on the current thread
241 public static SynchronizationContext Current
244 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
248 return Thread.CurrentThread.GetExecutionContextReader().SynchronizationContext ?? GetThreadLocalContext();
252 // Get the last SynchronizationContext that was set explicitly (not flowed via ExecutionContext.Capture/Run)
253 internal static SynchronizationContext CurrentNoFlow
255 [FriendAccessAllowed]
258 return Thread.CurrentThread.GetExecutionContextReader().SynchronizationContextNoFlow ?? GetThreadLocalContext();
263 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
265 private static SynchronizationContext GetThreadLocalContext()
267 SynchronizationContext context = null;
270 #if FEATURE_LEGACYNETCF
271 if (CompatibilitySwitches.IsAppEarlierThanWindowsPhoneMango)
272 context = s_appDomainStaticContext;
274 #endif //FEATURE_LEGACYNETCF
275 context = s_threadStaticContext;
276 #endif //FEATURE_CORECLR
279 if (context == null && Environment.IsWinRTSupported)
280 context = GetWinRTContext();
285 context = AndroidPlatform.GetDefaultSyncContext ();
292 [SecuritySafeCritical]
293 private static SynchronizationContext GetWinRTContext()
295 Contract.Assert(Environment.IsWinRTSupported);
297 // Temporary hack to avoid loading a bunch of DLLs in every managed process.
298 // This disables this feature for non-AppX processes that happen to use CoreWindow/CoreDispatcher,
299 // which is not what we want.
300 if (!AppDomain.IsAppXModel())
304 // We call into the VM to get the dispatcher. This is because:
306 // a) We cannot call the WinRT APIs directly from mscorlib, because we don't have the fancy projections here.
307 // b) We cannot call into System.Runtime.WindowsRuntime here, because we don't want to load that assembly
308 // into processes that don't need it (for performance reasons).
310 // So, we check the VM to see if the current thread has a dispatcher; if it does, we pass that along to
311 // System.Runtime.WindowsRuntime to get a corresponding SynchronizationContext.
313 object dispatcher = GetWinRTDispatcherForCurrentThread();
314 if (dispatcher != null)
315 return GetWinRTSynchronizationContextFactory().Create(dispatcher);
321 static WinRTSynchronizationContextFactoryBase s_winRTContextFactory;
324 private static WinRTSynchronizationContextFactoryBase GetWinRTSynchronizationContextFactory()
327 // Since we can't directly reference System.Runtime.WindowsRuntime from mscorlib, we have to get the factory via reflection.
328 // It would be better if we could just implement WinRTSynchronizationContextFactory in mscorlib, but we can't, because
329 // we can do very little with WinRT stuff in mscorlib.
331 WinRTSynchronizationContextFactoryBase factory = s_winRTContextFactory;
334 Type factoryType = Type.GetType("System.Threading.WinRTSynchronizationContextFactory, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
335 s_winRTContextFactory = factory = (WinRTSynchronizationContextFactoryBase)Activator.CreateInstance(factoryType, true);
340 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
342 [ResourceExposure(ResourceScope.None)]
343 [SuppressUnmanagedCodeSecurity]
344 [return: MarshalAs(UnmanagedType.Interface)]
345 private static extern object GetWinRTDispatcherForCurrentThread();
346 #endif //FEATURE_APPX
349 // helper to Clone this SynchronizationContext,
350 public virtual SynchronizationContext CreateCopy()
352 // the CLR dummy has an empty clone function - no member data
353 return new SynchronizationContext();
356 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
357 [System.Security.SecurityCritical] // auto-generated
358 private static int InvokeWaitMethodHelper(SynchronizationContext syncContext, IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
360 return syncContext.Wait(waitHandles, waitAll, millisecondsTimeout);