// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. /*============================================================ ** ** 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 "domainfile.h" #include "objectlist.h" #include "fptrstubs.h" #include "ilstubcache.h" #include "testhookmgr.h" #include "gcheaputilities.h" #include "gchandleutilities.h" #include "../binder/inc/applicationcontext.hpp" #include "rejit.h" #ifdef FEATURE_MULTICOREJIT #include "multicorejit.h" #endif #ifdef FEATURE_COMINTEROP #include "clrprivbinderwinrt.h" #include "..\md\winmd\inc\adapter.h" #include "winrttypenameconverter.h" #endif // FEATURE_COMINTEROP #include "appxutil.h" #ifdef FEATURE_TIERED_COMPILATION #include "tieredcompilation.h" #include "callcounter.h" #endif #include "codeversion.h" class BaseDomain; class SystemDomain; class SharedDomain; class AppDomain; class CompilationDomain; class AppDomainEnum; class AssemblySink; class EEMarshalingData; class Context; class GlobalStringLiteralMap; class StringLiteralMap; class MngStdInterfacesInfo; class DomainModule; class DomainAssembly; struct InteropMethodTableData; class LoadLevelLimiter; class UMEntryThunkCache; class TypeEquivalenceHashTable; class StringArrayList; extern INT64 g_PauseTime; // Total time in millisecond the CLR has been paused #ifdef FEATURE_COMINTEROP class ComCallWrapperCache; struct SimpleComCallWrapper; class RCWRefCache; // This enum is used to specify whether user want COM or remoting enum COMorRemotingFlag { COMorRemoting_NotInitialized = 0, COMorRemoting_COM = 1, // COM will be used both cross-domain and cross-runtime COMorRemoting_Remoting = 2, // Remoting will be used cross-domain; cross-runtime will use Remoting only if it looks like it's expected (default) COMorRemoting_LegacyMode = 3 // Remoting will be used both cross-domain and cross-runtime }; #endif // FEATURE_COMINTEROP #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4200) // Disable zero-sized array warning #endif GPTR_DECL(IdDispenser, g_pModuleIndexDispenser); // This enum is aligned to System.ExceptionCatcherType. enum ExceptionCatcher { ExceptionCatcher_ManagedCode = 0, ExceptionCatcher_AppDomainTransition = 1, ExceptionCatcher_COMInterop = 2, }; // 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<(dynamicClassInfoParam);\ DomainLocalModule::PTR_DynamicEntry pDynamicEntry = dac_cast((DomainLocalModule::DynamicEntry*)dynamicClassInfo->m_pDynamicEntry.Load()); \ if ((dynamicClassInfo->m_dwFlags) & ClassInitFlags::COLLECTIBLE_FLAG) \ {\ PTRARRAYREF objArray;\ objArray = (PTRARRAYREF)pLoaderAllocator->GetHandleValueFastCannotFailType2( \ (dac_cast(pDynamicEntry))->m_hGCStatics);\ *(pGCStatics) = dac_cast(PTR_READ(PTR_TO_TADDR(OBJECTREFToObject( objArray )) + offsetof(PtrArray, m_Array), objArray->GetNumComponents() * sizeof(void*))) ;\ }\ else\ {\ *(pGCStatics) = (dac_cast(pDynamicEntry))->GetGCStaticsBasePointer();\ }\ }\ #define GET_DYNAMICENTRY_NONGCSTATICS_BASEPOINTER(pLoaderAllocator, dynamicClassInfoParam, pNonGCStatics) \ {\ DomainLocalModule::PTR_DynamicClassInfo dynamicClassInfo = dac_cast(dynamicClassInfoParam);\ DomainLocalModule::PTR_DynamicEntry pDynamicEntry = dac_cast((DomainLocalModule::DynamicEntry*)(dynamicClassInfo)->m_pDynamicEntry.Load()); \ if (((dynamicClassInfo)->m_dwFlags) & ClassInitFlags::COLLECTIBLE_FLAG) \ {\ if ((dac_cast(pDynamicEntry))->m_hNonGCStatics != 0) \ { \ U1ARRAYREF objArray;\ objArray = (U1ARRAYREF)pLoaderAllocator->GetHandleValueFastCannotFailType2( \ (dac_cast(pDynamicEntry))->m_hNonGCStatics);\ *(pNonGCStatics) = dac_cast(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(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(m_pGCStatics); } inline PTR_BYTE GetNonGCStaticsBasePointer() { LIMITED_METHOD_CONTRACT SUPPORTS_DAC; return dac_cast(this); } }; typedef DPTR(NormalDynamicEntry) PTR_NormalDynamicEntry; struct DynamicClassInfo { VolatilePtr m_pDynamicEntry; Volatile 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(this); } inline PTR_BYTE GetPrecomputedStaticsClassData() { LIMITED_METHOD_CONTRACT return dac_cast(this) + offsetof(DomainLocalModule, m_pDataBlob); } static SIZE_T GetOffsetOfDataBlob() { return offsetof(DomainLocalModule, m_pDataBlob); } static SIZE_T GetOffsetOfGCStaticPointer() { return offsetof(DomainLocalModule, m_pGCStatics); } inline DomainFile* GetDomainFile() { LIMITED_METHOD_CONTRACT SUPPORTS_DAC; return m_pDomainFile; } #ifndef DACCESS_COMPILE inline void SetDomainFile(DomainFile* pDomainFile) { LIMITED_METHOD_CONTRACT m_pDomainFile = pDomainFile; } #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(GetDomainFile()->GetModule() == pMT->GetModuleForStatics()); return GetDynamicEntryGCStaticsBasePointer(pMT->GetModuleDynamicEntryID(), pMT->GetLoaderAllocator()); } else { return dac_cast(m_pGCStatics); } } inline PTR_BYTE GetNonGCStaticsBasePointer(MethodTable * pMT) { WRAPPER_NO_CONTRACT SUPPORTS_DAC; if (pMT->IsDynamicStatics()) { _ASSERTE(GetDomainFile()->GetModule() == pMT->GetModuleForStatics()); return GetDynamicEntryNonGCStaticsBasePointer(pMT->GetModuleDynamicEntryID(), pMT->GetLoaderAllocator()); } else { return dac_cast(this); } } inline DynamicClassInfo* GetDynamicClassInfo(DWORD n) { LIMITED_METHOD_CONTRACT SUPPORTS_DAC; _ASSERTE(m_pDynamicClassTable.Load() && m_aDynamicEntries > n); dac_cast(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; SO_TOLERANT; 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; SO_TOLERANT; 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 = GetDomainFile()->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_DomainFile m_pDomainFile; VolatilePtr m_pDynamicClassTable; // used for generics and reflection.emit in memory Volatile m_aDynamicEntries; // number of entries in dynamic table VolatilePtr 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 typedef DPTR(class DomainLocalBlock) PTR_DomainLocalBlock; class DomainLocalBlock { friend class ClrDataAccess; friend class CheckAsmOffsets; private: PTR_AppDomain m_pDomain; DPTR(PTR_DomainLocalModule) m_pModuleSlots; SIZE_T m_aModuleIndices; // Module entries the shared block has allocated public: // used by code generators static SIZE_T GetOffsetOfModuleSlotsPointer() { return offsetof(DomainLocalBlock, m_pModuleSlots);} public: #ifndef DACCESS_COMPILE DomainLocalBlock() : m_pDomain(NULL), m_pModuleSlots(NULL), m_aModuleIndices(0) {} void EnsureModuleIndex(ModuleIndex index); void Init(AppDomain *pDomain) { LIMITED_METHOD_CONTRACT; m_pDomain = pDomain; } #endif void SetModuleSlot(ModuleIndex index, PTR_DomainLocalModule pLocalModule); FORCEINLINE PTR_DomainLocalModule GetModuleSlot(ModuleIndex index) { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; _ASSERTE(index.m_dwIndex < m_aModuleIndices); return m_pModuleSlots[index.m_dwIndex]; } inline PTR_DomainLocalModule GetModuleSlot(MethodTable* pMT) { WRAPPER_NO_CONTRACT; return GetModuleSlot(pMT->GetModuleForStatics()->GetModuleIndex()); } DomainFile* TryGetDomainFile(ModuleIndex index) { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; // the publishing of m_aModuleIndices and m_pModuleSlots is dependent // on the order of accesses; we must ensure that we read from m_aModuleIndices // before m_pModuleSlots. if (index.m_dwIndex < m_aModuleIndices) { MemoryBarrier(); if (m_pModuleSlots[index.m_dwIndex]) { return m_pModuleSlots[index.m_dwIndex]->GetDomainFile(); } } return NULL; } DomainFile* GetDomainFile(SIZE_T ModuleID) { WRAPPER_NO_CONTRACT; ModuleIndex index = Module::IDToIndex(ModuleID); _ASSERTE(index.m_dwIndex < m_aModuleIndices); return m_pModuleSlots[index.m_dwIndex]->GetDomainFile(); } #ifndef DACCESS_COMPILE void SetDomainFile(ModuleIndex index, DomainFile* pDomainFile) { WRAPPER_NO_CONTRACT; _ASSERTE(index.m_dwIndex < m_aModuleIndices); m_pModuleSlots[index.m_dwIndex]->SetDomainFile(pDomainFile); } #endif #ifdef DACCESS_COMPILE void EnumMemoryRegions(CLRDataEnumMemoryFlags flags); #endif private: // // Low level routines to get & set class entries // }; #ifdef _MSC_VER #pragma warning(pop) #endif // The large heap handle bucket class is used to contain handles allocated // from an array contained in the large heap. class LargeHeapHandleBucket { public: // Constructor and desctructor. LargeHeapHandleBucket(LargeHeapHandleBucket *pNext, DWORD Size, BaseDomain *pDomain, BOOL bCrossAD = FALSE); ~LargeHeapHandleBucket(); // This returns the next bucket. LargeHeapHandleBucket *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; } private: LargeHeapHandleBucket *m_pNext; int m_ArraySize; int m_CurrentPos; int m_CurrentEmbeddedFreePos; OBJECTHANDLE m_hndHandleArray; OBJECTREF *m_pArrayDataPtr; }; // 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 LargeHeapHandleTable { public: // Constructor and desctructor. LargeHeapHandleTable(BaseDomain *pDomain, DWORD InitialBucketSize); ~LargeHeapHandleTable(); // Allocate handles from the large heap handle table. OBJECTREF* AllocateHandles(DWORD nRequested, BOOL bCrossAD = FALSE); // Release object handles allocated using AllocateHandles(). void ReleaseHandles(OBJECTREF *pObjRef, DWORD nReleased); private: // The buckets of object handles. LargeHeapHandleBucket *m_pHead; // We need to know the containing domain so we know where to allocate handles BaseDomain *m_pDomain; // The size of the LargeHeapHandleBuckets. DWORD m_NextBucketSize; // for finding and re-using embedded free items in the list LargeHeapHandleBucket *m_pFreeSearchHint; DWORD m_cEmbeddedFree; #ifdef _DEBUG // these functions are present to enforce that there is a locking mechanism in place // for each LargeHeapHandleTable even though the code itself does not do the locking // you must tell the table which lock you intend to use and it will verify that it has // in fact been taken before performing any operations public: void RegisterCrstDebug(CrstBase *pCrst) { LIMITED_METHOD_CONTRACT; // this function must be called exactly once _ASSERTE(pCrst != NULL); _ASSERTE(m_pCrstDebug == NULL); m_pCrstDebug = pCrst; } private: // we will assert that this Crst is held before using the object CrstBase *m_pCrstDebug; #endif }; class LargeHeapHandleBlockHolder; void LargeHeapHandleBlockHolder__StaticFree(LargeHeapHandleBlockHolder*); class LargeHeapHandleBlockHolder:public Holder { LargeHeapHandleTable* m_pTable; DWORD m_Count; OBJECTREF* m_Data; public: FORCEINLINE LargeHeapHandleBlockHolder(LargeHeapHandleTable* pOwner, DWORD nCount) { WRAPPER_NO_CONTRACT; 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(idxFreeData(); }; // 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 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(PEFile *pFile) { 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 (((PEFile *)pEntry->m_data)->Equals(pFile)) { return pEntry; } } return NULL; } #endif // DACCESS_COMPILE DEBUG_NOINLINE static void HolderEnter(PEFileListLock *pThis) PUB { WRAPPER_NO_CONTRACT; ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT; pThis->Enter(); } DEBUG_NOINLINE static void HolderLeave(PEFileListLock *pThis) PUB { WRAPPER_NO_CONTRACT; ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT; pThis->Leave(); } typedef Wrapper Holder; }; typedef PEFileListLock::Holder PEFileListLockHolder; // Loading infrastructure: // // a DomainFile 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 occuring 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; DomainFile *m_pDomainFile; HRESULT m_cachedHR; ADID m_AppDomainId; public: static FileLoadLock *Create(PEFileListLock *pLock, PEFile *pFile, DomainFile *pDomainFile); ~FileLoadLock(); DomainFile *GetDomainFile(); ADID GetAppDomainId(); 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, PEFile *pFile, DomainFile *pDomainFile); static void HolderLeave(FileLoadLock *pThis); public: typedef Wrapper Holder; }; typedef FileLoadLock::Holder FileLoadLockHolder; #ifndef DACCESS_COMPILE typedef ReleaseHolder FileLoadLockRefHolder; #endif // DACCESS_COMPILE typedef ListLockBase JitListLock; typedef ListLockEntryBase 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) protected: // These 2 variables are only used on the AppDomain, but by placing them here // we reduce the cost of keeping the asmconstants file up to date. // The creation sequence number of this app domain (starting from 1) // This ID is generated by the code:SystemDomain::GetNewAppDomainId routine // The ID are recycled. // // see also code:ADID ADID m_dwId; DomainLocalBlock m_sDomainLocalBlock; 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(); void Terminate(); // ID to uniquely identify this AppDomain - used by the AppDomain publishing // service (to publish the list of all appdomains present in the process), // which in turn is used by, for eg., the debugger (to decide which App- // Domain(s) to attach to). // This is also used by Remoting for routing cross-appDomain calls. ADID GetId (void) { LIMITED_METHOD_DAC_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; return m_dwId; } virtual BOOL IsAppDomain() { LIMITED_METHOD_DAC_CONTRACT; return FALSE; } virtual BOOL IsSharedDomain() { LIMITED_METHOD_DAC_CONTRACT; return FALSE; } inline BOOL IsDefaultDomain(); // defined later in this file virtual PTR_LoaderAllocator GetLoaderAllocator() = 0; virtual PTR_AppDomain AsAppDomain() { LIMITED_METHOD_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; _ASSERTE(!"Not an AppDomain"); return NULL; } // If one domain is the SharedDomain and one is an AppDomain then // return the AppDomain, i.e. return the domain with the shorter lifetime // of the two given domains. static PTR_BaseDomain ComputeBaseDomain( BaseDomain *pGenericDefinitionDomain, // the domain that owns the generic type or method Instantiation classInst, // the type arguments to the type (if any) Instantiation methodInst = Instantiation()); // the type arguments to the method (if any) static PTR_BaseDomain ComputeBaseDomain(TypeKey * pTypeKey); #ifdef FEATURE_COMINTEROP //**************************************************************************************** // // This will look up interop data for a method table // #ifndef DACCESS_COMPILE // Returns the data pointer if present, NULL otherwise InteropMethodTableData *LookupComInteropData(MethodTable *pMT) { // Take the lock CrstHolder holder(&m_InteropDataCrst); // Lookup InteropMethodTableData *pData = (InteropMethodTableData*) m_interopDataHash.LookupValue((UPTR) pMT, (LPVOID) NULL); // Not there... if (pData == (InteropMethodTableData*) INVALIDENTRY) return NULL; // Found it return pData; } // Returns TRUE if successfully inserted, FALSE if this would be a duplicate entry BOOL InsertComInteropData(MethodTable* pMT, InteropMethodTableData *pData) { // We don't keep track of this kind of information for interfaces _ASSERTE(!pMT->IsInterface()); // Take the lock CrstHolder holder(&m_InteropDataCrst); // Check to see that it's not already in there InteropMethodTableData *pDupData = (InteropMethodTableData*) m_interopDataHash.LookupValue((UPTR) pMT, (LPVOID) NULL); if (pDupData != (InteropMethodTableData*) INVALIDENTRY) return FALSE; // Not in there, so insert m_interopDataHash.InsertValue((UPTR) pMT, (LPVOID) pData); // Success return TRUE; } #endif // DACCESS_COMPILE #endif // FEATURE_COMINTEROP void SetDisableInterfaceCache() { m_fDisableInterfaceCache = TRUE; } BOOL GetDisableInterfaceCache() { return m_fDisableInterfaceCache; } #ifdef FEATURE_COMINTEROP MngStdInterfacesInfo * GetMngStdInterfacesInfo() { LIMITED_METHOD_CONTRACT; return m_pMngStdInterfacesInfo; } PTR_CLRPrivBinderWinRT GetWinRtBinder() { return m_pWinRtBinder; } #endif // FEATURE_COMINTEROP //**************************************************************************************** // This method returns marshaling data that the EE uses that is stored on a per app domain // basis. EEMarshalingData *GetMarshalingData(); // Deletes marshaling data at shutdown (which contains cached factories that needs to be released) void DeleteMarshalingData(); #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; } STRINGREF *IsStringInterned(STRINGREF *pString); STRINGREF *GetOrInternString(STRINGREF *pString); virtual BOOL CanUnload() { LIMITED_METHOD_CONTRACT; return FALSE; } // can never unload BaseDomain // 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, BOOL bCrossAD = FALSE); #ifdef FEATURE_PREJIT // Ensures that the file for logging profile data is open (we only open it once) // return false on failure static BOOL EnsureNGenLogFileOpen(); #endif //**************************************************************************************** // Handles #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) OBJECTHANDLE CreateTypedHandle(OBJECTREF object, HandleType type) { WRAPPER_NO_CONTRACT; OBJECTHANDLE hnd = m_handleStore->CreateHandleOfType(OBJECTREFToObject(object), type); if (!hnd) { COMPlusThrowOM(); } return hnd; } OBJECTHANDLE CreateHandle(OBJECTREF object) { WRAPPER_NO_CONTRACT; CONDITIONAL_CONTRACT_VIOLATION(ModeViolation, object == NULL) return ::CreateHandle(m_handleStore, object); } OBJECTHANDLE CreateWeakHandle(OBJECTREF object) { WRAPPER_NO_CONTRACT; return ::CreateWeakHandle(m_handleStore, object); } OBJECTHANDLE CreateShortWeakHandle(OBJECTREF object) { WRAPPER_NO_CONTRACT; return ::CreateShortWeakHandle(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 CreateSizedRefHandle(OBJECTREF object) { WRAPPER_NO_CONTRACT; OBJECTHANDLE h; if (GCHeapUtilities::IsServerHeap()) { h = ::CreateSizedRefHandle(m_handleStore, object, m_dwSizedRefHandles % m_iNumberOfProcessors); } else { h = ::CreateSizedRefHandle(m_handleStore, object); } InterlockedIncrement((LONG*)&m_dwSizedRefHandles); return h; } #ifdef FEATURE_COMINTEROP OBJECTHANDLE CreateRefcountedHandle(OBJECTREF object) { WRAPPER_NO_CONTRACT; return ::CreateRefcountedHandle(m_handleStore, object); } OBJECTHANDLE CreateWinRTWeakHandle(OBJECTREF object, IWeakReference* pWinRTWeakReference) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_COOPERATIVE; } CONTRACTL_END; return ::CreateWinRTWeakHandle(m_handleStore, object, pWinRTWeakReference); } #endif // FEATURE_COMINTEROP OBJECTHANDLE CreateVariableHandle(OBJECTREF object, UINT type) { WRAPPER_NO_CONTRACT; return ::CreateVariableHandle(m_handleStore, object, type); } OBJECTHANDLE CreateDependentHandle(OBJECTREF primary, OBJECTREF secondary) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_COOPERATIVE; } CONTRACTL_END; OBJECTHANDLE hnd = m_handleStore->CreateDependentHandle(OBJECTREFToObject(primary), OBJECTREFToObject(secondary)); if (!hnd) { COMPlusThrowOM(); } return hnd; } #endif // DACCESS_COMPILE && !CROSSGEN_COMPILE IUnknown *GetFusionContext() {LIMITED_METHOD_CONTRACT; return m_pFusionContext; } CLRPrivBinderCoreCLR *GetTPABinderContext() {LIMITED_METHOD_CONTRACT; return m_pTPABinderContext; } CrstExplicitInit * GetLoaderAllocatorReferencesLock() { LIMITED_METHOD_CONTRACT; return &m_crstLoaderAllocatorReferences; } protected: //**************************************************************************************** // Helper method to initialize the large heap handle table. void InitLargeHeapHandleTable(); //**************************************************************************************** // Adds an assembly to the domain. void AddAssemblyNoLock(Assembly* assem); //**************************************************************************************** // // Hash table that maps a MethodTable to COM Interop compatibility data. PtrHashMap m_interopDataHash; // Critical sections & locks PEFileListLock m_FileLoadLock; // Protects the list of assemblies in the domain CrstExplicitInit m_DomainCrst; // General Protection for the Domain CrstExplicitInit m_DomainCacheCrst; // Protects the Assembly and Unmanaged caches CrstExplicitInit m_DomainLocalBlockCrst; CrstExplicitInit m_InteropDataCrst; // Used for COM Interop compatiblilty // Used to protect the reference lists in the collectible loader allocators attached to this appdomain CrstExplicitInit m_crstLoaderAllocatorReferences; CrstExplicitInit m_WinRTFactoryCacheCrst; // For WinRT factory cache //#AssemblyListLock // 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; BOOL m_fDisableInterfaceCache; // RCW COM interface cache ListLock m_ClassInitLock; JitListLock m_JITLock; ListLock m_ILStubGenLock; // Fusion context, used for adding assemblies to the is domain. It defines // fusion properties for finding assemblyies such as SharedBinPath, // PrivateBinPath, Application Directory, etc. IUnknown *m_pFusionContext; // Current binding context for the domain CLRPrivBinderCoreCLR *m_pTPABinderContext; // Reference to the binding context that holds TPA list details IGCHandleStore* m_handleStore; // The large heap handle table. LargeHeapHandleTable *m_pLargeHeapHandleTable; // The large heap handle table critical section. CrstExplicitInit m_LargeHeapHandleTableCrst; EEMarshalingData *m_pMarshalingData; #ifdef FEATURE_COMINTEROP // Information regarding the managed standard interfaces. MngStdInterfacesInfo *m_pMngStdInterfacesInfo; // WinRT binder PTR_CLRPrivBinderWinRT m_pWinRtBinder; #endif // FEATURE_COMINTEROP // Number of allocated slots for context local statics of this domain DWORD m_dwContextStatics; // Protects allocation of slot IDs for thread and context statics static CrstStatic m_SpecialStaticsCrst; public: // Lazily allocate offset for context static DWORD AllocateContextStaticsOffset(DWORD* pOffsetSlot); public: // Only call this routine when you can guarantee there are no // loads in progress. void ClearFusionContext(); public: //**************************************************************************************** // Synchronization holders. class LockHolder : public CrstHolder { public: LockHolder(BaseDomain *pD) : CrstHolder(&pD->m_DomainCrst) { WRAPPER_NO_CONTRACT; } }; friend class LockHolder; class CacheLockHolder : public CrstHolder { public: CacheLockHolder(BaseDomain *pD) : CrstHolder(&pD->m_DomainCacheCrst) { WRAPPER_NO_CONTRACT; } }; friend class CacheLockHolder; class DomainLocalBlockLockHolder : public CrstHolder { public: DomainLocalBlockLockHolder(BaseDomain *pD) : CrstHolder(&pD->m_DomainLocalBlockCrst) { WRAPPER_NO_CONTRACT; } }; friend class DomainLocalBlockLockHolder; class LoadLockHolder : public PEFileListLockHolder { public: LoadLockHolder(BaseDomain *pD, BOOL Take = TRUE) : PEFileListLockHolder(&pD->m_FileLoadLock, Take) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; CAN_TAKE_LOCK; } CONTRACTL_END; } }; friend class LoadLockHolder; class WinRTFactoryCacheLockHolder : public CrstHolder { public: WinRTFactoryCacheLockHolder(BaseDomain *pD) : CrstHolder(&pD->m_WinRTFactoryCacheCrst) { WRAPPER_NO_CONTRACT; } }; friend class WinRTFactoryCacheLockHolder; public: void InitVSD(); RangeList *GetCollectibleVSDRanges() { return &m_collVSDRanges; } private: TypeIDMap m_typeIDMap; // Range list for collectible types. Maps VSD PCODEs back to the VirtualCallStubManager they belong to LockedRangeList m_collVSDRanges; public: UINT32 GetTypeID(PTR_MethodTable pMT); UINT32 LookupTypeID(PTR_MethodTable pMT); PTR_MethodTable LookupType(UINT32 id); private: // I have yet to figure out an efficent way to get the number of handles // of a particular type that's currently used by the process without // spending more time looking at the handle table code. We know that // our only customer (asp.net) in Dev10 is not going to create many of // these handles so I am taking a shortcut for now and keep the sizedref // handle count on the AD itself. DWORD m_dwSizedRefHandles; static int m_iNumberOfProcessors; public: // Called by DestroySizedRefHandle void DecNumSizedRefHandles() { WRAPPER_NO_CONTRACT; LONG result; result = InterlockedDecrement((LONG*)&m_dwSizedRefHandles); _ASSERTE(result >= 0); } DWORD GetNumSizedRefHandles() { return m_dwSizedRefHandles; } #ifdef FEATURE_CODE_VERSIONING private: CodeVersionManager m_codeVersionManager; public: CodeVersionManager* GetCodeVersionManager() { return &m_codeVersionManager; } #endif //FEATURE_CODE_VERSIONING #ifdef FEATURE_TIERED_COMPILATION private: CallCounter m_callCounter; public: CallCounter* GetCallCounter() { return &m_callCounter; } #endif #ifdef DACCESS_COMPILE public: virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, bool enumThis); #endif }; // class BaseDomain enum { ATTACH_ASSEMBLY_LOAD = 0x1, ATTACH_MODULE_LOAD = 0x2, ATTACH_CLASS_LOAD = 0x4, ATTACH_ALL = 0x7 }; class ADUnloadSink { protected: ~ADUnloadSink(); CLREvent m_UnloadCompleteEvent; HRESULT m_UnloadResult; Volatile m_cRef; public: ADUnloadSink(); void ReportUnloadResult (HRESULT hr, OBJECTREF* pException); void WaitUnloadCompletion(); HRESULT GetUnloadResult() {LIMITED_METHOD_CONTRACT; return m_UnloadResult;}; void Reset(); ULONG AddRef(); ULONG Release(); }; FORCEINLINE void ADUnloadSink__Release(ADUnloadSink* pADSink) { WRAPPER_NO_CONTRACT; if (pADSink) pADSink->Release(); } typedef Wrapper ADUnloadSinkHolder; // 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|kIncludeIntrospection 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:DomainFile::IsAvailableToProfilers // Execution / introspection flags kIncludeExecution = 0x00000004, // include assemblies that are loaded for execution only kIncludeIntrospection = 0x00000008, // include assemblies that are loaded for introspection 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 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->GetLoaderAllocator(); } BOOL IsCollectible(DomainAssembly * pDomainAssembly) { WRAPPER_NO_CONTRACT; return pDomainAssembly->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 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<> //--------------------------------------------------------------------------------------- // #ifdef FEATURE_LOADER_OPTIMIZATION class SharedAssemblyLocator { public: enum { DOMAINASSEMBLY = 1, PEASSEMBLY = 2, PEASSEMBLYEXACT = 3 }; DWORD GetType() {LIMITED_METHOD_CONTRACT; return m_type;}; #ifndef DACCESS_COMPILE DomainAssembly* GetDomainAssembly() {LIMITED_METHOD_CONTRACT; _ASSERTE(m_type==DOMAINASSEMBLY); return (DomainAssembly*)m_value;}; PEAssembly* GetPEAssembly() {LIMITED_METHOD_CONTRACT; _ASSERTE(m_type==PEASSEMBLY||m_type==PEASSEMBLYEXACT); return (PEAssembly*)m_value;}; SharedAssemblyLocator(DomainAssembly* pAssembly) { LIMITED_METHOD_CONTRACT; m_type=DOMAINASSEMBLY; m_value=pAssembly; } SharedAssemblyLocator(PEAssembly* pFile, DWORD type = PEASSEMBLY) { LIMITED_METHOD_CONTRACT; m_type = type; m_value = pFile; } #endif // DACCESS_COMPILE DWORD Hash(); protected: DWORD m_type; LPVOID m_value; ULONG m_uIdentityHash; }; #endif // FEATURE_LOADER_OPTIMIZATION // // Stores binding information about failed assembly loads for DAC // struct FailedAssembly { SString displayName; SString location; HRESULT error; void Initialize(AssemblySpec *pSpec, Exception *ex) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; displayName.SetASCII(pSpec->GetName()); location.Set(pSpec->GetCodeBase()); 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. // } }; #ifdef FEATURE_COMINTEROP // Cache used by COM Interop struct NameToTypeMapEntry { // Host space representation of the key struct Key { LPCWSTR m_wzName; // The type name or registry string representation of the GUID "{}" SIZE_T m_cchName; // wcslen(m_wzName) for faster hashtable lookup }; struct DacKey { PTR_CWSTR m_wzName; // The type name or registry string representation of the GUID "{}" SIZE_T m_cchName; // wcslen(m_wzName) for faster hashtable lookup } m_key; TypeHandle m_typeHandle; // Using TypeHandle instead of MethodTable* to avoid losing information when sharing method tables. UINT m_nEpoch; // tracks creation Epoch. This is incremented each time an external reader enumerate the cache BYTE m_bFlags; }; typedef DPTR(NameToTypeMapEntry) PTR_NameToTypeMapEntry; class NameToTypeMapTraits : public NoRemoveSHashTraits< DefaultSHashTraits > { public: typedef NameToTypeMapEntry::Key key_t; static const NameToTypeMapEntry Null() { NameToTypeMapEntry e; e.m_key.m_wzName = NULL; e.m_key.m_cchName = 0; return e; } static bool IsNull(const NameToTypeMapEntry &e) { return e.m_key.m_wzName == NULL; } static const key_t GetKey(const NameToTypeMapEntry &e) { key_t key; key.m_wzName = (LPCWSTR)(e.m_key.m_wzName); // this cast brings the string over to the host, in a DAC build key.m_cchName = e.m_key.m_cchName; return key; } static count_t Hash(const key_t &key) { WRAPPER_NO_CONTRACT; return HashStringN(key.m_wzName, key.m_cchName); } static BOOL Equals(const key_t &lhs, const key_t &rhs) { WRAPPER_NO_CONTRACT; return (lhs.m_cchName == rhs.m_cchName) && memcmp(lhs.m_wzName, rhs.m_wzName, lhs.m_cchName * sizeof(WCHAR)) == 0; } void OnDestructPerEntryCleanupAction(const NameToTypeMapEntry& e) { WRAPPER_NO_CONTRACT; _ASSERTE(e.m_key.m_cchName == wcslen(e.m_key.m_wzName)); #ifndef DACCESS_COMPILE delete [] e.m_key.m_wzName; #endif // DACCESS_COMPILE } static const bool s_DestructPerEntryCleanupAction = true; }; typedef SHash NameToTypeMapTable; typedef DPTR(NameToTypeMapTable) PTR_NameToTypeMapTable; struct WinRTFactoryCacheEntry { typedef MethodTable *Key; Key key; // Type as KEY CtxEntry *m_pCtxEntry; // Context entry - used to verify whether the cache is a match OBJECTHANDLE m_ohFactoryObject; // Handle to factory object }; class WinRTFactoryCacheTraits : public DefaultSHashTraits { public: typedef WinRTFactoryCacheEntry::Key key_t; static const WinRTFactoryCacheEntry Null() { WinRTFactoryCacheEntry e; e.key = NULL; return e; } static bool IsNull(const WinRTFactoryCacheEntry &e) { return e.key == NULL; } static const WinRTFactoryCacheEntry::Key GetKey(const WinRTFactoryCacheEntry& e) { return e.key; } static count_t Hash(WinRTFactoryCacheEntry::Key key) { return (count_t)((size_t)key); } static BOOL Equals(WinRTFactoryCacheEntry::Key lhs, WinRTFactoryCacheEntry::Key rhs) { return lhs == rhs; } static const WinRTFactoryCacheEntry Deleted() { WinRTFactoryCacheEntry e; e.key = (MethodTable *)-1; return e; } static bool IsDeleted(const WinRTFactoryCacheEntry &e) { return e.key == (MethodTable *)-1; } static void OnDestructPerEntryCleanupAction(const WinRTFactoryCacheEntry& e); static const bool s_DestructPerEntryCleanupAction = true; }; typedef SHash WinRTFactoryCache; #endif // FEATURE_COMINTEROP class AppDomainIterator; const DWORD DefaultADID = 1; template class AppDomainCreationHolder; // 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. // // see code:BaseDomain for much of the meat of a AppDomain (heaps locks, etc) // * code:AppDomain.m_Assemblies - is a list of code:Assembly in the appdomain // class AppDomain : public BaseDomain { friend class ADUnloadSink; friend class SystemDomain; friend class AssemblySink; friend class AppDomainNative; friend class AssemblyNative; friend class AssemblySpec; friend class ClassLoader; friend class ThreadNative; friend class RCWCache; friend class ClrDataAccess; friend class CheckAsmOffsets; friend class AppDomainFromIDHolder; VPTR_VTABLE_CLASS(AppDomain, BaseDomain) public: #ifndef DACCESS_COMPILE AppDomain(); virtual ~AppDomain(); #endif static void DoADUnloadWork(); DomainAssembly* FindDomainAssembly(Assembly*); void EnterContext(Thread* pThread, Context* pCtx,ContextTransitionFrame *pFrame); #ifndef DACCESS_COMPILE //----------------------------------------------------------------------------------------------------------------- // Convenience wrapper for ::GetAppDomain to provide better encapsulation. static AppDomain * GetCurrentDomain() { return ::GetAppDomain(); } #endif //!DACCESS_COMPILE //----------------------------------------------------------------------------------------------------------------- // Initializes an AppDomain. (this functions is not called from the SystemDomain) void Init(); // creates only unamaged part static void CreateUnmanagedObject(AppDomainCreationHolder& result); #if defined(FEATURE_COMINTEROP) HRESULT SetWinrtApplicationContext(SString &appLocalWinMD); #endif // FEATURE_COMINTEROP BOOL CanReversePInvokeEnter(); void SetReversePInvokeCannotEnter(); bool MustForceTrivialWaitOperations(); void SetForceTrivialWaitOperations(); //**************************************************************************************** // // Stop deletes all the assemblies but does not remove other resources like // the critical sections void Stop(); // Gets rid of resources void Terminate(); #ifdef FEATURE_PREJIT //assembly cleanup that requires suspended runtime void DeleteNativeCodeRanges(); #endif // final assembly cleanup void ShutdownAssemblies(); void ShutdownFreeLoaderAllocators(BOOL bFromManagedCode); void ReleaseDomainBoundInfo(); void ReleaseFiles(); // Remove the Appdomain for the system and cleans up. This call should not be // called from shut down code. void CloseDomain(); virtual BOOL IsAppDomain() { LIMITED_METHOD_DAC_CONTRACT; return TRUE; } virtual PTR_AppDomain AsAppDomain() { LIMITED_METHOD_CONTRACT; return dac_cast(this); } OBJECTREF DoSetup(OBJECTREF* setupInfo); OBJECTREF GetExposedObject(); OBJECTREF GetRawExposedObject() { CONTRACTL { NOTHROW; GC_NOTRIGGER; SO_TOLERANT; MODE_COOPERATIVE; } CONTRACTL_END; if (m_ExposedObject) { return ObjectFromHandle(m_ExposedObject); } else { return NULL; } } OBJECTHANDLE GetRawExposedObjectHandleForDebugger() { LIMITED_METHOD_DAC_CONTRACT; return m_ExposedObject; } #ifdef FEATURE_COMINTEROP HRESULT GetComIPForExposedObject(IUnknown **pComIP); MethodTable *GetRedirectedType(WinMDAdapter::RedirectedTypeIndex index); #endif // FEATURE_COMINTEROP //**************************************************************************************** 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 * 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 * pAssemblyHolder) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; _ASSERTE(dbg_m_pAppDomain->GetAssemblyListLock()->OwnedByCurrentThread()); *pAssemblyHolder = dac_cast(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(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(); } }; // 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; } public: class AssemblyIterator { // AppDomain context with the assembly list AppDomain * m_pAppDomain; ArrayList::Iterator m_Iterator; AssemblyIterationFlags m_assemblyIterationFlags; public: BOOL Next(CollectibleAssemblyHolder * pDomainAssemblyHolder); // Note: Does not lock the assembly list, but AddRefs collectible assemblies. BOOL Next_Unlocked(CollectibleAssemblyHolder * pDomainAssemblyHolder); #ifndef DACCESS_COMPILE private: // Can be called only from AppDomain shutdown code:AppDomain::ShutdownAssemblies. // Note: Does not lock the assembly list and does not AddRefs collectible assemblies. BOOL Next_UnsafeNoAddRef(DomainAssembly ** ppDomainAssembly); #endif private: inline DWORD GetIndex() { LIMITED_METHOD_CONTRACT; return m_Iterator.GetIndex(); } private: friend class AppDomain; // Cannot have constructor so this iterator can be used inside a union static AssemblyIterator Create(AppDomain * pAppDomain, AssemblyIterationFlags assemblyIterationFlags) { LIMITED_METHOD_CONTRACT; AssemblyIterator i; i.m_pAppDomain = pAppDomain; i.m_Iterator = pAppDomain->m_Assemblies.GetArrayListIterator(); i.m_assemblyIterationFlags = assemblyIterationFlags; return i; } }; // class AssemblyIterator AssemblyIterator IterateAssembliesEx(AssemblyIterationFlags assemblyIterationFlags) { LIMITED_METHOD_CONTRACT; return AssemblyIterator::Create(this, assemblyIterationFlags); } private: struct NativeImageDependenciesEntry { BaseAssemblySpec m_AssemblySpec; GUID m_guidMVID; }; class NativeImageDependenciesTraits : public NoRemoveSHashTraits > { public: typedef BaseAssemblySpec *key_t; static key_t GetKey(NativeImageDependenciesEntry * e) { return &(e->m_AssemblySpec); } static count_t Hash(key_t k) { return k->Hash(); } static BOOL Equals(key_t lhs, key_t rhs) { return lhs->CompareEx(rhs); } }; SHash m_NativeImageDependencies; public: void CheckForMismatchedNativeImages(AssemblySpec * pSpec, const GUID * pGuid); public: class PathIterator { friend class AppDomain; ArrayList::Iterator m_i; public: BOOL Next() { WRAPPER_NO_CONTRACT; return m_i.Next(); } SString* GetPath() { WRAPPER_NO_CONTRACT; return dac_cast(m_i.GetElement()); } }; BOOL BindingByManifestFile(); PathIterator IterateNativeDllSearchDirectories(); void SetNativeDllSearchDirectories(LPCWSTR paths); BOOL HasNativeDllSearchDirectories(); void ShutdownNativeDllSearchDirectories(); public: SIZE_T GetAssemblyCount() { WRAPPER_NO_CONTRACT; return m_Assemblies.GetCount(this); } CHECK CheckCanLoadTypes(Assembly *pAssembly); CHECK CheckCanExecuteManagedCode(MethodDesc* pMD); CHECK CheckLoading(DomainFile *pFile, FileLoadLevel level); FileLoadLevel GetDomainFileLoadLevel(DomainFile *pFile); BOOL IsLoading(DomainFile *pFile, FileLoadLevel level); static FileLoadLevel GetThreadFileLoadLevel(); void LoadDomainFile(DomainFile *pFile, FileLoadLevel targetLevel); enum FindAssemblyOptions { FindAssemblyOptions_None = 0x0, FindAssemblyOptions_IncludeFailedToLoad = 0x1 }; DomainAssembly * FindAssembly(PEAssembly * pFile, FindAssemblyOptions options = FindAssemblyOptions_None) DAC_EMPTY_RET(NULL); Assembly *LoadAssembly(AssemblySpec* pIdentity, PEAssembly *pFile, FileLoadLevel targetLevel); // this function does not provide caching, you must use LoadDomainAssembly // unless the call is guaranteed to succeed or you don't need the caching // (e.g. if you will FailFast or tear down the AppDomain anyway) // The main point that you should not bypass caching if you might try to load the same file again, // resulting in multiple DomainAssembly objects that share the same PEAssembly for ngen image //which is violating our internal assumptions DomainAssembly *LoadDomainAssemblyInternal( AssemblySpec* pIdentity, PEAssembly *pFile, FileLoadLevel targetLevel); DomainAssembly *LoadDomainAssembly( AssemblySpec* pIdentity, PEAssembly *pFile, FileLoadLevel targetLevel); CHECK CheckValidModule(Module *pModule); #ifdef FEATURE_LOADER_OPTIMIZATION DomainFile *LoadDomainNeutralModuleDependency(Module *pModule, FileLoadLevel targetLevel); #endif // private: void LoadSystemAssemblies(); DomainFile *LoadDomainFile(FileLoadLock *pLock, FileLoadLevel targetLevel); void TryIncrementalLoad(DomainFile *pFile, FileLoadLevel workLevel, FileLoadLockHolder &lockHolder); Assembly *LoadAssemblyHelper(LPCWSTR wszAssembly, LPCWSTR wszCodeBase); #ifndef DACCESS_COMPILE // needs AssemblySpec void GetCacheAssemblyList(SetSHash& assemblyList); //**************************************************************************************** // Returns and Inserts assemblies into a lookup cache based on the binding information // in the AssemblySpec. There can be many AssemblySpecs to a single assembly. DomainAssembly* FindCachedAssembly(AssemblySpec* pSpec, BOOL fThrow=TRUE) { WRAPPER_NO_CONTRACT; return m_AssemblyCache.LookupAssembly(pSpec, fThrow); } PEAssembly* FindCachedFile(AssemblySpec* pSpec, BOOL fThrow = TRUE); BOOL IsCached(AssemblySpec *pSpec); #endif // DACCESS_COMPILE void CacheStringsForDAC(); BOOL AddFileToCache(AssemblySpec* pSpec, PEAssembly *pFile, BOOL fAllowFailure = FALSE); BOOL AddAssemblyToCache(AssemblySpec* pSpec, DomainAssembly *pAssembly); BOOL AddExceptionToCache(AssemblySpec* pSpec, Exception *ex); void AddUnmanagedImageToCache(LPCWSTR libraryName, HMODULE hMod); HMODULE FindUnmanagedImageInCache(LPCWSTR libraryName); //**************************************************************************************** // // Adds an assembly to the domain. void AddAssembly(DomainAssembly * assem); void RemoveAssembly_Unlocked(DomainAssembly * pAsm); BOOL ContainsAssembly(Assembly * assem); #ifdef FEATURE_LOADER_OPTIMIZATION enum SharePolicy { // Attributes to control when to use domain neutral assemblies SHARE_POLICY_UNSPECIFIED, // Use the current default policy (LoaderOptimization.NotSpecified) SHARE_POLICY_NEVER, // Do not share anything, except the system assembly (LoaderOptimization.SingleDomain) SHARE_POLICY_ALWAYS, // Share everything possible (LoaderOptimization.MultiDomain) SHARE_POLICY_GAC, // Share only GAC-bound assemblies (LoaderOptimization.MultiDomainHost) SHARE_POLICY_COUNT, SHARE_POLICY_MASK = 0x3, // NOTE that previously defined was a bit 0x40 which might be set on this value // in custom attributes. SHARE_POLICY_DEFAULT = SHARE_POLICY_NEVER, }; SharePolicy GetSharePolicy(); #endif // FEATURE_LOADER_OPTIMIZATION //**************************************************************************************** // // Reference count. When an appdomain is first created the reference is bump // to one when it is added to the list of domains (see SystemDomain). An explicit // Removal from the list is necessary before it will be deleted. ULONG AddRef(void); ULONG Release(void) DAC_EMPTY_RET(0); //**************************************************************************************** LPCWSTR GetFriendlyName(BOOL fDebuggerCares = TRUE); LPCWSTR GetFriendlyNameForDebugger(); LPCWSTR GetFriendlyNameForLogging(); #ifdef DACCESS_COMPILE PVOID GetFriendlyNameNoSet(bool* isUtf8); #endif void SetFriendlyName(LPCWSTR pwzFriendlyName, BOOL fDebuggerCares = TRUE); void ResetFriendlyName(BOOL fDebuggerCares = TRUE); //**************************************************************************************** // This can be used to override the binding behavior of the appdomain. It // is overridden in the compilation domain. It is important that all // static binding goes through this path. virtual PEAssembly * BindAssemblySpec( AssemblySpec *pSpec, BOOL fThrowOnFileNotFound, BOOL fRaisePrebindEvents, StackCrawlMark *pCallerStackMark = NULL, BOOL fUseHostBinderIfAvailable = TRUE) DAC_EMPTY_RET(NULL); HRESULT BindAssemblySpecForHostedBinder( AssemblySpec * pSpec, IAssemblyName * pAssemblyName, ICLRPrivBinder * pBinder, PEAssembly ** ppAssembly) DAC_EMPTY_RET(E_FAIL); HRESULT BindHostedPrivAssembly( PEAssembly * pParentPEAssembly, ICLRPrivAssembly * pPrivAssembly, IAssemblyName * pAssemblyName, PEAssembly ** ppAssembly, BOOL fIsIntrospectionOnly = FALSE) DAC_EMPTY_RET(S_OK); PEAssembly *TryResolveAssembly(AssemblySpec *pSpec, BOOL fPreBind); // Store a successful binding into the cache. This will keep the file from // being physically unmapped, as well as shortcutting future attempts to bind // the same spec throught the Cached entry point. // // Right now we only cache assembly binds for "probing" type // binding situations, basically when loading domain neutral assemblies or // zap files. // // @todo: We may want to be more aggressive about this if // there are other situations where we are repeatedly binding the // same assembly specs, though. // // Returns TRUE if stored // FALSE if it's a duplicate (caller should clean up args) BOOL StoreBindAssemblySpecResult(AssemblySpec *pSpec, PEAssembly *pFile, BOOL clone = TRUE); BOOL StoreBindAssemblySpecError(AssemblySpec *pSpec, HRESULT hr, OBJECTREF *pThrowable, BOOL clone = TRUE); //**************************************************************************************** // //**************************************************************************************** // // Uses the first assembly to add an application base to the Context. This is done // in a lazy fashion so executables do not take the perf hit unless the load other // assemblies #ifndef DACCESS_COMPILE void OnAssemblyLoad(Assembly *assem); void OnAssemblyLoadUnlocked(Assembly *assem); static BOOL OnUnhandledException(OBJECTREF *pThrowable, BOOL isTerminating = TRUE); #endif // True iff a debugger is attached to the process (same as CORDebuggerAttached) BOOL IsDebuggerAttached (void); #ifdef DEBUGGING_SUPPORTED // Notify debugger of all assemblies, modules, and possibly classes in this AppDomain BOOL NotifyDebuggerLoad(int flags, BOOL attaching); // Send unload notifications to the debugger for all assemblies, modules and classes in this AppDomain void NotifyDebuggerUnload(); #endif // DEBUGGING_SUPPORTED void SetSystemAssemblyLoadEventSent (BOOL fFlag); BOOL WasSystemAssemblyLoadEventSent (void); #ifndef DACCESS_COMPILE OBJECTREF* AllocateStaticFieldObjRefPtrs(int nRequested, OBJECTREF** ppLazyAllocate = NULL) { WRAPPER_NO_CONTRACT; return AllocateObjRefPtrsInLargeTable(nRequested, ppLazyAllocate); } OBJECTREF* AllocateStaticFieldObjRefPtrsCrossDomain(int nRequested, OBJECTREF** ppLazyAllocate = NULL) { WRAPPER_NO_CONTRACT; return AllocateObjRefPtrsInLargeTable(nRequested, ppLazyAllocate, TRUE); } #endif // DACCESS_COMPILE void EnumStaticGCRefs(promote_func* fn, ScanContext* sc); DomainLocalBlock *GetDomainLocalBlock() { LIMITED_METHOD_DAC_CONTRACT; return &m_sDomainLocalBlock; } static SIZE_T GetOffsetOfModuleSlotsPointer() { WRAPPER_NO_CONTRACT; return offsetof(AppDomain,m_sDomainLocalBlock) + DomainLocalBlock::GetOffsetOfModuleSlotsPointer(); } void SetupSharedStatics(); ADUnloadSink* PrepareForWaitUnloadCompletion(); //**************************************************************************************** // // Create a quick lookup for classes loaded into this domain based on their GUID. // void InsertClassForCLSID(MethodTable* pMT, BOOL fForceInsert = FALSE); void InsertClassForCLSID(MethodTable* pMT, GUID *pGuid); #ifdef FEATURE_COMINTEROP private: void CacheTypeByNameWorker(const SString &ssClassName, const UINT vCacheVersion, TypeHandle typeHandle, BYTE flags, BOOL bReplaceExisting = FALSE); TypeHandle LookupTypeByNameWorker(const SString &ssClassName, UINT *pvCacheVersion, BYTE *pbFlags); public: // Used by COM Interop for mapping WinRT runtime class names to real types. void CacheTypeByName(const SString &ssClassName, const UINT vCacheVersion, TypeHandle typeHandle, BYTE flags, BOOL bReplaceExisting = FALSE); TypeHandle LookupTypeByName(const SString &ssClassName, UINT *pvCacheVersion, BYTE *pbFlags); PTR_MethodTable LookupTypeByGuid(const GUID & guid); #ifndef DACCESS_COMPILE inline BOOL CanCacheWinRTTypeByGuid(TypeHandle typeHandle) { CONTRACTL { THROWS; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; // Only allow caching guid/types maps for types loaded during // "normal" domain operation if (IsCompilationDomain() || (m_Stage < STAGE_OPEN)) return FALSE; MethodTable *pMT = typeHandle.GetMethodTable(); if (pMT != NULL) { // Don't cache mscorlib-internal declarations of WinRT types. if (pMT->GetModule()->IsSystem() && pMT->IsProjectedFromWinRT()) return FALSE; // Don't cache redirected WinRT types. if (WinRTTypeNameConverter::IsRedirectedWinRTSourceType(pMT)) return FALSE; } return TRUE; } #endif // !DACCESS_COMPILE void CacheWinRTTypeByGuid(TypeHandle typeHandle); void GetCachedWinRTTypes(SArray * pTypes, SArray * pGuids, UINT minEpoch, UINT * pCurEpoch); // Used by COM Interop for caching WinRT factory objects. void CacheWinRTFactoryObject(MethodTable *pClassMT, OBJECTREF *refFactory, LPVOID lpCtxCookie); OBJECTREF LookupWinRTFactoryObject(MethodTable *pClassMT, LPVOID lpCtxCookie); void RemoveWinRTFactoryObjects(LPVOID pCtxCookie); MethodTable *LoadCOMClass(GUID clsid, BOOL bLoadRecord = FALSE, BOOL* pfAssemblyInReg = NULL); COMorRemotingFlag GetComOrRemotingFlag(); BOOL GetPreferComInsteadOfManagedRemoting(); OBJECTREF GetMissingObject(); // DispatchInfo will call function to retrieve the Missing.Value object. #endif // FEATURE_COMINTEROP #ifndef DACCESS_COMPILE MethodTable* LookupClass(REFIID iid) { WRAPPER_NO_CONTRACT; MethodTable *pMT = (MethodTable*) m_clsidHash.LookupValue((UPTR) GetKeyFromGUID(&iid), (LPVOID)&iid); return (pMT == (MethodTable*) INVALIDENTRY ? NULL : pMT); } #endif // DACCESS_COMPILE //@todo get a better key ULONG GetKeyFromGUID(const GUID *pguid) { LIMITED_METHOD_CONTRACT; return *(ULONG *) pguid; } #ifdef FEATURE_COMINTEROP ComCallWrapperCache* GetComCallWrapperCache(); RCWCache *GetRCWCache() { WRAPPER_NO_CONTRACT; if (m_pRCWCache) return m_pRCWCache; // By separating the cache creation from the common lookup, we // can keep the (x86) EH prolog/epilog off the path. return CreateRCWCache(); } private: RCWCache *CreateRCWCache(); public: RCWCache *GetRCWCacheNoCreate() { LIMITED_METHOD_CONTRACT; return m_pRCWCache; } RCWRefCache *GetRCWRefCache(); void ResetComCallWrapperCache() { LIMITED_METHOD_CONTRACT; m_pComCallWrapperCache = NULL; } MethodTable* GetLicenseInteropHelperMethodTable(); #endif // FEATURE_COMINTEROP //**************************************************************************************** // Get the proxy for this app domain ADIndex GetIndex() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return m_dwIndex; } TPIndex GetTPIndex() { LIMITED_METHOD_CONTRACT; return m_tpIndex; } void InitializeDomainContext(BOOL allowRedirects, LPCWSTR pwszPath, LPCWSTR pwszConfig); IUnknown *CreateFusionContext(); void OverrideDefaultContextBinder(IUnknown *pOverrideBinder) { LIMITED_METHOD_CONTRACT; _ASSERTE(pOverrideBinder != NULL); pOverrideBinder->AddRef(); m_pFusionContext->Release(); m_pFusionContext = pOverrideBinder; } #ifdef FEATURE_PREJIT CorCompileConfigFlags GetNativeConfigFlags(); #endif // FEATURE_PREJIT //**************************************************************************************** // Create a domain context rooted at the fileName. The directory containing the file name // is the application base and the configuration file is the fileName appended with // .config. If no name is passed in then no domain is created. static AppDomain* CreateDomainContext(LPCWSTR fileName); // Sets up the current domain's fusion context based on the given exe file name // (app base & config file) void SetupExecutableFusionContext(LPCWSTR exePath); //**************************************************************************************** // Manage a pool of asyncrhonous objects used to fetch assemblies. When a sink is released // it places itself back on the pool list. Only one object is kept in the pool. void SetIsUserCreatedDomain() { LIMITED_METHOD_CONTRACT; m_dwFlags |= USER_CREATED_DOMAIN; } BOOL IsUserCreatedDomain() { LIMITED_METHOD_CONTRACT; return (m_dwFlags & USER_CREATED_DOMAIN); } void SetIgnoreUnhandledExceptions() { LIMITED_METHOD_CONTRACT; m_dwFlags |= IGNORE_UNHANDLED_EXCEPTIONS; } BOOL IgnoreUnhandledExceptions() { LIMITED_METHOD_CONTRACT; return (m_dwFlags & IGNORE_UNHANDLED_EXCEPTIONS); } void SetPassiveDomain() { LIMITED_METHOD_CONTRACT; m_dwFlags |= PASSIVE_DOMAIN; } BOOL IsPassiveDomain() { LIMITED_METHOD_CONTRACT; return (m_dwFlags & PASSIVE_DOMAIN); } void SetVerificationDomain() { LIMITED_METHOD_CONTRACT; m_dwFlags |= VERIFICATION_DOMAIN; } BOOL IsVerificationDomain() { LIMITED_METHOD_CONTRACT; return (m_dwFlags & VERIFICATION_DOMAIN); } void SetIllegalVerificationDomain() { LIMITED_METHOD_CONTRACT; m_dwFlags |= ILLEGAL_VERIFICATION_DOMAIN; } BOOL IsIllegalVerificationDomain() { LIMITED_METHOD_CONTRACT; return (m_dwFlags & ILLEGAL_VERIFICATION_DOMAIN); } void SetCompilationDomain() { LIMITED_METHOD_CONTRACT; m_dwFlags |= (PASSIVE_DOMAIN|COMPILATION_DOMAIN); } BOOL IsCompilationDomain(); PTR_CompilationDomain ToCompilationDomain() { LIMITED_METHOD_CONTRACT; _ASSERTE(IsCompilationDomain()); return dac_cast(this); } void SetCanUnload() { LIMITED_METHOD_CONTRACT; m_dwFlags |= APP_DOMAIN_CAN_BE_UNLOADED; } BOOL CanUnload() { LIMITED_METHOD_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; return m_dwFlags & APP_DOMAIN_CAN_BE_UNLOADED; } void SetRemotingConfigured() { LIMITED_METHOD_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; FastInterlockOr((ULONG*)&m_dwFlags, REMOTING_CONFIGURED_FOR_DOMAIN); } BOOL IsRemotingConfigured() { LIMITED_METHOD_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; return m_dwFlags & REMOTING_CONFIGURED_FOR_DOMAIN; } void SetOrphanedLocks() { LIMITED_METHOD_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; FastInterlockOr((ULONG*)&m_dwFlags, ORPHANED_LOCKS); } BOOL HasOrphanedLocks() { LIMITED_METHOD_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; return m_dwFlags & ORPHANED_LOCKS; } // This function is used to relax asserts in the lock accounting. // It returns true if we are fine with hosed lock accounting in this domain. BOOL OkToIgnoreOrphanedLocks() { WRAPPER_NO_CONTRACT; return HasOrphanedLocks() && m_Stage >= STAGE_UNLOAD_REQUESTED; } static void ExceptionUnwind(Frame *pFrame); #ifdef _DEBUG void TrackADThreadEnter(Thread *pThread, Frame *pFrame); void TrackADThreadExit(Thread *pThread, Frame *pFrame); void DumpADThreadTrack(); #endif #ifndef DACCESS_COMPILE void ThreadEnter(Thread *pThread, Frame *pFrame) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; #ifdef _DEBUG if (LoggingOn(LF_APPDOMAIN, LL_INFO100)) TrackADThreadEnter(pThread, pFrame); else #endif { InterlockedIncrement((LONG*)&m_dwThreadEnterCount); LOG((LF_APPDOMAIN, LL_INFO1000, "AppDomain::ThreadEnter %p to [%d] (%8.8x) %S count %d\n", pThread,GetId().m_dwId, this, GetFriendlyNameForLogging(),GetThreadEnterCount())); #if _DEBUG_AD_UNLOAD printf("AppDomain::ThreadEnter %p to [%d] (%8.8x) %S count %d\n", pThread, GetId().m_dwId, this, GetFriendlyNameForLogging(), GetThreadEnterCount()); #endif } } void ThreadExit(Thread *pThread, Frame *pFrame) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; #ifdef _DEBUG if (LoggingOn(LF_APPDOMAIN, LL_INFO100)) { TrackADThreadExit(pThread, pFrame); } else #endif { LONG result; result = InterlockedDecrement((LONG*)&m_dwThreadEnterCount); _ASSERTE(result >= 0); LOG((LF_APPDOMAIN, LL_INFO1000, "AppDomain::ThreadExit from [%d] (%8.8x) %S count %d\n", this, GetId().m_dwId, GetFriendlyNameForLogging(), GetThreadEnterCount())); #if _DEBUG_ADUNLOAD printf("AppDomain::ThreadExit %x from [%d] (%8.8x) %S count %d\n", pThread->GetThreadId(), this, GetId().m_dwId, GetFriendlyNameForLogging(), GetThreadEnterCount()); #endif } } #endif // DACCESS_COMPILE ULONG GetThreadEnterCount() { LIMITED_METHOD_CONTRACT; return m_dwThreadEnterCount; } BOOL OnlyOneThreadLeft() { LIMITED_METHOD_CONTRACT; return m_dwThreadEnterCount==1 || m_dwThreadsStillInAppDomain ==1; } Context *GetDefaultContext() { LIMITED_METHOD_CONTRACT; return m_pDefaultContext; } BOOL CanLoadCode() { LIMITED_METHOD_CONTRACT; return m_Stage >= STAGE_READYFORMANAGEDCODE && m_Stage < STAGE_CLOSED; } void SetAnonymouslyHostedDynamicMethodsAssembly(DomainAssembly * pDomainAssembly) { LIMITED_METHOD_CONTRACT; _ASSERTE(pDomainAssembly != NULL); _ASSERTE(m_anonymouslyHostedDynamicMethodsAssembly == NULL); m_anonymouslyHostedDynamicMethodsAssembly = pDomainAssembly; } DomainAssembly * GetAnonymouslyHostedDynamicMethodsAssembly() { LIMITED_METHOD_CONTRACT; return m_anonymouslyHostedDynamicMethodsAssembly; } BOOL HasUnloadStarted() { LIMITED_METHOD_CONTRACT; return m_Stage>=STAGE_EXITED; } static void RefTakerAcquire(AppDomain* pDomain) { WRAPPER_NO_CONTRACT; if(!pDomain) return; pDomain->AddRef(); #ifdef _DEBUG FastInterlockIncrement(&pDomain->m_dwRefTakers); #endif } static void RefTakerRelease(AppDomain* pDomain) { WRAPPER_NO_CONTRACT; if(!pDomain) return; #ifdef _DEBUG _ASSERTE(pDomain->m_dwRefTakers); FastInterlockDecrement(&pDomain->m_dwRefTakers); #endif pDomain->Release(); } #ifdef _DEBUG BOOL IsHeldByIterator() { LIMITED_METHOD_CONTRACT; return m_dwIterHolders>0; } BOOL IsHeldByRefTaker() { LIMITED_METHOD_CONTRACT; return m_dwRefTakers>0; } void IteratorRelease() { LIMITED_METHOD_CONTRACT; _ASSERTE(m_dwIterHolders); FastInterlockDecrement(&m_dwIterHolders); } void IteratorAcquire() { LIMITED_METHOD_CONTRACT; FastInterlockIncrement(&m_dwIterHolders); } #endif BOOL IsActive() { LIMITED_METHOD_DAC_CONTRACT; return m_Stage >= STAGE_ACTIVE && m_Stage < STAGE_CLOSED; } // Range for normal execution of code in the appdomain, currently used for // appdomain resource monitoring since we don't care to update resource usage // unless it's in these stages (as fields of AppDomain may not be valid if it's // not within these stages) BOOL IsUserActive() { LIMITED_METHOD_DAC_CONTRACT; return m_Stage >= STAGE_ACTIVE && m_Stage <= STAGE_OPEN; } BOOL IsValid() { LIMITED_METHOD_DAC_CONTRACT; #ifdef DACCESS_COMPILE // We want to see all appdomains in SOS, even the about to be destructed ones. // There is no risk of races under DAC, so we will pretend to be unconditionally valid. return TRUE; #else return m_Stage > STAGE_CREATING && m_Stage < STAGE_CLOSED; #endif } #ifdef _DEBUG BOOL IsBeingCreated() { LIMITED_METHOD_CONTRACT; return m_dwCreationHolders > 0; } void IncCreationCount() { LIMITED_METHOD_CONTRACT; FastInterlockIncrement(&m_dwCreationHolders); _ASSERTE(m_dwCreationHolders > 0); } void DecCreationCount() { LIMITED_METHOD_CONTRACT; FastInterlockDecrement(&m_dwCreationHolders); _ASSERTE(m_dwCreationHolders > -1); } #endif BOOL IsRunningIn(Thread* pThread); BOOL IsUnloading() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return m_Stage > STAGE_UNLOAD_REQUESTED; } BOOL NotReadyForManagedCode() { LIMITED_METHOD_CONTRACT; return m_Stage < STAGE_READYFORMANAGEDCODE; } void SetFinalized() { LIMITED_METHOD_CONTRACT; SetStage(STAGE_FINALIZED); } BOOL IsFinalizing() { LIMITED_METHOD_CONTRACT; return m_Stage >= STAGE_FINALIZING; } BOOL IsFinalized() { LIMITED_METHOD_CONTRACT; return m_Stage >= STAGE_FINALIZED; } BOOL NoAccessToHandleTable() { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; return m_Stage >= STAGE_HANDLETABLE_NOACCESS; } // Checks whether the given thread can enter the app domain BOOL CanThreadEnter(Thread *pThread); // Following two are needed for the Holder static void SetUnloadInProgress(AppDomain *pThis) PUB; static void SetUnloadComplete(AppDomain *pThis) PUB; // Predicates for GC asserts BOOL ShouldHaveFinalization() { LIMITED_METHOD_CONTRACT; return ((DWORD) m_Stage) < STAGE_COLLECTED; } BOOL ShouldHaveCode() { LIMITED_METHOD_CONTRACT; return ((DWORD) m_Stage) < STAGE_COLLECTED; } BOOL ShouldHaveRoots() { LIMITED_METHOD_CONTRACT; return ((DWORD) m_Stage) < STAGE_CLEARED; } BOOL ShouldHaveInstances() { LIMITED_METHOD_CONTRACT; return ((DWORD) m_Stage) < STAGE_COLLECTED; } static void RaiseExitProcessEvent(); Assembly* RaiseResourceResolveEvent(DomainAssembly* pAssembly, LPCSTR szName); DomainAssembly* RaiseTypeResolveEventThrowing(DomainAssembly* pAssembly, LPCSTR szName, ASSEMBLYREF *pResultingAssemblyRef); Assembly* RaiseAssemblyResolveEvent(AssemblySpec *pSpec, BOOL fIntrospection, BOOL fPreBind); private: CrstExplicitInit m_ReflectionCrst; CrstExplicitInit m_RefClassFactCrst; EEClassFactoryInfoHashTable *m_pRefClassFactHash; // Hash table that maps a class factory info to a COM comp. #ifdef FEATURE_COMINTEROP DispIDCache *m_pRefDispIDCache; COMorRemotingFlag m_COMorRemotingFlag; OBJECTHANDLE m_hndMissing; //Handle points to Missing.Value Object which is used for [Optional] arg scenario during IDispatch CCW Call MethodTable* m_rpCLRTypes[WinMDAdapter::RedirectedTypeIndex_Count]; MethodTable* LoadRedirectedType(WinMDAdapter::RedirectedTypeIndex index, WinMDAdapter::FrameworkAssemblyIndex assembly); #endif // FEATURE_COMINTEROP public: CrstBase *GetRefClassFactCrst() { LIMITED_METHOD_CONTRACT; return &m_RefClassFactCrst; } #ifndef DACCESS_COMPILE EEClassFactoryInfoHashTable* GetClassFactHash() { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_TRIGGERS; STATIC_CONTRACT_FAULT; if (m_pRefClassFactHash != NULL) { return m_pRefClassFactHash; } return SetupClassFactHash(); } #endif // DACCESS_COMPILE #ifdef FEATURE_COMINTEROP DispIDCache* GetRefDispIDCache() { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_TRIGGERS; STATIC_CONTRACT_FAULT; if (m_pRefDispIDCache != NULL) { return m_pRefDispIDCache; } return SetupRefDispIDCache(); } #endif // FEATURE_COMINTEROP PTR_LoaderHeap GetStubHeap(); PTR_LoaderHeap GetLowFrequencyHeap(); PTR_LoaderHeap GetHighFrequencyHeap(); virtual PTR_LoaderAllocator GetLoaderAllocator(); #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING #define ARM_ETW_ALLOC_THRESHOLD (4 * 1024 * 1024) // cache line size in ULONGLONG - 128 bytes which are 16 ULONGLONG's #define ARM_CACHE_LINE_SIZE_ULL 16 inline ULONGLONG GetAllocBytes() { LIMITED_METHOD_CONTRACT; ULONGLONG ullTotalAllocBytes = 0; // Ensure that m_pullAllocBytes is non-null to avoid an AV in a race between GC and AD unload. // A race can occur when a new appdomain is created, but an OOM is thrown when allocating for m_pullAllocBytes, causing the AD unload. if(NULL != m_pullAllocBytes) { for (DWORD i = 0; i < m_dwNumHeaps; i++) { ullTotalAllocBytes += m_pullAllocBytes[i * ARM_CACHE_LINE_SIZE_ULL]; } } return ullTotalAllocBytes; } void RecordAllocBytes(size_t allocatedBytes, DWORD dwHeapNumber) { LIMITED_METHOD_CONTRACT; _ASSERTE(dwHeapNumber < m_dwNumHeaps); // Ensure that m_pullAllocBytes is non-null to avoid an AV in a race between GC and AD unload. // A race can occur when a new appdomain is created, but an OOM is thrown when allocating for m_pullAllocBytes, causing the AD unload. if(NULL != m_pullAllocBytes) { m_pullAllocBytes[dwHeapNumber * ARM_CACHE_LINE_SIZE_ULL] += allocatedBytes; } ULONGLONG ullTotalAllocBytes = GetAllocBytes(); if ((ullTotalAllocBytes - m_ullLastEtwAllocBytes) >= ARM_ETW_ALLOC_THRESHOLD) { m_ullLastEtwAllocBytes = ullTotalAllocBytes; FireEtwAppDomainMemAllocated((ULONGLONG)this, ullTotalAllocBytes, GetClrInstanceId()); } } inline ULONGLONG GetSurvivedBytes() { LIMITED_METHOD_CONTRACT; ULONGLONG ullTotalSurvivedBytes = 0; // Ensure that m_pullSurvivedBytes is non-null to avoid an AV in a race between GC and AD unload. // A race can occur when a new appdomain is created, but an OOM is thrown when allocating for m_pullSurvivedBytes, causing the AD unload. if(NULL != m_pullSurvivedBytes) { for (DWORD i = 0; i < m_dwNumHeaps; i++) { ullTotalSurvivedBytes += m_pullSurvivedBytes[i * ARM_CACHE_LINE_SIZE_ULL]; } } return ullTotalSurvivedBytes; } void RecordSurvivedBytes(size_t promotedBytes, DWORD dwHeapNumber) { WRAPPER_NO_CONTRACT; _ASSERTE(dwHeapNumber < m_dwNumHeaps); // Ensure that m_pullSurvivedBytes is non-null to avoid an AV in a race between GC and AD unload. // A race can occur when a new appdomain is created, but an OOM is thrown when allocating for m_pullSurvivedBytes, causing the AD unload. if(NULL != m_pullSurvivedBytes) { m_pullSurvivedBytes[dwHeapNumber * ARM_CACHE_LINE_SIZE_ULL] += promotedBytes; } } inline void ResetSurvivedBytes() { LIMITED_METHOD_CONTRACT; // Ensure that m_pullSurvivedBytes is non-null to avoid an AV in a race between GC and AD unload. // A race can occur when a new appdomain is created, but an OOM is thrown when allocating for m_pullSurvivedBytes, causing the AD unload. if(NULL != m_pullSurvivedBytes) { for (DWORD i = 0; i < m_dwNumHeaps; i++) { m_pullSurvivedBytes[i * ARM_CACHE_LINE_SIZE_ULL] = 0; } } } // Return the total processor time (user and kernel) used by threads executing in this AppDomain so far. // The result is in 100ns units. ULONGLONG QueryProcessorUsage(); // Add to the current count of processor time used by threads within this AppDomain. This API is called by // threads transitioning between AppDomains. void UpdateProcessorUsage(ULONGLONG ullAdditionalUsage); #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING private: static void RaiseOneExitProcessEvent_Wrapper(AppDomainIterator* pi); static void RaiseOneExitProcessEvent(); size_t EstimateSize(); EEClassFactoryInfoHashTable* SetupClassFactHash(); #ifdef FEATURE_COMINTEROP DispIDCache* SetupRefDispIDCache(); COMorRemotingFlag GetPreferComInsteadOfManagedRemotingFromConfigFile(); #endif // FEATURE_COMINTEROP void InitializeDefaultDomainManager (); public: protected: BOOL PostBindResolveAssembly(AssemblySpec *pPrePolicySpec, AssemblySpec *pPostPolicySpec, HRESULT hrBindResult, AssemblySpec **ppFailedSpec); #ifdef FEATURE_COMINTEROP public: void ReleaseRCWs(LPVOID pCtxCookie); void DetachRCWs(); protected: #endif // FEATURE_COMINTEROP LPWSTR m_pwDynamicDir; private: void RaiseLoadingAssemblyEvent(DomainAssembly* pAssembly); friend class DomainAssembly; public: static void ProcessUnloadDomainEventOnFinalizeThread(); static BOOL HasWorkForFinalizerThread() { LIMITED_METHOD_CONTRACT; return s_pAppDomainToRaiseUnloadEvent != NULL; } private: static AppDomain* s_pAppDomainToRaiseUnloadEvent; static BOOL s_fProcessUnloadDomainEvent; void RaiseUnloadDomainEvent(); static void RaiseUnloadDomainEvent_Wrapper(LPVOID /* AppDomain * */); BOOL RaiseUnhandledExceptionEvent(OBJECTREF *pSender, OBJECTREF *pThrowable, BOOL isTerminating); BOOL HasUnhandledExceptionEventHandler(); BOOL RaiseUnhandledExceptionEventNoThrow(OBJECTREF *pSender, OBJECTREF *pThrowable, BOOL isTerminating); struct RaiseUnhandled_Args { AppDomain *pExceptionDomain; AppDomain *pTargetDomain; OBJECTREF *pSender; OBJECTREF *pThrowable; BOOL isTerminating; BOOL *pResult; }; static void AllowThreadEntrance(AppDomain *pApp); static void RestrictThreadEntrance(AppDomain *pApp); typedef Holder,AppDomain::AllowThreadEntrance,NULL> RestrictEnterHolder; enum Stage { STAGE_CREATING, STAGE_READYFORMANAGEDCODE, STAGE_ACTIVE, STAGE_OPEN, STAGE_UNLOAD_REQUESTED, STAGE_EXITING, STAGE_EXITED, STAGE_FINALIZING, STAGE_FINALIZED, STAGE_HANDLETABLE_NOACCESS, STAGE_CLEARED, STAGE_COLLECTED, STAGE_CLOSED }; void SetStage(Stage stage) { CONTRACTL { NOTHROW; GC_NOTRIGGER; SO_TOLERANT; MODE_ANY; } CONTRACTL_END; STRESS_LOG2(LF_APPDOMAIN, LL_INFO100,"Updating AD stage, ADID=%d, stage=%d\n",GetId().m_dwId,stage); TESTHOOKCALL(AppDomainStageChanged(GetId().m_dwId,m_Stage,stage)); Stage lastStage=m_Stage; while (lastStage !=stage) lastStage = (Stage)FastInterlockCompareExchange((LONG*)&m_Stage,stage,lastStage); }; void Exit(BOOL fRunFinalizers, BOOL fAsyncExit); void Close(); void ClearGCRoots(); void ClearGCHandles(); void HandleAsyncPinHandles(); void UnwindThreads(); // Return TRUE if EE is stopped // Return FALSE if more work is needed BOOL StopEEAndUnwindThreads(unsigned int retryCount, BOOL *pFMarkUnloadRequestThread); // Use Rude Abort to unload the domain. BOOL m_fRudeUnload; Thread *m_pUnloadRequestThread; ADUnloadSink* m_ADUnloadSink; BOOL m_bForceGCOnUnload; BOOL m_bUnloadingFromUnloadEvent; AppDomainLoaderAllocator m_LoaderAllocator; // List of unloaded LoaderAllocators, protected by code:GetLoaderAllocatorReferencesLock (for now) LoaderAllocator * m_pDelayedLoaderAllocatorUnloadList; public: // Register the loader allocator for deletion in code:ShutdownFreeLoaderAllocators. void RegisterLoaderAllocatorForDeletion(LoaderAllocator * pLoaderAllocator); AppDomain * m_pNextInDelayedUnloadList; void SetForceGCOnUnload(BOOL bSet) { m_bForceGCOnUnload=bSet; } void SetUnloadingFromUnloadEvent() { m_bUnloadingFromUnloadEvent=TRUE; } BOOL IsUnloadingFromUnloadEvent() { return m_bUnloadingFromUnloadEvent; } void SetRudeUnload() { LIMITED_METHOD_CONTRACT; m_fRudeUnload = TRUE; } BOOL IsRudeUnload() { LIMITED_METHOD_CONTRACT; return m_fRudeUnload; } ADUnloadSink* GetADUnloadSink(); ADUnloadSink* GetADUnloadSinkForUnload(); void SetUnloadRequestThread(Thread *pThread) { LIMITED_METHOD_CONTRACT; m_pUnloadRequestThread = pThread; } Thread *GetUnloadRequestThread() { LIMITED_METHOD_CONTRACT; return m_pUnloadRequestThread; } public: void SetGCRefPoint(int gccounter) { LIMITED_METHOD_CONTRACT; m_LoaderAllocator.SetGCRefPoint(gccounter); } int GetGCRefPoint() { LIMITED_METHOD_CONTRACT; return m_LoaderAllocator.GetGCRefPoint(); } static USHORT GetOffsetOfId() { LIMITED_METHOD_CONTRACT; size_t ofs = offsetof(class AppDomain, m_dwId); _ASSERTE(FitsInI2(ofs)); return (USHORT)ofs; } void AddMemoryPressure(); void RemoveMemoryPressure(); void Unload(BOOL fForceUnload); static HRESULT UnloadById(ADID Id, BOOL fSync, BOOL fExceptionsPassThrough=FALSE); static HRESULT UnloadWait(ADID Id, ADUnloadSink* pSink); #ifdef FEATURE_TESTHOOKS static HRESULT UnloadWaitNoCatch(ADID Id, ADUnloadSink* pSink); #endif static void ResetUnloadRequestThread(ADID Id); void UnlinkClass(MethodTable *pMT); typedef Holder UnloadHolder; Assembly *GetRootAssembly() { LIMITED_METHOD_CONTRACT; return m_pRootAssembly; } #ifndef DACCESS_COMPILE void SetRootAssembly(Assembly *pAssembly) { LIMITED_METHOD_CONTRACT; m_pRootAssembly = pAssembly; } #endif private: SString m_friendlyName; PTR_Assembly m_pRootAssembly; // General purpose flags. DWORD m_dwFlags; // When an application domain is created the ref count is artifically incremented // by one. For it to hit zero an explicit close must have happened. LONG m_cRef; // Ref count. OBJECTHANDLE m_ExposedObject; #ifdef FEATURE_LOADER_OPTIMIZATION // Indicates where assemblies will be loaded for // this domain. By default all assemblies are loaded into the domain. // There are two additional settings, all // assemblies can be loaded into the shared domain or assemblies // that are strong named are loaded into the shared area. SharePolicy m_SharePolicy; #endif IUnknown *m_pComIPForExposedObject; // Hash table that maps a clsid to a type PtrHashMap m_clsidHash; #ifdef FEATURE_COMINTEROP // Hash table that maps WinRT class names to MethodTables. PTR_NameToTypeMapTable m_pNameToTypeMap; UINT m_vNameToTypeMapVersion; UINT m_nEpoch; // incremented each time m_pNameToTypeMap is enumerated // Hash table that remembers the last cached WinRT factory object per type per appdomain. WinRTFactoryCache *m_pWinRTFactoryCache; // The wrapper cache for this domain - it has its own CCacheLineAllocator on a per domain basis // to allow the domain to go away and eventually kill the memory when all refs are gone ComCallWrapperCache *m_pComCallWrapperCache; // this cache stores the RCWs in this domain RCWCache *m_pRCWCache; // this cache stores the RCW -> CCW references in this domain RCWRefCache *m_pRCWRefCache; // The method table used for LicenseInteropHelper MethodTable* m_pLicenseInteropHelperMT; #endif // FEATURE_COMINTEROP AssemblySink* m_pAsyncPool; // asynchronous retrival object pool (only one is kept) // The index of this app domain among existing app domains (starting from 1) ADIndex m_dwIndex; // The thread-pool index of this app domain among existing app domains (starting from 1) TPIndex m_tpIndex; #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING ULONGLONG* m_pullAllocBytes; ULONGLONG* m_pullSurvivedBytes; DWORD m_dwNumHeaps; ULONGLONG m_ullLastEtwAllocBytes; // Total processor time (user and kernel) utilized by threads running in this AppDomain so far. May not // account for threads currently executing in the AppDomain until a call to QueryProcessorUsage() is // made. Volatile m_ullTotalProcessorUsage; #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING #ifdef _DEBUG struct ThreadTrackInfo; typedef CDynArray ThreadTrackInfoList; ThreadTrackInfoList *m_pThreadTrackInfoList; DWORD m_TrackSpinLock; #endif // IL stub cache with fabricated MethodTable parented by a random module in this AD. ILStubCache m_ILStubCache; // U->M thunks created in this domain and not associated with a delegate. // The cache is keyed by MethodDesc pointers. UMEntryThunkCache *m_pUMEntryThunkCache; // The number of times we have entered this AD ULONG m_dwThreadEnterCount; // The number of threads that have entered this AD, for ADU only ULONG m_dwThreadsStillInAppDomain; Volatile m_Stage; // The default context for this domain Context *m_pDefaultContext; ArrayList m_failedAssemblies; DomainAssembly * m_anonymouslyHostedDynamicMethodsAssembly; #ifdef _DEBUG Volatile m_dwIterHolders; Volatile m_dwRefTakers; Volatile m_dwCreationHolders; #endif // // DAC iterator for failed assembly loads // class FailedAssemblyIterator { ArrayList::Iterator m_i; public: BOOL Next() { WRAPPER_NO_CONTRACT; return m_i.Next(); } FailedAssembly *GetFailedAssembly() { WRAPPER_NO_CONTRACT; return dac_cast(m_i.GetElement()); } SIZE_T GetIndex() { WRAPPER_NO_CONTRACT; return m_i.GetIndex(); } private: friend class AppDomain; // Cannot have constructor so this iterator can be used inside a union static FailedAssemblyIterator Create(AppDomain *pDomain) { WRAPPER_NO_CONTRACT; FailedAssemblyIterator i; i.m_i = pDomain->m_failedAssemblies.Iterate(); return i; } }; friend class FailedAssemblyIterator; FailedAssemblyIterator IterateFailedAssembliesEx() { WRAPPER_NO_CONTRACT; return FailedAssemblyIterator::Create(this); } //--------------------------------------------------------- // Stub caches for Method stubs //--------------------------------------------------------- public: private: Volatile m_fIsBindingModelLocked; public: BOOL IsHostAssemblyResolverInUse(); BOOL IsBindingModelLocked(); BOOL LockBindingModel(); UMEntryThunkCache *GetUMEntryThunkCache(); ILStubCache* GetILStubCache() { LIMITED_METHOD_CONTRACT; return &m_ILStubCache; } static AppDomain* GetDomain(ILStubCache* pILStubCache) { return CONTAINING_RECORD(pILStubCache, AppDomain, m_ILStubCache); } enum { CONTEXT_INITIALIZED = 0x0001, USER_CREATED_DOMAIN = 0x0002, // created by call to AppDomain.CreateDomain ALLOCATEDCOM = 0x0008, LOAD_SYSTEM_ASSEMBLY_EVENT_SENT = 0x0040, REMOTING_CONFIGURED_FOR_DOMAIN = 0x0100, COMPILATION_DOMAIN = 0x0400, // Are we ngenning? APP_DOMAIN_CAN_BE_UNLOADED = 0x0800, // if need extra bits, can derive this at runtime ORPHANED_LOCKS = 0x1000, // Orphaned locks exist in this appdomain. PASSIVE_DOMAIN = 0x2000, // Can we execute code in this AppDomain VERIFICATION_DOMAIN = 0x4000, // This is a verification domain ILLEGAL_VERIFICATION_DOMAIN = 0x8000, // This can't be a verification domain IGNORE_UNHANDLED_EXCEPTIONS = 0x10000, // AppDomain was created using the APPDOMAIN_IGNORE_UNHANDLED_EXCEPTIONS flag ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP = 0x20000, // AppDomain was created using the APPDOMAIN_ENABLE_PINVOKE_AND_CLASSIC_COMINTEROP flag ENABLE_SKIP_PLAT_CHECKS = 0x200000, // Skip various assembly checks (like platform check) ENABLE_ASSEMBLY_LOADFILE = 0x400000, // Allow Assembly.LoadFile in CoreCLR DISABLE_TRANSPARENCY_ENFORCEMENT= 0x800000, // Disable enforcement of security transparency rules }; AssemblySpecBindingCache m_AssemblyCache; DomainAssemblyCache m_UnmanagedCache; size_t m_MemoryPressure; ArrayList m_NativeDllSearchDirectories; BOOL m_ReversePInvokeCanEnter; bool m_ForceTrivialWaitOperations; // Section to support AD unload due to escalation public: static void CreateADUnloadWorker(); static void CreateADUnloadStartEvent(); static DWORD WINAPI ADUnloadThreadStart(void *args); // Default is safe unload with test hook void EnableADUnloadWorker(); // If called to handle stack overflow, we can not set event, since the thread has limit stack. void EnableADUnloadWorker(EEPolicy::AppDomainUnloadTypes type, BOOL fHasStack = TRUE); static void EnableADUnloadWorkerForThreadAbort(); static void EnableADUnloadWorkerForFinalizer(); static void EnableADUnloadWorkerForCollectedADCleanup(); BOOL IsUnloadRequested() { LIMITED_METHOD_CONTRACT; return (m_Stage == STAGE_UNLOAD_REQUESTED); } BOOL IsImageFromTrustedPath(PEImage* pImage); #ifdef FEATURE_TYPEEQUIVALENCE private: VolatilePtr m_pTypeEquivalenceTable; CrstExplicitInit m_TypeEquivalenceCrst; public: TypeEquivalenceHashTable * GetTypeEquivalenceCache(); #endif private: static void ADUnloadWorkerHelper(AppDomain *pDomain); static CLREvent * g_pUnloadStartEvent; #ifdef DACCESS_COMPILE public: virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, bool enumThis); #endif #ifdef FEATURE_MULTICOREJIT private: MulticoreJitManager m_MulticoreJitManager; public: MulticoreJitManager & GetMulticoreJitManager() { LIMITED_METHOD_CONTRACT; return m_MulticoreJitManager; } #endif #if defined(FEATURE_TIERED_COMPILATION) public: TieredCompilationManager * GetTieredCompilationManager() { LIMITED_METHOD_CONTRACT; return &m_tieredCompilationManager; } private: TieredCompilationManager m_tieredCompilationManager; #endif #ifdef FEATURE_COMINTEROP private: #endif //FEATURE_COMINTEROP public: private: // This is the root-level default load context root binder. If null, then // the Fusion binder is used; otherwise this binder is used. ReleaseHolder m_pLoadContextHostBinder; // ------------------------- // IMPORTANT! // The shared and designer context binders are ONLY to be used in tool // scenarios. There are known issues where use of these binders will // cause application crashes, and interesting behaviors. // ------------------------- // This is the default designer shared context root binder. // This is used as the parent binder for ImmersiveDesignerContextBinders ReleaseHolder m_pSharedContextHostBinder; // This is the current context root binder. // Normally, this variable is immutable for appdomain lifetime, but in designer scenarios // it may be replaced by designer context binders Volatile m_pCurrentContextHostBinder; public: // Returns the current hosted binder, or null if none available. inline ICLRPrivBinder * GetCurrentLoadContextHostBinder() const { LIMITED_METHOD_CONTRACT; return m_pCurrentContextHostBinder; } // Returns the shared context binder, or null if none available. inline ICLRPrivBinder * GetSharedContextHostBinder() const { LIMITED_METHOD_CONTRACT; return m_pSharedContextHostBinder; } // Returns the load context binder, or null if none available. inline ICLRPrivBinder * GetLoadContextHostBinder() const { LIMITED_METHOD_CONTRACT; return m_pLoadContextHostBinder; } #ifndef DACCESS_COMPILE // This is only called from the ImmersiveDesignerContext code // It is protected with a managed monitor lock inline void SetSharedContextHostBinder(ICLRPrivBinder * pBinder) { LIMITED_METHOD_CONTRACT; pBinder->AddRef(); m_pSharedContextHostBinder = pBinder; } // This is called from CorHost2's implementation of ICLRPrivRuntime::CreateAppDomain. // Should only be called during AppDomain creation. inline void SetLoadContextHostBinder(ICLRPrivBinder * pBinder) { LIMITED_METHOD_CONTRACT; pBinder->AddRef(); m_pLoadContextHostBinder = m_pCurrentContextHostBinder = pBinder; } inline void SetCurrentContextHostBinder(ICLRPrivBinder * pBinder) { CONTRACTL { THROWS; GC_TRIGGERS; } CONTRACTL_END; LockHolder lh(this); #ifdef FEATURE_COMINTEROP if (m_pNameToTypeMap != nullptr) { delete m_pNameToTypeMap; m_pNameToTypeMap = nullptr; } m_vNameToTypeMapVersion++; #endif m_pCurrentContextHostBinder = pBinder; } #endif // DACCESS_COMPILE // Indicates that a hosted binder is present. inline bool HasLoadContextHostBinder() { LIMITED_METHOD_CONTRACT; return m_pLoadContextHostBinder != nullptr; } class ComInterfaceReleaseList { SArray m_objects; public: ~ComInterfaceReleaseList() { WRAPPER_NO_CONTRACT; for (COUNT_T i = 0; i < m_objects.GetCount(); i++) { IUnknown *pItf = *(m_objects.GetElements() + i); if (pItf != nullptr) pItf->Release(); } } // Append to the list of object to free. Only use under the AppDomain "LockHolder(pAppDomain)" void Append(IUnknown *pInterfaceToRelease) { WRAPPER_NO_CONTRACT; m_objects.Append(pInterfaceToRelease); } } AppDomainInterfaceReleaseList; private: //----------------------------------------------------------- // Static ICLRPrivAssembly -> DomainAssembly mapping functions. // This map does not maintain a reference count to either key or value. // PEFile maintains a reference count on the ICLRPrivAssembly through its code:PEFile::m_pHostAssembly field. // It is removed from this hash table by code:DomainAssembly::~DomainAssembly. struct HostAssemblyHashTraits : public DefaultSHashTraits { public: typedef PTR_ICLRPrivAssembly key_t; static key_t GetKey(element_t const & elem) { STATIC_CONTRACT_WRAPPER; return elem->GetFile()->GetHostAssembly(); } static BOOL Equals(key_t key1, key_t key2) { LIMITED_METHOD_CONTRACT; return dac_cast(key1) == dac_cast(key2); } static count_t Hash(key_t key) { STATIC_CONTRACT_LIMITED_METHOD; //return reinterpret_cast(dac_cast(key)); return (count_t)(dac_cast(key)); } static const element_t Null() { return NULL; } static const element_t Deleted() { return (element_t)(TADDR)-1; } static bool IsNull(const element_t & e) { return e == NULL; } static bool IsDeleted(const element_t & e) { return dac_cast(e) == (TADDR)-1; } }; struct OriginalFileHostAssemblyHashTraits : public HostAssemblyHashTraits { public: static key_t GetKey(element_t const & elem) { STATIC_CONTRACT_WRAPPER; return elem->GetOriginalFile()->GetHostAssembly(); } }; typedef SHash HostAssemblyMap; typedef SHash OriginalFileHostAssemblyMap; HostAssemblyMap m_hostAssemblyMap; OriginalFileHostAssemblyMap m_hostAssemblyMapForOrigFile; CrstExplicitInit m_crstHostAssemblyMap; // Lock to serialize all Add operations (in addition to the "read-lock" above) CrstExplicitInit m_crstHostAssemblyMapAdd; public: // Returns DomainAssembly. PTR_DomainAssembly FindAssembly(PTR_ICLRPrivAssembly pHostAssembly); #ifndef DACCESS_COMPILE private: friend void DomainAssembly::Allocate(); friend DomainAssembly::~DomainAssembly(); // Called from DomainAssembly::Begin. void PublishHostedAssembly( DomainAssembly* pAssembly); // Called from DomainAssembly::UpdatePEFile. void UpdatePublishHostedAssembly( DomainAssembly* pAssembly, PTR_PEFile pFile); // Called from DomainAssembly::~DomainAssembly void UnPublishHostedAssembly( DomainAssembly* pAssembly); #endif // DACCESS_COMPILE #ifdef FEATURE_PREJIT friend void DomainFile::InsertIntoDomainFileWithNativeImageList(); Volatile m_pDomainFileWithNativeImageList; public: DomainFile *GetDomainFilesWithNativeImagesList() { LIMITED_METHOD_CONTRACT; return m_pDomainFileWithNativeImageList; } #endif }; // class AppDomain // This holder is to be used to take a reference to make sure AppDomain* is still valid // Please do not use if you are aleady ADU-safe typedef Wrapper AppDomainRefTaker; // Just a ref holder typedef ReleaseHolder AppDomainRefHolder; // This class provides a way to access AppDomain by ID // without risking the appdomain getting invalid in the process class AppDomainFromIDHolder { public: enum SyncType { SyncType_GC, // Prevents AD from being unloaded by forbidding GC for the lifetime of the object SyncType_ADLock // Prevents AD from being unloaded by requiring ownership of DomainLock for the lifetime of the object }; protected: AppDomain* m_pDomain; #ifdef _DEBUG BOOL m_bAcquired; BOOL m_bChecked; SyncType m_type; #endif public: DEBUG_NOINLINE AppDomainFromIDHolder(ADID adId, BOOL bUnsafePoint, SyncType synctype=SyncType_GC); DEBUG_NOINLINE AppDomainFromIDHolder(SyncType synctype=SyncType_GC); DEBUG_NOINLINE ~AppDomainFromIDHolder(); void* GetAddress() { return m_pDomain; } // Used to get an identfier for ETW void Assign(ADID adId, BOOL bUnsafePoint); void ThrowIfUnloaded(); void Release(); BOOL IsUnloaded() { LIMITED_METHOD_CONTRACT; #ifdef _DEBUG m_bChecked=TRUE; if (m_pDomain==NULL) { // no need to enforce anything Release(); } #endif return m_pDomain==NULL; }; AppDomain* operator->(); }; // class AppDomainFromIDHolder typedef VPTR(class SystemDomain) PTR_SystemDomain; class SystemDomain : public BaseDomain { friend class AppDomainNative; friend class AppDomainIterator; friend class UnsafeAppDomainIterator; friend class ClrDataAccess; friend class AppDomainFromIDHolder; friend Frame *Thread::IsRunningIn(AppDomain* pDomain, int *count); VPTR_VTABLE_CLASS(SystemDomain, BaseDomain) VPTR_UNIQUE(VPTR_UNIQUE_SystemDomain) static AppDomain *GetAppDomainAtId(ADID indx); public: static PTR_LoaderAllocator GetGlobalLoaderAllocator(); virtual PTR_LoaderAllocator GetLoaderAllocator() { WRAPPER_NO_CONTRACT; return GetGlobalLoaderAllocator(); } static AppDomain* GetAppDomainFromId(ADID indx,DWORD ADValidityKind) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_COOPERATIVE; } CONTRACTL_END; AppDomain* pRetVal; if (indx.m_dwId==DefaultADID) pRetVal= SystemDomain::System()->DefaultDomain(); else pRetVal= GetAppDomainAtId(indx); #ifdef _DEBUG // Only call CheckADValidity in DEBUG builds for non-NULL return values if (pRetVal != NULL) CheckADValidity(pRetVal, ADValidityKind); #endif return pRetVal; } //**************************************************************************************** // // To be run during the initial start up of the EE. This must be // performed prior to any class operations. static void Attach(); //**************************************************************************************** // // To be run during shutdown. This must be done after all operations // that require the use of system classes (i.e., exceptions). // DetachBegin stops all domains, while DetachEnd deallocates domain resources. static void DetachBegin(); //**************************************************************************************** // // To be run during shutdown. This must be done after all operations // that require the use of system classes (i.e., exceptions). // DetachBegin stops release resources held by systemdomain and the default domain. static void DetachEnd(); //**************************************************************************************** // // Initializes and shutdowns the single instance of the SystemDomain // in the EE #ifndef DACCESS_COMPILE void *operator new(size_t size, void *pInPlace); void operator delete(void *pMem); #endif void Init(); void Stop(); void Terminate(); static void LazyInitGlobalStringLiteralMap(); //**************************************************************************************** // // Load the base system classes, these classes are required before // any other classes are loaded void LoadBaseSystemClasses(); AppDomain* DefaultDomain() { LIMITED_METHOD_DAC_CONTRACT; return m_pDefaultDomain; } // Notification when an assembly is loaded into the system domain void OnAssemblyLoad(Assembly *assem); //**************************************************************************************** // // Global Static to get the one and only system domain static SystemDomain * System() { LIMITED_METHOD_DAC_CONTRACT; return m_pSystemDomain; } static PEAssembly* SystemFile() { WRAPPER_NO_CONTRACT; _ASSERTE(m_pSystemDomain); return System()->m_pSystemFile; } static Assembly* SystemAssembly() { WRAPPER_NO_CONTRACT; return System()->m_pSystemAssembly; } static Module* SystemModule() { WRAPPER_NO_CONTRACT; return SystemAssembly()->GetManifestModule(); } static BOOL IsSystemLoaded() { WRAPPER_NO_CONTRACT; return System()->m_pSystemAssembly != NULL; } #ifndef DACCESS_COMPILE static GlobalStringLiteralMap *GetGlobalStringLiteralMap() { WRAPPER_NO_CONTRACT; if (m_pGlobalStringLiteralMap == NULL) { SystemDomain::LazyInitGlobalStringLiteralMap(); } _ASSERTE(m_pGlobalStringLiteralMap); return m_pGlobalStringLiteralMap; } static GlobalStringLiteralMap *GetGlobalStringLiteralMapNoCreate() { LIMITED_METHOD_CONTRACT; _ASSERTE(m_pGlobalStringLiteralMap); return m_pGlobalStringLiteralMap; } #endif // DACCESS_COMPILE static void ActivateApplication(int *pReturnValue); static void InitializeDefaultDomain(BOOL allowRedirects, ICLRPrivBinder * pBinder = NULL); static void SetupDefaultDomain(); static HRESULT SetupDefaultDomainNoThrow(); #if defined(FEATURE_COMINTEROP_APARTMENT_SUPPORT) && !defined(CROSSGEN_COMPILE) static Thread::ApartmentState GetEntryPointThreadAptState(IMDInternalImport* pScope, mdMethodDef mdMethod); static void SetThreadAptState(Thread::ApartmentState state); #endif static BOOL SetGlobalSharePolicyUsingAttribute(IMDInternalImport* pScope, mdMethodDef mdMethod); //**************************************************************************************** // // Use an already exising & inited Application Domain (e.g. a subclass). static void LoadDomain(AppDomain *pDomain); #ifndef DACCESS_COMPILE static void MakeUnloadable(AppDomain* pApp) { WRAPPER_NO_CONTRACT; System()->AddDomain(pApp); pApp->SetCanUnload(); } #endif // DACCESS_COMPILE //**************************************************************************************** // Methods used to get the callers module and hence assembly and app domain. __declspec(deprecated("This method is deprecated, use the version that takes a StackCrawlMark instead")) static Module* GetCallersModule(int skip); static MethodDesc* GetCallersMethod(StackCrawlMark* stackMark, AppDomain **ppAppDomain = NULL); static MethodTable* GetCallersType(StackCrawlMark* stackMark, AppDomain **ppAppDomain = NULL); static Module* GetCallersModule(StackCrawlMark* stackMark, AppDomain **ppAppDomain = NULL); static Assembly* GetCallersAssembly(StackCrawlMark* stackMark, AppDomain **ppAppDomain = NULL); static bool IsReflectionInvocationMethod(MethodDesc* pMeth); #ifndef DACCESS_COMPILE //**************************************************************************************** // Returns the domain associated with the current context. (this can only be a child domain) static inline AppDomain * GetCurrentDomain() { WRAPPER_NO_CONTRACT; return ::GetAppDomain(); } #endif //!DACCESS_COMPILE #ifdef DEBUGGING_SUPPORTED //**************************************************************************************** // Debugger/Publisher helper function to indicate creation of new app domain to debugger // and publishing it in the IPC block static void PublishAppDomainAndInformDebugger (AppDomain *pDomain); #endif // DEBUGGING_SUPPORTED //**************************************************************************************** // Helper function to remove a domain from the system BOOL RemoveDomain(AppDomain* pDomain); // Does not decrement the reference #ifdef PROFILING_SUPPORTED //**************************************************************************************** // Tell profiler about system created domains which are created before the profiler is // actually activated. static void NotifyProfilerStartup(); //**************************************************************************************** // Tell profiler at shutdown that system created domains are going away. They are not // torn down using the normal sequence. static HRESULT NotifyProfilerShutdown(); #endif // PROFILING_SUPPORTED //**************************************************************************************** // return the dev path #ifndef DACCESS_COMPILE void IncrementNumAppDomains () { LIMITED_METHOD_CONTRACT; s_dNumAppDomains++; } void DecrementNumAppDomains () { LIMITED_METHOD_CONTRACT; s_dNumAppDomains--; } ULONG GetNumAppDomains () { LIMITED_METHOD_CONTRACT; return s_dNumAppDomains; } #endif // DACCESS_COMPILE // // AppDomains currently have both an index and an ID. The // index is "densely" assigned; indices are reused as domains // are unloaded. The Id's on the other hand, are not reclaimed // so may be sparse. // // Another important difference - it's OK to call GetAppDomainAtId for // an unloaded domain (it will return NULL), while GetAppDomainAtIndex // will assert if the domain is unloaded. // // @todo: // I'm not really happy with this situation, but // (a) we need an ID for a domain which will last the process lifetime for the // remoting code. // (b) we need a dense ID, for the handle table index. // So for now, I'm leaving both, but hopefully in the future we can come up // with something better. // static ADIndex GetNewAppDomainIndex(AppDomain * pAppDomain); static void ReleaseAppDomainIndex(ADIndex indx); static PTR_AppDomain GetAppDomainAtIndex(ADIndex indx); static PTR_AppDomain TestGetAppDomainAtIndex(ADIndex indx); static DWORD GetCurrentAppDomainMaxIndex() { WRAPPER_NO_CONTRACT; ArrayListStatic* list = (ArrayListStatic *)&m_appDomainIndexList; PREFIX_ASSUME(list!=NULL); return list->GetCount(); } static ADID GetNewAppDomainId(AppDomain *pAppDomain); static void ReleaseAppDomainId(ADID indx); #ifndef DACCESS_COMPILE static ADID GetCurrentAppDomainMaxId() { ADID id; id.m_dwId=m_appDomainIdList.GetCount(); return id;} #endif // DACCESS_COMPILE #ifndef DACCESS_COMPILE DWORD RequireAppDomainCleanup() { LIMITED_METHOD_CONTRACT; return m_pDelayedUnloadList != 0 || m_pDelayedUnloadListOfLoaderAllocators != 0; } void AddToDelayedUnloadList(AppDomain* pDomain, BOOL bAsync) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_COOPERATIVE; } CONTRACTL_END; m_UnloadIsAsync = bAsync; CrstHolder lh(&m_DelayedUnloadCrst); pDomain->m_pNextInDelayedUnloadList=m_pDelayedUnloadList; m_pDelayedUnloadList=pDomain; if (m_UnloadIsAsync) { pDomain->AddRef(); int iGCRefPoint=GCHeapUtilities::GetGCHeap()->CollectionCount(GCHeapUtilities::GetGCHeap()->GetMaxGeneration()); if (GCHeapUtilities::IsGCInProgress()) iGCRefPoint++; pDomain->SetGCRefPoint(iGCRefPoint); } } void AddToDelayedUnloadList(LoaderAllocator * pAllocator) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_COOPERATIVE; } CONTRACTL_END; CrstHolder lh(&m_DelayedUnloadCrst); pAllocator->m_pLoaderAllocatorDestroyNext=m_pDelayedUnloadListOfLoaderAllocators; m_pDelayedUnloadListOfLoaderAllocators=pAllocator; int iGCRefPoint=GCHeapUtilities::GetGCHeap()->CollectionCount(GCHeapUtilities::GetGCHeap()->GetMaxGeneration()); if (GCHeapUtilities::IsGCInProgress()) iGCRefPoint++; pAllocator->SetGCRefPoint(iGCRefPoint); } void ClearCollectedDomains(); void ProcessClearingDomains(); void ProcessDelayedUnloadDomains(); static void SetUnloadInProgress(AppDomain *pDomain) { WRAPPER_NO_CONTRACT; _ASSERTE(m_pAppDomainBeingUnloaded == NULL); m_pAppDomainBeingUnloaded = pDomain; m_dwIndexOfAppDomainBeingUnloaded = pDomain->GetIndex(); } static void SetUnloadDomainCleared() { LIMITED_METHOD_CONTRACT; // about to delete, so clear this pointer so nobody uses it m_pAppDomainBeingUnloaded = NULL; } static void SetUnloadComplete() { LIMITED_METHOD_CONTRACT; // should have already cleared the AppDomain* prior to delete // either we succesfully unloaded and cleared or we failed and restored the ID _ASSERTE(m_pAppDomainBeingUnloaded == NULL && m_dwIndexOfAppDomainBeingUnloaded.m_dwIndex != 0 || m_pAppDomainBeingUnloaded && SystemDomain::GetAppDomainAtId(m_pAppDomainBeingUnloaded->GetId()) != NULL); m_pAppDomainBeingUnloaded = NULL; m_pAppDomainUnloadingThread = NULL; } static AppDomain *AppDomainBeingUnloaded() { LIMITED_METHOD_CONTRACT; return m_pAppDomainBeingUnloaded; } static ADIndex IndexOfAppDomainBeingUnloaded() { LIMITED_METHOD_CONTRACT; return m_dwIndexOfAppDomainBeingUnloaded; } static void SetUnloadRequestingThread(Thread *pRequestingThread) { LIMITED_METHOD_CONTRACT; m_pAppDomainUnloadRequestingThread = pRequestingThread; } static Thread *GetUnloadRequestingThread() { LIMITED_METHOD_CONTRACT; return m_pAppDomainUnloadRequestingThread; } static void SetUnloadingThread(Thread *pUnloadingThread) { LIMITED_METHOD_CONTRACT; m_pAppDomainUnloadingThread = pUnloadingThread; } static Thread *GetUnloadingThread() { LIMITED_METHOD_CONTRACT; return m_pAppDomainUnloadingThread; } static void EnumAllStaticGCRefs(promote_func* fn, ScanContext* sc); #endif // DACCESS_COMPILE #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING // The *AD* methods are what we got from tracing through EE roots. // RecordTotalSurvivedBytes is the total promoted from a GC. static void ResetADSurvivedBytes(); static ULONGLONG GetADSurvivedBytes(); static void RecordTotalSurvivedBytes(size_t totalSurvivedBytes); static ULONGLONG GetTotalSurvivedBytes() { LIMITED_METHOD_CONTRACT; return m_totalSurvivedBytes; } #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING //**************************************************************************************** // Routines to deal with the base library (currently mscorlib.dll) LPCWSTR BaseLibrary() { WRAPPER_NO_CONTRACT; return m_BaseLibrary; } #ifndef DACCESS_COMPILE BOOL IsBaseLibrary(SString &path) { WRAPPER_NO_CONTRACT; // See if it is the installation path to mscorlib if (path.EqualsCaseInsensitive(m_BaseLibrary, PEImage::GetFileSystemLocale())) return TRUE; // Or, it might be the GAC location of mscorlib if (System()->SystemAssembly() != NULL && path.EqualsCaseInsensitive(System()->SystemAssembly()->GetManifestFile()->GetPath(), PEImage::GetFileSystemLocale())) return TRUE; return FALSE; } BOOL IsBaseLibrarySatellite(SString &path) { WRAPPER_NO_CONTRACT; // See if it is the installation path to mscorlib.resources SString s(SString::Ascii,g_psBaseLibrarySatelliteAssemblyName); if (path.EqualsCaseInsensitive(s, PEImage::GetFileSystemLocale())) return TRUE; // workaround! Must implement some code to do this string comparison for // mscorlib.resources in a culture-specific directory in the GAC. /* // Or, it might be the GAC location of mscorlib.resources if (System()->SystemAssembly() != NULL && path.EqualsCaseInsensitive(System()->SystemAssembly()->GetManifestFile()->GetPath(), PEImage::GetFileSystemLocale())) return TRUE; */ return FALSE; } #endif // DACCESS_COMPILE // Return the system directory LPCWSTR SystemDirectory() { WRAPPER_NO_CONTRACT; return m_SystemDirectory; } private: //**************************************************************************************** // Helper function to create the single COM domain void CreateDefaultDomain(); //**************************************************************************************** // Helper function to add a domain to the global list void AddDomain(AppDomain* pDomain); void CreatePreallocatedExceptions(); void PreallocateSpecialObjects(); //**************************************************************************************** // static StackWalkAction CallersMethodCallback(CrawlFrame* pCrawlFrame, VOID* pClientData); static StackWalkAction CallersMethodCallbackWithStackMark(CrawlFrame* pCrawlFrame, VOID* pClientData); #ifndef DACCESS_COMPILE // This class is not to be created through normal allocation. SystemDomain() { STANDARD_VM_CONTRACT; m_pDefaultDomain = NULL; m_pDelayedUnloadList=NULL; m_pDelayedUnloadListOfLoaderAllocators=NULL; m_UnloadIsAsync = FALSE; m_GlobalAllocator.Init(this); } #endif PTR_PEAssembly m_pSystemFile; // Single assembly (here for quicker reference); PTR_Assembly m_pSystemAssembly; // Single assembly (here for quicker reference); PTR_AppDomain m_pDefaultDomain; // Default domain for COM+ classes exposed through IClassFactory. GlobalLoaderAllocator m_GlobalAllocator; InlineSString<100> m_BaseLibrary; InlineSString<100> m_SystemDirectory; LPWSTR m_pwDevpath; DWORD m_dwDevpath; BOOL m_fDevpath; // have we searched the environment // @TODO: CTS, we can keep the com modules in a single assembly or in different assemblies. // We are currently using different assemblies but this is potentitially to slow... // Global domain that every one uses SPTR_DECL(SystemDomain, m_pSystemDomain); AppDomain* m_pDelayedUnloadList; BOOL m_UnloadIsAsync; LoaderAllocator * m_pDelayedUnloadListOfLoaderAllocators; #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING // This is what gets promoted for the whole GC heap. static size_t m_totalSurvivedBytes; #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING SVAL_DECL(ArrayListStatic, m_appDomainIndexList); #ifndef DACCESS_COMPILE static CrstStatic m_DelayedUnloadCrst; static CrstStatic m_SystemDomainCrst; static ArrayListStatic m_appDomainIdList; // only one ad can be unloaded at a time static AppDomain* m_pAppDomainBeingUnloaded; // need this so can determine AD being unloaded after it has been deleted static ADIndex m_dwIndexOfAppDomainBeingUnloaded; // if had to spin off a separate thread to do the unload, this is the original thread. // allows us to delay aborting it until it's the last one so that it can receive // notification of an unload failure static Thread *m_pAppDomainUnloadRequestingThread; // this is the thread doing the actual unload. He's allowed to enter the domain // even if have started unloading. static Thread *m_pAppDomainUnloadingThread; static GlobalStringLiteralMap *m_pGlobalStringLiteralMap; static ULONG s_dNumAppDomains; // Maintain a count of children app domains. static DWORD m_dwLowestFreeIndex; #endif // DACCESS_COMPILE protected: // These flags let the correct native image of mscorlib to be loaded. // This is important for hardbinding to it SVAL_DECL(BOOL, s_fForceDebug); SVAL_DECL(BOOL, s_fForceProfiling); SVAL_DECL(BOOL, s_fForceInstrument); public: static void SetCompilationOverrides(BOOL fForceDebug, BOOL fForceProfiling, BOOL fForceInstrument); static void GetCompilationOverrides(BOOL * fForceDebug, BOOL * fForceProfiling, BOOL * fForceInstrument); public: //**************************************************************************************** // #ifndef DACCESS_COMPILE #ifdef _DEBUG inline static BOOL IsUnderDomainLock() { LIMITED_METHOD_CONTRACT; return m_SystemDomainCrst.OwnedByCurrentThread();}; #endif // This lock controls adding and removing domains from the system domain class LockHolder : public CrstHolder { public: LockHolder() : CrstHolder(&m_SystemDomainCrst) { WRAPPER_NO_CONTRACT; } }; #endif // DACCESS_COMPILE public: DWORD GetTotalNumSizedRefHandles(); #ifdef DACCESS_COMPILE public: virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, bool enumThis); #endif }; // class SystemDomain // // an UnsafeAppDomainIterator is used to iterate over all existing domains // // The iteration is guaranteed to include all domains that exist at the // start & end of the iteration. This iterator is considered unsafe because it does not // reference count the various appdomains, and can only be used when the runtime is stopped, // or external synchronization is used. (and therefore no other thread may cause the appdomain list to change.) // class UnsafeAppDomainIterator { friend class SystemDomain; public: UnsafeAppDomainIterator(BOOL bOnlyActive) { m_bOnlyActive = bOnlyActive; } void Init() { LIMITED_METHOD_CONTRACT; SystemDomain* sysDomain = SystemDomain::System(); if (sysDomain) { ArrayListStatic* list = &sysDomain->m_appDomainIndexList; PREFIX_ASSUME(list != NULL); m_i = list->Iterate(); } else { m_i.SetEmpty(); } m_pCurrent = NULL; } BOOL Next() { WRAPPER_NO_CONTRACT; while (m_i.Next()) { m_pCurrent = dac_cast(m_i.GetElement()); if (m_pCurrent != NULL && (m_bOnlyActive ? m_pCurrent->IsActive() : m_pCurrent->IsValid())) { return TRUE; } } m_pCurrent = NULL; return FALSE; } AppDomain * GetDomain() { LIMITED_METHOD_DAC_CONTRACT; return m_pCurrent; } private: ArrayList::Iterator m_i; AppDomain * m_pCurrent; BOOL m_bOnlyActive; }; // class UnsafeAppDomainIterator // // an AppDomainIterator is used to iterate over all existing domains. // // The iteration is guaranteed to include all domains that exist at the // start & end of the iteration. Any domains added or deleted during // iteration may or may not be included. The iterator also guarantees // that the current iterated appdomain (GetDomain()) will not be deleted. // class AppDomainIterator : public UnsafeAppDomainIterator { friend class SystemDomain; public: AppDomainIterator(BOOL bOnlyActive) : UnsafeAppDomainIterator(bOnlyActive) { WRAPPER_NO_CONTRACT; Init(); } ~AppDomainIterator() { WRAPPER_NO_CONTRACT; #ifndef DACCESS_COMPILE if (GetDomain() != NULL) { #ifdef _DEBUG GetDomain()->IteratorRelease(); #endif GetDomain()->Release(); } #endif } BOOL Next() { WRAPPER_NO_CONTRACT; #ifndef DACCESS_COMPILE if (GetDomain() != NULL) { #ifdef _DEBUG GetDomain()->IteratorRelease(); #endif GetDomain()->Release(); } SystemDomain::LockHolder lh; #endif if (UnsafeAppDomainIterator::Next()) { #ifndef DACCESS_COMPILE GetDomain()->AddRef(); #ifdef _DEBUG GetDomain()->IteratorAcquire(); #endif #endif return TRUE; } return FALSE; } }; // class AppDomainIterator typedef VPTR(class SharedDomain) PTR_SharedDomain; class SharedDomain : public BaseDomain { VPTR_VTABLE_CLASS_AND_CTOR(SharedDomain, BaseDomain) public: static void Attach(); static void Detach(); virtual BOOL IsSharedDomain() { LIMITED_METHOD_DAC_CONTRACT; return TRUE; } virtual PTR_LoaderAllocator GetLoaderAllocator() { WRAPPER_NO_CONTRACT; return SystemDomain::GetGlobalLoaderAllocator(); } virtual PTR_AppDomain AsAppDomain() { LIMITED_METHOD_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; _ASSERTE(!"Not an AppDomain"); return NULL; } static SharedDomain * GetDomain(); void Init(); void Terminate(); // This will also set the tenured bit if and only if the add was successful, // and will make sure that the bit appears atomically set to all readers that // might be accessing the hash on another thread. MethodTable * FindIndexClass(SIZE_T index); #ifdef FEATURE_LOADER_OPTIMIZATION void AddShareableAssembly(Assembly * pAssembly); class SharedAssemblyIterator { PtrHashMap::PtrIterator i; Assembly * m_pAssembly; public: SharedAssemblyIterator() : i(GetDomain() ? GetDomain()->m_assemblyMap.firstBucket() : NULL) { LIMITED_METHOD_DAC_CONTRACT; } BOOL Next() { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; if (i.end()) return FALSE; m_pAssembly = PTR_Assembly(dac_cast(i.GetValue())); ++i; return TRUE; } Assembly * GetAssembly() { LIMITED_METHOD_DAC_CONTRACT; return m_pAssembly; } private: friend class SharedDomain; }; Assembly * FindShareableAssembly(SharedAssemblyLocator * pLocator); SIZE_T GetShareableAssemblyCount(); #endif //FEATURE_LOADER_OPTIMIZATION private: friend class SharedAssemblyIterator; friend class SharedFileLockHolder; friend class ClrDataAccess; #ifndef DACCESS_COMPILE void *operator new(size_t size, void *pInPlace); void operator delete(void *pMem); #endif SPTR_DECL(SharedDomain, m_pSharedDomain); #ifdef FEATURE_LOADER_OPTIMIZATION PEFileListLock m_FileCreateLock; SIZE_T m_nextClassIndex; PtrHashMap m_assemblyMap; #endif public: #ifdef DACCESS_COMPILE virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags, bool enumThis); #endif #ifdef FEATURE_LOADER_OPTIMIZATION // Hash map comparison function` static BOOL CompareSharedAssembly(UPTR u1, UPTR u2); #endif }; #ifdef FEATURE_LOADER_OPTIMIZATION class SharedFileLockHolderBase : protected HolderBase { protected: PEFileListLock *m_pLock; ListLockEntry *m_pLockElement; SharedFileLockHolderBase(PEFile *value) : HolderBase(value) { LIMITED_METHOD_CONTRACT; m_pLock = NULL; m_pLockElement = NULL; } #ifndef DACCESS_COMPILE void DoAcquire() { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_TRIGGERS; STATIC_CONTRACT_FAULT; PEFileListLockHolder lockHolder(m_pLock); m_pLockElement = m_pLock->FindFileLock(m_value); if (m_pLockElement == NULL) { m_pLockElement = new ListLockEntry(m_pLock, m_value); m_pLock->AddElement(m_pLockElement); } else m_pLockElement->AddRef(); lockHolder.Release(); m_pLockElement->Enter(); } void DoRelease() { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_TRIGGERS; STATIC_CONTRACT_FORBID_FAULT; m_pLockElement->Leave(); m_pLockElement->Release(); m_pLockElement = NULL; } #endif // DACCESS_COMPILE }; class SharedFileLockHolder : public BaseHolder { public: DEBUG_NOINLINE SharedFileLockHolder(SharedDomain *pDomain, PEFile *pFile, BOOL Take = TRUE) : BaseHolder(pFile, FALSE) { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_TRIGGERS; STATIC_CONTRACT_FAULT; ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT; m_pLock = &pDomain->m_FileCreateLock; if (Take) Acquire(); } }; #endif // FEATURE_LOADER_OPTIMIZATION inline BOOL BaseDomain::IsDefaultDomain() { LIMITED_METHOD_DAC_CONTRACT; return (SystemDomain::System()->DefaultDomain() == this); } #include "comreflectioncache.inl" #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) // holds an extra reference so needs special Extract() and should not have SuppressRelease() // Holders/Wrappers have nonvirtual methods so cannot use them as the base class template class AppDomainCreationHolder { private: // disable the copy ctor AppDomainCreationHolder(const AppDomainCreationHolder&) {} protected: AppDomainType* m_pDomain; BOOL m_bAcquired; void ReleaseAppDomainDuringCreation() { CONTRACTL { NOTHROW; WRAPPER(GC_TRIGGERS); PRECONDITION(m_bAcquired); PRECONDITION(CheckPointer(m_pDomain)); } CONTRACTL_END; if (m_pDomain->NotReadyForManagedCode()) { m_pDomain->Release(); } else { STRESS_LOG2 (LF_APPDOMAIN, LL_INFO100, "Unload domain during creation [%d] %p\n", m_pDomain->GetId().m_dwId, m_pDomain); SystemDomain::MakeUnloadable(m_pDomain); #ifdef _DEBUG DWORD hostTestADUnload = g_pConfig->GetHostTestADUnload(); m_pDomain->EnableADUnloadWorker(hostTestADUnload != 2?EEPolicy::ADU_Safe:EEPolicy::ADU_Rude); #else m_pDomain->EnableADUnloadWorker(EEPolicy::ADU_Safe); #endif } }; public: AppDomainCreationHolder() { m_pDomain=NULL; m_bAcquired=FALSE; }; ~AppDomainCreationHolder() { if (m_bAcquired) { Release(); } }; void Assign(AppDomainType* pDomain) { if(m_bAcquired) Release(); m_pDomain=pDomain; if(m_pDomain) { AppDomain::RefTakerAcquire(m_pDomain); #ifdef _DEBUG m_pDomain->IncCreationCount(); #endif // _DEBUG } m_bAcquired=TRUE; }; void Release() { _ASSERTE(m_bAcquired); if(m_pDomain) { #ifdef _DEBUG m_pDomain->DecCreationCount(); #endif // _DEBUG if(!m_pDomain->IsDefaultDomain()) ReleaseAppDomainDuringCreation(); AppDomain::RefTakerRelease(m_pDomain); }; m_bAcquired=FALSE; }; AppDomainType* Extract() { _ASSERTE(m_bAcquired); if(m_pDomain) { #ifdef _DEBUG m_pDomain->DecCreationCount(); #endif // _DEBUG AppDomain::RefTakerRelease(m_pDomain); } m_bAcquired=FALSE; return m_pDomain; }; AppDomainType* operator ->() { _ASSERTE(m_bAcquired); return m_pDomain; } operator AppDomainType*() { _ASSERTE(m_bAcquired); return m_pDomain; } void DoneCreating() { Extract(); } }; #endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE #define INVALID_APPDOMAIN_ID ((DWORD)-1) #define CURRENT_APPDOMAIN_ID ((ADID)(DWORD)0) #endif