зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
b5433ccce5
Коммит
dd2cca5c61
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче