Reduce Execution Context Save+Restore (#15629)
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Threading / Thread.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 **
9 **
10 ** Purpose: Class for creating and managing a thread.
11 **
12 **
13 =============================================================================*/
14
15 using Internal.Runtime.Augments;
16
17 namespace System.Threading
18 {
19     using System.Threading;
20     using System.Runtime;
21     using System.Runtime.InteropServices;
22     using System;
23     using System.Globalization;
24     using System.Collections.Generic;
25     using System.Runtime.Serialization;
26     using System.Runtime.CompilerServices;
27     using System.Runtime.ConstrainedExecution;
28     using System.Security;
29     using System.Runtime.Versioning;
30     using System.Diagnostics;
31
32     internal delegate Object InternalCrossContextDelegate(Object[] args);
33
34     internal class ThreadHelper
35     {
36         private Delegate _start;
37         private Object _startArg = null;
38         private ExecutionContext _executionContext = null;
39         internal ThreadHelper(Delegate start)
40         {
41             _start = start;
42         }
43
44         internal void SetExecutionContextHelper(ExecutionContext ec)
45         {
46             _executionContext = ec;
47         }
48
49         static internal ContextCallback _ccb = new ContextCallback(ThreadStart_Context);
50
51         static private void ThreadStart_Context(Object state)
52         {
53             ThreadHelper t = (ThreadHelper)state;
54             if (t._start is ThreadStart)
55             {
56                 ((ThreadStart)t._start)();
57             }
58             else
59             {
60                 ((ParameterizedThreadStart)t._start)(t._startArg);
61             }
62         }
63
64         // call back helper
65         internal void ThreadStart(object obj)
66         {
67             _startArg = obj;
68             ExecutionContext context = _executionContext;
69             if (context != null)
70             {
71                 ExecutionContext.RunInternal(context, _ccb, (Object)this);
72             }
73             else
74             {
75                 ((ParameterizedThreadStart)_start)(obj);
76             }
77         }
78
79         // call back helper
80         internal void ThreadStart()
81         {
82             ExecutionContext context = _executionContext;
83             if (context != null)
84             {
85                 ExecutionContext.RunInternal(context, _ccb, (Object)this);
86             }
87             else
88             {
89                 ((ThreadStart)_start)();
90             }
91         }
92     }
93
94     internal struct ThreadHandle
95     {
96         private IntPtr m_ptr;
97
98         internal ThreadHandle(IntPtr pThread)
99         {
100             m_ptr = pThread;
101         }
102     }
103
104     internal sealed class Thread : RuntimeThread
105     {
106         /*=========================================================================
107         ** Data accessed from managed code that needs to be defined in
108         ** ThreadBaseObject to maintain alignment between the two classes.
109         ** DON'T CHANGE THESE UNLESS YOU MODIFY ThreadBaseObject in vm\object.h
110         =========================================================================*/
111         private ExecutionContext m_ExecutionContext;    // this call context follows the logical thread
112         private SynchronizationContext m_SynchronizationContext;    // On CoreCLR, this is maintained separately from ExecutionContext
113
114         private String m_Name;
115         private Delegate m_Delegate;             // Delegate
116
117         private Object m_ThreadStartArg;
118
119         /*=========================================================================
120         ** The base implementation of Thread is all native.  The following fields
121         ** should never be used in the C# code.  They are here to define the proper
122         ** space so the thread object may be allocated.  DON'T CHANGE THESE UNLESS
123         ** YOU MODIFY ThreadBaseObject in vm\object.h
124         =========================================================================*/
125 #pragma warning disable 169
126 #pragma warning disable 414  // These fields are not used from managed.
127         // IntPtrs need to be together, and before ints, because IntPtrs are 64-bit
128         //  fields on 64-bit platforms, where they will be sorted together.
129
130         private IntPtr DONT_USE_InternalThread;        // Pointer
131         private int m_Priority;                     // INT32
132
133         // The following field is required for interop with the VS Debugger
134         // Prior to making any changes to this field, please reach out to the VS Debugger 
135         // team to make sure that your changes are not going to prevent the debugger
136         // from working.
137         private int _managedThreadId;              // INT32
138
139 #pragma warning restore 414
140 #pragma warning restore 169
141
142         private bool m_ExecutionContextBelongsToOuterScope;
143 #if DEBUG
144         private bool m_ForbidExecutionContextMutation;
145 #endif
146
147         // Do not move! Order of above fields needs to be preserved for alignment
148         // with native code
149         // See code:#threadCultureInfo
150         [ThreadStatic]
151         internal static CultureInfo m_CurrentCulture;
152         [ThreadStatic]
153         internal static CultureInfo m_CurrentUICulture;
154
155         // Adding an empty default ctor for annotation purposes
156         internal Thread() { }
157
158         /*=========================================================================
159         ** Creates a new Thread object which will begin execution at
160         ** start.ThreadStart on a new thread when the Start method is called.
161         **
162         ** Exceptions: ArgumentNullException if start == null.
163         =========================================================================*/
164         public Thread(ThreadStart start)
165         {
166             if (start == null)
167             {
168                 throw new ArgumentNullException(nameof(start));
169             }
170             SetStartHelper((Delegate)start, 0);  //0 will setup Thread with default stackSize
171         }
172
173         internal Thread(ThreadStart start, int maxStackSize)
174         {
175             if (start == null)
176             {
177                 throw new ArgumentNullException(nameof(start));
178             }
179             if (0 > maxStackSize)
180                 throw new ArgumentOutOfRangeException(nameof(maxStackSize), SR.ArgumentOutOfRange_NeedNonNegNum);
181             SetStartHelper((Delegate)start, maxStackSize);
182         }
183         public Thread(ParameterizedThreadStart start)
184         {
185             if (start == null)
186             {
187                 throw new ArgumentNullException(nameof(start));
188             }
189             SetStartHelper((Delegate)start, 0);
190         }
191
192         internal Thread(ParameterizedThreadStart start, int maxStackSize)
193         {
194             if (start == null)
195             {
196                 throw new ArgumentNullException(nameof(start));
197             }
198             if (0 > maxStackSize)
199                 throw new ArgumentOutOfRangeException(nameof(maxStackSize), SR.ArgumentOutOfRange_NeedNonNegNum);
200             SetStartHelper((Delegate)start, maxStackSize);
201         }
202
203         public override int GetHashCode()
204         {
205             return _managedThreadId;
206         }
207
208         extern public new int ManagedThreadId
209         {
210             [MethodImplAttribute(MethodImplOptions.InternalCall)]
211             get;
212         }
213
214         // Returns handle for interop with EE. The handle is guaranteed to be non-null.
215         internal unsafe ThreadHandle GetNativeHandle()
216         {
217             IntPtr thread = DONT_USE_InternalThread;
218
219             // This should never happen under normal circumstances. m_assembly is always assigned before it is handed out to the user.
220             // There are ways how to create an unitialized objects through remoting, etc. Avoid AVing in the EE by throwing a nice
221             // exception here.
222             if (thread == IntPtr.Zero)
223                 throw new ArgumentException(null, SR.Argument_InvalidHandle);
224
225             return new ThreadHandle(thread);
226         }
227
228
229         /*=========================================================================
230         ** Spawns off a new thread which will begin executing at the ThreadStart
231         ** method on the IThreadable interface passed in the constructor. Once the
232         ** thread is dead, it cannot be restarted with another call to Start.
233         **
234         ** Exceptions: ThreadStateException if the thread has already been started.
235         =========================================================================*/
236         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
237         public new void Start()
238         {
239             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
240             Start(ref stackMark);
241         }
242
243         [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
244         public new void Start(object parameter)
245         {
246             //In the case of a null delegate (second call to start on same thread)
247             //    StartInternal method will take care of the error reporting
248             if (m_Delegate is ThreadStart)
249             {
250                 //We expect the thread to be setup with a ParameterizedThreadStart
251                 //    if this constructor is called.
252                 //If we got here then that wasn't the case
253                 throw new InvalidOperationException(SR.InvalidOperation_ThreadWrongThreadStart);
254             }
255             m_ThreadStartArg = parameter;
256             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
257             Start(ref stackMark);
258         }
259
260         private void Start(ref StackCrawlMark stackMark)
261         {
262 #if FEATURE_COMINTEROP_APARTMENT_SUPPORT
263             // Eagerly initialize the COM Apartment state of the thread if we're allowed to.
264             StartupSetApartmentStateInternal();
265 #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
266
267             // Attach current thread's security principal object to the new
268             // thread. Be careful not to bind the current thread to a principal
269             // if it's not already bound.
270             if (m_Delegate != null)
271             {
272                 // If we reach here with a null delegate, something is broken. But we'll let the StartInternal method take care of
273                 // reporting an error. Just make sure we dont try to dereference a null delegate.
274                 ThreadHelper t = (ThreadHelper)(m_Delegate.Target);
275                 ExecutionContext ec = ExecutionContext.Capture();
276                 t.SetExecutionContextHelper(ec);
277             }
278
279             StartInternal(ref stackMark);
280         }
281
282         internal ExecutionContext ExecutionContext
283         {
284             get { return m_ExecutionContext; }
285             set { m_ExecutionContext = value; }
286         }
287
288         internal SynchronizationContext SynchronizationContext
289         {
290             get { return m_SynchronizationContext; }
291             set { m_SynchronizationContext = value; }
292         }
293
294         [MethodImplAttribute(MethodImplOptions.InternalCall)]
295         private extern void StartInternal(ref StackCrawlMark stackMark);
296
297
298         // Helper method to get a logical thread ID for StringBuilder (for
299         // correctness) and for FileStream's async code path (for perf, to
300         // avoid creating a Thread instance).
301         [MethodImplAttribute(MethodImplOptions.InternalCall)]
302         internal extern static IntPtr InternalGetCurrentThread();
303
304         /*=========================================================================
305         ** Suspends the current thread for timeout milliseconds. If timeout == 0,
306         ** forces the thread to give up the remainer of its timeslice.  If timeout
307         ** == Timeout.Infinite, no timeout will occur.
308         **
309         ** Exceptions: ArgumentException if timeout < 0.
310         **             ThreadInterruptedException if the thread is interrupted while sleeping.
311         =========================================================================*/
312         [MethodImplAttribute(MethodImplOptions.InternalCall)]
313         private static extern void SleepInternal(int millisecondsTimeout);
314
315         public static new void Sleep(int millisecondsTimeout)
316         {
317             SleepInternal(millisecondsTimeout);
318         }
319
320         public static void Sleep(TimeSpan timeout)
321         {
322             long tm = (long)timeout.TotalMilliseconds;
323             if (tm < -1 || tm > (long)Int32.MaxValue)
324                 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
325             Sleep((int)tm);
326         }
327
328
329         /* wait for a length of time proportial to 'iterations'.  Each iteration is should
330            only take a few machine instructions.  Calling this API is preferable to coding
331            a explict busy loop because the hardware can be informed that it is busy waiting. */
332
333         [MethodImplAttribute(MethodImplOptions.InternalCall)]
334         private static extern void SpinWaitInternal(int iterations);
335
336         public static new void SpinWait(int iterations)
337         {
338             SpinWaitInternal(iterations);
339         }
340
341         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
342         private static extern bool YieldInternal();
343
344         internal static new bool Yield()
345         {
346             return YieldInternal();
347         }
348
349         public static new Thread CurrentThread
350         {
351             get
352             {
353                 return GetCurrentThreadNative();
354             }
355         }
356         [MethodImplAttribute(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
357         private static extern Thread GetCurrentThreadNative();
358
359         private void SetStartHelper(Delegate start, int maxStackSize)
360         {
361             Debug.Assert(maxStackSize >= 0);
362
363             ThreadHelper threadStartCallBack = new ThreadHelper(start);
364             if (start is ThreadStart)
365             {
366                 SetStart(new ThreadStart(threadStartCallBack.ThreadStart), maxStackSize);
367             }
368             else
369             {
370                 SetStart(new ParameterizedThreadStart(threadStartCallBack.ThreadStart), maxStackSize);
371             }
372         }
373
374         /*=========================================================================
375         ** PRIVATE Sets the IThreadable interface for the thread. Assumes that
376         ** start != null.
377         =========================================================================*/
378         [MethodImplAttribute(MethodImplOptions.InternalCall)]
379         private extern void SetStart(Delegate start, int maxStackSize);
380
381         /*=========================================================================
382         ** Clean up the thread when it goes away.
383         =========================================================================*/
384         ~Thread()
385         {
386             // Delegate to the unmanaged portion.
387             InternalFinalize();
388         }
389
390         [MethodImplAttribute(MethodImplOptions.InternalCall)]
391         private extern void InternalFinalize();
392
393 #if FEATURE_COMINTEROP_APARTMENT_SUPPORT
394
395         [MethodImplAttribute(MethodImplOptions.InternalCall)]
396         private extern void StartupSetApartmentStateInternal();
397 #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
398
399         // #threadCultureInfo
400         //
401         // Background:
402         // In the desktop runtime, we allow a thread's cultures to travel with the thread
403         // across AppDomain boundaries. Furthermore we update the native thread with the
404         // culture of the managed thread. Because of security concerns and potential SxS
405         // effects, in Silverlight we are making the changes listed below. 
406         // 
407         // Silverlight Changes:
408         // - thread instance member cultures (CurrentCulture and CurrentUICulture) 
409         //   confined within AppDomains
410         // - changes to these properties don't affect the underlying native thread
411         // 
412         // Implementation notes:
413         // In Silverlight, culture members thread static (per Thread, per AppDomain). 
414         //
415         // Quirks:
416         // An interesting side-effect of isolating cultures within an AppDomain is that we
417         // now need to special case resource lookup for mscorlib, which transitions to the 
418         // default domain to lookup resources. See Environment.cs for more details.
419         // 
420
421         // As the culture can be customized object then we cannot hold any 
422         // reference to it before we check if it is safe because the app domain 
423         // owning this customized culture may get unloaded while executing this 
424         // code. To achieve that we have to do the check using nativeGetSafeCulture 
425         // as the thread cannot get interrupted during the FCALL. 
426         // If the culture is safe (not customized or created in current app domain) 
427         // then the FCALL will return a reference to that culture otherwise the 
428         // FCALL will return failure. In case of failure we'll return the default culture.
429         // If the app domain owning a customized culture that is set to the thread and this
430         // app domain get unloaded there is a code to clean up the culture from the thread
431         // using the code in AppDomain::ReleaseDomainStores.
432
433         public CultureInfo CurrentUICulture
434         {
435             get
436             {
437                 return CultureInfo.CurrentUICulture;
438             }
439
440             set
441             {
442                 // If you add more pre-conditions to this method, check to see if you also need to 
443                 // add them to CultureInfo.DefaultThreadCurrentUICulture.set.
444
445                 if (m_CurrentUICulture == null && m_CurrentCulture == null)
446                     nativeInitCultureAccessors();
447
448                 CultureInfo.CurrentUICulture = value;
449             }
450         }
451
452         // This returns the exposed context for a given context ID.
453
454         // As the culture can be customized object then we cannot hold any 
455         // reference to it before we check if it is safe because the app domain 
456         // owning this customized culture may get unloaded while executing this 
457         // code. To achieve that we have to do the check using nativeGetSafeCulture 
458         // as the thread cannot get interrupted during the FCALL. 
459         // If the culture is safe (not customized or created in current app domain) 
460         // then the FCALL will return a reference to that culture otherwise the 
461         // FCALL will return failure. In case of failure we'll return the default culture.
462         // If the app domain owning a customized culture that is set to the thread and this
463         // app domain get unloaded there is a code to clean up the culture from the thread
464         // using the code in AppDomain::ReleaseDomainStores.
465
466         public CultureInfo CurrentCulture
467         {
468             get
469             {
470                 return CultureInfo.CurrentCulture;
471             }
472
473             set
474             {
475                 // If you add more pre-conditions to this method, check to see if you also need to 
476                 // add them to CultureInfo.DefaultThreadCurrentCulture.set.
477
478                 if (m_CurrentCulture == null && m_CurrentUICulture == null)
479                     nativeInitCultureAccessors();
480                 
481                 CultureInfo.CurrentCulture = value;
482             }
483         }
484
485         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
486         private static extern void nativeInitCultureAccessors();
487
488         /*======================================================================
489         ** Returns the current domain in which current thread is running.
490         ======================================================================*/
491
492         [MethodImplAttribute(MethodImplOptions.InternalCall)]
493         private static extern AppDomain GetDomainInternal();
494         [MethodImplAttribute(MethodImplOptions.InternalCall)]
495         private static extern AppDomain GetFastDomainInternal();
496
497         internal static AppDomain GetDomain()
498         {
499
500             AppDomain ad;
501             ad = GetFastDomainInternal();
502             if (ad == null)
503                 ad = GetDomainInternal();
504
505             return ad;
506         }
507
508
509         /*
510          *  This returns a unique id to identify an appdomain.
511          */
512         internal static int GetDomainID()
513         {
514             return GetDomain().GetId();
515         }
516
517
518         // Retrieves the name of the thread.
519         //
520         public new String Name
521         {
522             get
523             {
524                 return m_Name;
525             }
526             set
527             {
528                 lock (this)
529                 {
530                     if (m_Name != null)
531                         throw new InvalidOperationException(SR.InvalidOperation_WriteOnce);
532                     m_Name = value;
533
534                     InformThreadNameChange(GetNativeHandle(), value, (value != null) ? value.Length : 0);
535                 }
536             }
537         }
538
539         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
540         private static extern void InformThreadNameChange(ThreadHandle t, String name, int len);
541
542     } // End of class Thread
543
544     // declaring a local var of this enum type and passing it by ref into a function that needs to do a
545     // stack crawl will both prevent inlining of the calle and pass an ESP point to stack crawl to
546     // Declaring these in EH clauses is illegal; they must declared in the main method body
547     internal enum StackCrawlMark
548     {
549         LookForMe = 0,
550         LookForMyCaller = 1,
551         LookForMyCallersCaller = 2,
552         LookForThread = 3
553     }
554 }