forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
/
thread.h
345 lines (281 loc) · 13.4 KB
/
thread.h
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
345
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#ifndef __thread_h__
#define __thread_h__
#include "StackFrameIterator.h"
#include "slist.h" // DefaultSListTraits
struct gc_alloc_context;
class RuntimeInstance;
class ThreadStore;
class CLREventStatic;
class Thread;
class TypeManager;
#ifdef TARGET_UNIX
#include "UnixContext.h"
#endif
// The offsets of some fields in the thread (in particular, m_pTransitionFrame) are known to the compiler and get
// inlined into the code. Let's make sure they don't change just because we enable/disable server GC in a particular
// runtime build.
#define KEEP_THREAD_LAYOUT_CONSTANT
#ifndef HOST_64BIT
# if defined(FEATURE_SVR_GC) || defined(KEEP_THREAD_LAYOUT_CONSTANT)
# define SIZEOF_ALLOC_CONTEXT 40
# else
# define SIZEOF_ALLOC_CONTEXT 28
# endif
#else // HOST_64BIT
# if defined(FEATURE_SVR_GC) || defined(KEEP_THREAD_LAYOUT_CONSTANT)
# define SIZEOF_ALLOC_CONTEXT 56
# else
# define SIZEOF_ALLOC_CONTEXT 40
# endif
#endif // HOST_64BIT
#define TOP_OF_STACK_MARKER ((PInvokeTransitionFrame*)(ptrdiff_t)-1)
// the thread has been interrupted and context for the interruption point
// can be retrieved via GetInterruptedContext()
#define INTERRUPTED_THREAD_MARKER ((PInvokeTransitionFrame*)(ptrdiff_t)-2)
typedef DPTR(PAL_LIMITED_CONTEXT) PTR_PAL_LIMITED_CONTEXT;
struct ExInfo;
typedef DPTR(ExInfo) PTR_ExInfo;
// Also defined in ExceptionHandling.cs, layouts must match.
// When adding new fields to this struct, ensure they get properly initialized in the exception handling
// assembly stubs
struct ExInfo
{
PTR_ExInfo m_pPrevExInfo;
PTR_PAL_LIMITED_CONTEXT m_pExContext;
PTR_Object m_exception; // actual object reference, specially reported by GcScanRootsWorker
ExKind m_kind;
uint8_t m_passNumber;
uint32_t m_idxCurClause;
StackFrameIterator m_frameIter;
volatile void* m_notifyDebuggerSP;
};
struct GCFrameRegistration
{
Thread* m_pThread;
GCFrameRegistration* m_pNext;
void** m_pObjRefs;
uint32_t m_numObjRefs;
int m_MaybeInterior;
};
struct InlinedThreadStaticRoot
{
// The reference to the memory block that stores variables for the current {thread, typeManager} combination
Object* m_threadStaticsBase;
// The next root in the list. All roots in the list belong to the same thread, but to different typeManagers.
InlinedThreadStaticRoot* m_next;
// m_typeManager is used by NativeAOT.natvis when debugging
TypeManager* m_typeManager;
};
struct ThreadBuffer
{
uint8_t m_rgbAllocContextBuffer[SIZEOF_ALLOC_CONTEXT];
uint32_t volatile m_ThreadStateFlags; // see Thread::ThreadStateFlags enum
PInvokeTransitionFrame* m_pTransitionFrame;
PInvokeTransitionFrame* m_pDeferredTransitionFrame; // see Thread::EnablePreemptiveMode
PInvokeTransitionFrame* m_pCachedTransitionFrame;
PTR_Thread m_pNext; // used by ThreadStore's SList<Thread>
HANDLE m_hPalThread; // WARNING: this may legitimately be INVALID_HANDLE_VALUE
#ifdef FEATURE_HIJACK
void ** m_ppvHijackedReturnAddressLocation;
void * m_pvHijackedReturnAddress;
uintptr_t m_uHijackedReturnValueFlags;
#endif // FEATURE_HIJACK
PTR_ExInfo m_pExInfoStackHead;
Object* m_threadAbortException; // ThreadAbortException instance -set only during thread abort
#ifdef TARGET_X86
PCODE m_LastRedirectIP;
uint64_t m_SpinCount;
#endif
Object* m_pThreadLocalStatics;
InlinedThreadStaticRoot* m_pInlinedThreadLocalStatics;
GCFrameRegistration* m_pGCFrameRegistrations;
PTR_VOID m_pStackLow;
PTR_VOID m_pStackHigh;
uint64_t m_threadId; // OS thread ID
PTR_VOID m_pThreadStressLog; // pointer to head of thread's StressLogChunks
NATIVE_CONTEXT* m_interruptedContext; // context for an asynchronously interrupted thread.
#ifdef FEATURE_SUSPEND_REDIRECTION
uint8_t* m_redirectionContextBuffer; // storage for redirection context, allocated on demand
#endif //FEATURE_SUSPEND_REDIRECTION
#ifdef FEATURE_GC_STRESS
uint32_t m_uRand; // current per-thread random number
#endif // FEATURE_GC_STRESS
};
struct ReversePInvokeFrame
{
PInvokeTransitionFrame* m_savedPInvokeTransitionFrame;
Thread* m_savedThread;
};
class Thread : private ThreadBuffer
{
friend class AsmOffsets;
friend struct DefaultSListTraits<Thread>;
friend class ThreadStore;
IN_DAC(friend class ClrDataAccess;)
public:
enum ThreadStateFlags
{
TSF_Unknown = 0x00000000, // Threads are created in this state
TSF_Attached = 0x00000001, // Thread was inited by first U->M transition on this thread
TSF_Detached = 0x00000002, // Thread was detached by DllMain
TSF_SuppressGcStress = 0x00000008, // Do not allow gc stress on this thread, used in DllMain
// ...and on the Finalizer thread
TSF_DoNotTriggerGc = 0x00000010, // Do not allow hijacking of this thread, also intended to
// ...be checked during allocations in debug builds.
TSF_IsGcSpecialThread = 0x00000020, // Set to indicate a GC worker thread used for background GC
#ifdef FEATURE_GC_STRESS
TSF_IsRandSeedSet = 0x00000040, // set to indicate the random number generator for GCStress was inited
#endif // FEATURE_GC_STRESS
#ifdef FEATURE_SUSPEND_REDIRECTION
TSF_Redirected = 0x00000080, // Set to indicate the thread is redirected and will inevitably
// suspend once resumed.
// If we see this flag, we skip hijacking as an optimization.
#endif //FEATURE_SUSPEND_REDIRECTION
TSF_ActivationPending = 0x00000100, // An APC with QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC can interrupt another APC.
// For suspension APCs it is mostly harmless, but wasteful and in extreme
// cases may force the target thread into stack oveflow.
// We use this flag to avoid sending another APC when one is still going through.
//
// On Unix this is an optimization to not queue up more signals when one is
// still being processed.
};
private:
void Construct();
void SetState(ThreadStateFlags flags);
void ClearState(ThreadStateFlags flags);
bool IsStateSet(ThreadStateFlags flags);
#ifdef FEATURE_HIJACK
static void HijackCallback(NATIVE_CONTEXT* pThreadContext, void* pThreadToHijack);
//
// Hijack funcs are not called, they are "returned to". And when done, they return to the actual caller.
// Thus they cannot have any parameters or return anything.
//
typedef void FASTCALL HijackFunc();
void HijackReturnAddress(PAL_LIMITED_CONTEXT* pSuspendCtx, HijackFunc* pfnHijackFunction);
void HijackReturnAddress(NATIVE_CONTEXT* pSuspendCtx, HijackFunc* pfnHijackFunction);
void HijackReturnAddressWorker(StackFrameIterator* frameIterator, HijackFunc* pfnHijackFunction);
bool InlineSuspend(NATIVE_CONTEXT* interruptedContext);
void CrossThreadUnhijack();
void UnhijackWorker();
#else // FEATURE_HIJACK
void CrossThreadUnhijack() { }
#endif // FEATURE_HIJACK
#ifdef FEATURE_SUSPEND_REDIRECTION
bool Redirect();
#endif //FEATURE_SUSPEND_REDIRECTION
bool CacheTransitionFrameForSuspend();
void ResetCachedTransitionFrame();
void EnsureRuntimeInitialized();
//
// SyncState members
//
PInvokeTransitionFrame* GetTransitionFrame();
void GcScanRootsWorker(ScanFunc* pfnEnumCallback, ScanContext* pvCallbackData, StackFrameIterator & sfIter);
// Tracks the amount of bytes that were reserved for threads in their gc_alloc_context and went unused when they died.
// Used for GC.GetTotalAllocatedBytes
static uint64_t s_DeadThreadsNonAllocBytes;
public:
static uint64_t GetDeadThreadsNonAllocBytes();
// First phase of thread destructor, disposes stuff related to GC.
// Executed with thread store lock taken so GC cannot happen.
void Detach();
// Second phase of thread destructor.
// Executed without thread store lock taken.
void Destroy();
bool IsInitialized();
gc_alloc_context * GetAllocContext();
uint64_t GetPalThreadIdForLogging();
void GcScanRoots(ScanFunc* pfnEnumCallback, ScanContext * pvCallbackData);
#ifdef FEATURE_HIJACK
void Hijack();
void Unhijack();
bool IsHijacked();
void* GetHijackedReturnAddress();
static bool IsHijackTarget(void * address);
#else // FEATURE_HIJACK
void Unhijack() { }
bool IsHijacked() { return false; }
static bool IsHijackTarget(void * address) { return false; }
#endif // FEATURE_HIJACK
#ifdef FEATURE_GC_STRESS
static void HijackForGcStress(PAL_LIMITED_CONTEXT * pSuspendCtx);
#endif // FEATURE_GC_STRESS
bool IsSuppressGcStressSet();
void SetSuppressGcStress();
void ClearSuppressGcStress();
bool IsWithinStackBounds(PTR_VOID p);
void GetStackBounds(PTR_VOID * ppStackLow, PTR_VOID * ppStackHigh);
void PushExInfo(ExInfo * pExInfo);
void ValidateExInfoPop(ExInfo * pExInfo, void * limitSP);
void ValidateExInfoStack();
bool IsDoNotTriggerGcSet();
void SetDoNotTriggerGc();
void ClearDoNotTriggerGc();
bool IsDetached();
void SetDetached();
PTR_VOID GetThreadStressLog() const;
#ifndef DACCESS_COMPILE
void SetThreadStressLog(void * ptsl);
#endif // DACCESS_COMPILE
#ifdef FEATURE_GC_STRESS
void SetRandomSeed(uint32_t seed);
uint32_t NextRand();
bool IsRandInited();
#endif // FEATURE_GC_STRESS
PTR_ExInfo GetCurExInfo();
bool IsCurrentThreadInCooperativeMode();
PInvokeTransitionFrame* GetTransitionFrameForStackTrace();
void * GetCurrentThreadPInvokeReturnAddress();
//
// The set of operations used to support unmanaged code running in cooperative mode
//
void EnablePreemptiveMode();
void DisablePreemptiveMode();
// Set the m_pDeferredTransitionFrame field for GC allocation helpers that setup transition frame
// in assembly code. Do not use anywhere else.
void SetDeferredTransitionFrame(PInvokeTransitionFrame* pTransitionFrame);
// Setup the m_pDeferredTransitionFrame field for GC helpers entered via regular PInvoke.
// Do not use anywhere else.
void DeferTransitionFrame();
// Setup the m_pDeferredTransitionFrame field for GC helpers entered from native helper thread
// code (e.g. ETW or EventPipe threads). Do not use anywhere else.
void SetDeferredTransitionFrameForNativeHelperThread();
//
// GC support APIs - do not use except from GC itself
//
void SetGCSpecial();
bool IsGCSpecial();
//
// Managed/unmanaged interop transitions support APIs
//
void WaitForGC(PInvokeTransitionFrame* pTransitionFrame);
void ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame);
bool InlineTryFastReversePInvoke(ReversePInvokeFrame * pFrame);
void InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame);
void InlinePInvoke(PInvokeTransitionFrame * pFrame);
void InlinePInvokeReturn(PInvokeTransitionFrame * pFrame);
Object* GetThreadAbortException();
void SetThreadAbortException(Object *exception);
Object** GetThreadStaticStorage();
InlinedThreadStaticRoot* GetInlinedThreadStaticList();
void RegisterInlinedThreadStaticRoot(InlinedThreadStaticRoot* newRoot, TypeManager* typeManager);
NATIVE_CONTEXT* GetInterruptedContext();
void PushGCFrameRegistration(GCFrameRegistration* pRegistration);
void PopGCFrameRegistration(GCFrameRegistration* pRegistration);
#ifdef FEATURE_SUSPEND_REDIRECTION
NATIVE_CONTEXT* EnsureRedirectionContext();
#endif //FEATURE_SUSPEND_REDIRECTION
bool IsActivationPending();
void SetActivationPending(bool isPending);
#ifdef TARGET_X86
void SetPendingRedirect(PCODE eip);
bool CheckPendingRedirect(PCODE eip);
#endif
};
#ifndef __GCENV_BASE_INCLUDED__
typedef DPTR(Object) PTR_Object;
typedef DPTR(PTR_Object) PTR_PTR_Object;
#endif // !__GCENV_BASE_INCLUDED__
#endif // __thread_h__