mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backed out changeset 4d2839eea957 (bug 970307)
This commit is contained in:
parent
da2820c9f9
commit
f5f5b9ee8f
@ -103,6 +103,12 @@ this.DOMApplicationRegistry = {
|
||||
this.cpmm.addMessageListener(aMsgName, this);
|
||||
}).bind(this));
|
||||
|
||||
this.resetList();
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
},
|
||||
|
||||
resetList: function() {
|
||||
this.cpmm.sendAsyncMessage("Webapps:RegisterForMessages", {
|
||||
messages: APPS_IPC_MSG_NAMES
|
||||
});
|
||||
@ -110,6 +116,7 @@ this.DOMApplicationRegistry = {
|
||||
// We need to prime the cache with the list of apps.
|
||||
let list = this.cpmm.sendSyncMessage("Webapps:GetList", { })[0];
|
||||
this.webapps = list.webapps;
|
||||
|
||||
// We need a fast mapping from localId -> app, so we add an index.
|
||||
// We also add the manifest to the app object.
|
||||
this.localIdIndex = { };
|
||||
@ -118,8 +125,6 @@ this.DOMApplicationRegistry = {
|
||||
this.localIdIndex[app.localId] = app;
|
||||
app.manifest = list.manifests[id];
|
||||
}
|
||||
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
|
@ -538,6 +538,12 @@ NS_IMPL_ISUPPORTS(BackgroundChildPrimer, nsIIPCBackgroundChildCreateCallback)
|
||||
|
||||
ContentChild* ContentChild::sSingleton;
|
||||
|
||||
static void
|
||||
PostForkPreload()
|
||||
{
|
||||
TabChild::PostForkPreload();
|
||||
}
|
||||
|
||||
// Performs initialization that is not fork-safe, i.e. that must be done after
|
||||
// forking from the Nuwa process.
|
||||
static void
|
||||
@ -548,6 +554,7 @@ InitOnContentProcessCreated()
|
||||
if (IsNuwaProcess()) {
|
||||
return;
|
||||
}
|
||||
PostForkPreload();
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permManager =
|
||||
services::GetPermissionManager();
|
||||
@ -1349,7 +1356,6 @@ ContentChild::RecvPBrowserConstructor(PBrowserChild* aActor,
|
||||
{
|
||||
// This runs after AllocPBrowserChild() returns and the IPC machinery for this
|
||||
// PBrowserChild has been set up.
|
||||
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
nsITabChild* tc =
|
||||
@ -2195,8 +2201,11 @@ bool
|
||||
ContentChild::RecvFlushMemory(const nsString& reason)
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
if (IsNuwaProcess() || ManagedPBrowserChild().Length() == 0) {
|
||||
// Don't flush memory in the nuwa process: the GC thread could be frozen.
|
||||
// If there's no PBrowser child, don't flush memory, either. GC writes
|
||||
// to copy-on-write pages and makes preallocated process take more memory
|
||||
// before it actually becomes an app.
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@ -2290,12 +2299,28 @@ ContentChild::RecvAppInit()
|
||||
// BrowserElementChild.js.
|
||||
if ((mIsForApp || mIsForBrowser)
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
&& !IsNuwaProcess()
|
||||
&& IsNuwaProcess()
|
||||
#endif
|
||||
) {
|
||||
PreloadSlowThings();
|
||||
#ifndef MOZ_NUWA_PROCESS
|
||||
PostForkPreload();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
// Some modules are initialized in preloading. We need to wait until the
|
||||
// tasks they dispatched to chrome process are done.
|
||||
if (IsNuwaProcess()) {
|
||||
SendNuwaWaitForFreeze();
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvNuwaFreeze()
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
ContentChild::GetSingleton()->RecvGarbageCollect();
|
||||
@ -2303,7 +2328,6 @@ ContentChild::RecvAppInit()
|
||||
FROM_HERE, NewRunnableFunction(OnFinishNuwaPreparation));
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2580,6 +2604,36 @@ RunNuwaFork()
|
||||
DoNuwaFork();
|
||||
}
|
||||
}
|
||||
|
||||
class NuwaForkCaller: public nsRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHODIMP
|
||||
Run() {
|
||||
// We want to ensure that the PBackground actor gets cloned in the Nuwa
|
||||
// process before we freeze. Also, we have to do this to avoid deadlock.
|
||||
// Protocols that are "opened" (e.g. PBackground, PCompositor) block the
|
||||
// main thread to wait for the IPC thread during the open operation.
|
||||
// NuwaSpawnWait() blocks the IPC thread to wait for the main thread when
|
||||
// the Nuwa process is forked. Unless we ensure that the two cannot happen
|
||||
// at the same time then we risk deadlock. Spinning the event loop here
|
||||
// guarantees the ordering is safe for PBackground.
|
||||
if (!BackgroundChild::GetForCurrentThread()) {
|
||||
// Dispatch ourself again.
|
||||
NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
MessageLoop* ioloop = XRE_GetIOMessageLoop();
|
||||
ioloop->PostTask(FROM_HERE, NewRunnableFunction(RunNuwaFork));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
virtual
|
||||
~NuwaForkCaller()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
bool
|
||||
@ -2591,22 +2645,9 @@ ContentChild::RecvNuwaFork()
|
||||
}
|
||||
sNuwaForking = true;
|
||||
|
||||
// We want to ensure that the PBackground actor gets cloned in the Nuwa
|
||||
// process before we freeze. Also, we have to do this to avoid deadlock.
|
||||
// Protocols that are "opened" (e.g. PBackground, PCompositor) block the
|
||||
// main thread to wait for the IPC thread during the open operation.
|
||||
// NuwaSpawnWait() blocks the IPC thread to wait for the main thread when
|
||||
// the Nuwa process is forked. Unless we ensure that the two cannot happen
|
||||
// at the same time then we risk deadlock. Spinning the event loop here
|
||||
// guarantees the ordering is safe for PBackground.
|
||||
while (!BackgroundChild::GetForCurrentThread()) {
|
||||
if (NS_WARN_IF(!NS_ProcessNextEvent())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
nsRefPtr<NuwaForkCaller> runnable = new NuwaForkCaller();
|
||||
NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
|
||||
|
||||
MessageLoop* ioloop = XRE_GetIOMessageLoop();
|
||||
ioloop->PostTask(FROM_HERE, NewRunnableFunction(RunNuwaFork));
|
||||
return true;
|
||||
#else
|
||||
return false; // Makes the underlying IPC channel abort.
|
||||
|
@ -379,6 +379,8 @@ public:
|
||||
|
||||
virtual bool RecvNotifyPhoneStateChange(const nsString& state) override;
|
||||
|
||||
virtual bool RecvNuwaFreeze() override;
|
||||
|
||||
void AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
|
||||
void RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
|
||||
virtual bool RecvNotifyIdleObserver(const uint64_t& aObserver,
|
||||
|
@ -140,6 +140,7 @@
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsStyleSheetService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsThreadManager.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "PreallocatedProcessManager.h"
|
||||
@ -1562,6 +1563,28 @@ StaticAutoPtr<LinkedList<SystemMessageHandledListener> >
|
||||
NS_IMPL_ISUPPORTS(SystemMessageHandledListener,
|
||||
nsITimerCallback)
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
class NuwaFreezeListener : public nsThreadManager::AllThreadsWereIdleListener
|
||||
{
|
||||
public:
|
||||
NuwaFreezeListener(ContentParent* parent)
|
||||
: mParent(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void OnAllThreadsWereIdle()
|
||||
{
|
||||
unused << mParent->SendNuwaFreeze();
|
||||
nsThreadManager::get()->RemoveAllThreadsWereIdleListener(this);
|
||||
}
|
||||
private:
|
||||
nsRefPtr<ContentParent> mParent;
|
||||
virtual ~NuwaFreezeListener()
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
@ -2339,6 +2362,8 @@ ContentParent::ContentParent(ContentParent* aTemplate,
|
||||
priority = PROCESS_PRIORITY_FOREGROUND;
|
||||
}
|
||||
|
||||
mSendPermissionUpdates = aTemplate->mSendPermissionUpdates;
|
||||
|
||||
InitInternal(priority,
|
||||
false, /* Setup Off-main thread compositing */
|
||||
false /* Send registered chrome */);
|
||||
@ -2502,7 +2527,7 @@ ContentParent::IsForApp()
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
bool
|
||||
ContentParent::IsNuwaProcess()
|
||||
ContentParent::IsNuwaProcess() const
|
||||
{
|
||||
return mIsNuwaProcess;
|
||||
}
|
||||
@ -2877,6 +2902,19 @@ ContentParent::RecvNuwaReady()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvNuwaWaitForFreeze()
|
||||
{
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
nsRefPtr<NuwaFreezeListener> listener = new NuwaFreezeListener(this);
|
||||
nsThreadManager::get()->AddAllThreadsWereIdleListener(listener);
|
||||
return true;
|
||||
#else // MOZ_NUWA_PROCESS
|
||||
NS_ERROR("ContentParent::RecvNuwaWaitForFreeze() not implemented!");
|
||||
return false;
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvAddNewProcess(const uint32_t& aPid,
|
||||
InfallibleTArray<ProtocolFdMapping>&& aFds)
|
||||
|
@ -233,7 +233,7 @@ public:
|
||||
return mIsForBrowser;
|
||||
}
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
bool IsNuwaProcess();
|
||||
bool IsNuwaProcess() const;
|
||||
#endif
|
||||
|
||||
GeckoChildProcessHost* Process() {
|
||||
@ -247,7 +247,11 @@ public:
|
||||
}
|
||||
|
||||
bool NeedsPermissionsUpdate() const {
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
return !IsNuwaProcess() && mSendPermissionUpdates;
|
||||
#else
|
||||
return mSendPermissionUpdates;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NeedsDataStoreInfos() const {
|
||||
@ -779,6 +783,8 @@ private:
|
||||
|
||||
virtual bool RecvNuwaReady() override;
|
||||
|
||||
virtual bool RecvNuwaWaitForFreeze() override;
|
||||
|
||||
virtual bool RecvAddNewProcess(const uint32_t& aPid,
|
||||
InfallibleTArray<ProtocolFdMapping>&& aFds) override;
|
||||
|
||||
|
@ -632,6 +632,7 @@ child:
|
||||
InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action);
|
||||
|
||||
EndDragSession(bool aDoneDrag, bool aUserCancelled);
|
||||
NuwaFreeze();
|
||||
|
||||
async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, OptionalURIParams aDomain);
|
||||
|
||||
@ -898,6 +899,9 @@ parent:
|
||||
async SystemMessageHandled();
|
||||
|
||||
NuwaReady();
|
||||
// Sent when nuwa finished its initialization process and is waiting for
|
||||
// parent's signal to make it freeze.
|
||||
NuwaWaitForFreeze();
|
||||
|
||||
sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds);
|
||||
|
||||
|
@ -775,6 +775,21 @@ TabChild::PreloadSlowThings()
|
||||
ClearOnShutdown(&sPreallocatedTab);
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
TabChild::PostForkPreload()
|
||||
{
|
||||
// Preallocated Tab can be null if we are forked directly from b2g. In such
|
||||
// case we don't need to preload anything, just return.
|
||||
if (!sPreallocatedTab) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Rebuild connections to parent.
|
||||
sPreallocatedTab->RecvLoadRemoteScript(
|
||||
NS_LITERAL_STRING("chrome://global/content/post-fork-preload.js"),
|
||||
true);
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<TabChild>
|
||||
TabChild::Create(nsIContentChild* aManager,
|
||||
const TabId& aTabId,
|
||||
|
@ -263,6 +263,7 @@ public:
|
||||
* on the critical path.
|
||||
*/
|
||||
static void PreloadSlowThings();
|
||||
static void PostForkPreload();
|
||||
|
||||
/** Return a TabChild with the given attributes. */
|
||||
static already_AddRefed<TabChild>
|
||||
|
@ -12,3 +12,4 @@ toolkit.jar:
|
||||
content/global/manifestMessages.js (manifestMessages.js)
|
||||
content/global/PushServiceChildPreload.js (../push/PushServiceChildPreload.js)
|
||||
content/global/preload.js (preload.js)
|
||||
content/global/post-fork-preload.js (post-fork-preload.js)
|
||||
|
20
dom/ipc/post-fork-preload.js
Normal file
20
dom/ipc/post-fork-preload.js
Normal file
@ -0,0 +1,20 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Preload some things, in an attempt to make app startup faster.
|
||||
//
|
||||
// This script is run when the preallocated process starts. It is injected as
|
||||
// a frame script.
|
||||
// If Nuwa process is enabled, this script will run in preallocated process
|
||||
// forked by Nuwa.
|
||||
|
||||
(function (global) {
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/AppsServiceChild.jsm");
|
||||
Components.classes["@mozilla.org/network/protocol-proxy-service;1"].
|
||||
getService(Ci["nsIProtocolProxyService"]);
|
||||
|
||||
DOMApplicationRegistry.resetList();
|
||||
})(this);
|
@ -6,6 +6,7 @@
|
||||
//
|
||||
// This script is run when the preallocated process starts. It is injected as
|
||||
// a frame script.
|
||||
// If nuwa is enabled, this script will run in Nuwa process before frozen.
|
||||
|
||||
const BrowserElementIsPreloaded = true;
|
||||
|
||||
@ -58,7 +59,6 @@ const BrowserElementIsPreloaded = true;
|
||||
Cc["@mozilla.org/network/idn-service;1"].getService(Ci["nsIIDNService"]);
|
||||
Cc["@mozilla.org/network/io-service;1"].getService(Ci["nsIIOService2"]);
|
||||
Cc["@mozilla.org/network/mime-hdrparam;1"].getService(Ci["nsIMIMEHeaderParam"]);
|
||||
Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(Ci["nsIProtocolProxyService"]);
|
||||
Cc["@mozilla.org/network/socket-transport-service;1"].getService(Ci["nsISocketTransportService"]);
|
||||
Cc["@mozilla.org/network/stream-transport-service;1"].getService(Ci["nsIStreamTransportService"]);
|
||||
Cc["@mozilla.org/network/url-parser;1?auth=maybe"].getService(Ci["nsIURLParser"]);
|
||||
|
@ -67,6 +67,9 @@ public:
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
NS_SetIgnoreStatusOfCurrentThread();
|
||||
#endif
|
||||
nsAutoString event;
|
||||
gWpaSupplicant->WaitForEvent(event, mInterface);
|
||||
if (!event.IsEmpty()) {
|
||||
|
@ -85,6 +85,7 @@
|
||||
#include "nsQueryObject.h"
|
||||
#include "nsSandboxFlags.h"
|
||||
#include "prthread.h"
|
||||
#include "nsThread.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
@ -5106,6 +5107,17 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
{
|
||||
nsThread *thr = static_cast<nsThread*>(NS_GetCurrentThread());
|
||||
ReentrantMonitorAutoEnter mon(thr->ThreadStatusMonitor());
|
||||
if (mControlQueue.IsEmpty() &&
|
||||
!(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
|
||||
thr->SetIdle();
|
||||
}
|
||||
}
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
while (mControlQueue.IsEmpty() &&
|
||||
!(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
|
||||
!(normalRunnablesPending = NS_HasPendingEvents(mThread))) {
|
||||
|
@ -753,6 +753,7 @@ nsSocketTransportService::Run()
|
||||
if (IsNuwaProcess()) {
|
||||
NuwaMarkCurrentThread(nullptr, nullptr);
|
||||
}
|
||||
NS_SetIgnoreStatusOfCurrentThread();
|
||||
#endif
|
||||
|
||||
SOCKET_LOG(("STS thread init\n"));
|
||||
|
@ -127,6 +127,8 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_SetIgnoreStatusOfCurrentThread();
|
||||
|
||||
int lowMemFd = open("/sys/kernel/mm/lowmemkiller/notify_trigger_active",
|
||||
O_RDONLY | O_CLOEXEC);
|
||||
NS_ENSURE_STATE(lowMemFd != -1);
|
||||
|
@ -375,3 +375,11 @@ nsAutoLowPriorityIO::~nsAutoLowPriorityIO()
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
void
|
||||
NS_SetIgnoreStatusOfCurrentThread() {
|
||||
nsThreadManager::get()->SetIgnoreThreadStatus();
|
||||
}
|
||||
#endif // MOZILLA_INTERNAL_API
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
@ -1019,4 +1019,19 @@ private:
|
||||
void
|
||||
NS_SetMainThread();
|
||||
|
||||
/**
|
||||
* Helpers for thread to report their status when compiled with Nuwa.
|
||||
*/
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
extern void
|
||||
NS_SetIgnoreStatusOfCurrentThread();
|
||||
#else // MOZ_NUWA_PROCESS
|
||||
inline void
|
||||
NS_SetIgnoreStatusOfCurrentThread()
|
||||
{
|
||||
}
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
#endif // MOZILLA_INTERNAL_API
|
||||
|
||||
#endif // nsThreadUtils_h__
|
||||
|
@ -205,6 +205,7 @@ TimerThread::Run()
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_SetIgnoreStatusOfCurrentThread();
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
// We need to know how many microseconds give a positive PRIntervalTime. This
|
||||
|
@ -15,7 +15,6 @@
|
||||
#undef LOG
|
||||
#endif
|
||||
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "nsMemoryPressure.h"
|
||||
#include "nsThreadManager.h"
|
||||
#include "nsIClassInfoImpl.h"
|
||||
@ -331,6 +330,10 @@ nsThread::ThreadFunc(void* aArg)
|
||||
|
||||
// Inform the ThreadManager
|
||||
nsThreadManager::get()->RegisterCurrentThread(self);
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
self->mThreadStatusInfo =
|
||||
static_cast<void*>(nsThreadManager::get()->GetCurrentThreadStatusInfo());
|
||||
#endif
|
||||
|
||||
#if !defined(MOZILLA_XPCOMRT_API)
|
||||
mozilla::IOInterposer::RegisterCurrentThread();
|
||||
@ -448,6 +451,10 @@ nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
|
||||
, mShutdownRequired(false)
|
||||
, mEventsAreDoomed(false)
|
||||
, mIsMainThread(aMainThread)
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
, mThreadStatusMonitor("nsThread.mThreadStatusLock")
|
||||
, mThreadStatusInfo(nullptr)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@ -495,6 +502,11 @@ nsThread::InitCurrentThread()
|
||||
SetupCurrentThreadForChaosMode();
|
||||
|
||||
nsThreadManager::get()->RegisterCurrentThread(this);
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
mThreadStatusInfo =
|
||||
static_cast<void*>(nsThreadManager::get()->GetCurrentThreadStatusInfo());
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -510,7 +522,15 @@ nsThread::PutEvent(nsIRunnable* aEvent, nsNestedEventTarget* aTarget)
|
||||
NS_WARNING("An event was posted to a thread that will never run it (rejected)");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
queue->PutEvent(aEvent);
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mThreadStatusMonitor);
|
||||
SetWorking();
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
queue->PutEvent(aEvent);
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
}
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
// Make sure to grab the observer before dropping the lock, otherwise the
|
||||
// event that we just placed into the queue could run and eventually delete
|
||||
@ -853,6 +873,27 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
|
||||
|
||||
--mNestedEventLoopDepth;
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
nsCOMPtr<nsIRunnable> notifyAllIdleRunnable;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mThreadStatusMonitor);
|
||||
if ((!mEvents->GetEvent(false, nullptr)) && (mNestedEventLoopDepth == 0)) {
|
||||
nsThreadManager::get()->SetThreadIsWorking(
|
||||
static_cast<nsThreadManager::ThreadStatusInfo*>(mThreadStatusInfo),
|
||||
false, getter_AddRefs(notifyAllIdleRunnable));
|
||||
}
|
||||
}
|
||||
if (notifyAllIdleRunnable) {
|
||||
// Dispatching a task leads us to acquire |mLock| of the thread. If we
|
||||
// dispatch to main thread while holding main thread's
|
||||
// |mThreadStatusMonitor|, deadlock could happen if other thread is
|
||||
// blocked by main thread's |mThreadStatusMonitor| and is holding
|
||||
// main thread's |mLock|.
|
||||
Dispatch(notifyAllIdleRunnable, NS_DISPATCH_NORMAL);
|
||||
nsThreadManager::get()->ResetIsDispatchingToMainThread();
|
||||
}
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent,
|
||||
(this, mNestedEventLoopDepth, *aResult));
|
||||
|
||||
@ -1063,6 +1104,24 @@ nsThread::SetMainThreadObserver(nsIThreadObserver* aObserver)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
void
|
||||
nsThread::SetWorking()
|
||||
{
|
||||
nsThreadManager::get()->SetThreadIsWorking(
|
||||
static_cast<nsThreadManager::ThreadStatusInfo*>(mThreadStatusInfo),
|
||||
true, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
nsThread::SetIdle()
|
||||
{
|
||||
nsThreadManager::get()->SetThreadIsWorking(
|
||||
static_cast<nsThreadManager::ThreadStatusInfo*>(mThreadStatusInfo),
|
||||
false, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "nsTObserverArray.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
|
||||
// A native thread
|
||||
class nsThread
|
||||
@ -65,6 +66,14 @@ public:
|
||||
static nsresult
|
||||
SetMainThreadObserver(nsIThreadObserver* aObserver);
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
void SetWorking();
|
||||
void SetIdle();
|
||||
mozilla::ReentrantMonitor& ThreadStatusMonitor() {
|
||||
return mThreadStatusMonitor;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static nsIThreadObserver* sMainThreadObserver;
|
||||
|
||||
@ -182,6 +191,12 @@ protected:
|
||||
// Set to true when events posted to this thread will never run.
|
||||
bool mEventsAreDoomed;
|
||||
MainThreadFlag mIsMainThread;
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
mozilla::ReentrantMonitor mThreadStatusMonitor;
|
||||
// The actual type is defined in nsThreadManager.h which is not exposed to
|
||||
// file out of thread module.
|
||||
void* mThreadStatusInfo;
|
||||
#endif
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "nsTArray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#ifdef MOZ_CANARY
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
@ -40,6 +41,45 @@ NS_SetMainThread()
|
||||
|
||||
typedef nsTArray<nsRefPtr<nsThread>> nsThreadArray;
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
class NotifyAllThreadsWereIdle: public nsRunnable
|
||||
{
|
||||
public:
|
||||
|
||||
NotifyAllThreadsWereIdle(
|
||||
nsTArray<nsRefPtr<nsThreadManager::AllThreadsWereIdleListener>>* aListeners)
|
||||
: mListeners(aListeners)
|
||||
{
|
||||
}
|
||||
|
||||
virtual NS_IMETHODIMP
|
||||
Run() {
|
||||
// Copy listener array, which may be modified during call back.
|
||||
nsTArray<nsRefPtr<nsThreadManager::AllThreadsWereIdleListener>> arr(*mListeners);
|
||||
for (size_t i = 0; i < arr.Length(); i++) {
|
||||
arr[i]->OnAllThreadsWereIdle();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
// Raw pointer, since it's pointing to a member of thread manager.
|
||||
nsTArray<nsRefPtr<nsThreadManager::AllThreadsWereIdleListener>>* mListeners;
|
||||
};
|
||||
|
||||
struct nsThreadManager::ThreadStatusInfo {
|
||||
Atomic<bool> mWorking;
|
||||
Atomic<bool> mWillBeWorking;
|
||||
bool mIgnored;
|
||||
ThreadStatusInfo()
|
||||
: mWorking(false)
|
||||
, mWillBeWorking(false)
|
||||
, mIgnored(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
@ -48,6 +88,24 @@ ReleaseObject(void* aData)
|
||||
static_cast<nsISupports*>(aData)->Release();
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
void
|
||||
nsThreadManager::DeleteThreadStatusInfo(void* aData)
|
||||
{
|
||||
nsThreadManager* mgr = nsThreadManager::get();
|
||||
nsThreadManager::ThreadStatusInfo* thrInfo =
|
||||
static_cast<nsThreadManager::ThreadStatusInfo*>(aData);
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(*(mgr->mMonitor));
|
||||
mgr->mThreadStatusInfos.RemoveElement(thrInfo);
|
||||
if (NS_IsMainThread()) {
|
||||
mgr->mMainThreadStatusInfo = nullptr;
|
||||
}
|
||||
}
|
||||
delete thrInfo;
|
||||
}
|
||||
#endif
|
||||
|
||||
static PLDHashOperator
|
||||
AppendAndRemoveThread(PRThread* aKey, nsRefPtr<nsThread>& aThread, void* aArg)
|
||||
{
|
||||
@ -89,6 +147,17 @@ nsThreadManager::Init()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (PR_NewThreadPrivateIndex(
|
||||
&mThreadStatusInfoIndex,
|
||||
nsThreadManager::DeleteThreadStatusInfo) == PR_FAILURE) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
mMonitor = MakeUnique<ReentrantMonitor>("nsThreadManager.mMonitor");
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
#ifdef MOZ_CANARY
|
||||
const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
|
||||
@ -181,6 +250,9 @@ nsThreadManager::Shutdown()
|
||||
|
||||
// Remove the TLS entry for the main thread.
|
||||
PR_SetThreadPrivate(mCurThreadIndex, nullptr);
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
PR_SetThreadPrivate(mThreadStatusInfoIndex, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -213,6 +285,9 @@ nsThreadManager::UnregisterCurrentThread(nsThread* aThread)
|
||||
|
||||
PR_SetThreadPrivate(mCurThreadIndex, nullptr);
|
||||
// Ref-count balanced via ReleaseObject
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
PR_SetThreadPrivate(mThreadStatusInfoIndex, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
nsThread*
|
||||
@ -237,6 +312,27 @@ nsThreadManager::GetCurrentThread()
|
||||
return thread.get(); // reference held in TLS
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
nsThreadManager::ThreadStatusInfo*
|
||||
nsThreadManager::GetCurrentThreadStatusInfo()
|
||||
{
|
||||
void* data = PR_GetThreadPrivate(mThreadStatusInfoIndex);
|
||||
if (!data) {
|
||||
ThreadStatusInfo *thrInfo = new ThreadStatusInfo();
|
||||
PR_SetThreadPrivate(mThreadStatusInfoIndex, thrInfo);
|
||||
data = thrInfo;
|
||||
|
||||
ReentrantMonitorAutoEnter mon(*mMonitor);
|
||||
mThreadStatusInfos.AppendElement(thrInfo);
|
||||
if (NS_IsMainThread()) {
|
||||
mMainThreadStatusInfo = thrInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<ThreadStatusInfo*>(data);
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadManager::NewThread(uint32_t aCreationFlags,
|
||||
uint32_t aStackSize,
|
||||
@ -333,3 +429,157 @@ nsThreadManager::GetHighestNumberOfThreads()
|
||||
OffTheBooksMutexAutoLock lock(mLock);
|
||||
return mHighestNumberOfThreads;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
void
|
||||
nsThreadManager::SetIgnoreThreadStatus()
|
||||
{
|
||||
GetCurrentThreadStatusInfo()->mIgnored = true;
|
||||
}
|
||||
|
||||
void
|
||||
nsThreadManager::SetThreadIdle(nsIRunnable **aReturnRunnable)
|
||||
{
|
||||
SetThreadIsWorking(GetCurrentThreadStatusInfo(), false, aReturnRunnable);
|
||||
}
|
||||
|
||||
void
|
||||
nsThreadManager::SetThreadWorking()
|
||||
{
|
||||
SetThreadIsWorking(GetCurrentThreadStatusInfo(), true, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
nsThreadManager::SetThreadIsWorking(ThreadStatusInfo* aInfo,
|
||||
bool aIsWorking,
|
||||
nsIRunnable **aReturnRunnable)
|
||||
{
|
||||
aInfo->mWillBeWorking = aIsWorking;
|
||||
if (mThreadsIdledListeners.Length() > 0) {
|
||||
|
||||
// A race condition occurs since we don't want threads to try to enter the
|
||||
// monitor (nsThreadManager::mMonitor) when no one cares about their status.
|
||||
// And thus the race can happen when we put the first listener into
|
||||
// |mThreadsIdledListeners|:
|
||||
//
|
||||
// (1) Thread A wants to dispatch a task to Thread B.
|
||||
// (2) Thread A checks |mThreadsIdledListeners|, and nothing is in the
|
||||
// list. So Thread A decides not to enter |mMonitor| when updating B's
|
||||
// status.
|
||||
// (3) Thread A is suspended just before it changed status of B.
|
||||
// (4) A listener is added to |mThreadsIdledListeners|
|
||||
// (5) Now is Thread C's turn to run. Thread C finds there's something in
|
||||
// |mThreadsIdledListeners|, so it enters |mMonitor| and check all
|
||||
// thread info structs in |mThreadStatusInfos| while A is in the middle
|
||||
// of changing B's status.
|
||||
//
|
||||
// Then C may find Thread B is an idle thread (which is not correct, because
|
||||
// A attempted to change B's status prior to C starting to walk throught
|
||||
// |mThreadStatusInfo|), but the fact that thread A is working (thread A
|
||||
// hasn't finished dispatching a task to thread B) can prevent thread C from
|
||||
// firing a bogus notification.
|
||||
//
|
||||
// If the state transition that happens outside the monitor is in the other
|
||||
// direction, the race condition could be:
|
||||
//
|
||||
// (1) Thread D has just finished its jobs and wants to set its status to idle.
|
||||
// (2) Thread D checks |mThreadsIdledListeners|, and nothing is in the list.
|
||||
// So Thread D decides not to enter |mMonitor|.
|
||||
// (3) Thread D is is suspended before it updates its own status.
|
||||
// (4) A listener is put into |mThreadsIdledListeners|.
|
||||
// (5) Thread C wants to changes status of itself. It checks
|
||||
// |mThreadsIdledListeners| and finds something inside the list. Thread C
|
||||
// then enters |mMonitor|, updates its status and checks thread info in
|
||||
// |mThreadStatusInfos| while D is changing status of itself out of monitor.
|
||||
//
|
||||
// Thread C will find that thread D is working (D actually wants to change its
|
||||
// status to idle before C starting to check), then C returns without firing
|
||||
// any notification. Finding that thread D is working can make our checking
|
||||
// mechanism miss a chance to fire a notification: because thread D thought
|
||||
// there's nothing in |mThreadsIdledListeners| and thus won't check the
|
||||
// |mThreadStatusInfos| after changing the status of itself.
|
||||
//
|
||||
// |mWillBeWorking| can be used to address this problem. We require each
|
||||
// thread to put the value that is going to be set to |mWorking| to
|
||||
// |mWillBeWorking| before the thread decide whether it should enter
|
||||
// |mMonitor| to change status or not. Thus C finds that D is working while
|
||||
// D's |mWillBeWorking| is false, and C realizes that D is just updating and
|
||||
// can treat D as an idle thread.
|
||||
//
|
||||
// It doesn't matter whether D will check thread status after changing its
|
||||
// own status or not. If D checks, which means D will enter the monitor
|
||||
// before updating status, thus D must be blocked until C has finished
|
||||
// dispatching the notification task to main thread, and D will find that main
|
||||
// thread is working and will not fire an additional event. On the other hand,
|
||||
// if D doesn't check |mThreadStatusInfos|, it's still ok, because C has
|
||||
// treated D as an idle thread already.
|
||||
|
||||
bool hasWorkingThread = false;
|
||||
nsRefPtr<NotifyAllThreadsWereIdle> runnable;
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(*mMonitor);
|
||||
// Get data structure of thread info.
|
||||
aInfo->mWorking = aIsWorking;
|
||||
if (aIsWorking) {
|
||||
// We are working, so there's no need to check futher.
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mThreadStatusInfos.Length(); i++) {
|
||||
ThreadStatusInfo *info = mThreadStatusInfos[i];
|
||||
if (!info->mIgnored) {
|
||||
if (info->mWorking) {
|
||||
if (info->mWillBeWorking) {
|
||||
hasWorkingThread = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasWorkingThread && !mDispatchingToMainThread) {
|
||||
runnable = new NotifyAllThreadsWereIdle(&mThreadsIdledListeners);
|
||||
mDispatchingToMainThread = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (runnable) {
|
||||
if (NS_IsMainThread()) {
|
||||
// We are holding the main thread's |nsThread::mThreadStatusMonitor|.
|
||||
// If we dispatch a task to ourself, then we are in danger of causing
|
||||
// deadlock. Instead, return the task, and let the caller dispatch it
|
||||
// for us.
|
||||
MOZ_ASSERT(aReturnRunnable,
|
||||
"aReturnRunnable must be provided on main thread");
|
||||
runnable.forget(aReturnRunnable);
|
||||
} else {
|
||||
NS_DispatchToMainThread(runnable);
|
||||
ResetIsDispatchingToMainThread();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Update thread info without holding any lock.
|
||||
aInfo->mWorking = aIsWorking;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsThreadManager::ResetIsDispatchingToMainThread()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(*mMonitor);
|
||||
mDispatchingToMainThread = false;
|
||||
}
|
||||
|
||||
void
|
||||
nsThreadManager::AddAllThreadsWereIdleListener(AllThreadsWereIdleListener *listener)
|
||||
{
|
||||
MOZ_ASSERT(GetCurrentThreadStatusInfo()->mWorking);
|
||||
mThreadsIdledListeners.AppendElement(listener);
|
||||
}
|
||||
|
||||
void
|
||||
nsThreadManager::RemoveAllThreadsWereIdleListener(AllThreadsWereIdleListener *listener)
|
||||
{
|
||||
mThreadsIdledListeners.RemoveElement(listener);
|
||||
}
|
||||
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
@ -14,9 +14,26 @@
|
||||
|
||||
class nsIRunnable;
|
||||
|
||||
namespace mozilla {
|
||||
class ReentrantMonitor;
|
||||
}
|
||||
|
||||
class nsThreadManager : public nsIThreadManager
|
||||
{
|
||||
public:
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
struct ThreadStatusInfo;
|
||||
class AllThreadsWereIdleListener {
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(AllThreadsWereIdleListener);
|
||||
virtual void OnAllThreadsWereIdle() = 0;
|
||||
protected:
|
||||
virtual ~AllThreadsWereIdleListener()
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITHREADMANAGER
|
||||
|
||||
@ -54,6 +71,31 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
void SetIgnoreThreadStatus();
|
||||
|
||||
// |SetThreadWorking| and |SetThreadIdle| set status of thread that is
|
||||
// currently running. They get thread status information from TLS and pass
|
||||
// the information to |SetThreadIsWorking|.
|
||||
void SetThreadIdle(nsIRunnable** aReturnRunnable);
|
||||
void SetThreadWorking();
|
||||
|
||||
// |SetThreadIsWorking| is where is status actually changed. Thread status
|
||||
// information is passed as a argument so caller must obtain the structure
|
||||
// by itself. If this method is invoked on main thread, |aReturnRunnable|
|
||||
// should be provided to receive the runnable of notifying listeners.
|
||||
// |ResetIsDispatchingToMainThread| should be invoked after caller on main
|
||||
// thread dispatched the task to main thread's queue.
|
||||
void SetThreadIsWorking(ThreadStatusInfo* aInfo,
|
||||
bool aIsWorking,
|
||||
nsIRunnable** aReturnRunnable);
|
||||
void ResetIsDispatchingToMainThread();
|
||||
|
||||
void AddAllThreadsWereIdleListener(AllThreadsWereIdleListener *listener);
|
||||
void RemoveAllThreadsWereIdleListener(AllThreadsWereIdleListener *listener);
|
||||
ThreadStatusInfo* GetCurrentThreadStatusInfo();
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
|
||||
private:
|
||||
nsThreadManager()
|
||||
: mCurThreadIndex(0)
|
||||
@ -62,6 +104,11 @@ private:
|
||||
, mInitialized(false)
|
||||
, mCurrentNumberOfThreads(1)
|
||||
, mHighestNumberOfThreads(1)
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
, mMonitor(nullptr)
|
||||
, mMainThreadStatusInfo(nullptr)
|
||||
, mDispatchingToMainThread(nullptr)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@ -76,6 +123,19 @@ private:
|
||||
uint32_t mCurrentNumberOfThreads;
|
||||
// The highest number of threads encountered so far during the session
|
||||
uint32_t mHighestNumberOfThreads;
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
static void DeleteThreadStatusInfo(void *aData);
|
||||
unsigned mThreadStatusInfoIndex;
|
||||
nsTArray<nsRefPtr<AllThreadsWereIdleListener>> mThreadsIdledListeners;
|
||||
nsTArray<ThreadStatusInfo*> mThreadStatusInfos;
|
||||
mozilla::UniquePtr<mozilla::ReentrantMonitor> mMonitor;
|
||||
ThreadStatusInfo* mMainThreadStatusInfo;
|
||||
// |mDispatchingToMainThread| is set when all thread are found to be idle
|
||||
// before task of notifying all listeners are dispatched to main thread.
|
||||
// The flag is protected by |mMonitor|.
|
||||
bool mDispatchingToMainThread;
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
};
|
||||
|
||||
#define NS_THREADMANAGER_CID \
|
||||
|
@ -151,7 +151,6 @@ NS_IMETHODIMP
|
||||
nsThreadPool::Run()
|
||||
{
|
||||
LOG(("THRD-P(%p) enter\n", this));
|
||||
|
||||
mThreadNaming.SetThreadPoolName(mName);
|
||||
|
||||
nsCOMPtr<nsIThread> current;
|
||||
@ -209,6 +208,9 @@ nsThreadPool::Run()
|
||||
} else {
|
||||
PRIntervalTime delta = timeout - (now - idleSince);
|
||||
LOG(("THRD-P(%p) waiting [%d]\n", this, delta));
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
nsThreadManager::get()->SetThreadIdle(nullptr);
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
mon.Wait(delta);
|
||||
}
|
||||
} else if (wasIdle) {
|
||||
@ -218,6 +220,9 @@ nsThreadPool::Run()
|
||||
}
|
||||
if (event) {
|
||||
LOG(("THRD-P(%p) running [%p]\n", this, event.get()));
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
nsThreadManager::get()->SetThreadWorking();
|
||||
#endif // MOZ_NUWA_PROCESS
|
||||
event->Run();
|
||||
}
|
||||
} while (!exitThread);
|
||||
|
@ -549,6 +549,13 @@ nsTimerImpl::Fire()
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess() && IsNuwaReady()) {
|
||||
// A timer event fired after Nuwa frozen can freeze main thread.
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(MOZILLA_XPCOMRT_API)
|
||||
PROFILER_LABEL("Timer", "Fire",
|
||||
js::ProfileEntry::Category::OTHER);
|
||||
|
Loading…
Reference in New Issue
Block a user