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