Permalink
Fetching contributors…
Cannot retrieve contributors at this time
11765 lines (9760 sloc) 348 KB
// 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.
#include "common.h"
#include "appdomain.hpp"
#include "peimagelayout.inl"
#include "field.h"
#include "strongnameinternal.h"
#include "excep.h"
#include "eeconfig.h"
#include "gcheaputilities.h"
#include "eventtrace.h"
#include "perfcounters.h"
#include "assemblyname.hpp"
#include "eeprofinterfaces.h"
#include "dbginterface.h"
#ifndef DACCESS_COMPILE
#include "eedbginterfaceimpl.h"
#endif
#include "comdynamic.h"
#include "mlinfo.h"
#include "posterror.h"
#include "assemblynative.hpp"
#include "shimload.h"
#include "stringliteralmap.h"
#include "codeman.h"
#include "comcallablewrapper.h"
#include "apithreadstress.h"
#include "eventtrace.h"
#include "comdelegate.h"
#include "siginfo.hpp"
#include "typekey.h"
#include "caparser.h"
#include "ecall.h"
#include "finalizerthread.h"
#include "threadsuspend.h"
#ifdef FEATURE_PREJIT
#include "corcompile.h"
#include "compile.h"
#endif // FEATURE_PREJIT
#ifdef FEATURE_COMINTEROP
#include "comtoclrcall.h"
#include "runtimecallablewrapper.h"
#include "mngstdinterfaces.h"
#include "olevariant.h"
#include "rcwrefcache.h"
#include "olecontexthelpers.h"
#endif // FEATURE_COMINTEROP
#ifdef FEATURE_TYPEEQUIVALENCE
#include "typeequivalencehash.hpp"
#endif
#include "appdomain.inl"
#include "typeparse.h"
#include "mdaassistants.h"
#include "threadpoolrequest.h"
#include "nativeoverlapped.h"
#ifndef FEATURE_PAL
#include "dwreport.h"
#endif // !FEATURE_PAL
#include "stringarraylist.h"
#include "../binder/inc/clrprivbindercoreclr.h"
#include "clrprivtypecachewinrt.h"
// this file handles string conversion errors for itself
#undef MAKE_TRANSLATIONFAILED
// Define these macro's to do strict validation for jit lock and class
// init entry leaks. This defines determine if the asserts that
// verify for these leaks are defined or not. These asserts can
// sometimes go off even if no entries have been leaked so this
// defines should be used with caution.
//
// If we are inside a .cctor when the application shut's down then the
// class init lock's head will be set and this will cause the assert
// to go off.
//
// If we are jitting a method when the application shut's down then
// the jit lock's head will be set causing the assert to go off.
//#define STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
static const WCHAR DEFAULT_DOMAIN_FRIENDLY_NAME[] = W("DefaultDomain");
static const WCHAR OTHER_DOMAIN_FRIENDLY_NAME_PREFIX[] = W("Domain");
#define STATIC_OBJECT_TABLE_BUCKET_SIZE 1020
#define MAX_URL_LENGTH 2084 // same as INTERNET_MAX_URL_LENGTH
//#define _DEBUG_ADUNLOAD 1
HRESULT RunDllMain(MethodDesc *pMD, HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved); // clsload.cpp
// Statics
SPTR_IMPL(SystemDomain, SystemDomain, m_pSystemDomain);
SVAL_IMPL(ArrayListStatic, SystemDomain, m_appDomainIndexList);
SPTR_IMPL(SharedDomain, SharedDomain, m_pSharedDomain);
SVAL_IMPL(BOOL, SystemDomain, s_fForceDebug);
SVAL_IMPL(BOOL, SystemDomain, s_fForceProfiling);
SVAL_IMPL(BOOL, SystemDomain, s_fForceInstrument);
#ifndef DACCESS_COMPILE
// Base Domain Statics
CrstStatic BaseDomain::m_SpecialStaticsCrst;
int BaseDomain::m_iNumberOfProcessors = 0;
// Shared Domain Statics
DECLSPEC_ALIGN(16)
static BYTE g_pSharedDomainMemory[sizeof(SharedDomain)];
// System Domain Statics
GlobalStringLiteralMap* SystemDomain::m_pGlobalStringLiteralMap = NULL;
DECLSPEC_ALIGN(16)
static BYTE g_pSystemDomainMemory[sizeof(SystemDomain)];
#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
size_t SystemDomain::m_totalSurvivedBytes = 0;
#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
CrstStatic SystemDomain::m_SystemDomainCrst;
CrstStatic SystemDomain::m_DelayedUnloadCrst;
ULONG SystemDomain::s_dNumAppDomains = 0;
AppDomain * SystemDomain::m_pAppDomainBeingUnloaded = NULL;
ADIndex SystemDomain::m_dwIndexOfAppDomainBeingUnloaded;
Thread *SystemDomain::m_pAppDomainUnloadRequestingThread = 0;
Thread *SystemDomain::m_pAppDomainUnloadingThread = 0;
ArrayListStatic SystemDomain::m_appDomainIdList;
DWORD SystemDomain::m_dwLowestFreeIndex = 0;
// comparison function to be used for matching clsids in our clsid hash table
BOOL CompareCLSID(UPTR u1, UPTR u2)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
SO_INTOLERANT;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
GUID *pguid = (GUID *)(u1 << 1);
_ASSERTE(pguid != NULL);
MethodTable *pMT= (MethodTable *)u2;
_ASSERTE(pMT!= NULL);
GUID guid;
pMT->GetGuid(&guid, TRUE);
if (!IsEqualIID(guid, *pguid))
return FALSE;
return TRUE;
}
#ifndef CROSSGEN_COMPILE
// Constructor for the LargeHeapHandleBucket class.
LargeHeapHandleBucket::LargeHeapHandleBucket(LargeHeapHandleBucket *pNext, DWORD Size, BaseDomain *pDomain, BOOL bCrossAD)
: m_pNext(pNext)
, m_ArraySize(Size)
, m_CurrentPos(0)
, m_CurrentEmbeddedFreePos(0) // hint for where to start a search for an embedded free item
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pDomain));
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
PTRARRAYREF HandleArrayObj;
// Allocate the array in the large object heap.
if (!bCrossAD)
{
OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
HandleArrayObj = (PTRARRAYREF)AllocateObjectArray(Size, g_pObjectClass, TRUE);
}
else
{
// During AD creation we don't want to assign the handle array to the currently running AD but
// to the AD being created. Ensure that AllocateArrayEx doesn't set the AD and then set it here.
AppDomain *pAD = pDomain->AsAppDomain();
_ASSERTE(pAD);
_ASSERTE(pAD->IsBeingCreated());
OBJECTREF array;
{
OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
array = AllocateArrayEx(
ClassLoader::LoadArrayTypeThrowing(g_pObjectClass),
(INT32 *)(&Size),
1,
TRUE
DEBUG_ARG(TRUE));
}
array->SetAppDomain(pAD);
HandleArrayObj = (PTRARRAYREF)array;
}
// Retrieve the pointer to the data inside the array. This is legal since the array
// is located in the large object heap and is guaranteed not to move.
m_pArrayDataPtr = (OBJECTREF *)HandleArrayObj->GetDataPtr();
// Store the array in a strong handle to keep it alive.
m_hndHandleArray = pDomain->CreatePinningHandle((OBJECTREF)HandleArrayObj);
}
// Destructor for the LargeHeapHandleBucket class.
LargeHeapHandleBucket::~LargeHeapHandleBucket()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
}
CONTRACTL_END;
if (m_hndHandleArray)
{
DestroyPinningHandle(m_hndHandleArray);
m_hndHandleArray = NULL;
}
}
// Allocate handles from the bucket.
OBJECTREF *LargeHeapHandleBucket::AllocateHandles(DWORD nRequested)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
}
CONTRACTL_END;
_ASSERTE(nRequested > 0 && nRequested <= GetNumRemainingHandles());
_ASSERTE(m_pArrayDataPtr == (OBJECTREF*)((PTRARRAYREF)ObjectFromHandle(m_hndHandleArray))->GetDataPtr());
// Store the handles in the buffer that was passed in
OBJECTREF* ret = &m_pArrayDataPtr[m_CurrentPos];
m_CurrentPos += nRequested;
return ret;
}
// look for a free item embedded in the table
OBJECTREF *LargeHeapHandleBucket::TryAllocateEmbeddedFreeHandle()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
}
CONTRACTL_END;
OBJECTREF pPreallocatedSentinalObject = ObjectFromHandle(g_pPreallocatedSentinelObject);
_ASSERTE(pPreallocatedSentinalObject != NULL);
for (int i = m_CurrentEmbeddedFreePos; i < m_CurrentPos; i++)
{
if (m_pArrayDataPtr[i] == pPreallocatedSentinalObject)
{
m_CurrentEmbeddedFreePos = i;
m_pArrayDataPtr[i] = NULL;
return &m_pArrayDataPtr[i];
}
}
// didn't find it (we don't bother wrapping around for a full search, it's not worth it to try that hard, we'll get it next time)
m_CurrentEmbeddedFreePos = 0;
return NULL;
}
// Maximum bucket size will be 64K on 32-bit and 128K on 64-bit.
// We subtract out a small amount to leave room for the object
// header and length of the array.
#define MAX_BUCKETSIZE (16384 - 4)
// Constructor for the LargeHeapHandleTable class.
LargeHeapHandleTable::LargeHeapHandleTable(BaseDomain *pDomain, DWORD InitialBucketSize)
: m_pHead(NULL)
, m_pDomain(pDomain)
, m_NextBucketSize(InitialBucketSize)
, m_pFreeSearchHint(NULL)
, m_cEmbeddedFree(0)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pDomain));
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
#ifdef _DEBUG
m_pCrstDebug = NULL;
#endif
}
// Destructor for the LargeHeapHandleTable class.
LargeHeapHandleTable::~LargeHeapHandleTable()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
}
CONTRACTL_END;
// Delete the buckets.
while (m_pHead)
{
LargeHeapHandleBucket *pOld = m_pHead;
m_pHead = pOld->GetNext();
delete pOld;
}
}
//*****************************************************************************
//
// LOCKING RULES FOR AllocateHandles() and ReleaseHandles() 12/08/2004
//
//
// These functions are not protected by any locking in this location but rather the callers are
// assumed to be doing suitable locking for the handle table. The handle table itself is
// behaving rather like a thread-agnostic collection class -- it doesn't want to know
// much about the outside world and so it is just doing its job with no awareness of
// thread notions.
//
// The instance in question is
// There are two locations you can find a LargeHeapHandleTable
// 1) there is one in every BaseDomain, it is used to keep track of the static members
// in that domain
// 2) there is one in the System Domain that is used for the GlobalStringLiteralMap
//
// the one in (2) is not the same as the one that is in the BaseDomain object that corresponds
// to the SystemDomain -- that one is basically stilborn because the string literals don't go
// there and of course the System Domain has no code loaded into it -- only regular
// AppDomains (like Domain 0) actually execute code. As a result handle tables are in
// practice used either for string literals or for static members but never for both.
// At least not at this writing.
//
// Now it's useful to consider what the locking discipline is for these classes.
//
// ---------
//
// First case: (easiest) is the statics members
//
// Each BaseDomain has its own critical section
//
// BaseDomain::AllocateObjRefPtrsInLargeTable takes a lock with
// CrstHolder ch(&m_LargeHeapHandleTableCrst);
//
// it does this before it calls AllocateHandles which suffices. It does not call ReleaseHandles
// at any time (although ReleaseHandles may be called via AllocateHandles if the request
// doesn't fit in the current block, the remaining handles at the end of the block are released
// automatically as part of allocation/recycling)
//
// note: Recycled handles are only used during String Literal allocation because we only try
// to recycle handles if the allocation request is for exactly one handle.
//
// The handles in the BaseDomain handle table are released when the Domain is unloaded
// as the GC objects become rootless at that time.
//
// This dispenses with all of the Handle tables except the one that is used for string literals
//
// ---------
//
// Second case: Allocation for use in a string literal
//
// AppDomainStringLiteralMap::GetStringLiteral
// leads to calls to
// LargeHeapHandleBlockHolder constructor
// leads to calls to
// m_Data = pOwner->AllocateHandles(nCount);
//
// before doing this AppDomainStringLiteralMap::GetStringLiteral takes this lock
//
// CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal));
//
// which is the lock for the hash table that it owns
//
// STRINGREF *AppDomainStringLiteralMap::GetInternedString
//
// has a similar call path and uses the same approach and the same lock
// this covers all the paths which allocate
//
// ---------
//
// Third case: Releases for use in a string literal entry
//
// CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()->m_HashTableCrstGlobal));
// taken in the AppDomainStringLiteralMap functions below protects the 4 ways that this can happen
//
// case 3a)
//
// in an appdomain unload case
//
// AppDomainStringLiteralMap::~AppDomainStringLiteralMap() takes the lock then
// leads to calls to
// StringLiteralEntry::Release
// which leads to
// SystemDomain::GetGlobalStringLiteralMapNoCreate()->RemoveStringLiteralEntry(this)
// which leads to
// m_LargeHeapHandleTable.ReleaseHandles((OBJECTREF*)pObjRef, 1);
//
// case 3b)
//
// AppDomainStringLiteralMap::GetStringLiteral() can call StringLiteralEntry::Release in some
// error cases, leading to the same stack as above
//
// case 3c)
//
// AppDomainStringLiteralMap::GetInternedString() can call StringLiteralEntry::Release in some
// error cases, leading to the same stack as above
//
// case 3d)
//
// The same code paths in 3b and 3c and also end up releasing if an exception is thrown
// during their processing. Both these paths use a StringLiteralEntryHolder to assist in cleanup,
// the StaticRelease method of the StringLiteralEntry gets called, which in turn calls the
// Release method.
// Allocate handles from the large heap handle table.
OBJECTREF* LargeHeapHandleTable::AllocateHandles(DWORD nRequested, BOOL bCrossAD)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(nRequested > 0);
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
// SEE "LOCKING RULES FOR AllocateHandles() and ReleaseHandles()" above
// the lock must be registered and already held by the caller per contract
#ifdef _DEBUG
_ASSERTE(m_pCrstDebug != NULL);
_ASSERTE(m_pCrstDebug->OwnedByCurrentThread());
#endif
if (nRequested == 1 && m_cEmbeddedFree != 0)
{
// special casing singleton requests to look for slots that can be re-used
// we need to do this because string literals are allocated one at a time and then sometimes
// released. we do not wish for the number of handles consumed by string literals to
// increase forever as assemblies are loaded and unloaded
if (m_pFreeSearchHint == NULL)
m_pFreeSearchHint = m_pHead;
while (m_pFreeSearchHint)
{
OBJECTREF* pObjRef = m_pFreeSearchHint->TryAllocateEmbeddedFreeHandle();
if (pObjRef != NULL)
{
// the slot is to have been prepared with a null ready to go
_ASSERTE(*pObjRef == NULL);
m_cEmbeddedFree--;
return pObjRef;
}
m_pFreeSearchHint = m_pFreeSearchHint->GetNext();
}
// the search doesn't wrap around so it's possible that we might have embedded free items
// and not find them but that's ok, we'll get them on the next alloc... all we're trying to do
// is to not have big leaks over time.
}
// Retrieve the remaining number of handles in the bucket.
DWORD NumRemainingHandlesInBucket = (m_pHead != NULL) ? m_pHead->GetNumRemainingHandles() : 0;
// create a new block if this request doesn't fit in the current block
if (nRequested > NumRemainingHandlesInBucket)
{
if (m_pHead != NULL)
{
// mark the handles in that remaining region as available for re-use
ReleaseHandles(m_pHead->CurrentPos(), NumRemainingHandlesInBucket);
// mark what's left as having been used
m_pHead->ConsumeRemaining();
}
// create a new bucket for this allocation
// We need a block big enough to hold the requested handles
DWORD NewBucketSize = max(m_NextBucketSize, nRequested);
m_pHead = new LargeHeapHandleBucket(m_pHead, NewBucketSize, m_pDomain, bCrossAD);
m_NextBucketSize = min(m_NextBucketSize * 2, MAX_BUCKETSIZE);
}
return m_pHead->AllocateHandles(nRequested);
}
//*****************************************************************************
// Release object handles allocated using AllocateHandles().
void LargeHeapHandleTable::ReleaseHandles(OBJECTREF *pObjRef, DWORD nReleased)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pObjRef));
}
CONTRACTL_END;
// SEE "LOCKING RULES FOR AllocateHandles() and ReleaseHandles()" above
// the lock must be registered and already held by the caller per contract
#ifdef _DEBUG
_ASSERTE(m_pCrstDebug != NULL);
_ASSERTE(m_pCrstDebug->OwnedByCurrentThread());
#endif
OBJECTREF pPreallocatedSentinalObject = ObjectFromHandle(g_pPreallocatedSentinelObject);
_ASSERTE(pPreallocatedSentinalObject != NULL);
// Add the released handles to the list of available handles.
for (DWORD i = 0; i < nReleased; i++)
{
SetObjectReference(&pObjRef[i], pPreallocatedSentinalObject, NULL);
}
m_cEmbeddedFree += nReleased;
}
// Constructor for the ThreadStaticHandleBucket class.
ThreadStaticHandleBucket::ThreadStaticHandleBucket(ThreadStaticHandleBucket *pNext, DWORD Size, BaseDomain *pDomain)
: m_pNext(pNext)
, m_ArraySize(Size)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pDomain));
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
PTRARRAYREF HandleArrayObj;
// Allocate the array on the GC heap.
OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
HandleArrayObj = (PTRARRAYREF)AllocateObjectArray(Size, g_pObjectClass, FALSE);
// Store the array in a strong handle to keep it alive.
m_hndHandleArray = pDomain->CreateStrongHandle((OBJECTREF)HandleArrayObj);
}
// Destructor for the ThreadStaticHandleBucket class.
ThreadStaticHandleBucket::~ThreadStaticHandleBucket()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
}
CONTRACTL_END;
if (m_hndHandleArray)
{
DestroyStrongHandle(m_hndHandleArray);
m_hndHandleArray = NULL;
}
}
// Allocate handles from the bucket.
OBJECTHANDLE ThreadStaticHandleBucket::GetHandles()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
}
CONTRACTL_END;
return m_hndHandleArray;
}
// Constructor for the ThreadStaticHandleTable class.
ThreadStaticHandleTable::ThreadStaticHandleTable(BaseDomain *pDomain)
: m_pHead(NULL)
, m_pDomain(pDomain)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(CheckPointer(pDomain));
}
CONTRACTL_END;
}
// Destructor for the ThreadStaticHandleTable class.
ThreadStaticHandleTable::~ThreadStaticHandleTable()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
}
CONTRACTL_END;
// Delete the buckets.
while (m_pHead)
{
ThreadStaticHandleBucket *pOld = m_pHead;
m_pHead = pOld->GetNext();
delete pOld;
}
}
// Allocate handles from the large heap handle table.
OBJECTHANDLE ThreadStaticHandleTable::AllocateHandles(DWORD nRequested)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(nRequested > 0);
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
// create a new bucket for this allocation
m_pHead = new ThreadStaticHandleBucket(m_pHead, nRequested, m_pDomain);
return m_pHead->GetHandles();
}
#endif // CROSSGEN_COMPILE
//*****************************************************************************
// BaseDomain
//*****************************************************************************
void BaseDomain::Attach()
{
m_SpecialStaticsCrst.Init(CrstSpecialStatics);
}
BaseDomain::BaseDomain()
{
// initialize fields so the domain can be safely destructed
// shouldn't call anything that can fail here - use ::Init instead
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
FORBID_FAULT;
}
CONTRACTL_END;
m_fDisableInterfaceCache = FALSE;
m_pFusionContext = NULL;
m_pTPABinderContext = NULL;
// Make sure the container is set to NULL so that it gets loaded when it is used.
m_pLargeHeapHandleTable = NULL;
#ifndef CROSSGEN_COMPILE
// Note that m_handleStore is overridden by app domains
m_handleStore = GCHandleUtilities::GetGCHandleManager()->GetGlobalHandleStore();
#else
m_handleStore = NULL;
#endif
m_pMarshalingData = NULL;
m_dwContextStatics = 0;
#ifdef FEATURE_COMINTEROP
m_pMngStdInterfacesInfo = NULL;
m_pWinRtBinder = NULL;
#endif
m_FileLoadLock.PreInit();
m_JITLock.PreInit();
m_ClassInitLock.PreInit();
m_ILStubGenLock.PreInit();
#ifdef FEATURE_CODE_VERSIONING
m_codeVersionManager.PreInit(this == (BaseDomain *)g_pSharedDomainMemory);
#endif
} //BaseDomain::BaseDomain
//*****************************************************************************
void BaseDomain::Init()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
//
// Initialize the domain locks
//
if (this == reinterpret_cast<BaseDomain*>(&g_pSharedDomainMemory[0]))
m_DomainCrst.Init(CrstSharedBaseDomain);
else if (this == reinterpret_cast<BaseDomain*>(&g_pSystemDomainMemory[0]))
m_DomainCrst.Init(CrstSystemBaseDomain);
else
m_DomainCrst.Init(CrstBaseDomain);
m_DomainCacheCrst.Init(CrstAppDomainCache);
m_DomainLocalBlockCrst.Init(CrstDomainLocalBlock);
m_InteropDataCrst.Init(CrstInteropData, CRST_REENTRANCY);
m_WinRTFactoryCacheCrst.Init(CrstWinRTFactoryCache, CRST_UNSAFE_COOPGC);
// NOTE: CRST_UNSAFE_COOPGC prevents a GC mode switch to preemptive when entering this crst.
// If you remove this flag, we will switch to preemptive mode when entering
// m_FileLoadLock, which means all functions that enter it will become
// GC_TRIGGERS. (This includes all uses of PEFileListLockHolder, LoadLockHolder, etc.) So be sure
// to update the contracts if you remove this flag.
m_FileLoadLock.Init(CrstAssemblyLoader,
CrstFlags(CRST_HOST_BREAKABLE), TRUE);
//
// The JIT lock and the CCtor locks are at the same level (and marked as
// UNSAFE_SAME_LEVEL) because they are all part of the same deadlock detection mechanism. We
// see through cycles of JITting and .cctor execution and then explicitly allow the cycle to
// be broken by giving access to uninitialized classes. If there is no cycle or if the cycle
// involves other locks that arent part of this special deadlock-breaking semantics, then
// we continue to block.
//
m_JITLock.Init(CrstJit, CrstFlags(CRST_REENTRANCY | CRST_UNSAFE_SAMELEVEL), TRUE);
m_ClassInitLock.Init(CrstClassInit, CrstFlags(CRST_REENTRANCY | CRST_UNSAFE_SAMELEVEL), TRUE);
m_ILStubGenLock.Init(CrstILStubGen, CrstFlags(CRST_REENTRANCY), TRUE);
// Large heap handle table CRST.
m_LargeHeapHandleTableCrst.Init(CrstAppDomainHandleTable);
m_crstLoaderAllocatorReferences.Init(CrstLoaderAllocatorReferences);
// Has to switch thread to GC_NOTRIGGER while being held (see code:BaseDomain#AssemblyListLock)
m_crstAssemblyList.Init(CrstAssemblyList, CrstFlags(
CRST_GC_NOTRIGGER_WHEN_TAKEN | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN));
// Initialize the EE marshaling data to NULL.
m_pMarshalingData = NULL;
#ifdef FEATURE_COMINTEROP
// Allocate the managed standard interfaces information.
m_pMngStdInterfacesInfo = new MngStdInterfacesInfo();
{
CLRPrivBinderWinRT::NamespaceResolutionKind fNamespaceResolutionKind = CLRPrivBinderWinRT::NamespaceResolutionKind_WindowsAPI;
if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DesignerNamespaceResolutionEnabled) != FALSE)
{
fNamespaceResolutionKind = CLRPrivBinderWinRT::NamespaceResolutionKind_DesignerResolveEvent;
}
CLRPrivTypeCacheWinRT * pWinRtTypeCache = CLRPrivTypeCacheWinRT::GetOrCreateTypeCache();
m_pWinRtBinder = CLRPrivBinderWinRT::GetOrCreateBinder(pWinRtTypeCache, fNamespaceResolutionKind);
}
#endif // FEATURE_COMINTEROP
// Init the COM Interop data hash
{
LockOwner lock = {&m_InteropDataCrst, IsOwnerOfCrst};
m_interopDataHash.Init(0, NULL, false, &lock);
}
m_dwSizedRefHandles = 0;
if (!m_iNumberOfProcessors)
{
m_iNumberOfProcessors = GetCurrentProcessCpuCount();
}
}
#undef LOADERHEAP_PROFILE_COUNTER
#ifndef CROSSGEN_COMPILE
//*****************************************************************************
void BaseDomain::Terminate()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
m_crstLoaderAllocatorReferences.Destroy();
m_DomainCrst.Destroy();
m_DomainCacheCrst.Destroy();
m_DomainLocalBlockCrst.Destroy();
m_InteropDataCrst.Destroy();
JitListLockEntry* pJitElement;
ListLockEntry* pElement;
// All the threads that are in this domain had better be stopped by this
// point.
//
// We might be jitting or running a .cctor so we need to empty that queue.
pJitElement = m_JITLock.Pop(TRUE);
while (pJitElement)
{
#ifdef STRICT_JITLOCK_ENTRY_LEAK_DETECTION
_ASSERTE ((m_JITLock.m_pHead->m_dwRefCount == 1
&& m_JITLock.m_pHead->m_hrResultCode == E_FAIL) ||
dbg_fDrasticShutdown || g_fInControlC);
#endif // STRICT_JITLOCK_ENTRY_LEAK_DETECTION
delete(pJitElement);
pJitElement = m_JITLock.Pop(TRUE);
}
m_JITLock.Destroy();
pElement = m_ClassInitLock.Pop(TRUE);
while (pElement)
{
#ifdef STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
_ASSERTE (dbg_fDrasticShutdown || g_fInControlC);
#endif
delete(pElement);
pElement = m_ClassInitLock.Pop(TRUE);
}
m_ClassInitLock.Destroy();
FileLoadLock* pFileElement;
pFileElement = (FileLoadLock*) m_FileLoadLock.Pop(TRUE);
while (pFileElement)
{
#ifdef STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
_ASSERTE (dbg_fDrasticShutdown || g_fInControlC);
#endif
pFileElement->Release();
pFileElement = (FileLoadLock*) m_FileLoadLock.Pop(TRUE);
}
m_FileLoadLock.Destroy();
pElement = m_ILStubGenLock.Pop(TRUE);
while (pElement)
{
#ifdef STRICT_JITLOCK_ENTRY_LEAK_DETECTION
_ASSERTE ((m_ILStubGenLock.m_pHead->m_dwRefCount == 1
&& m_ILStubGenLock.m_pHead->m_hrResultCode == E_FAIL) ||
dbg_fDrasticShutdown || g_fInControlC);
#endif // STRICT_JITLOCK_ENTRY_LEAK_DETECTION
delete(pElement);
pElement = m_ILStubGenLock.Pop(TRUE);
}
m_ILStubGenLock.Destroy();
m_LargeHeapHandleTableCrst.Destroy();
if (m_pLargeHeapHandleTable != NULL)
{
delete m_pLargeHeapHandleTable;
m_pLargeHeapHandleTable = NULL;
}
if (!IsAppDomain())
{
// Kind of a workaround - during unloading, we need to have an EE halt
// around deleting this stuff. So it gets deleted in AppDomain::Terminate()
// for those things (because there is a convenient place there.)
GetLoaderAllocator()->CleanupStringLiteralMap();
}
#ifdef FEATURE_COMINTEROP
if (m_pMngStdInterfacesInfo)
{
delete m_pMngStdInterfacesInfo;
m_pMngStdInterfacesInfo = NULL;
}
if (m_pWinRtBinder != NULL)
{
m_pWinRtBinder->Release();
}
#endif // FEATURE_COMINTEROP
ClearFusionContext();
m_dwSizedRefHandles = 0;
}
#endif // CROSSGEN_COMPILE
void BaseDomain::InitVSD()
{
STANDARD_VM_CONTRACT;
// This is a workaround for gcc, since it fails to successfully resolve
// "TypeIDMap::STARTING_SHARED_DOMAIN_ID" when used within the ?: operator.
UINT32 startingId;
if (IsSharedDomain())
{
startingId = TypeIDMap::STARTING_SHARED_DOMAIN_ID;
}
else
{
startingId = TypeIDMap::STARTING_UNSHARED_DOMAIN_ID;
}
// By passing false as the last parameter, interfaces loaded in the
// shared domain will not be given fat type ids if RequiresFatDispatchTokens
// is set. This is correct, as the fat dispatch tokens are only needed to solve
// uniqueness problems involving domain specific types.
m_typeIDMap.Init(startingId, 2, !IsSharedDomain());
#ifndef CROSSGEN_COMPILE
GetLoaderAllocator()->InitVirtualCallStubManager(this);
#endif
}
#ifndef CROSSGEN_COMPILE
DWORD BaseDomain::AllocateContextStaticsOffset(DWORD* pOffsetSlot)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
}
CONTRACTL_END;
CrstHolder ch(&m_SpecialStaticsCrst);
DWORD dwOffset = *pOffsetSlot;
if (dwOffset == (DWORD)-1)
{
// Allocate the slot
dwOffset = m_dwContextStatics++;
*pOffsetSlot = dwOffset;
}
return dwOffset;
}
void BaseDomain::ClearFusionContext()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_PREEMPTIVE;
}
CONTRACTL_END;
if(m_pFusionContext) {
m_pFusionContext->Release();
m_pFusionContext = NULL;
}
if (m_pTPABinderContext) {
m_pTPABinderContext->Release();
m_pTPABinderContext = NULL;
}
}
#ifdef FEATURE_PREJIT
void AppDomain::DeleteNativeCodeRanges()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
FORBID_FAULT;
}
CONTRACTL_END;
// Fast path to skip using the assembly iterator when the appdomain has not yet completely been initialized
// and yet we are destroying it. (This is the case if we OOM during AppDomain creation.)
if (m_Assemblies.IsEmpty())
return;
// Shutdown assemblies
AssemblyIterator i = IterateAssembliesEx( (AssemblyIterationFlags)(kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeFailedToLoad) );
CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
while (i.Next(pDomainAssembly.This()))
{
Assembly * assembly = pDomainAssembly->m_pAssembly;
if ((assembly != NULL) && !assembly->IsDomainNeutral())
assembly->DeleteNativeCodeRanges();
}
}
#endif
void AppDomain::ShutdownAssemblies()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
// Fast path to skip using the assembly iterator when the appdomain has not yet completely been initialized
// and yet we are destroying it. (This is the case if we OOM during AppDomain creation.)
if (m_Assemblies.IsEmpty())
return;
// Shutdown assemblies
// has two stages because Terminate needs info from the Assembly's dependencies
// Stage 1: call code:Assembly::Terminate
AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(
kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeFailedToLoad | kIncludeCollected));
DomainAssembly * pDomainAssembly = NULL;
while (i.Next_UnsafeNoAddRef(&pDomainAssembly))
{
// Note: cannot use DomainAssembly::GetAssembly() here as it asserts that the assembly has been
// loaded to at least the FILE_LOAD_ALLOCATE level. Since domain shutdown can take place
// asynchronously this property cannot be guaranteed. Access the m_pAssembly field directly instead.
Assembly * assembly = pDomainAssembly->m_pAssembly;
if (assembly && !assembly->IsDomainNeutral())
assembly->Terminate();
}
// Stage 2: Clear the list of assemblies
i = IterateAssembliesEx((AssemblyIterationFlags)(
kIncludeLoaded | kIncludeLoading | kIncludeExecution | kIncludeFailedToLoad | kIncludeCollected));
while (i.Next_UnsafeNoAddRef(&pDomainAssembly))
{
// We are in shutdown path, no one else can get to the list anymore
delete pDomainAssembly;
}
m_Assemblies.Clear(this);
// Stage 2: Clear the loader allocators registered for deletion from code:Assembly:Terminate calls in
// stage 1
// Note: It is not clear to me why we cannot delete the loader allocator from within
// code:DomainAssembly::~DomainAssembly
ShutdownFreeLoaderAllocators(FALSE);
} // AppDomain::ShutdownAssemblies
void AppDomain::ShutdownFreeLoaderAllocators(BOOL bFromManagedCode)
{
// If we're called from managed code (i.e. the finalizer thread) we take a lock in
// LoaderAllocator::CleanupFailedTypeInit, which may throw. Otherwise we're called
// from the app-domain shutdown path in which we can avoid taking the lock.
CONTRACTL
{
GC_TRIGGERS;
if (bFromManagedCode) THROWS; else NOTHROW;
MODE_ANY;
CAN_TAKE_LOCK;
}
CONTRACTL_END;
CrstHolder ch(GetLoaderAllocatorReferencesLock());
// Shutdown the LoaderAllocators associated with collectible assemblies
while (m_pDelayedLoaderAllocatorUnloadList != NULL)
{
LoaderAllocator * pCurrentLoaderAllocator = m_pDelayedLoaderAllocatorUnloadList;
// Remove next loader allocator from the list
m_pDelayedLoaderAllocatorUnloadList = m_pDelayedLoaderAllocatorUnloadList->m_pLoaderAllocatorDestroyNext;
if (bFromManagedCode)
{
// For loader allocator finalization, we need to be careful about cleaning up per-appdomain allocations
// and synchronizing with GC using delay unload list. We need to wait for next Gen2 GC to finish to ensure
// that GC heap does not have any references to the MethodTables being unloaded.
pCurrentLoaderAllocator->CleanupFailedTypeInit();
pCurrentLoaderAllocator->CleanupHandles();
GCX_COOP();
SystemDomain::System()->AddToDelayedUnloadList(pCurrentLoaderAllocator);
}
else
{
// For appdomain unload, delete the loader allocator right away
delete pCurrentLoaderAllocator;
}
}
} // AppDomain::ShutdownFreeLoaderAllocators
//---------------------------------------------------------------------------------------
//
// Register the loader allocator for deletion in code:AppDomain::ShutdownFreeLoaderAllocators.
//
void AppDomain::RegisterLoaderAllocatorForDeletion(LoaderAllocator * pLoaderAllocator)
{
CONTRACTL
{
GC_TRIGGERS;
NOTHROW;
MODE_ANY;
CAN_TAKE_LOCK;
}
CONTRACTL_END;
CrstHolder ch(GetLoaderAllocatorReferencesLock());
pLoaderAllocator->m_pLoaderAllocatorDestroyNext = m_pDelayedLoaderAllocatorUnloadList;
m_pDelayedLoaderAllocatorUnloadList = pLoaderAllocator;
}
void AppDomain::ShutdownNativeDllSearchDirectories()
{
LIMITED_METHOD_CONTRACT;
// Shutdown assemblies
PathIterator i = IterateNativeDllSearchDirectories();
while (i.Next())
{
delete i.GetPath();
}
m_NativeDllSearchDirectories.Clear();
}
void AppDomain::ReleaseDomainBoundInfo()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;;
// Shutdown assemblies
m_AssemblyCache.OnAppDomainUnload();
AssemblyIterator i = IterateAssembliesEx( (AssemblyIterationFlags)(kIncludeFailedToLoad) );
CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
while (i.Next(pDomainAssembly.This()))
{
pDomainAssembly->ReleaseManagedData();
}
}
void AppDomain::ReleaseFiles()
{
STANDARD_VM_CONTRACT;
// Shutdown assemblies
AssemblyIterator i = IterateAssembliesEx((AssemblyIterationFlags)(
kIncludeLoaded | kIncludeExecution | kIncludeFailedToLoad | kIncludeLoading));
CollectibleAssemblyHolder<DomainAssembly *> pAsm;
while (i.Next(pAsm.This()))
{
if (pAsm->GetCurrentAssembly() == NULL)
{
// Might be domain neutral or not, but should have no live objects as it has not been
// really loaded yet. Just reset it.
_ASSERTE(FitsIn<DWORD>(i.GetIndex()));
m_Assemblies.Set(this, static_cast<DWORD>(i.GetIndex()), NULL);
delete pAsm.Extract();
}
else
{
if (!pAsm->GetCurrentAssembly()->IsDomainNeutral())
pAsm->ReleaseFiles();
}
}
} // AppDomain::ReleaseFiles
OBJECTREF* BaseDomain::AllocateObjRefPtrsInLargeTable(int nRequested, OBJECTREF** ppLazyAllocate, BOOL bCrossAD)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION((nRequested > 0));
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
if (ppLazyAllocate && *ppLazyAllocate)
{
// Allocation already happened
return *ppLazyAllocate;
}
// Enter preemptive state, take the lock and go back to cooperative mode.
{
CrstHolder ch(&m_LargeHeapHandleTableCrst);
GCX_COOP();
if (ppLazyAllocate && *ppLazyAllocate)
{
// Allocation already happened
return *ppLazyAllocate;
}
// Make sure the large heap handle table is initialized.
if (!m_pLargeHeapHandleTable)
InitLargeHeapHandleTable();
// Allocate the handles.
OBJECTREF* result = m_pLargeHeapHandleTable->AllocateHandles(nRequested, bCrossAD);
if (ppLazyAllocate)
{
*ppLazyAllocate = result;
}
return result;
}
}
#endif // CROSSGEN_COMPILE
#endif // !DACCESS_COMPILE
/*static*/
PTR_BaseDomain 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) // the type arguments to the method (if any)
{
CONTRACT(PTR_BaseDomain)
{
NOTHROW;
GC_NOTRIGGER;
FORBID_FAULT;
MODE_ANY;
POSTCONDITION(CheckPointer(RETVAL));
SUPPORTS_DAC;
SO_TOLERANT;
}
CONTRACT_END
if (pGenericDefinitionDomain && pGenericDefinitionDomain->IsAppDomain())
RETURN PTR_BaseDomain(pGenericDefinitionDomain);
for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
{
PTR_BaseDomain pArgDomain = classInst[i].GetDomain();
if (pArgDomain->IsAppDomain())
RETURN pArgDomain;
}
for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
{
PTR_BaseDomain pArgDomain = methodInst[i].GetDomain();
if (pArgDomain->IsAppDomain())
RETURN pArgDomain;
}
RETURN (pGenericDefinitionDomain ?
PTR_BaseDomain(pGenericDefinitionDomain) :
PTR_BaseDomain(SystemDomain::System()));
}
PTR_BaseDomain BaseDomain::ComputeBaseDomain(TypeKey * pKey)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
SUPPORTS_DAC;
}
CONTRACTL_END;
if (pKey->GetKind() == ELEMENT_TYPE_CLASS)
return BaseDomain::ComputeBaseDomain(pKey->GetModule()->GetDomain(),
pKey->GetInstantiation());
else if (pKey->GetKind() != ELEMENT_TYPE_FNPTR)
return pKey->GetElementType().GetDomain();
else
return BaseDomain::ComputeBaseDomain(NULL,Instantiation(pKey->GetRetAndArgTypes(), pKey->GetNumArgs()+1));
}
#ifndef DACCESS_COMPILE
// Insert class in the hash table
void AppDomain::InsertClassForCLSID(MethodTable* pMT, BOOL fForceInsert /*=FALSE*/)
{
CONTRACTL
{
GC_TRIGGERS;
MODE_ANY;
THROWS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
CVID cvid;
// Ensure that registered classes are activated for allocation
pMT->EnsureInstanceActive();
// Note that it is possible for multiple classes to claim the same CLSID, and in such a
// case it is arbitrary which one we will return for a future query for a given app domain.
pMT->GetGuid(&cvid, fForceInsert);
if (!IsEqualIID(cvid, GUID_NULL))
{
//<TODO>@todo get a better key</TODO>
LPVOID val = (LPVOID)pMT;
{
LockHolder lh(this);
if (LookupClass(cvid) != pMT)
{
m_clsidHash.InsertValue(GetKeyFromGUID(&cvid), val);
}
}
}
}
void AppDomain::InsertClassForCLSID(MethodTable* pMT, GUID *pGuid)
{
CONTRACT_VOID
{
NOTHROW;
PRECONDITION(CheckPointer(pMT));
PRECONDITION(CheckPointer(pGuid));
}
CONTRACT_END;
LPVOID val = (LPVOID)pMT;
{
LockHolder lh(this);
CVID* cvid = pGuid;
if (LookupClass(*cvid) != pMT)
{
m_clsidHash.InsertValue(GetKeyFromGUID(pGuid), val);
}
}
RETURN;
}
#endif // DACCESS_COMPILE
#ifdef FEATURE_COMINTEROP
#ifndef DACCESS_COMPILE
void AppDomain::CacheTypeByName(const SString &ssClassName, const UINT vCacheVersion, TypeHandle typeHandle, BYTE bFlags, BOOL bReplaceExisting /*= FALSE*/)
{
WRAPPER_NO_CONTRACT;
LockHolder lh(this);
CacheTypeByNameWorker(ssClassName, vCacheVersion, typeHandle, bFlags, bReplaceExisting);
}
void AppDomain::CacheTypeByNameWorker(const SString &ssClassName, const UINT vCacheVersion, TypeHandle typeHandle, BYTE bFlags, BOOL bReplaceExisting /*= FALSE*/)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
PRECONDITION(!typeHandle.IsNull());
}
CONTRACTL_END;
NewArrayHolder<WCHAR> wzClassName(DuplicateStringThrowing(ssClassName.GetUnicode()));
if (m_vNameToTypeMapVersion != vCacheVersion)
return;
if (m_pNameToTypeMap == nullptr)
{
m_pNameToTypeMap = new NameToTypeMapTable();
}
NameToTypeMapEntry e;
e.m_key.m_wzName = wzClassName;
e.m_key.m_cchName = ssClassName.GetCount();
e.m_typeHandle = typeHandle;
e.m_nEpoch = this->m_nEpoch;
e.m_bFlags = bFlags;
if (!bReplaceExisting)
m_pNameToTypeMap->Add(e);
else
m_pNameToTypeMap->AddOrReplace(e);
wzClassName.SuppressRelease();
}
#endif // DACCESS_COMPILE
TypeHandle AppDomain::LookupTypeByName(const SString &ssClassName, UINT* pvCacheVersion, BYTE *pbFlags)
{
WRAPPER_NO_CONTRACT;
LockHolder lh(this);
return LookupTypeByNameWorker(ssClassName, pvCacheVersion, pbFlags);
}
TypeHandle AppDomain::LookupTypeByNameWorker(const SString &ssClassName, UINT* pvCacheVersion, BYTE *pbFlags)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
SUPPORTS_DAC;
PRECONDITION(CheckPointer(pbFlags, NULL_OK));
}
CONTRACTL_END;
*pvCacheVersion = m_vNameToTypeMapVersion;
if (m_pNameToTypeMap == nullptr)
return TypeHandle(); // a null TypeHandle
NameToTypeMapEntry::Key key;
key.m_cchName = ssClassName.GetCount();
key.m_wzName = ssClassName.GetUnicode();
const NameToTypeMapEntry * pEntry = m_pNameToTypeMap->LookupPtr(key);
if (pEntry == NULL)
return TypeHandle(); // a null TypeHandle
if (pbFlags != NULL)
*pbFlags = pEntry->m_bFlags;
return pEntry->m_typeHandle;
}
PTR_MethodTable AppDomain::LookupTypeByGuid(const GUID & guid)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
SUPPORTS_DAC;
}
CONTRACTL_END;
SString sGuid;
{
WCHAR wszGuid[64];
GuidToLPWSTR(guid, wszGuid, _countof(wszGuid));
sGuid.Append(wszGuid);
}
UINT ver;
TypeHandle th = LookupTypeByName(sGuid, &ver, NULL);
if (!th.IsNull())
{
_ASSERTE(!th.IsTypeDesc());
return th.AsMethodTable();
}
#ifdef FEATURE_PREJIT
else
{
// Next look in each ngen'ed image in turn
AssemblyIterator assemblyIterator = IterateAssembliesEx((AssemblyIterationFlags)(
kIncludeLoaded | kIncludeExecution));
CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
while (assemblyIterator.Next(pDomainAssembly.This()))
{
CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly();
DomainAssembly::ModuleIterator i = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
while (i.Next())
{
Module * pModule = i.GetLoadedModule();
if (!pModule->HasNativeImage())
continue;
_ASSERTE(!pModule->IsCollectible());
PTR_MethodTable pMT = pModule->LookupTypeByGuid(guid);
if (pMT != NULL)
{
return pMT;
}
}
}
}
#endif // FEATURE_PREJIT
return NULL;
}
#ifndef DACCESS_COMPILE
void AppDomain::CacheWinRTTypeByGuid(TypeHandle typeHandle)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(!typeHandle.IsTypeDesc());
PRECONDITION(CanCacheWinRTTypeByGuid(typeHandle));
}
CONTRACTL_END;
PTR_MethodTable pMT = typeHandle.AsMethodTable();
GUID guid;
if (pMT->GetGuidForWinRT(&guid))
{
SString sGuid;
{
WCHAR wszGuid[64];
GuidToLPWSTR(guid, wszGuid, _countof(wszGuid));
sGuid.Append(wszGuid);
}
BYTE bFlags = 0x80;
TypeHandle th;
UINT vCacheVersion;
{
LockHolder lh(this);
th = LookupTypeByNameWorker(sGuid, &vCacheVersion, &bFlags);
if (th.IsNull())
{
// no other entry with the same GUID exists in the cache
CacheTypeByNameWorker(sGuid, vCacheVersion, typeHandle, bFlags);
}
else if (typeHandle.AsMethodTable() != th.AsMethodTable() && th.IsProjectedFromWinRT())
{
// If we found a native WinRT type cached with the same GUID, replace it.
// Otherwise simply add the new mapping to the cache.
CacheTypeByNameWorker(sGuid, vCacheVersion, typeHandle, bFlags, TRUE);
}
}
}
}
#endif // DACCESS_COMPILE
void AppDomain::GetCachedWinRTTypes(
SArray<PTR_MethodTable> * pTypes,
SArray<GUID> * pGuids,
UINT minEpoch,
UINT * pCurEpoch)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
SUPPORTS_DAC;
}
CONTRACTL_END;
LockHolder lh(this);
for (auto it = m_pNameToTypeMap->Begin(), end = m_pNameToTypeMap->End();
it != end;
++it)
{
NameToTypeMapEntry entry = (NameToTypeMapEntry)(*it);
TypeHandle th = entry.m_typeHandle;
if (th.AsMethodTable() != NULL &&
entry.m_key.m_wzName[0] == W('{') &&
entry.m_nEpoch >= minEpoch)
{
_ASSERTE(!th.IsTypeDesc());
PTR_MethodTable pMT = th.AsMethodTable();
// we're parsing the GUID value from the cache, because projected types do not cache the
// COM GUID in their GetGuid() but rather the legacy GUID
GUID iid;
if (LPWSTRToGuid(&iid, entry.m_key.m_wzName, 38) && iid != GUID_NULL)
{
pTypes->Append(pMT);
pGuids->Append(iid);
}
}
}
#ifdef FEATURE_PREJIT
// Next look in each ngen'ed image in turn
AssemblyIterator assemblyIterator = IterateAssembliesEx((AssemblyIterationFlags)(
kIncludeLoaded | kIncludeExecution));
CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
while (assemblyIterator.Next(pDomainAssembly.This()))
{
CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly();
DomainAssembly::ModuleIterator i = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
while (i.Next())
{
Module * pModule = i.GetLoadedModule();
if (!pModule->HasNativeImage())
continue;
_ASSERTE(!pModule->IsCollectible());
pModule->GetCachedWinRTTypes(pTypes, pGuids);
}
}
#endif // FEATURE_PREJIT
if (pCurEpoch != NULL)
*pCurEpoch = m_nEpoch;
++m_nEpoch;
}
#ifndef CROSSGEN_COMPILE
#ifndef DACCESS_COMPILE
// static
void WinRTFactoryCacheTraits::OnDestructPerEntryCleanupAction(const WinRTFactoryCacheEntry& e)
{
WRAPPER_NO_CONTRACT;
if (e.m_pCtxEntry != NULL)
{
e.m_pCtxEntry->Release();
}
// the AD is going away, no need to destroy the OBJECTHANDLE
}
void AppDomain::CacheWinRTFactoryObject(MethodTable *pClassMT, OBJECTREF *refFactory, LPVOID lpCtxCookie)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pClassMT));
}
CONTRACTL_END;
CtxEntryHolder pNewCtxEntry;
if (lpCtxCookie != NULL)
{
// We don't want to insert the context cookie in the cache because it's just an address
// of an internal COM data structure which will be freed when the apartment is torn down.
// What's worse, if another apartment is later created, its context cookie may have exactly
// the same value leading to incorrect cache hits. We'll use our CtxEntry instead which
// is ref-counted and keeps the COM data structure alive even after the apartment ceases
// to exist.
pNewCtxEntry = CtxEntryCache::GetCtxEntryCache()->FindCtxEntry(lpCtxCookie, GetThread());
}
WinRTFactoryCacheLockHolder lh(this);
if (m_pWinRTFactoryCache == nullptr)
{
m_pWinRTFactoryCache = new WinRTFactoryCache();
}
WinRTFactoryCacheEntry *pEntry = const_cast<WinRTFactoryCacheEntry*>(m_pWinRTFactoryCache->LookupPtr(pClassMT));
if (!pEntry)
{
//
// No existing entry for this cache
// Create a new one
//
WinRTFactoryCacheEntry e;
OBJECTHANDLEHolder ohNewHandle(CreateHandle(*refFactory));
e.key = pClassMT;
e.m_pCtxEntry = pNewCtxEntry;
e.m_ohFactoryObject = ohNewHandle;
m_pWinRTFactoryCache->Add(e);
// suppress release of the CtxEntry and handle after we successfully inserted the new entry
pNewCtxEntry.SuppressRelease();
ohNewHandle.SuppressRelease();
}
else
{
//
// Existing entry
//
// release the old CtxEntry and update the entry
CtxEntry *pTemp = pNewCtxEntry.Extract();
pNewCtxEntry = pEntry->m_pCtxEntry;
pEntry->m_pCtxEntry = pTemp;
IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
mgr->StoreObjectInHandle(pEntry->m_ohFactoryObject, OBJECTREFToObject(*refFactory));
}
}
OBJECTREF AppDomain::LookupWinRTFactoryObject(MethodTable *pClassMT, LPVOID lpCtxCookie)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pClassMT));
PRECONDITION(CheckPointer(m_pWinRTFactoryCache, NULL_OK));
}
CONTRACTL_END;
if (m_pWinRTFactoryCache == nullptr)
return NULL;
//
// Retrieve cached factory
//
WinRTFactoryCacheLockHolder lh(this);
const WinRTFactoryCacheEntry *pEntry = m_pWinRTFactoryCache->LookupPtr(pClassMT);
if (pEntry == NULL)
return NULL;
//
// Ignore factories from a different context, unless lpCtxCookie == NULL,
// which means the factory is free-threaded
// Note that we cannot touch the RCW to retrieve cookie at this point
// because the RCW might belong to a STA thread and that STA thread might die
// and take the RCW with it. Therefore we have to save cookie in this cache
//
if (pEntry->m_pCtxEntry == NULL || pEntry->m_pCtxEntry->GetCtxCookie() == lpCtxCookie)
return ObjectFromHandle(pEntry->m_ohFactoryObject);
return NULL;
}
void AppDomain::RemoveWinRTFactoryObjects(LPVOID pCtxCookie)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
if (m_pWinRTFactoryCache == nullptr)
return;
// helper class for delayed CtxEntry cleanup
class CtxEntryListReleaseHolder
{
public:
CQuickArrayList<CtxEntry *> m_list;
~CtxEntryListReleaseHolder()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
for (SIZE_T i = 0; i < m_list.Size(); i++)
{
m_list[i]->Release();
}
}
} ctxEntryListReleaseHolder;
GCX_COOP();
{
WinRTFactoryCacheLockHolder lh(this);
// Go through the hash table and remove items in the given context
for (WinRTFactoryCache::Iterator it = m_pWinRTFactoryCache->Begin(); it != m_pWinRTFactoryCache->End(); it++)
{
if (it->m_pCtxEntry != NULL && it->m_pCtxEntry->GetCtxCookie() == pCtxCookie)
{
// Releasing the CtxEntry may trigger GC which we can't do under the lock so we push
// it on our local list and release them all after we're done iterating the hashtable.
ctxEntryListReleaseHolder.m_list.Push(it->m_pCtxEntry);
DestroyHandle(it->m_ohFactoryObject);
m_pWinRTFactoryCache->Remove(it);
}
}
}
}
OBJECTREF AppDomain::GetMissingObject()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
}
CONTRACTL_END;
if (!m_hndMissing)
{
// Get the field
FieldDesc *pValueFD = MscorlibBinder::GetField(FIELD__MISSING__VALUE);
pValueFD->CheckRunClassInitThrowing();
// Retrieve the value static field and store it.
OBJECTHANDLE hndMissing = CreateHandle(pValueFD->GetStaticOBJECTREF());
if (FastInterlockCompareExchangePointer(&m_hndMissing, hndMissing, NULL) != NULL)
{
// Exchanged failed. The m_hndMissing did not equal NULL and was returned.
DestroyHandle(hndMissing);
}
}
return ObjectFromHandle(m_hndMissing);
}
#endif // DACCESS_COMPILE
#endif //CROSSGEN_COMPILE
#endif // FEATURE_COMINTEROP
#ifndef DACCESS_COMPILE
EEMarshalingData *BaseDomain::GetMarshalingData()
{
CONTRACT (EEMarshalingData*)
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM());
POSTCONDITION(CheckPointer(m_pMarshalingData));
}
CONTRACT_END;
if (!m_pMarshalingData)
{
// Take the lock
CrstHolder holder(&m_InteropDataCrst);
if (!m_pMarshalingData)
{
LoaderHeap* pHeap = GetLoaderAllocator()->GetLowFrequencyHeap();
m_pMarshalingData = new (pHeap) EEMarshalingData(this, pHeap, &m_DomainCrst);
}
}
RETURN m_pMarshalingData;
}
void BaseDomain::DeleteMarshalingData()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
// We are in shutdown - no need to take any lock
if (m_pMarshalingData)
{
delete m_pMarshalingData;
m_pMarshalingData = NULL;
}
}
#ifndef CROSSGEN_COMPILE
STRINGREF *BaseDomain::IsStringInterned(STRINGREF *pString)
{
CONTRACTL
{
GC_TRIGGERS;
THROWS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pString));
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
return GetLoaderAllocator()->IsStringInterned(pString);
}
STRINGREF *BaseDomain::GetOrInternString(STRINGREF *pString)
{
CONTRACTL
{
GC_TRIGGERS;
THROWS;
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(pString));
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
return GetLoaderAllocator()->GetOrInternString(pString);
}
void BaseDomain::InitLargeHeapHandleTable()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(m_pLargeHeapHandleTable==NULL);
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
m_pLargeHeapHandleTable = new LargeHeapHandleTable(this, STATIC_OBJECT_TABLE_BUCKET_SIZE);
#ifdef _DEBUG
m_pLargeHeapHandleTable->RegisterCrstDebug(&m_LargeHeapHandleTableCrst);
#endif
}
#ifdef FEATURE_COMINTEROP
MethodTable* AppDomain::GetLicenseInteropHelperMethodTable()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
}
CONTRACTL_END;
if(m_pLicenseInteropHelperMT == NULL)
{
// Do this work outside of the lock so we don't have an unbreakable lock condition
TypeHandle licenseMgrTypeHnd;
MethodDescCallSite loadLM(METHOD__MARSHAL__LOAD_LICENSE_MANAGER);
licenseMgrTypeHnd = (MethodTable*) loadLM.Call_RetLPVOID((ARG_SLOT*)NULL);
//
// Look up this method by name, because the type is actually declared in System.dll. <TODO>@todo: why?</TODO>
//
MethodDesc *pGetLIHMD = MemberLoader::FindMethod(licenseMgrTypeHnd.AsMethodTable(),
"GetLicenseInteropHelperType", &gsig_SM_Void_RetIntPtr);
_ASSERTE(pGetLIHMD);
TypeHandle lihTypeHnd;
MethodDescCallSite getLIH(pGetLIHMD);
lihTypeHnd = (MethodTable*) getLIH.Call_RetLPVOID((ARG_SLOT*)NULL);
BaseDomain::LockHolder lh(this);
if(m_pLicenseInteropHelperMT == NULL)
m_pLicenseInteropHelperMT = lihTypeHnd.AsMethodTable();
}
return m_pLicenseInteropHelperMT;
}
COMorRemotingFlag AppDomain::GetComOrRemotingFlag()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
// 0. check if the value is already been set
if (m_COMorRemotingFlag != COMorRemoting_NotInitialized)
return m_COMorRemotingFlag;
// 1. check whether the process is AppX
if (AppX::IsAppXProcess())
{
// do not use Remoting in AppX
m_COMorRemotingFlag = COMorRemoting_COM;
return m_COMorRemotingFlag;
}
// 2. check the xml file
m_COMorRemotingFlag = GetPreferComInsteadOfManagedRemotingFromConfigFile();
if (m_COMorRemotingFlag != COMorRemoting_NotInitialized)
{
return m_COMorRemotingFlag;
}
// 3. check the global setting
if (NULL != g_pConfig && g_pConfig->ComInsteadOfManagedRemoting())
{
m_COMorRemotingFlag = COMorRemoting_COM;
}
else
{
m_COMorRemotingFlag = COMorRemoting_Remoting;
}
return m_COMorRemotingFlag;
}
BOOL AppDomain::GetPreferComInsteadOfManagedRemoting()
{
WRAPPER_NO_CONTRACT;
return (GetComOrRemotingFlag() == COMorRemoting_COM);
}
COMorRemotingFlag AppDomain::GetPreferComInsteadOfManagedRemotingFromConfigFile()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
return COMorRemoting_COM;
}
#endif // FEATURE_COMINTEROP
#endif // CROSSGEN_COMPILE
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
void *SystemDomain::operator new(size_t size, void *pInPlace)
{
LIMITED_METHOD_CONTRACT;
return pInPlace;
}
void SystemDomain::operator delete(void *pMem)
{
LIMITED_METHOD_CONTRACT;
// Do nothing - new() was in-place
}
void SystemDomain::SetCompilationOverrides(BOOL fForceDebug,
BOOL fForceProfiling,
BOOL fForceInstrument)
{
LIMITED_METHOD_CONTRACT;
s_fForceDebug = fForceDebug;
s_fForceProfiling = fForceProfiling;
s_fForceInstrument = fForceInstrument;
}
#endif //!DACCESS_COMPILE
void SystemDomain::GetCompilationOverrides(BOOL * fForceDebug,
BOOL * fForceProfiling,
BOOL * fForceInstrument)
{
LIMITED_METHOD_DAC_CONTRACT;
*fForceDebug = s_fForceDebug;
*fForceProfiling = s_fForceProfiling;
*fForceInstrument = s_fForceInstrument;
}
#ifndef DACCESS_COMPILE
void SystemDomain::Attach()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(m_pSystemDomain == NULL);
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
#ifndef CROSSGEN_COMPILE
// Initialize stub managers
PrecodeStubManager::Init();
DelegateInvokeStubManager::Init();
JumpStubStubManager::Init();
RangeSectionStubManager::Init();
ILStubManager::Init();
InteropDispatchStubManager::Init();
StubLinkStubManager::Init();
ThunkHeapStubManager::Init();
TailCallStubManager::Init();
PerAppDomainTPCountList::InitAppDomainIndexList();
#endif // CROSSGEN_COMPILE
m_appDomainIndexList.Init();
m_appDomainIdList.Init();
m_SystemDomainCrst.Init(CrstSystemDomain, (CrstFlags)(CRST_REENTRANCY | CRST_TAKEN_DURING_SHUTDOWN));
m_DelayedUnloadCrst.Init(CrstSystemDomainDelayedUnloadList, CRST_UNSAFE_COOPGC);
// Initialize the ID dispenser that is used for domain neutral module IDs
g_pModuleIndexDispenser = new IdDispenser();
// Create the global SystemDomain and initialize it.
m_pSystemDomain = new (&g_pSystemDomainMemory[0]) SystemDomain();
// No way it can fail since g_pSystemDomainMemory is a static array.
CONSISTENCY_CHECK(CheckPointer(m_pSystemDomain));
LOG((LF_CLASSLOADER,
LL_INFO10,
"Created system domain at %p\n",
m_pSystemDomain));
// We need to initialize the memory pools etc. for the system domain.
m_pSystemDomain->BaseDomain::Init(); // Setup the memory heaps
// Create the default domain
m_pSystemDomain->CreateDefaultDomain();
SharedDomain::Attach();
// Each domain gets its own ReJitManager, and ReJitManager has its own static
// initialization to run
ReJitManager::InitStatic();
}
#ifndef CROSSGEN_COMPILE
void SystemDomain::DetachBegin()
{
WRAPPER_NO_CONTRACT;
// Shut down the domain and its children (but don't deallocate anything just
// yet).
// TODO: we should really not running managed DLLMain during process detach.
if (GetThread() == NULL)
{
return;
}
if(m_pSystemDomain)
m_pSystemDomain->Stop();
}
void SystemDomain::DetachEnd()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
// Shut down the domain and its children (but don't deallocate anything just
// yet).
if(m_pSystemDomain)
{
GCX_PREEMP();
m_pSystemDomain->ClearFusionContext();
if (m_pSystemDomain->m_pDefaultDomain)
m_pSystemDomain->m_pDefaultDomain->ClearFusionContext();
}
}
void SystemDomain::Stop()
{
WRAPPER_NO_CONTRACT;
AppDomainIterator i(TRUE);
while (i.Next())
if (i.GetDomain()->m_Stage < AppDomain::STAGE_CLEARED)
i.GetDomain()->Stop();
}
void SystemDomain::Terminate() // bNotifyProfiler is ignored
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
// This ignores the refences and terminates the appdomains
AppDomainIterator i(FALSE);
while (i.Next())
{
delete i.GetDomain();
// Keep the iterator from Releasing the current domain
i.m_pCurrent = NULL;
}
if (m_pSystemFile != NULL) {
m_pSystemFile->Release();
m_pSystemFile = NULL;
}
m_pSystemAssembly = NULL;
if(m_pwDevpath) {
delete[] m_pwDevpath;
m_pwDevpath = NULL;
}
m_dwDevpath = 0;
m_fDevpath = FALSE;
if (m_pGlobalStringLiteralMap) {
delete m_pGlobalStringLiteralMap;
m_pGlobalStringLiteralMap = NULL;
}
SharedDomain::Detach();
BaseDomain::Terminate();
#ifdef FEATURE_COMINTEROP
if (g_pRCWCleanupList != NULL)
delete g_pRCWCleanupList;
#endif // FEATURE_COMINTEROP
m_GlobalAllocator.Terminate();
}
void SystemDomain::PreallocateSpecialObjects()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
_ASSERTE(g_pPreallocatedSentinelObject == NULL);
OBJECTREF pPreallocatedSentinalObject = AllocateObject(g_pObjectClass);
g_pPreallocatedSentinelObject = CreatePinningHandle( pPreallocatedSentinalObject );
#ifdef FEATURE_PREJIT
if (SystemModule()->HasNativeImage())
{
CORCOMPILE_EE_INFO_TABLE *pEEInfo = SystemModule()->GetNativeImage()->GetNativeEEInfoTable();
pEEInfo->emptyString = (CORINFO_Object **)StringObject::GetEmptyStringRefPtr();
}
#endif
}
void SystemDomain::CreatePreallocatedExceptions()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
EXCEPTIONREF pBaseException = (EXCEPTIONREF)AllocateObject(g_pExceptionClass);
pBaseException->SetHResult(COR_E_EXCEPTION);
pBaseException->SetXCode(EXCEPTION_COMPLUS);
_ASSERTE(g_pPreallocatedBaseException == NULL);
g_pPreallocatedBaseException = CreateHandle(pBaseException);
EXCEPTIONREF pOutOfMemory = (EXCEPTIONREF)AllocateObject(g_pOutOfMemoryExceptionClass);
pOutOfMemory->SetHResult(COR_E_OUTOFMEMORY);
pOutOfMemory->SetXCode(EXCEPTION_COMPLUS);
_ASSERTE(g_pPreallocatedOutOfMemoryException == NULL);
g_pPreallocatedOutOfMemoryException = CreateHandle(pOutOfMemory);
EXCEPTIONREF pStackOverflow = (EXCEPTIONREF)AllocateObject(g_pStackOverflowExceptionClass);
pStackOverflow->SetHResult(COR_E_STACKOVERFLOW);
pStackOverflow->SetXCode(EXCEPTION_COMPLUS);
_ASSERTE(g_pPreallocatedStackOverflowException == NULL);
g_pPreallocatedStackOverflowException = CreateHandle(pStackOverflow);
EXCEPTIONREF pExecutionEngine = (EXCEPTIONREF)AllocateObject(g_pExecutionEngineExceptionClass);
pExecutionEngine->SetHResult(COR_E_EXECUTIONENGINE);
pExecutionEngine->SetXCode(EXCEPTION_COMPLUS);
_ASSERTE(g_pPreallocatedExecutionEngineException == NULL);
g_pPreallocatedExecutionEngineException = CreateHandle(pExecutionEngine);
EXCEPTIONREF pRudeAbortException = (EXCEPTIONREF)AllocateObject(g_pThreadAbortExceptionClass);
pRudeAbortException->SetHResult(COR_E_THREADABORTED);
pRudeAbortException->SetXCode(EXCEPTION_COMPLUS);
_ASSERTE(g_pPreallocatedRudeThreadAbortException == NULL);
g_pPreallocatedRudeThreadAbortException = CreateHandle(pRudeAbortException);
EXCEPTIONREF pAbortException = (EXCEPTIONREF)AllocateObject(g_pThreadAbortExceptionClass);
pAbortException->SetHResult(COR_E_THREADABORTED);
pAbortException->SetXCode(EXCEPTION_COMPLUS);
_ASSERTE(g_pPreallocatedThreadAbortException == NULL);
g_pPreallocatedThreadAbortException = CreateHandle( pAbortException );
}
#endif // CROSSGEN_COMPILE
void SystemDomain::Init()
{
STANDARD_VM_CONTRACT;
HRESULT hr = S_OK;
#ifdef _DEBUG
LOG((
LF_EEMEM,
LL_INFO10,
"sizeof(EEClass) = %d\n"
"sizeof(MethodTable) = %d\n"
"sizeof(MethodDesc)= %d\n"
"sizeof(FieldDesc) = %d\n"
"sizeof(Module) = %d\n",
sizeof(EEClass),
sizeof(MethodTable),
sizeof(MethodDesc),
sizeof(FieldDesc),
sizeof(Module)
));
#endif // _DEBUG
// The base domain is initialized in SystemDomain::Attach()
// to allow stub caches to use the memory pool. Do not
// initialze it here!
#ifndef CROSSGEN_COMPILE
#ifdef _DEBUG
Context *curCtx = GetCurrentContext();
#endif
_ASSERTE(curCtx);
_ASSERTE(curCtx->GetDomain() != NULL);
#endif
#ifdef FEATURE_PREJIT
if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ZapDisable) != 0)
g_fAllowNativeImages = false;
#endif
m_pSystemFile = NULL;
m_pSystemAssembly = NULL;
DWORD size = 0;
// Get the install directory so we can find mscorlib
hr = GetInternalSystemDirectory(NULL, &size);
if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
ThrowHR(hr);
// GetInternalSystemDirectory returns a size, including the null!
WCHAR *buffer = m_SystemDirectory.OpenUnicodeBuffer(size-1);
IfFailThrow(GetInternalSystemDirectory(buffer, &size));
m_SystemDirectory.CloseBuffer();
m_SystemDirectory.Normalize();
// At this point m_SystemDirectory should already be canonicalized
m_BaseLibrary.Append(m_SystemDirectory);
if (!m_BaseLibrary.EndsWith(DIRECTORY_SEPARATOR_CHAR_W))
{
m_BaseLibrary.Append(DIRECTORY_SEPARATOR_CHAR_W);
}
m_BaseLibrary.Append(g_pwBaseLibrary);
m_BaseLibrary.Normalize();
LoadBaseSystemClasses();
{
// We are about to start allocating objects, so we must be in cooperative mode.
// However, many of the entrypoints to the system (DllGetClassObject and all
// N/Direct exports) get called multiple times. Sometimes they initialize the EE,
// but generally they remain in preemptive mode. So we really want to push/pop
// the state here:
GCX_COOP();
#ifndef CROSSGEN_COMPILE
if (!NingenEnabled())
{
CreatePreallocatedExceptions();
PreallocateSpecialObjects();
}
#endif
// Finish loading mscorlib now.
m_pSystemAssembly->GetDomainAssembly()->EnsureActive();
}
#ifdef _DEBUG
BOOL fPause = EEConfig::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_PauseOnLoad, FALSE);
while(fPause)
{
ClrSleepEx(20, TRUE);
}
#endif // _DEBUG
}
#ifndef CROSSGEN_COMPILE
void SystemDomain::LazyInitGlobalStringLiteralMap()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
// Allocate the global string literal map.
NewHolder<GlobalStringLiteralMap> pGlobalStringLiteralMap(new GlobalStringLiteralMap());
// Initialize the global string literal map.
pGlobalStringLiteralMap->Init();
if (InterlockedCompareExchangeT<GlobalStringLiteralMap *>(&m_pGlobalStringLiteralMap, pGlobalStringLiteralMap, NULL) == NULL)
{
pGlobalStringLiteralMap.SuppressRelease();
}
}
void AppDomain::CreateADUnloadStartEvent()
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
SO_TOLERANT;
MODE_ANY;
}
CONTRACTL_END;
g_pUnloadStartEvent = new CLREvent();
g_pUnloadStartEvent->CreateAutoEvent(FALSE);
}
/*static*/ void SystemDomain::EnumAllStaticGCRefs(promote_func* fn, ScanContext* sc)
{
CONTRACT_VOID
{
NOTHROW;
GC_NOTRIGGER;
MODE_COOPERATIVE;
}
CONTRACT_END;
// We don't do a normal AppDomainIterator because we can't take the SystemDomain lock from
// here.
// We're only supposed to call this from a Server GC. We're walking here m_appDomainIdList
// m_appDomainIdList will have an AppDomain* or will be NULL. So the only danger is if we
// Fetch an AppDomain and then in some other thread the AppDomain is deleted.
//
// If the thread deleting the AppDomain (AppDomain::~AppDomain)was in Preemptive mode
// while doing SystemDomain::EnumAllStaticGCRefs we will issue a GCX_COOP(), which will wait
// for the GC to finish, so we are safe
//
// If the thread is in cooperative mode, it must have been suspended for the GC so a delete
// can't happen.
_ASSERTE(GCHeapUtilities::IsGCInProgress() &&
GCHeapUtilities::IsServerHeap() &&
IsGCSpecialThread());
SystemDomain* sysDomain = SystemDomain::System();
if (sysDomain)
{
DWORD i;
DWORD count = (DWORD) m_appDomainIdList.GetCount();
for (i = 0 ; i < count ; i++)
{
AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
if (pAppDomain && pAppDomain->IsActive() && !pAppDomain->IsUnloading())
{
#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
if (g_fEnableARM)
{
sc->pCurrentDomain = pAppDomain;
}
#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
pAppDomain->EnumStaticGCRefs(fn, sc);
}
}
}
RETURN;
}
#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
void SystemDomain::ResetADSurvivedBytes()
{
CONTRACT_VOID
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACT_END;
_ASSERTE(GCHeapUtilities::IsGCInProgress());
SystemDomain* sysDomain = SystemDomain::System();
if (sysDomain)
{
DWORD i;
DWORD count = (DWORD) m_appDomainIdList.GetCount();
for (i = 0 ; i < count ; i++)
{
AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
if (pAppDomain && pAppDomain->IsUserActive())
{
pAppDomain->ResetSurvivedBytes();
}
}
}
RETURN;
}
ULONGLONG SystemDomain::GetADSurvivedBytes()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
SystemDomain* sysDomain = SystemDomain::System();
ULONGLONG ullTotalADSurvived = 0;
if (sysDomain)
{
DWORD i;
DWORD count = (DWORD) m_appDomainIdList.GetCount();
for (i = 0 ; i < count ; i++)
{
AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
if (pAppDomain && pAppDomain->IsUserActive())
{
ULONGLONG ullSurvived = pAppDomain->GetSurvivedBytes();
ullTotalADSurvived += ullSurvived;
}
}
}
return ullTotalADSurvived;
}
void SystemDomain::RecordTotalSurvivedBytes(size_t totalSurvivedBytes)
{
CONTRACT_VOID
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACT_END;
m_totalSurvivedBytes = totalSurvivedBytes;
SystemDomain* sysDomain = SystemDomain::System();
if (sysDomain)
{
DWORD i;
DWORD count = (DWORD) m_appDomainIdList.GetCount();
for (i = 0 ; i < count ; i++)
{
AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
if (pAppDomain && pAppDomain->IsUserActive())
{
FireEtwAppDomainMemSurvived((ULONGLONG)pAppDomain, pAppDomain->GetSurvivedBytes(), totalSurvivedBytes, GetClrInstanceId());
}
}
}
RETURN;
}
#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
// Only called when EE is suspended.
DWORD SystemDomain::GetTotalNumSizedRefHandles()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
SystemDomain* sysDomain = SystemDomain::System();
DWORD dwTotalNumSizedRefHandles = 0;
if (sysDomain)
{
DWORD i;
DWORD count = (DWORD) m_appDomainIdList.GetCount();
for (i = 0 ; i < count ; i++)
{
AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
if (pAppDomain && pAppDomain->IsActive() && !pAppDomain->IsUnloading())
{
dwTotalNumSizedRefHandles += pAppDomain->GetNumSizedRefHandles();
}
}
}
return dwTotalNumSizedRefHandles;
}
#endif // CROSSGEN_COMPILE
void SystemDomain::LoadBaseSystemClasses()
{
STANDARD_VM_CONTRACT;
ETWOnStartup(LdSysBases_V1, LdSysBasesEnd_V1);
{
m_pSystemFile = PEAssembly::OpenSystem(NULL);
}
// Only partially load the system assembly. Other parts of the code will want to access
// the globals in this function before finishing the load.
m_pSystemAssembly = DefaultDomain()->LoadDomainAssembly(NULL, m_pSystemFile, FILE_LOAD_POST_LOADLIBRARY)->GetCurrentAssembly();
// Set up binder for mscorlib
MscorlibBinder::AttachModule(m_pSystemAssembly->GetManifestModule());
// Load Object
g_pObjectClass = MscorlibBinder::GetClass(CLASS__OBJECT);
// Now that ObjectClass is loaded, we can set up
// the system for finalizers. There is no point in deferring this, since we need
// to know this before we allocate our first object.
g_pObjectFinalizerMD = MscorlibBinder::GetMethod(METHOD__OBJECT__FINALIZE);
g_pCanonMethodTableClass = MscorlibBinder::GetClass(CLASS____CANON);
// NOTE: !!!IMPORTANT!!! ValueType and Enum MUST be loaded one immediately after
// the other, because we have coded MethodTable::IsChildValueType
// in such a way that it depends on this behaviour.
// Load the ValueType class
g_pValueTypeClass = MscorlibBinder::GetClass(CLASS__VALUE_TYPE);
// Load the enum class
g_pEnumClass = MscorlibBinder::GetClass(CLASS__ENUM);
_ASSERTE(!g_pEnumClass->IsValueType());
// Load System.RuntimeType
g_pRuntimeTypeClass = MscorlibBinder::GetClass(CLASS__CLASS);
_ASSERTE(g_pRuntimeTypeClass->IsFullyLoaded());
// Load Array class
g_pArrayClass = MscorlibBinder::GetClass(CLASS__ARRAY);
// Calling a method on IList<T> for an array requires redirection to a method on
// the SZArrayHelper class. Retrieving such methods means calling
// GetActualImplementationForArrayGenericIListMethod, which calls FetchMethod for
// the corresponding method on SZArrayHelper. This basically results in a class
// load due to a method call, which the debugger cannot handle, so we pre-load
// the SZArrayHelper class here.
g_pSZArrayHelperClass = MscorlibBinder::GetClass(CLASS__SZARRAYHELPER);
// Load ByReference class
//
// NOTE: ByReference<T> must be the first by-ref-like system type to be loaded,
// because MethodTable::ClassifyEightBytesWithManagedLayout depends on it.
g_pByReferenceClass = MscorlibBinder::GetClass(CLASS__BYREFERENCE);
// Load Nullable class
g_pNullableClass = MscorlibBinder::GetClass(CLASS__NULLABLE);
// Load the Object array class.
g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT] = ClassLoader::LoadArrayTypeThrowing(TypeHandle(g_pObjectClass)).AsArray();
// We have delayed allocation of mscorlib's static handles until we load the object class
MscorlibBinder::GetModule()->AllocateRegularStaticHandles(DefaultDomain());
g_TypedReferenceMT = MscorlibBinder::GetClass(CLASS__TYPED_REFERENCE);
// Make sure all primitive types are loaded
for (int et = ELEMENT_TYPE_VOID; et <= ELEMENT_TYPE_R8; et++)
MscorlibBinder::LoadPrimitiveType((CorElementType)et);
MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_I);
MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_U);
// unfortunately, the following cannot be delay loaded since the jit
// uses it to compute method attributes within a function that cannot
// handle Complus exception and the following call goes through a path
// where a complus exception can be thrown. It is unfortunate, because
// we know that the delegate class and multidelegate class are always
// guaranteed to be found.
g_pDelegateClass = MscorlibBinder::GetClass(CLASS__DELEGATE);
g_pMulticastDelegateClass = MscorlibBinder::GetClass(CLASS__MULTICAST_DELEGATE);
// used by IsImplicitInterfaceOfSZArray
MscorlibBinder::GetClass(CLASS__IENUMERABLEGENERIC);
MscorlibBinder::GetClass(CLASS__ICOLLECTIONGENERIC);
MscorlibBinder::GetClass(CLASS__ILISTGENERIC);
MscorlibBinder::GetClass(CLASS__IREADONLYCOLLECTIONGENERIC);
MscorlibBinder::GetClass(CLASS__IREADONLYLISTGENERIC);
// Load String
g_pStringClass = MscorlibBinder::LoadPrimitiveType(ELEMENT_TYPE_STRING);
// Used by Buffer::BlockCopy
g_pByteArrayMT = ClassLoader::LoadArrayTypeThrowing(
TypeHandle(MscorlibBinder::GetElementType(ELEMENT_TYPE_U1))).AsArray()->GetMethodTable();
#ifndef CROSSGEN_COMPILE
ECall::PopulateManagedStringConstructors();
#endif // CROSSGEN_COMPILE
g_pExceptionClass = MscorlibBinder::GetClass(CLASS__EXCEPTION);
g_pOutOfMemoryExceptionClass = MscorlibBinder::GetException(kOutOfMemoryException);
g_pStackOverflowExceptionClass = MscorlibBinder::GetException(kStackOverflowException);
g_pExecutionEngineExceptionClass = MscorlibBinder::GetException(kExecutionEngineException);
g_pThreadAbortExceptionClass = MscorlibBinder::GetException(kThreadAbortException);
g_pThreadClass = MscorlibBinder::GetClass(CLASS__THREAD);
#ifdef FEATURE_COMINTEROP
g_pBaseCOMObject = MscorlibBinder::GetClass(CLASS__COM_OBJECT);
g_pBaseRuntimeClass = MscorlibBinder::GetClass(CLASS__RUNTIME_CLASS);
MscorlibBinder::GetClass(CLASS__IDICTIONARYGENERIC);
MscorlibBinder::GetClass(CLASS__IREADONLYDICTIONARYGENERIC);
MscorlibBinder::GetClass(CLASS__ATTRIBUTE);
MscorlibBinder::GetClass(CLASS__EVENT_HANDLERGENERIC);
MscorlibBinder::GetClass(CLASS__IENUMERABLE);
MscorlibBinder::GetClass(CLASS__ICOLLECTION);
MscorlibBinder::GetClass(CLASS__ILIST);
MscorlibBinder::GetClass(CLASS__IDISPOSABLE);
#ifdef _DEBUG
WinRTInterfaceRedirector::VerifyRedirectedInterfaceStubs();
#endif // _DEBUG
#endif
#ifdef FEATURE_ICASTABLE
g_pICastableInterface = MscorlibBinder::GetClass(CLASS__ICASTABLE);
#endif // FEATURE_ICASTABLE
// Load a special marker method used to detect Constrained Execution Regions
// at jit time.
g_pExecuteBackoutCodeHelperMethod = MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__EXECUTE_BACKOUT_CODE_HELPER);
// Make sure that FCall mapping for Monitor.Enter is initialized. We need it in case Monitor.Enter is used only as JIT helper.
// For more details, see comment in code:JITutil_MonEnterWorker around "__me = GetEEFuncEntryPointMacro(JIT_MonEnter)".
ECall::GetFCallImpl(MscorlibBinder::GetMethod(METHOD__MONITOR__ENTER));
#ifdef PROFILING_SUPPORTED
// Note that g_profControlBlock.fBaseSystemClassesLoaded must be set to TRUE only after
// all base system classes are loaded. Profilers are not allowed to call any type-loading
// APIs until g_profControlBlock.fBaseSystemClassesLoaded is TRUE. It is important that
// all base system classes need to be loaded before profilers can trigger the type loading.
g_profControlBlock.fBaseSystemClassesLoaded = TRUE;
#endif // PROFILING_SUPPORTED
#if defined(_DEBUG) && !defined(CROSSGEN_COMPILE)
if (!NingenEnabled())
{
g_Mscorlib.Check();
}
#endif
#if defined(HAVE_GCCOVER) && defined(FEATURE_PREJIT)
if (GCStress<cfg_instr_ngen>::IsEnabled())
{
// Setting up gc coverage requires the base system classes
// to be initialized. So we have deferred it until now for mscorlib.
Module *pModule = MscorlibBinder::GetModule();
_ASSERTE(pModule->IsSystem());
if(pModule->HasNativeImage())
{
SetupGcCoverageForNativeImage(pModule);
}
}
#endif // defined(HAVE_GCCOVER) && !defined(FEATURE_PREJIT)
}
/*static*/
void SystemDomain::LoadDomain(AppDomain *pDomain)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(System()));
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
pDomain->SetCanUnload(); // by default can unload any domain
SystemDomain::System()->AddDomain(pDomain);
}
ADIndex SystemDomain::GetNewAppDomainIndex(AppDomain *pAppDomain)
{
STANDARD_VM_CONTRACT;
DWORD count = m_appDomainIndexList.GetCount();
DWORD i;
#ifdef _DEBUG
if (count < 2000)
{
// So that we can keep AD index inside object header.
// We do not want to create syncblock unless needed.
i = count;
}
else
{
#endif // _DEBUG
//
// Look for an unused index. Note that in a checked build,
// we never reuse indexes - this makes it easier to tell
// when we are looking at a stale app domain.
//
i = m_appDomainIndexList.FindElement(m_dwLowestFreeIndex, NULL);
if (i == (DWORD) ArrayList::NOT_FOUND)
i = count;
m_dwLowestFreeIndex = i+1;
#ifdef _DEBUG
if (m_dwLowestFreeIndex >= 2000)
{
m_dwLowestFreeIndex = 0;
}
}
#endif // _DEBUG
if (i == count)
IfFailThrow(m_appDomainIndexList.Append(pAppDomain));
else
m_appDomainIndexList.Set(i, pAppDomain);
_ASSERTE(i < m_appDomainIndexList.GetCount());
// Note that index 0 means domain agile.
return ADIndex(i+1);
}
void SystemDomain::ReleaseAppDomainIndex(ADIndex index)
{
WRAPPER_NO_CONTRACT;
SystemDomain::LockHolder lh;
// Note that index 0 means domain agile.
index.m_dwIndex--;
_ASSERTE(m_appDomainIndexList.Get(index.m_dwIndex) != NULL);
m_appDomainIndexList.Set(index.m_dwIndex, NULL);
#ifndef _DEBUG
if (index.m_dwIndex < m_dwLowestFreeIndex)
m_dwLowestFreeIndex = index.m_dwIndex;
#endif // !_DEBUG
}
#endif // !DACCESS_COMPILE
PTR_AppDomain SystemDomain::GetAppDomainAtIndex(ADIndex index)
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
_ASSERTE(index.m_dwIndex != 0);
PTR_AppDomain pAppDomain = TestGetAppDomainAtIndex(index);
_ASSERTE(pAppDomain || !"Attempt to access unloaded app domain");
return pAppDomain;
}
PTR_AppDomain SystemDomain::TestGetAppDomainAtIndex(ADIndex index)
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
_ASSERTE(index.m_dwIndex != 0);
index.m_dwIndex--;
#ifndef DACCESS_COMPILE
_ASSERTE(index.m_dwIndex < (DWORD)m_appDomainIndexList.GetCount());
AppDomain *pAppDomain = (AppDomain*) m_appDomainIndexList.Get(index.m_dwIndex);
#else // DACCESS_COMPILE
PTR_ArrayListStatic pList = &m_appDomainIndexList;
AppDomain *pAppDomain = dac_cast<PTR_AppDomain>(pList->Get(index.m_dwIndex));
#endif // DACCESS_COMPILE
return PTR_AppDomain(pAppDomain);
}
#ifndef DACCESS_COMPILE
// See also code:SystemDomain::ReleaseAppDomainId
ADID SystemDomain::GetNewAppDomainId(AppDomain *pAppDomain)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
DWORD i = m_appDomainIdList.GetCount();
IfFailThrow(m_appDomainIdList.Append(pAppDomain));
_ASSERTE(i < m_appDomainIdList.GetCount());
return ADID(i+1);
}
AppDomain *SystemDomain::GetAppDomainAtId(ADID index)
{
CONTRACTL
{
#ifdef _DEBUG
if (!SystemDomain::IsUnderDomainLock() && !IsGCThread()) { MODE_COOPERATIVE;} else { DISABLED(MODE_ANY);}
#endif
GC_NOTRIGGER;
SO_TOLERANT;
NOTHROW;
}
CONTRACTL_END;
if(index.m_dwId == 0)
return NULL;
DWORD requestedID = index.m_dwId - 1;
if(requestedID >= (DWORD)m_appDomainIdList.GetCount())
return NULL;
AppDomain * result = (AppDomain *)m_appDomainIdList.Get(requestedID);
#ifndef CROSSGEN_COMPILE
if(result==NULL && GetThread() == FinalizerThread::GetFinalizerThread() &&
SystemDomain::System()->AppDomainBeingUnloaded()!=NULL &&
SystemDomain::System()->AppDomainBeingUnloaded()->GetId()==index)
result=SystemDomain::System()->AppDomainBeingUnloaded();
// If the current thread can't enter the AppDomain, then don't return it.
if (!result || !result->CanThreadEnter(GetThread()))
return NULL;
#endif // CROSSGEN_COMPILE
return result;
}
// Releases an appdomain index. Note that today we have code that depends on these
// indexes not being recycled, so we don't actually shrink m_appDomainIdList, but
// simply zero out an entry. THus we 'leak' the memory associated the slot in
// m_appDomainIdList.
//
// TODO make this a sparse structure so that we avoid that leak.
//
void SystemDomain::ReleaseAppDomainId(ADID index)
{
LIMITED_METHOD_CONTRACT;
index.m_dwId--;
_ASSERTE(index.m_dwId < (DWORD)m_appDomainIdList.GetCount());
m_appDomainIdList.Set(index.m_dwId, NULL);
}
#if defined(FEATURE_COMINTEROP_APARTMENT_SUPPORT) && !defined(CROSSGEN_COMPILE)
#ifdef _DEBUG
int g_fMainThreadApartmentStateSet = 0;
#endif
Thread::ApartmentState SystemDomain::GetEntryPointThreadAptState(IMDInternalImport* pScope, mdMethodDef mdMethod)
{
STANDARD_VM_CONTRACT;
HRESULT hr;
IfFailThrow(hr = pScope->GetCustomAttributeByName(mdMethod,
DEFAULTDOMAIN_MTA_TYPE,
NULL,
NULL));
BOOL fIsMTA = FALSE;
if(hr == S_OK)
fIsMTA = TRUE;
IfFailThrow(hr = pScope->GetCustomAttributeByName(mdMethod,
DEFAULTDOMAIN_STA_TYPE,
NULL,
NULL));
BOOL fIsSTA = FALSE;
if (hr == S_OK)
fIsSTA = TRUE;
if (fIsSTA && fIsMTA)
COMPlusThrowHR(COR_E_CUSTOMATTRIBUTEFORMAT);
if (fIsSTA)
return Thread::AS_InSTA;
else if (fIsMTA)
return Thread::AS_InMTA;
return Thread::AS_Unknown;
}
void SystemDomain::SetThreadAptState (Thread::ApartmentState state)
{
STANDARD_VM_CONTRACT;
Thread* pThread = GetThread();
_ASSERTE(pThread);
if(state == Thread::AS_InSTA)
{
Thread::ApartmentState pState = pThread->SetApartment(Thread::AS_InSTA, TRUE);
_ASSERTE(pState == Thread::AS_InSTA);
}
else
{
// If an apartment state was not explicitly requested, default to MTA
Thread::ApartmentState pState = pThread->SetApartment(Thread::AS_InMTA, TRUE);
_ASSERTE(pState == Thread::AS_InMTA);
}
#ifdef _DEBUG
g_fMainThreadApartmentStateSet++;
#endif
}
#endif // defined(FEATURE_COMINTEROP_APARTMENT_SUPPORT) && !defined(CROSSGEN_COMPILE)
// Looks in all the modules for the DefaultDomain attribute
// The order is assembly and then the modules. It is first
// come, first serve.
BOOL SystemDomain::SetGlobalSharePolicyUsingAttribute(IMDInternalImport* pScope, mdMethodDef mdMethod)
{
STANDARD_VM_CONTRACT;
return FALSE;
}
void SystemDomain::SetupDefaultDomain()
{
CONTRACT_VOID
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACT_END;
Thread *pThread = GetThread();
_ASSERTE(pThread);
AppDomain *pDomain;
pDomain = pThread->GetDomain();
_ASSERTE(pDomain);
GCX_COOP();
ENTER_DOMAIN_PTR(SystemDomain::System()->DefaultDomain(),ADV_DEFAULTAD)
{
// Push this frame around loading the main assembly to ensure the
// debugger can properly recgonize any managed code that gets run
// as "class initializaion" code.
FrameWithCookie<DebuggerClassInitMarkFrame> __dcimf;
{
GCX_PREEMP();
InitializeDefaultDomain(TRUE);
}
__dcimf.Pop();
}
END_DOMAIN_TRANSITION;
RETURN;
}
HRESULT SystemDomain::SetupDefaultDomainNoThrow()
{
CONTRACTL
{
NOTHROW;
MODE_ANY;
}
CONTRACTL_END;
HRESULT hr = S_OK;
EX_TRY
{
SystemDomain::SetupDefaultDomain();
}
EX_CATCH_HRESULT(hr);
return hr;
}
#ifdef _DEBUG
int g_fInitializingInitialAD = 0;
#endif
// This routine completes the initialization of the default domaine.
// After this call mananged code can be executed.
void SystemDomain::InitializeDefaultDomain(
BOOL allowRedirects,
ICLRPrivBinder * pBinder)
{
STANDARD_VM_CONTRACT;
WCHAR* pwsConfig = NULL;
WCHAR* pwsPath = NULL;
ETWOnStartup (InitDefaultDomain_V1, InitDefaultDomainEnd_V1);
// Setup the default AppDomain.
#ifdef _DEBUG
g_fInitializingInitialAD++;
#endif
AppDomain* pDefaultDomain = SystemDomain::System()->DefaultDomain();
if (pBinder != nullptr)
{
pDefaultDomain->SetLoadContextHostBinder(pBinder);
}
{
GCX_COOP();
pDefaultDomain->InitializeDomainContext(allowRedirects, pwsPath, pwsConfig);
#ifndef CROSSGEN_COMPILE
if (!NingenEnabled())
{
if (!IsSingleAppDomain())
{
pDefaultDomain->InitializeDefaultDomainManager();
}
}
#endif // CROSSGEN_COMPILE
}
// DefaultDomain Load event
ETW::LoaderLog::DomainLoad(pDefaultDomain);
#ifdef _DEBUG
g_fInitializingInitialAD--;
#endif
TESTHOOKCALL(RuntimeStarted(RTS_DEFAULTADREADY));
}
#ifndef CROSSGEN_COMPILE
#ifdef _DEBUG
Volatile<LONG> g_fInExecuteMainMethod = 0;
#endif
#endif // CROSSGEN_COMPILE
// Helper function to load an assembly. This is called from LoadCOMClass.
/* static */
Assembly *AppDomain::LoadAssemblyHelper(LPCWSTR wszAssembly,
LPCWSTR wszCodeBase)
{
CONTRACT(Assembly *)
{
THROWS;
POSTCONDITION(CheckPointer(RETVAL));
PRECONDITION(wszAssembly || wszCodeBase);
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACT_END;
AssemblySpec spec;
if(wszAssembly) {
#define MAKE_TRANSLATIONFAILED { ThrowOutOfMemory(); }
MAKE_UTF8PTR_FROMWIDE(szAssembly,wszAssembly);
#undef MAKE_TRANSLATIONFAILED
IfFailThrow(spec.Init(szAssembly));
}
if (wszCodeBase) {
spec.SetCodeBase(wszCodeBase);
}
RETURN spec.LoadAssembly(FILE_LOADED);
}
#if defined(FEATURE_CLASSIC_COMINTEROP) && !defined(CROSSGEN_COMPILE)
MethodTable *AppDomain::LoadCOMClass(GUID clsid,
BOOL bLoadRecord/*=FALSE*/,
BOOL* pfAssemblyInReg/*=NULL*/)
{
// @CORESYSTODO: what to do here?
return NULL;
}
#endif // FEATURE_CLASSIC_COMINTEROP && !CROSSGEN_COMPILE
/*static*/
bool SystemDomain::IsReflectionInvocationMethod(MethodDesc* pMeth)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
MethodTable* pCaller = pMeth->GetMethodTable();
// All Reflection Invocation methods are defined in mscorlib.dll
if (!pCaller->GetModule()->IsSystem())
return false;
/* List of types that should be skipped to identify true caller */
static const BinderClassID reflectionInvocationTypes[] = {
CLASS__METHOD,
CLASS__METHOD_BASE,
CLASS__METHOD_INFO,
CLASS__CONSTRUCTOR,
CLASS__CONSTRUCTOR_INFO,
CLASS__CLASS,
CLASS__TYPE_HANDLE,
CLASS__METHOD_HANDLE,
CLASS__FIELD_HANDLE,
CLASS__TYPE,
CLASS__FIELD,
CLASS__RT_FIELD_INFO,
CLASS__FIELD_INFO,
CLASS__EVENT,
CLASS__EVENT_INFO,
CLASS__PROPERTY,
CLASS__PROPERTY_INFO,
CLASS__ACTIVATOR,
CLASS__ARRAY,
CLASS__ASSEMBLYBASE,
CLASS__ASSEMBLY,
CLASS__TYPE_DELEGATOR,
CLASS__RUNTIME_HELPERS,
CLASS__LAZY_INITIALIZER,
CLASS__DYNAMICMETHOD,
CLASS__DELEGATE,
CLASS__MULTICAST_DELEGATE,
CLASS__APP_DOMAIN
};
static const BinderClassID genericReflectionInvocationTypes[] = {
CLASS__LAZY
};
static mdTypeDef genericReflectionInvocationTypeDefs[NumItems(genericReflectionInvocationTypes)];
static bool fInited = false;
if (!VolatileLoad(&fInited))
{
// Make sure all types are loaded so that we can use faster GetExistingClass()
for (unsigned i = 0; i < NumItems(reflectionInvocationTypes); i++)
{
MscorlibBinder::GetClass(reflectionInvocationTypes[i]);
}
// Make sure all types are loaded so that we can use faster GetExistingClass()
for (unsigned i = 0; i < NumItems(genericReflectionInvocationTypes); i++)
{
genericReflectionInvocationTypeDefs[i] = MscorlibBinder::GetClass(genericReflectionInvocationTypes[i])->GetCl();
}
MscorlibBinder::GetClass(CLASS__APP_DOMAIN);
VolatileStore(&fInited, true);
}
if (pCaller->HasInstantiation())
{
// For generic types, pCaller will be an instantiated type and never equal to the type definition.
// So we compare their TypeDef tokens instead.
for (unsigned i = 0; i < NumItems(genericReflectionInvocationTypeDefs); i++)
{
if (pCaller->GetCl() == genericReflectionInvocationTypeDefs[i])
return true;
}
}
else
{
for (unsigned i = 0; i < NumItems(reflectionInvocationTypes); i++)
{
if (MscorlibBinder::GetExistingClass(reflectionInvocationTypes[i]) == pCaller)
return true;
}
}
return false;
}
#ifndef CROSSGEN_COMPILE
struct CallersDataWithStackMark
{
StackCrawlMark* stackMark;
BOOL foundMe;
MethodDesc* pFoundMethod;
MethodDesc* pPrevMethod;
AppDomain* pAppDomain;
};
/*static*/
MethodDesc* SystemDomain::GetCallersMethod(StackCrawlMark* stackMark,
AppDomain **ppAppDomain/*=NULL*/)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
GCX_COOP();
CallersDataWithStackMark cdata;
ZeroMemory(&cdata, sizeof(CallersDataWithStackMark));
cdata.stackMark = stackMark;
GetThread()->StackWalkFrames(CallersMethodCallbackWithStackMark, &cdata, FUNCTIONSONLY | LIGHTUNWIND);
if(cdata.pFoundMethod) {
if (ppAppDomain)
*ppAppDomain = cdata.pAppDomain;
return cdata.pFoundMethod;
} else
return NULL;
}
/*static*/
MethodTable* SystemDomain::GetCallersType(StackCrawlMark* stackMark,
AppDomain **ppAppDomain/*=NULL*/)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
CallersDataWithStackMark cdata;
ZeroMemory(&cdata, sizeof(CallersDataWithStackMark));
cdata.stackMark = stackMark;
GetThread()->StackWalkFrames(CallersMethodCallbackWithStackMark, &cdata, FUNCTIONSONLY | LIGHTUNWIND);
if(cdata.pFoundMethod) {
if (ppAppDomain)
*ppAppDomain = cdata.pAppDomain;
return cdata.pFoundMethod->GetMethodTable();
} else
return NULL;
}
/*static*/
Module* SystemDomain::GetCallersModule(StackCrawlMark* stackMark,
AppDomain **ppAppDomain/*=NULL*/)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
GCX_COOP();
CallersDataWithStackMark cdata;
ZeroMemory(&cdata, sizeof(CallersDataWithStackMark));
cdata.stackMark = stackMark;
GetThread()->StackWalkFrames(CallersMethodCallbackWithStackMark, &cdata, FUNCTIONSONLY | LIGHTUNWIND);
if(cdata.pFoundMethod) {
if (ppAppDomain)
*ppAppDomain = cdata.pAppDomain;
return cdata.pFoundMethod->GetModule();
} else
return NULL;
}
struct CallersData
{
int skip;
MethodDesc* pMethod;
};
/*static*/
Assembly* SystemDomain::GetCallersAssembly(StackCrawlMark *stackMark,
AppDomain **ppAppDomain/*=NULL*/)
{
WRAPPER_NO_CONTRACT;
Module* mod = GetCallersModule(stackMark, ppAppDomain);
if (mod)
return mod->GetAssembly();
return NULL;
}
/*static*/
Module* SystemDomain::GetCallersModule(int skip)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
GCX_COOP();
CallersData cdata;
ZeroMemory(&cdata, sizeof(CallersData));
cdata.skip = skip;
StackWalkFunctions(GetThread(), CallersMethodCallback, &cdata);
if(cdata.pMethod)
return cdata.pMethod->GetModule();
else
return NULL;
}
/*private static*/
StackWalkAction SystemDomain::CallersMethodCallbackWithStackMark(CrawlFrame* pCf, VOID* data)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
SO_INTOLERANT;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
MethodDesc *pFunc = pCf->GetFunction();
/* We asked to be called back only for functions */
_ASSERTE(pFunc);
CallersDataWithStackMark* pCaller = (CallersDataWithStackMark*) data;
if (pCaller->stackMark)
{
if (!pCf->IsInCalleesFrames(pCaller->stackMark))
{
// save the current in case it is the one we want
pCaller->pPrevMethod = pFunc;
pCaller->pAppDomain = pCf->GetAppDomain();
return SWA_CONTINUE;
}
// LookForMe stack crawl marks needn't worry about reflection or
// remoting frames on the stack. Each frame above (newer than) the
// target will be captured by the logic above. Once we transition to
// finding the stack mark below the AofRA, we know that we hit the
// target last time round and immediately exit with the cached result.
if (*(pCaller->stackMark) == LookForMe)
{
pCaller->pFoundMethod = pCaller->pPrevMethod;
return SWA_ABORT;
}
}
// Skip reflection and remoting frames that could lie between a stack marked
// method and its true caller (or that caller and its own caller). These
// frames are infrastructure and logically transparent to the stack crawling
// algorithm.
// Skipping remoting frames. We always skip entire client to server spans
// (though we see them in the order server then client during a stack crawl
// obviously).
// We spot the server dispatcher end because all calls are dispatched
// through a single method: StackBuilderSink._PrivateProcessMessage.
Frame* frame = pCf->GetFrame();
_ASSERTE(pCf->IsFrameless() || frame);
// Skipping reflection frames. We don't need to be quite as exhaustive here
// as the security or reflection stack walking code since we know this logic
// is only invoked for selected methods in mscorlib itself. So we're
// reasonably sure we won't have any sensitive methods late bound invoked on
// constructors, properties or events. This leaves being invoked via
// MethodInfo, Type or Delegate (and depending on which invoke overload is
// being used, several different reflection classes may be involved).
g_IBCLogger.LogMethodDescAccess(pFunc);
if (SystemDomain::IsReflectionInvocationMethod(pFunc))
return SWA_CONTINUE;
if (frame && frame->GetFrameType() == Frame::TYPE_MULTICAST)
{
// This must be either a secure delegate frame or a true multicast delegate invocation.
_ASSERTE(pFunc->GetMethodTable()->IsDelegate());
DELEGATEREF del = (DELEGATEREF)((SecureDelegateFrame*)frame)->GetThis(); // This can throw.
if (COMDelegate::IsSecureDelegate(del))
{
if (del->IsWrapperDelegate())
{
// On ARM, we use secure delegate infrastructure to preserve R4 register.
return SWA_CONTINUE;
}
// For a secure delegate frame, we should return the delegate creator instead
// of the delegate method itself.
pFunc = (MethodDesc*) del->GetMethodPtrAux();
}
else
{
_ASSERTE(COMDelegate::IsTrueMulticastDelegate(del));
return SWA_CONTINUE;
}
}
// Return the first non-reflection/remoting frame if no stack mark was
// supplied.
if (!pCaller->stackMark)
{
pCaller->pFoundMethod = pFunc;
pCaller->pAppDomain = pCf->GetAppDomain();
return SWA_ABORT;
}
// If we got here, we must already be in the frame containing the stack mark and we are not looking for "me".
_ASSERTE(pCaller->stackMark &&
pCf->IsInCalleesFrames(pCaller->stackMark) &&
*(pCaller->stackMark) != LookForMe);
// When looking for caller's caller, we delay returning results for another
// round (the way this is structured, we will still be able to skip
// reflection and remoting frames between the caller and the caller's
// caller).
if ((*(pCaller->stackMark) == LookForMyCallersCaller) &&
(pCaller->pFoundMethod == NULL))
{
pCaller->pFoundMethod = pFunc;
return SWA_CONTINUE;
}
// If remoting is not available, we only set the caller if the crawlframe is from the same domain.
// Why? Because if the callerdomain is different from current domain,
// there have to be interop/native frames in between.
// For example, in the CORECLR, if we find the caller to be in a different domain, then the
// call into reflection is due to an unmanaged call into mscorlib. For that
// case, the caller really is an INTEROP method.
// In general, if the caller is INTEROP, we set the caller/callerdomain to be NULL
// (To be precise: they are already NULL and we don't change them).
if (pCf->GetAppDomain() == GetAppDomain())
// We must either be looking for the caller, or the caller's caller when
// we've already found the caller (we used a non-null value in pFoundMethod
// simply as a flag, the correct method to return in both case is the
// current method).
{
pCaller->pFoundMethod = pFunc;
pCaller->pAppDomain = pCf->GetAppDomain();
}
return SWA_ABORT;
}
/*private static*/
StackWalkAction SystemDomain::CallersMethodCallback(CrawlFrame* pCf, VOID* data)
{
LIMITED_METHOD_CONTRACT;
STATIC_CONTRACT_SO_TOLERANT;
MethodDesc *pFunc = pCf->GetFunction();
/* We asked to be called back only for functions */
_ASSERTE(pFunc);
CallersData* pCaller = (CallersData*) data;
if(pCaller->skip == 0) {
pCaller->pMethod = pFunc;
return SWA_ABORT;
}
else {
pCaller->skip--;
return SWA_CONTINUE;
}
}
#endif // CROSSGEN_COMPILE
#ifdef CROSSGEN_COMPILE
// defined in compile.cpp
extern CompilationDomain * theDomain;
#endif
void SystemDomain::CreateDefaultDomain()
{
STANDARD_VM_CONTRACT;
#ifdef CROSSGEN_COMPILE
AppDomainRefHolder pDomain(theDomain);
#else
AppDomainRefHolder pDomain(new AppDomain());
#endif
SystemDomain::LockHolder lh;
pDomain->Init();
// need to make this assignment here since we'll be releasing
// the lock before calling AddDomain. So any other thread
// grabbing this lock after we release it will find that
// the COM Domain has already been created
m_pDefaultDomain = pDomain;
_ASSERTE (pDomain->GetId().m_dwId == DefaultADID);
// allocate a Virtual Call Stub Manager for the default domain
m_pDefaultDomain->InitVSD();
pDomain->SetStage(AppDomain::STAGE_OPEN);
pDomain.SuppressRelease();
LOG((LF_CLASSLOADER | LF_CORDB,
LL_INFO10,
"Created default domain at %p\n", m_pDefaultDomain));
}
#ifdef DEBUGGING_SUPPORTED
void SystemDomain::PublishAppDomainAndInformDebugger (AppDomain *pDomain)
{
CONTRACTL
{
if(!g_fEEInit) {THROWS;} else {DISABLED(NOTHROW);};
if(!g_fEEInit) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);};
MODE_ANY;
}
CONTRACTL_END;
LOG((LF_CORDB, LL_INFO100, "SD::PADAID: Adding 0x%x\n", pDomain));
// Call the publisher API to add this appdomain entry to the list
// The publisher will handle failures, so we don't care if this succeeds or fails.
if (g_pDebugInterface != NULL)
{
g_pDebugInterface->AddAppDomainToIPC(pDomain);
}
}
#endif // DEBUGGING_SUPPORTED
void SystemDomain::AddDomain(AppDomain* pDomain)
{
CONTRACTL
{
NOTHROW;
MODE_ANY;
GC_TRIGGERS;
PRECONDITION(CheckPointer((pDomain)));
}
CONTRACTL_END;
{
LockHolder lh;
_ASSERTE (pDomain->m_Stage != AppDomain::STAGE_CREATING);
if (pDomain->m_Stage == AppDomain::STAGE_READYFORMANAGEDCODE ||
pDomain->m_Stage == AppDomain::STAGE_ACTIVE)
{
pDomain->SetStage(AppDomain::STAGE_OPEN);
IncrementNumAppDomains(); // Maintain a count of app domains added to the list.
}
}
// Note that if you add another path that can reach here without calling
// PublishAppDomainAndInformDebugger, then you should go back & make sure
// that PADAID gets called. Right after this call, if not sooner.
LOG((LF_CORDB, LL_INFO1000, "SD::AD:Would have added domain here! 0x%x\n",
pDomain));
}
BOOL SystemDomain::RemoveDomain(AppDomain* pDomain)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(CheckPointer(pDomain));
PRECONDITION(!pDomain->IsDefaultDomain());
}
CONTRACTL_END;
// You can not remove the default domain.
if (!pDomain->IsActive())
return FALSE;
pDomain->Release();
return TRUE;
}
#ifdef PROFILING_SUPPORTED
void SystemDomain::NotifyProfilerStartup()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_PREEMPTIVE;
}
CONTRACTL_END;
{
BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
_ASSERTE(System());
g_profControlBlock.pProfInterface->AppDomainCreationStarted((AppDomainID) System());
END_PIN_PROFILER();
}
{
BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
_ASSERTE(System());
g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID) System(), S_OK);
END_PIN_PROFILER();
}
{
BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
_ASSERTE(System()->DefaultDomain());
g_profControlBlock.pProfInterface->AppDomainCreationStarted((AppDomainID) System()->DefaultDomain());
END_PIN_PROFILER();
}
{
BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
_ASSERTE(System()->DefaultDomain());
g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID) System()->DefaultDomain(), S_OK);
END_PIN_PROFILER();
}
{
BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
_ASSERTE(SharedDomain::GetDomain());
g_profControlBlock.pProfInterface->AppDomainCreationStarted((AppDomainID) SharedDomain::GetDomain());
END_PIN_PROFILER();
}
{
BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
_ASSERTE(SharedDomain::GetDomain());
g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID) SharedDomain::GetDomain(), S_OK);
END_PIN_PROFILER();
}
}
HRESULT SystemDomain::NotifyProfilerShutdown()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_PREEMPTIVE;
}
CONTRACTL_END;
{
BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
_ASSERTE(System());
g_profControlBlock.pProfInterface->AppDomainShutdownStarted((AppDomainID) System());
END_PIN_PROFILER();
}
{
BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
_ASSERTE(System());
g_profControlBlock.pProfInterface->AppDomainShutdownFinished((AppDomainID) System(), S_OK);
END_PIN_PROFILER();
}
{
BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
_ASSERTE(System()->DefaultDomain());
g_profControlBlock.pProfInterface->AppDomainShutdownStarted((AppDomainID) System()->DefaultDomain());
END_PIN_PROFILER();
}
{
BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
_ASSERTE(System()->DefaultDomain());
g_profControlBlock.pProfInterface->AppDomainShutdownFinished((AppDomainID) System()->DefaultDomain(), S_OK);
END_PIN_PROFILER();
}
return (S_OK);
}
#endif // PROFILING_SUPPORTED
#ifdef _DEBUG
struct AppDomain::ThreadTrackInfo {
Thread *pThread;
CDynArray<Frame *> frameStack;
};
#endif // _DEBUG
AppDomain::AppDomain()
{
// initialize fields so the appdomain can be safely destructed
// shouldn't call anything that can fail here - use ::Init instead
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
FORBID_FAULT;
}
CONTRACTL_END;
m_cRef=1;
m_pNextInDelayedUnloadList = NULL;
m_fRudeUnload = FALSE;
m_pUnloadRequestThread = NULL;
m_ADUnloadSink=NULL;
// Initialize Shared state. Assemblies are loaded
// into each domain by default.
#ifdef FEATURE_LOADER_OPTIMIZATION
m_SharePolicy = SHARE_POLICY_UNSPECIFIED;
#endif
m_pRootAssembly = NULL;
m_pwDynamicDir = NULL;
m_dwFlags = 0;
m_pDefaultContext = NULL;
#ifdef FEATURE_COMINTEROP
m_pComCallWrapperCache = NULL;
m_pRCWCache = NULL;
m_pRCWRefCache = NULL;
m_pLicenseInteropHelperMT = NULL;
m_COMorRemotingFlag = COMorRemoting_NotInitialized;
memset(m_rpCLRTypes, 0, sizeof(m_rpCLRTypes));
#endif // FEATURE_COMINTEROP
m_pUMEntryThunkCache = NULL;
m_pAsyncPool = NULL;
m_handleStore = NULL;
m_ExposedObject = NULL;
m_pComIPForExposedObject = NULL;
#ifdef _DEBUG
m_pThreadTrackInfoList = NULL;
m_TrackSpinLock = 0;
m_Assemblies.Debug_SetAppDomain(this);
#endif // _DEBUG
m_dwThreadEnterCount = 0;
m_dwThreadsStillInAppDomain = (ULONG)-1;
#ifdef FEATURE_COMINTEROP
m_pRefDispIDCache = NULL;
m_hndMissing = NULL;
#endif
m_pRefClassFactHash = NULL;
m_anonymouslyHostedDynamicMethodsAssembly = NULL;
m_ReversePInvokeCanEnter=TRUE;
m_ForceTrivialWaitOperations = false;
m_Stage=STAGE_CREATING;
m_bForceGCOnUnload=FALSE;
m_bUnloadingFromUnloadEvent=FALSE;
#ifdef _DEBUG
m_dwIterHolders=0;
m_dwRefTakers=0;
m_dwCreationHolders=0;
#endif
#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
m_ullTotalProcessorUsage = 0;
m_pullAllocBytes = NULL;
m_pullSurvivedBytes = NULL;
#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
#ifdef FEATURE_TYPEEQUIVALENCE
m_pTypeEquivalenceTable = NULL;
#endif // FEATURE_TYPEEQUIVALENCE
#ifdef FEATURE_COMINTEROP
m_pNameToTypeMap = NULL;
m_vNameToTypeMapVersion = 0;
m_nEpoch = 0;
m_pWinRTFactoryCache = NULL;
#endif // FEATURE_COMINTEROP
#ifdef FEATURE_PREJIT
m_pDomainFileWithNativeImageList = NULL;
#endif
m_fIsBindingModelLocked.Store(FALSE);
} // AppDomain::AppDomain
AppDomain::~AppDomain()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
#ifndef CROSSGEN_COMPILE
_ASSERTE(m_dwCreationHolders == 0);
// release the TPIndex. note that since TPIndex values are recycled the TPIndex
// can only be released once all threads in the AppDomain have exited.
if (GetTPIndex().m_dwIndex != 0)
PerAppDomainTPCountList::ResetAppDomainIndex(GetTPIndex());
if (m_dwId.m_dwId!=0)
SystemDomain::ReleaseAppDomainId(m_dwId);
m_AssemblyCache.Clear();
if (m_ADUnloadSink)
m_ADUnloadSink->Release();
if(!g_fEEInit)
Terminate();
#ifdef FEATURE_COMINTEROP
if (m_pNameToTypeMap != nullptr)
{
delete m_pNameToTypeMap;
m_pNameToTypeMap = nullptr;
}
if (m_pWinRTFactoryCache != nullptr)
{
delete m_pWinRTFactoryCache;
m_pWinRTFactoryCache = nullptr;
}
#endif //FEATURE_COMINTEROP
#ifdef _DEBUG
// If we were tracking thread AD transitions, cleanup the list on shutdown
if (m_pThreadTrackInfoList)
{
while (m_pThreadTrackInfoList->Count() > 0)
{
// Get the very last element
ThreadTrackInfo *pElem = *(m_pThreadTrackInfoList->Get(m_pThreadTrackInfoList->Count() - 1));
_ASSERTE(pElem);
// Free the memory
delete pElem;
// Remove pointer entry from the list
m_pThreadTrackInfoList->Delete(m_pThreadTrackInfoList->Count() - 1);
}
// Now delete the list itself
delete m_pThreadTrackInfoList;
m_pThreadTrackInfoList = NULL;
}
#endif // _DEBUG
#endif // CROSSGEN_COMPILE
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
void AppDomain::Init()
{
CONTRACTL
{
STANDARD_VM_CHECK;
PRECONDITION(SystemDomain::IsUnderDomainLock());
}
CONTRACTL_END;
m_pDelayedLoaderAllocatorUnloadList = NULL;
SetStage( STAGE_CREATING);
// The lock is taken also during stack walking (GC or profiler)
// - To prevent deadlock with GC thread, we cannot trigger GC while holding the lock
// - To prevent deadlock with profiler thread, we cannot allow thread suspension
m_crstHostAssemblyMap.Init(
CrstHostAssemblyMap,
(CrstFlags)(CRST_GC_NOTRIGGER_WHEN_TAKEN
| CRST_DEBUGGER_THREAD
INDEBUG(| CRST_DEBUG_ONLY_CHECK_FORBID_SUSPEND_THREAD)));
m_crstHostAssemblyMapAdd.Init(CrstHostAssemblyMapAdd);
m_dwId = SystemDomain::GetNewAppDomainId(this);
m_LoaderAllocator.Init(this);
#ifndef CROSSGEN_COMPILE
//Allocate the threadpool entry before the appdomain id list. Otherwise,
//the thread pool list will be out of sync if insertion of id in
//the appdomain fails.
m_tpIndex = PerAppDomainTPCountList::AddNewTPIndex();
#endif // CROSSGEN_COMPILE
m_dwIndex = SystemDomain::GetNewAppDomainIndex(this);
#ifndef CROSSGEN_COMPILE
PerAppDomainTPCountList::SetAppDomainId(m_tpIndex, m_dwId);
m_ADUnloadSink=new ADUnloadSink();
#endif
BaseDomain::Init();
// Set up the IL stub cache
</