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 ExecutionContext context = _executionContext;
71 ExecutionContext.RunInternal(context, _ccb, (Object)this);
75 ((ParameterizedThreadStart)_start)(obj);
80 internal void ThreadStart()
82 ExecutionContext context = _executionContext;
85 ExecutionContext.RunInternal(context, _ccb, (Object)this);
89 ((ThreadStart)_start)();
94 internal struct ThreadHandle
98 internal ThreadHandle(IntPtr pThread)
104 internal sealed class Thread : RuntimeThread
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
114 private String m_Name;
115 private Delegate m_Delegate; // Delegate
117 private Object m_ThreadStartArg;
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.
130 private IntPtr DONT_USE_InternalThread; // Pointer
131 private int m_Priority; // INT32
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
137 private int _managedThreadId; // INT32
139 #pragma warning restore 414
140 #pragma warning restore 169
142 private bool m_ExecutionContextBelongsToOuterScope;
144 private bool m_ForbidExecutionContextMutation;
147 // Do not move! Order of above fields needs to be preserved for alignment
149 // See code:#threadCultureInfo
151 internal static CultureInfo m_CurrentCulture;
153 internal static CultureInfo m_CurrentUICulture;
155 // Adding an empty default ctor for annotation purposes
156 internal Thread() { }
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.
162 ** Exceptions: ArgumentNullException if start == null.
163 =========================================================================*/
164 public Thread(ThreadStart start)
168 throw new ArgumentNullException(nameof(start));
170 SetStartHelper((Delegate)start, 0); //0 will setup Thread with default stackSize
173 internal Thread(ThreadStart start, int maxStackSize)
177 throw new ArgumentNullException(nameof(start));
179 if (0 > maxStackSize)
180 throw new ArgumentOutOfRangeException(nameof(maxStackSize), SR.ArgumentOutOfRange_NeedNonNegNum);
181 SetStartHelper((Delegate)start, maxStackSize);
183 public Thread(ParameterizedThreadStart start)
187 throw new ArgumentNullException(nameof(start));
189 SetStartHelper((Delegate)start, 0);
192 internal Thread(ParameterizedThreadStart start, int maxStackSize)
196 throw new ArgumentNullException(nameof(start));
198 if (0 > maxStackSize)
199 throw new ArgumentOutOfRangeException(nameof(maxStackSize), SR.ArgumentOutOfRange_NeedNonNegNum);
200 SetStartHelper((Delegate)start, maxStackSize);
203 public override int GetHashCode()
205 return _managedThreadId;
208 extern public new int ManagedThreadId
210 [MethodImplAttribute(MethodImplOptions.InternalCall)]
214 // Returns handle for interop with EE. The handle is guaranteed to be non-null.
215 internal unsafe ThreadHandle GetNativeHandle()
217 IntPtr thread = DONT_USE_InternalThread;
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
222 if (thread == IntPtr.Zero)
223 throw new ArgumentException(null, SR.Argument_InvalidHandle);
225 return new ThreadHandle(thread);
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.
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()
239 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
240 Start(ref stackMark);
243 [System.Security.DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
244 public new void Start(object parameter)
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)
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);
255 m_ThreadStartArg = parameter;
256 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
257 Start(ref stackMark);
260 private void Start(ref StackCrawlMark stackMark)
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
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)
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);
279 StartInternal(ref stackMark);
282 internal ExecutionContext ExecutionContext
284 get { return m_ExecutionContext; }
285 set { m_ExecutionContext = value; }
288 internal SynchronizationContext SynchronizationContext
290 get { return m_SynchronizationContext; }
291 set { m_SynchronizationContext = value; }
294 [MethodImplAttribute(MethodImplOptions.InternalCall)]
295 private extern void StartInternal(ref StackCrawlMark stackMark);
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();
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.
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);
315 public static new void Sleep(int millisecondsTimeout)
317 SleepInternal(millisecondsTimeout);
320 public static void Sleep(TimeSpan timeout)
322 long tm = (long)timeout.TotalMilliseconds;
323 if (tm < -1 || tm > (long)Int32.MaxValue)
324 throw new ArgumentOutOfRangeException(nameof(timeout), SR.ArgumentOutOfRange_NeedNonNegOrNegative1);
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. */
333 [MethodImplAttribute(MethodImplOptions.InternalCall)]
334 private static extern void SpinWaitInternal(int iterations);
336 public static new void SpinWait(int iterations)
338 SpinWaitInternal(iterations);
341 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
342 private static extern bool YieldInternal();
344 internal static new bool Yield()
346 return YieldInternal();
349 public static new Thread CurrentThread
353 return GetCurrentThreadNative();
356 [MethodImplAttribute(MethodImplOptions.InternalCall), ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
357 private static extern Thread GetCurrentThreadNative();
359 private void SetStartHelper(Delegate start, int maxStackSize)
361 Debug.Assert(maxStackSize >= 0);
363 ThreadHelper threadStartCallBack = new ThreadHelper(start);
364 if (start is ThreadStart)
366 SetStart(new ThreadStart(threadStartCallBack.ThreadStart), maxStackSize);
370 SetStart(new ParameterizedThreadStart(threadStartCallBack.ThreadStart), maxStackSize);
374 /*=========================================================================
375 ** PRIVATE Sets the IThreadable interface for the thread. Assumes that
377 =========================================================================*/
378 [MethodImplAttribute(MethodImplOptions.InternalCall)]
379 private extern void SetStart(Delegate start, int maxStackSize);
381 /*=========================================================================
382 ** Clean up the thread when it goes away.
383 =========================================================================*/
386 // Delegate to the unmanaged portion.
390 [MethodImplAttribute(MethodImplOptions.InternalCall)]
391 private extern void InternalFinalize();
393 #if FEATURE_COMINTEROP_APARTMENT_SUPPORT
395 [MethodImplAttribute(MethodImplOptions.InternalCall)]
396 private extern void StartupSetApartmentStateInternal();
397 #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
399 // #threadCultureInfo
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.
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
412 // Implementation notes:
413 // In Silverlight, culture members thread static (per Thread, per AppDomain).
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.
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.
433 public CultureInfo CurrentUICulture
437 return CultureInfo.CurrentUICulture;
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.
445 if (m_CurrentUICulture == null && m_CurrentCulture == null)
446 nativeInitCultureAccessors();
448 CultureInfo.CurrentUICulture = value;
452 // This returns the exposed context for a given context ID.
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.
466 public CultureInfo CurrentCulture
470 return CultureInfo.CurrentCulture;
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.
478 if (m_CurrentCulture == null && m_CurrentUICulture == null)
479 nativeInitCultureAccessors();
481 CultureInfo.CurrentCulture = value;
485 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
486 private static extern void nativeInitCultureAccessors();
488 /*======================================================================
489 ** Returns the current domain in which current thread is running.
490 ======================================================================*/
492 [MethodImplAttribute(MethodImplOptions.InternalCall)]
493 private static extern AppDomain GetDomainInternal();
494 [MethodImplAttribute(MethodImplOptions.InternalCall)]
495 private static extern AppDomain GetFastDomainInternal();
497 internal static AppDomain GetDomain()
501 ad = GetFastDomainInternal();
503 ad = GetDomainInternal();
510 * This returns a unique id to identify an appdomain.
512 internal static int GetDomainID()
514 return GetDomain().GetId();
518 // Retrieves the name of the thread.
520 public new String Name
531 throw new InvalidOperationException(SR.InvalidOperation_WriteOnce);
534 InformThreadNameChange(GetNativeHandle(), value, (value != null) ? value.Length : 0);
539 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
540 private static extern void InformThreadNameChange(ThreadHandle t, String name, int len);
542 } // End of class Thread
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
551 LookForMyCallersCaller = 2,