1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 /*============================================================
8 ** Purpose: Capture synchronization semantics for asynchronous callbacks
11 ===========================================================*/
13 namespace System.Threading
15 using Microsoft.Win32.SafeHandles;
16 using System.Runtime.InteropServices;
17 using System.Runtime.CompilerServices;
18 using System.Runtime.ExceptionServices;
20 using System.Runtime.Versioning;
21 using System.Runtime.ConstrainedExecution;
22 using System.Reflection;
23 using System.Security;
24 using System.Diagnostics;
25 using System.Diagnostics.CodeAnalysis;
29 internal enum SynchronizationContextProperties
32 RequireWaitNotification = 0x1
35 #if FEATURE_COMINTEROP && FEATURE_APPX
37 // This is implemented in System.Runtime.WindowsRuntime, allowing us to ask that assembly for a WinRT-specific SyncCtx.
39 // [FriendAccessAllowed]
40 internal abstract class WinRTSynchronizationContextFactoryBase
42 public abstract SynchronizationContext Create(object coreDispatcher);
44 #endif //FEATURE_COMINTEROP
46 public class SynchronizationContext
48 private SynchronizationContextProperties _props = SynchronizationContextProperties.None;
50 public SynchronizationContext()
55 // helper delegate to statically bind to Wait method
56 private delegate int WaitDelegate(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
58 private static Type s_cachedPreparedType1;
59 private static Type s_cachedPreparedType2;
60 private static Type s_cachedPreparedType3;
61 private static Type s_cachedPreparedType4;
62 private static Type s_cachedPreparedType5;
64 // protected so that only the derived sync context class can enable these flags
65 [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "We never dereference s_cachedPreparedType*, so ordering is unimportant")]
66 protected void SetWaitNotificationRequired()
69 // Prepare the method so that it can be called in a reliable fashion when a wait is needed.
70 // This will obviously only make the Wait reliable if the Wait method is itself reliable. The only thing
71 // preparing the method here does is to ensure there is no failure point before the method execution begins.
73 // Preparing the method in this way is quite expensive, but only needs to be done once per type, per AppDomain.
74 // So we keep track of a few types we've already prepared in this AD. It is uncommon to have more than
75 // a few SynchronizationContext implementations, so we only cache the first five we encounter; this lets
76 // our cache be much faster than a more general cache might be. This is important, because this
77 // is a *very* hot code path for many WPF and WinForms apps.
79 Type type = this.GetType();
80 if (s_cachedPreparedType1 != type &&
81 s_cachedPreparedType2 != type &&
82 s_cachedPreparedType3 != type &&
83 s_cachedPreparedType4 != type &&
84 s_cachedPreparedType5 != type)
86 RuntimeHelpers.PrepareDelegate(new WaitDelegate(this.Wait));
88 if (s_cachedPreparedType1 == null) s_cachedPreparedType1 = type;
89 else if (s_cachedPreparedType2 == null) s_cachedPreparedType2 = type;
90 else if (s_cachedPreparedType3 == null) s_cachedPreparedType3 = type;
91 else if (s_cachedPreparedType4 == null) s_cachedPreparedType4 = type;
92 else if (s_cachedPreparedType5 == null) s_cachedPreparedType5 = type;
95 _props |= SynchronizationContextProperties.RequireWaitNotification;
98 public bool IsWaitNotificationRequired()
100 return ((_props & SynchronizationContextProperties.RequireWaitNotification) != 0);
104 public virtual void Send(SendOrPostCallback d, Object state)
109 public virtual void Post(SendOrPostCallback d, Object state)
111 ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
116 /// Optional override for subclasses, for responding to notification that operation is starting.
118 public virtual void OperationStarted()
123 /// Optional override for subclasses, for responding to notification that operation has completed.
125 public virtual void OperationCompleted()
129 // Method called when the CLR does a wait operation
130 [CLSCompliant(false)]
131 public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
133 return WaitHelper(waitHandles, waitAll, millisecondsTimeout);
136 // Method that can be called by Wait overrides
137 [CLSCompliant(false)]
138 protected static int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
140 if (waitHandles == null)
142 throw new ArgumentNullException(nameof(waitHandles));
145 return WaitHelperNative(waitHandles, waitAll, millisecondsTimeout);
148 // Static helper to which the above method can delegate to in order to get the default
150 [CLSCompliant(false)]
151 [MethodImplAttribute(MethodImplOptions.InternalCall)]
152 private static extern int WaitHelperNative(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
154 public static void SetSynchronizationContext(SynchronizationContext syncContext)
156 Thread.CurrentThread.SynchronizationContext = syncContext;
159 public static SynchronizationContext Current
163 SynchronizationContext context = Thread.CurrentThread.SynchronizationContext;
166 if (context == null && AppDomain.IsAppXModel())
167 context = GetWinRTContext();
175 private static SynchronizationContext GetWinRTContext()
177 Debug.Assert(Environment.IsWinRTSupported);
178 Debug.Assert(AppDomain.IsAppXModel());
181 // We call into the VM to get the dispatcher. This is because:
183 // a) We cannot call the WinRT APIs directly from mscorlib, because we don't have the fancy projections here.
184 // b) We cannot call into System.Runtime.WindowsRuntime here, because we don't want to load that assembly
185 // into processes that don't need it (for performance reasons).
187 // So, we check the VM to see if the current thread has a dispatcher; if it does, we pass that along to
188 // System.Runtime.WindowsRuntime to get a corresponding SynchronizationContext.
190 object dispatcher = GetWinRTDispatcherForCurrentThread();
191 if (dispatcher != null)
192 return GetWinRTSynchronizationContextFactory().Create(dispatcher);
197 private static WinRTSynchronizationContextFactoryBase s_winRTContextFactory;
199 private static WinRTSynchronizationContextFactoryBase GetWinRTSynchronizationContextFactory()
202 // Since we can't directly reference System.Runtime.WindowsRuntime from mscorlib, we have to get the factory via reflection.
203 // It would be better if we could just implement WinRTSynchronizationContextFactory in mscorlib, but we can't, because
204 // we can do very little with WinRT stuff in mscorlib.
206 WinRTSynchronizationContextFactoryBase factory = s_winRTContextFactory;
209 Type factoryType = Type.GetType("System.Threading.WinRTSynchronizationContextFactory, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
210 s_winRTContextFactory = factory = (WinRTSynchronizationContextFactoryBase)Activator.CreateInstance(factoryType, true);
215 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
216 [return: MarshalAs(UnmanagedType.Interface)]
217 private static extern object GetWinRTDispatcherForCurrentThread();
218 #endif //FEATURE_APPX
221 // helper to Clone this SynchronizationContext,
222 public virtual SynchronizationContext CreateCopy()
224 // the CLR dummy has an empty clone function - no member data
225 return new SynchronizationContext();
228 private static int InvokeWaitMethodHelper(SynchronizationContext syncContext, IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
230 return syncContext.Wait(waitHandles, waitAll, millisecondsTimeout);