Bug 1425559 - Implement nsIThreadManager::spinEventLoopUntilOrShutdown, r=smaug
Currently nsIThreadManager::spinEventLoopUntil doesn't monitor the shutting down. Firefox shutting down can be blocked by a 'broken' use of nsIThreadManager::spinEventLoopUntil. nsIThreadManager::spinEventLoopUntilOrShutdown should be used instead.
This commit is contained in:
@@ -14,10 +14,12 @@
|
||||
#include "LabeledEventQueue.h"
|
||||
#include "MainThreadQueue.h"
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/EventQueue.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Scheduler.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/ThreadEventQueue.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include "PrioritizedEventQueue.h"
|
||||
@@ -103,6 +105,83 @@ NS_IMPL_CLASSINFO(nsThreadManager, nullptr,
|
||||
NS_IMPL_QUERY_INTERFACE_CI(nsThreadManager, nsIThreadManager)
|
||||
NS_IMPL_CI_INTERFACE_GETTER(nsThreadManager, nsIThreadManager)
|
||||
|
||||
namespace {
|
||||
|
||||
// Simple observer to monitor the beginning of the shutdown.
|
||||
class ShutdownObserveHelper final : public nsIObserver
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
static nsresult
|
||||
Create(ShutdownObserveHelper** aObserver)
|
||||
{
|
||||
MOZ_ASSERT(aObserver);
|
||||
|
||||
RefPtr<ShutdownObserveHelper> observer = new ShutdownObserveHelper();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (NS_WARN_IF(!obs)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = obs->AddObserver(observer, "content-child-will-shutdown", true);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
observer.forget(aObserver);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) override
|
||||
{
|
||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) ||
|
||||
!strcmp(aTopic, "content-child-will-shutdown")) {
|
||||
mShuttingDown = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
ShuttingDown() const
|
||||
{
|
||||
return mShuttingDown;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ShutdownObserveHelper()
|
||||
: mShuttingDown(false)
|
||||
{}
|
||||
|
||||
~ShutdownObserveHelper() = default;
|
||||
|
||||
bool mShuttingDown;
|
||||
};
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(ShutdownObserveHelper)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(ShutdownObserveHelper)
|
||||
NS_IMPL_RELEASE(ShutdownObserveHelper)
|
||||
|
||||
StaticRefPtr<ShutdownObserveHelper> gShutdownObserveHelper;
|
||||
|
||||
} // anonymous
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*static*/ nsThreadManager&
|
||||
@@ -112,6 +191,21 @@ nsThreadManager::get()
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsThreadManager::InitializeShutdownObserver()
|
||||
{
|
||||
MOZ_ASSERT(!gShutdownObserveHelper);
|
||||
|
||||
RefPtr<ShutdownObserveHelper> observer;
|
||||
nsresult rv = ShutdownObserveHelper::Create(getter_AddRefs(observer));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
gShutdownObserveHelper = observer;
|
||||
ClearOnShutdown(&gShutdownObserveHelper);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsThreadManager::Init()
|
||||
{
|
||||
@@ -424,11 +518,38 @@ nsThreadManager::GetCurrentThread(nsIThread** aResult)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadManager::SpinEventLoopUntil(nsINestedEventLoopCondition* aCondition)
|
||||
{
|
||||
return SpinEventLoopUntilInternal(aCondition, false);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadManager::SpinEventLoopUntilOrShutdown(nsINestedEventLoopCondition* aCondition)
|
||||
{
|
||||
return SpinEventLoopUntilInternal(aCondition, true);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsThreadManager::SpinEventLoopUntilInternal(nsINestedEventLoopCondition* aCondition,
|
||||
bool aCheckingShutdown)
|
||||
{
|
||||
nsCOMPtr<nsINestedEventLoopCondition> condition(aCondition);
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Nothing to do if already shutting down. Note that gShutdownObserveHelper is
|
||||
// nullified on shutdown.
|
||||
if (aCheckingShutdown &&
|
||||
(!gShutdownObserveHelper || gShutdownObserveHelper->ShuttingDown())) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mozilla::SpinEventLoopUntil([&]() -> bool {
|
||||
// Shutting down is started.
|
||||
if (aCheckingShutdown &&
|
||||
(!gShutdownObserveHelper ||
|
||||
gShutdownObserveHelper->ShuttingDown())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isDone = false;
|
||||
rv = condition->IsDone(&isDone);
|
||||
// JS failure should be unusual, but we need to stop and propagate
|
||||
|
||||
Reference in New Issue
Block a user