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.
6 /*=============================================================================
10 ** Purpose: Class for creating and managing a thread.
13 =============================================================================*/
15 using Internal.Runtime.Augments;
17 namespace System.Threading
19 using System.Threading;
21 using System.Runtime.InteropServices;
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;
32 internal delegate Object InternalCrossContextDelegate(Object[] args);
34 internal class ThreadHelper
36 private Delegate _start;
37 private Object _startArg = null;
38 private ExecutionContext _executionContext = null;
39 internal ThreadHelper(Delegate start)
44 internal void SetExecutionContextHelper(ExecutionContext ec)
46 _executionContext = ec;
49 static internal ContextCallback _ccb = new ContextCallback(ThreadStart_Context);
51 static private void ThreadStart_Context(Object state)
53 ThreadHelper t = (ThreadHelper)state;
54 if (t._start is ThreadStart)
56 ((ThreadStart)t._start)();
60 ((ParameterizedThreadStart)t._start)(t._startArg);
65 internal void ThreadStart(object obj)
68 if (_executionContext != null)
70 ExecutionContext.Run(_executionContext, _ccb, (Object)this);
74 ((ParameterizedThreadStart)_start)(obj);
79 internal void ThreadStart()
81 if (_executionContext != null)
83 ExecutionContext.Run(_executionContext, _ccb, (Object)this);
87 ((ThreadStart)_start)();
92 internal struct ThreadHandle
96 internal ThreadHandle(IntPtr pThread)
102 internal sealed class Thread : RuntimeThread
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
112 private String m_Name;
113 private Delegate m_Delegate; // Delegate
115 private Object m_ThreadStartArg;
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.
128 private IntPtr DONT_USE_InternalThread; // Pointer
129 private int m_Priority; // INT32
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
135 private int _managedThreadId; // INT32
137 #pragma warning restore 414
138 #pragma warning restore 169
140 private bool m_ExecutionContextBelongsToOuterScope;
142 private bool m_ForbidExecutionContextMutation;
145 // Do not move! Order of above fields needs to be preserved for alignment
147 // See code:#threadCultureInfo
149 internal static CultureInfo m_CurrentCulture;
151 internal static CultureInfo m_CurrentUICulture;
153 // Adding an empty default ctor for annotation purposes
154 internal Thread() { }
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.
160 ** Exceptions: ArgumentNullException if start == null.
161 =========================================================================*/
162 public Thread(ThreadStart start)
166 throw new ArgumentNullException(nameof(start));
168 SetStartHelper((Delegate)start, 0); //0 will setup Thread with default stackSize
171 internal Thread(ThreadStart start, int maxStackSize)
175 throw new ArgumentNullException(nameof(start));
177 if (0 > maxStackSize)
178 throw new ArgumentOutOfRangeException(nameof(maxStackSize), SR.ArgumentOutOfRange_NeedNonNegNum);
179 SetStartHelper((Delegate)start, maxStackSize);
181 public Thread(ParameterizedThreadStart start)
185 throw new ArgumentNullException(nameof(start));
187 SetStartHelper((Delegate)start, 0);
190 internal Thread(ParameterizedThreadStart start, int maxStackSize)
194 throw new ArgumentNullException(nameof(start));
196 if (0 > maxStackSize)
197 throw new ArgumentOutOfRangeException(nameof(maxStackSize), SR.ArgumentOutOfRange_NeedNonNegNum);
198 SetStartHelper((Delegate)start, maxStackSize);
201 public override int GetHashCode()
203 return _managedThreadId;
206 extern public new int ManagedThreadId
208 [MethodImplAttribute(MethodImplOptions.InternalCall)]
212 // Returns handle for interop with EE. The handle is guaranteed to be non-null.
213 internal unsafe ThreadHandle GetNativeHandle()
215 IntPtr thread = DONT_USE_InternalThread;
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
220 if (thread == IntPtr.Zero)
221 throw new ArgumentException(null, SR.Argument_InvalidHandle);
223 return new ThreadHandle(thread);
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.
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()
237 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
238 Start(ref stackMark);
241 [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
242 public new void Start(object parameter)
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)
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);
253 m_ThreadStartArg = parameter;
254 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
255 Start(ref stackMark);
258 private void Start(ref StackCrawlMark stackMark)
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
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)
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);
277 StartInternal(ref stackMark);
280 internal ExecutionContext ExecutionContext
282 get { return m_ExecutionContext; }
283 set { m_ExecutionContext = value; }
286 internal SynchronizationContext SynchronizationContext
288 get { return m_SynchronizationContext; }
289 set { m_SynchronizationContext = value; }
292 [MethodImplAttribute(MethodImplOptions.InternalCall)]
293 private extern void StartInternal(ref StackCrawlMark stackMark);
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();
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.
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);
313 public static new void Sleep(int millisecondsTimeout)
315 SleepInternal(millisecondsTimeout);
318 public static void Sleep(TimeSpan timeout)
320 long tm = (long)timeout.TotalMilliseconds;
321 if (tm < -1 || tm > (long)Int32.MaxValue)
322 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
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. */
331 [MethodImplAttribute(MethodImplOptions.InternalCall)]
332 private static extern void SpinWaitInternal(int iterations);
334 public static new void SpinWait(int iterations)
336 SpinWaitInternal(iterations);
339 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
340 private static extern bool YieldInternal();
342 internal static new bool Yield()
344 return YieldInternal();
347 public static new Thread CurrentThread
351 return GetCurrentThreadNative();
354 [MethodImplAttribute(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
355 private static extern Thread GetCurrentThreadNative();
357 private void SetStartHelper(Delegate start, int maxStackSize)
359 Debug.Assert(maxStackSize >= 0);
361 ThreadHelper threadStartCallBack = new ThreadHelper(start);
362 if (start is ThreadStart)
364 SetStart(new ThreadStart(threadStartCallBack.ThreadStart), maxStackSize);
368 SetStart(new ParameterizedThreadStart(threadStartCallBack.ThreadStart), maxStackSize);
372 /*=========================================================================
373 ** PRIVATE Sets the IThreadable interface for the thread. Assumes that
375 =========================================================================*/
376 [MethodImplAttribute(MethodImplOptions.InternalCall)]
377 private extern void SetStart(Delegate start, int maxStackSize);
379 /*=========================================================================
380 ** Clean up the thread when it goes away.
381 =========================================================================*/
384 // Delegate to the unmanaged portion.
388 [MethodImplAttribute(MethodImplOptions.InternalCall)]
389 private extern void InternalFinalize();
391 #if FEATURE_COMINTEROP_APARTMENT_SUPPORT
393 [MethodImplAttribute(MethodImplOptions.InternalCall)]
394 private extern void StartupSetApartmentStateInternal();
395 #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
397 // #threadCultureInfo
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.
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
410 // Implementation notes:
411 // In Silverlight, culture members thread static (per Thread, per AppDomain).
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.
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.
431 public CultureInfo CurrentUICulture
435 return CultureInfo.CurrentUICulture;
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.
443 if (m_CurrentUICulture == null && m_CurrentCulture == null)
444 nativeInitCultureAccessors();
446 CultureInfo.CurrentUICulture = value;
450 // This returns the exposed context for a given context ID.
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.
464 public CultureInfo CurrentCulture
468 return CultureInfo.CurrentCulture;
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.
476 if (m_CurrentCulture == null && m_CurrentUICulture == null)
477 nativeInitCultureAccessors();
479 CultureInfo.CurrentCulture = value;
483 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
484 private static extern void nativeInitCultureAccessors();
486 /*======================================================================
487 ** Returns the current domain in which current thread is running.
488 ======================================================================*/
490 [MethodImplAttribute(MethodImplOptions.InternalCall)]
491 private static extern AppDomain GetDomainInternal();
492 [MethodImplAttribute(MethodImplOptions.InternalCall)]
493 private static extern AppDomain GetFastDomainInternal();
495 internal static AppDomain GetDomain()
499 ad = GetFastDomainInternal();
501 ad = GetDomainInternal();
508 * This returns a unique id to identify an appdomain.
510 internal static int GetDomainID()
512 return GetDomain().GetId();
516 // Retrieves the name of the thread.
518 public new String Name
529 throw new InvalidOperationException(SR.InvalidOperation_WriteOnce);
532 InformThreadNameChange(GetNativeHandle(), value, (value != null) ? value.Length : 0);
537 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
538 private static extern void InformThreadNameChange(ThreadHandle t, String name, int len);
540 } // End of class Thread
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
549 LookForMyCallersCaller = 2,