// aggressively finalize objects referred to by static variables OR
// if someone is unloading the current AppDomain AND we have started
// finalizing objects referred to by static variables.
- FC_RETURN_BOOL((g_fEEShutDown & ShutDown_Finalize2) || GetAppDomain()->IsFinalizing());
+ FC_RETURN_BOOL(g_fEEShutDown & ShutDown_Finalize2);
}
FCIMPLEND
DD_ENTER_MAY_THROW;
AppDomain * pAppDomain = vmAppDomain.GetDacPtr();
- if (pAppDomain->IsUnloading())
- {
- return;
- }
{
pTypes->Alloc(iids.Count());
DD_ENTER_MAY_THROW;
AppDomain * pAppDomain = vmAppDomain.GetDacPtr();
- if (pAppDomain->IsUnloading())
- {
- return;
- }
InlineSArray<PTR_MethodTable, 32> rgMT;
InlineSArray<GUID, 32> rgGuid;
// It's critical that we don't yield appdomains after the unload event has been sent.
// See code:IDacDbiInterface#Enumeration for details.
AppDomain * pAppDomain = iterator.GetDomain();
- if (pAppDomain->IsUnloading())
- {
- continue;
- }
VMPTR_AppDomain vmAppDomain = VMPTR_AppDomain::NullPtr();
vmAppDomain.SetHostPtr(pAppDomain);
// in the domain. This is to enforce rules at code:IDacDbiInterface#Enumeration.
// See comment in code:DacDbiInterfaceImpl::EnumerateModulesInAssembly code for details.
AppDomain * pAppDomain = vmAppDomain.GetDacPtr();
- if (pAppDomain->IsUnloading())
- {
- return;
- }
// Pass the magical flags to the loader enumerator to get all Execution-only assemblies.
iterator = pAppDomain->IterateAssembliesEx((AssemblyIterationFlags)(kIncludeLoading | kIncludeLoaded | kIncludeExecution));
DomainAssembly * pDomainAssembly = vmAssembly.GetDacPtr();
- // If the appdomain or assembly containing this module is unloading, then don't enumerate any modules.
- // in the domain. This is to enforce rules at code:IDacDbiInterface#Enumeration, specifically
- // that new objects are not available after the unload event is sent.
- // This is a very large hammer, but since modules only unload with appdomains or assemblies, we're
- // erring on the side of safety. If the debugger happens to have VMPTR_DomainFiles (CordbModules) already
- // cached, it can still use those until the unload event.
- if (pDomainAssembly->IsUnloading())
- {
- return;
- }
-
-
// If the domain is not yet fully-loaded, don't advertise it yet.
// It's not ready to be inspected.
DomainModuleIterator iterator = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
// triggers too early in the loading process. FindDomainFile will not become
// non-NULL until the module is fully loaded into the domain which is what we
// want.
- if ((classModule->FindDomainFile(pAppDomain) != NULL ) &&
- !(fIsLoadEvent && pAppDomain->IsUnloading()) )
+ if (classModule->FindDomainFile(pAppDomain) != NULL )
{
// Find the Left Side module that this class belongs in.
DebuggerModule* pModule = LookupOrCreateModule(classModule, pAppDomain);
CONFIG_DWORD_INFO_DIRECT_ACCESS(INTERNAL_GenerateLongJumpDispatchStubRatio, W("GenerateLongJumpDispatchStubRatio"), "Useful for testing VSD on AMD64")
CONFIG_DWORD_INFO_EX(INTERNAL_HashStack, W("HashStack"), 0, "", CLRConfig::REGUTIL_default)
CONFIG_DWORD_INFO(INTERNAL_HostManagerConfig, W("HostManagerConfig"), (DWORD)-1, "")
-CONFIG_DWORD_INFO(INTERNAL_HostTestADUnload, W("HostTestADUnload"), 0, "Allows setting Rude unload as default")
CONFIG_DWORD_INFO(INTERNAL_HostTestThreadAbort, W("HostTestThreadAbort"), 0, "")
RETAIL_CONFIG_DWORD_INFO_EX(UNSUPPORTED_IgnoreDllMainReturn, W("IgnoreDllMainReturn"), 0, "Don't check the return value of DllMain if this is set", CLRConfig::ConfigFile_ApplicationFirst)
CONFIG_STRING_INFO(INTERNAL_InvokeHalt, W("InvokeHalt"), "Throws an assert when the given method is invoked through reflection.")
return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_Finalizer);
}
-inline BOOL IsADUnloadHelperThread ()
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_NOTRIGGER;
- STATIC_CONTRACT_MODE_ANY;
-
- return !!(((size_t)ClrFlsGetValue (TlsIdx_ThreadType)) & ThreadType_ADUnloadHelper);
-}
-
inline BOOL IsShutdownHelperThread ()
{
STATIC_CONTRACT_NOTHROW;
extern UMEntryPrestubUnwindFrameChainHandler:proc
extern UMThunkStubUnwindFrameChainHandler:proc
extern g_TrapReturningThreads:dword
-extern UM2MDoADCallBack:proc
extern UMThunkStubRareDisableWorker:proc
extern ReversePInvokeBadTransition:proc
InCooperativeMode:
- mov rax, [r12 + OFFSETOF__Thread__m_pDomain]
- mov eax, [rax + OFFSETOF__AppDomain__m_dwId]
-
- mov r11d, [METHODDESC_REGISTER + OFFSETOF__UMEntryThunk__m_dwDomainId]
-
- cmp rax, r11
- jne WrongAppDomain
-
mov r11, [METHODDESC_REGISTER + OFFSETOF__UMEntryThunk__m_pUMThunkMarshInfo]
mov eax, [r11 + OFFSETOF__UMThunkMarshInfo__m_cbActualArgSize] ; stack_args
test rax, rax ; stack_args
jmp ArgumentsSetup
-
-WrongAppDomain:
- ;
- ; home register args to the stack
- ;
- mov [rbp + UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET + 0h], rcx
- mov [rbp + UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET + 8h], rdx
- mov [rbp + UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET + 10h], r8
- mov [rbp + UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET + 18h], r9
-
- ;
- ; save off xmm registers
- ;
- movdqa xmmword ptr [rbp + UMThunkStubAMD64_XMM_SAVE_OFFSET + 0h], xmm0
- movdqa xmmword ptr [rbp + UMThunkStubAMD64_XMM_SAVE_OFFSET + 10h], xmm1
- movdqa xmmword ptr [rbp + UMThunkStubAMD64_XMM_SAVE_OFFSET + 20h], xmm2
- movdqa xmmword ptr [rbp + UMThunkStubAMD64_XMM_SAVE_OFFSET + 30h], xmm3
-
- ;
- ; call our helper to perform the AD transtion
- ;
- mov rcx, METHODDESC_REGISTER
- lea r8, [rbp + UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET]
- mov rax, [METHODDESC_REGISTER + OFFSETOF__UMEntryThunk__m_pUMThunkMarshInfo]
- mov r9d, [rax + OFFSETOF__UMThunkMarshInfo__m_cbActualArgSize]
- call UM2MDoADCallBack
-
- ; restore return value
- mov rax, [rbp + UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET + 0h]
- movdqa xmm0, xmmword ptr [rbp + UMThunkStubAMD64_XMM_SAVE_OFFSET + 0h]
-
- jmp PostCall
-
NESTED_END UMThunkStub, _TEXT
-;
-; EXTERN_C void __stdcall UM2MThunk_WrapperHelper(
-; void *pThunkArgs, ; rcx
-; int argLen, ; rdx
-; void *pAddr, ; r8 // not used
-; UMEntryThunk *pEntryThunk, ; r9
-; Thread *pThread); ; [entry_sp + 28h]
-;
-NESTED_ENTRY UM2MThunk_WrapperHelper, _TEXT
-
-
-UM2MThunk_WrapperHelper_STACK_FRAME_SIZE = 0
-
-; number of integer registers saved in prologue
-UM2MThunk_WrapperHelper_NUM_REG_PUSHES = 3
-UM2MThunk_WrapperHelper_STACK_FRAME_SIZE = UM2MThunk_WrapperHelper_STACK_FRAME_SIZE + (UM2MThunk_WrapperHelper_NUM_REG_PUSHES * 8)
-
-UM2MThunk_WrapperHelper_CALLEE_SCRATCH_SIZE = SIZEOF_MAX_OUTGOING_ARGUMENT_HOMES
-UM2MThunk_WrapperHelper_STACK_FRAME_SIZE = UM2MThunk_WrapperHelper_STACK_FRAME_SIZE + UM2MThunk_WrapperHelper_CALLEE_SCRATCH_SIZE
-
-; Ensure that rsp remains 16-byte aligned
-if ((UM2MThunk_WrapperHelper_STACK_FRAME_SIZE + 8) MOD 16) ne 0 ; +8 for caller-pushed return address
-UM2MThunk_WrapperHelper_STACK_FRAME_SIZE = UM2MThunk_WrapperHelper_STACK_FRAME_SIZE + 8
-endif
-
-UM2MThunk_WrapperHelper_FRAME_OFFSET = UM2MThunk_WrapperHelper_CALLEE_SCRATCH_SIZE
-UM2MThunk_WrapperHelper_FIXED_STACK_ALLOC_SIZE = UM2MThunk_WrapperHelper_STACK_FRAME_SIZE - (UM2MThunk_WrapperHelper_NUM_REG_PUSHES * 8)
-
- push_nonvol_reg rsi
- push_nonvol_reg rdi
- push_nonvol_reg rbp
- alloc_stack UM2MThunk_WrapperHelper_FIXED_STACK_ALLOC_SIZE
- set_frame rbp, UM2MThunk_WrapperHelper_FRAME_OFFSET
- END_PROLOGUE
-
- ;
- ; We are in cooperative mode and in the correct domain.
- ; The host has also been notified that we've entered the
- ; runtime. All we have left to do is to copy the stack,
- ; setup the register args and then call the managed target
- ;
-
- test rdx, rdx
- jg CopyStackArgs
-
-ArgumentsSetup:
- mov METHODDESC_REGISTER, r9
-
- mov rsi, rcx ; rsi <- pThunkArgs
- mov rcx, [rsi + 0h]
- mov rdx, [rsi + 8h]
- mov r8, [rsi + 10h]
- mov r9, [rsi + 18h]
-
- movdqa xmm0, xmmword ptr [rsi + UMThunkStubAMD64_XMM_SAVE_OFFSET - UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET + 0h]
- movdqa xmm1, xmmword ptr [rsi + UMThunkStubAMD64_XMM_SAVE_OFFSET - UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET + 10h]
- movdqa xmm2, xmmword ptr [rsi + UMThunkStubAMD64_XMM_SAVE_OFFSET - UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET + 20h]
- movdqa xmm3, xmmword ptr [rsi + UMThunkStubAMD64_XMM_SAVE_OFFSET - UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET + 30h]
-
- mov rax, [METHODDESC_REGISTER + OFFSETOF__UMEntryThunk__m_pUMThunkMarshInfo] ; rax <- UMThunkMarshInfo*
- mov rax, [rax + OFFSETOF__UMThunkMarshInfo__m_pILStub] ; rax <- Stub*
- call rax
-
- ; make sure we don't trash the return value
- mov [rsi + 0h], rax
- movdqa xmmword ptr [rsi + UMThunkStubAMD64_XMM_SAVE_OFFSET - UMThunkStubAMD64_ARGUMENTS_STACK_HOME_OFFSET + 0h], xmm0
-
- lea rsp, [rbp - UM2MThunk_WrapperHelper_FRAME_OFFSET + UM2MThunk_WrapperHelper_FIXED_STACK_ALLOC_SIZE]
- pop rbp
- pop rdi
- pop rsi
- ret
-
-
-CopyStackArgs:
- ; rdx = cbStackArgs (with 20h for register args subtracted out already)
- ; rcx = pSrcArgStack
-
- sub rsp, rdx
- and rsp, -16
-
- mov r8, rcx
-
- lea rsi, [rcx + SIZEOF_MAX_OUTGOING_ARGUMENT_HOMES]
- lea rdi, [rsp + UM2MThunk_WrapperHelper_CALLEE_SCRATCH_SIZE]
-
- mov rcx, rdx
- shr rcx, 3
-
- rep movsq
-
- mov rcx, r8
-
- jmp ArgumentsSetup
-
-NESTED_END UM2MThunk_WrapperHelper, _TEXT
-
end
ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_State
== offsetof(Thread, m_State));
-#define OFFSETOF__Thread__m_pDomain 0x20
+#define OFFSETOF__Thread__m_pDomain 0x18
ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_pDomain
== offsetof(Thread, m_pDomain));
-#define OFFSETOF__Thread__m_dwLockCount 0x28
+#define OFFSETOF__Thread__m_dwLockCount 0x20
ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_dwLockCount
== offsetof(Thread, m_dwLockCount));
-#define OFFSETOF__Thread__m_ThreadId 0x2C
+#define OFFSETOF__Thread__m_ThreadId 0x24
ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_ThreadId
== offsetof(Thread, m_ThreadId));
-#define OFFSET__Thread__m_alloc_context__alloc_ptr 0x60
+#define OFFSET__Thread__m_alloc_context__alloc_ptr 0x58
ASMCONSTANTS_C_ASSERT(OFFSET__Thread__m_alloc_context__alloc_ptr == offsetof(Thread, m_alloc_context) + offsetof(gc_alloc_context, alloc_ptr));
-#define OFFSET__Thread__m_alloc_context__alloc_limit 0x68
+#define OFFSET__Thread__m_alloc_context__alloc_limit 0x60
ASMCONSTANTS_C_ASSERT(OFFSET__Thread__m_alloc_context__alloc_limit == offsetof(Thread, m_alloc_context) + offsetof(gc_alloc_context, alloc_limit));
#define OFFSETOF__gc_alloc_context__alloc_ptr 0x0
#endif
NESTED_END UMThunkStub, _TEXT
-
-//
-// EXTERN_C void __stdcall UM2MThunk_WrapperHelper(
-// void *pThunkArgs, // rdi
-// int argLen, // rsi
-// void *pAddr, // rdx // not used
-// UMEntryThunk *pEntryThunk, // rcx
-// Thread *pThread); // r8
-//
-NESTED_ENTRY UM2MThunk_WrapperHelper, _TEXT, NoHandler
- int3
-NESTED_END UM2MThunk_WrapperHelper, _TEXT
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;
AppDomainIterator i(TRUE);
while (i.Next())
- if (i.GetDomain()->m_Stage < AppDomain::STAGE_CLEARED)
- i.GetDomain()->Stop();
+ i.GetDomain()->Stop();
}
}
}
-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
for (i = 0 ; i < count ; i++)
{
AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
- if (pAppDomain && pAppDomain->IsActive() && !pAppDomain->IsUnloading())
+ if (pAppDomain && pAppDomain->IsActive())
{
#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
if (g_fEnableARM)
for (i = 0 ; i < count ; i++)
{
AppDomain* pAppDomain = (AppDomain *)m_appDomainIdList.Get(i);
- if (pAppDomain && pAppDomain->IsActive() && !pAppDomain->IsUnloading())
+ if (pAppDomain && pAppDomain->IsActive())
{
dwTotalNumSizedRefHandles += pAppDomain->GetNumSizedRefHandles();
}
}
CONTRACTL_END;
- pDomain->SetCanUnload(); // by default can unload any domain
SystemDomain::System()->AddDomain(pDomain);
}
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()))
+ if (!result)
return NULL;
#endif // CROSSGEN_COMPILE
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.
m_ForceTrivialWaitOperations = false;
m_Stage=STAGE_CREATING;
- m_bForceGCOnUnload=FALSE;
- m_bUnloadingFromUnloadEvent=FALSE;
#ifdef _DEBUG
m_dwIterHolders=0;
m_dwRefTakers=0;
m_AssemblyCache.Clear();
- if (m_ADUnloadSink)
- m_ADUnloadSink->Release();
-
if(!g_fEEInit)
Terminate();
#ifndef CROSSGEN_COMPILE
PerAppDomainTPCountList::SetAppDomainId(m_tpIndex, m_dwId);
-
- m_ADUnloadSink=new ADUnloadSink();
#endif
BaseDomain::Init();
#ifndef CROSSGEN_COMPILE
-extern int g_fADUnloadWorkerOK;
-
-// Notes:
-// This helper will send the AppDomain creation notifications for profiler / debugger.
-// If it throws, its backout code will also send a notification.
-// If it succeeds, then we still need to send a AppDomainCreateFinished notification.
-void AppDomain::CreateUnmanagedObject(AppDomainCreationHolder<AppDomain>& pDomain)
-{
- CONTRACTL
- {
- THROWS;
- MODE_COOPERATIVE;
- GC_TRIGGERS;
- INJECT_FAULT(COMPlusThrowOM(););
- }
- CONTRACTL_END;
-
- GCX_PREEMP();
-
- pDomain.Assign(new AppDomain());
- if (g_fADUnloadWorkerOK<0)
- {
- AppDomain::CreateADUnloadWorker();
- }
-
- //@todo: B#25921
- // We addref Appdomain object here and notify a profiler that appdomain
- // creation has started, then return to managed code which will call
- // the function that releases the appdomain and notifies a profiler that we finished
- // creating the appdomain. If an exception is raised while we're in that managed code
- // we will leak memory and the profiler will not be notified about the failure
-
-#ifdef PROFILING_SUPPORTED
- // Signal profile if present.
- {
- BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
- g_profControlBlock.pProfInterface->AppDomainCreationStarted((AppDomainID) (AppDomain*) pDomain);
- END_PIN_PROFILER();
- }
- EX_TRY
-#endif // PROFILING_SUPPORTED
- {
- {
- SystemDomain::LockHolder lh;
- pDomain->Init();
- // allocate a Virtual Call Stub Manager for this domain
- pDomain->InitVSD();
- }
-
- pDomain->SetCanUnload(); // by default can unload any domain
-
- #ifdef DEBUGGING_SUPPORTED
- // Notify the debugger here, before the thread transitions into the
- // AD to finish the setup, and before any assemblies are loaded into it.
- SystemDomain::PublishAppDomainAndInformDebugger(pDomain);
- #endif // DEBUGGING_SUPPORTED
-
- STRESS_LOG2 (LF_APPDOMAIN, LL_INFO100, "Create domain [%d] %p\n", pDomain->GetId().m_dwId, (AppDomain*)pDomain);
- pDomain->LoadSystemAssemblies();
- pDomain->SetupSharedStatics();
-
- pDomain->SetStage(AppDomain::STAGE_ACTIVE);
- }
-#ifdef PROFILING_SUPPORTED
- EX_HOOK
- {
- // Need the first assembly loaded in to get any data on an app domain.
- {
- BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
- g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID)(AppDomain*) pDomain, GET_EXCEPTION()->GetHR());
- END_PIN_PROFILER();
- }
- }
- EX_END_HOOK;
-
- // On success, caller must still send the AppDomainCreationFinished notification.
-#endif // PROFILING_SUPPORTED
-}
-
void AppDomain::Stop()
{
CONTRACTL
OBJECTREF *ref;
};
-static void GetExposedObject_Wrapper(LPVOID ptr)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- }
- CONTRACTL_END;
- GetExposedObject_Args *args = (GetExposedObject_Args *) ptr;
- *(args->ref) = args->pDomain->GetExposedObject();
-}
-
-
OBJECTREF AppDomain::GetExposedObject()
{
CONTRACTL
{
APPDOMAINREF obj = NULL;
- Thread *pThread = GetThread();
- if (pThread->GetDomain() != this)
- {
- GCPROTECT_BEGIN(ref);
- GetExposedObject_Args args = {this, &ref};
- // call through DoCallBack with a domain transition
- pThread->DoADCallBack(this,GetExposedObject_Wrapper, &args,ADV_CREATING|ADV_RUNNINGIN);
- GCPROTECT_END();
- return ref;
- }
MethodTable *pMT = MscorlibBinder::GetClass(CLASS__APP_DOMAIN);
// Create the module object
if (!pMD->IsInterface() || pMD->IsStatic()) //interfaces require no activation for instance methods
{
//cctor could have been interupted by ADU
- CHECK_MSG(HasUnloadStarted() || pModule->CheckActivated(),
+ CHECK_MSG(pModule->CheckActivated(),
"Managed code can only run when its module has been activated in the current app domain");
}
DomainFile *result;
};
-#ifndef CROSSGEN_COMPILE
-static void LoadDomainFile_Wrapper(void *ptr)
-{
- WRAPPER_NO_CONTRACT;
- STATIC_CONTRACT_SO_INTOLERANT;
- GCX_PREEMP();
- LoadFileArgs *args = (LoadFileArgs *) ptr;
- args->result = GetAppDomain()->LoadDomainFile(args->pLock, args->targetLevel);
-}
-#endif // !CROSSGEN_COMPILE
-
DomainFile *AppDomain::LoadDomainFile(FileLoadLock *pLock, FileLoadLevel targetLevel)
{
CONTRACT(DomainFile *)
RETURN pFile;
}
-#ifndef CROSSGEN_COMPILE
- // Make sure we are in the right domain. Many of the load operations require the target domain
- // to be the current app domain, most notably anything involving managed code or managed object
- // creation.
- if (this != GetAppDomain()
- && (!pFile->GetFile()->IsSystem() || targetLevel > FILE_LOAD_ALLOCATE))
- {
- // Transition to the correct app domain and perform the load there.
- GCX_COOP();
-
- // we will release the lock in the other app domain
- lockRef.SuppressRelease();
-
- if(!CanLoadCode() || GetDefaultContext() ==NULL)
- COMPlusThrow(kAppDomainUnloadedException);
- LoadFileArgs args = {pLock, targetLevel, NULL};
- GetThread()->DoADCallBack(this, LoadDomainFile_Wrapper, (void *) &args, ADV_CREATING);
-
- RETURN args.result;
- }
-#endif // CROSSGEN_COMPILE
-
// Initialize a loading queue. This will hold any loads which are triggered recursively but
// which cannot be immediately satisfied due to anti-deadlock constraints.
ULONG cRef = InterlockedDecrement(&m_cRef);
if (!cRef)
{
- _ASSERTE (m_Stage == STAGE_CREATING || m_Stage == STAGE_CLOSED);
+ _ASSERTE (m_Stage == STAGE_CREATING);
ADID adid=GetId();
delete this;
TESTHOOKCALL(AppDomainDestroyed(adid.m_dwId));
}
-AppDomain* AppDomain::s_pAppDomainToRaiseUnloadEvent;
-BOOL AppDomain::s_fProcessUnloadDomainEvent = FALSE;
-
#ifndef CROSSGEN_COMPILE
-void AppDomain::RaiseUnloadDomainEvent_Wrapper(LPVOID ptr)
-{
- CONTRACTL
- {
- THROWS;
- MODE_COOPERATIVE;
- GC_TRIGGERS;
- INJECT_FAULT(COMPlusThrowOM(););
- SO_INTOLERANT;
- }
- CONTRACTL_END;
-
- AppDomain* pDomain = (AppDomain *) ptr;
- pDomain->RaiseUnloadDomainEvent();
-}
-
-void AppDomain::ProcessUnloadDomainEventOnFinalizeThread()
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- }
- CONTRACTL_END;
- Thread *pThread = GetThread();
- _ASSERTE (pThread && IsFinalizerThread());
-
- // if we are not unloading domain now, do not process the event
- if (SystemDomain::AppDomainBeingUnloaded() == NULL)
- {
- s_pAppDomainToRaiseUnloadEvent->SetStage(STAGE_UNLOAD_REQUESTED);
- s_pAppDomainToRaiseUnloadEvent->EnableADUnloadWorker(
- s_pAppDomainToRaiseUnloadEvent->IsRudeUnload()?EEPolicy::ADU_Rude:EEPolicy::ADU_Safe);
- FastInterlockExchangePointer(&s_pAppDomainToRaiseUnloadEvent, NULL);
- return;
- }
- FastInterlockExchange((LONG*)&s_fProcessUnloadDomainEvent, TRUE);
- AppDomain::EnableADUnloadWorkerForFinalizer();
- pThread->SetThreadStateNC(Thread::TSNC_RaiseUnloadEvent);
- s_pAppDomainToRaiseUnloadEvent->RaiseUnloadDomainEvent();
- pThread->ResetThreadStateNC(Thread::TSNC_RaiseUnloadEvent);
- s_pAppDomainToRaiseUnloadEvent->EnableADUnloadWorker(
- s_pAppDomainToRaiseUnloadEvent->IsRudeUnload()?EEPolicy::ADU_Rude:EEPolicy::ADU_Safe);
- FastInterlockExchangePointer(&s_pAppDomainToRaiseUnloadEvent, NULL);
- FastInterlockExchange((LONG*)&s_fProcessUnloadDomainEvent, FALSE);
-
- if (pThread->IsAbortRequested())
- {
- pThread->UnmarkThreadForAbort(Thread::TAR_Thread);
- }
-}
-
-void AppDomain::RaiseUnloadDomainEvent()
-{
- CONTRACTL
- {
- THROWS;
- MODE_COOPERATIVE;
- GC_TRIGGERS;
- SO_INTOLERANT;
- }
- CONTRACTL_END;
-
- Thread *pThread = GetThread();
- if (this != pThread->GetDomain())
- {
- pThread->DoADCallBack(this, AppDomain::RaiseUnloadDomainEvent_Wrapper, this,ADV_FINALIZER|ADV_COMPILATION);
- }
- else
- {
- struct _gc
- {
- APPDOMAINREF Domain;
- OBJECTREF Delegate;
- } gc;
- ZeroMemory(&gc, sizeof(gc));
-
- GCPROTECT_BEGIN(gc);
- gc.Domain = (APPDOMAINREF) GetRawExposedObject();
- if (gc.Domain != NULL)
- {
- gc.Delegate = gc.Domain->m_pDomainUnloadEventHandler;
- if (gc.Delegate != NULL)
- DistributeEvent(&gc.Delegate, (OBJECTREF *) &gc.Domain);
- }
- GCPROTECT_END();
- }
-}
-
void AppDomain::RaiseLoadingAssemblyEvent(DomainAssembly *pAssembly)
{
CONTRACTL
NOTHROW;
}
CONTRACTL_END;
- if (!CanThreadEnter(GetThread()))
- return FALSE;
if (GetRawExposedObject()==NULL)
return FALSE;
return (((APPDOMAINREF)GetRawExposedObject())->m_pUnhandledExceptionEventHandler!=NULL);
#endif // FEATURE_COMINTEROP
-BOOL AppDomain::CanThreadEnter(Thread *pThread)
-{
- WRAPPER_NO_CONTRACT;
-
- if (m_Stage < STAGE_EXITED)
- return TRUE;
-
- if (pThread == SystemDomain::System()->GetUnloadingThread())
- return m_Stage < STAGE_FINALIZING;
- if (pThread == FinalizerThread::GetFinalizerThread())
- return m_Stage < STAGE_FINALIZED;
-
- return FALSE;
-}
-
-void AppDomain::AllowThreadEntrance(AppDomain * pApp)
+void AppDomain::ExceptionUnwind(Frame *pFrame)
{
CONTRACTL
{
- NOTHROW;
- GC_TRIGGERS;
+ DISABLED(GC_TRIGGERS); // EEResourceException
+ DISABLED(THROWS); // EEResourceException
MODE_ANY;
- FORBID_FAULT;
- PRECONDITION(CheckPointer(pApp));
}
CONTRACTL_END;
- if (pApp->GetUnloadRequestThread() == NULL)
- {
- // This is asynchonous unload, either by a host, or by AppDomain.Unload from AD unload event.
- if (!pApp->IsUnloadingFromUnloadEvent())
- {
- pApp->SetStage(STAGE_UNLOAD_REQUESTED);
- pApp->EnableADUnloadWorker(
- pApp->IsRudeUnload()?EEPolicy::ADU_Rude:EEPolicy::ADU_Safe);
- return;
- }
- }
-
- SystemDomain::LockHolder lh; // we don't want to reopen appdomain if other thread can be preparing to unload it
-
-#ifdef FEATURE_COMINTEROP
- if (pApp->m_pComCallWrapperCache)
- pApp->m_pComCallWrapperCache->ResetDomainIsUnloading();
-#endif // FEATURE_COMINTEROP
+ LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind for %8.8x\n", pFrame));
+#if _DEBUG_ADUNLOAD
+ printf("%x AppDomain::ExceptionUnwind for %8.8p\n", GetThread()->GetThreadId(), pFrame);
+#endif
+ Thread *pThread = GetThread();
+ _ASSERTE(pThread);
- pApp->SetStage(STAGE_OPEN);
+ LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: not first transition or abort\n"));
}
-void AppDomain::RestrictThreadEntrance(AppDomain * pApp)
+BOOL AppDomain::StopEEAndUnwindThreads(unsigned int retryCount, BOOL *pFMarkUnloadRequestThread)
{
CONTRACTL
{
- DISABLED(NOTHROW);
- DISABLED(GC_TRIGGERS);
+ THROWS;
+ GC_TRIGGERS;
MODE_ANY;
- DISABLED(FORBID_FAULT);
- PRECONDITION(CheckPointer(pApp));
+ SO_INTOLERANT;
}
CONTRACTL_END;
-#ifdef FEATURE_COMINTEROP
- // Set the flag on our CCW cache so stubs won't enter
- if (pApp->m_pComCallWrapperCache)
- pApp->m_pComCallWrapperCache->SetDomainIsUnloading();
-#endif // FEATURE_COMINTEROP
-
- SystemDomain::LockHolder lh; // we don't want to reopen appdomain if other thread can be preparing to unload it
- // Release our ID so remoting and thread pool won't enter
- pApp->SetStage(STAGE_EXITED);
-};
-
-void AppDomain::Exit(BOOL fRunFinalizers, BOOL fAsyncExit)
-{
- CONTRACTL
+ Thread *pThread = NULL;
+ DWORD nThreadsNeedMoreWork=0;
+ if (retryCount != (unsigned int)-1 && retryCount < g_pConfig->AppDomainUnloadRetryCount())
{
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
+ Thread *pCurThread = GetThread();
+ if (pCurThread->CatchAtSafePoint())
+ pCurThread->PulseGCMode();
+
+ m_dwThreadsStillInAppDomain=nThreadsNeedMoreWork;
+ return !nThreadsNeedMoreWork;
}
- CONTRACTL_END;
- LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Exiting domain [%d] %#08x %ls\n",
- GetId().m_dwId, this, GetFriendlyNameForLogging()));
+ // For now piggyback on the GC's suspend EE mechanism
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN);
+#ifdef _DEBUG
+ // <TODO>@todo: what to do with any threads that didn't stop?</TODO>
+ _ASSERTE(ThreadStore::s_pThreadStore->DbgBackgroundThreadCount() > 0);
+#endif // _DEBUG
- RestrictEnterHolder RestrictEnter(this);
+ int totalADCount = 0;
+ int finalizerADCount = 0;
+ pThread = NULL;
- {
- SystemDomain::LockHolder lh; // we don't want to close appdomain if other thread can be preparing to unload it
- SetStage(STAGE_EXITING); // Note that we're trying to exit
- }
+ RuntimeExceptionKind reKind = kLastException;
+ UINT resId = 0;
+ SmallStackSString ssThreadId;
- // Raise the event indicating the domain is being unloaded.
- if (GetDefaultContext())
+ while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
{
- FastInterlockExchangePointer(&s_pAppDomainToRaiseUnloadEvent, this);
-
- DWORD timeout = GetEEPolicy()->GetTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload);
- //if (timeout == INFINITE)
- //{
- // timeout = 20000; // 20 seconds
- //}
- DWORD timeoutForFinalizer = GetEEPolicy()->GetTimeout(OPR_FinalizerRun);
- ULONGLONG curTime = CLRGetTickCount64();
- ULONGLONG endTime = 0;
- if (timeout != INFINITE)
+ // we already checked that we're not running in the unload domain
+ if (pThread == GetThread())
{
- endTime = curTime + timeout;
- // We will try to kill AD unload event if it takes too long, and then we move on to the next registered caller.
- timeout /= 5;
+ continue;
}
- while (s_pAppDomainToRaiseUnloadEvent != NULL)
- {
- FinalizerThread::FinalizerThreadWait(s_fProcessUnloadDomainEvent?timeout:timeoutForFinalizer);
- if (endTime != 0 && s_pAppDomainToRaiseUnloadEvent != NULL)
- {
- if (CLRGetTickCount64() >= endTime)
- {
- SString sThreadId;
- sThreadId.Printf(W("%x"), FinalizerThread::GetFinalizerThread()->GetThreadId());
- COMPlusThrow(kCannotUnloadAppDomainException,
- IDS_EE_ADUNLOAD_CANT_UNWIND_THREAD,
- sThreadId);
- }
- }
+#ifdef _DEBUG
+ void PrintStackTraceWithADToLog(Thread *pThread);
+ if (LoggingOn(LF_APPDOMAIN, LL_INFO100)) {
+ LOG((LF_APPDOMAIN, LL_INFO100, "\nStackTrace for %x\n", pThread->GetThreadId()));
+ PrintStackTraceWithADToLog(pThread);
+ }
+#endif // _DEBUG
+ int count = 0;
+ Frame *pFrame = pThread->GetFirstTransitionInto(this, &count);
+ if (! pFrame) {
+ _ASSERTE(count == 0);
+ continue;
}
- }
-
- // Tell the tiered compilation manager to stop initiating any new work for background
- // jit optimization. Its possible the standard thread unwind mechanisms would pre-emptively
- // evacuate the jit threadpool worker threads from the domain on their own, but I see no reason
- // to take the risk of relying on them when we can easily augment with a cooperative
- // shutdown check. This notification only initiates the process of evacuating the threads
- // and then the UnwindThreads() call below is where blocking will occur to ensure the threads
- // have exited the domain.
- //
-#ifdef FEATURE_TIERED_COMPILATION
- m_tieredCompilationManager.Shutdown();
-#endif
-
- //
- // Set up blocks so no threads can enter except for the finalizer and the thread
- // doing the unload.
- //
-
- RestrictThreadEntrance(this);
- // Cause existing threads to abort out of this domain. This should ensure all
- // normal threads are outside the domain, and we've already ensured that no new threads
- // can enter.
+ if (pThread != FinalizerThread::GetFinalizerThread())
+ {
+ totalADCount += count;
+ nThreadsNeedMoreWork++;
+ }
+ else
+ {
+ finalizerADCount = count;
+ }
- PerAppDomainTPCountList::AppDomainUnloadingHolder tpAdUnloadHolder(GetTPIndex());
+ // don't setup the exception info for the unloading thread unless it's the last one in
+ if (retryCount != ((unsigned int) -1) && retryCount > g_pConfig->AppDomainUnloadRetryCount() && reKind == kLastException)
+ {
+#ifdef AD_BREAK_ON_CANNOT_UNLOAD
+ static int breakOnCannotUnload = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADBreakOnCannotUnload);
+ if (breakOnCannotUnload)
+ _ASSERTE(!"Cannot unload AD");
+#endif // AD_BREAK_ON_CANNOT_UNLOAD
+ reKind = kCannotUnloadAppDomainException;
+ resId = IDS_EE_ADUNLOAD_CANT_UNWIND_THREAD;
+ ssThreadId.Printf(W("%x"), pThread->GetThreadId());
+ STRESS_LOG2(LF_APPDOMAIN, LL_INFO10, "AppDomain::UnwindThreads cannot stop thread %x with %d transitions\n", pThread->GetThreadId(), count);
+ // don't break out of this early or the assert totalADCount == (int)m_dwThreadEnterCount below will fire
+ // it's better to chew a little extra time here and make sure our counts are consistent
+ }
+ // only abort the thread requesting the unload if it's the last one in, that way it will get
+ // notification that the unload failed for some other thread not being aborted. And don't abort
+ // the finalizer thread - let it finish it's work as it's allowed to be in there. If it won't finish,
+ // then we will eventually get a CannotUnloadException on it.
+ if (pThread != FinalizerThread::GetFinalizerThread())
+ {
- if (!NingenEnabled())
- {
- UnwindThreads();
+ STRESS_LOG2(LF_APPDOMAIN, LL_INFO100, "AppDomain::UnwindThreads stopping %x with %d transitions\n", pThread->GetThreadId(), count);
+ LOG((LF_APPDOMAIN, LL_INFO100, "AppDomain::UnwindThreads stopping %x with %d transitions\n", pThread->GetThreadId(), count));
+#if _DEBUG_ADUNLOAD
+ printf("AppDomain::UnwindThreads %x stopping %x with first frame %8.8p\n", GetThread()->GetThreadId(), pThread->GetThreadId(), pFrame);
+#endif
+ pThread->SetAbortRequest(EEPolicy::TA_V1Compatible);
+ }
+ TESTHOOKCALL(UnwindingThreads(GetId().m_dwId)) ;
}
-
- TESTHOOKCALL(UnwoundThreads(GetId().m_dwId)) ;
- ProcessEventForHost(Event_DomainUnload, (PVOID)(UINT_PTR)GetId().m_dwId);
+ _ASSERTE(totalADCount + finalizerADCount == (int)m_dwThreadEnterCount);
- RestrictEnter.SuppressRelease(); //after this point we don't guarantee appdomain consistency
-#ifdef PROFILING_SUPPORTED
- // Signal profile if present.
- {
- BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
- GCX_PREEMP();
- g_profControlBlock.pProfInterface->AppDomainShutdownStarted((AppDomainID) this);
- END_PIN_PROFILER();
- }
-#endif // PROFILING_SUPPORTED
- COUNTER_ONLY(GetPerfCounters().m_Loading.cAppDomains--);
- COUNTER_ONLY(GetPerfCounters().m_Loading.cAppDomainsUnloaded++);
+ //@TODO: This is intended to catch a stress bug. Remove when no longer needed.
+ if (totalADCount + finalizerADCount != (int)m_dwThreadEnterCount)
+ FreeBuildDebugBreak();
- LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Domain [%d] %#08x %ls is exited.\n",
- GetId().m_dwId, this, GetFriendlyNameForLogging()));
+ // if our count did get messed up, set it to whatever count we actually found in the domain to avoid looping
+ // or other problems related to incorrect count. This is very much a bug if this happens - a thread should always
+ // exit the domain gracefully.
+ // m_dwThreadEnterCount = totalADCount;
- // Send ETW events for this domain's unload and potentially iterate through this
- // domain's modules & assemblies to send events for their unloads as well. This
- // needs to occur before STAGE_FINALIZED (to ensure everything is there), so we do
- // this before any finalization occurs at all.
- ETW::LoaderLog::DomainUnload(this);
+ // CommonTripThread will handle the abort for any threads that we've marked
+ ThreadSuspend::RestartEE(FALSE, TRUE);
+ if (reKind != kLastException)
+ COMPlusThrow(reKind, resId, ssThreadId.GetUnicode());
- CodeVersionManager::OnAppDomainExit(this);
+ _ASSERTE((totalADCount==0 && nThreadsNeedMoreWork==0) ||(totalADCount!=0 && nThreadsNeedMoreWork!=0));
+
+ m_dwThreadsStillInAppDomain=nThreadsNeedMoreWork;
+ return (totalADCount == 0);
+}
- //
- // Spin running finalizers until we flush them all. We need to make multiple passes
- // in case the finalizers create more finalizable objects. This is important to clear
- // the finalizable objects as roots, as well as to actually execute the finalizers. This
- // will only finalize instances instances of types that aren't potentially agile becuase we can't
- // risk finalizing agile objects. So we will be left with instances of potentially agile types
- // in handles or statics.
- //
- // <TODO>@todo: Need to ensure this will terminate in a reasonable amount of time. Eventually
- // we should probably start passing FALSE for fRunFinalizers. Also I'm not sure we
- // guarantee that FinalizerThreadWait will ever terminate in general.</TODO>
- //
+void AppDomain::UnwindThreads()
+{
+ // This function should guarantee appdomain
+ // consistency even if it fails. Everything that is going
+ // to make the appdomain impossible to reenter
+ // should be factored out
- SetStage(STAGE_FINALIZING);
+ // <TODO>@todo: need real synchronization here!!!</TODO>
+ CONTRACTL
+ {
+ MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ }
+ CONTRACTL_END;
- // Flush finalizers now.
- FinalizerThread::UnloadAppDomain(this, fRunFinalizers);
-
- DWORD timeout = GetEEPolicy()->GetTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload);
+ int retryCount = -1;
+ m_dwThreadsStillInAppDomain=(ULONG)-1;
ULONGLONG startTime = CLRGetTickCount64();
- ULONGLONG elapsedTime = 0;
- DWORD finalizerWait = 0;
- while (FinalizerThread::GetUnloadingAppDomain() != NULL)
- {
+ // Force threads to go through slow path during AD unload.
+ TSSuspendHolder shTrap;
+
+ BOOL fMarkUnloadRequestThread = TRUE;
+ // now wait for all the threads running in our AD to get out
+ do
+ {
+ DWORD timeout = GetEEPolicy()->GetTimeout(OPR_AppDomainUnload);
+ EPolicyAction action = GetEEPolicy()->GetActionOnTimeout(OPR_AppDomainUnload, NULL);
+ if (timeout != INFINITE && action >= eExitProcess) {
+ // Escalation policy specified.
+ ULONGLONG curTime = CLRGetTickCount64();
+ ULONGLONG elapseTime = curTime - startTime;
+ if (elapseTime > timeout)
+ {
+ // Escalate
+ switch (action)
+ {
+ case eExitProcess:
+ case eFastExitProcess:
+ case eRudeExitProcess:
+ case eDisableRuntime:
+ GetEEPolicy()->NotifyHostOnTimeout(OPR_AppDomainUnload, action);
+ EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_TIMEOUT);
+ _ASSERTE (!"Should not reach here");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+#ifdef _DEBUG
+ if (LoggingOn(LF_APPDOMAIN, LL_INFO100))
+ DumpADThreadTrack();
+#endif // _DEBUG
+ if (StopEEAndUnwindThreads(retryCount, &fMarkUnloadRequestThread))
+ break;
if (timeout != INFINITE)
{
- elapsedTime = CLRGetTickCount64() - startTime;
+ // Turn off the timeout used by AD.
+ retryCount = 1;
}
- if (timeout > elapsedTime)
+ else
{
- finalizerWait = timeout - static_cast<DWORD>(elapsedTime);
+ // GCStress takes a long time to unwind, due to expensive creation of
+ // a threadabort exception.
+ if (!GCStress<cfg_any>::IsEnabled())
+ ++retryCount;
+ LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::UnwindThreads iteration %d waiting on thread count %d\n", retryCount, m_dwThreadEnterCount));
+#if _DEBUG_ADUNLOAD
+ printf("AppDomain::UnwindThreads iteration %d waiting on thread count %d\n", retryCount, m_dwThreadEnterCount);
+#endif
}
- FinalizerThread::FinalizerThreadWait(finalizerWait); //will set stage to finalized
- if (timeout != INFINITE && FinalizerThread::GetUnloadingAppDomain() != NULL)
+
+ if (m_dwThreadEnterCount != 0)
{
- elapsedTime = CLRGetTickCount64() - startTime;
- if (timeout <= elapsedTime)
- {
- SetRudeUnload();
- // TODO: Consider escalation from RudeAppDomain
- timeout = INFINITE;
- }
+#ifdef _DEBUG
+ GetThread()->UserSleep(20);
+#else // !_DEBUG
+ GetThread()->UserSleep(10);
+#endif // !_DEBUG
}
}
+ while (TRUE) ;
+}
- tpAdUnloadHolder.SuppressRelease();
- PerAppDomainTPCountList::ResetAppDomainTPCounts(GetTPIndex());
-
- LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Domain [%d] %#08x %ls is finalized.\n",
- GetId().m_dwId, this, GetFriendlyNameForLogging()));
-
-
- AppDomainRefHolder This(this);
- AddRef(); // Hold a reference so CloseDomain won't delete us yet
- CloseDomain(); // Remove ourself from the list of app domains
-
- // This needs to be done prior to destroying the handle tables below.
- ReleaseDomainBoundInfo();
-
- //
- // It should be impossible to run non-mscorlib code in this domain now.
- // Cleanup all of our roots except the handles. We do this to allow as many
- // finalizers as possible to run correctly. If we delete the handles, they
- // can't run.
- //
- if (!NingenEnabled())
+void AppDomain::ClearGCRoots()
+{
+ CONTRACTL
{
+ GC_TRIGGERS;
+ MODE_COOPERATIVE;
+ NOTHROW;
}
+ CONTRACTL_END;
- ClearGCRoots();
- ClearGCHandles();
+ Thread *pThread = NULL;
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN);
- LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Domain [%d] %#08x %ls is cleared.\n",
- GetId().m_dwId, this, GetFriendlyNameForLogging()));
+ // Tell the JIT managers to delete any entries in their structures. All the cooperative mode threads are stopped at
+ // this point, so only need to synchronize the preemptive mode threads.
+ ExecutionManager::Unload(GetLoaderAllocator());
- if (fAsyncExit && fRunFinalizers)
+ while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL)
{
- GCX_PREEMP();
- m_AssemblyCache.Clear();
- ClearFusionContext();
- ReleaseFiles();
- if (!NingenEnabled())
+ // Delete the thread local static store
+ pThread->DeleteThreadStaticData(this);
+
+ // <TODO>@TODO: A pre-allocated AppDomainUnloaded exception might be better.</TODO>
+ if (m_handleStore->ContainsHandle(pThread->m_LastThrownObjectHandle))
{
- AddMemoryPressure();
+ // Never delete a handle to a preallocated exception object.
+ if (!CLRException::IsPreallocatedExceptionHandle(pThread->m_LastThrownObjectHandle))
+ {
+ DestroyHandle(pThread->m_LastThrownObjectHandle);
+ }
+
+ pThread->m_LastThrownObjectHandle = NULL;
}
+
+ // Clear out the exceptions objects held by a thread.
+ pThread->GetExceptionState()->ClearThrowablesForUnload(m_handleStore);
}
- SystemDomain::System()->AddToDelayedUnloadList(this, fAsyncExit);
- SystemDomain::SetUnloadDomainCleared();
- if (m_dwId.m_dwId!=0)
- SystemDomain::ReleaseAppDomainId(m_dwId);
-#ifdef PROFILING_SUPPORTED
- // Always signal profile if present, even when failed.
+
+ //delete them while we still have the runtime suspended
+ // This must be deleted before the loader heaps are deleted.
+ if (m_pMarshalingData != NULL)
{
- BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads());
- GCX_PREEMP();
- g_profControlBlock.pProfInterface->AppDomainShutdownFinished((AppDomainID) this, S_OK);
- END_PIN_PROFILER();
+ delete m_pMarshalingData;
+ m_pMarshalingData = NULL;
}
-#endif // PROFILING_SUPPORTED
+ if (m_pLargeHeapHandleTable != NULL)
+ {
+ delete m_pLargeHeapHandleTable;
+ m_pLargeHeapHandleTable = NULL;
+ }
+
+ ThreadSuspend::RestartEE(FALSE, TRUE);
}
-void AppDomain::Close()
+#ifdef _DEBUG
+
+void AppDomain::TrackADThreadEnter(Thread *pThread, Frame *pFrame)
{
CONTRACTL
{
- GC_TRIGGERS;
NOTHROW;
+ GC_NOTRIGGER;
+ // REENTRANT
+ PRECONDITION(CheckPointer(pThread));
+ PRECONDITION(pFrame != (Frame*)(size_t) INVALID_POINTER_CD);
}
CONTRACTL_END;
- LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Domain [%d] %#08x %ls is collected.\n",
- GetId().m_dwId, this, GetFriendlyNameForLogging()));
+ while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0)
+ ;
+ if (m_pThreadTrackInfoList == NULL)
+ m_pThreadTrackInfoList = new (nothrow) ThreadTrackInfoList;
+ // If we don't assert here, we will AV in the for loop below
+ _ASSERTE(m_pThreadTrackInfoList);
- {
- GCX_PREEMP();
- RemoveMemoryPressure();
+ ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList;
+
+ ThreadTrackInfo *pTrack = NULL;
+ int i;
+ for (i=0; i < pTrackList->Count(); i++) {
+ if ((*(pTrackList->Get(i)))->pThread == pThread) {
+ pTrack = *(pTrackList->Get(i));
+ break;
+ }
+ }
+ if (! pTrack) {
+ pTrack = new (nothrow) ThreadTrackInfo;
+ // If we don't assert here, we will AV in the for loop below.
+ _ASSERTE(pTrack);
+ pTrack->pThread = pThread;
+ ThreadTrackInfo **pSlot = pTrackList->Append();
+ *pSlot = pTrack;
}
- _ASSERTE(m_cRef>0); //should be alive at this point otherwise iterator can revive us and crash
+
+ InterlockedIncrement((LONG*)&m_dwThreadEnterCount);
+ Frame **pSlot;
+ if (pTrack)
{
- SystemDomain::LockHolder lh; // Avoid races with AppDomainIterator
- SetStage(STAGE_CLOSED);
+ pSlot = pTrack->frameStack.Insert(0);
+ *pSlot = pFrame;
}
+ int totThreads = 0;
+ for (i=0; i < pTrackList->Count(); i++)
+ totThreads += (*(pTrackList->Get(i)))->frameStack.Count();
+ _ASSERTE(totThreads == (int)m_dwThreadEnterCount);
- // CONSIDER: move releasing remoting cache from managed code to here.
+ InterlockedExchange((LONG*)&m_TrackSpinLock, 0);
}
-void AppDomain::ResetUnloadRequestThread(ADID Id)
+void AppDomain::TrackADThreadExit(Thread *pThread, Frame *pFrame)
{
CONTRACTL
{
+ if (GetThread()) {MODE_COOPERATIVE;}
NOTHROW;
- MODE_ANY;
- PRECONDITION(!IsADUnloadHelperThread());
+ GC_NOTRIGGER;
}
CONTRACTL_END;
- GCX_COOP();
- AppDomainFromIDHolder ad(Id, TRUE);
- if(!ad.IsUnloaded() && ad->m_Stage < STAGE_UNLOAD_REQUESTED)
+ while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0)
+ ;
+ ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList;
+ _ASSERTE(pTrackList);
+ ThreadTrackInfo *pTrack = NULL;
+ int i;
+ for (i=0; i < pTrackList->Count(); i++)
{
- Thread *pThread = ad->GetUnloadRequestThread();
- if(pThread==GetThread())
+ if ((*(pTrackList->Get(i)))->pThread == pThread)
{
- ad->m_dwThreadsStillInAppDomain=(ULONG)-1;
-
- if(pThread)
- {
- if (pThread->GetUnloadBoundaryFrame() && pThread->IsBeingAbortedForADUnload())
- {
- pThread->UnmarkThreadForAbort(Thread::TAR_ADUnload);
- }
- ad->GetUnloadRequestThread()->ResetUnloadBoundaryFrame();
- pThread->ResetBeginAbortedForADUnload();
- }
-
- ad->SetUnloadRequestThread(NULL);
- }
+ pTrack = *(pTrackList->Get(i));
+ break;
+ }
}
-}
+ _ASSERTE(pTrack);
+ _ASSERTE(*(pTrack->frameStack.Get(0)) == pFrame);
+ pTrack->frameStack.Delete(0);
+ InterlockedDecrement((LONG*)&m_dwThreadEnterCount);
+ int totThreads = 0;
+ for (i=0; i < pTrackList->Count(); i++)
+ totThreads += (*(pTrackList->Get(i)))->frameStack.Count();
+ _ASSERTE(totThreads == (int)m_dwThreadEnterCount);
-int g_fADUnloadWorkerOK = -1;
+ InterlockedExchange((LONG*)&m_TrackSpinLock, 0);
+}
-HRESULT AppDomain::UnloadById(ADID dwId, BOOL fSync,BOOL fExceptionsPassThrough)
+void AppDomain::DumpADThreadTrack()
{
CONTRACTL
{
- if(fExceptionsPassThrough) {THROWS;} else {NOTHROW;}
- MODE_ANY;
- if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_TRIGGERS);}
- FORBID_FAULT;
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_COOPERATIVE;
}
CONTRACTL_END;
- if (dwId==(ADID)DefaultADID)
- return COR_E_CANNOTUNLOADAPPDOMAIN;
-
- Thread *pThread = GetThread();
-
- // Finalizer thread can not wait until AD unload is done,
- // because AD unload is going to wait for Finalizer Thread.
- if (fSync && pThread == FinalizerThread::GetFinalizerThread() &&
- !pThread->HasThreadStateNC(Thread::TSNC_RaiseUnloadEvent))
- return COR_E_CANNOTUNLOADAPPDOMAIN;
-
-
- // AD unload helper thread should have been created.
- _ASSERTE (g_fADUnloadWorkerOK == 1);
-
- _ASSERTE (!IsADUnloadHelperThread());
-
- BOOL fIsRaisingUnloadEvent = (pThread != NULL && pThread->HasThreadStateNC(Thread::TSNC_RaiseUnloadEvent));
-
- if (fIsRaisingUnloadEvent)
- {
- AppDomainFromIDHolder pApp(dwId, TRUE, AppDomainFromIDHolder::SyncType_GC);
-
- if (pApp.IsUnloaded() || ! pApp->CanLoadCode() || pApp->GetId().m_dwId == 0)
- return COR_E_APPDOMAINUNLOADED;
-
- pApp->EnableADUnloadWorker();
-
- return S_FALSE;
- }
-
-
- ADUnloadSinkHolder pSink;
+ while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0)
+ ;
+ ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList;
+ if (!pTrackList)
+ goto end;
{
- SystemDomain::LockHolder ulh;
-
- AppDomainFromIDHolder pApp(dwId, TRUE, AppDomainFromIDHolder::SyncType_ADLock);
-
- if (pApp.IsUnloaded() || ! pApp->CanLoadCode() || pApp->GetId().m_dwId == 0)
- return COR_E_APPDOMAINUNLOADED;
-
- if (g_fADUnloadWorkerOK != 1)
- {
- _ASSERTE(FALSE);
- return E_UNEXPECTED;
- }
-
- if (!fSync)
+ LOG((LF_APPDOMAIN, LL_INFO10000, "\nThread dump of %d threads for [%d] %#08x %S\n",
+ m_dwThreadEnterCount, GetId().m_dwId, this, GetFriendlyNameForLogging()));
+ int totThreads = 0;
+ for (int i=0; i < pTrackList->Count(); i++)
{
- pApp->EnableADUnloadWorker();
- return S_OK;
+ ThreadTrackInfo *pTrack = *(pTrackList->Get(i));
+ if (pTrack->frameStack.Count()==0)
+ continue;
+ LOG((LF_APPDOMAIN, LL_INFO100, " ADEnterCount for %x is %d\n", pTrack->pThread->GetThreadId(), pTrack->frameStack.Count()));
+ totThreads += pTrack->frameStack.Count();
+ for (int j=0; j < pTrack->frameStack.Count(); j++)
+ LOG((LF_APPDOMAIN, LL_INFO100, " frame %8.8x\n", *(pTrack->frameStack.Get(j))));
}
+ _ASSERTE(totThreads == (int)m_dwThreadEnterCount);
+ }
+end:
+ InterlockedExchange((LONG*)&m_TrackSpinLock, 0);
+}
+#endif // _DEBUG
- pSink = pApp->PrepareForWaitUnloadCompletion();
-
- pApp->EnableADUnloadWorker();
- // release the holders - we don't care anymore if the appdomain is gone
- }
+#endif // CROSSGEN_COMPILE
-#ifdef FEATURE_TESTHOOKS
- if (fExceptionsPassThrough)
- {
- CONTRACT_VIOLATION(FaultViolation);
- return UnloadWaitNoCatch(dwId,pSink);
- }
-#endif
+void *SharedDomain::operator new(size_t size, void *pInPlace)
+{
+ LIMITED_METHOD_CONTRACT;
+ return pInPlace;
+}
- return UnloadWait(dwId,pSink);
+void SharedDomain::operator delete(void *pMem)
+{
+ LIMITED_METHOD_CONTRACT;
+ // Do nothing - new() was in-place
}
-HRESULT AppDomain::UnloadWait(ADID Id, ADUnloadSink * pSink)
+
+void SharedDomain::Attach()
{
CONTRACTL
{
- NOTHROW;
+ THROWS;
+ GC_TRIGGERS;
MODE_ANY;
- if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_TRIGGERS);}
+ INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
-
- HRESULT hr=S_OK;
- EX_TRY
- {
- // IF you ever try to change this to something not using events, please address the fact that
- // AppDomain::StopEEAndUnwindThreads relies on that events are used.
- pSink->WaitUnloadCompletion();
- }
- EX_CATCH_HRESULT(hr);
+ // Create the global SharedDomain and initialize it.
+ m_pSharedDomain = new (&g_pSharedDomainMemory[0]) SharedDomain();
+ SystemDomain::GetGlobalLoaderAllocator()->m_pDomain = m_pSharedDomain;
+ // This cannot fail since g_pSharedDomainMemory is a static array.
+ CONSISTENCY_CHECK(CheckPointer(m_pSharedDomain));
+
+ LOG((LF_CLASSLOADER,
+ LL_INFO10,
+ "Created shared domain at %p\n",
+ m_pSharedDomain));
- if (SUCCEEDED(hr))
- hr=pSink->GetUnloadResult();
+ // We need to initialize the memory pools etc. for the system domain.
+ m_pSharedDomain->Init(); // Setup the memory heaps
- if (FAILED(hr))
- {
- ResetUnloadRequestThread(Id);
- }
- return hr;
+ // allocate a Virtual Call Stub Manager for the shared domain
+ m_pSharedDomain->InitVSD();
}
-#ifdef FEATURE_TESTHOOKS
-HRESULT AppDomain::UnloadWaitNoCatch(ADID Id, ADUnloadSink * pSink)
+#ifndef CROSSGEN_COMPILE
+void SharedDomain::Detach()
{
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_MODE_ANY;
-
- Holder<ADID, DoNothing<ADID>, AppDomain::ResetUnloadRequestThread> resetUnloadHolder(Id);
-
- // IF you ever try to change this to something not using events, please address the fact that
- // AppDomain::StopEEAndUnwindThreads relies on that events are used.
- pSink->WaitUnloadCompletion();
+ if (m_pSharedDomain)
+ {
+ m_pSharedDomain->Terminate();
+ delete m_pSharedDomain;
+ m_pSharedDomain = NULL;
+ }
+}
+#endif // CROSSGEN_COMPILE
- HRESULT hr = pSink->GetUnloadResult();
+#endif // !DACCESS_COMPILE
- if (SUCCEEDED(hr))
- resetUnloadHolder.SuppressRelease();
+SharedDomain *SharedDomain::GetDomain()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
- return hr;
+ return m_pSharedDomain;
}
-#endif
-void AppDomain::Unload(BOOL fForceUnload)
+#ifndef DACCESS_COMPILE
+
+#define INITIAL_ASSEMBLY_MAP_SIZE 17
+void SharedDomain::Init()
{
CONTRACTL
{
THROWS;
- MODE_COOPERATIVE;
GC_TRIGGERS;
+ MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
-#ifdef FEATURE_MULTICOREJIT
-
- // Avoid profiling file is partially written in ASP.net scenarios, call it earlier
- GetMulticoreJitManager().StopProfile(true);
-
-#endif
-
- Thread *pThread = GetThread();
+ BaseDomain::Init();
+#ifdef FEATURE_LOADER_OPTIMIZATION
+ m_FileCreateLock.Init(CrstSharedAssemblyCreate, CRST_DEFAULT,TRUE);
- if (! fForceUnload && !g_pConfig->AppDomainUnload())
- return;
+ LockOwner lock = { &m_DomainCrst, IsOwnerOfCrst };
+ m_assemblyMap.Init(INITIAL_ASSEMBLY_MAP_SIZE, CompareSharedAssembly, TRUE, &lock);
+#endif // FEATURE_LOADER_OPTIMIZATION
- EPolicyAction action;
- EClrOperation operation;
- if (!IsRudeUnload())
- {
- operation = OPR_AppDomainUnload;
- }
- else
- {
- operation = OPR_AppDomainRudeUnload;
- }
- action = GetEEPolicy()->GetDefaultAction(operation,NULL);
- GetEEPolicy()->NotifyHostOnDefaultAction(operation,action);
+ ETW::LoaderLog::DomainLoad(this);
+}
- switch (action)
- {
- case eUnloadAppDomain:
- break;
- case eRudeUnloadAppDomain:
- SetRudeUnload();
- break;
- case eExitProcess:
- case eFastExitProcess:
- case eRudeExitProcess:
- case eDisableRuntime:
- EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_ADUNLOAD);
- _ASSERTE (!"Should not get here");
- break;
- default:
- break;
- }
+#ifndef CROSSGEN_COMPILE
+void SharedDomain::Terminate()
+{
+ // make sure we delete the StringLiteralMap before unloading
+ // the asemblies since the string literal map entries can
+ // point to metadata string literals.
+ GetLoaderAllocator()->CleanupStringLiteralMap();
-#if (defined(_DEBUG) || defined(BREAK_ON_UNLOAD) || defined(AD_LOG_MEMORY) || defined(AD_SNAPSHOT))
- static int unloadCount = 0;
-#endif
+#ifdef FEATURE_LOADER_OPTIMIZATION
+ PtrHashMap::PtrIterator i = m_assemblyMap.begin();
-#ifdef AD_LOG_MEMORY
+ while (!i.end())
{
- GCX_PREEMP();
- static int logMemory = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADLogMemory);
- typedef void (__cdecl *LogItFcn) ( int );
- static LogItFcn pLogIt = NULL;
-
- if (logMemory && ! pLogIt)
- {
- HMODULE hMod = CLRLoadLibrary(W("mpdh.dll"));
- if (hMod)
- {
- pLogIt = (LogItFcn)GetProcAddress(hMod, "logIt");
- if (pLogIt)
- {
- pLogIt(9999);
- pLogIt(9999);
- }
- }
- }
+ Assembly *pAssembly = (Assembly*) i.GetValue();
+ delete pAssembly;
+ ++i;
}
-#endif // AD_LOG_MEMORY
-
- if (IsDefaultDomain() && !IsSingleAppDomain())
- COMPlusThrow(kCannotUnloadAppDomainException, IDS_EE_ADUNLOAD_DEFAULT);
-
- _ASSERTE(CanUnload());
-
- if (pThread == FinalizerThread::GetFinalizerThread() || GetUnloadRequestThread() == FinalizerThread::GetFinalizerThread())
- COMPlusThrow(kCannotUnloadAppDomainException, IDS_EE_ADUNLOAD_IN_FINALIZER);
- _ASSERTE(! SystemDomain::AppDomainBeingUnloaded());
-
- // should not be running in this AD because unload spawned thread in default domain
- if (!NingenEnabled())
+ ListLockEntry* pElement;
+ pElement = m_FileCreateLock.Pop(TRUE);
+ while (pElement)
{
- _ASSERTE(!pThread->IsRunningIn(this, NULL));
- }
-
-
-#ifdef APPDOMAIN_STATE
- _ASSERTE_ALL_BUILDS("clr/src/VM/AppDomain.cpp", pThread->GetDomain()->IsDefaultDomain());
+#ifdef STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
+ _ASSERTE (dbg_fDrasticShutdown || g_fInControlC);
#endif
-
- LOG((LF_APPDOMAIN | LF_CORDB, LL_INFO10, "AppDomain::Unloading domain [%d] %#08x %ls\n", GetId().m_dwId, this, GetFriendlyName()));
-
- STRESS_LOG3 (LF_APPDOMAIN, LL_INFO100, "Unload domain [%d, %d] %p\n", GetId().m_dwId, GetIndex().m_dwIndex, this);
-
- UnloadHolder hold(this);
-
- SystemDomain::System()->SetUnloadRequestingThread(GetUnloadRequestThread());
- SystemDomain::System()->SetUnloadingThread(pThread);
-
-
-#ifdef _DEBUG
- static int dumpSB = -1;
-
- if (dumpSB == -1)
- dumpSB = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADDumpSB);
-
- if (dumpSB > 1)
- {
- LogSpewAlways("Starting unload %3.3d\n", unloadCount);
- DumpSyncBlockCache();
+ delete(pElement);
+ pElement = (FileLoadLock*) m_FileCreateLock.Pop(TRUE);
}
-#endif // _DEBUG
-
- BOOL bForceGC=m_bForceGCOnUnload;
-
-#ifdef AD_LOG_MEMORY
- if (pLogIt)
- bForceGC=TRUE;
-#endif // AD_LOG_MEMORY
-
-#ifdef AD_SNAPSHOT
- static int takeSnapShot = -1;
-
- if (takeSnapShot == -1)
- takeSnapShot = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADTakeSnapShot);
-
- if (takeSnapShot)
- bForceGC=TRUE;
-#endif // AD_SNAPSHOT
+ m_FileCreateLock.Destroy();
+#endif // FEATURE_LOADER_OPTIMIZATION
+ BaseDomain::Terminate();
+}
+#endif // CROSSGEN_COMPILE
-#ifdef _DEBUG
- if (dumpSB > 0)
- bForceGC=TRUE;
-#endif // _DEBUG
- static int cfgForceGC = -1;
- if (cfgForceGC == -1)
- cfgForceGC =!CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_ADULazyMemoryRelease);
- bForceGC=bForceGC||cfgForceGC;
- AppDomainRefHolder This(this);
- AddRef();
+#ifdef FEATURE_LOADER_OPTIMIZATION
- // Do the actual unloading
- {
- // We do not want other threads to abort the current one.
- ThreadPreventAsyncHolder preventAsync;
- Exit(TRUE, !bForceGC);
- }
- if(bForceGC)
+BOOL SharedDomain::CompareSharedAssembly(UPTR u1, UPTR u2)
+{
+ CONTRACTL
{
- GCHeapUtilities::GetGCHeap()->GarbageCollect();
- FinalizerThread::FinalizerThreadWait();
- SetStage(STAGE_COLLECTED);
- Close(); //NOTHROW!
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
}
+ CONTRACTL_END;
-#ifdef AD_LOG_MEMORY
- if (pLogIt)
- {
- GCX_PREEMP();
- pLogIt(unloadCount);
- }
-#endif // AD_LOG_MEMORY
+ // This is the input to the lookup
+ SharedAssemblyLocator *pLocator = (SharedAssemblyLocator *) (u1<<1);
-#ifdef AD_SNAPSHOT
- if (takeSnapShot)
+ // This is the value stored in the table
+ Assembly *pAssembly = (Assembly *) u2;
+ if (pLocator->GetType()==SharedAssemblyLocator::DOMAINASSEMBLY)
{
- char buffer[1024];
- sprintf_s(buffer, _countof(buffer), "vadump -p %d -o > vadump.%d", GetCurrentProcessId(), unloadCount);
- system(buffer);
- sprintf_s(buffer, _countof(buffer), "umdh -p:%d -d -i:1 -f:umdh.%d", GetCurrentProcessId(), unloadCount);
- system(buffer);
- int takeDHSnapShot = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADTakeDHSnapShot);
- if (takeDHSnapShot)
- {
- sprintf_s(buffer, _countof(buffer), "dh -p %d -s -g -h -b -f dh.%d", GetCurrentProcessId(), unloadCount);
- system(buffer);
- }
- }
-#endif // AD_SNAPSHOT
+ if (!pAssembly->GetManifestFile()->Equals(pLocator->GetDomainAssembly()->GetFile()))
+ return FALSE;
-#ifdef _DEBUG
- if (dumpSB > 0)
- {
- // do extra finalizer wait to remove any leftover sb entries
- FinalizerThread::FinalizerThreadWait();
- GCHeapUtilities::GetGCHeap()->GarbageCollect();
- FinalizerThread::FinalizerThreadWait();
- LogSpewAlways("Done unload %3.3d\n", unloadCount);
- DumpSyncBlockCache();
- ShutdownLogging();
- WCHAR buffer[128];
- swprintf_s(buffer, NumItems(buffer), W("DumpSB.%d"), unloadCount);
- _ASSERTE(WszMoveFileEx(W("COMPLUS.LOG"), buffer, MOVEFILE_REPLACE_EXISTING));
- // this will open a new file
- InitLogging();
+ return pAssembly->CanBeShared(pLocator->GetDomainAssembly());
}
-#endif // _DEBUG
+ else
+ if (pLocator->GetType()==SharedAssemblyLocator::PEASSEMBLY)
+ return pAssembly->GetManifestFile()->Equals(pLocator->GetPEAssembly());
+ else
+ if (pLocator->GetType()==SharedAssemblyLocator::PEASSEMBLYEXACT)
+ return pAssembly->GetManifestFile() == pLocator->GetPEAssembly();
+ _ASSERTE(!"Unexpected type of assembly locator");
+ return FALSE;
}
-void AppDomain::ExceptionUnwind(Frame *pFrame)
+DWORD SharedAssemblyLocator::Hash()
{
CONTRACTL
{
- DISABLED(GC_TRIGGERS); // EEResourceException
- DISABLED(THROWS); // EEResourceException
+ THROWS;
+ GC_TRIGGERS;
MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
+ if (m_type==DOMAINASSEMBLY)
+ return GetDomainAssembly()->HashIdentity();
+ if (m_type==PEASSEMBLY||m_type==PEASSEMBLYEXACT)
+ return GetPEAssembly()->HashIdentity();
+ _ASSERTE(!"Unexpected type of assembly locator");
+ return 0;
+}
- LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind for %8.8x\n", pFrame));
-#if _DEBUG_ADUNLOAD
- printf("%x AppDomain::ExceptionUnwind for %8.8p\n", GetThread()->GetThreadId(), pFrame);
-#endif
- Thread *pThread = GetThread();
- _ASSERTE(pThread);
-
- if (! pThread->ShouldChangeAbortToUnload(pFrame))
+Assembly * SharedDomain::FindShareableAssembly(SharedAssemblyLocator * pLocator)
+{
+ CONTRACTL
{
- LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: not first transition or abort\n"));
- return;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
}
+ CONTRACTL_END;
- LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::ExceptionUnwind: changing to unload\n"));
+ Assembly * match= (Assembly *) m_assemblyMap.LookupValue(pLocator->Hash(), pLocator);
+ if (match != (Assembly *) INVALIDENTRY)
+ return match;
+ else
+ return NULL;
+}
- GCX_COOP();
- OBJECTREF throwable = NULL;
- EEResourceException e(kAppDomainUnloadedException, W("Remoting_AppDomainUnloaded_ThreadUnwound"));
- throwable = e.GetThrowable();
+SIZE_T SharedDomain::GetShareableAssemblyCount()
+{
+ LIMITED_METHOD_CONTRACT;
- // reset the exception to an AppDomainUnloadedException
- if (throwable != NULL)
- {
- GetThread()->SafeSetThrowables(throwable);
- }
+ return m_assemblyMap.GetCount();
}
-BOOL AppDomain::StopEEAndUnwindThreads(unsigned int retryCount, BOOL *pFMarkUnloadRequestThread)
+void SharedDomain::AddShareableAssembly(Assembly * pAssembly)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
- SO_INTOLERANT;
+ INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
- Thread *pThread = NULL;
- DWORD nThreadsNeedMoreWork=0;
- if (retryCount != (unsigned int)-1 && retryCount < g_pConfig->AppDomainUnloadRetryCount())
- {
- Thread *pCurThread = GetThread();
- if (pCurThread->CatchAtSafePoint())
- pCurThread->PulseGCMode();
-
- {
- // We know which thread is not in the domain now. We just need to
- // work on those threads. We do not need to suspend the runtime.
- ThreadStoreLockHolder tsl;
-
- while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
- {
- if (pThread == pCurThread)
- {
- continue;
- }
-
- if (pThread == FinalizerThread::GetFinalizerThread())
- {
- continue;
- }
-
- if (pThread->GetUnloadBoundaryFrame() == NULL)
- {
- continue;
- }
-
- // A thread may have UnloadBoundaryFrame set if
- // 1. Being unloaded by AD unload helper thread
- // 2. Escalation from OOM or SO triggers AD unload
- // Here we only need to work on threads that are in the domain. If we work on other threads,
- // those threads may be stucked in a finally, and we will not be able to escalate for them,
- // therefore AD unload is blocked.
- if (pThread->IsBeingAbortedForADUnload() ||
- pThread == SystemDomain::System()->GetUnloadRequestingThread())
- {
- nThreadsNeedMoreWork++;
- }
-
- if (!(IsRudeUnload() ||
- (pThread != SystemDomain::System()->GetUnloadRequestingThread() || OnlyOneThreadLeft())))
- {
- continue;
- }
-
- if ((pThread == SystemDomain::System()->GetUnloadRequestingThread()) && *pFMarkUnloadRequestThread)
- {
- // Mark thread for abortion only once; later on interrupt only
- *pFMarkUnloadRequestThread = FALSE;
- pThread->SetAbortRequest(m_fRudeUnload? EEPolicy::TA_Rude : EEPolicy::TA_V1Compatible);
- }
- else
- {
- if (pThread->m_State & Thread::TS_Interruptible)
- {
- pThread->UserInterrupt(Thread::TI_Abort);
- }
- }
-
- if (pThread->PreemptiveGCDisabledOther())
- {
- #if defined(FEATURE_HIJACK) && !defined(PLATFORM_UNIX)
- Thread::SuspendThreadResult str = pThread->SuspendThread();
- if (str == Thread::STR_Success)
- {
- if (pThread->PreemptiveGCDisabledOther() &&
- (!pThread->IsAbortInitiated() || pThread->IsRudeAbort()))
- {
- pThread->HandleJITCaseForAbort();
- }
- pThread->ResumeThread();
- }
- #endif
- }
- }
- } // ThreadStoreLockHolder
-
- m_dwThreadsStillInAppDomain=nThreadsNeedMoreWork;
- return !nThreadsNeedMoreWork;
- }
-
- // For now piggyback on the GC's suspend EE mechanism
- ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN);
-#ifdef _DEBUG
- // <TODO>@todo: what to do with any threads that didn't stop?</TODO>
- _ASSERTE(ThreadStore::s_pThreadStore->DbgBackgroundThreadCount() > 0);
-#endif // _DEBUG
-
- int totalADCount = 0;
- int finalizerADCount = 0;
- pThread = NULL;
-
- RuntimeExceptionKind reKind = kLastException;
- UINT resId = 0;
- SmallStackSString ssThreadId;
+ // We have a lock on the file. There should be no races to add the same assembly.
- while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
{
- // we already checked that we're not running in the unload domain
- if (pThread == GetThread())
- {
- continue;
- }
-
-#ifdef _DEBUG
- void PrintStackTraceWithADToLog(Thread *pThread);
- if (LoggingOn(LF_APPDOMAIN, LL_INFO100)) {
- LOG((LF_APPDOMAIN, LL_INFO100, "\nStackTrace for %x\n", pThread->GetThreadId()));
- PrintStackTraceWithADToLog(pThread);
- }
-#endif // _DEBUG
- int count = 0;
- Frame *pFrame = pThread->GetFirstTransitionInto(this, &count);
- if (! pFrame) {
- _ASSERTE(count == 0);
- if (pThread->IsBeingAbortedForADUnload())
- {
- pThread->ResetBeginAbortedForADUnload();
- }
- continue;
- }
+ LockHolder holder(this);
- if (pThread != FinalizerThread::GetFinalizerThread())
- {
- totalADCount += count;
- nThreadsNeedMoreWork++;
- pThread->SetUnloadBoundaryFrame(pFrame);
- }
- else
+ EX_TRY
{
- finalizerADCount = count;
+ pAssembly->SetIsTenured();
+ m_assemblyMap.InsertValue(pAssembly->HashIdentity(), pAssembly);
}
-
- // don't setup the exception info for the unloading thread unless it's the last one in
- if (retryCount != ((unsigned int) -1) && retryCount > g_pConfig->AppDomainUnloadRetryCount() && reKind == kLastException &&
- (pThread != SystemDomain::System()->GetUnloadRequestingThread() || OnlyOneThreadLeft()))
+ EX_HOOK
{
-#ifdef AD_BREAK_ON_CANNOT_UNLOAD
- static int breakOnCannotUnload = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ADBreakOnCannotUnload);
- if (breakOnCannotUnload)
- _ASSERTE(!"Cannot unload AD");
-#endif // AD_BREAK_ON_CANNOT_UNLOAD
- reKind = kCannotUnloadAppDomainException;
- resId = IDS_EE_ADUNLOAD_CANT_UNWIND_THREAD;
- ssThreadId.Printf(W("%x"), pThread->GetThreadId());
- STRESS_LOG2(LF_APPDOMAIN, LL_INFO10, "AppDomain::UnwindThreads cannot stop thread %x with %d transitions\n", pThread->GetThreadId(), count);
- // don't break out of this early or the assert totalADCount == (int)m_dwThreadEnterCount below will fire
- // it's better to chew a little extra time here and make sure our counts are consistent
+ // There was an error adding the assembly to the assembly hash (probably an OOM),
+ // so we need to unset the tenured bit so that correct cleanup can happen.
+ pAssembly->UnsetIsTenured();
}
- // only abort the thread requesting the unload if it's the last one in, that way it will get
- // notification that the unload failed for some other thread not being aborted. And don't abort
- // the finalizer thread - let it finish it's work as it's allowed to be in there. If it won't finish,
- // then we will eventually get a CannotUnloadException on it.
+ EX_END_HOOK
+ }
- if (pThread != FinalizerThread::GetFinalizerThread() &&
- // If the domain is rudely unloaded, we will unwind the requesting thread out
- // Rude unload is going to succeed, or escalated to disable runtime or higher.
- (IsRudeUnload() ||
- (pThread != SystemDomain::System()->GetUnloadRequestingThread() || OnlyOneThreadLeft())
- )
- )
- {
+ LOG((LF_CODESHARING,
+ LL_INFO100,
+ "Successfully added shareable assembly \"%s\".\n",
+ pAssembly->GetManifestFile()->GetSimpleName()));
+}
- STRESS_LOG2(LF_APPDOMAIN, LL_INFO100, "AppDomain::UnwindThreads stopping %x with %d transitions\n", pThread->GetThreadId(), count);
- LOG((LF_APPDOMAIN, LL_INFO100, "AppDomain::UnwindThreads stopping %x with %d transitions\n", pThread->GetThreadId(), count));
-#if _DEBUG_ADUNLOAD
- printf("AppDomain::UnwindThreads %x stopping %x with first frame %8.8p\n", GetThread()->GetThreadId(), pThread->GetThreadId(), pFrame);
-#endif
- if (pThread == SystemDomain::System()->GetUnloadRequestingThread())
- {
- // Mark thread for abortion only once; later on interrupt only
- *pFMarkUnloadRequestThread = FALSE;
- }
- pThread->SetAbortRequest(m_fRudeUnload? EEPolicy::TA_Rude : EEPolicy::TA_V1Compatible);
- }
- TESTHOOKCALL(UnwindingThreads(GetId().m_dwId)) ;
- }
- _ASSERTE(totalADCount + finalizerADCount == (int)m_dwThreadEnterCount);
+#endif // FEATURE_LOADER_OPTIMIZATION
+#endif // !DACCESS_COMPILE
- //@TODO: This is intended to catch a stress bug. Remove when no longer needed.
- if (totalADCount + finalizerADCount != (int)m_dwThreadEnterCount)
- FreeBuildDebugBreak();
+DWORD DomainLocalModule::GetClassFlags(MethodTable* pMT, DWORD iClassIndex /*=(DWORD)-1*/)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ } CONTRACTL_END;
- // if our count did get messed up, set it to whatever count we actually found in the domain to avoid looping
- // or other problems related to incorrect count. This is very much a bug if this happens - a thread should always
- // exit the domain gracefully.
- // m_dwThreadEnterCount = totalADCount;
+ { // SO tolerance exception for debug-only assertion.
+ CONTRACT_VIOLATION(SOToleranceViolation);
+ CONSISTENCY_CHECK(GetDomainFile()->GetModule() == pMT->GetModuleForStatics());
+ }
- if (reKind != kLastException)
+ if (pMT->IsDynamicStatics())
{
- pThread = NULL;
- while ((pThread = ThreadStore::GetThreadList(pThread)) != NULL)
- {
- if (pThread->IsBeingAbortedForADUnload())
- {
- pThread->ResetBeginAbortedForADUnload();
- }
- }
+ _ASSERTE(!pMT->ContainsGenericVariables());
+ DWORD dynamicClassID = pMT->GetModuleDynamicEntryID();
+ if(m_aDynamicEntries <= dynamicClassID)
+ return FALSE;
+ return (m_pDynamicClassTable[dynamicClassID].m_dwFlags);
+ }
+ else
+ {
+ if (iClassIndex == (DWORD)-1)
+ iClassIndex = pMT->GetClassIndex();
+ return GetPrecomputedStaticsClassData()[iClassIndex];
}
-
- // CommonTripThread will handle the abort for any threads that we've marked
- ThreadSuspend::RestartEE(FALSE, TRUE);
- if (reKind != kLastException)
- COMPlusThrow(reKind, resId, ssThreadId.GetUnicode());
-
- _ASSERTE((totalADCount==0 && nThreadsNeedMoreWork==0) ||(totalADCount!=0 && nThreadsNeedMoreWork!=0));
-
- m_dwThreadsStillInAppDomain=nThreadsNeedMoreWork;
- return (totalADCount == 0);
}
-void AppDomain::UnwindThreads()
-{
- // This function should guarantee appdomain
- // consistency even if it fails. Everything that is going
- // to make the appdomain impossible to reenter
- // should be factored out
+#ifndef DACCESS_COMPILE
- // <TODO>@todo: need real synchronization here!!!</TODO>
+void DomainLocalModule::SetClassInitialized(MethodTable* pMT)
+{
CONTRACTL
{
- MODE_COOPERATIVE;
THROWS;
GC_TRIGGERS;
+ MODE_ANY;
}
CONTRACTL_END;
- int retryCount = -1;
- m_dwThreadsStillInAppDomain=(ULONG)-1;
- ULONGLONG startTime = CLRGetTickCount64();
+ BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain());
- if (GetEEPolicy()->GetDefaultAction(OPR_AppDomainUnload, NULL) == eRudeUnloadAppDomain &&
- !IsRudeUnload())
- {
- GetEEPolicy()->NotifyHostOnDefaultAction(OPR_AppDomainUnload, eRudeUnloadAppDomain);
- SetRudeUnload();
- }
+ _ASSERTE(!IsClassInitialized(pMT));
+ _ASSERTE(!IsClassInitError(pMT));
- // Force threads to go through slow path during AD unload.
- TSSuspendHolder shTrap;
+ SetClassFlags(pMT, ClassInitFlags::INITIALIZED_FLAG);
+}
- BOOL fCurrentUnloadMode = IsRudeUnload();
- BOOL fMarkUnloadRequestThread = TRUE;
+void DomainLocalModule::SetClassInitError(MethodTable* pMT)
+{
+ WRAPPER_NO_CONTRACT;
- // now wait for all the threads running in our AD to get out
- do
- {
- DWORD timeout = GetEEPolicy()->GetTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload);
- EPolicyAction action = GetEEPolicy()->GetActionOnTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload, NULL);
- if (timeout != INFINITE && action > eUnloadAppDomain) {
- // Escalation policy specified.
- ULONGLONG curTime = CLRGetTickCount64();
- ULONGLONG elapseTime = curTime - startTime;
- if (elapseTime > timeout)
- {
- // Escalate
- switch (action)
- {
- case eRudeUnloadAppDomain:
- GetEEPolicy()->NotifyHostOnTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload, action);
- SetRudeUnload();
- STRESS_LOG1(LF_APPDOMAIN, LL_INFO100,"Escalating to RADU, adid=%d",GetId().m_dwId);
- break;
- case eExitProcess:
- case eFastExitProcess:
- case eRudeExitProcess:
- case eDisableRuntime:
- GetEEPolicy()->NotifyHostOnTimeout(m_fRudeUnload?OPR_AppDomainRudeUnload : OPR_AppDomainUnload, action);
- EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_TIMEOUT);
- _ASSERTE (!"Should not reach here");
- break;
- default:
- break;
- }
- }
- }
-#ifdef _DEBUG
- if (LoggingOn(LF_APPDOMAIN, LL_INFO100))
- DumpADThreadTrack();
-#endif // _DEBUG
- BOOL fNextUnloadMode = IsRudeUnload();
- if (fCurrentUnloadMode != fNextUnloadMode)
- {
- // We have changed from normal unload to rude unload. We need to mark the thread
- // with RudeAbort, but we can only do this safely if the runtime is suspended.
- fCurrentUnloadMode = fNextUnloadMode;
- retryCount = -1;
- }
- if (StopEEAndUnwindThreads(retryCount, &fMarkUnloadRequestThread))
- break;
- if (timeout != INFINITE)
- {
- // Turn off the timeout used by AD.
- retryCount = 1;
- }
- else
- {
- // GCStress takes a long time to unwind, due to expensive creation of
- // a threadabort exception.
- if (!GCStress<cfg_any>::IsEnabled())
- ++retryCount;
- LOG((LF_APPDOMAIN, LL_INFO10, "AppDomain::UnwindThreads iteration %d waiting on thread count %d\n", retryCount, m_dwThreadEnterCount));
-#if _DEBUG_ADUNLOAD
- printf("AppDomain::UnwindThreads iteration %d waiting on thread count %d\n", retryCount, m_dwThreadEnterCount);
-#endif
- }
+ BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain());
- if (m_dwThreadEnterCount != 0)
- {
-#ifdef _DEBUG
- GetThread()->UserSleep(20);
-#else // !_DEBUG
- GetThread()->UserSleep(10);
-#endif // !_DEBUG
- }
+ SetClassFlags(pMT, ClassInitFlags::ERROR_FLAG);
+}
+
+void DomainLocalModule::SetClassFlags(MethodTable* pMT, DWORD dwFlags)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(GetDomainFile()->GetModule() == pMT->GetModuleForStatics());
+ // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
+ PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock());
+ } CONTRACTL_END;
+
+ if (pMT->IsDynamicStatics())
+ {
+ _ASSERTE(!pMT->ContainsGenericVariables());
+ DWORD dwID = pMT->GetModuleDynamicEntryID();
+ EnsureDynamicClassIndex(dwID);
+ m_pDynamicClassTable[dwID].m_dwFlags |= dwFlags;
+ }
+ else
+ {
+ GetPrecomputedStaticsClassData()[pMT->GetClassIndex()] |= dwFlags;
}
- while (TRUE) ;
}
-void AppDomain::ClearGCHandles()
+void DomainLocalModule::EnsureDynamicClassIndex(DWORD dwID)
{
CONTRACTL
{
+ THROWS;
GC_TRIGGERS;
- MODE_COOPERATIVE;
- NOTHROW;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
+ PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock());
}
CONTRACTL_END;
- SetStage(STAGE_HANDLETABLE_NOACCESS);
+ if (dwID < m_aDynamicEntries)
+ {
+ _ASSERTE(m_pDynamicClassTable.Load() != NULL);
+ return;
+ }
+
+ SIZE_T aDynamicEntries = max(16, m_aDynamicEntries.Load());
+ while (aDynamicEntries <= dwID)
+ {
+ aDynamicEntries *= 2;
+ }
+
+ DynamicClassInfo* pNewDynamicClassTable;
+ pNewDynamicClassTable = (DynamicClassInfo*)
+ (void*)GetDomainFile()->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(
+ S_SIZE_T(sizeof(DynamicClassInfo)) * S_SIZE_T(aDynamicEntries));
+
+ memcpy(pNewDynamicClassTable, m_pDynamicClassTable, sizeof(DynamicClassInfo) * m_aDynamicEntries);
+
+ // Note: Memory allocated on loader heap is zero filled
+ // memset(pNewDynamicClassTable + m_aDynamicEntries, 0, (aDynamicEntries - m_aDynamicEntries) * sizeof(DynamicClassInfo));
- GCHeapUtilities::GetGCHeap()->WaitUntilConcurrentGCComplete();
+ _ASSERTE(m_aDynamicEntries%2 == 0);
- // Remove our handle store as a source of GC roots
- m_handleStore->Uproot();
+ // Commit new dynamic table. The lock-free helpers depend on the order.
+ MemoryBarrier();
+ m_pDynamicClassTable = pNewDynamicClassTable;
+ MemoryBarrier();
+ m_aDynamicEntries = aDynamicEntries;
}
-void AppDomain::ClearGCRoots()
+#ifndef CROSSGEN_COMPILE
+void DomainLocalModule::AllocateDynamicClass(MethodTable *pMT)
{
CONTRACTL
{
+ THROWS;
GC_TRIGGERS;
- MODE_COOPERATIVE;
- NOTHROW;
+ // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
+ PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock());
}
CONTRACTL_END;
- Thread *pThread = NULL;
- ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN);
+ _ASSERTE(!pMT->ContainsGenericVariables());
+ _ASSERTE(!pMT->IsSharedByGenericInstantiations());
+ _ASSERTE(GetDomainFile()->GetModule() == pMT->GetModuleForStatics());
+ _ASSERTE(pMT->IsDynamicStatics());
- // Tell the JIT managers to delete any entries in their structures. All the cooperative mode threads are stopped at
- // this point, so only need to synchronize the preemptive mode threads.
- ExecutionManager::Unload(GetLoaderAllocator());
+ DWORD dynamicEntryIDIndex = pMT->GetModuleDynamicEntryID();
- while ((pThread = ThreadStore::GetAllThreadList(pThread, 0, 0)) != NULL)
- {
- // Delete the thread local static store
- pThread->DeleteThreadStaticData(this);
+ EnsureDynamicClassIndex(dynamicEntryIDIndex);
- // <TODO>@TODO: A pre-allocated AppDomainUnloaded exception might be better.</TODO>
- if (m_handleStore->ContainsHandle(pThread->m_LastThrownObjectHandle))
+ _ASSERTE(m_aDynamicEntries > dynamicEntryIDIndex);
+
+ EEClass *pClass = pMT->GetClass();
+
+ DWORD dwStaticBytes = pClass->GetNonGCRegularStaticFieldBytes();
+ DWORD dwNumHandleStatics = pClass->GetNumHandleRegularStatics();
+
+ _ASSERTE(!IsClassAllocated(pMT));
+ _ASSERTE(!IsClassInitialized(pMT));
+ _ASSERTE(!IsClassInitError(pMT));
+
+ DynamicEntry *pDynamicStatics = m_pDynamicClassTable[dynamicEntryIDIndex].m_pDynamicEntry;
+
+ // We need this check because maybe a class had a cctor but no statics
+ if (dwStaticBytes > 0 || dwNumHandleStatics > 0)
+ {
+ if (pDynamicStatics == NULL)
{
- // Never delete a handle to a preallocated exception object.
- if (!CLRException::IsPreallocatedExceptionHandle(pThread->m_LastThrownObjectHandle))
+ LoaderHeap * pLoaderAllocator = GetDomainFile()->GetLoaderAllocator()->GetHighFrequencyHeap();
+
+ if (pMT->Collectible())
{
- DestroyHandle(pThread->m_LastThrownObjectHandle);
+ pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocMem(S_SIZE_T(sizeof(CollectibleDynamicEntry)));
}
+ else
+ {
+ SIZE_T dynamicEntrySize = DynamicEntry::GetOffsetOfDataBlob() + dwStaticBytes;
- pThread->m_LastThrownObjectHandle = NULL;
- }
+#ifdef FEATURE_64BIT_ALIGNMENT
+ // Allocate memory with extra alignment only if it is really necessary
+ if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE)
+ {
+ static_assert_no_msg(sizeof(NormalDynamicEntry) % MAX_PRIMITIVE_FIELD_SIZE == 0);
+ pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocAlignedMem(dynamicEntrySize, MAX_PRIMITIVE_FIELD_SIZE);
+ }
+ else
+#endif
+ pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocMem(S_SIZE_T(dynamicEntrySize));
+ }
- // Clear out the exceptions objects held by a thread.
- pThread->GetExceptionState()->ClearThrowablesForUnload(m_handleStore);
- }
+ // Note: Memory allocated on loader heap is zero filled
- //delete them while we still have the runtime suspended
- // This must be deleted before the loader heaps are deleted.
- if (m_pMarshalingData != NULL)
- {
- delete m_pMarshalingData;
- m_pMarshalingData = NULL;
- }
+ m_pDynamicClassTable[dynamicEntryIDIndex].m_pDynamicEntry = pDynamicStatics;
+ }
- if (m_pLargeHeapHandleTable != NULL)
- {
- delete m_pLargeHeapHandleTable;
- m_pLargeHeapHandleTable = NULL;
+ if (pMT->Collectible() && (dwStaticBytes != 0))
+ {
+ GCX_COOP();
+ OBJECTREF nongcStaticsArray = NULL;
+ GCPROTECT_BEGIN(nongcStaticsArray);
+#ifdef FEATURE_64BIT_ALIGNMENT
+ // Allocate memory with extra alignment only if it is really necessary
+ if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE)
+ nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_I8, (dwStaticBytes + (sizeof(CLR_I8)-1)) / (sizeof(CLR_I8)));
+ else
+#endif
+ nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_U1, dwStaticBytes);
+ ((CollectibleDynamicEntry *)pDynamicStatics)->m_hNonGCStatics = GetDomainFile()->GetModule()->GetLoaderAllocator()->AllocateHandle(nongcStaticsArray);
+ GCPROTECT_END();
+ }
+ if (dwNumHandleStatics > 0)
+ {
+ if (!pMT->Collectible())
+ {
+ GetAppDomain()->AllocateStaticFieldObjRefPtrs(dwNumHandleStatics,
+ &((NormalDynamicEntry *)pDynamicStatics)->m_pGCStatics);
+ }
+ else
+ {
+ GCX_COOP();
+ OBJECTREF gcStaticsArray = NULL;
+ GCPROTECT_BEGIN(gcStaticsArray);
+ gcStaticsArray = AllocateObjectArray(dwNumHandleStatics, g_pObjectClass);
+ ((CollectibleDynamicEntry *)pDynamicStatics)->m_hGCStatics = GetDomainFile()->GetModule()->GetLoaderAllocator()->AllocateHandle(gcStaticsArray);
+ GCPROTECT_END();
+ }
+ }
}
-
- ThreadSuspend::RestartEE(FALSE, TRUE);
}
-#ifdef _DEBUG
-void AppDomain::TrackADThreadEnter(Thread *pThread, Frame *pFrame)
+void DomainLocalModule::PopulateClass(MethodTable *pMT)
{
CONTRACTL
{
- NOTHROW;
- GC_NOTRIGGER;
- // REENTRANT
- PRECONDITION(CheckPointer(pThread));
- PRECONDITION(pFrame != (Frame*)(size_t) INVALID_POINTER_CD);
+ THROWS;
+ GC_TRIGGERS;
}
CONTRACTL_END;
- while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0)
- ;
- if (m_pThreadTrackInfoList == NULL)
- m_pThreadTrackInfoList = new (nothrow) ThreadTrackInfoList;
- // If we don't assert here, we will AV in the for loop below
- _ASSERTE(m_pThreadTrackInfoList);
+ _ASSERTE(!pMT->ContainsGenericVariables());
- ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList;
+ // <todo> the only work actually done here for non-dynamics is the freezing related work.
+ // See if we can eliminate this and make this a dynamic-only path </todo>
+ DWORD iClassIndex = pMT->GetClassIndex();
- ThreadTrackInfo *pTrack = NULL;
- int i;
- for (i=0; i < pTrackList->Count(); i++) {
- if ((*(pTrackList->Get(i)))->pThread == pThread) {
- pTrack = *(pTrackList->Get(i));
- break;
- }
- }
- if (! pTrack) {
- pTrack = new (nothrow) ThreadTrackInfo;
- // If we don't assert here, we will AV in the for loop below.
- _ASSERTE(pTrack);
- pTrack->pThread = pThread;
- ThreadTrackInfo **pSlot = pTrackList->Append();
- *pSlot = pTrack;
- }
-
- InterlockedIncrement((LONG*)&m_dwThreadEnterCount);
- Frame **pSlot;
- if (pTrack)
+ if (!IsClassAllocated(pMT, iClassIndex))
{
- pSlot = pTrack->frameStack.Insert(0);
- *pSlot = pFrame;
- }
- int totThreads = 0;
- for (i=0; i < pTrackList->Count(); i++)
- totThreads += (*(pTrackList->Get(i)))->frameStack.Count();
- _ASSERTE(totThreads == (int)m_dwThreadEnterCount);
+ BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain());
- InterlockedExchange((LONG*)&m_TrackSpinLock, 0);
-}
+ if (!IsClassAllocated(pMT, iClassIndex))
+ {
+ // Allocate dynamic space if necessary
+ if (pMT->IsDynamicStatics())
+ AllocateDynamicClass(pMT);
+
+ // determine flags to set on the statics block
+ DWORD dwFlags = ClassInitFlags::ALLOCATECLASS_FLAG;
+ if (!pMT->HasClassConstructor() && !pMT->HasBoxedRegularStatics())
+ {
+ _ASSERTE(!IsClassInitialized(pMT));
+ _ASSERTE(!IsClassInitError(pMT));
+ dwFlags |= ClassInitFlags::INITIALIZED_FLAG;
+ }
-void AppDomain::TrackADThreadExit(Thread *pThread, Frame *pFrame)
-{
- CONTRACTL
- {
- if (GetThread()) {MODE_COOPERATIVE;}
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
+ if (pMT->Collectible())
+ {
+ dwFlags |= ClassInitFlags::COLLECTIBLE_FLAG;
+ }
- while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0)
- ;
- ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList;
- _ASSERTE(pTrackList);
- ThreadTrackInfo *pTrack = NULL;
- int i;
- for (i=0; i < pTrackList->Count(); i++)
- {
- if ((*(pTrackList->Get(i)))->pThread == pThread)
- {
- pTrack = *(pTrackList->Get(i));
- break;
+ // Set all flags at the same time to avoid races
+ SetClassFlags(pMT, dwFlags);
}
}
- _ASSERTE(pTrack);
- _ASSERTE(*(pTrack->frameStack.Get(0)) == pFrame);
- pTrack->frameStack.Delete(0);
- InterlockedDecrement((LONG*)&m_dwThreadEnterCount);
-
- int totThreads = 0;
- for (i=0; i < pTrackList->Count(); i++)
- totThreads += (*(pTrackList->Get(i)))->frameStack.Count();
- _ASSERTE(totThreads == (int)m_dwThreadEnterCount);
- InterlockedExchange((LONG*)&m_TrackSpinLock, 0);
+ return;
}
+#endif // CROSSGEN_COMPILE
-void AppDomain::DumpADThreadTrack()
+void DomainLocalBlock::EnsureModuleIndex(ModuleIndex index)
{
CONTRACTL
{
- NOTHROW;
- GC_NOTRIGGER;
- MODE_COOPERATIVE;
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(COMPlusThrowOM(););
+ // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
+ PRECONDITION(m_pDomain->OwnDomainLocalBlockLock());
}
CONTRACTL_END;
- while (FastInterlockCompareExchange((LONG*)&m_TrackSpinLock, 1, 0) != 0)
- ;
- ThreadTrackInfoList *pTrackList= m_pThreadTrackInfoList;
- if (!pTrackList)
- goto end;
+ if (m_aModuleIndices > index.m_dwIndex)
+ {
+ _ASSERTE(m_pModuleSlots != NULL);
+ return;
+ }
+ SIZE_T aModuleIndices = max(16, m_aModuleIndices);
+ while (aModuleIndices <= index.m_dwIndex)
{
- LOG((LF_APPDOMAIN, LL_INFO10000, "\nThread dump of %d threads for [%d] %#08x %S\n",
- m_dwThreadEnterCount, GetId().m_dwId, this, GetFriendlyNameForLogging()));
- int totThreads = 0;
- for (int i=0; i < pTrackList->Count(); i++)
- {
- ThreadTrackInfo *pTrack = *(pTrackList->Get(i));
- if (pTrack->frameStack.Count()==0)
- continue;
- LOG((LF_APPDOMAIN, LL_INFO100, " ADEnterCount for %x is %d\n", pTrack->pThread->GetThreadId(), pTrack->frameStack.Count()));
- totThreads += pTrack->frameStack.Count();
- for (int j=0; j < pTrack->frameStack.Count(); j++)
- LOG((LF_APPDOMAIN, LL_INFO100, " frame %8.8x\n", *(pTrack->frameStack.Get(j))));
- }
- _ASSERTE(totThreads == (int)m_dwThreadEnterCount);
+ aModuleIndices *= 2;
}
-end:
- InterlockedExchange((LONG*)&m_TrackSpinLock, 0);
-}
-#endif // _DEBUG
+ PTR_DomainLocalModule* pNewModuleSlots = (PTR_DomainLocalModule*) (void*)m_pDomain->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PTR_DomainLocalModule)) * S_SIZE_T(aModuleIndices));
-#endif // CROSSGEN_COMPILE
+ memcpy(pNewModuleSlots, m_pModuleSlots, sizeof(SIZE_T)*m_aModuleIndices);
+
+ // Note: Memory allocated on loader heap is zero filled
+ // memset(pNewModuleSlots + m_aModuleIndices, 0 , (aModuleIndices - m_aModuleIndices)*sizeof(PTR_DomainLocalModule) );
+
+ // Commit new table. The lock-free helpers depend on the order.
+ MemoryBarrier();
+ m_pModuleSlots = pNewModuleSlots;
+ MemoryBarrier();
+ m_aModuleIndices = aModuleIndices;
-void *SharedDomain::operator new(size_t size, void *pInPlace)
-{
- LIMITED_METHOD_CONTRACT;
- return pInPlace;
}
-void SharedDomain::operator delete(void *pMem)
+void DomainLocalBlock::SetModuleSlot(ModuleIndex index, PTR_DomainLocalModule pLocalModule)
{
- LIMITED_METHOD_CONTRACT;
- // Do nothing - new() was in-place
+ // Need to synchronize with table growth in this domain
+ BaseDomain::DomainLocalBlockLockHolder lh(m_pDomain);
+
+ EnsureModuleIndex(index);
+
+ _ASSERTE(index.m_dwIndex < m_aModuleIndices);
+
+ // We would like this assert here, unfortunately, loading a module in this appdomain can fail
+ // after here and we will keep the module around and reuse the slot when we retry (if
+ // the failure happened due to a transient error, such as OOM). In that case the slot wont
+ // be null.
+ //_ASSERTE(m_pModuleSlots[index.m_dwIndex] == 0);
+
+ m_pModuleSlots[index.m_dwIndex] = pLocalModule;
}
+#ifndef CROSSGEN_COMPILE
-void SharedDomain::Attach()
+DomainAssembly* AppDomain::RaiseTypeResolveEventThrowing(DomainAssembly* pAssembly, LPCSTR szName, ASSEMBLYREF *pResultingAssemblyRef)
{
CONTRACTL
{
- THROWS;
- GC_TRIGGERS;
MODE_ANY;
+ GC_TRIGGERS;
+ THROWS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
- // Create the global SharedDomain and initialize it.
- m_pSharedDomain = new (&g_pSharedDomainMemory[0]) SharedDomain();
- SystemDomain::GetGlobalLoaderAllocator()->m_pDomain = m_pSharedDomain;
- // This cannot fail since g_pSharedDomainMemory is a static array.
- CONSISTENCY_CHECK(CheckPointer(m_pSharedDomain));
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
- LOG((LF_CLASSLOADER,
- LL_INFO10,
- "Created shared domain at %p\n",
- m_pSharedDomain));
- // We need to initialize the memory pools etc. for the system domain.
- m_pSharedDomain->Init(); // Setup the memory heaps
+ DomainAssembly* pResolvedAssembly = NULL;
+ _ASSERTE(strcmp(szName, g_AppDomainClassName));
- // allocate a Virtual Call Stub Manager for the shared domain
- m_pSharedDomain->InitVSD();
-}
+ GCX_COOP();
-#ifndef CROSSGEN_COMPILE
-void SharedDomain::Detach()
-{
- if (m_pSharedDomain)
+ struct _gc {
+ OBJECTREF AppDomainRef;
+ OBJECTREF AssemblyRef;
+ STRINGREF str;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+
+ GCPROTECT_BEGIN(gc);
+ if ((gc.AppDomainRef = GetRawExposedObject()) != NULL)
{
- m_pSharedDomain->Terminate();
- delete m_pSharedDomain;
- m_pSharedDomain = NULL;
- }
-}
-#endif // CROSSGEN_COMPILE
+ if (pAssembly != NULL)
+ gc.AssemblyRef = pAssembly->GetExposedAssemblyObject();
-#endif // !DACCESS_COMPILE
+ MethodDescCallSite onTypeResolve(METHOD__APP_DOMAIN__ON_TYPE_RESOLVE, &gc.AppDomainRef);
-SharedDomain *SharedDomain::GetDomain()
-{
- LIMITED_METHOD_DAC_CONTRACT;
+ gc.str = StringObject::NewString(szName);
+ ARG_SLOT args[3] =
+ {
+ ObjToArgSlot(gc.AppDomainRef),
+ ObjToArgSlot(gc.AssemblyRef),
+ ObjToArgSlot(gc.str)
+ };
+ ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onTypeResolve.Call_RetOBJECTREF(args);
- return m_pSharedDomain;
+ if (ResultingAssemblyRef != NULL)
+ {
+ pResolvedAssembly = ResultingAssemblyRef->GetDomainAssembly();
+
+ if (pResultingAssemblyRef)
+ *pResultingAssemblyRef = ResultingAssemblyRef;
+ else
+ {
+ if (pResolvedAssembly->IsCollectible())
+ {
+ COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible"));
+ }
+ }
+ }
+ }
+ GCPROTECT_END();
+
+ return pResolvedAssembly;
}
-#ifndef DACCESS_COMPILE
-#define INITIAL_ASSEMBLY_MAP_SIZE 17
-void SharedDomain::Init()
+Assembly* AppDomain::RaiseResourceResolveEvent(DomainAssembly* pAssembly, LPCSTR szName)
{
- CONTRACTL
+ CONTRACT(Assembly*)
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
INJECT_FAULT(COMPlusThrowOM(););
}
- CONTRACTL_END;
+ CONTRACT_END;
- BaseDomain::Init();
+ Assembly* pResolvedAssembly = NULL;
-#ifdef FEATURE_LOADER_OPTIMIZATION
- m_FileCreateLock.Init(CrstSharedAssemblyCreate, CRST_DEFAULT,TRUE);
+ GCX_COOP();
- LockOwner lock = { &m_DomainCrst, IsOwnerOfCrst };
- m_assemblyMap.Init(INITIAL_ASSEMBLY_MAP_SIZE, CompareSharedAssembly, TRUE, &lock);
-#endif // FEATURE_LOADER_OPTIMIZATION
+ struct _gc {
+ OBJECTREF AppDomainRef;
+ OBJECTREF AssemblyRef;
+ STRINGREF str;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
- ETW::LoaderLog::DomainLoad(this);
-}
-
-#ifndef CROSSGEN_COMPILE
-void SharedDomain::Terminate()
-{
- // make sure we delete the StringLiteralMap before unloading
- // the asemblies since the string literal map entries can
- // point to metadata string literals.
- GetLoaderAllocator()->CleanupStringLiteralMap();
-
-#ifdef FEATURE_LOADER_OPTIMIZATION
- PtrHashMap::PtrIterator i = m_assemblyMap.begin();
-
- while (!i.end())
- {
- Assembly *pAssembly = (Assembly*) i.GetValue();
- delete pAssembly;
- ++i;
- }
-
- ListLockEntry* pElement;
- pElement = m_FileCreateLock.Pop(TRUE);
- while (pElement)
- {
-#ifdef STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
- _ASSERTE (dbg_fDrasticShutdown || g_fInControlC);
-#endif
- delete(pElement);
- pElement = (FileLoadLock*) m_FileCreateLock.Pop(TRUE);
- }
- m_FileCreateLock.Destroy();
-#endif // FEATURE_LOADER_OPTIMIZATION
- BaseDomain::Terminate();
-}
-#endif // CROSSGEN_COMPILE
-
-
-
-#ifdef FEATURE_LOADER_OPTIMIZATION
-
-BOOL SharedDomain::CompareSharedAssembly(UPTR u1, UPTR u2)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- // This is the input to the lookup
- SharedAssemblyLocator *pLocator = (SharedAssemblyLocator *) (u1<<1);
-
- // This is the value stored in the table
- Assembly *pAssembly = (Assembly *) u2;
- if (pLocator->GetType()==SharedAssemblyLocator::DOMAINASSEMBLY)
- {
- if (!pAssembly->GetManifestFile()->Equals(pLocator->GetDomainAssembly()->GetFile()))
- return FALSE;
-
- return pAssembly->CanBeShared(pLocator->GetDomainAssembly());
- }
- else
- if (pLocator->GetType()==SharedAssemblyLocator::PEASSEMBLY)
- return pAssembly->GetManifestFile()->Equals(pLocator->GetPEAssembly());
- else
- if (pLocator->GetType()==SharedAssemblyLocator::PEASSEMBLYEXACT)
- return pAssembly->GetManifestFile() == pLocator->GetPEAssembly();
- _ASSERTE(!"Unexpected type of assembly locator");
- return FALSE;
-}
-
-DWORD SharedAssemblyLocator::Hash()
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- INJECT_FAULT(COMPlusThrowOM(););
- }
- CONTRACTL_END;
- if (m_type==DOMAINASSEMBLY)
- return GetDomainAssembly()->HashIdentity();
- if (m_type==PEASSEMBLY||m_type==PEASSEMBLYEXACT)
- return GetPEAssembly()->HashIdentity();
- _ASSERTE(!"Unexpected type of assembly locator");
- return 0;
-}
-
-Assembly * SharedDomain::FindShareableAssembly(SharedAssemblyLocator * pLocator)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- INJECT_FAULT(COMPlusThrowOM(););
- }
- CONTRACTL_END;
-
- Assembly * match= (Assembly *) m_assemblyMap.LookupValue(pLocator->Hash(), pLocator);
- if (match != (Assembly *) INVALIDENTRY)
- return match;
- else
- return NULL;
-}
-
-SIZE_T SharedDomain::GetShareableAssemblyCount()
-{
- LIMITED_METHOD_CONTRACT;
-
- return m_assemblyMap.GetCount();
-}
-
-void SharedDomain::AddShareableAssembly(Assembly * pAssembly)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- INJECT_FAULT(COMPlusThrowOM(););
- }
- CONTRACTL_END;
-
- // We have a lock on the file. There should be no races to add the same assembly.
-
- {
- LockHolder holder(this);
-
- EX_TRY
- {
- pAssembly->SetIsTenured();
- m_assemblyMap.InsertValue(pAssembly->HashIdentity(), pAssembly);
- }
- EX_HOOK
- {
- // There was an error adding the assembly to the assembly hash (probably an OOM),
- // so we need to unset the tenured bit so that correct cleanup can happen.
- pAssembly->UnsetIsTenured();
- }
- EX_END_HOOK
- }
-
- LOG((LF_CODESHARING,
- LL_INFO100,
- "Successfully added shareable assembly \"%s\".\n",
- pAssembly->GetManifestFile()->GetSimpleName()));
-}
-
-#endif // FEATURE_LOADER_OPTIMIZATION
-#endif // !DACCESS_COMPILE
-
-DWORD DomainLocalModule::GetClassFlags(MethodTable* pMT, DWORD iClassIndex /*=(DWORD)-1*/)
-{
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- SO_TOLERANT;
- } CONTRACTL_END;
-
- { // SO tolerance exception for debug-only assertion.
- CONTRACT_VIOLATION(SOToleranceViolation);
- CONSISTENCY_CHECK(GetDomainFile()->GetModule() == pMT->GetModuleForStatics());
- }
-
- if (pMT->IsDynamicStatics())
- {
- _ASSERTE(!pMT->ContainsGenericVariables());
- DWORD dynamicClassID = pMT->GetModuleDynamicEntryID();
- if(m_aDynamicEntries <= dynamicClassID)
- return FALSE;
- return (m_pDynamicClassTable[dynamicClassID].m_dwFlags);
- }
- else
- {
- if (iClassIndex == (DWORD)-1)
- iClassIndex = pMT->GetClassIndex();
- return GetPrecomputedStaticsClassData()[iClassIndex];
- }
-}
-
-#ifndef DACCESS_COMPILE
-
-void DomainLocalModule::SetClassInitialized(MethodTable* pMT)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain());
-
- _ASSERTE(!IsClassInitialized(pMT));
- _ASSERTE(!IsClassInitError(pMT));
-
- SetClassFlags(pMT, ClassInitFlags::INITIALIZED_FLAG);
-}
-
-void DomainLocalModule::SetClassInitError(MethodTable* pMT)
-{
- WRAPPER_NO_CONTRACT;
-
- BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain());
-
- SetClassFlags(pMT, ClassInitFlags::ERROR_FLAG);
-}
-
-void DomainLocalModule::SetClassFlags(MethodTable* pMT, DWORD dwFlags)
-{
- CONTRACTL {
- THROWS;
- GC_TRIGGERS;
- PRECONDITION(GetDomainFile()->GetModule() == pMT->GetModuleForStatics());
- // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
- PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock());
- } CONTRACTL_END;
-
- if (pMT->IsDynamicStatics())
- {
- _ASSERTE(!pMT->ContainsGenericVariables());
- DWORD dwID = pMT->GetModuleDynamicEntryID();
- EnsureDynamicClassIndex(dwID);
- m_pDynamicClassTable[dwID].m_dwFlags |= dwFlags;
- }
- else
- {
- GetPrecomputedStaticsClassData()[pMT->GetClassIndex()] |= dwFlags;
- }
-}
-
-void DomainLocalModule::EnsureDynamicClassIndex(DWORD dwID)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- INJECT_FAULT(COMPlusThrowOM(););
- // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
- PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock());
- }
- CONTRACTL_END;
-
- if (dwID < m_aDynamicEntries)
- {
- _ASSERTE(m_pDynamicClassTable.Load() != NULL);
- return;
- }
-
- SIZE_T aDynamicEntries = max(16, m_aDynamicEntries.Load());
- while (aDynamicEntries <= dwID)
- {
- aDynamicEntries *= 2;
- }
-
- DynamicClassInfo* pNewDynamicClassTable;
- pNewDynamicClassTable = (DynamicClassInfo*)
- (void*)GetDomainFile()->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(
- S_SIZE_T(sizeof(DynamicClassInfo)) * S_SIZE_T(aDynamicEntries));
-
- memcpy(pNewDynamicClassTable, m_pDynamicClassTable, sizeof(DynamicClassInfo) * m_aDynamicEntries);
-
- // Note: Memory allocated on loader heap is zero filled
- // memset(pNewDynamicClassTable + m_aDynamicEntries, 0, (aDynamicEntries - m_aDynamicEntries) * sizeof(DynamicClassInfo));
-
- _ASSERTE(m_aDynamicEntries%2 == 0);
-
- // Commit new dynamic table. The lock-free helpers depend on the order.
- MemoryBarrier();
- m_pDynamicClassTable = pNewDynamicClassTable;
- MemoryBarrier();
- m_aDynamicEntries = aDynamicEntries;
-}
-
-#ifndef CROSSGEN_COMPILE
-void DomainLocalModule::AllocateDynamicClass(MethodTable *pMT)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
- PRECONDITION(GetDomainFile()->GetAppDomain()->OwnDomainLocalBlockLock());
- }
- CONTRACTL_END;
-
- _ASSERTE(!pMT->ContainsGenericVariables());
- _ASSERTE(!pMT->IsSharedByGenericInstantiations());
- _ASSERTE(GetDomainFile()->GetModule() == pMT->GetModuleForStatics());
- _ASSERTE(pMT->IsDynamicStatics());
-
- DWORD dynamicEntryIDIndex = pMT->GetModuleDynamicEntryID();
-
- EnsureDynamicClassIndex(dynamicEntryIDIndex);
-
- _ASSERTE(m_aDynamicEntries > dynamicEntryIDIndex);
-
- EEClass *pClass = pMT->GetClass();
-
- DWORD dwStaticBytes = pClass->GetNonGCRegularStaticFieldBytes();
- DWORD dwNumHandleStatics = pClass->GetNumHandleRegularStatics();
-
- _ASSERTE(!IsClassAllocated(pMT));
- _ASSERTE(!IsClassInitialized(pMT));
- _ASSERTE(!IsClassInitError(pMT));
-
- DynamicEntry *pDynamicStatics = m_pDynamicClassTable[dynamicEntryIDIndex].m_pDynamicEntry;
-
- // We need this check because maybe a class had a cctor but no statics
- if (dwStaticBytes > 0 || dwNumHandleStatics > 0)
- {
- if (pDynamicStatics == NULL)
- {
- LoaderHeap * pLoaderAllocator = GetDomainFile()->GetLoaderAllocator()->GetHighFrequencyHeap();
-
- if (pMT->Collectible())
- {
- pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocMem(S_SIZE_T(sizeof(CollectibleDynamicEntry)));
- }
- else
- {
- SIZE_T dynamicEntrySize = DynamicEntry::GetOffsetOfDataBlob() + dwStaticBytes;
-
-#ifdef FEATURE_64BIT_ALIGNMENT
- // Allocate memory with extra alignment only if it is really necessary
- if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE)
- {
- static_assert_no_msg(sizeof(NormalDynamicEntry) % MAX_PRIMITIVE_FIELD_SIZE == 0);
- pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocAlignedMem(dynamicEntrySize, MAX_PRIMITIVE_FIELD_SIZE);
- }
- else
-#endif
- pDynamicStatics = (DynamicEntry*)(void*)pLoaderAllocator->AllocMem(S_SIZE_T(dynamicEntrySize));
- }
-
- // Note: Memory allocated on loader heap is zero filled
-
- m_pDynamicClassTable[dynamicEntryIDIndex].m_pDynamicEntry = pDynamicStatics;
- }
-
- if (pMT->Collectible() && (dwStaticBytes != 0))
- {
- GCX_COOP();
- OBJECTREF nongcStaticsArray = NULL;
- GCPROTECT_BEGIN(nongcStaticsArray);
-#ifdef FEATURE_64BIT_ALIGNMENT
- // Allocate memory with extra alignment only if it is really necessary
- if (dwStaticBytes >= MAX_PRIMITIVE_FIELD_SIZE)
- nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_I8, (dwStaticBytes + (sizeof(CLR_I8)-1)) / (sizeof(CLR_I8)));
- else
-#endif
- nongcStaticsArray = AllocatePrimitiveArray(ELEMENT_TYPE_U1, dwStaticBytes);
- ((CollectibleDynamicEntry *)pDynamicStatics)->m_hNonGCStatics = GetDomainFile()->GetModule()->GetLoaderAllocator()->AllocateHandle(nongcStaticsArray);
- GCPROTECT_END();
- }
- if (dwNumHandleStatics > 0)
- {
- if (!pMT->Collectible())
- {
- GetAppDomain()->AllocateStaticFieldObjRefPtrs(dwNumHandleStatics,
- &((NormalDynamicEntry *)pDynamicStatics)->m_pGCStatics);
- }
- else
- {
- GCX_COOP();
- OBJECTREF gcStaticsArray = NULL;
- GCPROTECT_BEGIN(gcStaticsArray);
- gcStaticsArray = AllocateObjectArray(dwNumHandleStatics, g_pObjectClass);
- ((CollectibleDynamicEntry *)pDynamicStatics)->m_hGCStatics = GetDomainFile()->GetModule()->GetLoaderAllocator()->AllocateHandle(gcStaticsArray);
- GCPROTECT_END();
- }
- }
- }
-}
-
-
-void DomainLocalModule::PopulateClass(MethodTable *pMT)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- }
- CONTRACTL_END;
-
- _ASSERTE(!pMT->ContainsGenericVariables());
-
- // <todo> the only work actually done here for non-dynamics is the freezing related work.
- // See if we can eliminate this and make this a dynamic-only path </todo>
- DWORD iClassIndex = pMT->GetClassIndex();
-
- if (!IsClassAllocated(pMT, iClassIndex))
- {
- BaseDomain::DomainLocalBlockLockHolder lh(GetDomainFile()->GetAppDomain());
-
- if (!IsClassAllocated(pMT, iClassIndex))
- {
- // Allocate dynamic space if necessary
- if (pMT->IsDynamicStatics())
- AllocateDynamicClass(pMT);
-
- // determine flags to set on the statics block
- DWORD dwFlags = ClassInitFlags::ALLOCATECLASS_FLAG;
-
- if (!pMT->HasClassConstructor() && !pMT->HasBoxedRegularStatics())
- {
- _ASSERTE(!IsClassInitialized(pMT));
- _ASSERTE(!IsClassInitError(pMT));
- dwFlags |= ClassInitFlags::INITIALIZED_FLAG;
- }
-
- if (pMT->Collectible())
- {
- dwFlags |= ClassInitFlags::COLLECTIBLE_FLAG;
- }
-
- // Set all flags at the same time to avoid races
- SetClassFlags(pMT, dwFlags);
- }
- }
-
- return;
-}
-#endif // CROSSGEN_COMPILE
-
-void DomainLocalBlock::EnsureModuleIndex(ModuleIndex index)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- INJECT_FAULT(COMPlusThrowOM(););
- // Assumes BaseDomain::DomainLocalBlockLockHolder is taken
- PRECONDITION(m_pDomain->OwnDomainLocalBlockLock());
- }
- CONTRACTL_END;
-
- if (m_aModuleIndices > index.m_dwIndex)
- {
- _ASSERTE(m_pModuleSlots != NULL);
- return;
- }
-
- SIZE_T aModuleIndices = max(16, m_aModuleIndices);
- while (aModuleIndices <= index.m_dwIndex)
- {
- aModuleIndices *= 2;
- }
-
- PTR_DomainLocalModule* pNewModuleSlots = (PTR_DomainLocalModule*) (void*)m_pDomain->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PTR_DomainLocalModule)) * S_SIZE_T(aModuleIndices));
-
- memcpy(pNewModuleSlots, m_pModuleSlots, sizeof(SIZE_T)*m_aModuleIndices);
-
- // Note: Memory allocated on loader heap is zero filled
- // memset(pNewModuleSlots + m_aModuleIndices, 0 , (aModuleIndices - m_aModuleIndices)*sizeof(PTR_DomainLocalModule) );
-
- // Commit new table. The lock-free helpers depend on the order.
- MemoryBarrier();
- m_pModuleSlots = pNewModuleSlots;
- MemoryBarrier();
- m_aModuleIndices = aModuleIndices;
-
-}
-
-void DomainLocalBlock::SetModuleSlot(ModuleIndex index, PTR_DomainLocalModule pLocalModule)
-{
- // Need to synchronize with table growth in this domain
- BaseDomain::DomainLocalBlockLockHolder lh(m_pDomain);
-
- EnsureModuleIndex(index);
-
- _ASSERTE(index.m_dwIndex < m_aModuleIndices);
-
- // We would like this assert here, unfortunately, loading a module in this appdomain can fail
- // after here and we will keep the module around and reuse the slot when we retry (if
- // the failure happened due to a transient error, such as OOM). In that case the slot wont
- // be null.
- //_ASSERTE(m_pModuleSlots[index.m_dwIndex] == 0);
-
- m_pModuleSlots[index.m_dwIndex] = pLocalModule;
-}
-
-#ifndef CROSSGEN_COMPILE
-
-DomainAssembly* AppDomain::RaiseTypeResolveEventThrowing(DomainAssembly* pAssembly, LPCSTR szName, ASSEMBLYREF *pResultingAssemblyRef)
-{
- CONTRACTL
- {
- MODE_ANY;
- GC_TRIGGERS;
- THROWS;
- INJECT_FAULT(COMPlusThrowOM(););
- }
- CONTRACTL_END;
-
- OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
-
-
- DomainAssembly* pResolvedAssembly = NULL;
- _ASSERTE(strcmp(szName, g_AppDomainClassName));
-
- GCX_COOP();
-
- struct _gc {
- OBJECTREF AppDomainRef;
- OBJECTREF AssemblyRef;
- STRINGREF str;
- } gc;
- ZeroMemory(&gc, sizeof(gc));
-
- GCPROTECT_BEGIN(gc);
- if ((gc.AppDomainRef = GetRawExposedObject()) != NULL)
- {
- if (pAssembly != NULL)
- gc.AssemblyRef = pAssembly->GetExposedAssemblyObject();
-
- MethodDescCallSite onTypeResolve(METHOD__APP_DOMAIN__ON_TYPE_RESOLVE, &gc.AppDomainRef);
-
- gc.str = StringObject::NewString(szName);
- ARG_SLOT args[3] =
- {
- ObjToArgSlot(gc.AppDomainRef),
- ObjToArgSlot(gc.AssemblyRef),
- ObjToArgSlot(gc.str)
- };
- ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onTypeResolve.Call_RetOBJECTREF(args);
-
- if (ResultingAssemblyRef != NULL)
- {
- pResolvedAssembly = ResultingAssemblyRef->GetDomainAssembly();
-
- if (pResultingAssemblyRef)
- *pResultingAssemblyRef = ResultingAssemblyRef;
- else
- {
- if (pResolvedAssembly->IsCollectible())
- {
- COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible"));
- }
- }
- }
- }
- GCPROTECT_END();
-
- return pResolvedAssembly;
-}
-
-
-Assembly* AppDomain::RaiseResourceResolveEvent(DomainAssembly* pAssembly, LPCSTR szName)
-{
- CONTRACT(Assembly*)
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
- INJECT_FAULT(COMPlusThrowOM(););
- }
- CONTRACT_END;
-
- Assembly* pResolvedAssembly = NULL;
-
- GCX_COOP();
-
- struct _gc {
- OBJECTREF AppDomainRef;
- OBJECTREF AssemblyRef;
- STRINGREF str;
- } gc;
- ZeroMemory(&gc, sizeof(gc));
-
- GCPROTECT_BEGIN(gc);
- if ((gc.AppDomainRef = GetRawExposedObject()) != NULL)
- {
- if (pAssembly != NULL)
- gc.AssemblyRef=pAssembly->GetExposedAssemblyObject();
-
- MethodDescCallSite onResourceResolve(METHOD__APP_DOMAIN__ON_RESOURCE_RESOLVE, &gc.AppDomainRef);
- gc.str = StringObject::NewString(szName);
- ARG_SLOT args[3] =
- {
- ObjToArgSlot(gc.AppDomainRef),
- ObjToArgSlot(gc.AssemblyRef),
- ObjToArgSlot(gc.str)
- };
- ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onResourceResolve.Call_RetOBJECTREF(args);
- if (ResultingAssemblyRef != NULL)
- {
- pResolvedAssembly = ResultingAssemblyRef->GetAssembly();
- if (pResolvedAssembly->IsCollectible())
- {
- COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleAssemblyResolve"));
- }
- }
- }
- GCPROTECT_END();
-
- RETURN pResolvedAssembly;
-}
-
-
-Assembly *
-AppDomain::RaiseAssemblyResolveEvent(
- AssemblySpec * pSpec,
- BOOL fPreBind)
-{
- CONTRACT(Assembly*)
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
- INJECT_FAULT(COMPlusThrowOM(););
- }
- CONTRACT_END;
-
- BinderMethodID methodId;
- StackSString ssName;
- pSpec->GetFileOrDisplayName(0, ssName);
-
- if (!fPreBind)
- {
- methodId = METHOD__APP_DOMAIN__ON_ASSEMBLY_RESOLVE; // post-bind execution event (the classic V1.0 event)
- }
- else
- {
- RETURN NULL;
- }
-
-
- // Elevate threads allowed loading level. This allows the host to load an assembly even in a restricted
- // condition. Note, however, that this exposes us to possible recursion failures, if the host tries to
- // load the assemblies currently being loaded. (Such cases would then throw an exception.)
-
- OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE);
- OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
-
- GCX_COOP();
-
- Assembly* pAssembly = NULL;
-
- struct _gc {
- OBJECTREF AppDomainRef;
- OBJECTREF AssemblyRef;
- STRINGREF str;
- } gc;
- ZeroMemory(&gc, sizeof(gc));
-
- GCPROTECT_BEGIN(gc);
- if ((gc.AppDomainRef = GetRawExposedObject()) != NULL)
- {
- if (pSpec->GetParentAssembly() != NULL)
- {
- {
- gc.AssemblyRef=pSpec->GetParentAssembly()->GetExposedAssemblyObject();
- }
- }
- MethodDescCallSite onAssemblyResolve(methodId, &gc.AppDomainRef);
-
- gc.str = StringObject::NewString(ssName);
- ARG_SLOT args[3] = {
- ObjToArgSlot(gc.AppDomainRef),
- ObjToArgSlot(gc.AssemblyRef),
- ObjToArgSlot(gc.str)
- };
-
- ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onAssemblyResolve.Call_RetOBJECTREF(args);
-
- if (ResultingAssemblyRef != NULL)
- {
- pAssembly = ResultingAssemblyRef->GetAssembly();
- if (pAssembly->IsCollectible())
- {
- COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleAssemblyResolve"));
- }
- }
- }
- GCPROTECT_END();
-
- if (pAssembly != NULL)
- {
- // Check that the public key token matches the one specified in the spec
- // MatchPublicKeys throws as appropriate
- pSpec->MatchPublicKeys(pAssembly);
- }
-
- RETURN pAssembly;
-} // AppDomain::RaiseAssemblyResolveEvent
-
-
-//---------------------------------------------------------------------------------------
-//
-// Determine the type of AppDomainManager to use for the default AppDomain
-//
-// Notes:
-// v2.0 of the CLR used environment variables APPDOMAIN_MANAGER_ASM and APPDOMAIN_MANAGER_TYPE to set the
-// domain manager. For compatibility these are still supported, along with appDomainManagerAsm and
-// appDomainManagerType config file switches. If the config switches are supplied, the entry point must be
-// fully trusted.
-//
-
-void AppDomain::InitializeDefaultDomainManager()
-{
- CONTRACTL
- {
- MODE_COOPERATIVE;
- GC_TRIGGERS;
- THROWS;
- INJECT_FAULT(COMPlusThrowOM(););
- PRECONDITION(GetId().m_dwId == DefaultADID);
- }
- CONTRACTL_END;
-
- OBJECTREF orThis = GetExposedObject();
- GCPROTECT_BEGIN(orThis);
-
- MethodDescCallSite initCompatFlags(METHOD__APP_DOMAIN__INITIALIZE_COMPATIBILITY_FLAGS);
- ARG_SLOT args[] =
- {
- ObjToArgSlot(orThis)
- };
-
- initCompatFlags.Call(args);
-
- GCPROTECT_END();
-}
-
-CLREvent * AppDomain::g_pUnloadStartEvent;
-
-void AppDomain::CreateADUnloadWorker()
-{
- STANDARD_VM_CONTRACT;
-
- // Do not create adUnload thread if there is only default domain
- if(IsSingleAppDomain())
- return;
-
-Retry:
- BOOL fCreator = FALSE;
- if (FastInterlockCompareExchange((LONG *)&g_fADUnloadWorkerOK,-2,-1)==-1) //we're first
- {
-#ifdef _TARGET_X86_ // use the smallest possible stack on X86
- DWORD stackSize = 128 * 1024;
-#else
- DWORD stackSize = 512 * 1024; // leave X64 unchanged since we have plenty of VM
-#endif
- Thread *pThread = SetupUnstartedThread();
- if (pThread->CreateNewThread(stackSize, ADUnloadThreadStart, pThread))
- {
- fCreator = TRUE;
- DWORD dwRet;
- dwRet = pThread->StartThread();
-
- // When running under a user mode native debugger there is a race
- // between the moment we've created the thread (in CreateNewThread) and
- // the moment we resume it (in StartThread); the debugger may receive
- // the "ct" (create thread) notification, and it will attempt to
- // suspend/resume all threads in the process. Now imagine the debugger
- // resumes this thread first, and only later does it try to resume the
- // newly created thread (the ADU worker thread). In these conditions our
- // call to ResumeThread may come before the debugger's call to ResumeThread
- // actually causing dwRet to equal 2.
- // We cannot use IsDebuggerPresent() in the condition below because the
- // debugger may have been detached between the time it got the notification
- // and the moment we execute the test below.
- _ASSERTE(dwRet == 1 || dwRet == 2);
- }
- else
- {
- pThread->DecExternalCount(FALSE);
- FastInterlockExchange((LONG *)&g_fADUnloadWorkerOK, -1);
- ThrowOutOfMemory();
- }
- }
-
- YIELD_WHILE (g_fADUnloadWorkerOK == -2);
-
- if (g_fADUnloadWorkerOK == -1) {
- if (fCreator)
- {
- ThrowOutOfMemory();
- }
- else
- {
- goto Retry;
- }
- }
-}
-
-/*static*/ void AppDomain::ADUnloadWorkerHelper(AppDomain *pDomain)
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_TRIGGERS;
- STATIC_CONTRACT_MODE_COOPERATIVE;
- ADUnloadSink* pADUnloadSink=pDomain->GetADUnloadSinkForUnload();
- HRESULT hr=S_OK;
-
- EX_TRY
- {
- pDomain->Unload(FALSE);
- }
- EX_CATCH_HRESULT(hr);
-
- if(pADUnloadSink)
- {
- SystemDomain::LockHolder lh;
- pADUnloadSink->ReportUnloadResult(hr,NULL);
- pADUnloadSink->Release();
- }
-}
-
-void AppDomain::DoADUnloadWork()
-{
- CONTRACTL
+ GCPROTECT_BEGIN(gc);
+ if ((gc.AppDomainRef = GetRawExposedObject()) != NULL)
{
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- INJECT_FAULT(COMPlusThrowOM(););
- }
- CONTRACTL_END;
-
- DWORD i = 1;
- while (TRUE) {
-
- AppDomain *pDomainToUnload = NULL;
+ if (pAssembly != NULL)
+ gc.AssemblyRef=pAssembly->GetExposedAssemblyObject();
+ MethodDescCallSite onResourceResolve(METHOD__APP_DOMAIN__ON_RESOURCE_RESOLVE, &gc.AppDomainRef);
+ gc.str = StringObject::NewString(szName);
+ ARG_SLOT args[3] =
{
- // Take the lock so that no domain can be added or removed from the system domain
- SystemDomain::LockHolder lh;
-
- DWORD numDomain = SystemDomain::GetCurrentAppDomainMaxIndex();
- for (; i <= numDomain; i ++) {
- AppDomain * pDomain = SystemDomain::TestGetAppDomainAtIndex(ADIndex(i));
- //
- // @todo: We used to also select a domain if pDomain->IsUnload() returned true. But that causes
- // problems when we've failed to completely unload the AD in the past. If we've reached the CLEARED
- // stage, for instance, then there will be no default context and AppDomain::Exit() will simply crash.
- //
- if (pDomain && pDomain->IsUnloadRequested())
- {
- pDomainToUnload = pDomain;
- i ++;
- break;
- }
+ ObjToArgSlot(gc.AppDomainRef),
+ ObjToArgSlot(gc.AssemblyRef),
+ ObjToArgSlot(gc.str)
+ };
+ ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onResourceResolve.Call_RetOBJECTREF(args);
+ if (ResultingAssemblyRef != NULL)
+ {
+ pResolvedAssembly = ResultingAssemblyRef->GetAssembly();
+ if (pResolvedAssembly->IsCollectible())
+ {
+ COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleAssemblyResolve"));
}
}
-
- if (!pDomainToUnload) {
- break;
- }
-
- // We are the only thread that can unload domains so no one else can delete the appdomain
- ADUnloadWorkerHelper(pDomainToUnload);
}
-}
-
-static void DoADUnloadWorkHelper()
-{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_GC_TRIGGERS;
- STATIC_CONTRACT_MODE_COOPERATIVE;
+ GCPROTECT_END();
- EX_TRY {
- AppDomain::DoADUnloadWork();
- }
- EX_CATCH
- {
- }
- EX_END_CATCH(SwallowAllExceptions);
+ RETURN pResolvedAssembly;
}
-ULONGLONG g_ObjFinalizeStartTime = 0;
-Volatile<BOOL> g_FinalizerIsRunning = FALSE;
-Volatile<ULONG> g_FinalizerLoopCount = 0;
-
-ULONGLONG GetObjFinalizeStartTime()
-{
- LIMITED_METHOD_CONTRACT;
- return g_ObjFinalizeStartTime;
-}
-void FinalizerThreadAbortOnTimeout()
+Assembly *
+AppDomain::RaiseAssemblyResolveEvent(
+ AssemblySpec * pSpec,
+ BOOL fPreBind)
{
- STATIC_CONTRACT_NOTHROW;
- STATIC_CONTRACT_MODE_COOPERATIVE;
- STATIC_CONTRACT_GC_TRIGGERS;
-
+ CONTRACT(Assembly*)
{
- // If finalizer thread is blocked because scheduler is running another task,
- // or it is waiting for another thread, we first see if we get finalizer thread
- // running again.
- Thread::ThreadAbortWatchDog();
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ INJECT_FAULT(COMPlusThrowOM(););
}
+ CONTRACT_END;
- EX_TRY
+ BinderMethodID methodId;
+ StackSString ssName;
+ pSpec->GetFileOrDisplayName(0, ssName);
+
+ if (!fPreBind)
{
- Thread *pFinalizerThread = FinalizerThread::GetFinalizerThread();
- EPolicyAction action = GetEEPolicy()->GetActionOnTimeout(OPR_FinalizerRun, pFinalizerThread);
- switch (action)
- {
- case eAbortThread:
- GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
- pFinalizerThread->UserAbort(Thread::TAR_Thread,
- EEPolicy::TA_Safe,
- INFINITE,
- Thread::UAC_FinalizerTimeout);
- break;
- case eRudeAbortThread:
- GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
- pFinalizerThread->UserAbort(Thread::TAR_Thread,
- EEPolicy::TA_Rude,
- INFINITE,
- Thread::UAC_FinalizerTimeout);
- break;
- case eUnloadAppDomain:
- {
- AppDomain *pDomain = pFinalizerThread->GetDomain();
- pFinalizerThread->UserAbort(Thread::TAR_Thread,
- EEPolicy::TA_Safe,
- INFINITE,
- Thread::UAC_FinalizerTimeout);
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Safe);
- }
- }
- break;
- case eRudeUnloadAppDomain:
- {
- AppDomain *pDomain = pFinalizerThread->GetDomain();
- pFinalizerThread->UserAbort(Thread::TAR_Thread,
- EEPolicy::TA_Rude,
- INFINITE,
- Thread::UAC_FinalizerTimeout);
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Rude);
- }
- }
- break;
- case eExitProcess:
- case eFastExitProcess:
- case eRudeExitProcess:
- case eDisableRuntime:
- GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
- EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_TIMEOUT);
- _ASSERTE (!"Should not get here");
- break;
- default:
- break;
- }
+ methodId = METHOD__APP_DOMAIN__ON_ASSEMBLY_RESOLVE; // post-bind execution event (the classic V1.0 event)
}
- EX_CATCH
+ else
{
+ RETURN NULL;
}
- EX_END_CATCH(SwallowAllExceptions);
-}
-
-enum WorkType
-{
- WT_UnloadDomain = 0x1,
- WT_ThreadAbort = 0x2,
- WT_FinalizerThread = 0x4,
- WT_ClearCollectedDomains=0x8
-};
-
-static Volatile<DWORD> s_WorkType = 0;
-
+
-DWORD WINAPI AppDomain::ADUnloadThreadStart(void *args)
-{
- CONTRACTL
- {
- NOTHROW;
- DISABLED(GC_TRIGGERS);
+ // Elevate threads allowed loading level. This allows the host to load an assembly even in a restricted
+ // condition. Note, however, that this exposes us to possible recursion failures, if the host tries to
+ // load the assemblies currently being loaded. (Such cases would then throw an exception.)
- // This function will always be at the very bottom of the stack. The only
- // user code it calls is the AppDomainUnload notifications which we will
- // not be hardenning for Whidbey.
- //
- ENTRY_POINT;
- }
- CONTRACTL_END;
+ OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE);
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
- BEGIN_ENTRYPOINT_NOTHROW;
+ GCX_COOP();
- ClrFlsSetThreadType (ThreadType_ADUnloadHelper);
+ Assembly* pAssembly = NULL;
- Thread *pThread = (Thread*)args;
- bool fOK = (pThread->HasStarted() != 0);
+ struct _gc {
+ OBJECTREF AppDomainRef;
+ OBJECTREF AssemblyRef;
+ STRINGREF str;
+ } gc;
+ ZeroMemory(&gc, sizeof(gc));
+ GCPROTECT_BEGIN(gc);
+ if ((gc.AppDomainRef = GetRawExposedObject()) != NULL)
{
- GCX_MAYBE_PREEMP(fOK);
-
- _ASSERTE (g_fADUnloadWorkerOK == -2);
-
- FastInterlockExchange((LONG *)&g_fADUnloadWorkerOK,fOK?1:-1);
-
- if (!fOK)
+ if (pSpec->GetParentAssembly() != NULL)
{
- DestroyThread(pThread);
- goto Exit;
- }
-
- pThread->SetBackground(TRUE);
-
- pThread->SetThreadStateNC(Thread::TSNC_ADUnloadHelper);
-
- while (TRUE) {
- DWORD TAtimeout = INFINITE;
- ULONGLONG endTime = Thread::GetNextSelfAbortEndTime();
- ULONGLONG curTime = CLRGetTickCount64();
- if (endTime <= curTime) {
- TAtimeout = 5;
- }
- else
- {
- ULONGLONG diff = endTime - curTime;
- if (diff < MAXULONG)
- {
- TAtimeout = (DWORD)diff;
- }
- }
- ULONGLONG finalizeStartTime = GetObjFinalizeStartTime();
- DWORD finalizeTimeout = INFINITE;
- DWORD finalizeTimeoutSetting = GetEEPolicy()->GetTimeout(OPR_FinalizerRun);
- if (finalizeTimeoutSetting != INFINITE && g_FinalizerIsRunning)
- {
- if (finalizeStartTime == 0)
- {
- finalizeTimeout = finalizeTimeoutSetting;
- }
- else
- {
- endTime = finalizeStartTime + finalizeTimeoutSetting;
- if (endTime <= curTime) {
- finalizeTimeout = 0;
- }
- else
- {
- ULONGLONG diff = endTime - curTime;
- if (diff < MAXULONG)
- {
- finalizeTimeout = (DWORD)diff;
- }
- }
- }
- }
-
- if (AppDomain::HasWorkForFinalizerThread())
- {
- if (finalizeTimeout > finalizeTimeoutSetting)
- {
- finalizeTimeout = finalizeTimeoutSetting;
- }
- }
-
- DWORD timeout = INFINITE;
- if (finalizeTimeout <= TAtimeout)
- {
- timeout = finalizeTimeout;
- }
- else
- {
- timeout = TAtimeout;
- }
-
- if (timeout != 0)
- {
- LOG((LF_APPDOMAIN, LL_INFO10, "Waiting to start unload\n"));
- g_pUnloadStartEvent->Wait(timeout,FALSE);
- }
-
- if (finalizeTimeout != INFINITE || (s_WorkType & WT_FinalizerThread) != 0)
- {
- STRESS_LOG0(LF_ALWAYS, LL_ALWAYS, "ADUnloadThreadStart work for Finalizer thread\n");
- FastInterlockAnd(&s_WorkType, ~WT_FinalizerThread);
- // only watch finalizer thread is finalizer method or unloadevent is being processed
- if (GetObjFinalizeStartTime() == finalizeStartTime && finalizeStartTime != 0 && g_FinalizerIsRunning)
- {
- if (CLRGetTickCount64() >= finalizeStartTime+finalizeTimeoutSetting)
- {
- GCX_COOP();
- FinalizerThreadAbortOnTimeout();
- }
- }
- if (s_fProcessUnloadDomainEvent && g_FinalizerIsRunning)
- {
- GCX_COOP();
- FinalizerThreadAbortOnTimeout();
- }
- }
-
- if (TAtimeout != INFINITE || (s_WorkType & WT_ThreadAbort) != 0)
- {
- STRESS_LOG0(LF_ALWAYS, LL_ALWAYS, "ADUnloadThreadStart work for thread abort\n");
- FastInterlockAnd(&s_WorkType, ~WT_ThreadAbort);
- GCX_COOP();
- Thread::ThreadAbortWatchDog();
- }
-
- if ((s_WorkType & WT_UnloadDomain) != 0 && !AppDomain::HasWorkForFinalizerThread())
{
- STRESS_LOG0(LF_ALWAYS, LL_ALWAYS, "ADUnloadThreadStart work for AD unload\n");
- FastInterlockAnd(&s_WorkType, ~WT_UnloadDomain);
- GCX_COOP();
- DoADUnloadWorkHelper();
+ gc.AssemblyRef=pSpec->GetParentAssembly()->GetExposedAssemblyObject();
}
+ }
+ MethodDescCallSite onAssemblyResolve(methodId, &gc.AppDomainRef);
- if ((s_WorkType & WT_ClearCollectedDomains) != 0)
+ gc.str = StringObject::NewString(ssName);
+ ARG_SLOT args[3] = {
+ ObjToArgSlot(gc.AppDomainRef),
+ ObjToArgSlot(gc.AssemblyRef),
+ ObjToArgSlot(gc.str)
+ };
+
+ ASSEMBLYREF ResultingAssemblyRef = (ASSEMBLYREF) onAssemblyResolve.Call_RetOBJECTREF(args);
+
+ if (ResultingAssemblyRef != NULL)
+ {
+ pAssembly = ResultingAssemblyRef->GetAssembly();
+ if (pAssembly->IsCollectible())
{
- STRESS_LOG0(LF_ALWAYS, LL_ALWAYS, "ADUnloadThreadStart work for AD cleanup\n");
- FastInterlockAnd(&s_WorkType, ~WT_ClearCollectedDomains);
- GCX_COOP();
- SystemDomain::System()->ClearCollectedDomains();
+ COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleAssemblyResolve"));
}
-
}
-Exit:;
- }
-
- END_ENTRYPOINT_NOTHROW;
-
- return 0;
-}
+ }
+ GCPROTECT_END();
-void AppDomain::EnableADUnloadWorker()
-{
- CONTRACTL
+ if (pAssembly != NULL)
{
- NOTHROW;
- GC_NOTRIGGER;
- SO_TOLERANT; // Called during a SO
+ // Check that the public key token matches the one specified in the spec
+ // MatchPublicKeys throws as appropriate
+ pSpec->MatchPublicKeys(pAssembly);
}
- CONTRACTL_END;
- EEPolicy::AppDomainUnloadTypes type = EEPolicy::ADU_Safe;
+ RETURN pAssembly;
+} // AppDomain::RaiseAssemblyResolveEvent
-#ifdef _DEBUG
- DWORD hostTestADUnload = g_pConfig->GetHostTestADUnload();
- if (hostTestADUnload == 2) {
- type = EEPolicy::ADU_Rude;
- }
-#endif // _DEBUG
- EnableADUnloadWorker(type);
-}
+//---------------------------------------------------------------------------------------
+//
+// Determine the type of AppDomainManager to use for the default AppDomain
+//
+// Notes:
+// v2.0 of the CLR used environment variables APPDOMAIN_MANAGER_ASM and APPDOMAIN_MANAGER_TYPE to set the
+// domain manager. For compatibility these are still supported, along with appDomainManagerAsm and
+// appDomainManagerType config file switches. If the config switches are supplied, the entry point must be
+// fully trusted.
+//
-void AppDomain::EnableADUnloadWorker(EEPolicy::AppDomainUnloadTypes type, BOOL fHasStack)
+void AppDomain::InitializeDefaultDomainManager()
{
CONTRACTL
{
- NOTHROW;
- GC_NOTRIGGER;
- SO_TOLERANT; // Called during a SO
+ MODE_COOPERATIVE;
+ GC_TRIGGERS;
+ THROWS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ PRECONDITION(GetId().m_dwId == DefaultADID);
}
CONTRACTL_END;
- FastInterlockOr (&s_WorkType, WT_UnloadDomain);
-
- LONG stage = m_Stage;
- static_assert_no_msg(sizeof(m_Stage) == sizeof(int));
-
- _ASSERTE(!IsDefaultDomain());
-
- // Mark unload requested.
- if (type == EEPolicy::ADU_Rude) {
- SetRudeUnload();
- }
- while (stage < STAGE_UNLOAD_REQUESTED) {
- stage = FastInterlockCompareExchange((LONG*)&m_Stage,STAGE_UNLOAD_REQUESTED,stage);
- }
+ OBJECTREF orThis = GetExposedObject();
+ GCPROTECT_BEGIN(orThis);
- if (!fHasStack)
+ MethodDescCallSite initCompatFlags(METHOD__APP_DOMAIN__INITIALIZE_COMPATIBILITY_FLAGS);
+ ARG_SLOT args[] =
{
- // Can not call Set due to limited stack.
- return;
- }
- LOG((LF_APPDOMAIN, LL_INFO10, "Enabling unload worker\n"));
- g_pUnloadStartEvent->Set();
-}
+ ObjToArgSlot(orThis)
+ };
-void AppDomain::EnableADUnloadWorkerForThreadAbort()
-{
- LIMITED_METHOD_CONTRACT;
- STRESS_LOG0(LF_ALWAYS, LL_ALWAYS, "Enabling unload worker for thread abort\n");
- LOG((LF_APPDOMAIN, LL_INFO10, "Enabling unload worker for thread abort\n"));
- FastInterlockOr (&s_WorkType, WT_ThreadAbort);
- g_pUnloadStartEvent->Set();
+ initCompatFlags.Call(args);
+
+ GCPROTECT_END();
}
+ULONGLONG g_ObjFinalizeStartTime = 0;
+Volatile<BOOL> g_FinalizerIsRunning = FALSE;
+Volatile<ULONG> g_FinalizerLoopCount = 0;
-void AppDomain::EnableADUnloadWorkerForFinalizer()
+ULONGLONG GetObjFinalizeStartTime()
{
LIMITED_METHOD_CONTRACT;
- if (GetEEPolicy()->GetTimeout(OPR_FinalizerRun) != INFINITE)
- {
- LOG((LF_APPDOMAIN, LL_INFO10, "Enabling unload worker for Finalizer Thread\n"));
- FastInterlockOr (&s_WorkType, WT_FinalizerThread);
- g_pUnloadStartEvent->Set();
- }
+ return g_ObjFinalizeStartTime;
}
-void AppDomain::EnableADUnloadWorkerForCollectedADCleanup()
+void FinalizerThreadAbortOnTimeout()
{
- LIMITED_METHOD_CONTRACT;
- LOG((LF_APPDOMAIN, LL_INFO10, "Enabling unload worker for collected domains\n"));
- FastInterlockOr (&s_WorkType, WT_ClearCollectedDomains);
- g_pUnloadStartEvent->Set();
-}
-
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_MODE_COOPERATIVE;
+ STATIC_CONTRACT_GC_TRIGGERS;
-void SystemDomain::ClearCollectedDomains()
-{
- CONTRACTL
{
- GC_TRIGGERS;
- NOTHROW;
- MODE_COOPERATIVE;
+ // If finalizer thread is blocked because scheduler is running another task,
+ // or it is waiting for another thread, we first see if we get finalizer thread
+ // running again.
+ Thread::ThreadAbortWatchDog();
}
- CONTRACTL_END;
-
- AppDomain* pDomainsToClear=NULL;
+
+ EX_TRY
{
- CrstHolder lh(&m_DelayedUnloadCrst);
- for (AppDomain** ppDomain=&m_pDelayedUnloadList;(*ppDomain)!=NULL; )
+ Thread *pFinalizerThread = FinalizerThread::GetFinalizerThread();
+ EPolicyAction action = GetEEPolicy()->GetActionOnTimeout(OPR_FinalizerRun, pFinalizerThread);
+ switch (action)
{
- if ((*ppDomain)->m_Stage==AppDomain::STAGE_COLLECTED)
+ case eAbortThread:
+ GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
+ pFinalizerThread->UserAbort(Thread::TAR_Thread,
+ EEPolicy::TA_Safe,
+ INFINITE,
+ Thread::UAC_FinalizerTimeout);
+ break;
+ case eRudeAbortThread:
+ GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
+ pFinalizerThread->UserAbort(Thread::TAR_Thread,
+ EEPolicy::TA_Rude,
+ INFINITE,
+ Thread::UAC_FinalizerTimeout);
+ break;
+ case eUnloadAppDomain:
{
- AppDomain* pAppDomain=*ppDomain;
- *ppDomain=(*ppDomain)->m_pNextInDelayedUnloadList;
- pAppDomain->m_pNextInDelayedUnloadList=pDomainsToClear;
- pDomainsToClear=pAppDomain;
+ AppDomain *pDomain = pFinalizerThread->GetDomain();
+ pFinalizerThread->UserAbort(Thread::TAR_Thread,
+ EEPolicy::TA_Safe,
+ INFINITE,
+ Thread::UAC_FinalizerTimeout);
}
- else
- ppDomain=&((*ppDomain)->m_pNextInDelayedUnloadList);
+ break;
+ case eRudeUnloadAppDomain:
+ {
+ AppDomain *pDomain = pFinalizerThread->GetDomain();
+ pFinalizerThread->UserAbort(Thread::TAR_Thread,
+ EEPolicy::TA_Rude,
+ INFINITE,
+ Thread::UAC_FinalizerTimeout);
+ }
+ break;
+ case eExitProcess:
+ case eFastExitProcess:
+ case eRudeExitProcess:
+ case eDisableRuntime:
+ GetEEPolicy()->NotifyHostOnTimeout(OPR_FinalizerRun, action);
+ EEPolicy::HandleExitProcessFromEscalation(action, HOST_E_EXITPROCESS_TIMEOUT);
+ _ASSERTE (!"Should not get here");
+ break;
+ default:
+ break;
}
}
-
- for (AppDomain* pDomain=pDomainsToClear;pDomain!=NULL;)
+ EX_CATCH
{
- AppDomain* pNext=pDomain->m_pNextInDelayedUnloadList;
- pDomain->Close(); //NOTHROW!
- pDomain->Release();
- pDomain=pNext;
}
+ EX_END_CATCH(SwallowAllExceptions);
}
-
-void SystemDomain::ProcessClearingDomains()
+
+enum WorkType
{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
- CrstHolder lh(&m_DelayedUnloadCrst);
+ WT_UnloadDomain = 0x1,
+ WT_ThreadAbort = 0x2,
+ WT_FinalizerThread = 0x4
+};
- for (AppDomain** ppDomain=&m_pDelayedUnloadList;(*ppDomain)!=NULL; )
- {
- if ((*ppDomain)->m_Stage==AppDomain::STAGE_HANDLETABLE_NOACCESS)
- {
- AppDomain* pAppDomain=*ppDomain;
- pAppDomain->SetStage(AppDomain::STAGE_CLEARED);
- }
- ppDomain=&((*ppDomain)->m_pNextInDelayedUnloadList);
- }
-
- if (!m_UnloadIsAsync)
- {
- // For synchronous mode, we are now done with the list.
- m_pDelayedUnloadList = NULL;
- }
-}
+static Volatile<DWORD> s_WorkType = 0;
-void SystemDomain::ProcessDelayedUnloadDomains()
+void SystemDomain::ProcessDelayedUnloadLoaderAllocators()
{
CONTRACTL
{
if (GCHeapUtilities::GetGCHeap()->IsConcurrentGCInProgress())
iGCRefPoint--;
- BOOL bAppDomainToCleanup = FALSE;
LoaderAllocator * pAllocatorsToDelete = NULL;
{
CrstHolder lh(&m_DelayedUnloadCrst);
- for (AppDomain* pDomain=m_pDelayedUnloadList; pDomain!=NULL; pDomain=pDomain->m_pNextInDelayedUnloadList)
- {
- if (pDomain->m_Stage==AppDomain::STAGE_CLEARED)
- {
- // Compare with 0 to handle overflows gracefully
- if (0 < iGCRefPoint - pDomain->GetGCRefPoint())
- {
- bAppDomainToCleanup=TRUE;
- pDomain->SetStage(AppDomain::STAGE_COLLECTED);
- }
- }
- }
-
LoaderAllocator ** ppAllocator=&m_pDelayedUnloadListOfLoaderAllocators;
while (*ppAllocator!= NULL)
{
}
}
- if (bAppDomainToCleanup)
- AppDomain::EnableADUnloadWorkerForCollectedADCleanup();
-
// Delete collected loader allocators on the finalizer thread. We cannot offload it to appdomain unload thread because of
// there is not guaranteed to be one, and it is not that expensive operation anyway.
while (pAllocatorsToDelete != NULL)
#endif // CROSSGEN_COMPILE
-AppDomainFromIDHolder::AppDomainFromIDHolder(ADID adId, BOOL bUnsafePoint, SyncType synctype)
-{
- WRAPPER_NO_CONTRACT;
- ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
-#ifdef _DEBUG
- m_bAcquired=false;
- m_bChecked=false;
- m_type=synctype;
-
-#endif
- Assign(adId, bUnsafePoint);
-}
-
-AppDomainFromIDHolder::AppDomainFromIDHolder(SyncType synctype)
-{
- LIMITED_METHOD_CONTRACT;
- ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
- m_pDomain=NULL;
-#ifdef _DEBUG
- m_bAcquired=false;
- m_bChecked=false;
- m_type=synctype;
-#endif
-}
-
-#ifndef CROSSGEN_COMPILE
-void ADUnloadSink::ReportUnloadResult (HRESULT hr, OBJECTREF* pException)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- PRECONDITION(CheckPointer(this));
- PRECONDITION(m_UnloadCompleteEvent.IsValid());
- }
- CONTRACTL_END;
-
- //pException is unused;
- m_UnloadResult=hr;
- m_UnloadCompleteEvent.Set();
-};
-
-void ADUnloadSink::WaitUnloadCompletion()
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- PRECONDITION(CheckPointer(this));
- PRECONDITION(m_UnloadCompleteEvent.IsValid());
- }
- CONTRACTL_END;
-
- CONTRACT_VIOLATION(FaultViolation);
- m_UnloadCompleteEvent.WaitEx(INFINITE, (WaitMode)(WaitMode_Alertable | WaitMode_ADUnload));
-};
-
-ADUnloadSink* AppDomain::PrepareForWaitUnloadCompletion()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- PRECONDITION(SystemDomain::IsUnderDomainLock());
- FORBID_FAULT;
- }
- CONTRACTL_END;
-
- ADUnloadSink* pADSink=GetADUnloadSink();
- PREFIX_ASSUME(pADSink!=NULL);
- if (m_Stage < AppDomain::STAGE_UNLOAD_REQUESTED) //we're first
- {
- pADSink->Reset();
- SetUnloadRequestThread(GetThread());
- }
- return pADSink;
-};
-
-ADUnloadSink::ADUnloadSink()
-{
- CONTRACTL
- {
- CONSTRUCTOR_CHECK;
- THROWS;
- GC_NOTRIGGER;
- MODE_ANY;
- INJECT_FAULT(COMPlusThrowOM(););
- }
- CONTRACTL_END;
-
- m_cRef=1;
- m_UnloadCompleteEvent.CreateManualEvent(FALSE);
- m_UnloadResult=S_OK;
-};
-
-ADUnloadSink::~ADUnloadSink()
-{
- CONTRACTL
- {
- DESTRUCTOR_CHECK;
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
- m_UnloadCompleteEvent.CloseEvent();
-
-};
-
-
-ULONG ADUnloadSink::AddRef()
-{
- LIMITED_METHOD_CONTRACT;
- return InterlockedIncrement(&m_cRef);
-};
-
-ULONG ADUnloadSink::Release()
-{
- LIMITED_METHOD_CONTRACT;
- ULONG ulRef = InterlockedDecrement(&m_cRef);
- if (ulRef == 0)
- {
- delete this;
- }
- return ulRef;
-};
-
-void ADUnloadSink::Reset()
-{
- LIMITED_METHOD_CONTRACT;
- m_UnloadResult=S_OK;
- m_UnloadCompleteEvent.Reset();
-}
-
-ADUnloadSink* AppDomain::GetADUnloadSink()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(SystemDomain::IsUnderDomainLock());
- if(m_ADUnloadSink)
- m_ADUnloadSink->AddRef();
- return m_ADUnloadSink;
-};
-
-ADUnloadSink* AppDomain::GetADUnloadSinkForUnload()
-{
- // unload thread only. Doesn't need to have AD lock
- LIMITED_METHOD_CONTRACT;
- if(m_ADUnloadSink)
- m_ADUnloadSink->AddRef();
- return m_ADUnloadSink;
-}
-#endif // CROSSGEN_COMPILE
-
void AppDomain::EnumStaticGCRefs(promote_func* fn, ScanContext* sc)
{
CONTRACT_VOID
STRINGREF *IsStringInterned(STRINGREF *pString);
STRINGREF *GetOrInternString(STRINGREF *pString);
- virtual BOOL CanUnload() { LIMITED_METHOD_CONTRACT; return FALSE; } // can never unload BaseDomain
-
// Returns an array of OBJECTREF* that can be used to store domain specific data.
// Statics and reflection info (Types, MemberInfo,..) are stored this way
// If ppLazyAllocate != 0, allocation will only take place if *ppLazyAllocate != 0 (and the allocation
ATTACH_ALL = 0x7
};
-class ADUnloadSink
-{
-
-protected:
- ~ADUnloadSink();
- CLREvent m_UnloadCompleteEvent;
- HRESULT m_UnloadResult;
- Volatile<LONG> m_cRef;
-public:
- ADUnloadSink();
- void ReportUnloadResult (HRESULT hr, OBJECTREF* pException);
- void WaitUnloadCompletion();
- HRESULT GetUnloadResult() {LIMITED_METHOD_CONTRACT; return m_UnloadResult;};
- void Reset();
- ULONG AddRef();
- ULONG Release();
-};
-
-
-FORCEINLINE void ADUnloadSink__Release(ADUnloadSink* pADSink)
-{
- WRAPPER_NO_CONTRACT;
-
- if (pADSink)
- pADSink->Release();
-}
-
-typedef Wrapper <ADUnloadSink*,DoNothing,ADUnloadSink__Release,NULL> ADUnloadSinkHolder;
-
// This filters the output of IterateAssemblies. This ought to be declared more locally
// but it would result in really verbose callsites.
//
//
class AppDomain : public BaseDomain
{
- friend class ADUnloadSink;
friend class SystemDomain;
friend class AssemblySink;
friend class AppDomainNative;
friend class RCWCache;
friend class ClrDataAccess;
friend class CheckAsmOffsets;
- friend class AppDomainFromIDHolder;
VPTR_VTABLE_CLASS(AppDomain, BaseDomain)
AppDomain();
virtual ~AppDomain();
#endif
- static void DoADUnloadWork();
DomainAssembly* FindDomainAssembly(Assembly*);
void EnterContext(Thread* pThread, Context* pCtx,ContextTransitionFrame *pFrame);
// Initializes an AppDomain. (this functions is not called from the SystemDomain)
void Init();
- // creates only unamaged part
- static void CreateUnmanagedObject(AppDomainCreationHolder<AppDomain>& result);
-
-
#if defined(FEATURE_COMINTEROP)
HRESULT SetWinrtApplicationContext(SString &appLocalWinMD);
#endif // FEATURE_COMINTEROP
void SetupSharedStatics();
- ADUnloadSink* PrepareForWaitUnloadCompletion();
-
//****************************************************************************************
//
// Create a quick lookup for classes loaded into this domain based on their GUID.
return dac_cast<PTR_CompilationDomain>(this);
}
- void SetCanUnload()
- {
- LIMITED_METHOD_CONTRACT;
-
- m_dwFlags |= APP_DOMAIN_CAN_BE_UNLOADED;
- }
-
- BOOL CanUnload()
- {
- LIMITED_METHOD_CONTRACT;
- STATIC_CONTRACT_SO_TOLERANT;
- return m_dwFlags & APP_DOMAIN_CAN_BE_UNLOADED;
- }
-
void SetRemotingConfigured()
{
LIMITED_METHOD_CONTRACT;
return m_dwFlags & ORPHANED_LOCKS;
}
- // This function is used to relax asserts in the lock accounting.
- // It returns true if we are fine with hosed lock accounting in this domain.
- BOOL OkToIgnoreOrphanedLocks()
- {
- WRAPPER_NO_CONTRACT;
- return HasOrphanedLocks() && m_Stage >= STAGE_UNLOAD_REQUESTED;
- }
-
static void ExceptionUnwind(Frame *pFrame);
#ifdef _DEBUG
BOOL CanLoadCode()
{
LIMITED_METHOD_CONTRACT;
- return m_Stage >= STAGE_READYFORMANAGEDCODE && m_Stage < STAGE_CLOSED;
+ return m_Stage >= STAGE_READYFORMANAGEDCODE;
}
void SetAnonymouslyHostedDynamicMethodsAssembly(DomainAssembly * pDomainAssembly)
return m_anonymouslyHostedDynamicMethodsAssembly;
}
- BOOL HasUnloadStarted()
- {
- LIMITED_METHOD_CONTRACT;
- return m_Stage>=STAGE_EXITED;
- }
static void RefTakerAcquire(AppDomain* pDomain)
{
WRAPPER_NO_CONTRACT;
{
LIMITED_METHOD_DAC_CONTRACT;
- return m_Stage >= STAGE_ACTIVE && m_Stage < STAGE_CLOSED;
+ return m_Stage >= STAGE_ACTIVE;
}
// Range for normal execution of code in the appdomain, currently used for
// appdomain resource monitoring since we don't care to update resource usage
// There is no risk of races under DAC, so we will pretend to be unconditionally valid.
return TRUE;
#else
- return m_Stage > STAGE_CREATING && m_Stage < STAGE_CLOSED;
+ return m_Stage > STAGE_CREATING;
#endif
}
#endif
BOOL IsRunningIn(Thread* pThread);
- BOOL IsUnloading()
- {
- LIMITED_METHOD_CONTRACT;
- SUPPORTS_DAC;
-
- return m_Stage > STAGE_UNLOAD_REQUESTED;
- }
-
BOOL NotReadyForManagedCode()
{
LIMITED_METHOD_CONTRACT;
return m_Stage < STAGE_READYFORMANAGEDCODE;
}
- void SetFinalized()
- {
- LIMITED_METHOD_CONTRACT;
- SetStage(STAGE_FINALIZED);
- }
-
- BOOL IsFinalizing()
- {
- LIMITED_METHOD_CONTRACT;
-
- return m_Stage >= STAGE_FINALIZING;
- }
-
- BOOL IsFinalized()
- {
- LIMITED_METHOD_CONTRACT;
-
- return m_Stage >= STAGE_FINALIZED;
- }
-
- BOOL NoAccessToHandleTable()
- {
- LIMITED_METHOD_CONTRACT;
- SUPPORTS_DAC;
-
- return m_Stage >= STAGE_HANDLETABLE_NOACCESS;
- }
-
- // Checks whether the given thread can enter the app domain
- BOOL CanThreadEnter(Thread *pThread);
-
- // Following two are needed for the Holder
- static void SetUnloadInProgress(AppDomain *pThis) PUB;
- static void SetUnloadComplete(AppDomain *pThis) PUB;
- // Predicates for GC asserts
- BOOL ShouldHaveFinalization()
- {
- LIMITED_METHOD_CONTRACT;
-
- return ((DWORD) m_Stage) < STAGE_COLLECTED;
- }
- BOOL ShouldHaveCode()
- {
- LIMITED_METHOD_CONTRACT;
-
- return ((DWORD) m_Stage) < STAGE_COLLECTED;
- }
- BOOL ShouldHaveRoots()
- {
- LIMITED_METHOD_CONTRACT;
-
- return ((DWORD) m_Stage) < STAGE_CLEARED;
- }
- BOOL ShouldHaveInstances()
- {
- LIMITED_METHOD_CONTRACT;
-
- return ((DWORD) m_Stage) < STAGE_COLLECTED;
- }
-
-
static void RaiseExitProcessEvent();
Assembly* RaiseResourceResolveEvent(DomainAssembly* pAssembly, LPCSTR szName);
DomainAssembly* RaiseTypeResolveEventThrowing(DomainAssembly* pAssembly, LPCSTR szName, ASSEMBLYREF *pResultingAssemblyRef);
friend class DomainAssembly;
-public:
- static void ProcessUnloadDomainEventOnFinalizeThread();
- static BOOL HasWorkForFinalizerThread()
- {
- LIMITED_METHOD_CONTRACT;
- return s_pAppDomainToRaiseUnloadEvent != NULL;
- }
-
private:
- static AppDomain* s_pAppDomainToRaiseUnloadEvent;
- static BOOL s_fProcessUnloadDomainEvent;
-
- void RaiseUnloadDomainEvent();
- static void RaiseUnloadDomainEvent_Wrapper(LPVOID /* AppDomain * */);
BOOL RaiseUnhandledExceptionEvent(OBJECTREF *pSender, OBJECTREF *pThrowable, BOOL isTerminating);
BOOL HasUnhandledExceptionEventHandler();
};
- static void AllowThreadEntrance(AppDomain *pApp);
- static void RestrictThreadEntrance(AppDomain *pApp);
-
- typedef Holder<AppDomain*,DoNothing<AppDomain*>,AppDomain::AllowThreadEntrance,NULL> RestrictEnterHolder;
-
enum Stage {
STAGE_CREATING,
STAGE_READYFORMANAGEDCODE,
STAGE_ACTIVE,
STAGE_OPEN,
- STAGE_UNLOAD_REQUESTED,
- STAGE_EXITING,
- STAGE_EXITED,
- STAGE_FINALIZING,
- STAGE_FINALIZED,
- STAGE_HANDLETABLE_NOACCESS,
- STAGE_CLEARED,
- STAGE_COLLECTED,
- STAGE_CLOSED
+ // Don't delete the following *_DONOTUSE members and in case a new member needs to be added,
+ // add it at the end. The reason is that debugger stuff has its own copy of this enum and
+ // it can use the members that are marked as *_DONOTUSE here when debugging older version
+ // of the runtime.
+ STAGE_UNLOAD_REQUESTED_DONOTUSE,
+ STAGE_EXITING_DONOTUSE,
+ STAGE_EXITED_DONOTUSE,
+ STAGE_FINALIZING_DONOTUSE,
+ STAGE_FINALIZED_DONOTUSE,
+ STAGE_HANDLETABLE_NOACCESS_DONOTUSE,
+ STAGE_CLEARED_DONOTUSE,
+ STAGE_COLLECTED_DONOTUSE,
+ STAGE_CLOSED_DONOTUSE
};
void SetStage(Stage stage)
{
while (lastStage !=stage)
lastStage = (Stage)FastInterlockCompareExchange((LONG*)&m_Stage,stage,lastStage);
};
- void Exit(BOOL fRunFinalizers, BOOL fAsyncExit);
- void Close();
void ClearGCRoots();
- void ClearGCHandles();
void UnwindThreads();
// Return TRUE if EE is stopped
// Return FALSE if more work is needed
BOOL StopEEAndUnwindThreads(unsigned int retryCount, BOOL *pFMarkUnloadRequestThread);
- // Use Rude Abort to unload the domain.
- BOOL m_fRudeUnload;
-
- Thread *m_pUnloadRequestThread;
- ADUnloadSink* m_ADUnloadSink;
- BOOL m_bForceGCOnUnload;
- BOOL m_bUnloadingFromUnloadEvent;
AppDomainLoaderAllocator m_LoaderAllocator;
// List of unloaded LoaderAllocators, protected by code:GetLoaderAllocatorReferencesLock (for now)
// Register the loader allocator for deletion in code:ShutdownFreeLoaderAllocators.
void RegisterLoaderAllocatorForDeletion(LoaderAllocator * pLoaderAllocator);
- AppDomain * m_pNextInDelayedUnloadList;
-
- void SetForceGCOnUnload(BOOL bSet)
- {
- m_bForceGCOnUnload=bSet;
- }
-
- void SetUnloadingFromUnloadEvent()
- {
- m_bUnloadingFromUnloadEvent=TRUE;
- }
-
- BOOL IsUnloadingFromUnloadEvent()
- {
- return m_bUnloadingFromUnloadEvent;
- }
-
- void SetRudeUnload()
- {
- LIMITED_METHOD_CONTRACT;
-
- m_fRudeUnload = TRUE;
- }
-
- BOOL IsRudeUnload()
- {
- LIMITED_METHOD_CONTRACT;
-
- return m_fRudeUnload;
- }
-
- ADUnloadSink* GetADUnloadSink();
- ADUnloadSink* GetADUnloadSinkForUnload();
- void SetUnloadRequestThread(Thread *pThread)
- {
- LIMITED_METHOD_CONTRACT;
-
- m_pUnloadRequestThread = pThread;
- }
-
- Thread *GetUnloadRequestThread()
- {
- LIMITED_METHOD_CONTRACT;
-
- return m_pUnloadRequestThread;
- }
-
public:
void SetGCRefPoint(int gccounter)
{
void AddMemoryPressure();
void RemoveMemoryPressure();
- void Unload(BOOL fForceUnload);
- static HRESULT UnloadById(ADID Id, BOOL fSync, BOOL fExceptionsPassThrough=FALSE);
- static HRESULT UnloadWait(ADID Id, ADUnloadSink* pSink);
-#ifdef FEATURE_TESTHOOKS
- static HRESULT UnloadWaitNoCatch(ADID Id, ADUnloadSink* pSink);
-#endif
- static void ResetUnloadRequestThread(ADID Id);
void UnlinkClass(MethodTable *pMT);
- typedef Holder<AppDomain *, AppDomain::SetUnloadInProgress, AppDomain::SetUnloadComplete> UnloadHolder;
Assembly *GetRootAssembly()
{
LIMITED_METHOD_CONTRACT;
LOAD_SYSTEM_ASSEMBLY_EVENT_SENT = 0x0040,
REMOTING_CONFIGURED_FOR_DOMAIN = 0x0100,
COMPILATION_DOMAIN = 0x0400, // Are we ngenning?
- APP_DOMAIN_CAN_BE_UNLOADED = 0x0800, // if need extra bits, can derive this at runtime
ORPHANED_LOCKS = 0x1000, // Orphaned locks exist in this appdomain.
PASSIVE_DOMAIN = 0x2000, // Can we execute code in this AppDomain
VERIFICATION_DOMAIN = 0x4000, // This is a verification domain
ArrayList m_NativeDllSearchDirectories;
BOOL m_ReversePInvokeCanEnter;
bool m_ForceTrivialWaitOperations;
- // Section to support AD unload due to escalation
-public:
- static void CreateADUnloadWorker();
-
- static void CreateADUnloadStartEvent();
-
- static DWORD WINAPI ADUnloadThreadStart(void *args);
- // Default is safe unload with test hook
- void EnableADUnloadWorker();
-
- // If called to handle stack overflow, we can not set event, since the thread has limit stack.
- void EnableADUnloadWorker(EEPolicy::AppDomainUnloadTypes type, BOOL fHasStack = TRUE);
-
- static void EnableADUnloadWorkerForThreadAbort();
- static void EnableADUnloadWorkerForFinalizer();
- static void EnableADUnloadWorkerForCollectedADCleanup();
-
- BOOL IsUnloadRequested()
- {
- LIMITED_METHOD_CONTRACT;
-
- return (m_Stage == STAGE_UNLOAD_REQUESTED);
- }
+public:
BOOL IsImageFromTrustedPath(PEImage* pImage);
#endif
private:
- static void ADUnloadWorkerHelper(AppDomain *pDomain);
- static CLREvent * g_pUnloadStartEvent;
#ifdef DACCESS_COMPILE
public:
// Just a ref holder
typedef ReleaseHolder<AppDomain> AppDomainRefHolder;
-// This class provides a way to access AppDomain by ID
-// without risking the appdomain getting invalid in the process
-class AppDomainFromIDHolder
-{
-public:
- enum SyncType
- {
- SyncType_GC, // Prevents AD from being unloaded by forbidding GC for the lifetime of the object
- SyncType_ADLock // Prevents AD from being unloaded by requiring ownership of DomainLock for the lifetime of the object
- };
-protected:
- AppDomain* m_pDomain;
-#ifdef _DEBUG
- BOOL m_bAcquired;
- BOOL m_bChecked;
- SyncType m_type;
-#endif
-public:
- DEBUG_NOINLINE AppDomainFromIDHolder(ADID adId, BOOL bUnsafePoint, SyncType synctype=SyncType_GC);
- DEBUG_NOINLINE AppDomainFromIDHolder(SyncType synctype=SyncType_GC);
- DEBUG_NOINLINE ~AppDomainFromIDHolder();
-
- void* GetAddress() { return m_pDomain; } // Used to get an identfier for ETW
- void Assign(ADID adId, BOOL bUnsafePoint);
- void ThrowIfUnloaded();
- void Release();
- BOOL IsUnloaded()
- {
- LIMITED_METHOD_CONTRACT;
-#ifdef _DEBUG
- m_bChecked=TRUE;
- if (m_pDomain==NULL)
- {
- // no need to enforce anything
- Release();
- }
-#endif
- return m_pDomain==NULL;
- };
- AppDomain* operator->();
-}; // class AppDomainFromIDHolder
-
-
-
typedef VPTR(class SystemDomain) PTR_SystemDomain;
class SystemDomain : public BaseDomain
friend class AppDomainIterator;
friend class UnsafeAppDomainIterator;
friend class ClrDataAccess;
- friend class AppDomainFromIDHolder;
friend Frame *Thread::IsRunningIn(AppDomain* pDomain, int *count);
VPTR_VTABLE_CLASS(SystemDomain, BaseDomain)
// Use an already exising & inited Application Domain (e.g. a subclass).
static void LoadDomain(AppDomain *pDomain);
-#ifndef DACCESS_COMPILE
- static void MakeUnloadable(AppDomain* pApp)
- {
- WRAPPER_NO_CONTRACT;
- System()->AddDomain(pApp);
- pApp->SetCanUnload();
- }
-#endif // DACCESS_COMPILE
-
//****************************************************************************************
// Methods used to get the callers module and hence assembly and app domain.
__declspec(deprecated("This method is deprecated, use the version that takes a StackCrawlMark instead"))
DWORD RequireAppDomainCleanup()
{
LIMITED_METHOD_CONTRACT;
- return m_pDelayedUnloadList != 0 || m_pDelayedUnloadListOfLoaderAllocators != 0;
- }
-
- void AddToDelayedUnloadList(AppDomain* pDomain, BOOL bAsync)
- {
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_COOPERATIVE;
- }
- CONTRACTL_END;
- m_UnloadIsAsync = bAsync;
-
- CrstHolder lh(&m_DelayedUnloadCrst);
- pDomain->m_pNextInDelayedUnloadList=m_pDelayedUnloadList;
- m_pDelayedUnloadList=pDomain;
- if (m_UnloadIsAsync)
- {
- pDomain->AddRef();
- int iGCRefPoint=GCHeapUtilities::GetGCHeap()->CollectionCount(GCHeapUtilities::GetGCHeap()->GetMaxGeneration());
- if (GCHeapUtilities::IsGCInProgress())
- iGCRefPoint++;
- pDomain->SetGCRefPoint(iGCRefPoint);
- }
+ return m_pDelayedUnloadListOfLoaderAllocators != 0;
}
void AddToDelayedUnloadList(LoaderAllocator * pAllocator)
pAllocator->SetGCRefPoint(iGCRefPoint);
}
- void ClearCollectedDomains();
- void ProcessClearingDomains();
- void ProcessDelayedUnloadDomains();
+ void ProcessDelayedUnloadLoaderAllocators();
- static void SetUnloadInProgress(AppDomain *pDomain)
- {
- WRAPPER_NO_CONTRACT;
-
- _ASSERTE(m_pAppDomainBeingUnloaded == NULL);
- m_pAppDomainBeingUnloaded = pDomain;
- m_dwIndexOfAppDomainBeingUnloaded = pDomain->GetIndex();
- }
-
- static void SetUnloadDomainCleared()
- {
- LIMITED_METHOD_CONTRACT;
-
- // about to delete, so clear this pointer so nobody uses it
- m_pAppDomainBeingUnloaded = NULL;
- }
- static void SetUnloadComplete()
- {
- LIMITED_METHOD_CONTRACT;
-
- // should have already cleared the AppDomain* prior to delete
- // either we succesfully unloaded and cleared or we failed and restored the ID
- _ASSERTE(m_pAppDomainBeingUnloaded == NULL && m_dwIndexOfAppDomainBeingUnloaded.m_dwIndex != 0
- || m_pAppDomainBeingUnloaded && SystemDomain::GetAppDomainAtId(m_pAppDomainBeingUnloaded->GetId()) != NULL);
- m_pAppDomainBeingUnloaded = NULL;
- m_pAppDomainUnloadingThread = NULL;
- }
-
- static AppDomain *AppDomainBeingUnloaded()
- {
- LIMITED_METHOD_CONTRACT;
- return m_pAppDomainBeingUnloaded;
- }
-
- static ADIndex IndexOfAppDomainBeingUnloaded()
- {
- LIMITED_METHOD_CONTRACT;
- return m_dwIndexOfAppDomainBeingUnloaded;
- }
-
- static void SetUnloadRequestingThread(Thread *pRequestingThread)
- {
- LIMITED_METHOD_CONTRACT;
- m_pAppDomainUnloadRequestingThread = pRequestingThread;
- }
-
- static Thread *GetUnloadRequestingThread()
- {
- LIMITED_METHOD_CONTRACT;
- return m_pAppDomainUnloadRequestingThread;
- }
-
- static void SetUnloadingThread(Thread *pUnloadingThread)
- {
- LIMITED_METHOD_CONTRACT;
- m_pAppDomainUnloadingThread = pUnloadingThread;
- }
-
- static Thread *GetUnloadingThread()
- {
- LIMITED_METHOD_CONTRACT;
- return m_pAppDomainUnloadingThread;
- }
-
static void EnumAllStaticGCRefs(promote_func* fn, ScanContext* sc);
#endif // DACCESS_COMPILE
STANDARD_VM_CONTRACT;
m_pDefaultDomain = NULL;
- m_pDelayedUnloadList=NULL;
m_pDelayedUnloadListOfLoaderAllocators=NULL;
- m_UnloadIsAsync = FALSE;
m_GlobalAllocator.Init(this);
}
// Global domain that every one uses
SPTR_DECL(SystemDomain, m_pSystemDomain);
- AppDomain* m_pDelayedUnloadList;
- BOOL m_UnloadIsAsync;
-
LoaderAllocator * m_pDelayedUnloadListOfLoaderAllocators;
#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
static ArrayListStatic m_appDomainIdList;
- // only one ad can be unloaded at a time
- static AppDomain* m_pAppDomainBeingUnloaded;
- // need this so can determine AD being unloaded after it has been deleted
- static ADIndex m_dwIndexOfAppDomainBeingUnloaded;
-
- // if had to spin off a separate thread to do the unload, this is the original thread.
- // allows us to delay aborting it until it's the last one so that it can receive
- // notification of an unload failure
- static Thread *m_pAppDomainUnloadRequestingThread;
-
- // this is the thread doing the actual unload. He's allowed to enter the domain
- // even if have started unloading.
- static Thread *m_pAppDomainUnloadingThread;
-
static GlobalStringLiteralMap *m_pGlobalStringLiteralMap;
static ULONG s_dNumAppDomains; // Maintain a count of children app domains.
{
m_pDomain->Release();
}
- else
- {
- STRESS_LOG2 (LF_APPDOMAIN, LL_INFO100, "Unload domain during creation [%d] %p\n", m_pDomain->GetId().m_dwId, m_pDomain);
- SystemDomain::MakeUnloadable(m_pDomain);
-#ifdef _DEBUG
- DWORD hostTestADUnload = g_pConfig->GetHostTestADUnload();
- m_pDomain->EnableADUnloadWorker(hostTestADUnload != 2?EEPolicy::ADU_Safe:EEPolicy::ADU_Rude);
-#else
- m_pDomain->EnableADUnloadWorker(EEPolicy::ADU_Safe);
-#endif
- }
};
public:
#include "appdomain.hpp"
-inline void AppDomain::SetUnloadInProgress(AppDomain *pThis)
-{
- WRAPPER_NO_CONTRACT;
-
- SystemDomain::System()->SetUnloadInProgress(pThis);
-}
-
-inline void AppDomain::SetUnloadComplete(AppDomain *pThis)
-{
- GCX_COOP();
-
- SystemDomain::System()->SetUnloadComplete();
-}
-
inline void AppDomain::EnterContext(Thread* pThread, Context* pCtx,ContextTransitionFrame *pFrame)
{
CONTRACTL
pThread->EnterContextRestricted(pCtx,pFrame);
};
-
-inline AppDomainFromIDHolder::~AppDomainFromIDHolder()
-{
- WRAPPER_NO_CONTRACT;
-#ifdef _DEBUG
- if(m_bAcquired)
- Release();
-#endif
-}
-
-inline void AppDomainFromIDHolder::Release()
-{
- //do not use real contract here!
- WRAPPER_NO_CONTRACT;
-#ifdef _DEBUG
- if(m_bAcquired)
- {
- if (m_type==SyncType_GC)
-#ifdef ENABLE_CONTRACTS_IMPL
- {
- if (GetThread())
- {
- STRESS_LOG1(LF_APPDOMAIN, LL_INFO10000, "AppDomainFromIDHolder::Assign is allowing GC - %08x",this);
- GetThread()->EndForbidGC();
- }
- else
- {
- if (!IsGCThread())
- {
- _ASSERTE(!"Should not be called from a non GC thread");
- }
- }
- }
-#else
- m_pDomain=NULL;
-#endif
- else
- if (m_type==SyncType_ADLock)
- SystemDomain::m_SystemDomainCrst.SetCantLeave(FALSE);
- else
- {
- _ASSERTE(!"Unknown type");
- }
- m_pDomain=NULL;
- m_bAcquired=FALSE;
- }
-#endif
-}
-
-inline void AppDomainFromIDHolder::Assign(ADID id, BOOL bUnsafePoint)
-{
- //do not use real contract here!
- WRAPPER_NO_CONTRACT;
- TESTHOOKCALL(AppDomainCanBeUnloaded(id.m_dwId, bUnsafePoint));
-#ifdef _DEBUG
- m_bChecked=FALSE;
- if (m_type==SyncType_GC)
- {
-#ifdef ENABLE_CONTRACTS_IMPL
- if (GetThread())
- {
- _ASSERTE(GetThread()->PreemptiveGCDisabled());
- STRESS_LOG1(LF_APPDOMAIN, LL_INFO10000, "AppDomainFromIDHolder::Assign is forbidding GC - %08x",this);
- GetThread()->BeginForbidGC(__FILE__, __LINE__);
- }
- else
- {
- if (!IsGCThread())
- {
- _ASSERTE(!"Should not be called from a non GC thread");
- }
- }
-#endif
- }
- else
- if (m_type==SyncType_ADLock)
- {
- _ASSERTE(SystemDomain::m_SystemDomainCrst.OwnedByCurrentThread());
- SystemDomain::m_SystemDomainCrst.SetCantLeave(TRUE);
- }
- else
- {
- _ASSERT(!"NI");
- }
-
- m_bAcquired=TRUE;
- #endif
- m_pDomain=SystemDomain::GetAppDomainAtId(id);
-
-}
-
-
-
-inline void AppDomainFromIDHolder::ThrowIfUnloaded()
-{
- STATIC_CONTRACT_THROWS;
- if (IsUnloaded())
- {
- COMPlusThrow(kAppDomainUnloadedException);
- }
-#ifdef _DEBUG
- m_bChecked=TRUE;
-#endif
-}
-
-inline AppDomain* AppDomainFromIDHolder::operator ->()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(m_bChecked && m_bAcquired);
- return m_pDomain;
-}
-
inline DomainAssembly* AppDomain::FindDomainAssembly(Assembly* assembly)
{
CONTRACTL
} // AppDomainNative::GetAssemblies
FCIMPLEND
-
-FCIMPL1(void, AppDomainNative::Unload, INT32 dwId)
-{
- FCALL_CONTRACT;
-
- HELPER_METHOD_FRAME_BEGIN_0();
-
- IfFailThrow(AppDomain::UnloadById(ADID(dwId),TRUE));
-
- HELPER_METHOD_FRAME_END();
-}
-FCIMPLEND
-
-FCIMPL1(FC_BOOL_RET, AppDomainNative::IsDomainIdValid, INT32 dwId)
-{
- FCALL_CONTRACT;
-
- BOOL retVal = FALSE;
- HELPER_METHOD_FRAME_BEGIN_RET_0()
-
- AppDomainFromIDHolder ad((ADID)dwId, TRUE);
- retVal=!ad.IsUnloaded();
- HELPER_METHOD_FRAME_END();
- FC_RETURN_BOOL(retVal);
-}
-FCIMPLEND
-
-
FCIMPL1(INT32, AppDomainNative::GetId, AppDomainBaseObject* refThisUNSAFE)
{
FCALL_CONTRACT;
static FCDECL2(Object*, GetOrInternString, AppDomainBaseObject* refThisUNSAFE, StringObject* pStringUNSAFE);
static FCDECL1(void, CreateContext, AppDomainBaseObject *refThisUNSAFE);
static void QCALLTYPE SetupBindingPaths(__in_z LPCWSTR wszTrustedPlatformAssemblies, __in_z LPCWSTR wszPlatformResourceRoots, __in_z LPCWSTR wszAppPaths, __in_z LPCWSTR wszAppNiPaths, __in_z LPCWSTR appLocalWinMD);
- static FCDECL1(void, Unload, INT32 dwId);
static FCDECL1(Object*, GetDynamicDir, AppDomainBaseObject* refThisUNSAFE);
static FCDECL1(INT32, GetId, AppDomainBaseObject* refThisUNSAFE);
- static FCDECL1(INT32, GetIdForUnload, AppDomainBaseObject* refDomainUNSAFE);
- static FCDECL1(FC_BOOL_RET, IsDomainIdValid, INT32 dwId);
static FCDECL1(void, ForceToSharedDomain, Object* pObjectUNSAFE);
static FCDECL1(LPVOID, GetFusionContext, AppDomainBaseObject* refThis);
static FCDECL2(Object*, IsStringInterned, AppDomainBaseObject* refThis, StringObject* pString);
#define Thread_m_pFrame Thread__m_pFrame
#ifndef CROSSGEN_COMPILE
-#define Thread__m_pDomain 0x14
+#define Thread__m_pDomain 0x10
ASMCONSTANTS_C_ASSERT(Thread__m_pDomain == offsetof(Thread, m_pDomain));
#define AppDomain__m_dwId 0x04
LOCAL_LABEL(UMThunkStub_InCooperativeMode):
ldr r12, [r7, #UMThunkStub_HiddenArgOffset]
-
- ldr r0, [r5, #Thread__m_pDomain]
- ldr r1, [r12, #UMEntryThunk__m_dwDomainId]
- ldr r0, [r0, #AppDomain__m_dwId]
ldr r3, [r12, #UMEntryThunk__m_pUMThunkMarshInfo]
- cmp r0, r1
- bne LOCAL_LABEL(UMThunkStub_WrongAppDomain)
-
ldr r2, [r3, #UMThunkMarshInfo__m_cbActualArgSize]
cbz r2, LOCAL_LABEL(UMThunkStub_ArgumentsSetup)
add sp, #SIZEOF__FloatArgumentRegisters
b LOCAL_LABEL(UMThunkStub_InCooperativeMode)
-LOCAL_LABEL(UMThunkStub_WrongAppDomain):
- sub sp, #SIZEOF__FloatArgumentRegisters
- vstm sp, {d0-d7}
-
- ldr r0, [r7, #UMThunkStub_HiddenArgOffset] // UMEntryThunk* pUMEntry
- mov r2, r7 // void * pArgs
- // remaining arguments are unused
- bl C_FUNC(UM2MDoADCallBack)
-
- // Restore non-FP return value.
- ldr r0, [r7, #0]
- ldr r1, [r7, #4]
-
- // Restore FP return value or HFA.
- vldm sp, {d0-d3}
- b LOCAL_LABEL(UMThunkStub_PostCall)
-
NESTED_END UMThunkStub,_TEXT
-// UM2MThunk_WrapperHelper(void *pThunkArgs, // r0
-// int cbStackArgs, // r1 (unused)
-// void *pAddr, // r2 (unused)
-// UMEntryThunk *pEntryThunk, // r3
-// Thread *pThread) // [sp, #0]
-
- NESTED_ENTRY UM2MThunk_WrapperHelper, _TEXT, NoHandler
-
- PROLOG_PUSH "{r4-r7,r11,lr}"
- PROLOG_STACK_SAVE_OFFSET r7, #12
-
- CHECK_STACK_ALIGNMENT
-
- mov r12, r3 // r12 = UMEntryThunk *
-
- //
- // Note that layout of the arguments is given by UMThunkStub frame
- //
- mov r5, r0 // r5 = pArgs
-
- ldr r3, [r12, #UMEntryThunk__m_pUMThunkMarshInfo]
-
- ldr r2, [r3, #UMThunkMarshInfo__m_cbActualArgSize]
- cbz r2, LOCAL_LABEL(UM2MThunk_WrapperHelper_ArgumentsSetup)
-
- add r0, r5, #UMThunkStub_StackArgsSize // Source pointer
- add r0, r0, r2
- lsr r1, r2, #2 // Count of stack slots to copy
-
- and r2, r2, #4 // Align the stack
- sub sp, sp, r2
-
-LOCAL_LABEL(UM2MThunk_WrapperHelper_StackLoop):
- ldr r2, [r0,#-4]!
- str r2, [sp,#-4]!
- subs r1, r1, #1
- bne LOCAL_LABEL(UM2MThunk_WrapperHelper_StackLoop)
-
-LOCAL_LABEL(UM2MThunk_WrapperHelper_ArgumentsSetup):
- ldr r4, [r3, #UMThunkMarshInfo__m_pILStub]
-
- // reload floating point registers
- sub r6, r5, #SIZEOF__FloatArgumentRegisters
- vldm r6, {d0-d7}
-
- // reload argument registers
- ldm r5, {r0-r3}
-
- CHECK_STACK_ALIGNMENT
-
- blx r4
-
- // Save non-floating point return
- str r0, [r5, #0]
- str r1, [r5, #4]
-
- // Save FP return value or HFA.
- vstm r6, {d0-d3}
-
-#ifdef _DEBUG
- // trash the floating point registers to ensure that the HFA return values
- // won't survive by accident
- vldm sp, {d0-d3}
-#endif
-
- EPILOG_STACK_RESTORE_OFFSET r7, #12
- EPILOG_POP "{r4-r7,r11,pc}"
-
- NESTED_END UM2MThunk_WrapperHelper, _TEXT
-
// ------------------------------------------------------------------
NESTED_ENTRY ThePreStub, _TEXT, NoHandler
IMPORT TheUMEntryPrestubWorker
IMPORT CreateThreadBlockThrow
IMPORT UMThunkStubRareDisableWorker
- IMPORT UM2MDoADCallBack
IMPORT PreStubWorker
IMPORT PreStubGetMethodDescForCompactEntryPoint
IMPORT NDirectImportWorker
UMThunkStub_InCooperativeMode
ldr r12, [r7, #UMThunkStub_HiddenArg]
- ldr r0, [r5, #Thread__m_pDomain]
- ldr r1, [r12, #UMEntryThunk__m_dwDomainId]
- ldr r0, [r0, #AppDomain__m_dwId]
ldr r3, [r12, #UMEntryThunk__m_pUMThunkMarshInfo]
- cmp r0, r1
- bne UMThunkStub_WrongAppDomain
-
ldr r2, [r3, #UMThunkMarshInfo__m_cbActualArgSize]
cbz r2, UMThunkStub_ArgumentsSetup
add sp, #SIZEOF__FloatArgumentRegisters
b UMThunkStub_InCooperativeMode
-UMThunkStub_WrongAppDomain
- sub sp, #SIZEOF__FloatArgumentRegisters
- vstm sp, {d0-d7}
-
- ldr r0, [r7, #UMThunkStub_HiddenArg] ; UMEntryThunk* pUMEntry
- mov r2, r7 ; void * pArgs
- ; remaining arguments are unused
- bl UM2MDoADCallBack
-
- ; Restore non-FP return value.
- ldr r0, [r7, #0]
- ldr r1, [r7, #4]
-
- ; Restore FP return value or HFA.
- vldm sp, {d0-d3}
- b UMThunkStub_PostCall
-
- NESTED_END
-
-; UM2MThunk_WrapperHelper(void *pThunkArgs, // r0
-; int cbStackArgs, // r1 (unused)
-; void *pAddr, // r2 (unused)
-; UMEntryThunk *pEntryThunk, // r3
-; Thread *pThread) // [sp, #0]
-
- NESTED_ENTRY UM2MThunk_WrapperHelper
-
- PROLOG_PUSH {r4-r7,r11,lr}
- PROLOG_STACK_SAVE r7
-
- CHECK_STACK_ALIGNMENT
-
- mov r12, r3 // r12 = UMEntryThunk *
-
- ;
- ; Note that layout of the arguments is given by UMThunkStub frame
- ;
- mov r5, r0 // r5 = pArgs
-
- ldr r3, [r12, #UMEntryThunk__m_pUMThunkMarshInfo]
-
- ldr r2, [r3, #UMThunkMarshInfo__m_cbActualArgSize]
- cbz r2, UM2MThunk_WrapperHelper_ArgumentsSetup
-
- add r0, r5, #UMThunkStub_StackArgs ; Source pointer
- add r0, r0, r2
- lsr r1, r2, #2 ; Count of stack slots to copy
-
- and r2, r2, #4 ; Align the stack
- sub sp, sp, r2
-
-UM2MThunk_WrapperHelper_StackLoop
- ldr r2, [r0,#-4]!
- str r2, [sp,#-4]!
- subs r1, r1, #1
- bne UM2MThunk_WrapperHelper_StackLoop
-
-UM2MThunk_WrapperHelper_ArgumentsSetup
- ldr r4, [r3, #UMThunkMarshInfo__m_pILStub]
-
- ; reload floating point registers
- sub r6, r5, #SIZEOF__FloatArgumentRegisters
- vldm r6, {d0-d7}
-
- ; reload argument registers
- ldm r5, {r0-r3}
-
- CHECK_STACK_ALIGNMENT
-
- blx r4
-
- ; Save non-floating point return
- str r0, [r5, #0]
- str r1, [r5, #4]
-
- ; Save FP return value or HFA.
- vstm r6, {d0-d3}
-
-#ifdef _DEBUG
- ;; trash the floating point registers to ensure that the HFA return values
- ;; won't survive by accident
- vldm sp, {d0-d3}
-#endif
-
- EPILOG_STACK_RESTORE r7
- EPILOG_POP {r4-r7,r11,pc}
-
NESTED_END
-
; ------------------------------------------------------------------
NESTED_ENTRY ThePreStub
#define Thread_m_fPreemptiveGCDisabled Thread__m_fPreemptiveGCDisabled
#ifndef CROSSGEN_COMPILE
-#define Thread__m_pDomain 0x20
+#define Thread__m_pDomain 0x18
ASMCONSTANTS_C_ASSERT(Thread__m_pDomain == offsetof(Thread, m_pDomain));
#define AppDomain__m_dwId 0x08
LOCAL_LABEL(UMThunkStub_InCooperativeMode):
ldr x12, [fp, #UMThunkStub_HiddenArg] // x12 = UMEntryThunk*
-
- ldr x0, [x19, #Thread__m_pDomain]
-
- // m_dwDomainId is 4 bytes so using 32-bit variant
- ldr w1, [x12, #UMEntryThunk__m_dwDomainId]
- ldr w0, [x0, #AppDomain__m_dwId]
- cmp w0, w1
- bne LOCAL_LABEL(UMThunkStub_WrongAppDomain)
-
ldr x3, [x12, #UMEntryThunk__m_pUMThunkMarshInfo] // x3 = m_pUMThunkMarshInfo
// m_cbActualArgSize is UINT32 and hence occupies 4 bytes
add sp, sp, #SIZEOF__FloatArgumentRegisters
b LOCAL_LABEL(UMThunkStub_InCooperativeMode)
-LOCAL_LABEL(UMThunkStub_WrongAppDomain):
- // Saving FP Args as this is read by UM2MThunk_WrapperHelper
- sub sp, sp, #SIZEOF__FloatArgumentRegisters
- SAVE_FLOAT_ARGUMENT_REGISTERS sp, 0
-
- // UMEntryThunk* pUMEntry
- ldr x0, [fp, #UMThunkStub_HiddenArg]
-
- // void * pArgs
- add x2, fp, #16
-
- // remaining arguments are unused
- bl C_FUNC(UM2MDoADCallBack)
-
- // restore integral return value
- ldp x0, x1, [fp, #16]
-
- // restore FP or HFA return value
- RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 0
-
- b LOCAL_LABEL(UMThunkStub_PostCall)
-
NESTED_END UMThunkStub, _TEXT
-
-// UM2MThunk_WrapperHelper(void *pThunkArgs, // x0
-// int cbStackArgs, // x1 (unused)
-// void *pAddr, // x2 (unused)
-// UMEntryThunk *pEntryThunk,// x3
-// Thread *pThread) // x4
-
-// pThunkArgs points to the argument registers pushed on the stack by UMThunkStub
-
-NESTED_ENTRY UM2MThunk_WrapperHelper, _TEXT, NoHandler
-
- PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -32
- PROLOG_SAVE_REG x19, 16
-
-
- // save pThunkArgs in non-volatile reg. It is required after return from call to ILStub
- mov x19, x0
-
- // ARM64TODO - Is this required by ILStub
- mov x12, x3 //// x12 = UMEntryThunk *
-
- //
- // Note that layout of the arguments is given by UMThunkStub frame
- //
- ldr x3, [x3, #UMEntryThunk__m_pUMThunkMarshInfo]
-
- // m_cbActualArgSize is 4-byte field
- ldr w2, [x3, #UMThunkMarshInfo__m_cbActualArgSize]
- cbz w2, LOCAL_LABEL(UM2MThunk_WrapperHelper_RegArgumentsSetup)
-
- // extend to 64- bits
- uxtw x2, w2
-
- // Source pointer. Subtracting 16 bytes due to fp & lr
- add x6, x0, #(UMThunkStub_StackArgs-16)
-
- // move source ptr to end of Stack Args
- add x6, x6, x2
-
- // Count of stack slot pairs to copy (divide by 16)
- lsr x1, x2, #4
-
- // Is there an extra stack slot? (can happen when stack arg bytes not multiple of 16)
- and x2, x2, #8
-
- // If yes then start source pointer from 16 byte aligned stack slot
- add x6, x6, x2
-
- // increment stack slot pair count by 1 if x2 is not zero
- add x1, x1, x2, LSR #3
-
-LOCAL_LABEL(UM2MThunk_WrapperHelper_StackLoop):
- ldp x4, x5, [x6, #-16]!
- stp x4, x5, [sp, #-16]!
- subs x1, x1, #1
- bne LOCAL_LABEL(UM2MThunk_WrapperHelper_StackLoop)
-
-LOCAL_LABEL(UM2MThunk_WrapperHelper_RegArgumentsSetup):
- ldr x16, [x3, #(UMThunkMarshInfo__m_pILStub)]
-
- // reload floating point registers
- RESTORE_FLOAT_ARGUMENT_REGISTERS x0, -1 * (SIZEOF__FloatArgumentRegisters + 16)
-
- // reload argument registers
- RESTORE_ARGUMENT_REGISTERS x0, 0
-
- blr x16
-
- // save integral return value
- stp x0, x1, [x19]
-
- // save FP/HFA return values
- SAVE_FLOAT_ARGUMENT_REGISTERS x19, -1 * (SIZEOF__FloatArgumentRegisters + 16)
-
- EPILOG_STACK_RESTORE
- EPILOG_RESTORE_REG x19, 16
- EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 32
- EPILOG_RETURN
-
-NESTED_END UM2MThunk_WrapperHelper, _TEXT
-
-
#ifdef FEATURE_HIJACK
// ------------------------------------------------------------------
// Hijack function for functions which return a scalar type or a struct (value type)
IMPORT GetThread
IMPORT CreateThreadBlockThrow
IMPORT UMThunkStubRareDisableWorker
- IMPORT UM2MDoADCallBack
IMPORT GetCurrentSavedRedirectContext
IMPORT LinkFrameAndThrow
IMPORT FixContextHandler
UMThunkStub_InCooperativeMode
ldr x12, [fp, #UMThunkStub_HiddenArg] ; x12 = UMEntryThunk*
-
- ldr x0, [x19, #Thread__m_pDomain]
-
- ; m_dwDomainId is 4 bytes so using 32-bit variant
- ldr w1, [x12, #UMEntryThunk__m_dwDomainId]
- ldr w0, [x0, #AppDomain__m_dwId]
- cmp w0, w1
- bne UMThunkStub_WrongAppDomain
-
ldr x3, [x12, #UMEntryThunk__m_pUMThunkMarshInfo] ; x3 = m_pUMThunkMarshInfo
; m_cbActualArgSize is UINT32 and hence occupies 4 bytes
add sp, sp, #SIZEOF__FloatArgumentRegisters
b UMThunkStub_InCooperativeMode
-UMThunkStub_WrongAppDomain
- ; Saving FP Args as this is read by UM2MThunk_WrapperHelper
- sub sp, sp, #SIZEOF__FloatArgumentRegisters
- SAVE_FLOAT_ARGUMENT_REGISTERS sp, 0
-
- ; UMEntryThunk* pUMEntry
- ldr x0, [fp, #UMThunkStub_HiddenArg]
-
- ; void * pArgs
- add x2, fp, #16
-
- ; remaining arguments are unused
- bl UM2MDoADCallBack
-
- ; restore any integral return value(s)
- ldp x0, x1, [fp, #16]
-
- ; restore any FP or HFA return value(s)
- RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 0
-
- b UMThunkStub_PostCall
-
- NESTED_END
-
-
-; UM2MThunk_WrapperHelper(void *pThunkArgs, // x0
-; int cbStackArgs, // x1 (unused)
-; void *pAddr, // x2 (unused)
-; UMEntryThunk *pEntryThunk, // x3
-; Thread *pThread) // x4
-
-; pThunkArgs points to the argument registers pushed on the stack by UMThunkStub
-
- NESTED_ENTRY UM2MThunk_WrapperHelper
-
- PROLOG_SAVE_REG_PAIR fp, lr, #-32!
- PROLOG_SAVE_REG x19, #16
-
-
- ; save pThunkArgs in non-volatile reg. It is required after return from call to ILStub
- mov x19, x0
-
- ; ARM64TODO - Is this required by ILStub
- mov x12, x3 ; // x12 = UMEntryThunk *
-
- ;
- ; Note that layout of the arguments is given by UMThunkStub frame
- ;
- ldr x3, [x3, #UMEntryThunk__m_pUMThunkMarshInfo]
-
- ; m_cbActualArgSize is 4-byte field
- ldr w2, [x3, #UMThunkMarshInfo__m_cbActualArgSize]
- cbz w2, UM2MThunk_WrapperHelper_RegArgumentsSetup
-
- ; extend to 64- bits
- uxtw x2, w2
-
- ; Source pointer. Subtracting 16 bytes due to fp & lr
- add x6, x0, #(UMThunkStub_StackArgs-16)
-
- ; move source ptr to end of Stack Args
- add x6, x6, x2
-
- ; Count of stack slot pairs to copy (divide by 16)
- lsr x1, x2, #4
-
- ; Is there an extra stack slot? (can happen when stack arg bytes not multiple of 16)
- and x2, x2, #8
-
- ; If yes then start source pointer from 16 byte aligned stack slot
- add x6, x6, x2
-
- ; increment stack slot pair count by 1 if x2 is not zero
- add x1, x1, x2, LSR #3
-
-UM2MThunk_WrapperHelper_StackLoop
- ldp x4, x5, [x6, #-16]!
- stp x4, x5, [sp, #-16]!
- subs x1, x1, #1
- bne UM2MThunk_WrapperHelper_StackLoop
-
-UM2MThunk_WrapperHelper_RegArgumentsSetup
- ldr x16, [x3, #(UMThunkMarshInfo__m_pILStub)]
-
- ; reload floating point registers
- RESTORE_FLOAT_ARGUMENT_REGISTERS x0, -1 * (SIZEOF__FloatArgumentRegisters + 16)
-
- ; reload argument registers
- RESTORE_ARGUMENT_REGISTERS x0, 0
-
- blr x16
-
- ; save any integral return value(s)
- stp x0, x1, [x19]
-
- ; save any FP or HFA return value(s)
- SAVE_FLOAT_ARGUMENT_REGISTERS x19, -1 * (SIZEOF__FloatArgumentRegisters + 16)
-
- EPILOG_STACK_RESTORE
- EPILOG_RESTORE_REG x19, #16
- EPILOG_RESTORE_REG_PAIR fp, lr, #32!
- EPILOG_RETURN
-
NESTED_END
#ifdef FEATURE_HIJACK
//
ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
- _ASSERTE(GetAppDomain()->ShouldHaveCode());
-
#ifdef FEATURE_INTERPRETER
_ASSERTE(isCallConv(m_methodSig.GetCallingConvention(), IMAGE_CEE_CS_CALLCONV_DEFAULT)
|| isCallConv(m_methodSig.GetCallingConvention(), CorCallingConvention(IMAGE_CEE_CS_CALLCONV_C))
StackwalkCache::Init();
- AppDomain::CreateADUnloadStartEvent();
-
// In coreclr, clrjit is compiled into it, but SO work in clrjit has not been done.
#ifdef FEATURE_STACK_PROBE
if (CLRHosted() && GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) == eRudeUnloadAppDomain)
SystemDomain::NotifyProfilerStartup();
#endif // PROFILING_SUPPORTED
-#ifndef CROSSGEN_COMPILE
- if (CLRHosted()
-#ifdef _DEBUG
- || ((fFlags & COINITEE_DLL) == 0 &&
- g_pConfig->GetHostTestADUnload())
-#endif
- ) {
- // If we are hosted, a host may specify unloading AD when a managed allocation in
- // critical region fails. We need to precreate a thread to unload AD.
- AppDomain::CreateADUnloadWorker();
- }
-#endif // CROSSGEN_COMPILE
-
g_fEEInit = false;
SystemDomain::System()->DefaultDomain()->LoadSystemAssemblies();
while (appDomainIterator.Next())
{
AppDomain * pAppDomain = appDomainIterator.GetDomain();
- if (pAppDomain->IsUnloading())
- {
- continue;
- }
hr = EnumerateDomainClosedMethodDescs(
pAppDomain,
pModule,
//
GCX_COOP_THREAD_EXISTS(GET_THREAD());
- AppDomainFromIDHolder ad(m_adid, TRUE);
-
- if (!ad.IsUnloaded())
- DestroyShortWeakHandle(m_ppObject);
+ DestroyShortWeakHandle(m_ppObject);
m_ppObject = NULL;
}
// so we must release it if the AD wasn't unloaded
if (IsAgile() && m_hOrigDomainHandle)
{
- // the domain which the original handle belongs to might be already unloaded
- if (GetRawDomainID()==::GetAppDomain()->GetId())
- DestroyRefcountedHandle(m_hOrigDomainHandle);
- else
- {
- GCX_COOP();
- {
- AppDomainFromIDHolder ad(GetRawDomainID(), TRUE);
- if (!ad.IsUnloaded())
- DestroyRefcountedHandle(m_hOrigDomainHandle);
- }
- }
+ DestroyRefcountedHandle(m_hOrigDomainHandle);
m_hOrigDomainHandle = NULL;
}
MODE_ANY;
}
CONTRACTL_END;
-#ifdef _DEBUG
- AppDomainFromIDHolder ad(GetSimpleWrapper()->GetDomainID(), TRUE);
- _ASSERTE(!ad.IsUnloaded());
-#endif
+
SyncBlock* pSyncBlock = GetSyncBlock();
_ASSERTE(pSyncBlock);
}
CONTRACTL_END;
-#ifdef _DEBUG
- AppDomainFromIDHolder ad(GetSimpleWrapper()->GetDomainID(), TRUE);
- _ASSERTE(!ad.IsUnloaded());
-#endif
SyncBlock* pSyncBlock = GetSyncBlock();
_ASSERTE(pSyncBlock);
ClearSimpleWrapper(this);
}
+ if (fOwnsHandle && m_ppThis)
{
- // Switch to cooperative mode for AppDomainFromIDHolder
- // AppDomainFromIDHolder.Assign might forbid GC and AppDomainFromIDHolder.Release might re-enable GC.
- // The state is stored in ClrDebugState, which GCX_COOP() macros will push into stack & pop from stack
- // So use GCX_COOP() around all these statements for AppDomainFromIDHolder
- GCX_COOP();
-
- // deregister the handle, in the first block. If no domain, then it's already done
- AppDomainFromIDHolder pTgtDomain;
- if (domainId != CURRENT_APPDOMAIN_ID)
- {
- pTgtDomain.Assign(domainId, FALSE);
- }
-
- if (fOwnsHandle && m_ppThis && !pTgtDomain.IsUnloaded())
- {
- LOG((LF_INTEROP, LL_INFO100, "ComCallWrapper::Cleanup on Object %8.8x\n", m_ppThis));
- ClearHandle();
- }
-
- pTgtDomain.Release();
+ LOG((LF_INTEROP, LL_INFO100, "ComCallWrapper::Cleanup on Object %8.8x\n", m_ppThis));
+ ClearHandle();
}
m_ppThis = NULL;
if (m_dwServerSyncBlockIndex != 0)
{
AppDomain* pCurrDomain = m_pThread->GetDomain();
- if (m_dwServerDomainId == pCurrDomain->GetId())
- {
- // if we are in the right AD, we know for sure that the object is still alive
- // since we keep the CCW addref'ed and the AD could not have been unloaded
- oref = ObjectToOBJECTREF(g_pSyncTable[m_dwServerSyncBlockIndex].m_Object);
- }
- else
- {
- // otherwise we have to make sure that the AD hasn't been unloaded
- AppDomainFromIDHolder ad(m_dwServerDomainId, TRUE);
- if (!ad.IsUnloaded())
- {
- oref = ObjectToOBJECTREF(g_pSyncTable[m_dwServerSyncBlockIndex].m_Object);
- }
- }
+
+ // if we are in the right AD, we know for sure that the object is still alive
+ // since we keep the CCW addref'ed
+ oref = ObjectToOBJECTREF(g_pSyncTable[m_dwServerSyncBlockIndex].m_Object);
}
return oref;
MODE_COOPERATIVE;
}
CONTRACTL_END;
- AppDomainFromIDHolder ad(internal->GetKickOffDomainId(), TRUE);
- if (ad.IsUnloaded())
- COMPlusThrow(kAppDomainUnloadedException);
+ AppDomain *ad = SystemDomain::GetAppDomainFromId(internal->GetKickOffDomainId(), ADV_CURRENTAD);
+
m_Threadable = ad->CreateHandle(threadable);
m_ThreadStartArg = ad->CreateHandle(threadStartArg);
}
CONTRACTL_END;
- // It's important to have no GC rendez-vous point between the checking and the clean-up below.
- // The three handles below could be in an appdomain which is just starting to be unloaded, or an appdomain
- // which has been unloaded already. Thus, we need to check whether the appdomain is still valid before
- // we do the clean-up. Since we suspend all runtime threads when we try to do the unload, there will be no
- // race condition between the checking and the clean-up as long as this thread cannot be suspended in between.
- AppDomainFromIDHolder ad(m_Internal->GetKickOffDomainId(), TRUE);
- if (!ad.IsUnloaded())
- {
- DestroyHandle(m_Threadable);
- DestroyHandle(m_ThreadStartArg);
- }
+ DestroyHandle(m_Threadable);
+ DestroyHandle(m_ThreadStartArg);
}
};
BEGIN_SO_INTOLERANT_CODE_NOTHROW(pThread, { *pRetValOut = COR_E_STACKOVERFLOW; return; } );
EX_TRY
{
- bool fNeedToTranslateTAEtoADUE = false;
ADID pTgtDomain = pWrap->GetDomainID();
ENTER_DOMAIN_ID(pTgtDomain)
{
fEnteredDomain = TRUE;
COMToCLRWorkerBody_SOIntolerant(pThread, pFrame, pWrap, pRetValOut);
-
- //
- // Below is some logic adapted from Thread::RaiseCrossContextExceptionHelper, which we now
- // bypass because the IL stub is catching the ThreadAbortException instead of a proper domain
- // transition, where the logic typically resides. This code applies some policy to transform
- // the ThreadAbortException into an AppDomainUnloadedException and sets up the HRESULT and
- // IErrorInfo accordingly.
- //
-
- // If the IL stub caught a TAE...
- if (COR_E_THREADABORTED == ((HRESULT)*pRetValOut))
- {
- // ...first, make sure it was actually an HRESULT return value...
- ComCallMethodDesc* pCMD = pFrame->GetComCallMethodDesc();
- if (pCMD->IsNativeHResultRetVal())
- {
- // There may be multiple AD transitions on the stack so the current unload boundary may
- // not be the transition frame that was set up to make our AD switch. Detect that by
- // comparing the unload boundary's Next with our ComMethodFrame and proceed to translate
- // the exception to ADUE only if they match. Otherwise the exception should stay as TAE.
-
- Frame* pUnloadBoundary = pThread->GetUnloadBoundaryFrame();
- // ...and we are at an unload boundary with a pending unload...
- if ( ( pUnloadBoundary != NULL
- && (pUnloadBoundary->Next() == pFrame
- && pThread->ShouldChangeAbortToUnload(pUnloadBoundary, pUnloadBoundary))
- )
- // ... or we don't have an unload boundary, but we're otherwise unloading
- // this domain from another thread (and we aren't the finalizer)...
- || ( (NULL == pUnloadBoundary)
- && (pThread->GetDomain() == SystemDomain::AppDomainBeingUnloaded())
- && (pThread != SystemDomain::System()->GetUnloadingThread())
- && (pThread != FinalizerThread::GetFinalizerThread())
- )
- )
- {
- // ... we take note and then create an ADUE in the domain we're returning to.
- fNeedToTranslateTAEtoADUE = true;
- }
- }
- }
}
END_DOMAIN_TRANSITION;
-
- if (fNeedToTranslateTAEtoADUE)
- {
- EEResourceException ex(kAppDomainUnloadedException, W("Remoting_AppDomainUnloaded_ThreadUnwound"));
- OBJECTREF oEx = CLRException::GetThrowableFromException(&ex);
- *pRetValOut = SetupErrorInfo(oEx, pFrame->GetComCallMethodDesc());
- }
}
EX_CATCH
{
}
CONTRACTL_END;
- HRESULT hr = S_OK;
-
- // No point going further if the runtime is not running...
- {
- // In IsRuntimeActive, we will call CanRunManagedCode that will
- // check if the current thread has taken the loader lock or not,
- // if MDA is supported. To do the check, MdaLoaderLock::ReportViolation
- // will be invoked that will internally end up invoking
- // MdaFactory<MdaXmlElement>::GetNext that will use the "new" operator
- // that has the "FAULT" contract set, resulting in FAULT_VIOLATION since
- // this method has the FORBID_FAULT contract set above.
- //
- // However, for a thread that holds the loader lock, unloading the appDomain is
- // not a supported scenario. Thus, we should not be ending up in this code
- // path for the FAULT violation.
- //
- // Hence, the CONTRACT_VIOLATION below for overriding the FORBID_FAULT
- // for this scope only.
- CONTRACT_VIOLATION(FaultViolation);
- if (!IsRuntimeActive()
- || !m_fStarted
- )
- {
- return HOST_E_CLRNOTAVAILABLE;
- }
- }
-
- BEGIN_ENTRYPOINT_NOTHROW;
-
- // We do not use BEGIN_EXTERNAL_ENTRYPOINT here because
- // we do not want to setup Thread. Process may be OOM, and we want Unload
- // to work.
- hr = AppDomain::UnloadById(ADID(dwDomainId), fWaitUntilDone);
-
- END_ENTRYPOINT_NOTHROW;
-
- if (pLatchedExitCode)
- {
- *pLatchedExitCode = GetLatchedExitCode();
- }
-
- return hr;
+ return COR_E_CANNOTUNLOADAPPDOMAIN;
}
//*****************************************************************************
BEGIN_ENTRYPOINT_NOTHROW;
SystemDomain::LockHolder lh;
- AppDomainFromIDHolder pAppDomain((ADID)dwAppDomainId, TRUE, AppDomainFromIDHolder::SyncType_ADLock);
- if (!pAppDomain.IsUnloaded())
+ AppDomain* pAppDomain = SystemDomain::GetAppDomainFromId((ADID)dwAppDomainId, ADV_CURRENTAD);
+ if (pBytesAllocated)
{
- if (pBytesAllocated)
- {
- *pBytesAllocated = pAppDomain->GetAllocBytes();
- }
- }
- else
- {
- hr = COR_E_APPDOMAINUNLOADED;
+ *pBytesAllocated = pAppDomain->GetAllocBytes();
}
END_ENTRYPOINT_NOTHROW;
BEGIN_ENTRYPOINT_NOTHROW;
SystemDomain::LockHolder lh;
- AppDomainFromIDHolder pAppDomain((ADID)dwAppDomainId, TRUE, AppDomainFromIDHolder::SyncType_ADLock);
- if (pAppDomain.IsUnloaded())
+ AppDomain* pAppDomain = SystemDomain::GetAppDomainFromId((ADID)dwAppDomainId, ADV_CURRENTAD);
+ if (pAppDomainBytesSurvived)
{
- hr = COR_E_APPDOMAINUNLOADED;
+ *pAppDomainBytesSurvived = pAppDomain->GetSurvivedBytes();
}
- else
+ if (pTotalBytesSurvived)
{
- if (pAppDomainBytesSurvived)
- {
- *pAppDomainBytesSurvived = pAppDomain->GetSurvivedBytes();
- }
- if (pTotalBytesSurvived)
- {
- *pTotalBytesSurvived = SystemDomain::GetTotalSurvivedBytes();
- }
+ *pTotalBytesSurvived = SystemDomain::GetTotalSurvivedBytes();
}
END_ENTRYPOINT_NOTHROW;
{
SystemDomain::LockHolder lh;
+ AppDomain* pAppDomain = SystemDomain::GetAppDomainFromId((ADID)dwAppDomainId, ADV_CURRENTAD);
+ if (pMilliseconds)
{
- AppDomainFromIDHolder pAppDomain((ADID)dwAppDomainId, TRUE, AppDomainFromIDHolder::SyncType_ADLock);
-
- if (!pAppDomain.IsUnloaded())
- {
- if (pMilliseconds)
- {
- *pMilliseconds = pAppDomain->QueryProcessorUsage() / 10000;
- }
- }
- else
- {
- hr = COR_E_APPDOMAINUNLOADED;
- }
+ *pMilliseconds = pAppDomain->QueryProcessorUsage() / 10000;
}
}
CONTRACTL_END;
- AppDomainFromIDHolder ad(m_appDomainId, FALSE);
- if (!ad.IsUnloaded())
- {
- if (m_stateHandle)
- DestroyHandle(m_stateHandle);
- if (m_eventHandle)
- DestroyHandle(m_eventHandle);
- if (m_registeredWaitHandle)
- DestroyHandle(m_registeredWaitHandle);
- }
-
+ if (m_stateHandle)
+ DestroyHandle(m_stateHandle);
+ if (m_eventHandle)
+ DestroyHandle(m_eventHandle);
+ if (m_registeredWaitHandle)
+ DestroyHandle(m_registeredWaitHandle);
}
#endif
DispatchMemberInfo* pInfo = new DispatchMemberInfo(this, DispID, strMemberName, MemberInfoObj);
- AppDomainFromIDHolder pDomain(m_pSimpleWrapperOwner->GetDomainID(), FALSE);
- pDomain.ThrowIfUnloaded();
+ AppDomain* pDomain = SystemDomain::GetAppDomainFromId(m_pSimpleWrapperOwner->GetDomainID(), ADV_CURRENTAD);
pInfo->SetHandle(pDomain->CreateHandle(MemberInfoObj));
static UMEntryThunkFreeList s_thunkFreeList(DEFAULT_THUNK_FREE_LIST_THRESHOLD);
-EXTERN_C void STDCALL UM2MThunk_WrapperHelper(void *pThunkArgs,
- int argLen,
- void *pAddr,
- UMEntryThunk *pEntryThunk,
- Thread *pThread);
-
-// This is used as target of callback from DoADCallBack. It sets up the environment and effectively
-// calls back into the thunk that needed to switch ADs.
-void UM2MThunk_Wrapper(LPVOID ptr) // UM2MThunk_Args
-{
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_GC_TRIGGERS;
- STATIC_CONTRACT_MODE_COOPERATIVE;
- STATIC_CONTRACT_SO_INTOLERANT;
-
- UM2MThunk_Args *pArgs = (UM2MThunk_Args *) ptr;
- Thread* pThread = GetThread();
-
- BEGIN_CALL_TO_MANAGED();
-
- // return value is saved to pArgs->pThunkArgs
- UM2MThunk_WrapperHelper(pArgs->pThunkArgs,
- pArgs->argLen,
- pArgs->pAddr,
- pArgs->pEntryThunk,
- pThread);
-
- END_CALL_TO_MANAGED();
-}
-
-EXTERN_C void STDCALL UM2MDoADCallBack(UMEntryThunk *pEntryThunk,
- void *pAddr,
- void *pArgs,
- int argLen)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- ENTRY_POINT;
- PRECONDITION(CheckPointer(pEntryThunk));
- PRECONDITION(CheckPointer(pArgs));
- }
- CONTRACTL_END;
-
- UM2MThunk_Args args = { pEntryThunk, pAddr, pArgs, argLen };
-
-
- INSTALL_MANAGED_EXCEPTION_DISPATCHER;
- INSTALL_UNWIND_AND_CONTINUE_HANDLER;
- {
- AppDomainFromIDHolder domain(pEntryThunk->GetDomainId(),FALSE);
- domain.ThrowIfUnloaded();
- if(!domain->CanReversePInvokeEnter())
- COMPlusThrow(kNotSupportedException);
- }
-
- GetThread()->DoADCallBack(pEntryThunk->GetDomainId(), UM2MThunk_Wrapper, &args);
-
- UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
- UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
-}
-
#if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
EXTERN_C VOID __cdecl UMThunkStubRareDisable();
CodeLabel* pRejoinThreadLabel = pcpusl->NewCodeLabel();
CodeLabel* pDisableGCLabel = pcpusl->NewCodeLabel();
CodeLabel* pRejoinGCLabel = pcpusl->NewCodeLabel();
- CodeLabel* pDoADCallBackLabel = pcpusl->NewCodeLabel();
- CodeLabel* pDoneADCallBackLabel = pcpusl->NewCodeLabel();
- CodeLabel* pADCallBackEpilog = pcpusl->NewCodeLabel();
- CodeLabel* pDoADCallBackStartLabel = pcpusl->NewAbsoluteCodeLabel();
// We come into this code with UMEntryThunk in EAX
const X86Reg kEAXentryThunk = kEAX;
// lea ebx, [ebp + 8]
pcpusl->X86EmitIndexLea(kEBX, kEBP, 8);
- // Load pThread->m_pDomain into edx
- // mov edx,[ecx + offsetof(Thread, m_pAppDomain)]
- pcpusl->X86EmitIndexRegLoad(kEDX, kECXthread, Thread::GetOffsetOfAppDomain());
-
- // Load pThread->m_pAppDomain->m_dwId into edx
- // mov edx,[edx + offsetof(AppDomain, m_dwId)]
- pcpusl->X86EmitIndexRegLoad(kEDX, kEDX, AppDomain::GetOffsetOfId());
-
- // check if the app domain of the thread matches that of delegate
- // cmp edx,[eax + offsetof(UMEntryThunk, m_dwDomainId))]
- pcpusl->X86EmitOffsetModRM(0x3b, kEDX, kEAXentryThunk, offsetof(UMEntryThunk, m_dwDomainId));
-
- // jne pWrongAppDomain ; mismatch. This will call back into the stub with the
- // correct AppDomain through DoADCallBack
- pcpusl->X86EmitCondJump(pDoADCallBackLabel, X86CondCode::kJNE);
-
//
// ----------------------------------------------------------------------------------------------
//
// +-------------------------+
//
- // It's important that the "restart" after an AppDomain switch will skip
- // the check for g_TrapReturningThreads. That's because, during shutdown,
- // we can only go through the UMThunkStubRareDisable pathway if we have
- // not yet pushed a frame. (Once pushed, the frame cannot be popped
- // without coordinating with the GC. During shutdown, such coordination
- // would deadlock).
- pcpusl->EmitLabel(pDoADCallBackStartLabel);
-
// save the thread pointer
pcpusl->X86EmitPushReg(kECXthread);
// restore the thread pointer
pcpusl->X86EmitPopReg(kECXthread);
-
- // Check whether we got here via the switch AD case. We can tell this by looking at whether the
- // caller's arguments immediately precede our EBP frame (they will for the non-switch case but
- // otherwise we will have pushed several frames in the interim). If we did switch now is the time
- // to jump to our inner epilog which will clean up the inner stack frame and return to the runtime
- // AD switching code.
-
- // Does EBX (argument pointer) == EBP + 8?
- // sub ebx, 8
- pcpusl->X86EmitSubReg(kEBX, 8);
-
- // cmp ebx, ebp
- pcpusl->X86EmitR2ROp(0x3B, kEBX, kEBP);
-
- // jne pADCallBackEpilog
- pcpusl->X86EmitCondJump(pADCallBackEpilog, X86CondCode::kJNE);
-
//
// Once we reach this point in the code we're back to a single scenario: the outer frame of the
- // reverse p/invoke. Either we never had to switch AppDomains or the AD switch code has already
- // unwound and returned here to pop off the outer frame.
+ // reverse p/invoke.
//
// ----------------------------------------------------------------------------------------------
//
- pcpusl->EmitLabel(pDoneADCallBackLabel);
-
// move byte ptr [ecx + Thread.m_fPreemptiveGCDisabled],0
pcpusl->X86EmitOffsetModRM(0xc6, (X86Reg)0, kECXthread, Thread::GetOffsetOfGCFlag());
pcpusl->Emit8(0);
pcpusl->X86EmitNearJump(pRejoinGCLabel);
//-------------------------------------------------------------
- // coming here if appdomain didn't match
- //
-
- pcpusl->EmitLabel(pDoADCallBackLabel);
-
- // we will call DoADCallBack which calls into managed code to switch ADs and then calls us
- // back. So when come in the second time the ADs will match and just keep processing.
- // So we need to setup the parms to pass to DoADCallBack one of which is an address inside
- // the stub that will branch back to the top of the stub to start again. Need to setup
- // the parms etc so that when we return from the 2nd call we pop things properly.
-
- // save thread pointer
- pcpusl->X86EmitPushReg(kECXthread);
-
- // push values for UM2MThunk_Args
-
- // Move address of args (EBX) into EDX since some paths below use EBX.
- pcpusl->X86EmitMovRegReg(kEDX, kEBX);
-
- // size of args
- pcpusl->X86EmitPushImm32(pInfo->m_cbSrcStack);
-
- // address of args
- pcpusl->X86EmitPushReg(kEDX);
-
- // addr to call
- pcpusl->X86EmitPushImm32(*pDoADCallBackStartLabel);
-
- // UMEntryThunk
- pcpusl->X86EmitPushReg(kEAXentryThunk);
-
- // call UM2MDoADCallBack
- pcpusl->X86EmitCall(pcpusl->NewExternalCodeLabel((LPVOID) UM2MDoADCallBack), 8);
-
- // We need to clear the thread off the top of the stack and place it in ECX. Two birds with one stone.
- pcpusl->X86EmitPopReg(kECX);
-
- // Re-join the original stub to perform the last parts of the epilog.
- pcpusl->X86EmitNearJump(pDoneADCallBackLabel);
-
- //-------------------------------------------------------------
// Coming here for rare case when enabling GC pre-emptive mode
//
// return to mainline of function
pcpusl->X86EmitNearJump(pEnableRejoin);
-
- //-------------------------------------------------------------
- // Coming here when we switched AppDomain and have successfully called the target. We must return
- // into the runtime code (which will eventually unwind the AD transition and return us to the
- // mainline stub in order to run the outer epilog).
- //
-
- pcpusl->EmitLabel(pADCallBackEpilog);
- pcpusl->X86EmitReturn(0);
}
// Compiles an unmanaged to managed thunk for the given signature.
// exceptions don't leak out into managed code.
INSTALL_UNWIND_AND_CONTINUE_HANDLER;
- // The thread object is guaranteed to have been set up at this point.
- Thread *pThread = GetThread();
-
- if (pThread->GetDomain()->GetId() != pUMEntryThunk->GetDomainId())
- {
- // call ourselves again through DoCallBack with a domain transition
- pThread->DoADCallBack(pUMEntryThunk->GetDomainId(), RunTimeInit_Wrapper, pUMEntryThunk);
- }
- else
{
GCX_PREEMP();
pUMEntryThunk->RunTimeInit();
#include "perfmap.h"
#endif // FEATURE_PERFMAP
-BOOL DomainAssembly::IsUnloading()
-{
- WRAPPER_NO_CONTRACT;
- SUPPORTS_DAC;
-
- BOOL fIsUnloading = FALSE;
-
- fIsUnloading = this->GetAppDomain()->IsUnloading();
-
- if (!fIsUnloading)
- {
- fIsUnloading = m_fDebuggerUnloadStarted;
- }
-
- return fIsUnloading;
-}
-
-
#ifndef DACCESS_COMPILE
DomainFile::DomainFile(AppDomain *pDomain, PEFile *pFile)
: m_pDomain(pDomain),
while (ai.Next())
{
STRESS_LOG3(LF_LOADER, LL_INFO100,"Attempting to propagate domain-neutral conditional module dependency %p -> %p to AppDomain %i\n",pModuleFrom,pModuleTo,ai.GetDomain()->GetId().m_dwId);
- // This is to minimize the chances of trying to run code in an appdomain that's shutting down.
- if (ai.GetDomain()->CanThreadEnter(pThread))
- {
- completed &= PropagateActivationInAppDomain(pModuleFrom,pModuleTo,ai.GetDomain());
- }
+ completed &= PropagateActivationInAppDomain(pModuleFrom,pModuleTo,ai.GetDomain());
}
}
else
BOOL IsVisibleToDebugger();
BOOL NotifyDebuggerLoad(int flags, BOOL attaching);
void NotifyDebuggerUnload();
- BOOL IsUnloading();
inline BOOL IsCollectible();
//
#ifdef _DEBUG
fShouldInjectFault = 0;
testThreadAbort = 0;
- testADUnload = 0;
#endif
#ifdef FEATURE_COMINTEROP
fShouldInjectFault = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault);
testThreadAbort = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HostTestThreadAbort);
- testADUnload = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HostTestADUnload);
#endif //_DEBUG
GCPollType GetGCPollType() { LIMITED_METHOD_CONTRACT; return iGCPollType; }
#ifdef _DEBUG
- DWORD GetHostTestADUnload() const {LIMITED_METHOD_CONTRACT; return testADUnload;}
DWORD GetHostTestThreadAbort() const {LIMITED_METHOD_CONTRACT; return testThreadAbort;}
#ifdef _DEBUG
DWORD fShouldInjectFault;
- DWORD testADUnload;
DWORD testThreadAbort;
#endif
return action;
}
-
-void EEPolicy::PerformADUnloadAction(EPolicyAction action, BOOL haveStack, BOOL forStackOverflow)
-{
- STATIC_CONTRACT_THROWS;
- STATIC_CONTRACT_GC_TRIGGERS;
- STATIC_CONTRACT_MODE_COOPERATIVE;
-
- STRESS_LOG0(LF_EH, LL_INFO100, "In EEPolicy::PerformADUnloadAction\n");
-
- Thread *pThread = GetThread();
-
- AppDomain *pDomain = GetAppDomain();
-
- if (!IsFinalizerThread())
- {
- int count = 0;
- Frame *pFrame = pThread->GetFirstTransitionInto(GetAppDomain(), &count);
- {
- pThread->SetUnloadBoundaryFrame(pFrame);
- }
- }
-
- pDomain->EnableADUnloadWorker(action==eUnloadAppDomain? ADU_Safe : ADU_Rude);
- // Can't perform a join when we are handling a true SO. We need to enable the unload woker but let the thread continue running
- // through EH processing so that we can recover the stack and reset the guard page.
- if (haveStack)
- {
- pThread->SetAbortRequest(action==eUnloadAppDomain? EEPolicy::TA_V1Compatible : EEPolicy::TA_Rude);
- if (forStackOverflow)
- {
- OBJECTREF exceptObj = CLRException::GetPreallocatedRudeThreadAbortException();
- pThread->SetAbortInitiated();
- RaiseTheExceptionInternalOnly(exceptObj, FALSE, TRUE);
- }
-
- OBJECTREF exceptObj = CLRException::GetPreallocatedThreadAbortException();
- pThread->SetAbortInitiated();
- RaiseTheExceptionInternalOnly(exceptObj, FALSE, FALSE);
- }
-}
-
void EEPolicy::PerformResourceConstraintAction(Thread *pThread, EPolicyAction action, UINT exitCode, BOOL haveStack)
{
WRAPPER_NO_CONTRACT;
case eRudeAbortThread:
pThread->UserAbort(Thread::TAR_Thread, TA_Rude, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal);
break;
- case eUnloadAppDomain:
- case eRudeUnloadAppDomain:
- {
- GCX_ASSERT_COOP();
- PerformADUnloadAction(action,haveStack);
- }
- break;
case eExitProcess:
case eFastExitProcess:
case eRudeExitProcess:
// But here we know that if we have only one page, we will only update states of the Domain.
CONTRACT_VIOLATION(SOToleranceViolation);
- // Mark the current domain requested for rude unload
- if (!fInDefaultDomain)
- {
- pCurrentDomain->EnableADUnloadWorker(ADU_Rude, FALSE);
- }
-
pThread->PrepareThreadForSOWork();
pThread->MarkThreadForAbort(
{
Thread* pThread = GetThread();
- if (pThread && pThread->PreemptiveGCDisabled())
- {
- // Mark the current domain requested for rude unload
- GCX_ASSERT_COOP();
- EEPolicy::PerformADUnloadAction(eRudeUnloadAppDomain, TRUE, TRUE);
- }
-
// We are leaving VM boundary, either entering managed code, or entering
// non-VM unmanaged code.
// We should not throw internal C++ exception. Instead we throw an exception
case eRudeAbortThread:
pThread->UserAbort(Thread::TAR_Thread, TA_Rude, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal);
break;
- case eUnloadAppDomain:
- // Register an appdomain unload, which starts on a separate thread.
- IfFailThrow(AppDomain::UnloadById(pCurrentDomain->GetId(), FALSE));
- // Don't continue execution on this thread.
- pThread->UserAbort(Thread::TAR_Thread, TA_Safe, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal);
- break;
- case eRudeUnloadAppDomain:
- pCurrentDomain->SetRudeUnload();
- // Register an appdomain unload, which starts on a separate thread.
- IfFailThrow(AppDomain::UnloadById(pCurrentDomain->GetId(), FALSE));
- // Don't continue execution on this thread.
- pThread->UserAbort(Thread::TAR_Thread, TA_Rude, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal);
- break;
-
case eExitProcess: // Merged w/ default case
default:
_ASSERTE(action == eExitProcess);
static void PerformResourceConstraintAction(Thread *pThread, EPolicyAction action, UINT exitCode, BOOL haveStack);
- static void PerformADUnloadAction(EPolicyAction action, BOOL haveStack, BOOL forStackOverflow = FALSE);
-
static void HandleOutOfMemory();
static void HandleStackOverflow(StackOverflowDetector detector, void * pLimitFrame);
TRACE_LEVEL_INFORMATION,
KEYWORDZERO))
{
- if(!pDomain->NoAccessToHandleTable())
- {
- DWORD enumerationOptions = ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords();
-
- // Domain unload also causes type unload events
- if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
- TRACE_LEVEL_INFORMATION,
- CLR_TYPE_KEYWORD))
- {
- enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::TypeUnload;
- }
+ DWORD enumerationOptions = ETW::EnumerationLog::GetEnumerationOptionsFromRuntimeKeywords();
- ETW::EnumerationLog::EnumerationHelper(NULL, pDomain, enumerationOptions);
+ // Domain unload also causes type unload events
+ if(ETW_TRACING_CATEGORY_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context,
+ TRACE_LEVEL_INFORMATION,
+ CLR_TYPE_KEYWORD))
+ {
+ enumerationOptions |= ETW::EnumerationLog::EnumerationStructs::TypeUnload;
}
+
+ ETW::EnumerationLog::EnumerationHelper(NULL, pDomain, enumerationOptions);
}
} EX_CATCH { } EX_END_CATCH(SwallowAllExceptions);
}
// ensure the App Domain does not get finalized until we're all done
SystemDomain::LockHolder lh;
- if (pAppDomain->IsFinalized())
- {
- return;
- }
-
- // Since we're not FINALIZED yet, the handle table should remain intact,
- // as should all type information in this AppDomain
- _ASSERTE(!pAppDomain->NoAccessToHandleTable());
-
// Now it's safe to do the iteration
IterateDomain(pAppDomain, enumerationOptions);
-
- // Since we're holding the system domain lock, the AD type info should be
- // there throughout the entire iteration we just did
- _ASSERTE(!pAppDomain->NoAccessToHandleTable());
}
/********************************************************************************/
Volatile<BOOL> g_TriggerHeapDump = FALSE;
#endif // __linux__
-AppDomain * FinalizerThread::UnloadingAppDomain;
-
CLREvent * FinalizerThread::hEventFinalizer = NULL;
CLREvent * FinalizerThread::hEventFinalizerDone = NULL;
CLREvent * FinalizerThread::hEventShutDownToFinalizer = NULL;
AppDomain* targetAppDomain = fobj->GetAppDomain();
AppDomain* currentDomain = pThread->GetDomain();
- if (! targetAppDomain || ! targetAppDomain->CanThreadEnter(pThread))
+ if (! targetAppDomain)
{
// if can't get into domain to finalize it, then it must be agile so finalize in current domain
targetAppDomain = currentDomain;
if (targetAppDomain == currentDomain)
{
- if (!targetAppDomain->IsRudeUnload() ||
- fobj->GetMethodTable()->HasCriticalFinalizer())
+ class ResetFinalizerStartTime
{
- class ResetFinalizerStartTime
+ public:
+ ResetFinalizerStartTime()
{
- public:
- ResetFinalizerStartTime()
+ if (CLRHosted())
{
- if (CLRHosted())
- {
- g_ObjFinalizeStartTime = CLRGetTickCount64();
- }
- }
- ~ResetFinalizerStartTime()
- {
- if (g_ObjFinalizeStartTime)
- {
- g_ObjFinalizeStartTime = 0;
- }
- }
- };
+ g_ObjFinalizeStartTime = CLRGetTickCount64();
+ }
+ }
+ ~ResetFinalizerStartTime()
{
- ThreadLocaleHolder localeHolder;
-
+ if (g_ObjFinalizeStartTime)
{
- ResetFinalizerStartTime resetTime;
- CallFinalizer(fobj);
+ g_ObjFinalizeStartTime = 0;
}
}
- pThread->InternalReset(FALSE);
+ };
+ {
+ ThreadLocaleHolder localeHolder;
+
+ {
+ ResetFinalizerStartTime resetTime;
+ CallFinalizer(fobj);
+ }
}
+ pThread->InternalReset(FALSE);
}
else
{
- if (! targetAppDomain->GetDefaultContext())
- {
- // Can no longer enter domain becuase the handle containing the context has been
- // destroyed so just bail. Should only get this if are at the stage of nuking the
- // handles in the domain if it's still open.
- _ASSERTE(targetAppDomain->IsUnloading() && targetAppDomain->ShouldHaveFinalization());
- }
- else if (!currentDomain->IsDefaultDomain())
+ _ASSERTE(targetAppDomain->GetDefaultContext());
+
+ if (!currentDomain->IsDefaultDomain())
{
// this means we are in some other domain, so need to return back out through the DoADCallback
// and handle the object from there in another domain.
if (fobj == NULL)
{
- if (AppDomain::HasWorkForFinalizerThread())
- {
- return NULL;
- }
fobj = GCHeapUtilities::GetGCHeap()->GetNextFinalizable();
}
if (fobj->GetHeader()->GetBits() & bitToCheck)
{
- if (AppDomain::HasWorkForFinalizerThread())
- {
- return NULL;
- }
fobj = GCHeapUtilities::GetGCHeap()->GetNextFinalizable();
}
else
if (fobj == NULL)
{
- if (AppDomain::HasWorkForFinalizerThread())
- {
- return NULL;
- }
fobj = GCHeapUtilities::GetGCHeap()->GetNextFinalizable();
}
}
GetFinalizerThread()->EEResetAbort(Thread::TAR_ALL);
}
FastInterlockExchange ((LONG*)&g_FinalizerIsRunning, TRUE);
- AppDomain::EnableADUnloadWorkerForFinalizer();
-
- do
- {
- FinalizeAllObjects(NULL, 0);
- _ASSERTE(GetFinalizerThread()->GetDomain()->IsDefaultDomain());
- if (AppDomain::HasWorkForFinalizerThread())
- {
- AppDomain::ProcessUnloadDomainEventOnFinalizeThread();
- }
- else if (UnloadingAppDomain == NULL)
- break;
- else if (!GCHeapUtilities::GetGCHeap()->FinalizeAppDomain(UnloadingAppDomain, !!fRunFinalizersOnUnload))
- {
- break;
- }
- // Now schedule any objects from an unloading app domain for finalization
- // on the next pass (even if they are reachable.)
- // Note that it may take several passes to complete the unload, if new objects are created during
- // finalization.
- }
- while(TRUE);
-
- if (UnloadingAppDomain != NULL)
- {
- SyncBlockCache::GetSyncBlockCache()->CleanupSyncBlocksInAppDomain(UnloadingAppDomain);
- {
- // Before we continue with AD unloading, mark the stage as
- // FINALIZED under the SystemDomain lock so that this portion
- // of unloading may be serialized with other parts of the CLR
- // that require the AD stage to be < FINALIZED, in particular
- // ETW's AD enumeration code used during its rundown events.
- SystemDomain::LockHolder lh;
- UnloadingAppDomain->SetFinalized(); // All finalizers have run except for FinalizableAndAgile objects
- }
- UnloadingAppDomain = NULL;
- }
+ FinalizeAllObjects(NULL, 0);
+ _ASSERTE(GetFinalizerThread()->GetDomain()->IsDefaultDomain());
FastInterlockExchange ((LONG*)&g_FinalizerIsRunning, FALSE);
// We may still have the finalizer thread for abort. If so the abort request is for previous finalizer method, not for next one.
if (status == WAIT_TIMEOUT)
{
ULONGLONG finalizeStartTime = GetObjFinalizeStartTime();
- if (finalizeStartTime || AppDomain::HasWorkForFinalizerThread())
+ if (finalizeStartTime)
{
if (CLRGetTickCount64() >= finalizeStartTime+timeout)
{
{
static BOOL fRunFinalizersOnUnload;
static BOOL fQuitFinalizer;
- static AppDomain *UnloadingAppDomain;
#if defined(__linux__) && defined(FEATURE_EVENT_TRACE)
static ULONGLONG LastHeapDumpTime;
return g_pFinalizerThread;
}
- // Start unloading app domain
- static void UnloadAppDomain(AppDomain *pDomain, BOOL fRunFinalizers)
- {
- LIMITED_METHOD_CONTRACT;
- UnloadingAppDomain = pDomain;
- fRunFinalizersOnUnload = fRunFinalizers;
- }
-
- static AppDomain* GetUnloadingAppDomain()
- {
- LIMITED_METHOD_CONTRACT;
- return UnloadingAppDomain;
- }
-
static BOOL IsCurrentThreadFinalizer();
static void EnableFinalization();
}
CONTRACTL_END;
- // Update AppDomain stage here.
- SystemDomain::System()->ProcessClearingDomains();
-
#ifdef VERIFY_HEAP
// Validate byrefs pinned by IL stubs since the last GC.
StubHelpers::ProcessByrefValidationList();
ADIndex index(appDomainID);
AppDomain *pDomain = SystemDomain::GetAppDomainAtIndex(index);
- return (pDomain != NULL) && !pDomain->NoAccessToHandleTable();
+ return (pDomain != NULL);
}
uint32_t GCToEEInterface::GetIndexOfAppDomainBeingUnloaded()
{
LIMITED_METHOD_CONTRACT;
- return SystemDomain::IndexOfAppDomainBeingUnloaded().m_dwIndex;
+ return 0xFFFFFFFF;
}
uint32_t GCToEEInterface::GetTotalNumSizedRefHandles()
{
LIMITED_METHOD_CONTRACT;
- AppDomain *realPtr = static_cast<AppDomain *>(appDomain);
- return realPtr->IsRudeUnload() != FALSE;
+ return false;
}
bool GCToEEInterface::AnalyzeSurvivorsRequested(int condemnedGeneration)
// Access to a handle in an unloaded domain is not allowed
assert(domain != nullptr);
- assert(!domain->NoAccessToHandleTable());
#endif // _DEBUG_IMPL
}
IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager();
ADIndex appDomainIndex = ADIndex(reinterpret_cast<DWORD>(mgr->GetHandleContext(handle)));
- AppDomain *unloadingDomain = SystemDomain::AppDomainBeingUnloaded();
- if (unloadingDomain && unloadingDomain->GetIndex() == appDomainIndex && unloadingDomain->NoAccessToHandleTable())
- {
- _ASSERTE (!"Access to a handle in unloaded domain is not allowed");
- }
-
ValidateObjectAndAppDomain(objRef, appDomainIndex);
#endif // _DEBUG_IMPL
}
#ifndef CROSSGEN_COMPILE
// from clr/src/vm/threads.h
-#define Thread_m_Context 0x38
+#define Thread_m_Context 0x34
ASMCONSTANTS_C_ASSERT(Thread_m_Context == offsetof(Thread, m_Context))
#define Thread_m_State 0x04
#endif // CROSSGEN_COMPILE
#ifndef CROSSGEN_COMPILE
-#define Thread_m_dwLockCount 0x18
+#define Thread_m_dwLockCount 0x14
ASMCONSTANTS_C_ASSERT(Thread_m_dwLockCount == offsetof(Thread, m_dwLockCount))
-#define Thread_m_ThreadId 0x1C
+#define Thread_m_ThreadId 0x18
ASMCONSTANTS_C_ASSERT(Thread_m_ThreadId == offsetof(Thread, m_ThreadId))
#ifdef FEATURE_HIJACK
#endif //FEATURE_STUBS_AS_IL
#ifndef CROSSGEN_COMPILE
-#define Thread__m_pDomain 0x14
+#define Thread__m_pDomain 0x10
ASMCONSTANTS_C_ASSERT(Thread__m_pDomain == offsetof(Thread, m_pDomain));
#endif
jmp C_FUNC(ThePreStub)
LEAF_END PrecodeFixupThunk, _TEXT
-// void __stdcall UM2MThunk_WrapperHelper(void *pThunkArgs,
-// int argLen,
-// void *pAddr,
-// UMEntryThunk *pEntryThunk,
-// Thread *pThread)
-NESTED_ENTRY UM2MThunk_WrapperHelper, _TEXT, NoHandler
- push ebx
-
- mov eax, [esp + 20] // pEntryThunk
- mov ecx, [esp + 24] // pThread
- mov ebx, [esp + 8] // pThunkArgs
-
- sub esp, 8
- CHECK_STACK_ALIGNMENT
- call [esp + 16 + 8] // pAddr
- add esp, 8
-
- pop ebx
-
- ret 20
-NESTED_END UM2MThunk_WrapperHelper, _TEXT
-
NESTED_ENTRY UMThunkStubRareDisable, _TEXT, NoHandler
push eax
push ecx
retn 8
_getFPReturn@8 endp
-; void __stdcall UM2MThunk_WrapperHelper(void *pThunkArgs,
-; int argLen,
-; void *pAddr,
-; UMEntryThunk *pEntryThunk,
-; Thread *pThread)
-UM2MThunk_WrapperHelper proc stdcall public,
- pThunkArgs : DWORD,
- argLen : DWORD,
- pAddr : DWORD,
- pEntryThunk : DWORD,
- pThread : DWORD
- UNREFERENCED argLen
-
- push ebx
-
- mov eax, pEntryThunk
- mov ecx, pThread
- mov ebx, pThunkArgs
- call pAddr
-
- pop ebx
-
- ret
-UM2MThunk_WrapperHelper endp
-
; VOID __cdecl UMThunkStubRareDisable()
;<TODO>
; @todo: this is very similar to StubRareDisable
SO_TOLERANT;
} CONTRACTL_END;
- return m_Id.GetAppDomain()->CanUnload();
+ return FALSE;
}
BOOL AssemblyLoaderAllocator::CanUnload()
CONTRACTL_END;
_ASSERTE(GetDomain()->IsAppDomain());
- _ASSERTE(!GetDomain()->AsAppDomain()->NoAccessToHandleTable());
// This method doesn't take a lock around RemoveHead because it's supposed to
// be called only from Terminate
if (pWrap == NULL)
COMPlusThrowOM();
- AppDomainFromIDHolder pDomain(pWrap->GetDomainID(), FALSE);
- pDomain.ThrowIfUnloaded();
if (fIsWeak != 0)
pWrap->MarkHandleWeak();
else
pWrap->ResetHandleStrength();
- pDomain.Release();
}
HELPER_METHOD_FRAME_END();
NOINLINE BYTE *MethodTable::GetLoaderAllocatorObjectForGC()
{
WRAPPER_NO_CONTRACT;
- if (!Collectible() || ((PTR_AppDomain)GetLoaderModule()->GetDomain())->NoAccessToHandleTable())
+ if (!Collectible())
{
return NULL;
}
g_pOverlappedDataClass = MscorlibBinder::GetClass(CLASS__OVERLAPPEDDATA);
// We have optimization to avoid creating event if IO is in default domain. This depends on default domain
// can not be unloaded.
- _ASSERTE(IsSingleAppDomain() || !SystemDomain::System()->DefaultDomain()->CanUnload());
+ _ASSERTE(IsSingleAppDomain());
_ASSERTE(SystemDomain::System()->DefaultDomain()->GetId().m_dwId == DefaultADID);
}
Module * pLoaderModule = pMTToCheck->GetLoaderModule();
BaseDomain * pBaseDomain = pLoaderModule->GetDomain();
- if ((pBaseDomain != NULL) &&
- (pBaseDomain->IsAppDomain()) &&
- (pBaseDomain->AsAppDomain()->IsUnloading()))
- {
- return NULL;
- }
// Don't look up types that are unloading due to Collectible Assemblies. Haven't been
// able to find a case where we actually encounter objects like this that can cause
// AD available in catch-up enumeration
// < AppDomainCreationFinished issued
// < AD NOT available from catch-up enumeration
- // < AppDomainShutdownStarted issued
//
// The AppDomainIterator constructor parameter m_bActive is set to be TRUE below,
- // meaning only AppDomains in the range [STAGE_ACTIVE;STAGE_CLOSED) will be included
+ // meaning only AppDomains in stage STAGE_ACTIVE or higher will be included
// in the iteration.
// * AppDomainCreationFinished (with S_OK hrStatus) is issued once the AppDomain
// reaches STAGE_ACTIVE.
- // * AppDomainShutdownStarted is issued while the AppDomain is in STAGE_EXITED,
- // just before it hits STAGE_FINALIZING. (STAGE_EXITED < STAGE_CLOSED)
- // * To prevent AppDomains from appearing in the enumeration after we would have
- // sent the AppDomainShutdownStarted event for them, we must add an
- // additional check in the enumeration loop to exclude ADs such that
- // pAppDomain->IsUnloading() (i.e., > STAGE_UNLOAD_REQUESTED). Thus, for an
- // AD for which AppDomainShutdownStarted callback is issued, we have AD >=
- // STAGE_EXITED > STAGE_UNLOAD_REQUESTED, and thus, that AD will be excluded
- // by the pAppDomain->IsUnloading() check.
AppDomainIterator appDomainIterator(TRUE);
while (appDomainIterator.Next())
{
AppDomain * pAppDomain = appDomainIterator.GetDomain();
- if (pAppDomain->IsUnloading())
- {
- // Must skip app domains that are in the process of unloading, to ensure
- // the rules around which entities the profiler should find in the
- // enumeration. See code:#ProfilerEnumAppDomains for details.
- continue;
- }
// Of course, the AD could start unloading here, but if it does we're guaranteed
// the profiler has had a chance to see the Unload callback for the AD, and thus
// * AssemblyLoadFinished is issued once the Assembly reaches
// code:FILE_LOAD_LOADLIBRARY
// * AssemblyUnloadStarted is issued as a result of either:
- // * AppDomain unloading. In this case such assemblies / modules would be
- // excluded by the AD iterator above, because it excludes ADs if
- // pAppDomain->IsUnloading()
// * Collectible assemblies unloading. Such assemblies will no longer be
// enumerable.
//
{
pThread->DisablePreemptiveGC();
}
- // PerformADUnloadAction is SO_INTOLERANT, but we might be
- // calling BEGIN_SO_TOLERANT_CODE from an entry point method
- BEGIN_CONTRACT_VIOLATION(SOToleranceViolation);
- BEGIN_GCX_ASSERT_COOP;
- // We have enough stack now. Start unload
- EEPolicy::PerformADUnloadAction(eRudeUnloadAppDomain, TRUE, TRUE);
- END_GCX_ASSERT_COOP;
- END_CONTRACT_VIOLATION;
}
COMPlusThrowSO();
}
}
if (bPresent != FALSE)
{
- AppDomainFromIDHolder pDomain(adId, FALSE);
- if (!pDomain.IsUnloaded())
- {
- return adId;
- }
+ return adId;
}
return defaultId;
}
SimpleComCallWrapper *pSimpleWrap = pWrap->GetSimpleWrapper();
- AppDomainFromIDHolder ad((ADID)pSimpleWrap->GetRawDomainID(), TRUE);
-
- if (ad.IsUnloaded() || ad->IsUnloading())
- return COR_E_APPDOMAINUNLOADED;
-
//
// For CCWs that have outstanding Jupiter-reference, they could be either:
// 1. Neutered - in this case it is unsafe to touch m_ppThis
HRESULT CLRTestHookManager::UnloadAppDomain(DWORD adid,DWORD flags)
{
- HRESULT hr = S_OK;
- BEGIN_ENTRYPOINT_NOTHROW;
- // We do not use BEGIN_EXTERNAL_ENTRYPOINT here because
- // we do not want to setup Thread. Process may be OOM, and we want Unload
- // to work.
- if (flags==ADUF_FORCEFULLGC)
- {
- SystemDomain::LockHolder ulh;
- ADID id(adid);
- AppDomainFromIDHolder pApp(id,TRUE,AppDomainFromIDHolder::SyncType_ADLock);//, AppDomainFromIDHolder::SyncType_ADLock);
- if(!pApp.IsUnloaded())
- pApp->SetForceGCOnUnload(TRUE);
- }
- hr = AppDomain::UnloadById(ADID(adid), flags!=ADUF_ASYNCHRONOUS,TRUE);
- END_ENTRYPOINT_NOTHROW;
- return hr;
+ return COR_E_CANNOTUNLOADAPPDOMAIN;
}
VOID CLRTestHookManager::DoAppropriateWait( int cObjs, HANDLE *pObjs, INT32 iTimeout, BOOL bWaitAll, int* res)
if ((ADValidityKind & ADV_FINALIZER) &&
IsFinalizerThread())
return;
- if ((ADValidityKind & ADV_ADUTHREAD) &&
- IsADUnloadHelperThread())
- return;
if ((ADValidityKind & ADV_RUNNINGIN) &&
pDomain->IsRunningIn(GetThread()))
return;
CONTRACTL_END;
m_pFrame = FRAME_TOP;
- m_pUnloadBoundaryFrame = NULL;
m_fPreemptiveGCDisabled = 0;
}
FinishSOWork();
- //GetAppDomain()->EnableADUnloadWorker(EEPolicy::ADU_Rude);
INDEBUG(DebugLogStackMBIs());
Thread* _ctx_trans_pThread = GetThread();
TESTHOOKCALL(EnteringAppDomain((TargetDomain.m_dwId)));
- AppDomainFromIDHolder pTargetDomain(TargetDomain, TRUE);
- pTargetDomain.ThrowIfUnloaded();
+ AppDomain* pTargetDomain = SystemDomain::GetAppDomainFromId(TargetDomain, ADV_CURRENTAD);
_ASSERTE(_ctx_trans_pThread != NULL);
_ASSERTE(_ctx_trans_pThread->GetDomain()->GetId()!= TargetDomain);
pTargetDomain->GetDefaultContext(),
_ctx_trans_pFrame);
- pTargetDomain.Release();
args->pCtxFrame = _ctx_trans_pFrame;
TESTHOOKCALL(EnteredAppDomain((TargetDomain.m_dwId)));
/* work around unreachable code warning */
Context* pCurrDefCtx = pCurrDomain->GetDefaultContext();
BOOL bDefaultTargetCtx=FALSE;
- {
- AppDomainFromIDHolder ad(appDomain, TRUE);
- ad.ThrowIfUnloaded();
- bDefaultTargetCtx=(ad->GetDefaultContext()==pContext);
- }
+ AppDomain* ad = SystemDomain::GetAppDomainFromId(appDomain, ADV_CURRENTAD);
+ bDefaultTargetCtx=(ad->GetDefaultContext()==pContext);
if (pCurrDefCtx == pThread->GetContext() && bDefaultTargetCtx)
{
LOG((LF_APPDOMAIN, LL_INFO100, "Thread::DoADCallBack Done at esp %p\n", espVal));
}
-
-void Thread::DoADCallBack(AppDomain* pDomain , Context::ADCallBackFcnType pTarget, LPVOID args, DWORD dwADV,
- BOOL fSetupEHAtTransition /* = TRUE */)
-{
-
-
-#ifdef _DEBUG
- TADDR espVal = (TADDR)GetCurrentSP();
-
- LOG((LF_APPDOMAIN, LL_INFO100, "Thread::DoADCallBack Calling %p at esp %p in [%d]\n",
- pTarget, espVal, pDomain->GetId().m_dwId));
-#endif
- Thread* pThread = GetThread();
-
- // Get the default context for the current domain as well as for the
- // destination domain.
- AppDomain* pCurrDomain = pThread->GetContext()->GetDomain();
-
- if (pCurrDomain!=pDomain)
- {
- // use the target domain's default context as the target context
- // so that the actual call to a transparent proxy would enter the object into the correct context.
-
- BOOL fThrow = FALSE;
-
-#ifdef FEATURE_PAL
- // FEATURE_PAL must setup EH at AD transition - the option to omit the setup
- // is only for regular Windows builds.
- _ASSERTE(fSetupEHAtTransition);
-#endif // FEATURE_PAL
-
- LOG((LF_APPDOMAIN, LL_INFO10, "Thread::DoADCallBack - performing AD transition with%s EH at transition boundary.\n",
- (fSetupEHAtTransition == FALSE)?"out":""));
-
- if (fSetupEHAtTransition)
- {
- ENTER_DOMAIN_PTR(pDomain,dwADV)
- {
- (pTarget)(args);
-
- // unloadBoundary is cleared by ReturnToContext, so get it now.
- Frame* unloadBoundaryFrame = pThread->GetUnloadBoundaryFrame();
- fThrow = pThread->ShouldChangeAbortToUnload(GET_CTX_TRANSITION_FRAME(), unloadBoundaryFrame);
- }
- END_DOMAIN_TRANSITION;
- }
-#ifndef FEATURE_PAL
- else
- {
- ENTER_DOMAIN_PTR_NO_EH_AT_TRANSITION(pDomain,dwADV)
- {
- (pTarget)(args);
-
- // unloadBoundary is cleared by ReturnToContext, so get it now.
- Frame* unloadBoundaryFrame = pThread->GetUnloadBoundaryFrame();
- fThrow = pThread->ShouldChangeAbortToUnload(GET_CTX_TRANSITION_FRAME(), unloadBoundaryFrame);
- }
- END_DOMAIN_TRANSITION_NO_EH_AT_TRANSITION;
- }
-#endif // !FEATURE_PAL
-
- // if someone caught the abort before it got back out to the AD transition (like DispatchEx_xxx does)
- // then need to turn the abort into an unload, as they're gonna keep seeing it anyway
- if (fThrow)
- {
- LOG((LF_APPDOMAIN, LL_INFO10, "Thread::DoADCallBack turning abort into unload\n"));
- COMPlusThrow(kAppDomainUnloadedException, W("Remoting_AppDomainUnloaded_ThreadUnwound"));
- }
- }
- else
- {
- UNREACHABLE();
- }
- LOG((LF_APPDOMAIN, LL_INFO100, "Thread::DoADCallBack Done at esp %p\n", espVal));
-}
-
-void Thread::DoADCallBack(ADID appDomainID , Context::ADCallBackFcnType pTarget, LPVOID args, BOOL fSetupEHAtTransition /* = TRUE */)
-{
-
-
-#ifdef _DEBUG
- TADDR espVal = (TADDR)GetCurrentSP();
-
- LOG((LF_APPDOMAIN, LL_INFO100, "Thread::DoADCallBack Calling %p at esp %p in [%d]\n",
- pTarget, espVal, appDomainID.m_dwId));
-#endif
- Thread* pThread = GetThread();
-
- // Get the default context for the current domain as well as for the
- // destination domain.
- AppDomain* pCurrDomain = pThread->GetContext()->GetDomain();
-
- if (pCurrDomain->GetId()!=appDomainID)
- {
- // use the target domain's default context as the target context
- // so that the actual call to a transparent proxy would enter the object into the correct context.
-
- BOOL fThrow = FALSE;
-
-#ifdef FEATURE_PAL
- // FEATURE_PAL must setup EH at AD transition - the option to omit the setup
- // is only for regular Windows builds.
- _ASSERTE(fSetupEHAtTransition);
-#endif // FEATURE_PAL
-
- LOG((LF_APPDOMAIN, LL_INFO10, "Thread::DoADCallBack - performing AD transition with%s EH at transition boundary.\n",
- (fSetupEHAtTransition == FALSE)?"out":""));
-
- if (fSetupEHAtTransition)
- {
- ENTER_DOMAIN_ID(appDomainID)
- {
- (pTarget)(args);
-
- // unloadBoundary is cleared by ReturnToContext, so get it now.
- Frame* unloadBoundaryFrame = pThread->GetUnloadBoundaryFrame();
- fThrow = pThread->ShouldChangeAbortToUnload(GET_CTX_TRANSITION_FRAME(), unloadBoundaryFrame);
- }
- END_DOMAIN_TRANSITION;
- }
-#ifndef FEATURE_PAL
- else
- {
- ENTER_DOMAIN_ID_NO_EH_AT_TRANSITION(appDomainID)
- {
- (pTarget)(args);
-
- // unloadBoundary is cleared by ReturnToContext, so get it now.
- Frame* unloadBoundaryFrame = pThread->GetUnloadBoundaryFrame();
- fThrow = pThread->ShouldChangeAbortToUnload(GET_CTX_TRANSITION_FRAME(), unloadBoundaryFrame);
- }
- END_DOMAIN_TRANSITION_NO_EH_AT_TRANSITION;
- }
-#endif // !FEATURE_PAL
-
- // if someone caught the abort before it got back out to the AD transition (like DispatchEx_xxx does)
- // then need to turn the abort into an unload, as they're gonna keep seeing it anyway
- if (fThrow)
- {
- LOG((LF_APPDOMAIN, LL_INFO10, "Thread::DoADCallBack turning abort into unload\n"));
- COMPlusThrow(kAppDomainUnloadedException, W("Remoting_AppDomainUnloaded_ThreadUnwound"));
- }
- }
- else
- {
- UNREACHABLE();
- }
- LOG((LF_APPDOMAIN, LL_INFO100, "Thread::DoADCallBack Done at esp %p\n", espVal));
-}
-
void Thread::EnterContextRestricted(Context *pContext, ContextTransitionFrame *pFrame)
{
CONTRACTL {
// and it should always have an AD set
_ASSERTE(pDomain);
- if (m_pDomain != pDomain && !pDomain->CanThreadEnter(this))
- {
- pFrame->SetReturnContext(NULL);
- COMPlusThrow(kAppDomainUnloadedException);
- }
-
pFrame->SetReturnContext(m_Context);
pFrame->SetReturnExecutionContext(NULL);
EPolicyAction action = GetEEPolicy()->GetActionOnFailure(FAIL_OrphanedLock);
switch (action)
{
- case eUnloadAppDomain:
- if (!pFromDomain->IsDefaultDomain())
- {
- pFromDomain->EnableADUnloadWorker(EEPolicy::ADU_Safe);
- }
- break;
- case eRudeUnloadAppDomain:
- if (!pFromDomain->IsDefaultDomain())
- {
- pFromDomain->EnableADUnloadWorker(EEPolicy::ADU_Rude);
- }
- break;
case eExitProcess:
case eFastExitProcess:
case eRudeExitProcess:
m_pDomain = pReturnDomain;
SetAppDomain(pReturnDomain);
- if (pFrame == m_pUnloadBoundaryFrame)
- {
- m_pUnloadBoundaryFrame = NULL;
- if (IsAbortRequested())
- {
- EEResetAbort(TAR_ADUnload);
- }
- ResetBeginAbortedForADUnload();
- }
-
// Restore the last thrown object to what it was before the AD transition. Note that if
// an exception was thrown out of the AD we transitionned into, it will be raised in
// RaiseCrossContextException and the EH system will store it as the last thrown
// _ASSERTE (action == eThrowException || !pReturnDomain->IsDefaultDomain());
switch (action)
{
- case eUnloadAppDomain:
- if (!pReturnDomain->IsDefaultDomain())
- {
- pReturnDomain->EnableADUnloadWorker(EEPolicy::ADU_Safe);
- }
- break;
- case eRudeUnloadAppDomain:
- if (!pReturnDomain->IsDefaultDomain())
- {
- pReturnDomain->EnableADUnloadWorker(EEPolicy::ADU_Rude);
- }
- break;
case eExitProcess:
case eFastExitProcess:
case eRudeExitProcess:
return pDomain;
}
-#ifndef DACCESS_COMPILE
-void Thread::SetUnloadBoundaryFrame(Frame *pFrame)
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE((this == GetThread() && PreemptiveGCDisabled()) ||
- ThreadStore::HoldingThreadStore());
- if ((ULONG_PTR)m_pUnloadBoundaryFrame < (ULONG_PTR)pFrame)
- {
- m_pUnloadBoundaryFrame = pFrame;
- }
- if (pFrame == NULL)
- {
- ResetBeginAbortedForADUnload();
- }
-}
-
-void Thread::ResetUnloadBoundaryFrame()
-{
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(this == GetThread() && PreemptiveGCDisabled());
- m_pUnloadBoundaryFrame=NULL;
- ResetBeginAbortedForADUnload();
-}
-
-#endif
-
-BOOL Thread::ShouldChangeAbortToUnload(Frame *pFrame, Frame *pUnloadBoundaryFrame)
-{
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- if (! pUnloadBoundaryFrame)
- pUnloadBoundaryFrame = GetUnloadBoundaryFrame();
-
- // turn the abort request into an AD unloaded exception when go past the boundary.
- if (pFrame != pUnloadBoundaryFrame)
- return FALSE;
-
- // Only time have an unloadboundaryframe is when have specifically marked that thread for aborting
- // during unload processing, so this won't trigger UnloadedException if have simply thrown a ThreadAbort
- // past an AD transition frame
- _ASSERTE (IsAbortRequested());
-
- EEResetAbort(TAR_ADUnload);
-
- if (m_AbortType == EEPolicy::TA_None)
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
-}
-
BOOL Thread::HaveExtraWorkForFinalizer()
{
LIMITED_METHOD_CONTRACT;
|| ExecutionManager::IsCacheCleanupRequired()
|| Thread::CleanupNeededForFinalizedThread()
|| (m_DetachCount > 0)
- || AppDomain::HasWorkForFinalizerThread()
|| SystemDomain::System()->RequireAppDomainCleanup()
|| ThreadStore::s_pThreadStore->ShouldTriggerGCForDeadThreads();
}
}
#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
- if (AppDomain::HasWorkForFinalizerThread())
- {
- AppDomain::ProcessUnloadDomainEventOnFinalizeThread();
- }
-
if (RequireSyncBlockCleanup())
{
#ifndef FEATURE_PAL
}
if (SystemDomain::System()->RequireAppDomainCleanup())
{
- SystemDomain::System()->ProcessDelayedUnloadDomains();
+ SystemDomain::System()->ProcessDelayedUnloadLoaderAllocators();
}
if(m_DetachCount > 0 || Thread::CleanupNeededForFinalizedThread())
static void ManagedThreadBase_DispatchOuter(ManagedThreadCallState *pCallState);
-
-// Here's the tricky part. *IF and only IF* we took an AppDomain transition at the base, then we
-// now want to push another complete set of handlers above us. The reason is that we want the
-// Watson report and the unhandled exception event to occur in the target AppDomain. If we don't
-// do this apparently redundant push of handlers, then we will marshal back the exception to the
-// handlers on the Default AppDomain side. This will erase all the important exception state by
-// unwinding (catch and rethrow) in DoADCallBack. And it will cause all unhandled exceptions to
-// be reported from the Default AppDomain, which is annoying to any AppDomain.UnhandledException
-// event listeners.
-//
-// So why not skip the handlers that are in the Default AppDomain and just push the ones after the
-// transition? Well, transitioning out of the Default AppDomain into the target AppDomain could
-// fail. We need handlers pushed for that case. And in that case it's perfectly reasonable to
-// report the problem as occurring in the Default AppDomain, which is what the base handlers will
-// do.
-
-static void ManagedThreadBase_DispatchInCorrectAD(LPVOID args)
-{
- CONTRACTL
- {
- GC_TRIGGERS;
- THROWS;
- MODE_COOPERATIVE;
- }
- CONTRACTL_END;
-
- ManagedThreadCallState *pCallState = (ManagedThreadCallState *) args;
-
- // Ensure we aren't going to infinitely recurse.
- _ASSERTE(pCallState->IsAppDomainEqual(GetThread()->GetDomain()));
-
- // And then go round one more time. But this time we want to ensure that the filter contains
- // any exceptions that aren't swallowed. These must be treated as unhandled, rather than
- // propagated through the AppDomain boundary in search of an outer handler. Otherwise we
- // will not get correct Watson behavior.
- pCallState->flags = MTCSF_ContainToAppDomain;
- ManagedThreadBase_DispatchOuter(pCallState);
- pCallState->flags = MTCSF_NormalBase;
-}
-
static void ManagedThreadBase_DispatchInner(ManagedThreadCallState *pCallState)
{
CONTRACTL
}
CONTRACTL_END;
-
- Thread *pThread = GetThread();
-
- if (!pCallState->IsAppDomainEqual(pThread->GetDomain()))
- {
- // On Win7 and later, AppDomain transitions at the threadbase will *not* have EH setup at transition boundary.
- // This implies that an unhandled exception from the base domain (i.e. AD in which the thread starts) will
- // not return to DefDomain but will continue to go up the stack with the thread still being in base domain.
- // We have a holder in ENTER_DOMAIN_*_NO_EH_AT_TRANSITION macro (ReturnToPreviousAppDomainHolder) that will
- // revert AD context at threadbase if an unwind is triggered after the exception has gone unhandled.
- //
- // This also implies that there will be no exception object marshalling (and it may not be required after all)
- // as well and once the holder reverts the AD context, the LastThrownObject in Thread will be set to NULL.
-#ifndef FEATURE_PAL
- BOOL fSetupEHAtTransition = FALSE;
-#else // !FEATURE_PAL
- BOOL fSetupEHAtTransition = TRUE;
-#endif // !FEATURE_PAL
-
- if (pCallState->bDomainIsAsID)
- pThread->DoADCallBack(pCallState->pAppDomainId,
- ManagedThreadBase_DispatchInCorrectAD,
- pCallState, fSetupEHAtTransition);
- else
- pThread->DoADCallBack(pCallState->pUnsafeAppDomain,
- ManagedThreadBase_DispatchInCorrectAD,
- pCallState, ADV_FINALIZER, fSetupEHAtTransition);
- }
- else
- {
- // Since no AppDomain transition is necessary, we need no additional handlers pushed
- // *AFTER* the transition. We now have adequate handlers below us. Go ahead and
- // dispatch the call.
- (*pCallState->pTarget) (pCallState->args);
- }
+ // Go ahead and dispatch the call.
+ (*pCallState->pTarget) (pCallState->args);
}
static void ManagedThreadBase_DispatchMiddle(ManagedThreadCallState *pCallState)
{
TaskType = TT_THREADPOOL_WAIT;
}
- else if (type & ThreadType_ADUnloadHelper)
- {
- TaskType = TT_ADUNLOAD;
- }
else if (type & ThreadType_Threadpool_IOCompletion)
{
TaskType = TT_THREADPOOL_IOCOMPLETION;
#define ADV_COMPILATION 0x10
// finalizer thread - synchronized with ADU
#define ADV_FINALIZER 0x40
-// adu thread - cannot race with itself
-#define ADV_ADUTHREAD 0x80
// held by AppDomainRefTaker
#define ADV_REFTAKER 0x100
Volatile<ULONG> m_fPreemptiveGCDisabled;
PTR_Frame m_pFrame; // The Current Frame
- PTR_Frame m_pUnloadBoundaryFrame;
//-----------------------------------------------------------
// If the thread has wandered in from the outside this is
bool DetectHandleILStubsForDebugger();
-#ifndef DACCESS_COMPILE
- void SetUnloadBoundaryFrame(Frame *pFrame);
- void ResetUnloadBoundaryFrame();
-#endif
-
- PTR_Frame GetUnloadBoundaryFrame()
- {
- LIMITED_METHOD_CONTRACT;
- return m_pUnloadBoundaryFrame;
- }
-
void SetWin32FaultAddress(DWORD eip)
{
LIMITED_METHOD_CONTRACT;
return m_Context;
}
-
- // This callback is used when we are executing in the EE and discover that we need
- // to switch appdomains.
- //
- // Set the last parameter to FALSE if you want to perform the AD transition *without*
- // EH (this can affect marshalling of exceptions).
- void DoADCallBack(ADID appDomain , Context::ADCallBackFcnType pTarget, LPVOID args, BOOL fSetupEHAtTransition = TRUE);
- void DoADCallBack(AppDomain* pDomain , Context::ADCallBackFcnType pTarget, LPVOID args, DWORD dwADV, BOOL fSetupEHAtTransition = TRUE);
void DoContextCallBack(ADID appDomain, Context* c , Context::ADCallBackFcnType pTarget, LPVOID args);
// Except for security and the call in from the remoting code in mscorlib, you should never do an
Frame *IsRunningIn(AppDomain* pDomain, int *count);
Frame *GetFirstTransitionInto(AppDomain *pDomain, int *count);
- BOOL ShouldChangeAbortToUnload(Frame *pFrame, Frame *pUnloadBoundaryFrame=NULL);
-
// Get outermost (oldest) AppDomain for this thread.
AppDomain *GetInitialDomain();
enum ThreadAbortRequester
{
TAR_Thread = 0x00000001, // Request by Thread
- TAR_ADUnload = 0x00000002, // Request by AD unload
TAR_FuncEval = 0x00000004, // Request by Func-Eval
TAR_StackOverflow = 0x00000008, // Request by StackOverflow. TAR_THREAD should be set at the same time.
TAR_ALL = 0xFFFFFFFF,
TAI_FuncEvalAbort = 0x00000040,
TAI_FuncEvalV1Abort = 0x00000080,
TAI_FuncEvalRudeAbort = 0x00000100,
- TAI_ForADUnloadThread = 0x10000000, // AD unload thread is working on the thread
};
static const DWORD TAI_AnySafeAbort = (TAI_ThreadAbort |
typedef Holder<Thread*, Thread::AcquireAbortControl, Thread::ReleaseAbortControl> AbortControlHolder;
- BOOL IsBeingAbortedForADUnload()
- {
- LIMITED_METHOD_CONTRACT;
- return (m_AbortInfo & TAI_ForADUnloadThread) != 0;
- }
-
- void ResetBeginAbortedForADUnload();
-
public:
#ifdef _DEBUG
BOOL m_fRudeAborted;
}
BOOL IsRudeAbort();
- BOOL IsRudeAbortOnlyForADUnload();
- BOOL IsRudeUnload();
BOOL IsFuncEvalAbort();
#if defined(_TARGET_AMD64_) && defined(FEATURE_HIJACK)
#define ENTER_DOMAIN_SWITCH_CTX_BY_ADID(_pCurrDomainPtr,_pDestDomainId,_bUnsafePoint) \
AppDomain* _ctx_trans_pCurrDomain=_pCurrDomainPtr; \
_ctx_trans_pDestDomainId=(ADID)_pDestDomainId; \
- BOOL _ctx_trans_bUnsafePoint=_bUnsafePoint; \
if (_ctx_trans_fPredicate && \
(_ctx_trans_pCurrDomain==NULL || \
(_ctx_trans_pCurrDomain->GetId() != _ctx_trans_pDestDomainId))) \
{ \
- AppDomainFromIDHolder _ctx_trans_ad(_ctx_trans_pDestDomainId,_ctx_trans_bUnsafePoint); \
- _ctx_trans_ad.ThrowIfUnloaded(); \
+ AppDomain* _ctx_trans_ad = SystemDomain::GetAppDomainFromId(_ctx_trans_pDestDomainId, ADV_CURRENTAD); \
\
_ctx_trans_ad->EnterContext(_ctx_trans_pThread, \
_ctx_trans_ad->GetDefaultContext(), \
_ctx_trans_pFrame); \
\
- _ctx_trans_ad.Release(); \
_ctx_trans_fTransitioned = true; \
}
{ \
TESTHOOKCALL(AppDomainCanBeUnloaded(_ctx_trans_pDestDomain->GetId().m_dwId,FALSE)); \
GCX_FORBID(); \
- if (!_ctx_trans_pDestDomain->CanThreadEnter(_ctx_trans_pThread)) \
- COMPlusThrow(kAppDomainUnloadedException); \
\
_ctx_trans_pThread->EnterContextRestricted( \
_ctx_trans_pDestDomain->GetDefaultContext(), \
#define ADV_COMPILATION 0x10
// finalizer thread - synchronized with ADU
#define ADV_FINALIZER 0x40
-// adu thread - cannot race with itself
-#define ADV_ADUTHREAD 0x80
// held by AppDomainRefTaker
#define ADV_REFTAKER 0x100
LIMITED_METHOD_CONTRACT;
_ASSERTE(GetThread() == this);
m_dwLockCount++;
- _ASSERTE(m_dwLockCount != 0 || HasThreadStateNC(TSNC_UnbalancedLocks) || GetDomain()->OkToIgnoreOrphanedLocks());
+ _ASSERTE(m_dwLockCount != 0 || HasThreadStateNC(TSNC_UnbalancedLocks));
}
inline void Thread::DecLockCount()
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(GetThread() == this);
- _ASSERTE(m_dwLockCount > 0 || HasThreadStateNC(TSNC_UnbalancedLocks) || GetDomain()->OkToIgnoreOrphanedLocks());
+ _ASSERTE(m_dwLockCount > 0 || HasThreadStateNC(TSNC_UnbalancedLocks));
m_dwLockCount--;
}
if (HasThreadStateNC(TSNC_SOWorkNeeded))
{
ResetThreadStateNC(TSNC_SOWorkNeeded);
- // Wake up AD unload thread to finish SO work that is delayed due to limit stack
- AppDomain::EnableADUnloadWorkerForThreadAbort();
}
#else
_ASSERTE(!HasThreadStateNC(TSNC_SOWorkNeeded));
// If there is an OBJECTHANDLE, try to clear it.
if (oh != 0 && adid.m_dwId != 0)
- { // See if the domain is still valid; if so, destroy the ObjectHandle
- AppDomainFromIDHolder ad(adid, TRUE);
- if (!ad.IsUnloaded())
- { // Still a valid domain, so destroy the handle.
- DestroyHandle(oh);
- }
- }
+ DestroyHandle(oh);
}
return (IsAbortRequested() && (m_AbortType == EEPolicy::TA_Rude));
}
-BOOL Thread::IsRudeAbortOnlyForADUnload()
-{
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- return (IsAbortRequested() &&
- (m_AbortInfo & TAI_ADUnloadRudeAbort) &&
- !(m_AbortInfo & (TAI_ThreadRudeAbort | TAI_FuncEvalRudeAbort))
- );
-}
-
-BOOL Thread::IsRudeUnload()
-{
- CONTRACTL {
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- return (IsAbortRequested() && (m_AbortInfo & TAI_ADUnloadRudeAbort));
-}
-
BOOL Thread::IsFuncEvalAbort()
{
CONTRACTL {
GetEEPolicy()->NotifyHostOnDefaultAction(operation,action);
break;
case eUnloadAppDomain:
- {
- AppDomain *pDomain = GetDomain();
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnDefaultAction(operation,action);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Safe);
- }
- }
- // AD unload does not abort finalizer thread.
- if (this != FinalizerThread::GetFinalizerThread())
- {
- if (this == GetThread())
- {
- Join(INFINITE,TRUE);
- }
- return S_OK;
- }
- break;
case eRudeUnloadAppDomain:
- {
- AppDomain *pDomain = GetDomain();
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnDefaultAction(operation,action);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Rude);
- }
- }
// AD unload does not abort finalizer thread.
if (this != FinalizerThread::GetFinalizerThread())
{
m_dwAbortPoint = 1;
#endif
- if (CLRHosted() && GetAbortEndTime() != MAXULONGLONG)
- {
- // ToDo: Skip debugger funcval
- // Use our helper thread to watch abort.
- AppDomain::EnableADUnloadWorkerForThreadAbort();
- }
-
GCX_COOP();
OBJECTREF exceptObj;
{
// A host may call ICLRTask::Abort on a critical thread. We don't want to
// block this thread.
- AppDomain::EnableADUnloadWorkerForThreadAbort();
return S_OK;
}
SetRudeAbortEndTimeFromEEPolicy();
goto LRetry;
case eUnloadAppDomain:
- {
- AppDomain *pDomain = GetDomain();
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnTimeout(operation1, action1);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Safe);
- }
- }
// AD unload does not abort finalizer thread.
if (this == FinalizerThread::GetFinalizerThread())
{
}
break;
case eRudeUnloadAppDomain:
- {
- AppDomain *pDomain = GetDomain();
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnTimeout(operation1, action1);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Rude);
- }
- }
// AD unload does not abort finalizer thread.
if (this == FinalizerThread::GetFinalizerThread())
{
GetEEPolicy()->NotifyHostOnTimeout(operation,action);
pThread->UserAbort(Thread::TAR_Thread, EEPolicy::TA_Rude, INFINITE, Thread::UAC_WatchDog);
break;
- case eUnloadAppDomain:
- {
- AppDomain *pDomain = pThread->GetDomain();
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnTimeout(operation,action);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Safe);
- }
- }
- break;
- case eRudeUnloadAppDomain:
- {
- AppDomain *pDomain = pThread->GetDomain();
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnTimeout(operation,action);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Rude);
- }
- }
- break;
case eExitProcess:
case eFastExitProcess:
case eRudeExitProcess:
}
}
- if (requester & TAR_ADUnload)
- {
- if (abortType == EEPolicy::TA_Safe)
- {
- abortInfo |= TAI_ADUnloadAbort;
- }
- else if (abortType == EEPolicy::TA_Rude)
- {
- abortInfo |= TAI_ADUnloadRudeAbort;
- }
- else if (abortType == EEPolicy::TA_V1Compatible)
- {
- abortInfo |= TAI_ADUnloadV1Abort;
- }
- if (IsADUnloadHelperThread())
- {
- abortInfo |= TAI_ForADUnloadThread;
- }
- }
-
if (requester & TAR_FuncEval)
{
if (abortType == EEPolicy::TA_Safe)
{
m_RudeAbortEndTime = endTime;
}
- // We can not call into host if we are in the middle of stack overflow.
- // And we don't need to wake up our watchdog if there is no timeout.
- if (GetThread() == this && (requester & TAR_StackOverflow) == 0)
- {
- AppDomain::EnableADUnloadWorkerForThreadAbort();
}
}
- }
if (abortInfo == (m_AbortInfo & abortInfo))
{
}
}
- if (requester & TAR_ADUnload)
- {
- m_AbortInfo &= ~(TAI_ADUnloadAbort |
- TAI_ADUnloadV1Abort |
- TAI_ADUnloadRudeAbort);
- }
-
if (requester & TAR_FuncEval)
{
m_AbortInfo &= ~(TAI_FuncEvalAbort |
STRESS_LOG3(LF_APPDOMAIN, LL_ALWAYS, "Unmark Thread %p Thread Id = %x for abort from requester %d\n", this, GetThreadId(), requester);
}
-// Make sure that when AbortRequest bit is cleared, we also dec TrapReturningThreads count.
-void Thread::ResetBeginAbortedForADUnload()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
- AbortRequestLockHolder lh(this);
-
- m_AbortInfo &= ~TAI_ForADUnloadThread;
-}
-
void Thread::InternalResetAbort(ThreadAbortRequester requester, BOOL fResetRudeAbort)
{
CONTRACTL {
}
CONTRACTL_END;
- MarkThreadForAbort(TAR_ADUnload, abortType);
+ MarkThreadForAbort(TAR_Thread, abortType);
if (m_State & TS_Interruptible)
{
GetEEPolicy()->NotifyHostOnTimeout(operation,action);
MarkThreadForAbort(TAR_Thread, EEPolicy::TA_Rude);
break;
- case eUnloadAppDomain:
- {
- AppDomain *pDomain = GetDomain();
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnTimeout(operation,action);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Safe);
- }
- }
- break;
- case eRudeUnloadAppDomain:
- {
- AppDomain *pDomain = GetDomain();
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnTimeout(operation,action);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Rude);
- }
- }
- break;
case eExitProcess:
case eFastExitProcess:
case eRudeExitProcess:
EPolicyAction action = GetEEPolicy()->GetDefaultAction(OPR_ThreadRudeAbortInCriticalRegion, this);
switch (action)
{
- case eRudeUnloadAppDomain:
- if (!pDomain->IsDefaultDomain())
- {
- GetEEPolicy()->NotifyHostOnDefaultAction(OPR_ThreadRudeAbortInCriticalRegion,action);
- pDomain->EnableADUnloadWorker(EEPolicy::ADU_Rude);
- }
- break;
case eExitProcess:
case eFastExitProcess:
case eRudeExitProcess:
{
GCX_COOP();
- AppDomainFromIDHolder ad(pDelegate->m_appDomainId, TRUE);
- if (!ad.IsUnloaded())
- // if no domain then handle already gone or about to go.
- StoreObjectInHandle(pDelegate->m_registeredWaitHandle, NULL);
+ StoreObjectInHandle(pDelegate->m_registeredWaitHandle, NULL);
}
}