From 11832401739148f1f1e9419cb51180ba5263a41b Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 4 Oct 2018 10:18:23 +0200 Subject: [PATCH] Remove AppDomain unload (#20250) * Remove AppDomain unload This change removes all code in AppDomain that's related to AppDomain unloading which is obsolete in CoreCLR. It also removes all calls to the removed methods. In few places, I have made the change simpler by taking into account the fact that there is always just one AppDomain. --- src/classlibnative/bcltype/system.cpp | 2 +- src/debug/daccess/dacdbiimpl.cpp | 28 - src/debug/ee/debugger.cpp | 3 +- src/inc/clrconfigvalues.h | 1 - src/inc/utilcode.h | 9 - src/vm/amd64/UMThunkStub.asm | 139 -- src/vm/amd64/asmconstants.h | 10 +- src/vm/amd64/umthunkstub.S | 12 - src/vm/appdomain.cpp | 3660 +++++++++------------------------ src/vm/appdomain.hpp | 452 +--- src/vm/appdomain.inl | 126 -- src/vm/appdomainnative.cpp | 28 - src/vm/appdomainnative.hpp | 3 - src/vm/arm/asmconstants.h | 2 +- src/vm/arm/asmhelpers.S | 94 - src/vm/arm/asmhelpers.asm | 95 - src/vm/arm64/asmconstants.h | 2 +- src/vm/arm64/asmhelpers.S | 113 - src/vm/arm64/asmhelpers.asm | 113 - src/vm/callhelpers.cpp | 2 - src/vm/ceemain.cpp | 15 - src/vm/codeversion.cpp | 4 - src/vm/comcallablewrapper.cpp | 50 +- src/vm/cominterfacemarshaler.cpp | 19 +- src/vm/comsynchronizable.cpp | 18 +- src/vm/comtoclrcall.cpp | 49 - src/vm/corhost.cpp | 89 +- src/vm/delegateinfo.h | 17 +- src/vm/dispatchinfo.cpp | 3 +- src/vm/dllimportcallback.cpp | 173 +- src/vm/domainfile.cpp | 24 +- src/vm/domainfile.h | 1 - src/vm/eeconfig.cpp | 2 - src/vm/eeconfig.h | 2 - src/vm/eepolicy.cpp | 75 - src/vm/eepolicy.h | 2 - src/vm/eventtrace.cpp | 34 +- src/vm/finalizerthread.cpp | 110 +- src/vm/finalizerthread.h | 15 - src/vm/gcenv.ee.cpp | 10 +- src/vm/gchandleutilities.cpp | 7 - src/vm/i386/asmconstants.h | 8 +- src/vm/i386/asmhelpers.S | 22 - src/vm/i386/asmhelpers.asm | 25 - src/vm/loaderallocator.cpp | 3 +- src/vm/marshalnative.cpp | 3 - src/vm/methodtable.cpp | 2 +- src/vm/nativeoverlapped.cpp | 2 +- src/vm/object.cpp | 6 - src/vm/profilingenumerators.cpp | 22 +- src/vm/stackprobe.cpp | 8 - src/vm/stacksampler.cpp | 6 +- src/vm/stdinterfaces_wrapper.cpp | 5 - src/vm/testhookmgr.cpp | 17 +- src/vm/threads.cpp | 353 +--- src/vm/threads.h | 45 +- src/vm/threads.inl | 6 +- src/vm/threadsuspend.cpp | 180 +- src/vm/win32threadpool.h | 5 +- 59 files changed, 1072 insertions(+), 5259 deletions(-) diff --git a/src/classlibnative/bcltype/system.cpp b/src/classlibnative/bcltype/system.cpp index a28adac..942ac3d 100644 --- a/src/classlibnative/bcltype/system.cpp +++ b/src/classlibnative/bcltype/system.cpp @@ -354,7 +354,7 @@ FCIMPL0(FC_BOOL_RET, SystemNative::HasShutdownStarted) // 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 diff --git a/src/debug/daccess/dacdbiimpl.cpp b/src/debug/daccess/dacdbiimpl.cpp index bed03ad..57f9f6b 100644 --- a/src/debug/daccess/dacdbiimpl.cpp +++ b/src/debug/daccess/dacdbiimpl.cpp @@ -3604,10 +3604,6 @@ void DacDbiInterfaceImpl::GetCachedWinRTTypesForIIDs( DD_ENTER_MAY_THROW; AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); - if (pAppDomain->IsUnloading()) - { - return; - } { pTypes->Alloc(iids.Count()); @@ -3654,10 +3650,6 @@ void DacDbiInterfaceImpl::GetCachedWinRTTypes( DD_ENTER_MAY_THROW; AppDomain * pAppDomain = vmAppDomain.GetDacPtr(); - if (pAppDomain->IsUnloading()) - { - return; - } InlineSArray rgMT; InlineSArray rgGuid; @@ -4308,10 +4300,6 @@ void DacDbiInterfaceImpl::EnumerateAppDomains( // 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); @@ -4338,10 +4326,6 @@ void DacDbiInterfaceImpl::EnumerateAssembliesInAppDomain( // 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)); @@ -4374,18 +4358,6 @@ void DacDbiInterfaceImpl::EnumerateModulesInAssembly( 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); diff --git a/src/debug/ee/debugger.cpp b/src/debug/ee/debugger.cpp index 839ed79..a2148b7 100644 --- a/src/debug/ee/debugger.cpp +++ b/src/debug/ee/debugger.cpp @@ -10134,8 +10134,7 @@ BOOL Debugger::SendSystemClassLoadUnloadEvent(mdTypeDef classMetadataToken, // 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); diff --git a/src/inc/clrconfigvalues.h b/src/inc/clrconfigvalues.h index cf31bc5..58c3113 100644 --- a/src/inc/clrconfigvalues.h +++ b/src/inc/clrconfigvalues.h @@ -783,7 +783,6 @@ CONFIG_DWORD_INFO_EX(INTERNAL_ForceRelocs, W("ForceRelocs"), 0, "", CLRConfig::R 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.") diff --git a/src/inc/utilcode.h b/src/inc/utilcode.h index 2356316..3c5a686 100644 --- a/src/inc/utilcode.h +++ b/src/inc/utilcode.h @@ -4610,15 +4610,6 @@ inline BOOL IsFinalizerThread () 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; diff --git a/src/vm/amd64/UMThunkStub.asm b/src/vm/amd64/UMThunkStub.asm index 123a630..b7701d8 100644 --- a/src/vm/amd64/UMThunkStub.asm +++ b/src/vm/amd64/UMThunkStub.asm @@ -18,7 +18,6 @@ extern TheUMEntryPrestubWorker:proc extern UMEntryPrestubUnwindFrameChainHandler:proc extern UMThunkStubUnwindFrameChainHandler:proc extern g_TrapReturningThreads:dword -extern UM2MDoADCallBack:proc extern UMThunkStubRareDisableWorker:proc extern ReversePInvokeBadTransition:proc @@ -188,14 +187,6 @@ HaveThread: 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 @@ -322,137 +313,7 @@ CopyLoop: 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 diff --git a/src/vm/amd64/asmconstants.h b/src/vm/amd64/asmconstants.h index cadc10a..1af0786 100644 --- a/src/vm/amd64/asmconstants.h +++ b/src/vm/amd64/asmconstants.h @@ -147,22 +147,22 @@ ASMCONSTANTS_C_ASSERT(OFFSETOF__Thread__m_pFrame 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 diff --git a/src/vm/amd64/umthunkstub.S b/src/vm/amd64/umthunkstub.S index 3e60bed..834f0ca 100644 --- a/src/vm/amd64/umthunkstub.S +++ b/src/vm/amd64/umthunkstub.S @@ -196,15 +196,3 @@ LOCAL_LABEL(WrongAppDomain): #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 diff --git a/src/vm/appdomain.cpp b/src/vm/appdomain.cpp index 18478eb..8f8efcf 100644 --- a/src/vm/appdomain.cpp +++ b/src/vm/appdomain.cpp @@ -142,11 +142,6 @@ CrstStatic SystemDomain::m_DelayedUnloadCrst; ULONG SystemDomain::s_dNumAppDomains = 0; -AppDomain * SystemDomain::m_pAppDomainBeingUnloaded = NULL; -ADIndex SystemDomain::m_dwIndexOfAppDomainBeingUnloaded; -Thread *SystemDomain::m_pAppDomainUnloadRequestingThread = 0; -Thread *SystemDomain::m_pAppDomainUnloadingThread = 0; - ArrayListStatic SystemDomain::m_appDomainIdList; DWORD SystemDomain::m_dwLowestFreeIndex = 0; @@ -2215,8 +2210,7 @@ void SystemDomain::Stop() AppDomainIterator i(TRUE); while (i.Next()) - if (i.GetDomain()->m_Stage < AppDomain::STAGE_CLEARED) - i.GetDomain()->Stop(); + i.GetDomain()->Stop(); } @@ -2476,21 +2470,6 @@ void SystemDomain::LazyInitGlobalStringLiteralMap() } } -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 @@ -2526,7 +2505,7 @@ void AppDomain::CreateADUnloadStartEvent() 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) @@ -2654,7 +2633,7 @@ DWORD SystemDomain::GetTotalNumSizedRefHandles() 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(); } @@ -2850,7 +2829,6 @@ void SystemDomain::LoadDomain(AppDomain *pDomain) } CONTRACTL_END; - pDomain->SetCanUnload(); // by default can unload any domain SystemDomain::System()->AddDomain(pDomain); } @@ -2995,12 +2973,8 @@ AppDomain *SystemDomain::GetAppDomainAtId(ADID index) 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 @@ -3913,11 +3887,6 @@ AppDomain::AppDomain() 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. @@ -3969,8 +3938,6 @@ AppDomain::AppDomain() m_ForceTrivialWaitOperations = false; m_Stage=STAGE_CREATING; - m_bForceGCOnUnload=FALSE; - m_bUnloadingFromUnloadEvent=FALSE; #ifdef _DEBUG m_dwIterHolders=0; m_dwRefTakers=0; @@ -4026,9 +3993,6 @@ AppDomain::~AppDomain() m_AssemblyCache.Clear(); - if (m_ADUnloadSink) - m_ADUnloadSink->Release(); - if(!g_fEEInit) Terminate(); @@ -4116,8 +4080,6 @@ void AppDomain::Init() #ifndef CROSSGEN_COMPILE PerAppDomainTPCountList::SetAppDomainId(m_tpIndex, m_dwId); - - m_ADUnloadSink=new ADUnloadSink(); #endif BaseDomain::Init(); @@ -4226,85 +4188,6 @@ BOOL AppDomain::IsCompilationDomain() #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& 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 @@ -4517,20 +4400,6 @@ struct GetExposedObject_Args 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 @@ -4547,16 +4416,6 @@ OBJECTREF AppDomain::GetExposedObject() { 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 @@ -5349,7 +5208,7 @@ CHECK AppDomain::CheckCanExecuteManagedCode(MethodDesc* pMD) 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"); } @@ -5652,17 +5511,6 @@ struct LoadFileArgs 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 *) @@ -5716,28 +5564,6 @@ DomainFile *AppDomain::LoadDomainFile(FileLoadLock *pLock, FileLoadLevel targetL 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. @@ -7280,7 +7106,7 @@ ULONG AppDomain::Release() 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)); @@ -7289,101 +7115,8 @@ ULONG AppDomain::Release() } -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 @@ -7593,8 +7326,6 @@ AppDomain::HasUnhandledExceptionEventHandler() NOTHROW; } CONTRACTL_END; - if (!CanThreadEnter(GetThread())) - return FALSE; if (GetRawExposedObject()==NULL) return FALSE; return (((APPDOMAINREF)GetRawExposedObject())->m_pUnhandledExceptionEventHandler!=NULL); @@ -7962,2657 +7693,1334 @@ void AppDomain::DetachRCWs() #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: what to do with any threads that didn't stop? + _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: 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. - // +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: need real synchronization here!!! + 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(elapsedTime); + // GCStress takes a long time to unwind, due to expensive creation of + // a threadabort exception. + if (!GCStress::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: A pre-allocated AppDomainUnloaded exception might be better. + 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, 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: what to do with any threads that didn't stop? - _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: need real synchronization here!!! +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::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: A pre-allocated AppDomainUnloaded exception might be better. - 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; + // 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 + 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()); - - // 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 - 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 g_FinalizerIsRunning = FALSE; -Volatile 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 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 g_FinalizerIsRunning = FALSE; +Volatile 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 s_WorkType = 0; -void SystemDomain::ProcessDelayedUnloadDomains() +void SystemDomain::ProcessDelayedUnloadLoaderAllocators() { CONTRACTL { @@ -10626,25 +9034,11 @@ void SystemDomain::ProcessDelayedUnloadDomains() 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) { @@ -10663,9 +9057,6 @@ void SystemDomain::ProcessDelayedUnloadDomains() } } - 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) @@ -10678,159 +9069,6 @@ void SystemDomain::ProcessDelayedUnloadDomains() #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 diff --git a/src/vm/appdomain.hpp b/src/vm/appdomain.hpp index eb28f3a..5ffc1b2 100644 --- a/src/vm/appdomain.hpp +++ b/src/vm/appdomain.hpp @@ -1230,8 +1230,6 @@ public: 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 @@ -1587,35 +1585,6 @@ enum ATTACH_ALL = 0x7 }; -class ADUnloadSink -{ - -protected: - ~ADUnloadSink(); - CLREvent m_UnloadCompleteEvent; - HRESULT m_UnloadResult; - Volatile m_cRef; -public: - ADUnloadSink(); - void ReportUnloadResult (HRESULT hr, OBJECTREF* pException); - void WaitUnloadCompletion(); - HRESULT GetUnloadResult() {LIMITED_METHOD_CONTRACT; return m_UnloadResult;}; - void Reset(); - ULONG AddRef(); - ULONG Release(); -}; - - -FORCEINLINE void ADUnloadSink__Release(ADUnloadSink* pADSink) -{ - WRAPPER_NO_CONTRACT; - - if (pADSink) - pADSink->Release(); -} - -typedef Wrapper ADUnloadSinkHolder; - // This filters the output of IterateAssemblies. This ought to be declared more locally // but it would result in really verbose callsites. // @@ -1957,7 +1926,6 @@ template class AppDomainCreationHolder; // class AppDomain : public BaseDomain { - friend class ADUnloadSink; friend class SystemDomain; friend class AssemblySink; friend class AppDomainNative; @@ -1968,7 +1936,6 @@ class AppDomain : public BaseDomain friend class RCWCache; friend class ClrDataAccess; friend class CheckAsmOffsets; - friend class AppDomainFromIDHolder; VPTR_VTABLE_CLASS(AppDomain, BaseDomain) @@ -1977,7 +1944,6 @@ public: AppDomain(); virtual ~AppDomain(); #endif - static void DoADUnloadWork(); DomainAssembly* FindDomainAssembly(Assembly*); void EnterContext(Thread* pThread, Context* pCtx,ContextTransitionFrame *pFrame); @@ -1992,10 +1958,6 @@ public: // Initializes an AppDomain. (this functions is not called from the SystemDomain) void Init(); - // creates only unamaged part - static void CreateUnmanagedObject(AppDomainCreationHolder& result); - - #if defined(FEATURE_COMINTEROP) HRESULT SetWinrtApplicationContext(SString &appLocalWinMD); #endif // FEATURE_COMINTEROP @@ -2591,8 +2553,6 @@ public: void SetupSharedStatics(); - ADUnloadSink* PrepareForWaitUnloadCompletion(); - //**************************************************************************************** // // Create a quick lookup for classes loaded into this domain based on their GUID. @@ -2844,20 +2804,6 @@ public: return dac_cast(this); } - void SetCanUnload() - { - LIMITED_METHOD_CONTRACT; - - m_dwFlags |= APP_DOMAIN_CAN_BE_UNLOADED; - } - - BOOL CanUnload() - { - LIMITED_METHOD_CONTRACT; - STATIC_CONTRACT_SO_TOLERANT; - return m_dwFlags & APP_DOMAIN_CAN_BE_UNLOADED; - } - void SetRemotingConfigured() { LIMITED_METHOD_CONTRACT; @@ -2886,14 +2832,6 @@ public: 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 @@ -2974,7 +2912,7 @@ public: BOOL CanLoadCode() { LIMITED_METHOD_CONTRACT; - return m_Stage >= STAGE_READYFORMANAGEDCODE && m_Stage < STAGE_CLOSED; + return m_Stage >= STAGE_READYFORMANAGEDCODE; } void SetAnonymouslyHostedDynamicMethodsAssembly(DomainAssembly * pDomainAssembly) @@ -2991,11 +2929,6 @@ public: return m_anonymouslyHostedDynamicMethodsAssembly; } - BOOL HasUnloadStarted() - { - LIMITED_METHOD_CONTRACT; - return m_Stage>=STAGE_EXITED; - } static void RefTakerAcquire(AppDomain* pDomain) { WRAPPER_NO_CONTRACT; @@ -3052,7 +2985,7 @@ public: { 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 @@ -3073,7 +3006,7 @@ public: // 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 } @@ -3103,14 +3036,6 @@ public: #endif BOOL IsRunningIn(Thread* pThread); - BOOL IsUnloading() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - - return m_Stage > STAGE_UNLOAD_REQUESTED; - } - BOOL NotReadyForManagedCode() { LIMITED_METHOD_CONTRACT; @@ -3118,67 +3043,6 @@ public: 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); @@ -3376,20 +3240,7 @@ private: 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(); @@ -3406,25 +3257,24 @@ private: }; - static void AllowThreadEntrance(AppDomain *pApp); - static void RestrictThreadEntrance(AppDomain *pApp); - - typedef Holder,AppDomain::AllowThreadEntrance,NULL> RestrictEnterHolder; - enum Stage { STAGE_CREATING, STAGE_READYFORMANAGEDCODE, STAGE_ACTIVE, STAGE_OPEN, - STAGE_UNLOAD_REQUESTED, - STAGE_EXITING, - STAGE_EXITED, - STAGE_FINALIZING, - STAGE_FINALIZED, - STAGE_HANDLETABLE_NOACCESS, - STAGE_CLEARED, - STAGE_COLLECTED, - STAGE_CLOSED + // 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) { @@ -3442,22 +3292,12 @@ private: 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) @@ -3468,53 +3308,6 @@ public: // Register the loader allocator for deletion in code:ShutdownFreeLoaderAllocators. void RegisterLoaderAllocatorForDeletion(LoaderAllocator * pLoaderAllocator); - AppDomain * m_pNextInDelayedUnloadList; - - void SetForceGCOnUnload(BOOL bSet) - { - m_bForceGCOnUnload=bSet; - } - - void SetUnloadingFromUnloadEvent() - { - m_bUnloadingFromUnloadEvent=TRUE; - } - - BOOL IsUnloadingFromUnloadEvent() - { - return m_bUnloadingFromUnloadEvent; - } - - void SetRudeUnload() - { - LIMITED_METHOD_CONTRACT; - - m_fRudeUnload = TRUE; - } - - BOOL IsRudeUnload() - { - LIMITED_METHOD_CONTRACT; - - return m_fRudeUnload; - } - - ADUnloadSink* GetADUnloadSink(); - ADUnloadSink* GetADUnloadSinkForUnload(); - void SetUnloadRequestThread(Thread *pThread) - { - LIMITED_METHOD_CONTRACT; - - m_pUnloadRequestThread = pThread; - } - - Thread *GetUnloadRequestThread() - { - LIMITED_METHOD_CONTRACT; - - return m_pUnloadRequestThread; - } - public: void SetGCRefPoint(int gccounter) { @@ -3538,17 +3331,9 @@ public: void AddMemoryPressure(); void RemoveMemoryPressure(); - void Unload(BOOL fForceUnload); - static HRESULT UnloadById(ADID Id, BOOL fSync, BOOL fExceptionsPassThrough=FALSE); - static HRESULT UnloadWait(ADID Id, ADUnloadSink* pSink); -#ifdef FEATURE_TESTHOOKS - static HRESULT UnloadWaitNoCatch(ADID Id, ADUnloadSink* pSink); -#endif - static void ResetUnloadRequestThread(ADID Id); void UnlinkClass(MethodTable *pMT); - typedef Holder UnloadHolder; Assembly *GetRootAssembly() { LIMITED_METHOD_CONTRACT; @@ -3745,7 +3530,6 @@ public: 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 @@ -3764,30 +3548,8 @@ public: 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); @@ -3800,8 +3562,6 @@ public: #endif private: - static void ADUnloadWorkerHelper(AppDomain *pDomain); - static CLREvent * g_pUnloadStartEvent; #ifdef DACCESS_COMPILE public: @@ -4070,50 +3830,6 @@ typedef Wrapper 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 @@ -4122,7 +3838,6 @@ 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) @@ -4280,15 +3995,6 @@ public: // 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")) @@ -4403,31 +4109,7 @@ public: 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) @@ -4450,74 +4132,8 @@ public: 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 @@ -4620,9 +4236,7 @@ private: STANDARD_VM_CONTRACT; m_pDefaultDomain = NULL; - m_pDelayedUnloadList=NULL; m_pDelayedUnloadListOfLoaderAllocators=NULL; - m_UnloadIsAsync = FALSE; m_GlobalAllocator.Init(this); } @@ -4650,9 +4264,6 @@ private: // 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 @@ -4668,20 +4279,6 @@ private: 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. @@ -5082,17 +4679,6 @@ protected: { 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: diff --git a/src/vm/appdomain.inl b/src/vm/appdomain.inl index 7fb4a95..37fb501 100644 --- a/src/vm/appdomain.inl +++ b/src/vm/appdomain.inl @@ -19,20 +19,6 @@ #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 @@ -48,118 +34,6 @@ inline void AppDomain::EnterContext(Thread* pThread, Context* pCtx,ContextTrans 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 diff --git a/src/vm/appdomainnative.cpp b/src/vm/appdomainnative.cpp index 713856f..3b2a538 100644 --- a/src/vm/appdomainnative.cpp +++ b/src/vm/appdomainnative.cpp @@ -302,34 +302,6 @@ FCIMPL2(Object*, AppDomainNative::GetAssemblies, AppDomainBaseObject* refThisUNS } // 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; diff --git a/src/vm/appdomainnative.hpp b/src/vm/appdomainnative.hpp index 9728ef8..dc1b133 100644 --- a/src/vm/appdomainnative.hpp +++ b/src/vm/appdomainnative.hpp @@ -28,11 +28,8 @@ public: 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); diff --git a/src/vm/arm/asmconstants.h b/src/vm/arm/asmconstants.h index 5af778b..c57c92a 100644 --- a/src/vm/arm/asmconstants.h +++ b/src/vm/arm/asmconstants.h @@ -179,7 +179,7 @@ ASMCONSTANTS_C_ASSERT(Thread__m_pFrame == offsetof(Thread, m_pFrame)); #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 diff --git a/src/vm/arm/asmhelpers.S b/src/vm/arm/asmhelpers.S index 22ec7c1..feadaa8 100644 --- a/src/vm/arm/asmhelpers.S +++ b/src/vm/arm/asmhelpers.S @@ -341,14 +341,7 @@ LOCAL_LABEL(UMThunkStub_HaveThread): 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) @@ -401,95 +394,8 @@ LOCAL_LABEL(UMThunkStub_DoTrapReturningThreads): 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 diff --git a/src/vm/arm/asmhelpers.asm b/src/vm/arm/asmhelpers.asm index 60def08..c0fef0c 100644 --- a/src/vm/arm/asmhelpers.asm +++ b/src/vm/arm/asmhelpers.asm @@ -22,7 +22,6 @@ IMPORT TheUMEntryPrestubWorker IMPORT CreateThreadBlockThrow IMPORT UMThunkStubRareDisableWorker - IMPORT UM2MDoADCallBack IMPORT PreStubWorker IMPORT PreStubGetMethodDescForCompactEntryPoint IMPORT NDirectImportWorker @@ -404,13 +403,7 @@ UMThunkStub_HaveThread 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 @@ -463,96 +456,8 @@ UMThunkStub_DoTrapReturningThreads 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 diff --git a/src/vm/arm64/asmconstants.h b/src/vm/arm64/asmconstants.h index 262e481..7d0a9f7 100644 --- a/src/vm/arm64/asmconstants.h +++ b/src/vm/arm64/asmconstants.h @@ -46,7 +46,7 @@ ASMCONSTANTS_C_ASSERT(Thread__m_pFrame == offsetof(Thread, m_pFrame)); #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 diff --git a/src/vm/arm64/asmhelpers.S b/src/vm/arm64/asmhelpers.S index e138feb..aa79b4d 100644 --- a/src/vm/arm64/asmhelpers.S +++ b/src/vm/arm64/asmhelpers.S @@ -767,15 +767,6 @@ LOCAL_LABEL(UMThunkStub_HaveThread): 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 @@ -846,112 +837,8 @@ LOCAL_LABEL(UMThunkStub_DoTrapReturningThreads): 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) diff --git a/src/vm/arm64/asmhelpers.asm b/src/vm/arm64/asmhelpers.asm index 7346c46..e9edec1 100644 --- a/src/vm/arm64/asmhelpers.asm +++ b/src/vm/arm64/asmhelpers.asm @@ -27,7 +27,6 @@ IMPORT GetThread IMPORT CreateThreadBlockThrow IMPORT UMThunkStubRareDisableWorker - IMPORT UM2MDoADCallBack IMPORT GetCurrentSavedRedirectContext IMPORT LinkFrameAndThrow IMPORT FixContextHandler @@ -754,15 +753,6 @@ UMThunkStub_HaveThread 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 @@ -833,109 +823,6 @@ UMThunkStub_DoTrapReturningThreads 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 diff --git a/src/vm/callhelpers.cpp b/src/vm/callhelpers.cpp index d11c4e8..b11f9d7 100644 --- a/src/vm/callhelpers.cpp +++ b/src/vm/callhelpers.cpp @@ -404,8 +404,6 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT * // 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)) diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp index 9874d26..04b1226 100644 --- a/src/vm/ceemain.cpp +++ b/src/vm/ceemain.cpp @@ -950,8 +950,6 @@ void EEStartupHelper(COINITIEE fFlags) 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) @@ -996,19 +994,6 @@ void EEStartupHelper(COINITIEE fFlags) 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(); diff --git a/src/vm/codeversion.cpp b/src/vm/codeversion.cpp index 5f7ef16..06451e9 100644 --- a/src/vm/codeversion.cpp +++ b/src/vm/codeversion.cpp @@ -2383,10 +2383,6 @@ HRESULT CodeVersionManager::EnumerateClosedMethodDescs( while (appDomainIterator.Next()) { AppDomain * pAppDomain = appDomainIterator.GetDomain(); - if (pAppDomain->IsUnloading()) - { - continue; - } hr = EnumerateDomainClosedMethodDescs( pAppDomain, pModule, diff --git a/src/vm/comcallablewrapper.cpp b/src/vm/comcallablewrapper.cpp index 4d116c1..49802ff 100644 --- a/src/vm/comcallablewrapper.cpp +++ b/src/vm/comcallablewrapper.cpp @@ -751,10 +751,7 @@ HRESULT WeakReferenceImpl::Cleanup() // GCX_COOP_THREAD_EXISTS(GET_THREAD()); - AppDomainFromIDHolder ad(m_adid, TRUE); - - if (!ad.IsUnloaded()) - DestroyShortWeakHandle(m_ppObject); + DestroyShortWeakHandle(m_ppObject); m_ppObject = NULL; } @@ -1103,18 +1100,7 @@ VOID SimpleComCallWrapper::Cleanup() // 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; } @@ -2127,10 +2113,7 @@ void ComCallWrapper::MarkHandleWeak() MODE_ANY; } CONTRACTL_END; -#ifdef _DEBUG - AppDomainFromIDHolder ad(GetSimpleWrapper()->GetDomainID(), TRUE); - _ASSERTE(!ad.IsUnloaded()); -#endif + SyncBlock* pSyncBlock = GetSyncBlock(); _ASSERTE(pSyncBlock); @@ -2152,10 +2135,6 @@ void ComCallWrapper::ResetHandleStrength() } CONTRACTL_END; -#ifdef _DEBUG - AppDomainFromIDHolder ad(GetSimpleWrapper()->GetDomainID(), TRUE); - _ASSERTE(!ad.IsUnloaded()); -#endif SyncBlock* pSyncBlock = GetSyncBlock(); _ASSERTE(pSyncBlock); @@ -2511,27 +2490,10 @@ void ComCallWrapper::Cleanup() 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; diff --git a/src/vm/cominterfacemarshaler.cpp b/src/vm/cominterfacemarshaler.cpp index 9b9d56a..4a74ddd 100644 --- a/src/vm/cominterfacemarshaler.cpp +++ b/src/vm/cominterfacemarshaler.cpp @@ -341,21 +341,10 @@ OBJECTREF COMInterfaceMarshaler::GetCCWObject() 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; diff --git a/src/vm/comsynchronizable.cpp b/src/vm/comsynchronizable.cpp index 0a6447b..5f54bcd 100644 --- a/src/vm/comsynchronizable.cpp +++ b/src/vm/comsynchronizable.cpp @@ -52,10 +52,9 @@ struct SharedState 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); @@ -72,17 +71,8 @@ struct SharedState } 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); } }; diff --git a/src/vm/comtoclrcall.cpp b/src/vm/comtoclrcall.cpp index 2a60f8c..df1ba64 100644 --- a/src/vm/comtoclrcall.cpp +++ b/src/vm/comtoclrcall.cpp @@ -615,62 +615,13 @@ void COMToCLRWorkerBodyWithADTransition( 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 { diff --git a/src/vm/corhost.cpp b/src/vm/corhost.cpp index ef99dec..f9785ab 100644 --- a/src/vm/corhost.cpp +++ b/src/vm/corhost.cpp @@ -1180,48 +1180,7 @@ HRESULT CorRuntimeHostBase::UnloadAppDomain2(DWORD dwDomainId, BOOL fWaitUntilDo } 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::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; } //***************************************************************************** @@ -1924,18 +1883,11 @@ public: 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; @@ -1959,22 +1911,15 @@ public: 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; @@ -2000,20 +1945,10 @@ public: { 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; } } diff --git a/src/vm/delegateinfo.h b/src/vm/delegateinfo.h index 80a69a6..8bc21b0 100644 --- a/src/vm/delegateinfo.h +++ b/src/vm/delegateinfo.h @@ -43,17 +43,12 @@ struct DelegateInfo 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 diff --git a/src/vm/dispatchinfo.cpp b/src/vm/dispatchinfo.cpp index f7b3068..393c2aa 100644 --- a/src/vm/dispatchinfo.cpp +++ b/src/vm/dispatchinfo.cpp @@ -3315,8 +3315,7 @@ DispatchMemberInfo* DispatchExInfo::CreateDispatchMemberInfoInstance(DISPID Disp 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)); diff --git a/src/vm/dllimportcallback.cpp b/src/vm/dllimportcallback.cpp index 2becba5..c064872 100644 --- a/src/vm/dllimportcallback.cpp +++ b/src/vm/dllimportcallback.cpp @@ -106,70 +106,6 @@ private: 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(); @@ -198,10 +134,6 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, 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; @@ -318,22 +250,6 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, // 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); - // // ---------------------------------------------------------------------------------------------- // @@ -376,14 +292,6 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, // +-------------------------+ // - // 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); @@ -564,33 +472,13 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, // 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); @@ -693,47 +581,6 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, 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 // @@ -751,15 +598,6 @@ VOID UMEntryThunk::CompileUMThunkWorker(UMThunkStubInfo *pInfo, // 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. @@ -1105,15 +943,6 @@ void STDCALL UMEntryThunk::DoRunTimeInit(UMEntryThunk* pUMEntryThunk) // 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(); diff --git a/src/vm/domainfile.cpp b/src/vm/domainfile.cpp index ded1e57..2a1b5e5 100644 --- a/src/vm/domainfile.cpp +++ b/src/vm/domainfile.cpp @@ -37,24 +37,6 @@ #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), @@ -1379,11 +1361,7 @@ BOOL DomainFile::PropagateNewActivation(Module *pModuleFrom, Module *pModuleTo) 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 diff --git a/src/vm/domainfile.h b/src/vm/domainfile.h index 070c616..5870651 100644 --- a/src/vm/domainfile.h +++ b/src/vm/domainfile.h @@ -709,7 +709,6 @@ public: BOOL IsVisibleToDebugger(); BOOL NotifyDebuggerLoad(int flags, BOOL attaching); void NotifyDebuggerUnload(); - BOOL IsUnloading(); inline BOOL IsCollectible(); // diff --git a/src/vm/eeconfig.cpp b/src/vm/eeconfig.cpp index 37864f1..0b3e413 100644 --- a/src/vm/eeconfig.cpp +++ b/src/vm/eeconfig.cpp @@ -345,7 +345,6 @@ HRESULT EEConfig::Init() #ifdef _DEBUG fShouldInjectFault = 0; testThreadAbort = 0; - testADUnload = 0; #endif #ifdef FEATURE_COMINTEROP @@ -1175,7 +1174,6 @@ HRESULT EEConfig::sync() fShouldInjectFault = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault); testThreadAbort = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HostTestThreadAbort); - testADUnload = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HostTestADUnload); #endif //_DEBUG diff --git a/src/vm/eeconfig.h b/src/vm/eeconfig.h index 60d8b70..68f160e 100644 --- a/src/vm/eeconfig.h +++ b/src/vm/eeconfig.h @@ -822,7 +822,6 @@ public: 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;} @@ -1100,7 +1099,6 @@ private: //---------------------------------------------------------------- #ifdef _DEBUG DWORD fShouldInjectFault; - DWORD testADUnload; DWORD testThreadAbort; #endif diff --git a/src/vm/eepolicy.cpp b/src/vm/eepolicy.cpp index efa5fc0..4cd4d0a 100644 --- a/src/vm/eepolicy.cpp +++ b/src/vm/eepolicy.cpp @@ -682,47 +682,6 @@ EPolicyAction EEPolicy::DetermineResourceConstraintAction(Thread *pThread) 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; @@ -740,13 +699,6 @@ void EEPolicy::PerformResourceConstraintAction(Thread *pThread, EPolicyAction ac 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: @@ -985,12 +937,6 @@ void EEPolicy::HandleStackOverflow(StackOverflowDetector detector, void * pLimit // 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( @@ -1051,13 +997,6 @@ void EEPolicy::HandleSoftStackOverflow(BOOL fSkipDebugger) { 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 @@ -1608,20 +1547,6 @@ void EEPolicy::HandleCodeContractFailure(LPCWSTR pMessage, LPCWSTR pCondition, L 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); diff --git a/src/vm/eepolicy.h b/src/vm/eepolicy.h index 44e0073..9ca09bb 100644 --- a/src/vm/eepolicy.h +++ b/src/vm/eepolicy.h @@ -112,8 +112,6 @@ public: 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); diff --git a/src/vm/eventtrace.cpp b/src/vm/eventtrace.cpp index be83f1c..6179b6c 100644 --- a/src/vm/eventtrace.cpp +++ b/src/vm/eventtrace.cpp @@ -4804,20 +4804,17 @@ VOID ETW::LoaderLog::DomainUnload(AppDomain *pDomain) 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); } @@ -7006,21 +7003,8 @@ VOID ETW::EnumerationLog::IterateAppDomain(AppDomain * pAppDomain, DWORD enumera // 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()); } /********************************************************************************/ diff --git a/src/vm/finalizerthread.cpp b/src/vm/finalizerthread.cpp index 62816eb..9e2e8d0 100644 --- a/src/vm/finalizerthread.cpp +++ b/src/vm/finalizerthread.cpp @@ -29,8 +29,6 @@ ULONGLONG FinalizerThread::LastHeapDumpTime = 0; Volatile g_TriggerHeapDump = FALSE; #endif // __linux__ -AppDomain * FinalizerThread::UnloadingAppDomain; - CLREvent * FinalizerThread::hEventFinalizer = NULL; CLREvent * FinalizerThread::hEventFinalizerDone = NULL; CLREvent * FinalizerThread::hEventShutDownToFinalizer = NULL; @@ -143,7 +141,7 @@ Object * FinalizerThread::DoOneFinalization(Object* fobj, Thread* pThread,int bi 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; @@ -151,48 +149,39 @@ Object * FinalizerThread::DoOneFinalization(Object* fobj, Thread* pThread,int bi 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. @@ -239,10 +228,6 @@ Object * FinalizerThread::FinalizeAllObjects(Object* fobj, int bitToCheck) if (fobj == NULL) { - if (AppDomain::HasWorkForFinalizerThread()) - { - return NULL; - } fobj = GCHeapUtilities::GetGCHeap()->GetNextFinalizable(); } @@ -264,10 +249,6 @@ Object * FinalizerThread::FinalizeAllObjects(Object* fobj, int bitToCheck) if (fobj->GetHeader()->GetBits() & bitToCheck) { - if (AppDomain::HasWorkForFinalizerThread()) - { - return NULL; - } fobj = GCHeapUtilities::GetGCHeap()->GetNextFinalizable(); } else @@ -281,10 +262,6 @@ Object * FinalizerThread::FinalizeAllObjects(Object* fobj, int bitToCheck) if (fobj == NULL) { - if (AppDomain::HasWorkForFinalizerThread()) - { - return NULL; - } fobj = GCHeapUtilities::GetGCHeap()->GetNextFinalizable(); } } @@ -621,44 +598,9 @@ VOID FinalizerThread::FinalizerThreadWorker(void *args) 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. @@ -1006,7 +948,7 @@ void FinalizerThread::FinalizerThreadWait(DWORD timeout) if (status == WAIT_TIMEOUT) { ULONGLONG finalizeStartTime = GetObjFinalizeStartTime(); - if (finalizeStartTime || AppDomain::HasWorkForFinalizerThread()) + if (finalizeStartTime) { if (CLRGetTickCount64() >= finalizeStartTime+timeout) { diff --git a/src/vm/finalizerthread.h b/src/vm/finalizerthread.h index be68451..bb1cfe4 100644 --- a/src/vm/finalizerthread.h +++ b/src/vm/finalizerthread.h @@ -10,7 +10,6 @@ class FinalizerThread { static BOOL fRunFinalizersOnUnload; static BOOL fQuitFinalizer; - static AppDomain *UnloadingAppDomain; #if defined(__linux__) && defined(FEATURE_EVENT_TRACE) static ULONGLONG LastHeapDumpTime; @@ -60,20 +59,6 @@ public: 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(); diff --git a/src/vm/gcenv.ee.cpp b/src/vm/gcenv.ee.cpp index 3dd22da..34645b3 100644 --- a/src/vm/gcenv.ee.cpp +++ b/src/vm/gcenv.ee.cpp @@ -193,9 +193,6 @@ void GCToEEInterface::GcStartWork (int condemned, int max_gen) } CONTRACTL_END; - // Update AppDomain stage here. - SystemDomain::System()->ProcessClearingDomains(); - #ifdef VERIFY_HEAP // Validate byrefs pinned by IL stubs since the last GC. StubHelpers::ProcessByrefValidationList(); @@ -1450,14 +1447,14 @@ bool GCToEEInterface::AppDomainCanAccessHandleTable(uint32_t appDomainID) 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() @@ -1472,8 +1469,7 @@ bool GCToEEInterface::AppDomainIsRudeUnload(void *appDomain) { LIMITED_METHOD_CONTRACT; - AppDomain *realPtr = static_cast(appDomain); - return realPtr->IsRudeUnload() != FALSE; + return false; } bool GCToEEInterface::AnalyzeSurvivorsRequested(int condemnedGeneration) diff --git a/src/vm/gchandleutilities.cpp b/src/vm/gchandleutilities.cpp index 4361778..220445a 100644 --- a/src/vm/gchandleutilities.cpp +++ b/src/vm/gchandleutilities.cpp @@ -18,7 +18,6 @@ void ValidateObjectAndAppDomain(OBJECTREF objRef, ADIndex appDomainIndex) // Access to a handle in an unloaded domain is not allowed assert(domain != nullptr); - assert(!domain->NoAccessToHandleTable()); #endif // _DEBUG_IMPL } @@ -36,12 +35,6 @@ void ValidateHandleAssignment(OBJECTHANDLE handle, OBJECTREF objRef) IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); ADIndex appDomainIndex = ADIndex(reinterpret_cast(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 } diff --git a/src/vm/i386/asmconstants.h b/src/vm/i386/asmconstants.h index 70f6e30..e17cec7 100644 --- a/src/vm/i386/asmconstants.h +++ b/src/vm/i386/asmconstants.h @@ -175,7 +175,7 @@ ASMCONSTANTS_C_ASSERT(CORINFO_ArgumentException_ASM == CORINFO_ArgumentException #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 @@ -193,10 +193,10 @@ ASMCONSTANTS_C_ASSERT(Thread_m_pFrame == offsetof(Thread, m_pFrame)) #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 @@ -338,7 +338,7 @@ ASMCONSTANTS_C_ASSERT(UMThunkMarshInfo__m_cbRetPop == offsetof(UMThunkMarshInfo, #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 diff --git a/src/vm/i386/asmhelpers.S b/src/vm/i386/asmhelpers.S index 2052088..09fb502 100644 --- a/src/vm/i386/asmhelpers.S +++ b/src/vm/i386/asmhelpers.S @@ -555,28 +555,6 @@ LEAF_ENTRY PrecodeFixupThunk, _TEXT 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 diff --git a/src/vm/i386/asmhelpers.asm b/src/vm/i386/asmhelpers.asm index 17d521f..18b5c6e 100644 --- a/src/vm/i386/asmhelpers.asm +++ b/src/vm/i386/asmhelpers.asm @@ -926,31 +926,6 @@ getFPReturn4: 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: this is very similar to StubRareDisable diff --git a/src/vm/loaderallocator.cpp b/src/vm/loaderallocator.cpp index 77f0c9e..cb5f817 100644 --- a/src/vm/loaderallocator.cpp +++ b/src/vm/loaderallocator.cpp @@ -1550,7 +1550,7 @@ BOOL AppDomainLoaderAllocator::CanUnload() SO_TOLERANT; } CONTRACTL_END; - return m_Id.GetAppDomain()->CanUnload(); + return FALSE; } BOOL AssemblyLoaderAllocator::CanUnload() @@ -1750,7 +1750,6 @@ void AssemblyLoaderAllocator::CleanupHandles() 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 diff --git a/src/vm/marshalnative.cpp b/src/vm/marshalnative.cpp index 133cbf9..2ce0bda 100644 --- a/src/vm/marshalnative.cpp +++ b/src/vm/marshalnative.cpp @@ -2256,13 +2256,10 @@ FCIMPL2(void, MarshalNative::ChangeWrapperHandleStrength, Object* orefUNSAFE, CL 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(); diff --git a/src/vm/methodtable.cpp b/src/vm/methodtable.cpp index a768ded..53a0be7 100644 --- a/src/vm/methodtable.cpp +++ b/src/vm/methodtable.cpp @@ -10075,7 +10075,7 @@ BOOL MethodTable::Validate() NOINLINE BYTE *MethodTable::GetLoaderAllocatorObjectForGC() { WRAPPER_NO_CONTRACT; - if (!Collectible() || ((PTR_AppDomain)GetLoaderModule()->GetDomain())->NoAccessToHandleTable()) + if (!Collectible()) { return NULL; } diff --git a/src/vm/nativeoverlapped.cpp b/src/vm/nativeoverlapped.cpp index 94e61c6..f808496 100644 --- a/src/vm/nativeoverlapped.cpp +++ b/src/vm/nativeoverlapped.cpp @@ -107,7 +107,7 @@ FCIMPL1(LPOVERLAPPED, AllocateNativeOverlapped, OverlappedDataObject* overlapped 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); } diff --git a/src/vm/object.cpp b/src/vm/object.cpp index a0427cd..ca54ba2 100644 --- a/src/vm/object.cpp +++ b/src/vm/object.cpp @@ -244,12 +244,6 @@ TypeHandle Object::GetGCSafeTypeHandleIfPossible() const 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 diff --git a/src/vm/profilingenumerators.cpp b/src/vm/profilingenumerators.cpp index a6b0a64..b5c1e57 100644 --- a/src/vm/profilingenumerators.cpp +++ b/src/vm/profilingenumerators.cpp @@ -177,33 +177,16 @@ HRESULT IterateAppDomains(CallbackObject * callbackObj, // 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 @@ -271,9 +254,6 @@ HRESULT IterateUnsharedModules(AppDomain * pAppDomain, // * 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. // diff --git a/src/vm/stackprobe.cpp b/src/vm/stackprobe.cpp index fd0488c..6663300 100644 --- a/src/vm/stackprobe.cpp +++ b/src/vm/stackprobe.cpp @@ -93,14 +93,6 @@ void SOTolerantCode_RecoverStack(DWORD dwFlags) { 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(); } diff --git a/src/vm/stacksampler.cpp b/src/vm/stacksampler.cpp index d95adb1..51e4b9d 100644 --- a/src/vm/stacksampler.cpp +++ b/src/vm/stacksampler.cpp @@ -195,11 +195,7 @@ ADID StackSampler::GetDomainId(MethodDesc* pMD, const ADID& defaultId) } if (bPresent != FALSE) { - AppDomainFromIDHolder pDomain(adId, FALSE); - if (!pDomain.IsUnloaded()) - { - return adId; - } + return adId; } return defaultId; } diff --git a/src/vm/stdinterfaces_wrapper.cpp b/src/vm/stdinterfaces_wrapper.cpp index c33c330..6265751 100644 --- a/src/vm/stdinterfaces_wrapper.cpp +++ b/src/vm/stdinterfaces_wrapper.cpp @@ -475,11 +475,6 @@ HRESULT __stdcall Unknown_QueryInterface_ICCW(IUnknown *pUnk, REFIID riid, void 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 diff --git a/src/vm/testhookmgr.cpp b/src/vm/testhookmgr.cpp index 991b6c6..f72f69d 100644 --- a/src/vm/testhookmgr.cpp +++ b/src/vm/testhookmgr.cpp @@ -603,22 +603,7 @@ HRESULT CLRTestHookManager::CheckConfig() 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) diff --git a/src/vm/threads.cpp b/src/vm/threads.cpp index ddf31f4..9a0ebdc 100644 --- a/src/vm/threads.cpp +++ b/src/vm/threads.cpp @@ -1350,9 +1350,6 @@ void CheckADValidity(AppDomain* pDomain, DWORD ADValidityKind) if ((ADValidityKind & ADV_FINALIZER) && IsFinalizerThread()) return; - if ((ADValidityKind & ADV_ADUTHREAD) && - IsADUnloadHelperThread()) - return; if ((ADValidityKind & ADV_RUNNINGIN) && pDomain->IsRunningIn(GetThread())) return; @@ -1377,7 +1374,6 @@ Thread::Thread() CONTRACTL_END; m_pFrame = FRAME_TOP; - m_pUnloadBoundaryFrame = NULL; m_fPreemptiveGCDisabled = 0; @@ -7438,7 +7434,6 @@ VOID Thread::RestoreGuardPage() } FinishSOWork(); - //GetAppDomain()->EnableADUnloadWorker(EEPolicy::ADU_Rude); INDEBUG(DebugLogStackMBIs()); @@ -7680,8 +7675,7 @@ void MakeCallWithAppDomainTransition( 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); @@ -7695,7 +7689,6 @@ void MakeCallWithAppDomainTransition( pTargetDomain->GetDefaultContext(), _ctx_trans_pFrame); - pTargetDomain.Release(); args->pCtxFrame = _ctx_trans_pFrame; TESTHOOKCALL(EnteredAppDomain((TargetDomain.m_dwId))); /* work around unreachable code warning */ @@ -7809,11 +7802,8 @@ void Thread::DoContextCallBack(ADID appDomain, Context *pContext, Context::ADCal 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) { @@ -7828,156 +7818,6 @@ void Thread::DoContextCallBack(ADID appDomain, Context *pContext, Context::ADCal 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 { @@ -7996,12 +7836,6 @@ void Thread::EnterContextRestricted(Context *pContext, ContextTransitionFrame *p // 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); @@ -8147,18 +7981,6 @@ void Thread::ReturnToContext(ContextTransitionFrame *pFrame) 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: @@ -8222,16 +8044,6 @@ void Thread::ReturnToContext(ContextTransitionFrame *pFrame) 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 @@ -8265,18 +8077,6 @@ void Thread::ReturnToContext(ContextTransitionFrame *pFrame) // _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: @@ -8483,64 +8283,6 @@ AppDomain *Thread::GetInitialDomain() 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; @@ -8550,7 +8292,6 @@ BOOL Thread::HaveExtraWorkForFinalizer() || ExecutionManager::IsCacheCleanupRequired() || Thread::CleanupNeededForFinalizedThread() || (m_DetachCount > 0) - || AppDomain::HasWorkForFinalizerThread() || SystemDomain::System()->RequireAppDomainCleanup() || ThreadStore::s_pThreadStore->ShouldTriggerGCForDeadThreads(); } @@ -8573,11 +8314,6 @@ void Thread::DoExtraWorkForFinalizer() } #endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT - if (AppDomain::HasWorkForFinalizerThread()) - { - AppDomain::ProcessUnloadDomainEventOnFinalizeThread(); - } - if (RequireSyncBlockCleanup()) { #ifndef FEATURE_PAL @@ -8592,7 +8328,7 @@ void Thread::DoExtraWorkForFinalizer() } if (SystemDomain::System()->RequireAppDomainCleanup()) { - SystemDomain::System()->ProcessDelayedUnloadDomains(); + SystemDomain::System()->ProcessDelayedUnloadLoaderAllocators(); } if(m_DetachCount > 0 || Thread::CleanupNeededForFinalizedThread()) @@ -8766,46 +8502,6 @@ protected: 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 @@ -8816,41 +8512,8 @@ static void ManagedThreadBase_DispatchInner(ManagedThreadCallState *pCallState) } 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) @@ -10758,10 +10421,6 @@ ETaskType GetCurrentTaskType() { TaskType = TT_THREADPOOL_WAIT; } - else if (type & ThreadType_ADUnloadHelper) - { - TaskType = TT_ADUNLOAD; - } else if (type & ThreadType_Threadpool_IOCompletion) { TaskType = TT_THREADPOOL_IOCOMPLETION; diff --git a/src/vm/threads.h b/src/vm/threads.h index e4b6487..dc46863 100644 --- a/src/vm/threads.h +++ b/src/vm/threads.h @@ -448,8 +448,6 @@ inline void CommonTripThread() { } #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 @@ -1586,7 +1584,6 @@ public: Volatile m_fPreemptiveGCDisabled; PTR_Frame m_pFrame; // The Current Frame - PTR_Frame m_pUnloadBoundaryFrame; //----------------------------------------------------------- // If the thread has wandered in from the outside this is @@ -2012,17 +2009,6 @@ public: 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; @@ -2441,14 +2427,6 @@ public: 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 @@ -2544,8 +2522,6 @@ public: 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(); @@ -2806,7 +2782,6 @@ public: 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, @@ -2828,7 +2803,6 @@ private: 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 | @@ -2887,14 +2861,6 @@ private: typedef Holder AbortControlHolder; - BOOL IsBeingAbortedForADUnload() - { - LIMITED_METHOD_CONTRACT; - return (m_AbortInfo & TAI_ForADUnloadThread) != 0; - } - - void ResetBeginAbortedForADUnload(); - public: #ifdef _DEBUG BOOL m_fRudeAborted; @@ -2965,8 +2931,6 @@ public: } BOOL IsRudeAbort(); - BOOL IsRudeAbortOnlyForADUnload(); - BOOL IsRudeUnload(); BOOL IsFuncEvalAbort(); #if defined(_TARGET_AMD64_) && defined(FEATURE_HIJACK) @@ -6974,19 +6938,16 @@ private: #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; \ } @@ -6999,8 +6960,6 @@ private: { \ 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(), \ @@ -7089,8 +7048,6 @@ private: #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 diff --git a/src/vm/threads.inl b/src/vm/threads.inl index f5a439c..1c07070 100644 --- a/src/vm/threads.inl +++ b/src/vm/threads.inl @@ -46,14 +46,14 @@ inline void Thread::IncLockCount() 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--; } @@ -167,8 +167,6 @@ inline void Thread::FinishSOWork() 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)); diff --git a/src/vm/threadsuspend.cpp b/src/vm/threadsuspend.cpp index 6c761f3..abf9597 100644 --- a/src/vm/threadsuspend.cpp +++ b/src/vm/threadsuspend.cpp @@ -639,13 +639,7 @@ void Thread::ClearAbortReason(BOOL pNoLock) // 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); } @@ -1272,31 +1266,6 @@ BOOL Thread::IsRudeAbort() 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 { @@ -1451,33 +1420,7 @@ Thread::UserAbort(ThreadAbortRequester requester, 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()) { @@ -1545,13 +1488,6 @@ Thread::UserAbort(ThreadAbortRequester requester, 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; @@ -1583,7 +1519,6 @@ Thread::UserAbort(ThreadAbortRequester requester, { // A host may call ICLRTask::Abort on a critical thread. We don't want to // block this thread. - AppDomain::EnableADUnloadWorkerForThreadAbort(); return S_OK; } @@ -2120,14 +2055,6 @@ LPrepareRetry: 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()) { @@ -2146,14 +2073,6 @@ LPrepareRetry: } 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()) { @@ -2285,26 +2204,6 @@ void Thread::ThreadAbortWatchDogEscalate(Thread *pThread) 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: @@ -2463,26 +2362,6 @@ void Thread::MarkThreadForAbort(ThreadAbortRequester requester, EEPolicy::Thread } } - 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) @@ -2534,14 +2413,8 @@ void Thread::MarkThreadForAbort(ThreadAbortRequester requester, EEPolicy::Thread { 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)) { @@ -2658,13 +2531,6 @@ void Thread::UnmarkThreadForAbort(ThreadAbortRequester requester, BOOL fForce) } } - if (requester & TAR_ADUnload) - { - m_AbortInfo &= ~(TAI_ADUnloadAbort | - TAI_ADUnloadV1Abort | - TAI_ADUnloadRudeAbort); - } - if (requester & TAR_FuncEval) { m_AbortInfo &= ~(TAI_FuncEvalAbort | @@ -2718,21 +2584,6 @@ void Thread::UnmarkThreadForAbort(ThreadAbortRequester requester, BOOL fForce) 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 { @@ -2762,7 +2613,7 @@ void Thread::SetAbortRequest(EEPolicy::ThreadAbortTypes abortType) } CONTRACTL_END; - MarkThreadForAbort(TAR_ADUnload, abortType); + MarkThreadForAbort(TAR_Thread, abortType); if (m_State & TS_Interruptible) { @@ -3212,26 +3063,6 @@ void Thread::HandleThreadAbortTimeout() 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: @@ -3345,13 +3176,6 @@ void Thread::PreWorkForThreadAbort() 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: diff --git a/src/vm/win32threadpool.h b/src/vm/win32threadpool.h index 2a3a3bb..ddbd34a 100644 --- a/src/vm/win32threadpool.h +++ b/src/vm/win32threadpool.h @@ -476,10 +476,7 @@ private: { 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); } } -- 2.7.4