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