3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <OWNER>Microsoft</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 partial class SynchronizationContext
65 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
66 SynchronizationContextProperties _props
= SynchronizationContextProperties
.None
;
69 public SynchronizationContext()
73 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
75 // helper delegate to statically bind to Wait method
76 private delegate int WaitDelegate(IntPtr
[] waitHandles
, bool waitAll
, int millisecondsTimeout
);
78 static Type s_cachedPreparedType1
;
79 static Type s_cachedPreparedType2
;
80 static Type s_cachedPreparedType3
;
81 static Type s_cachedPreparedType4
;
82 static Type s_cachedPreparedType5
;
84 // protected so that only the derived sync context class can enable these flags
85 [System
.Security
.SecuritySafeCritical
] // auto-generated
86 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification
= "We never dereference s_cachedPreparedType*, so ordering is unimportant")]
87 protected void SetWaitNotificationRequired()
90 // Prepare the method so that it can be called in a reliable fashion when a wait is needed.
91 // This will obviously only make the Wait reliable if the Wait method is itself reliable. The only thing
92 // preparing the method here does is to ensure there is no failure point before the method execution begins.
94 // Preparing the method in this way is quite expensive, but only needs to be done once per type, per AppDomain.
95 // So we keep track of a few types we've already prepared in this AD. It is uncommon to have more than
96 // a few SynchronizationContext implementations, so we only cache the first five we encounter; this lets
97 // our cache be much faster than a more general cache might be. This is important, because this
98 // is a *very* hot code path for many WPF and Microsoft apps.
100 Type type
= this.GetType();
101 if (s_cachedPreparedType1
!= type
&&
102 s_cachedPreparedType2
!= type
&&
103 s_cachedPreparedType3
!= type
&&
104 s_cachedPreparedType4
!= type
&&
105 s_cachedPreparedType5
!= type
)
107 RuntimeHelpers
.PrepareDelegate(new WaitDelegate(this.Wait
));
109 if (s_cachedPreparedType1
== null) s_cachedPreparedType1
= type
;
110 else if (s_cachedPreparedType2
== null) s_cachedPreparedType2
= type
;
111 else if (s_cachedPreparedType3
== null) s_cachedPreparedType3
= type
;
112 else if (s_cachedPreparedType4
== null) s_cachedPreparedType4
= type
;
113 else if (s_cachedPreparedType5
== null) s_cachedPreparedType5
= type
;
116 _props
|= SynchronizationContextProperties
.RequireWaitNotification
;
119 public bool IsWaitNotificationRequired()
121 return ((_props
& SynchronizationContextProperties
.RequireWaitNotification
) != 0);
126 public virtual void Send(SendOrPostCallback d
, Object state
)
131 public virtual void Post(SendOrPostCallback d
, Object state
)
133 ThreadPool
.QueueUserWorkItem(new WaitCallback(d
), state
);
138 /// Optional override for subclasses, for responding to notification that operation is starting.
140 public virtual void OperationStarted()
145 /// Optional override for subclasses, for responding to notification that operation has completed.
147 public virtual void OperationCompleted()
151 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
152 // Method called when the CLR does a wait operation
153 [System
.Security
.SecurityCritical
] // auto-generated_required
154 [CLSCompliant(false)]
156 public virtual int Wait(IntPtr
[] waitHandles
, bool waitAll
, int millisecondsTimeout
)
158 if (waitHandles
== null)
160 throw new ArgumentNullException("waitHandles");
162 Contract
.EndContractBlock();
163 return WaitHelper(waitHandles
, waitAll
, millisecondsTimeout
);
166 // Static helper to which the above method can delegate to in order to get the default
168 [System
.Security
.SecurityCritical
] // auto-generated_required
169 [CLSCompliant(false)]
171 [ResourceExposure(ResourceScope
.None
)]
172 [ReliabilityContract(Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
174 protected static int WaitHelper(IntPtr
[] waitHandles
, bool waitAll
, int millisecondsTimeout
)
177 fixed (IntPtr
* pWaitHandles
= waitHandles
) {
178 return System
.Threading
.WaitHandle
.Wait_internal (pWaitHandles
, waitHandles
.Length
, waitAll
, millisecondsTimeout
);
183 [MethodImplAttribute(MethodImplOptions
.InternalCall
)]
184 protected static extern int WaitHelper(IntPtr
[] waitHandles
, bool waitAll
, int millisecondsTimeout
);
190 private static SynchronizationContext s_threadStaticContext
;
193 // NetCF had a bug where SynchronizationContext.SetThreadStaticContext would set the SyncContext for every thread in the process.
194 // This was because they stored the value in a regular static field (NetCF has no support for ThreadStatic fields). This was fixed in
195 // 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
196 // to hold whatever context was last set on any thread.
198 private static SynchronizationContext s_appDomainStaticContext
;
200 [System
.Security
.SecurityCritical
]
201 public static void SetSynchronizationContext(SynchronizationContext syncContext
)
203 s_threadStaticContext
= syncContext
;
206 [System
.Security
.SecurityCritical
]
207 public static void SetThreadStaticContext(SynchronizationContext syncContext
)
210 // If this is a pre-Mango Windows Phone app, we need to set the SC for *all* threads to match the old NetCF behavior.
212 if (CompatibilitySwitches
.IsAppEarlierThanWindowsPhoneMango
)
213 s_appDomainStaticContext
= syncContext
;
215 s_threadStaticContext
= syncContext
;
218 public static SynchronizationContext Current
222 SynchronizationContext context
= null;
224 if (CompatibilitySwitches
.IsAppEarlierThanWindowsPhoneMango
)
225 context
= s_appDomainStaticContext
;
227 context
= s_threadStaticContext
;
230 if (context
== null && Environment
.IsWinRTSupported
)
231 context
= GetWinRTContext();
238 // Get the last SynchronizationContext that was set explicitly (not flowed via ExecutionContext.Capture/Run)
239 internal static SynchronizationContext CurrentNoFlow
241 [FriendAccessAllowed
]
244 return Current
; // SC never flows
248 #else //FEATURE_CORECLR
250 // set SynchronizationContext on the current thread
251 [System
.Security
.SecurityCritical
] // auto-generated_required
252 public static void SetSynchronizationContext(SynchronizationContext syncContext
)
254 ExecutionContext ec
= Thread
.CurrentThread
.GetMutableExecutionContext();
255 ec
.SynchronizationContext
= syncContext
;
256 ec
.SynchronizationContextNoFlow
= syncContext
;
260 [Obsolete("The method is not supported and will be removed")]
261 public static void SetThreadStaticContext(SynchronizationContext syncContext
)
263 throw new NotSupportedException ();
267 // Get the current SynchronizationContext on the current thread
268 public static SynchronizationContext Current
272 return Thread
.CurrentThread
.GetExecutionContextReader().SynchronizationContext
?? GetThreadLocalContext();
276 // Get the last SynchronizationContext that was set explicitly (not flowed via ExecutionContext.Capture/Run)
277 internal static SynchronizationContext CurrentNoFlow
279 [FriendAccessAllowed
]
282 return Thread
.CurrentThread
.GetExecutionContextReader().SynchronizationContextNoFlow
?? GetThreadLocalContext();
286 private static SynchronizationContext
GetThreadLocalContext()
288 SynchronizationContext context
= null;
291 if (context
== null && Environment
.IsWinRTSupported
)
292 context
= GetWinRTContext();
297 context
= AndroidPlatform
.GetDefaultSyncContext ();
303 #endif //FEATURE_CORECLR
306 [SecuritySafeCritical
]
307 private static SynchronizationContext
GetWinRTContext()
309 Contract
.Assert(Environment
.IsWinRTSupported
);
311 // Temporary hack to avoid loading a bunch of DLLs in every managed process.
312 // This disables this feature for non-AppX processes that happen to use CoreWindow/CoreDispatcher,
313 // which is not what we want.
314 if (!AppDomain
.IsAppXModel())
318 // We call into the VM to get the dispatcher. This is because:
320 // a) We cannot call the WinRT APIs directly from mscorlib, because we don't have the fancy projections here.
321 // b) We cannot call into System.Runtime.WindowsRuntime here, because we don't want to load that assembly
322 // into processes that don't need it (for performance reasons).
324 // So, we check the VM to see if the current thread has a dispatcher; if it does, we pass that along to
325 // System.Runtime.WindowsRuntime to get a corresponding SynchronizationContext.
327 object dispatcher
= GetWinRTDispatcherForCurrentThread();
328 if (dispatcher
!= null)
329 return GetWinRTSynchronizationContextFactory().Create(dispatcher
);
335 static WinRTSynchronizationContextFactoryBase s_winRTContextFactory
;
338 private static WinRTSynchronizationContextFactoryBase
GetWinRTSynchronizationContextFactory()
341 // Since we can't directly reference System.Runtime.WindowsRuntime from mscorlib, we have to get the factory via reflection.
342 // It would be better if we could just implement WinRTSynchronizationContextFactory in mscorlib, but we can't, because
343 // we can do very little with WinRT stuff in mscorlib.
345 WinRTSynchronizationContextFactoryBase factory
= s_winRTContextFactory
;
348 Type factoryType
= Type
.GetType("System.Threading.WinRTSynchronizationContextFactory, " + AssemblyRef
.SystemRuntimeWindowsRuntime
, true);
349 s_winRTContextFactory
= factory
= (WinRTSynchronizationContextFactoryBase
)Activator
.CreateInstance(factoryType
, true);
354 [DllImport(JitHelpers
.QCall
, CharSet
= CharSet
.Unicode
)]
356 [ResourceExposure(ResourceScope
.None
)]
357 [SuppressUnmanagedCodeSecurity
]
358 [return: MarshalAs(UnmanagedType
.Interface
)]
359 private static extern object GetWinRTDispatcherForCurrentThread();
360 #endif //FEATURE_APPX
363 // helper to Clone this SynchronizationContext,
364 public virtual SynchronizationContext
CreateCopy()
366 // the CLR dummy has an empty clone function - no member data
367 return new SynchronizationContext();
370 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
371 [System
.Security
.SecurityCritical
] // auto-generated
372 private static int InvokeWaitMethodHelper(SynchronizationContext syncContext
, IntPtr
[] waitHandles
, bool waitAll
, int millisecondsTimeout
)
374 return syncContext
.Wait(waitHandles
, waitAll
, millisecondsTimeout
);