Bug 1631304 - Reject AbstractThread dispatches after the main thread has ceased event proccessing. r=erahm

Differential Revision: https://phabricator.services.mozilla.com/D72263
This commit is contained in:
Bobby Holley 2020-04-28 21:18:19 +00:00
parent a50600019a
commit d90069e220
3 changed files with 30 additions and 3 deletions

View File

@ -148,6 +148,7 @@ nsresult nsLocalFileConstructor(nsISupports* aOuter, const nsIID& aIID,
nsComponentManagerImpl* nsComponentManagerImpl::gComponentManager = nullptr;
bool gXPCOMShuttingDown = false;
bool gXPCOMThreadsShutDown = false;
bool gXPCOMMainThreadEventsAreDoomed = false;
char16_t* gGREBinPath = nullptr;
static NS_DEFINE_CID(kINIParserFactoryCID, NS_INIPARSERFACTORY_CID);
@ -652,7 +653,10 @@ nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
// 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);
gXPCOMMainThreadEventsAreDoomed = true;
BackgroundHangMonitor().NotifyActivity();

View File

@ -32,6 +32,7 @@ DECL_CLASS(nsIDebug2);
#ifdef __cplusplus
extern bool gXPCOMShuttingDown;
extern bool gXPCOMThreadsShutDown;
extern bool gXPCOMMainThreadEventsAreDoomed;
#endif
#ifdef __cplusplus

View File

@ -47,14 +47,36 @@ class XPCOMThreadWrapper : public AbstractThread {
nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable,
DispatchReason aReason = NormalDispatch) override {
nsCOMPtr<nsIRunnable> r = aRunnable;
AbstractThread* currentThread;
if (aReason != TailDispatch && (currentThread = GetCurrent()) &&
RequiresTailDispatch(currentThread)) {
return currentThread->TailDispatcher().AddTask(this,
std::move(aRunnable));
return currentThread->TailDispatcher().AddTask(this, r.forget());
}
RefPtr<nsIRunnable> runner = new Runner(this, std::move(aRunnable));
// At a certain point during shutdown, we stop processing events from the
// main thread event queue (this happens long after all _other_ XPCOM
// threads have been shut down). However, various bits of subsequent
// teardown logic (the media shutdown blocker and the final shutdown cycle
// collection) can trigger state watching and state mirroring notifications
// that result in dispatch to the main thread. This causes shutdown leaks,
// because the |Runner| wrapper below creates a guaranteed cycle
// (Thread->EventQueue->Runnable->Thread) until the event is processed. So
// if we put the event into a queue that will never be processed, we'll wind
// up with a leak.
//
// We opt to just release the runnable in that case. Ordinarily, this
// approach could cause problems for runnables that are only safe to be
// released on the target thread (and not the dispatching thread). This is
// why XPCOM thread dispatch explicitly leaks the runnable when dispatch
// fails, rather than releasing it. But given that this condition only
// applies very late in shutdown when only one thread remains operational,
// that concern is unlikely to apply.
if (gXPCOMMainThreadEventsAreDoomed) {
return NS_ERROR_FAILURE;
}
RefPtr<nsIRunnable> runner = new Runner(this, r.forget());
return mThread->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
}