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 using Microsoft.Win32.SafeHandles; 6 using System; 7 using System.Diagnostics; 8 using System.Runtime; 9 using System.Runtime.InteropServices; 10 using System.Threading; 11 12 namespace Internal.Runtime.Augments 13 { 14 public sealed partial class RuntimeThread 15 { 16 // Event signaling that the thread has stopped 17 private ManualResetEvent _stopped; 18 19 private readonly WaitSubsystem.ThreadWaitInfo _waitInfo; 20 21 internal WaitSubsystem.ThreadWaitInfo WaitInfo => _waitInfo; 22 PlatformSpecificInitialize()23 private void PlatformSpecificInitialize() 24 { 25 RuntimeImports.RhSetThreadExitCallback(AddrofIntrinsics.AddrOf<Action>(OnThreadExit)); 26 } 27 28 // Platform-specific initialization of foreign threads, i.e. threads not created by Thread.Start PlatformSpecificInitializeExistingThread()29 private void PlatformSpecificInitializeExistingThread() 30 { 31 _stopped = new ManualResetEvent(false); 32 } 33 34 /// <summary> 35 /// Callers must ensure to clear and return the array after use 36 /// </summary> RentWaitedSafeWaitHandleArray(int requiredCapacity)37 internal SafeWaitHandle[] RentWaitedSafeWaitHandleArray(int requiredCapacity) 38 { 39 Debug.Assert(this == CurrentThread); 40 Debug.Assert(!ReentrantWaitsEnabled); // due to this, no need to actually rent and return the array 41 42 _waitedSafeWaitHandles.VerifyElementsAreDefault(); 43 _waitedSafeWaitHandles.EnsureCapacity(requiredCapacity); 44 return _waitedSafeWaitHandles.Items; 45 } 46 ReturnWaitedSafeWaitHandleArray(SafeWaitHandle[] waitedSafeWaitHandles)47 internal void ReturnWaitedSafeWaitHandleArray(SafeWaitHandle[] waitedSafeWaitHandles) 48 { 49 Debug.Assert(this == CurrentThread); 50 Debug.Assert(!ReentrantWaitsEnabled); // due to this, no need to actually rent and return the array 51 Debug.Assert(waitedSafeWaitHandles == _waitedSafeWaitHandles.Items); 52 } 53 GetPriorityLive()54 private ThreadPriority GetPriorityLive() 55 { 56 return ThreadPriority.Normal; 57 } 58 SetPriorityLive(ThreadPriority priority)59 private bool SetPriorityLive(ThreadPriority priority) 60 { 61 return true; 62 } 63 64 [NativeCallable] OnThreadExit()65 private static void OnThreadExit() 66 { 67 RuntimeThread currentThread = t_currentThread; 68 if (currentThread != null) 69 { 70 // Inform the wait subsystem that the thread is exiting. For instance, this would abandon any mutexes locked by 71 // the thread. 72 WaitSubsystem.OnThreadExiting(currentThread); 73 74 // Set the Stopped bit and signal the current thread as stopped 75 int state = currentThread._threadState; 76 if ((state & (int)(ThreadState.Stopped | ThreadState.Aborted)) == 0) 77 { 78 currentThread.SetThreadStateBit(ThreadState.Stopped); 79 } 80 currentThread._stopped.Set(); 81 } 82 } 83 GetThreadState()84 private ThreadState GetThreadState() => (ThreadState)_threadState; 85 JoinInternal(int millisecondsTimeout)86 private bool JoinInternal(int millisecondsTimeout) 87 { 88 // This method assumes the thread has been started 89 Debug.Assert(!GetThreadStateBit(ThreadState.Unstarted) || (millisecondsTimeout == 0)); 90 SafeWaitHandle waitHandle = _stopped.SafeWaitHandle; 91 92 // If an OS thread is terminated and its Thread object is resurrected, waitHandle may be finalized and closed 93 if (waitHandle.IsClosed) 94 { 95 return true; 96 } 97 98 // Prevent race condition with the finalizer 99 try 100 { 101 waitHandle.DangerousAddRef(); 102 } 103 catch (ObjectDisposedException) 104 { 105 return true; 106 } 107 108 try 109 { 110 return _stopped.WaitOne(millisecondsTimeout); 111 } 112 finally 113 { 114 waitHandle.DangerousRelease(); 115 } 116 } 117 CreateThread(GCHandle thisThreadHandle)118 private bool CreateThread(GCHandle thisThreadHandle) 119 { 120 // Create the Stop event before starting the thread to make sure 121 // it is ready to be signaled at thread shutdown time. 122 // This also avoids OOM after creating the thread. 123 _stopped = new ManualResetEvent(false); 124 125 if (!Interop.Sys.RuntimeThread_CreateThread((IntPtr)_maxStackSize, 126 AddrofIntrinsics.AddrOf<Interop.Sys.ThreadProc>(ThreadEntryPoint), (IntPtr)thisThreadHandle)) 127 { 128 return false; 129 } 130 131 // CoreCLR ignores OS errors while setting the priority, so do we 132 SetPriorityLive(_priority); 133 134 return true; 135 } 136 137 /// <summary> 138 /// This is an entry point for managed threads created by application 139 /// </summary> 140 [NativeCallable] ThreadEntryPoint(IntPtr parameter)141 private static IntPtr ThreadEntryPoint(IntPtr parameter) 142 { 143 StartThread(parameter); 144 return IntPtr.Zero; 145 } 146 Interrupt()147 public void Interrupt() => WaitSubsystem.Interrupt(this); UninterruptibleSleep0()148 internal static void UninterruptibleSleep0() => WaitSubsystem.UninterruptibleSleep0(); SleepInternal(int millisecondsTimeout)149 private static void SleepInternal(int millisecondsTimeout) => WaitSubsystem.Sleep(millisecondsTimeout); 150 151 internal const bool ReentrantWaitsEnabled = false; 152 SuppressReentrantWaits()153 internal static void SuppressReentrantWaits() 154 { 155 throw new PlatformNotSupportedException(); 156 } 157 RestoreReentrantWaits()158 internal static void RestoreReentrantWaits() 159 { 160 throw new PlatformNotSupportedException(); 161 } 162 } 163 } 164