Delete FriendAccessAllowedAttribute and associated dead code (#15101)
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Threading / SynchronizationContext.cs
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 /*============================================================
5 **
6 **
7 **
8 ** Purpose: Capture synchronization semantics for asynchronous callbacks
9 **
10 ** 
11 ===========================================================*/
12
13 namespace System.Threading
14 {
15     using Microsoft.Win32.SafeHandles;
16     using System.Runtime.InteropServices;
17     using System.Runtime.CompilerServices;
18     using System.Runtime.ExceptionServices;
19     using System.Runtime;
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;
26
27
28     [Flags]
29     internal enum SynchronizationContextProperties
30     {
31         None = 0,
32         RequireWaitNotification = 0x1
33     };
34
35 #if FEATURE_COMINTEROP && FEATURE_APPX
36     //
37     // This is implemented in System.Runtime.WindowsRuntime, allowing us to ask that assembly for a WinRT-specific SyncCtx.
38     //
39     // [FriendAccessAllowed]
40     internal abstract class WinRTSynchronizationContextFactoryBase
41     {
42         public abstract SynchronizationContext Create(object coreDispatcher);
43     }
44 #endif //FEATURE_COMINTEROP
45
46     public class SynchronizationContext
47     {
48         private SynchronizationContextProperties _props = SynchronizationContextProperties.None;
49
50         public SynchronizationContext()
51         {
52         }
53
54
55         // helper delegate to statically bind to Wait method
56         private delegate int WaitDelegate(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
57
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;
63
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()
67         {
68             //
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.
72             //
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.
78             //
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)
85             {
86                 RuntimeHelpers.PrepareDelegate(new WaitDelegate(this.Wait));
87
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;
93             }
94
95             _props |= SynchronizationContextProperties.RequireWaitNotification;
96         }
97
98         public bool IsWaitNotificationRequired()
99         {
100             return ((_props & SynchronizationContextProperties.RequireWaitNotification) != 0);
101         }
102
103
104         public virtual void Send(SendOrPostCallback d, Object state)
105         {
106             d(state);
107         }
108
109         public virtual void Post(SendOrPostCallback d, Object state)
110         {
111             ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
112         }
113
114
115         /// <summary>
116         ///     Optional override for subclasses, for responding to notification that operation is starting.
117         /// </summary>
118         public virtual void OperationStarted()
119         {
120         }
121
122         /// <summary>
123         ///     Optional override for subclasses, for responding to notification that operation has completed.
124         /// </summary>
125         public virtual void OperationCompleted()
126         {
127         }
128
129         // Method called when the CLR does a wait operation
130         [CLSCompliant(false)]
131         public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
132         {
133             return WaitHelper(waitHandles, waitAll, millisecondsTimeout);
134         }
135
136         // Method that can be called by Wait overrides
137         [CLSCompliant(false)]
138         protected static int WaitHelper(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
139         {
140             if (waitHandles == null)
141             {
142                 throw new ArgumentNullException(nameof(waitHandles));
143             }
144
145             return WaitHelperNative(waitHandles, waitAll, millisecondsTimeout);
146         }
147
148         // Static helper to which the above method can delegate to in order to get the default
149         // COM behavior.
150         [CLSCompliant(false)]
151         [MethodImplAttribute(MethodImplOptions.InternalCall)]
152         private static extern int WaitHelperNative(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
153
154         public static void SetSynchronizationContext(SynchronizationContext syncContext)
155         {
156             Thread.CurrentThread.SynchronizationContext = syncContext;
157         }
158
159         public static SynchronizationContext Current
160         {
161             get
162             {
163                 SynchronizationContext context = Thread.CurrentThread.SynchronizationContext;
164
165 #if FEATURE_APPX
166                 if (context == null && AppDomain.IsAppXModel())
167                     context = GetWinRTContext();
168 #endif
169
170                 return context;
171             }
172         }
173
174 #if FEATURE_APPX
175         private static SynchronizationContext GetWinRTContext()
176         {
177             Debug.Assert(Environment.IsWinRTSupported);
178             Debug.Assert(AppDomain.IsAppXModel());
179
180             //
181             // We call into the VM to get the dispatcher.  This is because:
182             //
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).
186             //
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.
189             //
190             object dispatcher = GetWinRTDispatcherForCurrentThread();
191             if (dispatcher != null)
192                 return GetWinRTSynchronizationContextFactory().Create(dispatcher);
193
194             return null;
195         }
196
197         private static WinRTSynchronizationContextFactoryBase s_winRTContextFactory;
198
199         private static WinRTSynchronizationContextFactoryBase GetWinRTSynchronizationContextFactory()
200         {
201             //
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.
205             //
206             WinRTSynchronizationContextFactoryBase factory = s_winRTContextFactory;
207             if (factory == null)
208             {
209                 Type factoryType = Type.GetType("System.Threading.WinRTSynchronizationContextFactory, " + AssemblyRef.SystemRuntimeWindowsRuntime, true);
210                 s_winRTContextFactory = factory = (WinRTSynchronizationContextFactoryBase)Activator.CreateInstance(factoryType, true);
211             }
212             return factory;
213         }
214
215         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
216         [return: MarshalAs(UnmanagedType.Interface)]
217         private static extern object GetWinRTDispatcherForCurrentThread();
218 #endif //FEATURE_APPX
219
220
221         // helper to Clone this SynchronizationContext, 
222         public virtual SynchronizationContext CreateCopy()
223         {
224             // the CLR dummy has an empty clone function - no member data
225             return new SynchronizationContext();
226         }
227
228         private static int InvokeWaitMethodHelper(SynchronizationContext syncContext, IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
229         {
230             return syncContext.Wait(waitHandles, waitAll, millisecondsTimeout);
231         }
232     }
233 }