Bug 970307: Part 2: Let Nuwa wait for all tasks of preload slow things to finish. r=cyu

This commit is contained in:
Chih-Kai (Patrick) Wang 2014-12-18 17:01:04 +08:00
parent 4ee772690e
commit c0ae800cc6
7 changed files with 104 additions and 23 deletions

View File

@ -1243,7 +1243,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 =
@ -1991,8 +1990,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
@ -2080,12 +2082,25 @@ ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID,
// BrowserElementChild.js.
if ((mIsForApp || mIsForBrowser)
#ifdef MOZ_NUWA_PROCESS
&& !IsNuwaProcess()
&& IsNuwaProcess()
#endif
) {
PreloadSlowThings();
}
#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();
@ -2093,7 +2108,6 @@ ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID,
FROM_HERE, NewRunnableFunction(OnFinishNuwaPreparation));
}
#endif
return true;
}
@ -2344,6 +2358,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
@ -2355,22 +2399,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.

View File

@ -350,6 +350,8 @@ public:
virtual bool RecvNotifyPhoneStateChange(const nsString& state) MOZ_OVERRIDE;
virtual bool RecvNuwaFreeze() MOZ_OVERRIDE;
void AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
void RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
virtual bool RecvNotifyIdleObserver(const uint64_t& aObserver,

View File

@ -124,6 +124,7 @@
#include "nsServiceManagerUtils.h"
#include "nsStyleSheetService.h"
#include "nsThreadUtils.h"
#include "nsThreadManager.h"
#include "nsToolkitCompsCID.h"
#include "nsWidgetsCID.h"
#include "PreallocatedProcessManager.h"
@ -1372,6 +1373,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
@ -2062,6 +2085,8 @@ ContentParent::ContentParent(ContentParent* aTemplate,
priority = PROCESS_PRIORITY_FOREGROUND;
}
mSendPermissionUpdates = aTemplate->mSendPermissionUpdates;
InitInternal(priority,
false, /* Setup Off-main thread compositing */
false /* Send registered chrome */);
@ -2219,7 +2244,7 @@ ContentParent::IsForApp()
#ifdef MOZ_NUWA_PROCESS
bool
ContentParent::IsNuwaProcess()
ContentParent::IsNuwaProcess() const
{
return mIsNuwaProcess;
}
@ -2568,6 +2593,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,
const InfallibleTArray<ProtocolFdMapping>& aFds)

View File

@ -206,7 +206,7 @@ public:
return mIsForBrowser;
}
#ifdef MOZ_NUWA_PROCESS
bool IsNuwaProcess();
bool IsNuwaProcess() const;
#endif
GeckoChildProcessHost* Process() {
@ -220,7 +220,11 @@ public:
}
bool NeedsPermissionsUpdate() const {
#ifdef MOZ_NUWA_PROCESS
return !IsNuwaProcess() && mSendPermissionUpdates;
#else
return mSendPermissionUpdates;
#endif
}
bool NeedsDataStoreInfos() const {
@ -680,6 +684,8 @@ private:
virtual bool RecvNuwaReady() MOZ_OVERRIDE;
virtual bool RecvNuwaWaitForFreeze() MOZ_OVERRIDE;
virtual bool RecvAddNewProcess(const uint32_t& aPid,
const InfallibleTArray<ProtocolFdMapping>& aFds) MOZ_OVERRIDE;

View File

@ -524,6 +524,7 @@ child:
intr GetProfile()
returns (nsCString aProfile);
NuwaFreeze();
parent:
/**
* Tell the parent process a new accessible document has been created.
@ -735,6 +736,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);

View File

@ -77,7 +77,7 @@ function runTest()
let timeout = setTimeout(function() {
ok(false, "Nuwa process is not launched");
testEnd();
}, 60000);
}, 240000);
function testEnd() {
cpmm.removeMessageListener("TEST-ONLY:nuwa-ready", msgHandler);

View File

@ -78,7 +78,7 @@ function runTest()
let timeout = setTimeout(function() {
ok(false, "Nuwa process is not launched");
testEnd();
}, 90000);
}, 240000);
function testEnd() {
cpmm.removeMessageListener("TEST-ONLY:nuwa-ready", msgHandler);