forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
/
appdomain.hpp
2655 lines (2165 loc) · 82.1 KB
/
appdomain.hpp
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
/*============================================================
**
** Header: AppDomain.cpp
**
**
** Purpose: Implements AppDomain (loader domain) architecture
**
**
===========================================================*/
#ifndef _APPDOMAIN_H
#define _APPDOMAIN_H
#include "eventtrace.h"
#include "assembly.hpp"
#include "clsload.hpp"
#include "eehash.h"
#include "arraylist.h"
#include "comreflectioncache.hpp"
#include "comutilnative.h"
#include "domainassembly.h"
#include "fptrstubs.h"
#include "gcheaputilities.h"
#include "gchandleutilities.h"
#include "rejit.h"
#ifdef FEATURE_MULTICOREJIT
#include "multicorejit.h"
#endif
#include "tieredcompilation.h"
#include "codeversion.h"
class BaseDomain;
class SystemDomain;
class AppDomain;
class GlobalStringLiteralMap;
class StringLiteralMap;
class FrozenObjectHeapManager;
class MngStdInterfacesInfo;
class DomainAssembly;
class LoadLevelLimiter;
class TypeEquivalenceHashTable;
#ifdef FEATURE_COMINTEROP
class RCWCache;
#endif //FEATURE_COMINTEROP
#ifdef FEATURE_COMWRAPPERS
class RCWRefCache;
#endif // FEATURE_COMWRAPPERS
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4200) // Disable zero-sized array warning
#endif
GPTR_DECL(IdDispenser, g_pModuleIndexDispenser);
// We would like *ALLOCATECLASS_FLAG to AV (in order to catch errors), so don't change it
struct ClassInitFlags {
enum
{
INITIALIZED_FLAG_BIT = 0,
INITIALIZED_FLAG = 1<<INITIALIZED_FLAG_BIT,
ERROR_FLAG_BIT = 1,
ERROR_FLAG = 1<<ERROR_FLAG_BIT,
ALLOCATECLASS_FLAG_BIT = 2, // Bit to avoid racing for InstantiateStaticHandles
ALLOCATECLASS_FLAG = 1<<ALLOCATECLASS_FLAG_BIT,
COLLECTIBLE_FLAG_BIT = 3,
COLLECTIBLE_FLAG = 1<<COLLECTIBLE_FLAG_BIT
};
};
struct DomainLocalModule
{
friend class ClrDataAccess;
friend class CheckAsmOffsets;
friend struct ThreadLocalModule;
// 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
// It is critically important that no GC is allowed to occur before this pointer is used.
#define GET_DYNAMICENTRY_GCSTATICS_BASEPOINTER(pLoaderAllocator, dynamicClassInfoParam, pGCStatics) \
{\
DomainLocalModule::PTR_DynamicClassInfo dynamicClassInfo = dac_cast<DomainLocalModule::PTR_DynamicClassInfo>(dynamicClassInfoParam);\
DomainLocalModule::PTR_DynamicEntry pDynamicEntry = dac_cast<DomainLocalModule::PTR_DynamicEntry>((DomainLocalModule::DynamicEntry*)dynamicClassInfo->m_pDynamicEntry.Load()); \
if ((dynamicClassInfo->m_dwFlags) & ClassInitFlags::COLLECTIBLE_FLAG) \
{\
PTRARRAYREF objArray;\
objArray = (PTRARRAYREF)pLoaderAllocator->GetHandleValueFastCannotFailType2( \
(dac_cast<DomainLocalModule::PTR_CollectibleDynamicEntry>(pDynamicEntry))->m_hGCStatics);\
*(pGCStatics) = dac_cast<PTR_BYTE>(PTR_READ(PTR_TO_TADDR(OBJECTREFToObject( objArray )) + offsetof(PtrArray, m_Array), objArray->GetNumComponents() * sizeof(void*))) ;\
}\
else\
{\
*(pGCStatics) = (dac_cast<DomainLocalModule::PTR_NormalDynamicEntry>(pDynamicEntry))->GetGCStaticsBasePointer();\
}\
}\
#define GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pLoaderAllocator, dynamicClassInfoParam, pNonGCStatics) \
{\
DomainLocalModule::PTR_DynamicClassInfo dynamicClassInfo = dac_cast<DomainLocalModule::PTR_DynamicClassInfo>(dynamicClassInfoParam);\
DomainLocalModule::PTR_DynamicEntry pDynamicEntry = dac_cast<DomainLocalModule::PTR_DynamicEntry>((DomainLocalModule::DynamicEntry*)(dynamicClassInfo)->m_pDynamicEntry.Load()); \
if (((dynamicClassInfo)->m_dwFlags) & ClassInitFlags::COLLECTIBLE_FLAG) \
{\
if ((dac_cast<DomainLocalModule::PTR_CollectibleDynamicEntry>(pDynamicEntry))->m_hNonGCStatics != 0) \
{ \
U1ARRAYREF objArray;\
objArray = (U1ARRAYREF)pLoaderAllocator->GetHandleValueFastCannotFailType2( \
(dac_cast<DomainLocalModule::PTR_CollectibleDynamicEntry>(pDynamicEntry))->m_hNonGCStatics);\
*(pNonGCStatics) = dac_cast<PTR_BYTE>(PTR_READ( \
PTR_TO_TADDR(OBJECTREFToObject( objArray )) + sizeof(ArrayBase) - DomainLocalModule::DynamicEntry::GetOffsetOfDataBlob(), \
objArray->GetNumComponents() * (DWORD)objArray->GetComponentSize() + DomainLocalModule::DynamicEntry::GetOffsetOfDataBlob())); \
} else (*pNonGCStatics) = NULL; \
}\
else\
{\
*(pNonGCStatics) = dac_cast<DomainLocalModule::PTR_NormalDynamicEntry>(pDynamicEntry)->GetNonGCStaticsBasePointer();\
}\
}\
struct DynamicEntry
{
static DWORD GetOffsetOfDataBlob();
};
typedef DPTR(DynamicEntry) PTR_DynamicEntry;
struct CollectibleDynamicEntry : public DynamicEntry
{
LOADERHANDLE m_hGCStatics;
LOADERHANDLE m_hNonGCStatics;
};
typedef DPTR(CollectibleDynamicEntry) PTR_CollectibleDynamicEntry;
struct NormalDynamicEntry : public DynamicEntry
{
PTR_OBJECTREF m_pGCStatics;
#ifdef FEATURE_64BIT_ALIGNMENT
// Padding to make m_pDataBlob aligned at MAX_PRIMITIVE_FIELD_SIZE
// code:MethodTableBuilder::PlaceRegularStaticFields assumes that the start of the data blob is aligned
SIZE_T m_padding;
#endif
BYTE m_pDataBlob[0];
inline PTR_BYTE GetGCStaticsBasePointer()
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
return dac_cast<PTR_BYTE>(m_pGCStatics);
}
inline PTR_BYTE GetNonGCStaticsBasePointer()
{
LIMITED_METHOD_CONTRACT
SUPPORTS_DAC;
return dac_cast<PTR_BYTE>(this);
}
};
typedef DPTR(NormalDynamicEntry) PTR_NormalDynamicEntry;
struct DynamicClassInfo
{
VolatilePtr<DynamicEntry, PTR_DynamicEntry> m_pDynamicEntry;
Volatile<DWORD> m_dwFlags;
};
typedef DPTR(DynamicClassInfo) PTR_DynamicClassInfo;
inline UMEntryThunk * GetADThunkTable()
{
LIMITED_METHOD_CONTRACT
return m_pADThunkTable;
}
inline void SetADThunkTable(UMEntryThunk* pADThunkTable)
{
LIMITED_METHOD_CONTRACT
InterlockedCompareExchangeT(m_pADThunkTable.GetPointer(), pADThunkTable, NULL);
}
// Note the difference between:
//
// GetPrecomputedNonGCStaticsBasePointer() and
// GetPrecomputedStaticsClassData()
//
// GetPrecomputedNonGCStaticsBasePointer returns the pointer that should be added to field offsets to retrieve statics
// GetPrecomputedStaticsClassData returns a pointer to the first byte of the precomputed statics block
inline TADDR GetPrecomputedNonGCStaticsBasePointer()
{
LIMITED_METHOD_CONTRACT
return dac_cast<TADDR>(this);
}
inline PTR_BYTE GetPrecomputedStaticsClassData()
{
LIMITED_METHOD_CONTRACT
return dac_cast<PTR_BYTE>(this) + offsetof(DomainLocalModule, m_pDataBlob);
}
static SIZE_T GetOffsetOfDataBlob() { return offsetof(DomainLocalModule, m_pDataBlob); }
static SIZE_T GetOffsetOfGCStaticPointer() { return offsetof(DomainLocalModule, m_pGCStatics); }
inline DomainAssembly* GetDomainAssembly()
{
LIMITED_METHOD_CONTRACT
SUPPORTS_DAC;
return m_pDomainAssembly;
}
#ifndef DACCESS_COMPILE
inline void SetDomainAssembly(DomainAssembly* pDomainAssembly)
{
LIMITED_METHOD_CONTRACT
m_pDomainAssembly = pDomainAssembly;
}
#endif
inline PTR_OBJECTREF GetPrecomputedGCStaticsBasePointer()
{
LIMITED_METHOD_CONTRACT
return m_pGCStatics;
}
inline PTR_OBJECTREF * GetPrecomputedGCStaticsBasePointerAddress()
{
LIMITED_METHOD_CONTRACT
return &m_pGCStatics;
}
// Returns bytes so we can add offsets
inline PTR_BYTE GetGCStaticsBasePointer(MethodTable * pMT)
{
WRAPPER_NO_CONTRACT
SUPPORTS_DAC;
if (pMT->IsDynamicStatics())
{
_ASSERTE(GetDomainAssembly()->GetModule() == pMT->GetModuleForStatics());
return GetDynamicEntryGCStaticsBasePointer(pMT->GetModuleDynamicEntryID(), pMT->GetLoaderAllocator());
}
else
{
return dac_cast<PTR_BYTE>(m_pGCStatics);
}
}
inline PTR_BYTE GetNonGCStaticsBasePointer(MethodTable * pMT)
{
WRAPPER_NO_CONTRACT
SUPPORTS_DAC;
if (pMT->IsDynamicStatics())
{
_ASSERTE(GetDomainAssembly()->GetModule() == pMT->GetModuleForStatics());
return GetDynamicEntryNonGCStaticsBasePointer(pMT->GetModuleDynamicEntryID(), pMT->GetLoaderAllocator());
}
else
{
return dac_cast<PTR_BYTE>(this);
}
}
inline DynamicClassInfo* GetDynamicClassInfo(DWORD n)
{
LIMITED_METHOD_CONTRACT
SUPPORTS_DAC;
_ASSERTE(m_pDynamicClassTable.Load() && m_aDynamicEntries > n);
dac_cast<PTR_DynamicEntry>(m_pDynamicClassTable[n].m_pDynamicEntry.Load());
return &m_pDynamicClassTable[n];
}
// These helpers can now return null, as the debugger may do queries on a type
// before the calls to PopulateClass happen
inline PTR_BYTE GetDynamicEntryGCStaticsBasePointer(DWORD n, PTR_LoaderAllocator pLoaderAllocator)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
SUPPORTS_DAC;
}
CONTRACTL_END;
if (n >= m_aDynamicEntries)
{
return NULL;
}
DynamicClassInfo* pClassInfo = GetDynamicClassInfo(n);
if (!pClassInfo->m_pDynamicEntry)
{
return NULL;
}
PTR_BYTE retval = NULL;
GET_DYNAMICENTRY_GCSTATICS_BASEPOINTER(pLoaderAllocator, pClassInfo, &retval);
return retval;
}
inline PTR_BYTE GetDynamicEntryNonGCStaticsBasePointer(DWORD n, PTR_LoaderAllocator pLoaderAllocator)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
SUPPORTS_DAC;
}
CONTRACTL_END;
if (n >= m_aDynamicEntries)
{
return NULL;
}
DynamicClassInfo* pClassInfo = GetDynamicClassInfo(n);
if (!pClassInfo->m_pDynamicEntry)
{
return NULL;
}
PTR_BYTE retval = NULL;
GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pLoaderAllocator, pClassInfo, &retval);
return retval;
}
FORCEINLINE PTR_DynamicClassInfo GetDynamicClassInfoIfInitialized(DWORD n)
{
WRAPPER_NO_CONTRACT;
// m_aDynamicEntries is set last, it needs to be checked first
if (n >= m_aDynamicEntries)
{
return NULL;
}
_ASSERTE(m_pDynamicClassTable.Load() != NULL);
PTR_DynamicClassInfo pDynamicClassInfo = (PTR_DynamicClassInfo)(m_pDynamicClassTable.Load() + n);
// INITIALIZED_FLAG is set last, it needs to be checked first
if ((pDynamicClassInfo->m_dwFlags & ClassInitFlags::INITIALIZED_FLAG) == 0)
{
return NULL;
}
PREFIX_ASSUME(pDynamicClassInfo != NULL);
return pDynamicClassInfo;
}
// iClassIndex is slightly expensive to compute, so if we already know
// it, we can use this helper
inline BOOL IsClassInitialized(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1)
{
WRAPPER_NO_CONTRACT;
return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::INITIALIZED_FLAG) != 0;
}
inline BOOL IsPrecomputedClassInitialized(DWORD classID)
{
return GetPrecomputedStaticsClassData()[classID] & ClassInitFlags::INITIALIZED_FLAG;
}
inline BOOL IsClassAllocated(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1)
{
WRAPPER_NO_CONTRACT;
return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::ALLOCATECLASS_FLAG) != 0;
}
BOOL IsClassInitError(MethodTable* pMT, DWORD iClassIndex = (DWORD)-1)
{
WRAPPER_NO_CONTRACT;
return (GetClassFlags(pMT, iClassIndex) & ClassInitFlags::ERROR_FLAG) != 0;
}
void SetClassInitialized(MethodTable* pMT);
void SetClassInitError(MethodTable* pMT);
void EnsureDynamicClassIndex(DWORD dwID);
void AllocateDynamicClass(MethodTable *pMT);
void PopulateClass(MethodTable *pMT);
#ifdef DACCESS_COMPILE
void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
#endif
static DWORD OffsetOfDataBlob()
{
LIMITED_METHOD_CONTRACT;
return offsetof(DomainLocalModule, m_pDataBlob);
}
FORCEINLINE MethodTable * GetMethodTableFromClassDomainID(DWORD dwClassDomainID)
{
DWORD rid = (DWORD)(dwClassDomainID) + 1;
TypeHandle th = GetDomainAssembly()->GetModule()->LookupTypeDef(TokenFromRid(rid, mdtTypeDef));
_ASSERTE(!th.IsNull());
MethodTable * pMT = th.AsMethodTable();
PREFIX_ASSUME(pMT != NULL);
return pMT;
}
private:
friend void EmitFastGetSharedStaticBase(CPUSTUBLINKER *psl, CodeLabel *init, bool bCCtorCheck);
void SetClassFlags(MethodTable* pMT, DWORD dwFlags);
DWORD GetClassFlags(MethodTable* pMT, DWORD iClassIndex);
PTR_DomainAssembly m_pDomainAssembly;
VolatilePtr<DynamicClassInfo, PTR_DynamicClassInfo> m_pDynamicClassTable; // used for generics and reflection.emit in memory
Volatile<SIZE_T> m_aDynamicEntries; // number of entries in dynamic table
VolatilePtr<UMEntryThunk> m_pADThunkTable;
PTR_OBJECTREF m_pGCStatics; // Handle to GC statics of the module
// In addition to storing the ModuleIndex in the Module class, we also
// keep a copy of the ModuleIndex in the DomainLocalModule class. This
// allows the thread static JIT helpers to quickly convert a pointer to
// a DomainLocalModule into a ModuleIndex.
ModuleIndex m_ModuleIndex;
// Note that the static offset calculation in code:Module::BuildStaticsOffsets takes the offset m_pDataBlob
// into consideration for alignment so we do not need any padding to ensure that the start of the data blob is aligned
BYTE m_pDataBlob[0]; // First byte of the statics blob
// Layout of m_pDataBlob is:
// ClassInit bytes (hold flags for cctor run, cctor error, etc)
// Non GC Statics
public:
// The Module class need to be able to initialized ModuleIndex,
// so for now I will make it a friend..
friend class Module;
FORCEINLINE ModuleIndex GetModuleIndex()
{
LIMITED_METHOD_DAC_CONTRACT;
return m_ModuleIndex;
}
}; // struct DomainLocalModule
#define OFFSETOF__DomainLocalModule__m_pDataBlob_ (6 * TARGET_POINTER_SIZE)
#ifdef FEATURE_64BIT_ALIGNMENT
#define OFFSETOF__DomainLocalModule__NormalDynamicEntry__m_pDataBlob (TARGET_POINTER_SIZE /* m_pGCStatics */ + TARGET_POINTER_SIZE /* m_padding */)
#else
#define OFFSETOF__DomainLocalModule__NormalDynamicEntry__m_pDataBlob TARGET_POINTER_SIZE /* m_pGCStatics */
#endif
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// The pinned heap handle bucket class is used to contain handles allocated
// from an array contained in the pinned heap.
class PinnedHeapHandleBucket
{
public:
// Constructor and desctructor.
PinnedHeapHandleBucket(PinnedHeapHandleBucket *pNext, PTRARRAYREF pinnedHandleArrayObj, DWORD size, BaseDomain *pDomain);
~PinnedHeapHandleBucket();
// This returns the next bucket.
PinnedHeapHandleBucket *GetNext()
{
LIMITED_METHOD_CONTRACT;
return m_pNext;
}
// This returns the number of remaining handle slots.
DWORD GetNumRemainingHandles()
{
LIMITED_METHOD_CONTRACT;
return m_ArraySize - m_CurrentPos;
}
void ConsumeRemaining()
{
LIMITED_METHOD_CONTRACT;
m_CurrentPos = m_ArraySize;
}
OBJECTREF *TryAllocateEmbeddedFreeHandle();
// Allocate handles from the bucket.
OBJECTREF* AllocateHandles(DWORD nRequested);
OBJECTREF* CurrentPos()
{
LIMITED_METHOD_CONTRACT;
return m_pArrayDataPtr + m_CurrentPos;
}
void EnumStaticGCRefs(promote_func* fn, ScanContext* sc);
private:
PinnedHeapHandleBucket *m_pNext;
int m_ArraySize;
int m_CurrentPos;
int m_CurrentEmbeddedFreePos;
OBJECTHANDLE m_hndHandleArray;
OBJECTREF *m_pArrayDataPtr;
};
// The pinned heap handle table is used to allocate handles that are pointers
// to objects stored in an array in the pinned object heap.
class PinnedHeapHandleTable
{
public:
// Constructor and desctructor.
PinnedHeapHandleTable(BaseDomain *pDomain, DWORD InitialBucketSize);
~PinnedHeapHandleTable();
// Allocate handles from the pinned heap handle table.
OBJECTREF* AllocateHandles(DWORD nRequested);
// Release object handles allocated using AllocateHandles().
void ReleaseHandles(OBJECTREF *pObjRef, DWORD nReleased);
void EnumStaticGCRefs(promote_func* fn, ScanContext* sc);
private:
void ReleaseHandlesLocked(OBJECTREF *pObjRef, DWORD nReleased);
// The buckets of object handles.
// synchronized by m_Crst
PinnedHeapHandleBucket *m_pHead;
// We need to know the containing domain so we know where to allocate handles
BaseDomain *m_pDomain;
// The size of the PinnedHeapHandleBucket.
// synchronized by m_Crst
DWORD m_NextBucketSize;
// for finding and re-using embedded free items in the list
// these fields are synchronized by m_Crst
PinnedHeapHandleBucket *m_pFreeSearchHint;
DWORD m_cEmbeddedFree;
CrstExplicitInit m_Crst;
};
class PinnedHeapHandleBlockHolder;
void PinnedHeapHandleBlockHolder__StaticFree(PinnedHeapHandleBlockHolder*);
class PinnedHeapHandleBlockHolder:public Holder<PinnedHeapHandleBlockHolder*,DoNothing,PinnedHeapHandleBlockHolder__StaticFree>
{
PinnedHeapHandleTable* m_pTable;
DWORD m_Count;
OBJECTREF* m_Data;
public:
FORCEINLINE PinnedHeapHandleBlockHolder(PinnedHeapHandleTable* pOwner, DWORD nCount)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
m_Data = pOwner->AllocateHandles(nCount);
m_Count=nCount;
m_pTable=pOwner;
};
FORCEINLINE void FreeData()
{
WRAPPER_NO_CONTRACT;
for (DWORD i=0;i< m_Count;i++)
ClearObjectReference(m_Data+i);
m_pTable->ReleaseHandles(m_Data, m_Count);
};
FORCEINLINE OBJECTREF* operator[] (DWORD idx)
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(idx<m_Count);
return &(m_Data[idx]);
}
};
FORCEINLINE void PinnedHeapHandleBlockHolder__StaticFree(PinnedHeapHandleBlockHolder* pHolder)
{
WRAPPER_NO_CONTRACT;
pHolder->FreeData();
};
// The large heap handle bucket class is used to contain handles allocated
// from an array contained in the large heap.
class ThreadStaticHandleBucket
{
public:
// Constructor and desctructor.
ThreadStaticHandleBucket(ThreadStaticHandleBucket *pNext, DWORD Size, BaseDomain *pDomain);
~ThreadStaticHandleBucket();
// This returns the next bucket.
ThreadStaticHandleBucket *GetNext()
{
LIMITED_METHOD_CONTRACT;
return m_pNext;
}
// Allocate handles from the bucket.
OBJECTHANDLE GetHandles();
private:
ThreadStaticHandleBucket *m_pNext;
int m_ArraySize;
OBJECTHANDLE m_hndHandleArray;
};
// The large heap handle table is used to allocate handles that are pointers
// to objects stored in an array in the large object heap.
class ThreadStaticHandleTable
{
public:
// Constructor and desctructor.
ThreadStaticHandleTable(BaseDomain *pDomain);
~ThreadStaticHandleTable();
// Allocate handles from the large heap handle table.
OBJECTHANDLE AllocateHandles(DWORD nRequested);
private:
// The buckets of object handles.
ThreadStaticHandleBucket *m_pHead;
// We need to know the containing domain so we know where to allocate handles
BaseDomain *m_pDomain;
};
//--------------------------------------------------------------------------------------
// Base class for domains. It provides an abstract way of finding the first assembly and
// for creating assemblies in the domain. The system domain only has one assembly, it
// contains the classes that are logically shared between domains. All other domains can
// have multiple assemblies. Iteration is done be getting the first assembly and then
// calling the Next() method on the assembly.
//
// The system domain should be as small as possible, it includes object, exceptions, etc.
// which are the basic classes required to load other assemblies. All other classes
// should be loaded into the domain. Of coarse there is a trade off between loading the
// same classes multiple times, requiring all domains to load certain assemblies (working
// set) and being able to specify specific versions.
//
#define LOW_FREQUENCY_HEAP_RESERVE_SIZE (3 * GetOsPageSize())
#define LOW_FREQUENCY_HEAP_COMMIT_SIZE (1 * GetOsPageSize())
#define HIGH_FREQUENCY_HEAP_RESERVE_SIZE (10 * GetOsPageSize())
#define HIGH_FREQUENCY_HEAP_COMMIT_SIZE (1 * GetOsPageSize())
#define STUB_HEAP_RESERVE_SIZE (3 * GetOsPageSize())
#define STUB_HEAP_COMMIT_SIZE (1 * GetOsPageSize())
// --------------------------------------------------------------------------------
// PE File List lock - for creating list locks on PE files
// --------------------------------------------------------------------------------
class PEFileListLock : public ListLock
{
public:
#ifndef DACCESS_COMPILE
ListLockEntry *FindFileLock(PEAssembly *pPEAssembly)
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_FORBID_FAULT;
PRECONDITION(HasLock());
ListLockEntry *pEntry;
for (pEntry = m_pHead;
pEntry != NULL;
pEntry = pEntry->m_pNext)
{
if (((PEAssembly *)pEntry->m_data)->Equals(pPEAssembly))
{
return pEntry;
}
}
return NULL;
}
#endif // DACCESS_COMPILE
DEBUG_NOINLINE static void HolderEnter(PEFileListLock *pThis)
{
WRAPPER_NO_CONTRACT;
ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
pThis->Enter();
}
DEBUG_NOINLINE static void HolderLeave(PEFileListLock *pThis)
{
WRAPPER_NO_CONTRACT;
ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
pThis->Leave();
}
typedef Wrapper<PEFileListLock*, PEFileListLock::HolderEnter, PEFileListLock::HolderLeave> Holder;
};
typedef PEFileListLock::Holder PEFileListLockHolder;
// Loading infrastructure:
//
// a DomainAssembly is a file being loaded. Files are loaded in layers to enable loading in the
// presence of dependency loops.
//
// FileLoadLevel describes the various levels available. These are implemented slightly
// differently for assemblies and modules, but the basic structure is the same.
//
// LoadLock and FileLoadLock form the ListLock data structures for files. The FileLoadLock
// is specialized in that it allows taking a lock at a particular level. Basicall any
// thread may obtain the lock at a level at which the file has previously been loaded to, but
// only one thread may obtain the lock at its current level.
//
// The PendingLoadQueue is a per thread data structure which serves two purposes. First, it
// holds a "load limit" which automatically restricts the level of recursive loads to be
// one less than the current load which is preceding. This, together with the AppDomain
// LoadLock level behavior, will prevent any deadlocks from occurring due to circular
// dependencies. (Note that it is important that the loading logic understands this restriction,
// and any given level of loading must deal with the fact that any recursive loads will be partially
// unfulfilled in a specific way.)
//
// The second function is to queue up any unfulfilled load requests for the thread. These
// are then delivered immediately after the current load request is dealt with.
class FileLoadLock : public ListLockEntry
{
private:
FileLoadLevel m_level;
DomainAssembly *m_pDomainAssembly;
HRESULT m_cachedHR;
public:
static FileLoadLock *Create(PEFileListLock *pLock, PEAssembly *pPEAssembly, DomainAssembly *pDomainAssembly);
~FileLoadLock();
DomainAssembly *GetDomainAssembly();
FileLoadLevel GetLoadLevel();
// CanAcquire will return FALSE if Acquire will definitely not take the lock due
// to levels or deadlock.
// (Note that there is a race exiting from the function, where Acquire may end
// up not taking the lock anyway if another thread did work in the meantime.)
BOOL CanAcquire(FileLoadLevel targetLevel);
// Acquire will return FALSE and not take the lock if the file
// has already been loaded to the target level. Otherwise,
// it will return TRUE and take the lock.
//
// Note that the taker must release the lock via IncrementLoadLevel.
BOOL Acquire(FileLoadLevel targetLevel);
// CompleteLoadLevel can be called after Acquire returns true
// returns TRUE if it updated load level, FALSE if the level was set already
BOOL CompleteLoadLevel(FileLoadLevel level, BOOL success);
void SetError(Exception *ex);
void AddRef();
UINT32 Release() DAC_EMPTY_RET(0);
private:
FileLoadLock(PEFileListLock *pLock, PEAssembly *pPEAssembly, DomainAssembly *pDomainAssembly);
static void HolderLeave(FileLoadLock *pThis);
public:
typedef Wrapper<FileLoadLock *, DoNothing, FileLoadLock::HolderLeave> Holder;
};
typedef FileLoadLock::Holder FileLoadLockHolder;
#ifndef DACCESS_COMPILE
typedef ReleaseHolder<FileLoadLock> FileLoadLockRefHolder;
#endif // DACCESS_COMPILE
typedef ListLockBase<NativeCodeVersion> JitListLock;
typedef ListLockEntryBase<NativeCodeVersion> JitListLockEntry;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning (disable: 4324) //sometimes 64bit compilers complain about alignment
#endif
class LoadLevelLimiter
{
FileLoadLevel m_currentLevel;
LoadLevelLimiter* m_previousLimit;
BOOL m_bActive;
public:
LoadLevelLimiter()
: m_currentLevel(FILE_ACTIVE),
m_previousLimit(NULL),
m_bActive(FALSE)
{
LIMITED_METHOD_CONTRACT;
}
void Activate()
{
WRAPPER_NO_CONTRACT;
m_previousLimit= GetThread()->GetLoadLevelLimiter();
if(m_previousLimit)
m_currentLevel=m_previousLimit->GetLoadLevel();
GetThread()->SetLoadLevelLimiter(this);
m_bActive=TRUE;
}
void Deactivate()
{
WRAPPER_NO_CONTRACT;
if (m_bActive)
{
GetThread()->SetLoadLevelLimiter(m_previousLimit);
m_bActive=FALSE;
}
}
~LoadLevelLimiter()
{
WRAPPER_NO_CONTRACT;
// PendingLoadQueues are allocated on the stack during a load, and
// shared with all nested loads on the same thread.
// Make sure the thread pointer gets reset after the
// top level queue goes out of scope.
if(m_bActive)
{
Deactivate();
}
}
FileLoadLevel GetLoadLevel()
{
LIMITED_METHOD_CONTRACT;
return m_currentLevel;
}
void SetLoadLevel(FileLoadLevel level)
{
LIMITED_METHOD_CONTRACT;
m_currentLevel = level;
}
};
#ifdef _MSC_VER
#pragma warning (pop) //4324
#endif
#define OVERRIDE_LOAD_LEVEL_LIMIT(newLimit) \
LoadLevelLimiter __newLimit; \
__newLimit.Activate(); \
__newLimit.SetLoadLevel(newLimit);
// A BaseDomain much basic information in a code:AppDomain including
//
// * code:#AppdomainHeaps - Heaps for any data structures that will be freed on appdomain unload
//
class BaseDomain
{
friend class Assembly;
friend class AssemblySpec;
friend class AppDomain;
friend class AppDomainNative;
VPTR_BASE_VTABLE_CLASS(BaseDomain)
VPTR_UNIQUE(VPTR_UNIQUE_BaseDomain)
public:
class AssemblyIterator;
friend class AssemblyIterator;
// Static initialization.
static void Attach();
//****************************************************************************************
//
// Initialization/shutdown routines for every instance of an BaseDomain.
BaseDomain();
virtual ~BaseDomain() {}
void Init();
void Stop();
virtual BOOL IsAppDomain() { LIMITED_METHOD_DAC_CONTRACT; return FALSE; }
PTR_LoaderAllocator GetLoaderAllocator();
virtual PTR_AppDomain AsAppDomain()
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(!"Not an AppDomain");
return NULL;
}
#ifdef FEATURE_COMINTEROP
MngStdInterfacesInfo * GetMngStdInterfacesInfo()
{
LIMITED_METHOD_CONTRACT;
return m_pMngStdInterfacesInfo;
}
#endif // FEATURE_COMINTEROP
#ifdef _DEBUG
BOOL OwnDomainLocalBlockLock()
{
WRAPPER_NO_CONTRACT;
return m_DomainLocalBlockCrst.OwnedByCurrentThread();
}
#endif
//****************************************************************************************
// Get the class init lock. The method is limited to friends because inappropriate use
// will cause deadlocks in the system
ListLock* GetClassInitLock()
{
LIMITED_METHOD_CONTRACT;
return &m_ClassInitLock;
}
JitListLock* GetJitLock()
{
LIMITED_METHOD_CONTRACT;
return &m_JITLock;
}
ListLock* GetILStubGenLock()
{
LIMITED_METHOD_CONTRACT;
return &m_ILStubGenLock;
}
ListLock* GetNativeTypeLoadLock()
{
LIMITED_METHOD_CONTRACT;
return &m_NativeTypeLoadLock;
}
STRINGREF *IsStringInterned(STRINGREF *pString);
STRINGREF *GetOrInternString(STRINGREF *pString);
// Returns an array of OBJECTREF* that can be used to store domain specific data.
// Statics and reflection info (Types, MemberInfo,..) are stored this way
// If ppLazyAllocate != 0, allocation will only take place if *ppLazyAllocate != 0 (and the allocation
// will be properly serialized)
OBJECTREF *AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF** ppLazyAllocate = NULL);
//****************************************************************************************
// Handles
#if !defined(DACCESS_COMPILE)
IGCHandleStore* GetHandleStore()
{
LIMITED_METHOD_CONTRACT;
return m_handleStore;
}
OBJECTHANDLE CreateTypedHandle(OBJECTREF object, HandleType type)