Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 5.6k
[browser][coreCLR] finalizer on a browser event loop#123629
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
455555e4d248aaa97615b74ded88ff1340e5d1b3fe8c843c9c15760c9d7734851d74838272ca1cdb1fd7c57b7be9abe1be943a522045c7225b9ce38df7b4200e13fbb4f0aab4364d6d23c89e415a73a97945bad55904b9d901a515ec59876623e43b9e27094a1a3840042ec8e5354bf4File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -40,12 +40,43 @@ bool FinalizerThread::IsCurrentThreadFinalizer() | ||
| return GetThreadNULLOk() == g_pFinalizerThread; | ||
| } | ||
| #ifdef TARGET_BROWSER | ||
| extern "C" void SystemJS_ScheduleFinalization(); | ||
| extern "C" void SystemJS_ExecuteFinalizationCallback() | ||
| { | ||
| CONTRACTL | ||
| { | ||
| NOTHROW; | ||
| GC_TRIGGERS; | ||
| MODE_PREEMPTIVE; | ||
| } | ||
| CONTRACTL_END; | ||
| INSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP; | ||
| { | ||
| GCX_COOP(); | ||
| // TODO-WASM https://github.com/dotnet/runtime/issues/123712 | ||
| // ManagedThreadBase::KickOff(FinalizerThread::FinalizerThreadWorkerIteration, NULL); | ||
| } | ||
| UNINSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP; | ||
| } | ||
| #endif // TARGET_BROWSER | ||
| void FinalizerThread::EnableFinalization() | ||
| { | ||
| WRAPPER_NO_CONTRACT; | ||
| #ifndef TARGET_WASM | ||
| hEventFinalizer->Set(); | ||
| #else // !TARGET_WASM | ||
| #ifdef TARGET_BROWSER | ||
| SystemJS_ScheduleFinalization(); | ||
| #else | ||
| // WASI is not implemented yet | ||
| #endif // TARGET_BROWSER | ||
| #endif // !TARGET_WASM | ||
| } | ||
| @@ -377,129 +408,167 @@ void FinalizerThread::WaitForFinalizerEvent (CLREvent *event) | ||
| } | ||
| } | ||
| static BOOL s_FinalizerThreadOK = FALSE; | ||
| static BOOL s_InitializedFinalizerThreadForPlatform = FALSE; | ||
| static bool s_FinalizerThreadOK = false; | ||
| static bool s_InitializedFinalizerThreadForPlatform = false; | ||
| static bool s_PriorityBoosted = false; | ||
| VOID FinalizerThread::FinalizerThreadWorker(void *args) | ||
| { | ||
| BOOL bPriorityBoosted = FALSE; | ||
| CONTRACTL | ||
| { | ||
| THROWS; | ||
| GC_TRIGGERS; | ||
| MODE_COOPERATIVE; | ||
| } | ||
| CONTRACTL_END; | ||
| while (!fQuitFinalizer) | ||
| { | ||
| // Wait for work to do... | ||
| FinalizerThread::FinalizerThreadWorkerIteration(nullptr); | ||
| } | ||
| if (s_InitializedFinalizerThreadForPlatform) | ||
| Thread::CleanUpForManagedThreadInNative(GetFinalizerThread()); | ||
| } | ||
| VOID FinalizerThread::FinalizerThreadWorkerIteration(void *args) | ||
| { | ||
jkotas marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. pavelsavara marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| CONTRACTL | ||
| { | ||
| THROWS; | ||
| GC_TRIGGERS; | ||
| MODE_COOPERATIVE; | ||
| } | ||
| CONTRACTL_END; | ||
| _ASSERTE(GetFinalizerThread()->PreemptiveGCDisabled()); | ||
| #ifdef _DEBUG | ||
pavelsavara marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (g_pConfig->FastGCStressLevel()) | ||
| { | ||
| GetFinalizerThread()->m_GCOnTransitionsOK = FALSE; | ||
| } | ||
| if (g_pConfig->FastGCStressLevel()) | ||
| { | ||
| GetFinalizerThread()->m_GCOnTransitionsOK = FALSE; | ||
| } | ||
| #endif | ||
| GetFinalizerThread()->EnablePreemptiveGC(); | ||
| GetFinalizerThread()->EnablePreemptiveGC(); | ||
| #ifdef _DEBUG | ||
| if (g_pConfig->FastGCStressLevel()) | ||
| { | ||
| GetFinalizerThread()->m_GCOnTransitionsOK = TRUE; | ||
| } | ||
| if (g_pConfig->FastGCStressLevel()) | ||
| { | ||
| GetFinalizerThread()->m_GCOnTransitionsOK = TRUE; | ||
| } | ||
| #endif | ||
| #ifndef TARGET_WASM | ||
| #if 0 | ||
| // Setting the event here, instead of at the bottom of the loop, could | ||
| // cause us to skip draining the Q, if the request is made as soon as | ||
| // the app starts running. | ||
| SignalFinalizationDone(); | ||
| // Setting the event here, instead of at the bottom of the loop, could | ||
| // cause us to skip draining the Q, if the request is made as soon as | ||
| // the app starts running. | ||
| SignalFinalizationDone(); | ||
| #endif //0 | ||
| WaitForFinalizerEvent (hEventFinalizer); | ||
| // Wait for work to do... | ||
| WaitForFinalizerEvent (hEventFinalizer); | ||
| #endif // !TARGET_WASM | ||
| // Process pending finalizer work items from the GC first. | ||
| FinalizerWorkItem* pWork = GCHeapUtilities::GetGCHeap()->GetExtraWorkForFinalization(); | ||
| while (pWork != NULL) | ||
| { | ||
| FinalizerWorkItem* pNext = pWork->next; | ||
| pWork->callback(pWork); | ||
| pWork = pNext; | ||
| } | ||
| // Process pending finalizer work items from the GC first. | ||
jkotas marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| FinalizerWorkItem* pWork = GCHeapUtilities::GetGCHeap()->GetExtraWorkForFinalization(); | ||
| while (pWork != NULL) | ||
| { | ||
| FinalizerWorkItem* pNext = pWork->next; | ||
| pWork->callback(pWork); | ||
| pWork = pNext; | ||
| } | ||
| #ifndef TARGET_WASM | ||
| #if defined(__linux__) && defined(FEATURE_EVENT_TRACE) | ||
| if (g_TriggerHeapDump && (minipal_lowres_ticks() > (LastHeapDumpTime + LINUX_HEAP_DUMP_TIME_OUT))) | ||
| { | ||
| s_forcedGCInProgress = true; | ||
| GetFinalizerThread()->DisablePreemptiveGC(); | ||
| GCHeapUtilities::GetGCHeap()->GarbageCollect(2, false, collection_blocking); | ||
| GetFinalizerThread()->EnablePreemptiveGC(); | ||
| s_forcedGCInProgress = false; | ||
| if (g_TriggerHeapDump && (minipal_lowres_ticks() > (LastHeapDumpTime + LINUX_HEAP_DUMP_TIME_OUT))) | ||
| { | ||
| s_forcedGCInProgress = true; | ||
| GetFinalizerThread()->DisablePreemptiveGC(); | ||
| GCHeapUtilities::GetGCHeap()->GarbageCollect(2, false, collection_blocking); | ||
| GetFinalizerThread()->EnablePreemptiveGC(); | ||
| s_forcedGCInProgress = false; | ||
| LastHeapDumpTime = minipal_lowres_ticks(); | ||
| g_TriggerHeapDump = FALSE; | ||
| } | ||
| LastHeapDumpTime = minipal_lowres_ticks(); | ||
| g_TriggerHeapDump = FALSE; | ||
| } | ||
| #endif | ||
| if (gcGenAnalysisState == GcGenAnalysisState::Done) | ||
| if (gcGenAnalysisState == GcGenAnalysisState::Done) | ||
| { | ||
| gcGenAnalysisState = GcGenAnalysisState::Disabled; | ||
| if (gcGenAnalysisTrace) | ||
| { | ||
| gcGenAnalysisState = GcGenAnalysisState::Disabled; | ||
| if (gcGenAnalysisTrace) | ||
| { | ||
| #ifdef FEATURE_PERFTRACING | ||
| EventPipeAdapter::Disable(gcGenAnalysisEventPipeSessionId); | ||
| EventPipeAdapter::Disable(gcGenAnalysisEventPipeSessionId); | ||
| #ifdef GEN_ANALYSIS_STRESS | ||
| GenAnalysis::EnableGenerationalAwareSession(); | ||
| GenAnalysis::EnableGenerationalAwareSession(); | ||
| #endif //GEN_ANALYSIS_STRESS | ||
| #endif //FEATURE_PERFTRACING | ||
| } | ||
| // Writing an empty file to indicate completion | ||
| WCHAR outputPath[MAX_PATH]; | ||
| ReplacePid(GENAWARE_COMPLETION_FILE_NAME, outputPath, MAX_PATH); | ||
| FILE* fp = NULL; | ||
| if (fopen_lp(&fp, outputPath, W("w+")) == 0) | ||
| { | ||
| fclose(fp); | ||
| } | ||
| } | ||
| if (!bPriorityBoosted) | ||
| { | ||
| if (GetFinalizerThread()->SetThreadPriority(THREAD_PRIORITY_HIGHEST)) | ||
| bPriorityBoosted = TRUE; | ||
| } | ||
| // The Finalizer thread is started very early in EE startup. We deferred | ||
| // some initialization until a point we are sure the EE is up and running. At | ||
| // this point we make a single attempt and if it fails won't try again. | ||
| if (!s_InitializedFinalizerThreadForPlatform) | ||
| // Writing an empty file to indicate completion | ||
| WCHAR outputPath[MAX_PATH]; | ||
| ReplacePid(GENAWARE_COMPLETION_FILE_NAME, outputPath, MAX_PATH); | ||
| FILE* fp = NULL; | ||
| if (fopen_lp(&fp, outputPath, W("w+")) == 0) | ||
| { | ||
| s_InitializedFinalizerThreadForPlatform = TRUE; | ||
| Thread::InitializationForManagedThreadInNative(GetFinalizerThread()); | ||
| fclose(fp); | ||
| } | ||
| } | ||
| JitHost::Reclaim(); | ||
| if (!s_PriorityBoosted) | ||
| { | ||
| if (GetFinalizerThread()->SetThreadPriority(THREAD_PRIORITY_HIGHEST)) | ||
| s_PriorityBoosted = true; | ||
| } | ||
| GetFinalizerThread()->DisablePreemptiveGC(); | ||
| // The Finalizer thread is started very early in EE startup. We deferred | ||
| // some initialization until a point we are sure the EE is up and running. At | ||
| // this point we make a single attempt and if it fails won't try again. | ||
| if (!s_InitializedFinalizerThreadForPlatform) | ||
| { | ||
| s_InitializedFinalizerThreadForPlatform = true; | ||
| Thread::InitializationForManagedThreadInNative(GetFinalizerThread()); | ||
| } | ||
| #endif // !TARGET_WASM | ||
| // we might want to do some extra work on the finalizer thread | ||
| // check and do it | ||
| if (HaveExtraWorkForFinalizer()) | ||
| { | ||
| DoExtraWorkForFinalizer(GetFinalizerThread()); | ||
| } | ||
| LOG((LF_GC, LL_INFO100, "***** Calling Finalizers\n")); | ||
| JitHost::Reclaim(); | ||
| int observedFullGcCount = | ||
| GCHeapUtilities::GetGCHeap()->CollectionCount(GCHeapUtilities::GetGCHeap()->GetMaxGeneration()); | ||
| FinalizeAllObjects(); | ||
| GetFinalizerThread()->DisablePreemptiveGC(); | ||
| // Anyone waiting to drain the Q can now wake up. Note that there is a | ||
| // race in that another thread starting a drain, as we leave a drain, may | ||
| // consider itself satisfied by the drain that just completed. | ||
| // Thus we include the Full GC count that we have certaily observed. | ||
| SignalFinalizationDone(observedFullGcCount); | ||
| // we might want to do some extra work on the finalizer thread | ||
| // check and do it | ||
| if (HaveExtraWorkForFinalizer()) | ||
| { | ||
| DoExtraWorkForFinalizer(GetFinalizerThread()); | ||
| } | ||
| LOG((LF_GC, LL_INFO100, "***** Calling Finalizers\n")); | ||
| if (s_InitializedFinalizerThreadForPlatform) | ||
| Thread::CleanUpForManagedThreadInNative(GetFinalizerThread()); | ||
| int observedFullGcCount = | ||
| GCHeapUtilities::GetGCHeap()->CollectionCount(GCHeapUtilities::GetGCHeap()->GetMaxGeneration()); | ||
| FinalizeAllObjects(); | ||
| #ifndef TARGET_WASM | ||
| // Anyone waiting to drain the Q can now wake up. Note that there is a | ||
| // race in that another thread starting a drain, as we leave a drain, may | ||
| // consider itself satisfied by the drain that just completed. | ||
| // Thus we include the Full GC count that we have certaily observed. | ||
| SignalFinalizationDone(observedFullGcCount); | ||
| #endif // !TARGET_WASM | ||
| } | ||
| DWORD WINAPI FinalizerThread::FinalizerThreadStart(void *args) | ||
| { | ||
| CONTRACTL | ||
| { | ||
| #ifdef TARGET_UNIX // because UNINSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP is catching exceptions only on Unix. On Windows OS is handling that. | ||
| NOTHROW; | ||
| #else // TARGET_UNIX | ||
| THROWS; | ||
| #endif // TARGET_UNIX | ||
| GC_TRIGGERS; | ||
| MODE_PREEMPTIVE; | ||
| } | ||
| CONTRACTL_END; | ||
| ClrFlsSetThreadType (ThreadType_Finalizer); | ||
| ASSERT(args == 0); | ||
| @@ -580,6 +649,7 @@ void FinalizerThread::FinalizerThreadCreate() | ||
| MODE_ANY; | ||
| } CONTRACTL_END; | ||
| #ifndef TARGET_WASM | ||
| #ifndef TARGET_UNIX | ||
| MHandles[kLowMemoryNotification] = | ||
| CreateMemoryResourceNotification(LowMemoryResourceNotification); | ||
| @@ -617,6 +687,10 @@ void FinalizerThread::FinalizerThreadCreate() | ||
| // and the moment we execute the test below. | ||
| _ASSERTE(dwRet == 1 || dwRet == 2); | ||
| } | ||
| #else // !TARGET_WASM | ||
| // capture the current (single) thread as the finalizer thread | ||
| g_pFinalizerThread = PTR_Thread(GetThread()); | ||
| #endif // !TARGET_WASM | ||
| } | ||
| static int g_fullGcCountSeenByFinalization; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -232,8 +232,7 @@ private static bool GetLinqExpressionsBuiltWithIsInterpretingOnly() | ||
| // heavily on Reflection.Emit | ||
| public static bool IsXmlDsigXsltTransformSupported => !PlatformDetection.IsInAppContainer && IsReflectionEmitSupported; | ||
| public static bool IsPreciseGcSupported => !IsMonoRuntime | ||
| && !IsBrowser; // TODO-WASM: https://github.com/dotnet/runtime/issues/114096 | ||
| public static bool IsPreciseGcSupported => !IsMonoRuntime; | ||
pavelsavara marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. pavelsavara marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| public static bool IsRareEnumsSupported => !IsNativeAot; | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.