Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / threading / synchronizationcontext.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 //
6 // <OWNER>Microsoft</OWNER>
7 /*============================================================
8 **
9 ** Class:  SynchronizationContext
10 **
11 **
12 ** Purpose: Capture synchronization semantics for asynchronous callbacks
13 **
14 ** 
15 ===========================================================*/
16
17 namespace System.Threading
18 {    
19 #if !MONO
20     using Microsoft.Win32.SafeHandles;
21 #endif
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
28     using System.Runtime;
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;
35
36
37 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
38     [Flags]
39     enum SynchronizationContextProperties
40     {
41         None = 0,
42         RequireWaitNotification = 0x1
43     };
44 #endif
45
46 #if FEATURE_COMINTEROP && FEATURE_APPX
47     //
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.
50     //
51     [FriendAccessAllowed]
52     [SecurityCritical]
53     internal class WinRTSynchronizationContextFactoryBase
54     {
55         [SecurityCritical]
56         public virtual SynchronizationContext Create(object coreDispatcher) {return null;}
57     }
58 #endif //FEATURE_COMINTEROP
59
60 #if !FEATURE_CORECLR
61     [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags =SecurityPermissionFlag.ControlPolicy|SecurityPermissionFlag.ControlEvidence)]
62 #endif
63     public class SynchronizationContext
64     {
65 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
66         SynchronizationContextProperties _props = SynchronizationContextProperties.None;
67 #endif
68         
69         public SynchronizationContext()
70         {
71         }
72                         
73 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
74
75         // helper delegate to statically bind to Wait method
76         private delegate int WaitDelegate(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
77
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;
83
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()
88         {
89             //
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.
93             //
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.
99             //
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)
106             {
107                 RuntimeHelpers.PrepareDelegate(new WaitDelegate(this.Wait));
108
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;
114             }
115
116             _props |= SynchronizationContextProperties.RequireWaitNotification;
117         }
118
119         public bool IsWaitNotificationRequired()
120         {
121             return ((_props & SynchronizationContextProperties.RequireWaitNotification) != 0);  
122         }
123 #endif
124
125     
126         public virtual void Send(SendOrPostCallback d, Object state)
127         {
128             d(state);
129         }
130
131         public virtual void Post(SendOrPostCallback d, Object state)
132         {
133             ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
134         }
135
136         
137         /// <summary>
138         ///     Optional override for subclasses, for responding to notification that operation is starting.
139         /// </summary>
140         public virtual void OperationStarted()
141         {
142         }
143
144         /// <summary>
145         ///     Optional override for subclasses, for responding to notification that operation has completed.
146         /// </summary>
147         public virtual void OperationCompleted()
148         {
149         }
150
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)]
155         [PrePrepareMethod]
156         public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
157         {
158             if (waitHandles == null)
159             {
160                 throw new ArgumentNullException("waitHandles");
161             }
162             Contract.EndContractBlock();
163             return WaitHelper(waitHandles, waitAll, millisecondsTimeout);
164         }
165                                 
166         // Static helper to which the above method can delegate to in order to get the default 
167         // COM behavior.
168         [System.Security.SecurityCritical]  // auto-generated_required
169         [CLSCompliant(false)]
170         [PrePrepareMethod]
171         [ResourceExposure(ResourceScope.None)]
172         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
173 #if MONO
174         protected static int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
175         {
176             throw new NotImplementedException ();
177         }
178 #else
179         [MethodImplAttribute(MethodImplOptions.InternalCall)]
180         protected static extern int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
181 #endif
182 #endif
183
184 #if FEATURE_CORECLR
185         [ThreadStatic]
186         private static SynchronizationContext s_threadStaticContext;
187
188                 //
189                 // NetCF had a bug where SynchronizationContext.SetThreadStaticContext would set the SyncContext for every thread in the process.  
190                 // This was because they stored the value in a regular static field (NetCF has no support for ThreadStatic fields).  This was fixed in 
191                 // 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
192                 // to hold whatever context was last set on any thread.
193                 //
194         private static SynchronizationContext s_appDomainStaticContext;
195
196         [System.Security.SecurityCritical]
197         public static void SetSynchronizationContext(SynchronizationContext syncContext)
198         {
199             s_threadStaticContext = syncContext;
200         }
201
202         [System.Security.SecurityCritical]
203         public static void SetThreadStaticContext(SynchronizationContext syncContext)
204         {
205                         //
206                         // If this is a pre-Mango Windows Phone app, we need to set the SC for *all* threads to match the old NetCF behavior.
207                         //
208             if (CompatibilitySwitches.IsAppEarlierThanWindowsPhoneMango)
209                 s_appDomainStaticContext = syncContext;
210             else
211                 s_threadStaticContext = syncContext;
212         }
213
214         public static SynchronizationContext Current 
215         {
216             get      
217             {
218                 SynchronizationContext context = null;
219             
220                 if (CompatibilitySwitches.IsAppEarlierThanWindowsPhoneMango)
221                     context = s_appDomainStaticContext;
222                 else
223                     context = s_threadStaticContext;
224
225 #if FEATURE_APPX
226                 if (context == null && Environment.IsWinRTSupported)
227                     context = GetWinRTContext();
228 #endif
229
230                 return context;
231             }
232         }
233
234         // Get the last SynchronizationContext that was set explicitly (not flowed via ExecutionContext.Capture/Run)        
235         internal static SynchronizationContext CurrentNoFlow
236         {
237             [FriendAccessAllowed]
238             get
239             {
240                 return Current; // SC never flows
241             }
242         }
243
244 #else //FEATURE_CORECLR
245
246         // set SynchronizationContext on the current thread
247         [System.Security.SecurityCritical]  // auto-generated_required
248         public static void SetSynchronizationContext(SynchronizationContext syncContext)
249         {
250             ExecutionContext ec = Thread.CurrentThread.GetMutableExecutionContext();
251             ec.SynchronizationContext = syncContext;
252             ec.SynchronizationContextNoFlow = syncContext;
253         }
254
255 #if MOBILE_LEGACY
256         [Obsolete("The method is not supported and will be removed")]
257         public static void SetThreadStaticContext(SynchronizationContext syncContext)
258         {
259             throw new NotSupportedException ();
260         }
261 #endif
262
263         // Get the current SynchronizationContext on the current thread
264         public static SynchronizationContext Current 
265         {
266             get      
267             {
268                 return Thread.CurrentThread.GetExecutionContextReader().SynchronizationContext ?? GetThreadLocalContext();
269             }
270         }
271
272         // Get the last SynchronizationContext that was set explicitly (not flowed via ExecutionContext.Capture/Run)        
273         internal static SynchronizationContext CurrentNoFlow
274         {
275             [FriendAccessAllowed]
276             get
277             {
278                 return Thread.CurrentThread.GetExecutionContextReader().SynchronizationContextNoFlow ?? GetThreadLocalContext();
279             }
280         }
281
282         private static SynchronizationContext GetThreadLocalContext()
283         {
284             SynchronizationContext context = null;
285             
286 #if FEATURE_APPX
287             if (context == null && Environment.IsWinRTSupported)
288                 context = GetWinRTContext();
289 #endif
290
291 #if MONODROID
292             if (context == null)
293                 context = AndroidPlatform.GetDefaultSyncContext ();
294 #endif
295
296             return context;
297         }
298
299 #endif //FEATURE_CORECLR
300
301 #if FEATURE_APPX
302         [SecuritySafeCritical]
303         private static SynchronizationContext GetWinRTContext()
304         {
305             Contract.Assert(Environment.IsWinRTSupported);
306
307             // Temporary hack to avoid loading a bunch of DLLs in every managed process.
308             // This disables this feature for non-AppX processes that happen to use CoreWindow/CoreDispatcher,
309             // which is not what we want.
310             if (!AppDomain.IsAppXModel())
311                 return null;
312
313             //
314             // We call into the VM to get the dispatcher.  This is because:
315             //
316             //  a) We cannot call the WinRT APIs directly from mscorlib, because we don't have the fancy projections here.
317             //  b) We cannot call into System.Runtime.WindowsRuntime here, because we don't want to load that assembly
318             //     into processes that don't need it (for performance reasons).
319             //
320             // So, we check the VM to see if the current thread has a dispatcher; if it does, we pass that along to
321             // System.Runtime.WindowsRuntime to get a corresponding SynchronizationContext.
322             //
323             object dispatcher = GetWinRTDispatcherForCurrentThread();
324             if (dispatcher != null)
325                 return GetWinRTSynchronizationContextFactory().Create(dispatcher);
326
327             return null;
328         }
329
330         [SecurityCritical]
331         static WinRTSynchronizationContextFactoryBase s_winRTContextFactory;
332
333         [SecurityCritical]
334         private static WinRTSynchronizationContextFactoryBase GetWinRTSynchronizationContextFactory()
335         {
336             //
337             // Since we can't directly reference System.Runtime.WindowsRuntime from mscorlib, we have to get the factory via reflection.
338             // It would be better if we could just implement WinRTSynchronizationContextFactory in mscorlib, but we can't, because
339             // we can do very little with WinRT stuff in mscorlib.
340             //
341             WinRTSynchronizationContextFactoryBase factory = s_winRTContextFactory;
342             if (factory == null)
343             {
344                 Type factoryType = Type.GetType("System.Threading.WinRTSynchronizationContextFactory, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
345                 s_winRTContextFactory = factory = (WinRTSynchronizationContextFactoryBase)Activator.CreateInstance(factoryType, true);
346             }
347             return factory;
348         }
349
350         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
351         [SecurityCritical]
352         [ResourceExposure(ResourceScope.None)]
353         [SuppressUnmanagedCodeSecurity]
354         [return: MarshalAs(UnmanagedType.Interface)]
355         private static extern object GetWinRTDispatcherForCurrentThread();
356 #endif //FEATURE_APPX
357
358
359         // helper to Clone this SynchronizationContext, 
360         public virtual SynchronizationContext CreateCopy()
361         {
362             // the CLR dummy has an empty clone function - no member data
363             return new SynchronizationContext();
364         }
365
366 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
367         [System.Security.SecurityCritical]  // auto-generated
368         private static int InvokeWaitMethodHelper(SynchronizationContext syncContext, IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
369         {
370             return syncContext.Wait(waitHandles, waitAll, millisecondsTimeout);
371         }
372 #endif
373     }
374 }