Bug 1764119 - Part 1: Do final KillClearOnShutdown after XPCOM threads shutdown, r=xpcom-reviewers,kmag,jstutte

This patch moves where we perform the final KillClearOnShutdown to occur
after we've shut down non-main threads, but before the main thread stops
accepting events. This should help ensure that unsuspecting events,
including those triggered from nsIThreadShutdownTask tasks, don't run
after KillClearOnShutdown has been run on background or main threads.

This KillClearOnShutdown was moved to occur before
nsThreadManager::Shutdown() in bug 1637890, as there were examples of
KillClearOnShutdown callbacks which needed to be able to dispatch
main-thread runnables. This change should not regress that use-case, as
we are still accepting new events on the main thread after the callback.

Non-main threads were already unreliable after this call as we already
block normal dispatches by setting gXPCOMThreadsShutdown, and new
threads cannot be started for the background thread pool.

Differential Revision: https://phabricator.services.mozilla.com/D144591
This commit is contained in:
Nika Layzell 2022-05-02 16:09:03 +00:00
Родитель 80ec444c16
Коммит 73061fa43e
3 изменённых файлов: 35 добавлений и 17 удалений

Просмотреть файл

@ -648,6 +648,11 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
mozilla::KillClearOnShutdown(ShutdownPhase::XPCOMShutdownLoaders);
// XXX: Why don't we try a MaybeFastShutdown for XPCOMShutdownLoaders ?
// Shutdown all remaining threads. This method does not return until
// all threads created using the thread manager (with the exception of
// the main thread) have exited.
nsThreadManager::get().ShutdownNonMainThreads();
RefPtr<nsObserverService> observerService;
CallGetService("@mozilla.org/observer-service;1",
(nsObserverService**)getter_AddRefs(observerService));
@ -660,15 +665,11 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
// observers themselves might call ClearOnShutdown().
// Some destructors may fire extra runnables that will be processed below.
mozilla::KillClearOnShutdown(ShutdownPhase::XPCOMShutdownFinal);
// Shutdown all remaining threads. This method does not return until
// all threads created using the thread manager (with the exception of
// the main thread) have exited.
nsThreadManager::get().Shutdown();
// Process our last round of events, and then mark that we've finished main
// thread event processing.
NS_ProcessPendingEvents(thread);
// Shutdown the main thread, processing our last round of events, and then
// mark that we've finished main thread event processing.
nsThreadManager::get().ShutdownMainThread();
gXPCOMMainThreadEventsAreDoomed = true;
BackgroundHangMonitor().NotifyActivity();

Просмотреть файл

@ -339,7 +339,7 @@ nsresult nsThreadManager::Init() {
return NS_OK;
}
void nsThreadManager::Shutdown() {
void nsThreadManager::ShutdownNonMainThreads() {
MOZ_ASSERT(NS_IsMainThread(), "shutdown not called from main thread");
// Prevent further access to the thread manager (no more new threads!)
@ -378,8 +378,8 @@ void nsThreadManager::Shutdown() {
mMainThread);
{
// We gather the threads from the hashtable into a list, so that we avoid
// holding the enumerator lock while calling nsIThread::Shutdown.
// We gather the threads into a list, so that we avoid holding the
// enumerator lock while calling nsIThread::Shutdown.
nsTArray<RefPtr<nsThread>> threadsToShutdown;
for (auto* thread : nsThread::Enumerate()) {
if (thread->ShutdownRequired()) {
@ -408,10 +408,23 @@ void nsThreadManager::Shutdown() {
// in-flight asynchronous thread shutdowns to complete.
mMainThread->WaitForAllAsynchronousShutdowns();
// In case there are any more events somehow...
NS_ProcessPendingEvents(mMainThread);
// There are no more background threads at this point.
}
void nsThreadManager::ShutdownMainThread() {
MOZ_ASSERT(!mInitialized, "Must have called BeginShutdown");
// Do NS_ProcessPendingEvents but with special handling to set
// mEventsAreDoomed atomically with the removal of the last event. This means
// that PutEvent cannot succeed if the event would be left in the main thread
// queue after our final call to NS_ProcessPendingEvents.
// See comments in `nsThread::ThreadFunc` for a more detailed explanation.
while (true) {
if (mMainThread->mEvents->ShutdownIfNoPendingEvents()) {
break;
}
NS_ProcessPendingEvents(mMainThread);
}
// Normally thread shutdown clears the observer for the thread, but since the
// main thread is special we do it manually here after we're sure all events

Просмотреть файл

@ -32,9 +32,13 @@ class nsThreadManager : public nsIThreadManager {
nsresult Init();
// Shutdown all threads. This function should only be called on the main
// thread of the application process.
void Shutdown();
// Shutdown all threads other than the main thread. This function should only
// be called on the main thread of the application process.
void ShutdownNonMainThreads();
// Finish shutting down all threads. This function must be called after
// ShutdownNonMainThreads and will take the main thread out of commission.
void ShutdownMainThread();
// Called by nsThread to inform the ThreadManager it exists. This method
// must be called when the given thread is the current thread.