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// ThreadStatics.h
5//
6
7//
8//
9// Classes can contain instance fields and statics fields. In addition to regular statics, .NET offers
10// several types of special statics. In IL, thread static fields are marked with the ThreadStaticAttribute,
11// distinguishing them from regular statics and other types of special statics. A thread static field is
12// not shared between threads. Each executing thread has a separate instance of the field, and independently
13// sets and gets values for that field.
14//
15// This implementation of thread statics closely parallels the implementation for regular statics. Regular
16// statics use the DomainLocalBlock and DomainLocalModule structures to allocate space for statics each time
17// a module is loaded in an AppDomain.
18//
19
20//
21
22#ifndef __threadstatics_h__
23#define __threadstatics_h__
24
25#include "vars.hpp"
26#include "util.hpp"
27
28#include "appdomain.hpp"
29#include "field.h"
30#include "methodtable.h"
31#include "threads.h"
32#include "spinlock.h"
33
34// Defines ObjectHandeList type
35#include "specialstatics.h"
36
37
38typedef DPTR(struct ThreadLocalModule) PTR_ThreadLocalModule;
39
40struct ThreadLocalModule
41{
42 friend class ClrDataAccess;
43 friend class CheckAsmOffsets;
44 friend struct ThreadLocalBlock;
45
46 // After these macros complete, they may have returned an interior pointer into a gc object. This pointer will have been cast to a byte pointer
47 // It is critically important that no GC is allowed to occur before this pointer is used.
48#define GET_DYNAMICENTRY_GCTHREADSTATICS_BASEPOINTER(pLoaderAllocator, dynamicClassInfoParam, pGCStatics) \
49 {\
50 ThreadLocalModule::PTR_DynamicClassInfo dynamicClassInfo = dac_cast<ThreadLocalModule::PTR_DynamicClassInfo>(dynamicClassInfoParam);\
51 ThreadLocalModule::PTR_DynamicEntry pDynamicEntry = dac_cast<ThreadLocalModule::PTR_DynamicEntry>((ThreadLocalModule::DynamicEntry*)dynamicClassInfo->m_pDynamicEntry); \
52 if ((dynamicClassInfo->m_dwFlags) & ClassInitFlags::COLLECTIBLE_FLAG) \
53 {\
54 PTRARRAYREF objArray;\
55 objArray = (PTRARRAYREF)pLoaderAllocator->GetHandleValueFastCannotFailType2( \
56 (dac_cast<ThreadLocalModule::PTR_CollectibleDynamicEntry>(pDynamicEntry))->m_hGCStatics);\
57 *(pGCStatics) = dac_cast<PTR_BYTE>(PTR_READ(PTR_TO_TADDR(OBJECTREFToObject( objArray )) + offsetof(PtrArray, m_Array), objArray->GetNumComponents() * sizeof(void*))) ;\
58 }\
59 else\
60 {\
61 *(pGCStatics) = (dac_cast<ThreadLocalModule::PTR_NormalDynamicEntry>(pDynamicEntry))->GetGCStaticsBasePointer();\
62 }\
63 }\
64
65#define GET_DYNAMICENTRY_NONGCTHREADSTATICS_BASEPOINTER(pLoaderAllocator, dynamicClassInfoParam, pNonGCStatics) \
66 {\
67 ThreadLocalModule::PTR_DynamicClassInfo dynamicClassInfo = dac_cast<ThreadLocalModule::PTR_DynamicClassInfo>(dynamicClassInfoParam);\
68 ThreadLocalModule::PTR_DynamicEntry pDynamicEntry = dac_cast<ThreadLocalModule::PTR_DynamicEntry>((ThreadLocalModule::DynamicEntry*)(dynamicClassInfo)->m_pDynamicEntry); \
69 if (((dynamicClassInfo)->m_dwFlags) & ClassInitFlags::COLLECTIBLE_FLAG) \
70 {\
71 if ((dac_cast<ThreadLocalModule::PTR_CollectibleDynamicEntry>(pDynamicEntry))->m_hNonGCStatics != 0) \
72 { \
73 U1ARRAYREF objArray;\
74 objArray = (U1ARRAYREF)pLoaderAllocator->GetHandleValueFastCannotFailType2( \
75 (dac_cast<ThreadLocalModule::PTR_CollectibleDynamicEntry>(pDynamicEntry))->m_hNonGCStatics);\
76 *(pNonGCStatics) = dac_cast<PTR_BYTE>(PTR_READ( \
77 PTR_TO_TADDR(OBJECTREFToObject( objArray )) + sizeof(ArrayBase) - ThreadLocalModule::DynamicEntry::GetOffsetOfDataBlob(), \
78 objArray->GetNumComponents() * (DWORD)objArray->GetComponentSize() + ThreadLocalModule::DynamicEntry::GetOffsetOfDataBlob())); \
79 } else (*pNonGCStatics) = NULL; \
80 }\
81 else\
82 {\
83 *(pNonGCStatics) = dac_cast<ThreadLocalModule::PTR_NormalDynamicEntry>(pDynamicEntry)->GetNonGCStaticsBasePointer();\
84 }\
85 }\
86
87 struct DynamicEntry
88 {
89 static DWORD GetOffsetOfDataBlob();
90 };
91 typedef DPTR(DynamicEntry) PTR_DynamicEntry;
92
93 struct CollectibleDynamicEntry : public DynamicEntry
94 {
95 LOADERHANDLE m_hGCStatics;
96 LOADERHANDLE m_hNonGCStatics;
97 PTR_LoaderAllocator m_pLoaderAllocator;
98 };
99 typedef DPTR(CollectibleDynamicEntry) PTR_CollectibleDynamicEntry;
100
101 struct NormalDynamicEntry : public DynamicEntry
102 {
103 OBJECTHANDLE m_pGCStatics;
104#ifdef FEATURE_64BIT_ALIGNMENT
105 // Padding to make m_pDataBlob aligned at MAX_PRIMITIVE_FIELD_SIZE.
106 // code:MethodTableBuilder::PlaceThreadStaticFields assumes that the start of the data blob is aligned
107 SIZE_T m_padding;
108#endif
109 BYTE m_pDataBlob[0];
110
111 inline PTR_BYTE GetGCStaticsBasePointer()
112 {
113 CONTRACTL
114 {
115 NOTHROW;
116 GC_NOTRIGGER;
117 MODE_ANY;
118 SO_TOLERANT;
119 SUPPORTS_DAC;
120 }
121 CONTRACTL_END;
122
123 _ASSERTE(m_pGCStatics != NULL);
124
125 return dac_cast<PTR_BYTE>(((PTRARRAYREF)ObjectFromHandle(m_pGCStatics))->GetDataPtr());
126 }
127 inline PTR_BYTE GetGCStaticsBaseHandle()
128 {
129 LIMITED_METHOD_CONTRACT;
130 SUPPORTS_DAC;
131 return dac_cast<PTR_BYTE>(m_pGCStatics);
132 }
133 inline PTR_BYTE GetNonGCStaticsBasePointer()
134 {
135 LIMITED_METHOD_CONTRACT;
136 SUPPORTS_DAC;
137 return dac_cast<PTR_BYTE>(this);
138 }
139 };
140 typedef DPTR(NormalDynamicEntry) PTR_NormalDynamicEntry;
141
142 struct DynamicClassInfo
143 {
144 PTR_DynamicEntry m_pDynamicEntry;
145 DWORD m_dwFlags;
146 };
147 typedef DPTR(DynamicClassInfo) PTR_DynamicClassInfo;
148
149 // Note the difference between:
150 //
151 // GetPrecomputedNonGCStaticsBasePointer() and
152 // GetPrecomputedStaticsClassData()
153 //
154 // GetPrecomputedNonGCStaticsBasePointer returns the pointer that should be added to field offsets to retrieve statics
155 // GetPrecomputedStaticsClassData returns a pointer to the first byte of the precomputed statics block
156 inline TADDR GetPrecomputedNonGCStaticsBasePointer()
157 {
158 LIMITED_METHOD_CONTRACT
159 return dac_cast<TADDR>(this);
160 }
161
162 static SIZE_T GetOffsetOfDataBlob() { return offsetof(ThreadLocalModule, m_pDataBlob); }
163 static SIZE_T GetOffsetOfGCStaticHandle() { return offsetof(ThreadLocalModule, m_pGCStatics); }
164
165 inline PTR_OBJECTREF GetPrecomputedGCStaticsBasePointer()
166 {
167 CONTRACTL
168 {
169 NOTHROW;
170 GC_NOTRIGGER;
171 MODE_ANY;
172 SO_TOLERANT;
173 SUPPORTS_DAC;
174 }
175 CONTRACTL_END;
176
177 _ASSERTE(m_pGCStatics != NULL);
178
179 return ((PTRARRAYREF)ObjectFromHandle(m_pGCStatics))->GetDataPtr();
180 }
181
182 inline OBJECTHANDLE GetPrecomputedGCStaticsBaseHandle()
183 {
184 CONTRACTL
185 {
186 NOTHROW;
187 GC_NOTRIGGER;
188 MODE_ANY;
189 }
190 CONTRACTL_END;
191
192 return m_pGCStatics;
193 }
194
195 inline OBJECTHANDLE * GetPrecomputedGCStaticsBaseHandleAddress()
196 {
197 CONTRACTL
198 {
199 NOTHROW;
200 GC_NOTRIGGER;
201 MODE_ANY;
202 }
203 CONTRACTL_END;
204
205 return &m_pGCStatics;
206 }
207
208 // Returns bytes so we can add offsets
209 inline PTR_BYTE GetGCStaticsBasePointer(MethodTable * pMT)
210 {
211 CONTRACTL
212 {
213 NOTHROW;
214 GC_NOTRIGGER;
215 MODE_ANY;
216 SUPPORTS_DAC;
217 }
218 CONTRACTL_END;
219
220 if (pMT->IsDynamicStatics())
221 {
222 return GetDynamicEntryGCStaticsBasePointer(pMT->GetModuleDynamicEntryID(), pMT->GetLoaderAllocator());
223 }
224 else
225 {
226 return dac_cast<PTR_BYTE>(GetPrecomputedGCStaticsBasePointer());
227 }
228 }
229
230 inline PTR_BYTE GetNonGCStaticsBasePointer(MethodTable * pMT)
231 {
232 CONTRACTL
233 {
234 NOTHROW;
235 GC_NOTRIGGER;
236 MODE_ANY;
237 SUPPORTS_DAC;
238 }
239 CONTRACTL_END;
240
241 if (pMT->IsDynamicStatics())
242 {
243 return GetDynamicEntryNonGCStaticsBasePointer(pMT->GetModuleDynamicEntryID(), pMT->GetLoaderAllocator());
244 }
245 else
246 {
247 return dac_cast<PTR_BYTE>(this);
248 }
249 }
250
251 inline DynamicEntry* GetDynamicEntry(DWORD n)
252 {
253 LIMITED_METHOD_CONTRACT
254 SUPPORTS_DAC;
255 _ASSERTE(m_pDynamicClassTable && m_aDynamicEntries > n);
256 DynamicEntry* pEntry = m_pDynamicClassTable[n].m_pDynamicEntry;
257
258 return pEntry;
259 }
260
261 inline DynamicClassInfo* GetDynamicClassInfo(DWORD n)
262 {
263 LIMITED_METHOD_CONTRACT
264 SUPPORTS_DAC;
265 _ASSERTE(m_pDynamicClassTable && m_aDynamicEntries > n);
266 dac_cast<PTR_DynamicEntry>(m_pDynamicClassTable[n].m_pDynamicEntry);
267
268 return &m_pDynamicClassTable[n];
269 }
270
271 // These helpers can now return null, as the debugger may do queries on a type
272 // before the calls to PopulateClass happen
273 inline PTR_BYTE GetDynamicEntryGCStaticsBasePointer(DWORD n, PTR_LoaderAllocator pLoaderAllocator)
274 {
275 CONTRACTL
276 {
277 NOTHROW;
278 GC_NOTRIGGER;
279 MODE_ANY;
280 SUPPORTS_DAC;
281 }
282 CONTRACTL_END;
283
284 if (n >= m_aDynamicEntries)
285 {
286 return NULL;
287 }
288
289 DynamicClassInfo* pClassInfo = GetDynamicClassInfo(n);
290 if (!pClassInfo->m_pDynamicEntry)
291 {
292 return NULL;
293 }
294
295 PTR_BYTE retval = NULL;
296
297 GET_DYNAMICENTRY_GCTHREADSTATICS_BASEPOINTER(pLoaderAllocator, pClassInfo, &retval);
298
299 return retval;
300 }
301
302 inline PTR_BYTE GetDynamicEntryNonGCStaticsBasePointer(DWORD n, PTR_LoaderAllocator pLoaderAllocator)
303 {
304 CONTRACTL
305 {
306 NOTHROW;
307 GC_NOTRIGGER;
308 MODE_ANY;
309 SUPPORTS_DAC;
310 }
311 CONTRACTL_END;
312
313 if (n >= m_aDynamicEntries)
314 {
315 return NULL;
316 }
317
318 DynamicClassInfo* pClassInfo = GetDynamicClassInfo(n);
319 if (!pClassInfo->m_pDynamicEntry)
320 {
321 return NULL;
322 }
323
324 PTR_BYTE retval = NULL;
325
326 GET_DYNAMICENTRY_NONGCTHREADSTATICS_BASEPOINTER(pLoaderAllocator, pClassInfo, &retval);
327
328 return retval;
329 }
330
331 FORCEINLINE PTR_DynamicClassInfo GetDynamicClassInfoIfInitialized(DWORD n)
332 {
333 WRAPPER_NO_CONTRACT;
334
335 // m_aDynamicEntries is set last, it needs to be checked first
336 if (n >= m_aDynamicEntries)
337 {
338 return NULL;
339 }
340
341 _ASSERTE(m_pDynamicClassTable != NULL);
342 PTR_DynamicClassInfo pDynamicClassInfo = (PTR_DynamicClassInfo)(m_pDynamicClassTable + n);
343
344 // ClassInitFlags::INITIALIZED_FLAG is set last, it needs to be checked first
345 if ((pDynamicClassInfo->m_dwFlags & ClassInitFlags::INITIALIZED_FLAG) == 0)
346 {
347 return NULL;
348 }
349
350 PREFIX_ASSUME(pDynamicClassInfo != NULL);
351 return pDynamicClassInfo;
352 }
353
354 // iClassIndex is slightly expensive to compute, so if we already know
355 // it, we can use this helper
356
357 inline BOOL IsClassInitialized(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1)
358 {
359 WRAPPER_NO_CONTRACT;
360 return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::INITIALIZED_FLAG) != 0;
361 }
362
363 inline BOOL IsClassAllocated(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1)
364 {
365 WRAPPER_NO_CONTRACT;
366 return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::ALLOCATECLASS_FLAG) != 0;
367 }
368
369 BOOL IsClassInitError(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1)
370 {
371 WRAPPER_NO_CONTRACT;
372 return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::ERROR_FLAG) != 0;
373 }
374
375 void SetClassInitialized(MethodTable* pMT)
376 {
377 CONTRACTL
378 {
379 THROWS;
380 GC_NOTRIGGER;
381 SO_INTOLERANT;
382 MODE_ANY;
383 }
384 CONTRACTL_END;
385
386 _ASSERTE(!IsClassInitialized(pMT));
387 _ASSERTE(!IsClassInitError(pMT));
388
389 SetClassFlags(pMT, ClassInitFlags::INITIALIZED_FLAG);
390 }
391
392 void SetClassAllocated(MethodTable* pMT)
393 {
394 WRAPPER_NO_CONTRACT;
395
396 SetClassFlags(pMT, ClassInitFlags::ALLOCATECLASS_FLAG);
397 }
398
399 void SetClassInitError(MethodTable* pMT)
400 {
401 WRAPPER_NO_CONTRACT;
402
403 SetClassFlags(pMT, ClassInitFlags::ERROR_FLAG);
404 }
405
406#ifndef DACCESS_COMPILE
407
408 void EnsureDynamicClassIndex(DWORD dwID);
409
410 void AllocateDynamicClass(MethodTable *pMT);
411
412 void PopulateClass(MethodTable *pMT);
413
414#endif
415
416#ifdef DACCESS_COMPILE
417 void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
418#endif
419
420 static DWORD OffsetOfDataBlob()
421 {
422 LIMITED_METHOD_CONTRACT;
423 return offsetof(ThreadLocalModule, m_pDataBlob);
424 }
425
426private:
427
428 void SetClassFlags(MethodTable* pMT, DWORD dwFlags);
429
430 DWORD GetClassFlags(MethodTable* pMT, DWORD iClassIndex);
431
432
433 PTR_DynamicClassInfo m_pDynamicClassTable; // used for generics and reflection.emit in memory
434 SIZE_T m_aDynamicEntries; // number of entries in dynamic table
435 OBJECTHANDLE m_pGCStatics; // Handle to GC statics of the module
436
437 // Note that the static offset calculation in code:Module::BuildStaticsOffsets takes the offset m_pDataBlob
438 // into consideration so we do not need any padding to ensure that the start of the data blob is aligned
439
440 BYTE m_pDataBlob[0]; // First byte of the statics blob
441
442 // Layout of m_pDataBlob is:
443 // ClassInit bytes (hold flags for cctor run, cctor error, etc)
444 // Non GC Statics
445
446public:
447 inline PTR_BYTE GetPrecomputedStaticsClassData()
448 {
449 LIMITED_METHOD_CONTRACT
450 return dac_cast<PTR_BYTE>(this) + offsetof(ThreadLocalModule, m_pDataBlob);
451 }
452
453 inline BOOL IsPrecomputedClassInitialized(DWORD classID)
454 {
455 return GetPrecomputedStaticsClassData()[classID] & ClassInitFlags::INITIALIZED_FLAG;
456 }
457
458#ifndef DACCESS_COMPILE
459
460 FORCEINLINE void EnsureClassAllocated(MethodTable * pMT)
461 {
462 _ASSERTE(this != NULL);
463
464 // Check if the class needs to be allocated
465 if (!IsClassAllocated(pMT))
466 PopulateClass(pMT);
467
468 // If PopulateClass() does not throw, then we are guaranteed
469 // that the class has been allocated
470 _ASSERTE(IsClassAllocated(pMT));
471 }
472
473 FORCEINLINE void CheckRunClassInitThrowing(MethodTable * pMT)
474 {
475 _ASSERTE(this != NULL);
476
477 // Check if the class has been marked as inited in the ThreadLocalModule
478 if (!IsClassInitialized(pMT))
479 {
480 // Ensure that the class has been allocated
481 EnsureClassAllocated(pMT);
482
483 // Check if the class has been marked as inited in the DomainLocalModule,
484 // if not we must call CheckRunClassInitThrowing()
485 if (!pMT->IsClassInited())
486 pMT->CheckRunClassInitThrowing();
487
488 // We cannot mark the class as inited in the TLM until it has been marked
489 // as inited in the DLM. MethodTable::CheckRunClassInitThrowing() can return
490 // before the class constructor has finished running (because of recursion),
491 // so we actually need to check if the class has been marked as inited in the
492 // DLM before marking it as inited in the TLM.
493 if (pMT->IsClassInited())
494 SetClassInitialized(pMT);
495 }
496 }
497
498#endif
499}; // struct ThreadLocalModule
500
501
502#define OFFSETOF__ThreadLocalModule__m_pDataBlob (3 * TARGET_POINTER_SIZE /* m_pDynamicClassTable + m_aDynamicEntries + m_pGCStatics */)
503#ifdef FEATURE_64BIT_ALIGNMENT
504#define OFFSETOF__ThreadLocalModule__DynamicEntry__m_pDataBlob (TARGET_POINTER_SIZE /* m_pGCStatics */ + TARGET_POINTER_SIZE /* m_padding */)
505#else
506#define OFFSETOF__ThreadLocalModule__DynamicEntry__m_pDataBlob TARGET_POINTER_SIZE /* m_pGCStatics */
507#endif
508
509typedef DPTR(struct TLMTableEntry) PTR_TLMTableEntry;
510
511struct TLMTableEntry
512{
513 PTR_ThreadLocalModule pTLM;
514};
515
516
517typedef DPTR(struct ThreadLocalBlock) PTR_ThreadLocalBlock;
518typedef DPTR(PTR_ThreadLocalBlock) PTR_PTR_ThreadLocalBlock;
519
520class ThreadStatics
521{
522 public:
523
524#ifndef DACCESS_COMPILE
525 static PTR_ThreadLocalModule AllocateTLM(Module * pModule);
526 static PTR_ThreadLocalModule AllocateAndInitTLM(ModuleIndex index, PTR_ThreadLocalBlock pThreadLocalBlock, Module * pModule);
527
528 static PTR_ThreadLocalModule GetTLM(ModuleIndex index, Module * pModule);
529 static PTR_ThreadLocalModule GetTLM(MethodTable * pMT);
530#endif
531
532 FORCEINLINE static PTR_ThreadLocalBlock GetCurrentTLB(PTR_Thread pThread)
533 {
534 SUPPORTS_DAC;
535
536 return dac_cast<PTR_ThreadLocalBlock>(PTR_TO_MEMBER_TADDR(Thread, pThread, m_ThreadLocalBlock));
537 }
538
539#ifndef DACCESS_COMPILE
540 FORCEINLINE static ThreadLocalBlock* GetCurrentTLB()
541 {
542 // Get the current thread
543 Thread * pThread = GetThread();
544
545 return &pThread->m_ThreadLocalBlock;
546 }
547
548 FORCEINLINE static ThreadLocalModule* GetTLMIfExists(ModuleIndex index)
549 {
550 // Get the current ThreadLocalBlock
551 PTR_ThreadLocalBlock pThreadLocalBlock = GetCurrentTLB();
552
553 // Get the TLM from the ThreadLocalBlock's table
554 return pThreadLocalBlock->GetTLMIfExists(index);
555 }
556
557 FORCEINLINE static ThreadLocalModule* GetTLMIfExists(MethodTable * pMT)
558 {
559 // Get the current ThreadLocalBlock
560 ThreadLocalBlock* pThreadLocalBlock = GetCurrentTLB();
561
562 // Get the TLM from the ThreadLocalBlock's table
563 return pThreadLocalBlock->GetTLMIfExists(pMT);
564 }
565#endif
566
567};
568
569/* static */
570inline DWORD ThreadLocalModule::DynamicEntry::GetOffsetOfDataBlob()
571{
572 LIMITED_METHOD_CONTRACT;
573 _ASSERTE(DWORD(offsetof(NormalDynamicEntry, m_pDataBlob)) == offsetof(NormalDynamicEntry, m_pDataBlob));
574 return (DWORD)offsetof(NormalDynamicEntry, m_pDataBlob);
575}
576
577#endif
578