/
Thread.CoreCLR.cs
344 lines (286 loc) · 13.2 KB
/
Thread.CoreCLR.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
namespace System.Threading
{
internal readonly struct ThreadHandle
{
private readonly IntPtr _ptr;
internal ThreadHandle(IntPtr pThread)
{
_ptr = pThread;
}
}
public sealed partial class Thread
{
/*=========================================================================
** Data accessed from managed code that needs to be defined in
** ThreadBaseObject to maintain alignment between the two classes.
** DON'T CHANGE THESE UNLESS YOU MODIFY ThreadBaseObject in vm\object.h
=========================================================================*/
internal ExecutionContext? _executionContext; // this call context follows the logical thread
internal SynchronizationContext? _synchronizationContext; // maintained separately from ExecutionContext
private string? _name;
private StartHelper? _startHelper;
/*=========================================================================
** The base implementation of Thread is all native. The following fields
** should never be used in the C# code. They are here to define the proper
** space so the thread object may be allocated. DON'T CHANGE THESE UNLESS
** YOU MODIFY ThreadBaseObject in vm\object.h
=========================================================================*/
#pragma warning disable CA1823, 169 // These fields are not used from managed.
// IntPtrs need to be together, and before ints, because IntPtrs are 64-bit
// fields on 64-bit platforms, where they will be sorted together.
private IntPtr _DONT_USE_InternalThread; // Pointer
private int _priority; // INT32
// The following field is required for interop with the VS Debugger
// Prior to making any changes to this field, please reach out to the VS Debugger
// team to make sure that your changes are not going to prevent the debugger
// from working.
private int _managedThreadId; // INT32
#pragma warning restore CA1823, 169
// This is used for a quick check on thread pool threads after running a work item to determine if the name, background
// state, or priority were changed by the work item, and if so to reset it. Other threads may also change some of those,
// but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized.
private bool _mayNeedResetForThreadPool;
private Thread() { }
public extern int ManagedThreadId
{
[Intrinsic]
[MethodImpl(MethodImplOptions.InternalCall)]
get;
}
/// <summary>Returns handle for interop with EE. The handle is guaranteed to be non-null.</summary>
internal ThreadHandle GetNativeHandle()
{
IntPtr thread = _DONT_USE_InternalThread;
// This should never happen under normal circumstances.
if (thread == IntPtr.Zero)
{
throw new ArgumentException(null, SR.Argument_InvalidHandle);
}
return new ThreadHandle(thread);
}
private unsafe void StartCore()
{
lock (this)
{
fixed (char* pThreadName = _name)
{
StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, pThreadName);
}
}
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Start")]
private static unsafe partial void StartInternal(ThreadHandle t, int stackSize, int priority, char* pThreadName);
// Called from the runtime
private void StartCallback()
{
StartHelper? startHelper = _startHelper;
Debug.Assert(startHelper != null);
_startHelper = null;
startHelper.Run();
}
// Invoked by VM. Helper method to get a logical thread ID for StringBuilder (for
// correctness) and for FileStream's async code path (for perf, to avoid creating
// a Thread instance).
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr InternalGetCurrentThread();
/// <summary>
/// Suspends the current thread for timeout milliseconds. If timeout == 0,
/// forces the thread to give up the remainder of its timeslice. If timeout
/// == Timeout.Infinite, no timeout will occur.
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void SleepInternal(int millisecondsTimeout);
/// <summary>
/// Wait for a length of time proportional to 'iterations'. Each iteration is should
/// only take a few machine instructions. Calling this API is preferable to coding
/// a explicit busy loop because the hardware can be informed that it is busy waiting.
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void SpinWaitInternal(int iterations);
public static void SpinWait(int iterations) => SpinWaitInternal(iterations);
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_YieldThread")]
private static partial Interop.BOOL YieldInternal();
public static bool Yield() => YieldInternal() != Interop.BOOL.FALSE;
[MethodImpl(MethodImplOptions.NoInlining)]
private static Thread InitializeCurrentThread() => t_currentThread = GetCurrentThreadNative();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern Thread GetCurrentThreadNative();
[MethodImpl(MethodImplOptions.InternalCall)]
private extern void Initialize();
/// <summary>Clean up the thread when it goes away.</summary>
~Thread() => InternalFinalize(); // Delegate to the unmanaged portion.
[MethodImpl(MethodImplOptions.InternalCall)]
private extern void InternalFinalize();
partial void ThreadNameChanged(string? value)
{
InformThreadNameChange(GetNativeHandle(), value, value?.Length ?? 0);
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_InformThreadNameChange", StringMarshalling = StringMarshalling.Utf16)]
private static partial void InformThreadNameChange(ThreadHandle t, string? name, int len);
/// <summary>Returns true if the thread has been started and is not dead.</summary>
public extern bool IsAlive
{
[MethodImpl(MethodImplOptions.InternalCall)]
get;
}
/// <summary>
/// Return whether or not this thread is a background thread. Background
/// threads do not affect when the Execution Engine shuts down.
/// </summary>
public bool IsBackground
{
get => IsBackgroundNative();
set
{
SetBackgroundNative(value);
if (!value)
{
_mayNeedResetForThreadPool = true;
}
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
private extern bool IsBackgroundNative();
[MethodImpl(MethodImplOptions.InternalCall)]
private extern void SetBackgroundNative(bool isBackground);
/// <summary>Returns true if the thread is a threadpool thread.</summary>
public extern bool IsThreadPoolThread
{
[MethodImpl(MethodImplOptions.InternalCall)]
get;
[MethodImpl(MethodImplOptions.InternalCall)]
internal set;
}
/// <summary>Returns the priority of the thread.</summary>
public ThreadPriority Priority
{
get => (ThreadPriority)GetPriorityNative();
set
{
SetPriorityNative((int)value);
if (value != ThreadPriority.Normal)
{
_mayNeedResetForThreadPool = true;
}
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
private extern int GetPriorityNative();
[MethodImpl(MethodImplOptions.InternalCall)]
private extern void SetPriorityNative(int priority);
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetCurrentOSThreadId")]
private static partial ulong GetCurrentOSThreadId();
/// <summary>
/// Return the thread state as a consistent set of bits. This is more
/// general then IsAlive or IsBackground.
/// </summary>
public ThreadState ThreadState => (ThreadState)GetThreadStateNative();
[MethodImpl(MethodImplOptions.InternalCall)]
private extern int GetThreadStateNative();
public ApartmentState GetApartmentState() =>
#if FEATURE_COMINTEROP_APARTMENT_SUPPORT
(ApartmentState)GetApartmentStateNative();
#else // !FEATURE_COMINTEROP_APARTMENT_SUPPORT
ApartmentState.Unknown;
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
/// <summary>
/// An unstarted thread can be marked to indicate that it will host a
/// single-threaded or multi-threaded apartment.
/// </summary>
#if FEATURE_COMINTEROP_APARTMENT_SUPPORT
private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
{
ApartmentState retState = (ApartmentState)SetApartmentStateNative((int)state);
// Special case where we pass in Unknown and get back MTA.
// Once we CoUninitialize the thread, the OS will still
// report the thread as implicitly in the MTA if any
// other thread in the process is CoInitialized.
if ((state == ApartmentState.Unknown) && (retState == ApartmentState.MTA))
{
return true;
}
if (retState != state)
{
if (throwOnError)
{
string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState);
throw new InvalidOperationException(msg);
}
return false;
}
return true;
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern int GetApartmentStateNative();
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern int SetApartmentStateNative(int state);
#else // FEATURE_COMINTEROP_APARTMENT_SUPPORT
private static bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
{
if (state != ApartmentState.Unknown)
{
if (throwOnError)
{
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ComInterop);
}
return false;
}
return true;
}
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
#if FEATURE_COMINTEROP
[MethodImpl(MethodImplOptions.InternalCall)]
public extern void DisableComObjectEagerCleanup();
#else // !FEATURE_COMINTEROP
public void DisableComObjectEagerCleanup()
{
}
#endif // FEATURE_COMINTEROP
/// <summary>
/// Interrupts a thread that is inside a Wait(), Sleep() or Join(). If that
/// thread is not currently blocked in that manner, it will be interrupted
/// when it next begins to block.
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
public extern void Interrupt();
/// <summary>
/// Waits for the thread to die or for timeout milliseconds to elapse.
/// </summary>
/// <returns>
/// Returns true if the thread died, or false if the wait timed out. If
/// -1 is given as the parameter, no timeout will occur.
/// </returns>
/// <exception cref="ArgumentException">if timeout < -1 (Timeout.Infinite)</exception>
/// <exception cref="ThreadInterruptedException">if the thread is interrupted while waiting</exception>
/// <exception cref="ThreadStateException">if the thread has not been started yet</exception>
[MethodImpl(MethodImplOptions.InternalCall)]
public extern bool Join(int millisecondsTimeout);
/// <summary>
/// Max value to be passed into <see cref="SpinWait(int)"/> for optimal delaying. This value is normalized to be
/// appropriate for the processor.
/// </summary>
internal static int OptimalMaxSpinWaitsPerSpinIteration
{
[MethodImpl(MethodImplOptions.InternalCall)]
get;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ResetThreadPoolThread()
{
Debug.Assert(this == CurrentThread);
Debug.Assert(IsThreadPoolThread);
if (_mayNeedResetForThreadPool)
{
ResetThreadPoolThreadSlow();
}
}
}
}