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:
Andrea Marchesini
2018-01-28 08:41:36 +01:00
parent d06dd69c12
commit 6ca084192f
5 changed files with 140 additions and 0 deletions

View File

@@ -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