-
Notifications
You must be signed in to change notification settings - Fork 5k
/
Copy pathappdomain.hpp
1970 lines (1613 loc) · 59.5 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 SystemDomain;
class AppDomain;
class GlobalStringLiteralMap;
class StringLiteralMap;
class FrozenObjectHeapManager;
class DomainAssembly;
class TypeEquivalenceHashTable;
#ifdef FEATURE_COMINTEROP
class RCWCache;
#endif //FEATURE_COMINTEROP
#ifdef FEATURE_COMWRAPPERS
class RCWRefCache;
#endif // FEATURE_COMWRAPPERS
// 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);
~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(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;
// 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();
};
//--------------------------------------------------------------------------------------
// 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 (8 * GetOsPageSize())
#define HIGH_FREQUENCY_HEAP_COMMIT_SIZE (1 * GetOsPageSize())
#define STUB_HEAP_RESERVE_SIZE (3 * GetOsPageSize())
#define STUB_HEAP_COMMIT_SIZE (1 * GetOsPageSize())
#define STATIC_FIELD_HEAP_RESERVE_SIZE (2 * GetOsPageSize())
#define STATIC_FIELD_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;
Assembly* m_pAssembly;
HRESULT m_cachedHR;
public:
static FileLoadLock *Create(PEFileListLock *pLock, PEAssembly *pPEAssembly, Assembly *pAssembly);
~FileLoadLock();
Assembly *GetAssembly();
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, Assembly *pAssembly);
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;
class LoadLevelLimiter final
{
static thread_local LoadLevelLimiter* t_currentLoadLevelLimiter;
FileLoadLevel m_currentLevel;
LoadLevelLimiter* m_previousLimit;
bool m_bActive;
public:
LoadLevelLimiter()
: m_currentLevel(FILE_ACTIVE),
m_previousLimit(nullptr),
m_bActive(false)
{
LIMITED_METHOD_CONTRACT;
}
void Activate()
{
WRAPPER_NO_CONTRACT;
m_previousLimit = t_currentLoadLevelLimiter;
if (m_previousLimit)
m_currentLevel = m_previousLimit->GetLoadLevel();
t_currentLoadLevelLimiter = this;
m_bActive = true;
}
void Deactivate()
{
WRAPPER_NO_CONTRACT;
if (m_bActive)
{
t_currentLoadLevelLimiter = 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;
}
static LoadLevelLimiter* GetCurrent()
{
LIMITED_METHOD_CONTRACT;
return t_currentLoadLevelLimiter;
}
};
#define OVERRIDE_LOAD_LEVEL_LIMIT(newLimit) \
LoadLevelLimiter __newLimit; \
__newLimit.Activate(); \
__newLimit.SetLoadLevel(newLimit);
enum
{
ATTACH_ASSEMBLY_LOAD = 0x1,
ATTACH_MODULE_LOAD = 0x2,
ATTACH_CLASS_LOAD = 0x4,
ATTACH_ALL = 0x7
};
// This filters the output of IterateAssemblies. This ought to be declared more locally
// but it would result in really verbose callsites.
//
// Assemblies can be categorized by their load status (loaded, loading, or loaded just
// enough that they would be made available to profilers)
// Independently, they can also be categorized as execution or introspection.
//
// An assembly will be included in the results of IterateAssemblies only if
// the appropriate bit is set for *both* characterizations.
//
// The flags can be combined so if you want all loaded assemblies, you must specify:
//
/// kIncludeLoaded|kIncludeExecution
enum AssemblyIterationFlags
{
// load status flags
kIncludeLoaded = 0x00000001, // include assemblies that are already loaded
// (m_level >= code:FILE_LOAD_DELIVER_EVENTS)
kIncludeLoading = 0x00000002, // include assemblies that are still in the process of loading
// (all m_level values)
kIncludeAvailableToProfilers
= 0x00000020, // include assemblies available to profilers
// See comment at code:DomainAssembly::IsAvailableToProfilers
// Execution / introspection flags
kIncludeExecution = 0x00000004, // include assemblies that are loaded for execution only
kIncludeFailedToLoad = 0x00000010, // include assemblies that failed to load
// Collectible assemblies flags
kExcludeCollectible = 0x00000040, // Exclude all collectible assemblies
kIncludeCollected = 0x00000080,
// Include assemblies which were collected and cannot be referenced anymore. Such assemblies are not
// AddRef-ed. Any manipulation with them should be protected by code:GetAssemblyListLock.
// Should be used only by code:LoaderAllocator::GCLoaderAllocators.
}; // enum AssemblyIterationFlags
//---------------------------------------------------------------------------------------
//
// Base class for holder code:CollectibleAssemblyHolder (see code:HolderBase).
// Manages AddRef/Release for collectible assemblies. It is no-op for 'normal' non-collectible assemblies.
//
// Each type of type parameter needs 2 methods implemented:
// code:CollectibleAssemblyHolderBase::GetLoaderAllocator
// code:CollectibleAssemblyHolderBase::IsCollectible
//
template<typename _Type>
class CollectibleAssemblyHolderBase
{
protected:
_Type m_value;
public:
CollectibleAssemblyHolderBase(const _Type & value = NULL)
{
LIMITED_METHOD_CONTRACT;
m_value = value;
}
void DoAcquire()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
// We don't need to keep the assembly alive in DAC - see code:#CAH_DAC
#ifndef DACCESS_COMPILE
if (this->IsCollectible(m_value))
{
LoaderAllocator * pLoaderAllocator = GetLoaderAllocator(m_value);
pLoaderAllocator->AddReference();
}
#endif //!DACCESS_COMPILE
}
void DoRelease()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
#ifndef DACCESS_COMPILE
if (this->IsCollectible(m_value))
{
LoaderAllocator * pLoaderAllocator = GetLoaderAllocator(m_value);
pLoaderAllocator->Release();
}
#endif //!DACCESS_COMPILE
}
private:
LoaderAllocator * GetLoaderAllocator(DomainAssembly * pDomainAssembly)
{
WRAPPER_NO_CONTRACT;
return pDomainAssembly->GetAssembly()->GetLoaderAllocator();
}
BOOL IsCollectible(DomainAssembly * pDomainAssembly)
{
WRAPPER_NO_CONTRACT;
return pDomainAssembly->GetAssembly()->IsCollectible();
}
LoaderAllocator * GetLoaderAllocator(Assembly * pAssembly)
{
WRAPPER_NO_CONTRACT;
return pAssembly->GetLoaderAllocator();
}
BOOL IsCollectible(Assembly * pAssembly)
{
WRAPPER_NO_CONTRACT;
return pAssembly->IsCollectible();
}
}; // class CollectibleAssemblyHolderBase<>
//---------------------------------------------------------------------------------------
//
// Holder of assembly reference which keeps collectible assembly alive while the holder is valid.
//
// Collectible assembly can be collected at any point when GC happens. Almost instantly all native data
// structures of the assembly (e.g. code:DomainAssembly, code:Assembly) could be deallocated.
// Therefore any usage of (collectible) assembly data structures from native world, has to prevent the
// deallocation by increasing ref-count on the assembly / associated loader allocator.
//
// #CAH_DAC
// In DAC we don't AddRef/Release as the assembly doesn't have to be kept alive: The process is stopped when
// DAC is used and therefore the assembly cannot just disappear.
//
template<typename _Type>
class CollectibleAssemblyHolder : public BaseWrapper<_Type, CollectibleAssemblyHolderBase<_Type> >
{
public:
FORCEINLINE
CollectibleAssemblyHolder(const _Type & value = NULL, BOOL fTake = TRUE)
: BaseWrapper<_Type, CollectibleAssemblyHolderBase<_Type> >(value, fTake)
{
STATIC_CONTRACT_WRAPPER;
}
FORCEINLINE
CollectibleAssemblyHolder &
operator=(const _Type & value)
{
STATIC_CONTRACT_WRAPPER;
BaseWrapper<_Type, CollectibleAssemblyHolderBase<_Type> >::operator=(value);
return *this;
}
// Operator & is overloaded in parent, therefore we have to get to 'this' pointer explicitly.
FORCEINLINE
CollectibleAssemblyHolder<_Type> *
This()
{
LIMITED_METHOD_CONTRACT;
return this;
}
}; // class CollectibleAssemblyHolder<>
//---------------------------------------------------------------------------------------
//
// Stores binding information about failed assembly loads for DAC
//
struct FailedAssembly {
SString displayName;
HRESULT error;
void Initialize(AssemblySpec *pSpec, Exception *ex)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
displayName.SetASCII(pSpec->GetName());
error = ex->GetHR();
//
// Determine the binding context assembly would have been in.
// If the parent has been set, use its binding context.
// If the parent hasn't been set but the code base has, use LoadFrom.
// Otherwise, use the default.
//
}
};
const DWORD DefaultADID = 1;
// An Appdomain is the managed equivalent of a process. It is an isolation unit (conceptually you don't
// have pointers directly from one appdomain to another, but rather go through remoting proxies). It is
// also a unit of unloading.
//
// Threads are always running in the context of a particular AppDomain. See
// file:threads.h#RuntimeThreadLocals for more details.
//
// * code:AppDomain.m_Assemblies - is a list of code:Assembly in the appdomain
//
class AppDomain final
{
friend struct _DacGlobals;
friend class ClrDataAccess;
public:
#ifndef DACCESS_COMPILE
AppDomain();
~AppDomain();
static void Create();
#endif
//-----------------------------------------------------------------------------------------------------------------
// Convenience wrapper for ::GetAppDomain to provide better encapsulation.
static PTR_AppDomain GetCurrentDomain()
{ return m_pTheAppDomain; }
//-----------------------------------------------------------------------------------------------------------------
// Initializes an AppDomain. (this functions is not called from the SystemDomain)
void Init();
bool MustForceTrivialWaitOperations();
void SetForceTrivialWaitOperations();
//****************************************************************************************
//
// Stop deletes all the assemblies but does not remove other resources like
// the critical sections
void Stop();
// final assembly cleanup
void ShutdownFreeLoaderAllocators();
PTR_LoaderAllocator GetLoaderAllocator();
STRINGREF *IsStringInterned(STRINGREF *pString);
STRINGREF *GetOrInternString(STRINGREF *pString);
OBJECTREF GetRawExposedObject() { LIMITED_METHOD_CONTRACT; return NULL; }
OBJECTHANDLE GetRawExposedObjectHandleForDebugger() { LIMITED_METHOD_DAC_CONTRACT; return (OBJECTHANDLE)NULL; }
#ifndef DACCESS_COMPILE
PTR_NativeImage GetNativeImage(LPCUTF8 compositeFileName);
PTR_NativeImage SetNativeImage(LPCUTF8 compositeFileName, PTR_NativeImage pNativeImage);
#endif // DACCESS_COMPILE
JitListLock* GetJitLock()
{
LIMITED_METHOD_CONTRACT;
return &m_JITLock;
}
ListLock* GetClassInitLock()
{
LIMITED_METHOD_CONTRACT;
return &m_ClassInitLock;
}
ListLock* GetILStubGenLock()
{
LIMITED_METHOD_CONTRACT;
return &m_ILStubGenLock;
}
ListLock* GetNativeTypeLoadLock()
{
LIMITED_METHOD_CONTRACT;
return &m_NativeTypeLoadLock;
}
CrstExplicitInit* GetGenericDictionaryExpansionLock()
{
LIMITED_METHOD_CONTRACT;
return &m_crstGenericDictionaryExpansionLock;
}
CrstExplicitInit* GetLoaderAllocatorReferencesLock()
{
LIMITED_METHOD_CONTRACT;
return &m_crstLoaderAllocatorReferences;
}
CrstExplicitInit* GetMethodTableExposedClassObjectLock()
{
LIMITED_METHOD_CONTRACT;
return &m_MethodTableExposedClassObjectCrst;
}
private:
JitListLock m_JITLock;
ListLock m_ClassInitLock;
ListLock m_ILStubGenLock;
ListLock m_NativeTypeLoadLock;
CrstExplicitInit m_crstGenericDictionaryExpansionLock;
// Used to protect the reference lists in the collectible loader allocators attached to the app domain
CrstExplicitInit m_crstLoaderAllocatorReferences;
// Protects allocation of slot IDs for thread statics
CrstExplicitInit m_MethodTableExposedClassObjectCrst;
#if !defined(DACCESS_COMPILE)
public: // Handles
IGCHandleStore* GetHandleStore()
{
LIMITED_METHOD_CONTRACT;
return m_handleStore;
}
OBJECTHANDLE CreateTypedHandle(OBJECTREF object, HandleType type)
{
WRAPPER_NO_CONTRACT;
return ::CreateHandleCommon(m_handleStore, object, type);
}
OBJECTHANDLE CreateHandle(OBJECTREF object)
{
WRAPPER_NO_CONTRACT;
CONDITIONAL_CONTRACT_VIOLATION(ModeViolation, object == NULL)
return ::CreateHandle(m_handleStore, object);
}
OBJECTHANDLE CreateLongWeakHandle(OBJECTREF object)
{
WRAPPER_NO_CONTRACT;
CONDITIONAL_CONTRACT_VIOLATION(ModeViolation, object == NULL)
return ::CreateLongWeakHandle(m_handleStore, object);
}
OBJECTHANDLE CreateStrongHandle(OBJECTREF object)
{
WRAPPER_NO_CONTRACT;
return ::CreateStrongHandle(m_handleStore, object);
}
OBJECTHANDLE CreatePinningHandle(OBJECTREF object)
{
WRAPPER_NO_CONTRACT;
return ::CreatePinningHandle(m_handleStore, object);
}
OBJECTHANDLE CreateWeakInteriorHandle(OBJECTREF object, void* pInteriorPointerLocation)
{
WRAPPER_NO_CONTRACT;
return ::CreateWeakInteriorHandle(m_handleStore, object, pInteriorPointerLocation);
}
#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
OBJECTHANDLE CreateRefcountedHandle(OBJECTREF object)
{
WRAPPER_NO_CONTRACT;
return ::CreateRefcountedHandle(m_handleStore, object);
}
#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
OBJECTHANDLE CreateDependentHandle(OBJECTREF primary, OBJECTREF secondary)
{
WRAPPER_NO_CONTRACT;
return ::CreateDependentHandle(m_handleStore, primary, secondary);
}
#endif // DACCESS_COMPILE
private:
IGCHandleStore* m_handleStore;
protected:
// Multi-thread safe access to the list of assemblies
class DomainAssemblyList
{
private:
ArrayList m_array;
#ifdef _DEBUG
AppDomain * dbg_m_pAppDomain;
public:
void Debug_SetAppDomain(AppDomain * pAppDomain)
{
dbg_m_pAppDomain = pAppDomain;
}
#endif //_DEBUG
public:
bool IsEmpty()
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
} CONTRACTL_END;
// This function can be reliably called without taking the lock, because the first assembly
// added to the arraylist is non-collectible, and the ArrayList itself allows lockless read access
return (m_array.GetCount() == 0);
}
void Clear(AppDomain * pAppDomain)
{
CONTRACTL {
NOTHROW;
WRAPPER(GC_TRIGGERS); // Triggers only in MODE_COOPERATIVE (by taking the lock)
MODE_ANY;
} CONTRACTL_END;
_ASSERTE(dbg_m_pAppDomain == pAppDomain);
CrstHolder ch(pAppDomain->GetAssemblyListLock());
m_array.Clear();
}
DWORD GetCount(AppDomain * pAppDomain)
{
CONTRACTL {
NOTHROW;
WRAPPER(GC_TRIGGERS); // Triggers only in MODE_COOPERATIVE (by taking the lock)
MODE_ANY;
} CONTRACTL_END;
_ASSERTE(dbg_m_pAppDomain == pAppDomain);
CrstHolder ch(pAppDomain->GetAssemblyListLock());
return GetCount_Unlocked();
}
DWORD GetCount_Unlocked()
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
} CONTRACTL_END;
#ifndef DACCESS_COMPILE
_ASSERTE(dbg_m_pAppDomain->GetAssemblyListLock()->OwnedByCurrentThread());
#endif
// code:Append_Unlock guarantees that we do not have more than MAXDWORD items
return m_array.GetCount();
}
void Get(AppDomain * pAppDomain, DWORD index, CollectibleAssemblyHolder<DomainAssembly *> * pAssemblyHolder)
{
CONTRACTL {
NOTHROW;
WRAPPER(GC_TRIGGERS); // Triggers only in MODE_COOPERATIVE (by taking the lock)
MODE_ANY;
} CONTRACTL_END;
_ASSERTE(dbg_m_pAppDomain == pAppDomain);
CrstHolder ch(pAppDomain->GetAssemblyListLock());
Get_Unlocked(index, pAssemblyHolder);
}
void Get_Unlocked(DWORD index, CollectibleAssemblyHolder<DomainAssembly *> * pAssemblyHolder)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
} CONTRACTL_END;
_ASSERTE(dbg_m_pAppDomain->GetAssemblyListLock()->OwnedByCurrentThread());
*pAssemblyHolder = dac_cast<PTR_DomainAssembly>(m_array.Get(index));
}
// Doesn't lock the assembly list (caller has to hold the lock already).
// Doesn't AddRef the returned assembly (if collectible).
DomainAssembly * Get_UnlockedNoReference(DWORD index)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
SUPPORTS_DAC;
} CONTRACTL_END;
#ifndef DACCESS_COMPILE
_ASSERTE(dbg_m_pAppDomain->GetAssemblyListLock()->OwnedByCurrentThread());
#endif
return dac_cast<PTR_DomainAssembly>(m_array.Get(index));
}
#ifndef DACCESS_COMPILE
void Set(AppDomain * pAppDomain, DWORD index, DomainAssembly * pAssembly)
{
CONTRACTL {
NOTHROW;
WRAPPER(GC_TRIGGERS); // Triggers only in MODE_COOPERATIVE (by taking the lock)
MODE_ANY;
} CONTRACTL_END;
_ASSERTE(dbg_m_pAppDomain == pAppDomain);
CrstHolder ch(pAppDomain->GetAssemblyListLock());
return Set_Unlocked(index, pAssembly);
}
void Set_Unlocked(DWORD index, DomainAssembly * pAssembly)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
} CONTRACTL_END;
_ASSERTE(dbg_m_pAppDomain->GetAssemblyListLock()->OwnedByCurrentThread());
m_array.Set(index, pAssembly);
}
HRESULT Append_Unlocked(DomainAssembly * pAssembly)
{
CONTRACTL {
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
} CONTRACTL_END;
_ASSERTE(dbg_m_pAppDomain->GetAssemblyListLock()->OwnedByCurrentThread());
return m_array.Append(pAssembly);
}
#else //DACCESS_COMPILE
void
EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
{
SUPPORTS_DAC;
m_array.EnumMemoryRegions(flags);
}
#endif // DACCESS_COMPILE
// Should be used only by code:AssemblyIterator::Create
ArrayList::Iterator GetArrayListIterator()
{
return m_array.Iterate();
}
friend struct cdac_data<AppDomain>;
}; // class DomainAssemblyList
// Conceptually a list of code:Assembly structures, protected by lock code:GetAssemblyListLock
DomainAssemblyList m_Assemblies;
public:
// Note that this lock switches thread into GC_NOTRIGGER region as GC can take it too.
CrstExplicitInit * GetAssemblyListLock()
{
LIMITED_METHOD_CONTRACT;
return &m_crstAssemblyList;
}
private:
// Used to protect the assembly list. Taken also by GC or debugger thread, therefore we have to avoid
// triggering GC while holding this lock (by switching the thread to GC_NOTRIGGER while it is held).
CrstExplicitInit m_crstAssemblyList;
public:
class AssemblyIterator
{
// AppDomain context with the assembly list
AppDomain * m_pAppDomain;
ArrayList::Iterator m_Iterator;
AssemblyIterationFlags m_assemblyIterationFlags;
public:
BOOL Next(CollectibleAssemblyHolder<Assembly *> * pAssemblyHolder);
// Note: Does not lock the assembly list, but AddRefs collectible assemblies' loader allocator.
BOOL Next_Unlocked(CollectibleAssemblyHolder<Assembly *> * pAssemblyHolder);
private: