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
Родитель b5433ccce5
Коммит dd2cca5c61
5 изменённых файлов: 140 добавлений и 0 удалений

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

@ -122,6 +122,7 @@
#include "mozilla/dom/U2FTokenManager.h"
#include "mozilla/dom/PointerEventHandler.h"
#include "nsHostObjectProtocolHandler.h"
#include "nsThreadManager.h"
using namespace mozilla;
using namespace mozilla::net;
@ -321,6 +322,8 @@ nsLayoutStatics::Initialize()
mozilla::dom::DOMPrefs::Initialize();
}
nsThreadManager::InitializeShutdownObserver();
return NS_OK;
}

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

@ -31,6 +31,7 @@ EXPORTS += [
'nsProcess.h',
'nsProxyRelease.h',
'nsThread.h',
'nsThreadManager.h',
'nsThreadUtils.h',
]

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

@ -120,6 +120,15 @@ interface nsIThreadManager : nsISupports
*/
void spinEventLoopUntil(in nsINestedEventLoopCondition condition);
/**
* Similar to the previous method, but the spinning of the event loop
* terminates when the shutting down starts.
*
* C++ code should not use this function, instead preferring
* mozilla::SpinEventLoopUntil.
*/
void spinEventLoopUntilOrShutdown(in nsINestedEventLoopCondition condition);
/**
* Spin the current thread's event loop until there are no more pending
* events. This could be done with spinEventLoopUntil, but that would

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

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

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

@ -22,6 +22,8 @@ public:
static nsThreadManager& get();
static void InitializeShutdownObserver();
nsresult Init();
// Shutdown all threads. This function should only be called on the main
@ -75,6 +77,10 @@ private:
{
}
nsresult
SpinEventLoopUntilInternal(nsINestedEventLoopCondition* aCondition,
bool aCheckingShutdown);
nsRefPtrHashtable<nsPtrHashKey<PRThread>, nsThread> mThreadsByPRThread;
unsigned mCurThreadIndex; // thread-local-storage index
RefPtr<nsThread> mMainThread;