Update missing WaitHelper attributes
[mono.git] / mcs / class / referencesource / mscorlib / system / threading / synchronizationcontext.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 //
6 // <OWNER>[....]</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         static Type s_cachedPreparedType1;
76         static Type s_cachedPreparedType2;
77         static Type s_cachedPreparedType3;
78         static Type s_cachedPreparedType4;
79         static Type s_cachedPreparedType5;
80
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()
85         {
86             //
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.
90             //
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.
96             //
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)
103             {
104                 RuntimeHelpers.PrepareDelegate(new WaitDelegate(this.Wait));
105
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;
111             }
112
113             _props |= SynchronizationContextProperties.RequireWaitNotification;
114         }
115
116         public bool IsWaitNotificationRequired()
117         {
118             return ((_props & SynchronizationContextProperties.RequireWaitNotification) != 0);  
119         }
120 #endif
121
122     
123         public virtual void Send(SendOrPostCallback d, Object state)
124         {
125             d(state);
126         }
127
128         public virtual void Post(SendOrPostCallback d, Object state)
129         {
130             ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
131         }
132
133         
134         /// <summary>
135         ///     Optional override for subclasses, for responding to notification that operation is starting.
136         /// </summary>
137         public virtual void OperationStarted()
138         {
139         }
140
141         /// <summary>
142         ///     Optional override for subclasses, for responding to notification that operation has completed.
143         /// </summary>
144         public virtual void OperationCompleted()
145         {
146         }
147
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)]
152         [PrePrepareMethod]
153         public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
154         {
155             if (waitHandles == null)
156             {
157                 throw new ArgumentNullException("waitHandles");
158             }
159             Contract.EndContractBlock();
160             return WaitHelper(waitHandles, waitAll, millisecondsTimeout);
161         }
162                                 
163         // Static helper to which the above method can delegate to in order to get the default 
164         // COM behavior.
165         [System.Security.SecurityCritical]  // auto-generated_required
166         [CLSCompliant(false)]
167         [PrePrepareMethod]
168         [ResourceExposure(ResourceScope.None)]
169         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
170 #if MONO
171         protected static int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
172         {
173             throw new NotImplementedException ();
174         }
175 #else
176         [MethodImplAttribute(MethodImplOptions.InternalCall)]
177         protected static extern int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
178 #endif
179 #endif
180
181         // set SynchronizationContext on the current thread
182         [System.Security.SecurityCritical]  // auto-generated_required
183 #if !FEATURE_CORECLR
184         [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
185 #endif
186         public static void SetSynchronizationContext(SynchronizationContext syncContext)
187         {
188             ExecutionContext ec = Thread.CurrentThread.GetMutableExecutionContext();
189             ec.SynchronizationContext = syncContext;
190             ec.SynchronizationContextNoFlow = syncContext;
191         }
192
193 #if FEATURE_CORECLR
194         //
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.
199         //
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.
202         //
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).
206         //
207
208         [ThreadStatic]
209         private static SynchronizationContext s_threadStaticContext;
210
211 #if FEATURE_LEGACYNETCF
212                 //
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.
217                 //
218         private static SynchronizationContext s_appDomainStaticContext;
219 #endif
220
221         [System.Security.SecurityCritical]
222 #if FEATURE_LEGACYNETCF
223         public static void SetThreadStaticContext(SynchronizationContext syncContext)
224 #else
225         internal static void SetThreadStaticContext(SynchronizationContext syncContext)
226 #endif
227         {
228 #if FEATURE_LEGACYNETCF
229                         //
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.
231                         //
232             if (CompatibilitySwitches.IsAppEarlierThanWindowsPhoneMango)
233                 s_appDomainStaticContext = syncContext;
234             else
235 #endif
236                 s_threadStaticContext = syncContext;
237         }
238 #endif
239
240         // Get the current SynchronizationContext on the current thread
241         public static SynchronizationContext Current 
242         {
243 #if !FEATURE_CORECLR
244             [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
245 #endif
246             get      
247             {
248                 return Thread.CurrentThread.GetExecutionContextReader().SynchronizationContext ?? GetThreadLocalContext();
249             }
250         }
251
252         // Get the last SynchronizationContext that was set explicitly (not flowed via ExecutionContext.Capture/Run)        
253         internal static SynchronizationContext CurrentNoFlow
254         {
255             [FriendAccessAllowed]
256             get
257             {
258                 return Thread.CurrentThread.GetExecutionContextReader().SynchronizationContextNoFlow ?? GetThreadLocalContext();
259             }
260         }
261
262 #if !FEATURE_CORECLR
263         [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
264 #endif
265         private static SynchronizationContext GetThreadLocalContext()
266         {
267             SynchronizationContext context = null;
268             
269 #if FEATURE_CORECLR
270 #if FEATURE_LEGACYNETCF
271             if (CompatibilitySwitches.IsAppEarlierThanWindowsPhoneMango)
272                 context = s_appDomainStaticContext;
273             else
274 #endif //FEATURE_LEGACYNETCF
275                 context = s_threadStaticContext;
276 #endif //FEATURE_CORECLR
277
278 #if FEATURE_APPX
279             if (context == null && Environment.IsWinRTSupported)
280                 context = GetWinRTContext();
281 #endif
282
283 #if MONODROID
284             if (context == null)
285                 context = AndroidPlatform.GetDefaultSyncContext ();
286 #endif
287
288             return context;
289         }
290
291 #if FEATURE_APPX
292         [SecuritySafeCritical]
293         private static SynchronizationContext GetWinRTContext()
294         {
295             Contract.Assert(Environment.IsWinRTSupported);
296
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())
301                 return null;
302
303             //
304             // We call into the VM to get the dispatcher.  This is because:
305             //
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).
309             //
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.
312             //
313             object dispatcher = GetWinRTDispatcherForCurrentThread();
314             if (dispatcher != null)
315                 return GetWinRTSynchronizationContextFactory().Create(dispatcher);
316
317             return null;
318         }
319
320         [SecurityCritical]
321         static WinRTSynchronizationContextFactoryBase s_winRTContextFactory;
322
323         [SecurityCritical]
324         private static WinRTSynchronizationContextFactoryBase GetWinRTSynchronizationContextFactory()
325         {
326             //
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.
330             //
331             WinRTSynchronizationContextFactoryBase factory = s_winRTContextFactory;
332             if (factory == null)
333             {
334                 Type factoryType = Type.GetType("System.Threading.WinRTSynchronizationContextFactory, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
335                 s_winRTContextFactory = factory = (WinRTSynchronizationContextFactoryBase)Activator.CreateInstance(factoryType, true);
336             }
337             return factory;
338         }
339
340         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
341         [SecurityCritical]
342         [ResourceExposure(ResourceScope.None)]
343         [SuppressUnmanagedCodeSecurity]
344         [return: MarshalAs(UnmanagedType.Interface)]
345         private static extern object GetWinRTDispatcherForCurrentThread();
346 #endif //FEATURE_APPX
347
348
349         // helper to Clone this SynchronizationContext, 
350         public virtual SynchronizationContext CreateCopy()
351         {
352             // the CLR dummy has an empty clone function - no member data
353             return new SynchronizationContext();
354         }
355
356 #if FEATURE_SYNCHRONIZATIONCONTEXT_WAIT
357         [System.Security.SecurityCritical]  // auto-generated
358         private static int InvokeWaitMethodHelper(SynchronizationContext syncContext, IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
359         {
360             return syncContext.Wait(waitHandles, waitAll, millisecondsTimeout);
361         }
362 #endif
363     }
364 }