// # Important entrypoints in this code:
//
//
-// a) .ctor and Init(...) - called once during AppDomain initialization
-// b) OnMethodCalled(...) - called when a method is being invoked. When a method
-// has been called enough times this is currently the only
-// trigger that initiates re-compilation.
-// c) OnAppDomainShutdown() - called during AppDomain::Exit() to begin the process
-// of stopping tiered compilation. After this point no more
-// background optimization work will be initiated but in-progress
-// work still needs to complete.
+// a) .ctor and Init(...) - called once during AppDomain initialization
+// b) OnMethodCalled(...) - called when a method is being invoked. When a method
+// has been called enough times this is currently the only
+// trigger that initiates re-compilation.
+// c) Shutdown() - called during AppDomain::Exit() to begin the process
+// of stopping tiered compilation. After this point no more
+// background optimization work will be initiated but in-progress
+// work still needs to complete.
+// d) ShutdownAllDomains() - Called from EEShutdownHelper to block until all async work is
+// complete. We must do this before we shutdown the JIT.
//
// # Overall workflow
//
SpinLockHolder holder(&m_lock);
m_domainId = appDomainId;
+ m_pAsyncWorkDoneEvent = new CLREvent();
+ m_pAsyncWorkDoneEvent->CreateManualEvent(TRUE);
}
// Called each time code in this AppDomain has been run. This is our sole entrypoint to begin
{
// Our current policy throttles at 1 thread, but in the future we
// could experiment with more parallelism.
- m_countOptimizationThreadsRunning++;
+ IncrementWorkerThreadCount();
}
else
{
if (!ThreadpoolMgr::QueueUserWorkItem(StaticOptimizeMethodsCallback, this, QUEUE_ONLY, TRUE))
{
SpinLockHolder holder(&m_lock);
- m_countOptimizationThreadsRunning--;
+ DecrementWorkerThreadCount();
STRESS_LOG1(LF_TIEREDCOMPILATION, LL_WARNING, "TieredCompilationManager::OnMethodCalled: "
"ThreadpoolMgr::QueueUserWorkItem returned FALSE (no thread will run), method=%pM\n",
pMethodDesc);
EX_CATCH
{
SpinLockHolder holder(&m_lock);
- m_countOptimizationThreadsRunning--;
+ DecrementWorkerThreadCount();
STRESS_LOG2(LF_TIEREDCOMPILATION, LL_WARNING, "TieredCompilationManager::OnMethodCalled: "
"Exception queuing work item to threadpool, hr=0x%x, method=%pM\n",
GET_EXCEPTION()->GetHR(), pMethodDesc);
return;
}
-void TieredCompilationManager::OnAppDomainShutdown()
+// static
+// called from EEShutDownHelper
+void TieredCompilationManager::ShutdownAllDomains()
{
- CONTRACTL
+ STANDARD_VM_CONTRACT;
+
+ AppDomainIterator domain(TRUE);
+ while (domain.Next())
{
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- CAN_TAKE_LOCK;
+ AppDomain * pDomain = domain.GetDomain();
+ if (pDomain != NULL)
+ {
+ pDomain->GetTieredCompilationManager()->Shutdown(TRUE);
+ }
}
- CONTRACTL_END
+}
- SpinLockHolder holder(&m_lock);
- m_isAppDomainShuttingDown = TRUE;
+void TieredCompilationManager::Shutdown(BOOL fBlockUntilAsyncWorkIsComplete)
+{
+ STANDARD_VM_CONTRACT;
+
+ {
+ SpinLockHolder holder(&m_lock);
+ m_isAppDomainShuttingDown = TRUE;
+ }
+ if (fBlockUntilAsyncWorkIsComplete)
+ {
+ m_pAsyncWorkDoneEvent->Wait(INFINITE, FALSE);
+ }
}
// This is the initial entrypoint for the background thread, called by
SpinLockHolder holder(&m_lock);
if (m_isAppDomainShuttingDown)
{
- m_countOptimizationThreadsRunning--;
+ DecrementWorkerThreadCount();
return;
}
}
if (nativeCodeVersion.IsNull() ||
m_isAppDomainShuttingDown)
{
- m_countOptimizationThreadsRunning--;
+ DecrementWorkerThreadCount();
break;
}
if (!ThreadpoolMgr::QueueUserWorkItem(StaticOptimizeMethodsCallback, this, QUEUE_ONLY, TRUE))
{
SpinLockHolder holder(&m_lock);
- m_countOptimizationThreadsRunning--;
+ DecrementWorkerThreadCount();
STRESS_LOG0(LF_TIEREDCOMPILATION, LL_WARNING, "TieredCompilationManager::OptimizeMethodsCallback: "
"ThreadpoolMgr::QueueUserWorkItem returned FALSE (no thread will run)\n");
}
return NativeCodeVersion();
}
+void TieredCompilationManager::IncrementWorkerThreadCount()
+{
+ STANDARD_VM_CONTRACT;
+ //m_lock should be held
+
+ m_countOptimizationThreadsRunning++;
+ m_pAsyncWorkDoneEvent->Reset();
+}
+
+void TieredCompilationManager::DecrementWorkerThreadCount()
+{
+ STANDARD_VM_CONTRACT;
+ //m_lock should be held
+
+ m_countOptimizationThreadsRunning--;
+ if (m_countOptimizationThreadsRunning == 0)
+ {
+ m_pAsyncWorkDoneEvent->Set();
+ }
+}
+
//static
CORJIT_FLAGS TieredCompilationManager::GetJitFlags(NativeCodeVersion nativeCodeVersion)
{
void Init(ADID appDomainId);
BOOL OnMethodCalled(MethodDesc* pMethodDesc, DWORD currentCallCount);
void AsyncPromoteMethodToTier1(MethodDesc* pMethodDesc);
- void OnAppDomainShutdown();
+ static void ShutdownAllDomains();
+ void Shutdown(BOOL fBlockUntilAsyncWorkIsComplete);
static CORJIT_FLAGS GetJitFlags(NativeCodeVersion nativeCodeVersion);
private:
BOOL CompileCodeVersion(NativeCodeVersion nativeCodeVersion);
void ActivateCodeVersion(NativeCodeVersion nativeCodeVersion);
+ void IncrementWorkerThreadCount();
+ void DecrementWorkerThreadCount();
+
SpinLock m_lock;
SList<SListElem<NativeCodeVersion>> m_methodsToOptimize;
ADID m_domainId;
DWORD m_countOptimizationThreadsRunning;
DWORD m_callCountOptimizationThreshhold;
DWORD m_optimizationQuantumMs;
+ CLREvent* m_pAsyncWorkDoneEvent;
};
#endif // FEATURE_TIERED_COMPILATION