Bug 928995: Move Nuwa to PreallocatedProcessManager. r=bent

This commit is contained in:
Patrick Wang 2013-11-22 11:25:34 +08:00
parent 59a3ad26e7
commit 1d1cdf444c
4 changed files with 250 additions and 192 deletions

View File

@ -121,10 +121,6 @@ using namespace mozilla::system;
#include "BluetoothService.h" #include "BluetoothService.h"
#endif #endif
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
#endif
#include "JavaScriptParent.h" #include "JavaScriptParent.h"
#ifdef MOZ_B2G_FM #ifdef MOZ_B2G_FM
@ -140,8 +136,6 @@ using namespace mozilla::system;
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
static const char* sClipboardTextFlavors[] = { kUnicodeMime }; static const char* sClipboardTextFlavors[] = { kUnicodeMime };
#define NUWA_FORK_WAIT_DURATION_MS 2000 // 2 seconds.
using base::ChildPrivileges; using base::ChildPrivileges;
using base::KillProcess; using base::KillProcess;
using namespace mozilla::dom::bluetooth; using namespace mozilla::dom::bluetooth;
@ -273,32 +267,16 @@ static bool sCanLaunchSubprocesses;
// The first content child has ID 1, so the chrome process can have ID 0. // The first content child has ID 1, so the chrome process can have ID 0.
static uint64_t gContentChildID = 1; static uint64_t gContentChildID = 1;
// sNuwaProcess points to the Nuwa process which is used for forking new
// processes later.
static StaticRefPtr<ContentParent> sNuwaProcess;
// Nuwa process is ready for creating new process.
static bool sNuwaReady = false;
// The array containing the preallocated processes. 4 as the inline storage size
// should be enough so we don't need to grow the nsAutoTArray.
static StaticAutoPtr<nsAutoTArray<nsRefPtr<ContentParent>, 4> > sSpareProcesses;
static StaticAutoPtr<nsTArray<CancelableTask*> > sNuwaForkWaitTasks;
// We want the prelaunched process to know that it's for apps, but not // We want the prelaunched process to know that it's for apps, but not
// actually for any app in particular. Use a magic manifest URL. // actually for any app in particular. Use a magic manifest URL.
// Can't be a static constant. // Can't be a static constant.
#define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}") #define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}")
void /* static */ already_AddRefed<ContentParent>
ContentParent::RunNuwaProcess() ContentParent::RunNuwaProcess()
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<ContentParent> nuwaProcess =
if (sNuwaProcess) {
NS_RUNTIMEABORT("sNuwaProcess is created twice.");
}
sNuwaProcess =
new ContentParent(/* aApp = */ nullptr, new ContentParent(/* aApp = */ nullptr,
/* aIsForBrowser = */ false, /* aIsForBrowser = */ false,
/* aIsForPreallocated = */ true, /* aIsForPreallocated = */ true,
@ -307,109 +285,10 @@ ContentParent::RunNuwaProcess()
base::PRIVILEGES_INHERIT, base::PRIVILEGES_INHERIT,
PROCESS_PRIORITY_BACKGROUND, PROCESS_PRIORITY_BACKGROUND,
/* aIsNuwaProcess = */ true); /* aIsNuwaProcess = */ true);
sNuwaProcess->Init(); nuwaProcess->Init();
return nuwaProcess.forget();
} }
#ifdef MOZ_NUWA_PROCESS
// initialization off the critical path of app startup.
static CancelableTask* sPreallocateAppProcessTask;
// This number is fairly arbitrary ... the intention is to put off
// launching another app process until the last one has finished
// loading its content, to reduce CPU/memory/IO contention.
static int sPreallocateDelayMs = 1000;
static void
DelayedNuwaFork()
{
MOZ_ASSERT(NS_IsMainThread());
sPreallocateAppProcessTask = nullptr;
if (!sNuwaReady) {
if (!sNuwaProcess) {
ContentParent::RunNuwaProcess();
}
// else sNuwaProcess is starting. It will SendNuwaFork() when ready.
} else if (sSpareProcesses->IsEmpty()) {
sNuwaProcess->SendNuwaFork();
}
}
static void
ScheduleDelayedNuwaFork()
{
MOZ_ASSERT(NS_IsMainThread());
if (sPreallocateAppProcessTask) {
// Make sure there is only one request running.
return;
}
sPreallocateAppProcessTask = NewRunnableFunction(DelayedNuwaFork);
MessageLoop::current()->
PostDelayedTask(FROM_HERE,
sPreallocateAppProcessTask,
sPreallocateDelayMs);
}
/**
* Get a spare ContentParent from sSpareProcesses list.
*/
static already_AddRefed<ContentParent>
GetSpareProcess()
{
MOZ_ASSERT(NS_IsMainThread());
if (sSpareProcesses->IsEmpty()) {
return nullptr;
}
nsRefPtr<ContentParent> process = sSpareProcesses->LastElement();
sSpareProcesses->RemoveElementAt(sSpareProcesses->Length() - 1);
if (sSpareProcesses->IsEmpty() && sNuwaReady) {
NS_ASSERTION(sNuwaProcess != nullptr,
"Nuwa process is not present!");
ScheduleDelayedNuwaFork();
}
return process.forget();
}
/**
* Publish a ContentParent to spare process list.
*/
static void
PublishSpareProcess(ContentParent* aContent)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sNuwaForkWaitTasks->IsEmpty()) {
sNuwaForkWaitTasks->ElementAt(0)->Cancel();
sNuwaForkWaitTasks->RemoveElementAt(0);
}
sSpareProcesses->AppendElement(aContent);
}
static void
MaybeForgetSpare(ContentParent* aContent)
{
MOZ_ASSERT(NS_IsMainThread());
if (sSpareProcesses->RemoveElement(aContent)) {
return;
}
if (aContent == sNuwaProcess) {
sNuwaProcess = nullptr;
sNuwaReady = false;
ScheduleDelayedNuwaFork();
}
}
#endif
// PreallocateAppProcess is called by the PreallocatedProcessManager. // PreallocateAppProcess is called by the PreallocatedProcessManager.
// ContentParent then takes this process back within // ContentParent then takes this process back within
// MaybeTakePreallocatedAppProcess. // MaybeTakePreallocatedAppProcess.
@ -433,11 +312,7 @@ ContentParent::MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL,
ChildPrivileges aPrivs, ChildPrivileges aPrivs,
ProcessPriority aInitialPriority) ProcessPriority aInitialPriority)
{ {
#ifdef MOZ_NUWA_PROCESS
nsRefPtr<ContentParent> process = GetSpareProcess();
#else
nsRefPtr<ContentParent> process = PreallocatedProcessManager::Take(); nsRefPtr<ContentParent> process = PreallocatedProcessManager::Take();
#endif
if (!process) { if (!process) {
return nullptr; return nullptr;
} }
@ -464,17 +339,8 @@ ContentParent::StartUp()
sCanLaunchSubprocesses = true; sCanLaunchSubprocesses = true;
sSpareProcesses = new nsAutoTArray<nsRefPtr<ContentParent>, 4>();
ClearOnShutdown(&sSpareProcesses);
sNuwaForkWaitTasks = new nsTArray<CancelableTask*>();
ClearOnShutdown(&sNuwaForkWaitTasks);
#ifdef MOZ_NUWA_PROCESS
ScheduleDelayedNuwaFork();
#else
// Try to preallocate a process that we can transform into an app later. // Try to preallocate a process that we can transform into an app later.
PreallocatedProcessManager::AllocateAfterDelay(); PreallocatedProcessManager::AllocateAfterDelay();
#endif
} }
/*static*/ void /*static*/ void
@ -1027,25 +893,13 @@ ContentParent::MarkAsDead()
mIsAlive = false; mIsAlive = false;
} }
void
ContentParent::OnNuwaForkTimeout()
{
if (!sNuwaForkWaitTasks->IsEmpty()) {
sNuwaForkWaitTasks->RemoveElementAt(0);
}
// We haven't RecvAddNewProcess() after SendNuwaFork(). Maybe the main
// thread of the Nuwa process is in deadlock.
MOZ_ASSERT(false, "Can't fork from the nuwa process.");
}
void void
ContentParent::OnChannelError() ContentParent::OnChannelError()
{ {
nsRefPtr<ContentParent> content(this); nsRefPtr<ContentParent> content(this);
PContentParent::OnChannelError(); PContentParent::OnChannelError();
#ifdef MOZ_NUWA_PROCESS #ifdef MOZ_NUWA_PROCESS
MaybeForgetSpare(this); PreallocatedProcessManager::MaybeForgetSpare(this);
#endif #endif
} }
@ -1801,19 +1655,12 @@ ContentParent::RecvGetShowPasswordSetting(bool* showPassword)
bool bool
ContentParent::RecvFirstIdle() ContentParent::RecvFirstIdle()
{ {
#ifdef MOZ_NUWA_PROCESS
if (sSpareProcesses->IsEmpty() && sNuwaReady) {
ScheduleDelayedNuwaFork();
}
return true;
#else
// When the ContentChild goes idle, it sends us a FirstIdle message // When the ContentChild goes idle, it sends us a FirstIdle message
// which we use as a good time to prelaunch another process. If we // which we use as a good time to prelaunch another process. If we
// prelaunch any sooner than this, then we'll be competing with the // prelaunch any sooner than this, then we'll be competing with the
// child process and slowing it down. // child process and slowing it down.
PreallocatedProcessManager::AllocateAfterDelay(); PreallocatedProcessManager::AllocateAfterDelay();
return true; return true;
#endif
} }
bool bool
@ -1897,34 +1744,16 @@ ContentParent::RecvBroadcastVolume(const nsString& aVolumeName)
#endif #endif
} }
bool
ContentParent::SendNuwaFork()
{
if (this != sNuwaProcess) {
return false;
}
CancelableTask* nuwaForkTimeoutTask = NewRunnableMethod(
this, &ContentParent::OnNuwaForkTimeout);
sNuwaForkWaitTasks->AppendElement(nuwaForkTimeoutTask);
MessageLoop::current()->
PostDelayedTask(FROM_HERE,
nuwaForkTimeoutTask,
NUWA_FORK_WAIT_DURATION_MS);
return PContentParent::SendNuwaFork();
}
bool bool
ContentParent::RecvNuwaReady() ContentParent::RecvNuwaReady()
{ {
NS_ASSERTION(!sNuwaReady, "Multiple Nuwa processes created!"); #ifdef MOZ_NUWA_PROCESS
ProcessPriorityManager::SetProcessPriority(sNuwaProcess, PreallocatedProcessManager::OnNuwaReady();
hal::PROCESS_PRIORITY_FOREGROUND);
sNuwaReady = true;
SendNuwaFork();
return true; return true;
#else
NS_ERROR("ContentParent::RecvNuwaReady() not implemented!");
return false;
#endif
} }
bool bool
@ -1939,7 +1768,7 @@ ContentParent::RecvAddNewProcess(const uint32_t& aPid,
aFds, aFds,
base::PRIVILEGES_DEFAULT); base::PRIVILEGES_DEFAULT);
content->Init(); content->Init();
PublishSpareProcess(content); PreallocatedProcessManager::PublishSpareProcess(content);
return true; return true;
#else #else
NS_ERROR("ContentParent::RecvAddNewProcess() not implemented!"); NS_ERROR("ContentParent::RecvAddNewProcess() not implemented!");

View File

@ -92,7 +92,7 @@ public:
*/ */
static already_AddRefed<ContentParent> PreallocateAppProcess(); static already_AddRefed<ContentParent> PreallocateAppProcess();
static void RunNuwaProcess(); static already_AddRefed<ContentParent> RunNuwaProcess();
/** /**
* Get or create a content process for the given TabContext. aFrameElement * Get or create a content process for the given TabContext. aFrameElement
@ -209,8 +209,6 @@ public:
return PContentParent::RecvPJavaScriptConstructor(aActor); return PContentParent::RecvPJavaScriptConstructor(aActor);
} }
virtual bool SendNuwaFork();
protected: protected:
void OnChannelConnected(int32_t pid) MOZ_OVERRIDE; void OnChannelConnected(int32_t pid) MOZ_OVERRIDE;
virtual void ActorDestroy(ActorDestroyReason why); virtual void ActorDestroy(ActorDestroyReason why);

View File

@ -9,6 +9,19 @@
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentParent.h"
#include "nsIPropertyBag2.h" #include "nsIPropertyBag2.h"
#include "ProcessPriorityManager.h"
#include "nsServiceManagerUtils.h"
#include "nsCxPusher.h"
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
#endif
// This number is fairly arbitrary ... the intention is to put off
// launching another app process until the last one has finished
// loading its content, to reduce CPU/memory/IO contention.
#define DEFAULT_ALLOCATE_DELAY 1000
#define NUWA_FORK_WAIT_DURATION_MS 2000 // 2 seconds.
using namespace mozilla; using namespace mozilla;
using namespace mozilla::hal; using namespace mozilla::hal;
@ -35,6 +48,31 @@ public:
void AllocateNow(); void AllocateNow();
already_AddRefed<ContentParent> Take(); already_AddRefed<ContentParent> Take();
#ifdef MOZ_NUWA_PROCESS
public:
void ScheduleDelayedNuwaFork();
void DelayedNuwaFork();
void PublishSpareProcess(ContentParent* aContent);
void MaybeForgetSpare(ContentParent* aContent);
void OnNuwaReady();
already_AddRefed<ContentParent> GetSpareProcess();
private:
void OnNuwaForkTimeout();
void NuwaFork();
// initialization off the critical path of app startup.
CancelableTask* mPreallocateAppProcessTask;
// The array containing the preallocated processes. 4 as the inline storage size
// should be enough so we don't need to grow the nsAutoTArray.
nsAutoTArray<nsRefPtr<ContentParent>, 4> mSpareProcesses;
nsTArray<CancelableTask*> mNuwaForkWaitTasks;
// Nuwa process is ready for creating new process.
bool mIsNuwaReady;
#endif
private: private:
static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton; static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
@ -72,6 +110,10 @@ NS_IMPL_ISUPPORTS1(PreallocatedProcessManagerImpl, nsIObserver)
PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl() PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
: mEnabled(false) : mEnabled(false)
#ifdef MOZ_NUWA_PROCESS
, mPreallocateAppProcessTask(nullptr)
, mIsNuwaReady(false)
#endif
{} {}
void void
@ -127,7 +169,11 @@ PreallocatedProcessManagerImpl::Enable()
} }
mEnabled = true; mEnabled = true;
#ifdef MOZ_NUWA_PROCESS
ScheduleDelayedNuwaFork();
#else
AllocateAfterDelay(); AllocateAfterDelay();
#endif
} }
void void
@ -140,7 +186,8 @@ PreallocatedProcessManagerImpl::AllocateAfterDelay()
MessageLoop::current()->PostDelayedTask( MessageLoop::current()->PostDelayedTask(
FROM_HERE, FROM_HERE,
NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle), NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle),
Preferences::GetUint("dom.ipc.processPrelaunch.delayMs", 1000)); Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
DEFAULT_ALLOCATE_DELAY));
} }
void void
@ -165,6 +212,135 @@ PreallocatedProcessManagerImpl::AllocateNow()
mPreallocatedAppProcess = ContentParent::PreallocateAppProcess(); mPreallocatedAppProcess = ContentParent::PreallocateAppProcess();
} }
#ifdef MOZ_NUWA_PROCESS
void
PreallocatedProcessManagerImpl::ScheduleDelayedNuwaFork()
{
MOZ_ASSERT(NS_IsMainThread());
if (mPreallocateAppProcessTask) {
// Make sure there is only one request running.
return;
}
mPreallocateAppProcessTask = NewRunnableMethod(
this, &PreallocatedProcessManagerImpl::DelayedNuwaFork);
MessageLoop::current()->PostDelayedTask(
FROM_HERE, mPreallocateAppProcessTask,
Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
DEFAULT_ALLOCATE_DELAY));
}
void
PreallocatedProcessManagerImpl::DelayedNuwaFork()
{
MOZ_ASSERT(NS_IsMainThread());
mPreallocateAppProcessTask = nullptr;
if (!mIsNuwaReady) {
if (!mPreallocatedAppProcess) {
mPreallocatedAppProcess = ContentParent::RunNuwaProcess();
}
// else mPreallocatedAppProcess is starting. It will NuwaFork() when ready.
} else if (mSpareProcesses.IsEmpty()) {
NuwaFork();
}
}
/**
* Get a spare ContentParent from mSpareProcesses list.
*/
already_AddRefed<ContentParent>
PreallocatedProcessManagerImpl::GetSpareProcess()
{
MOZ_ASSERT(NS_IsMainThread());
if (mSpareProcesses.IsEmpty()) {
return nullptr;
}
nsRefPtr<ContentParent> process = mSpareProcesses.LastElement();
mSpareProcesses.RemoveElementAt(mSpareProcesses.Length() - 1);
if (mSpareProcesses.IsEmpty() && mIsNuwaReady) {
NS_ASSERTION(mPreallocatedAppProcess != nullptr,
"Nuwa process is not present!");
ScheduleDelayedNuwaFork();
}
return process.forget();
}
/**
* Publish a ContentParent to spare process list.
*/
void
PreallocatedProcessManagerImpl::PublishSpareProcess(ContentParent* aContent)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mNuwaForkWaitTasks.IsEmpty()) {
mNuwaForkWaitTasks.ElementAt(0)->Cancel();
mNuwaForkWaitTasks.RemoveElementAt(0);
}
mSpareProcesses.AppendElement(aContent);
}
void
PreallocatedProcessManagerImpl::MaybeForgetSpare(ContentParent* aContent)
{
MOZ_ASSERT(NS_IsMainThread());
if (mSpareProcesses.RemoveElement(aContent)) {
return;
}
if (aContent == mPreallocatedAppProcess) {
mPreallocatedAppProcess = nullptr;
mIsNuwaReady = false;
ScheduleDelayedNuwaFork();
}
}
void
PreallocatedProcessManagerImpl::OnNuwaReady()
{
NS_ASSERTION(!mIsNuwaReady, "Multiple Nuwa processes created!");
ProcessPriorityManager::SetProcessPriority(mPreallocatedAppProcess,
hal::PROCESS_PRIORITY_FOREGROUND);
mIsNuwaReady = true;
NuwaFork();
}
void
PreallocatedProcessManagerImpl::OnNuwaForkTimeout()
{
if (!mNuwaForkWaitTasks.IsEmpty()) {
mNuwaForkWaitTasks.RemoveElementAt(0);
}
// We haven't RecvAddNewProcess() after NuwaFork(). Maybe the main
// thread of the Nuwa process is in deadlock.
MOZ_ASSERT(false, "Can't fork from the nuwa process.");
}
void
PreallocatedProcessManagerImpl::NuwaFork()
{
CancelableTask* nuwaForkTimeoutTask = NewRunnableMethod(
this, &PreallocatedProcessManagerImpl::OnNuwaForkTimeout);
mNuwaForkWaitTasks.AppendElement(nuwaForkTimeoutTask);
MessageLoop::current()->PostDelayedTask(FROM_HERE,
nuwaForkTimeoutTask,
NUWA_FORK_WAIT_DURATION_MS);
mPreallocatedAppProcess->SendNuwaFork();
}
#endif
void void
PreallocatedProcessManagerImpl::Disable() PreallocatedProcessManagerImpl::Disable()
{ {
@ -174,8 +350,24 @@ PreallocatedProcessManagerImpl::Disable()
mEnabled = false; mEnabled = false;
#ifdef MOZ_NUWA_PROCESS
// Cancel pending fork.
if (mPreallocateAppProcessTask) {
mPreallocateAppProcessTask->Cancel();
mPreallocateAppProcessTask = nullptr;
}
#endif
if (mPreallocatedAppProcess) { if (mPreallocatedAppProcess) {
mPreallocatedAppProcess->ShutDown(); #ifdef MOZ_NUWA_PROCESS
while (mSpareProcesses.Length() > 0){
nsRefPtr<ContentParent> process = mSpareProcesses[0];
process->Close();
mSpareProcesses.RemoveElementAt(0);
}
mIsNuwaReady = false;
#endif
mPreallocatedAppProcess->Close();
mPreallocatedAppProcess = nullptr; mPreallocatedAppProcess = nullptr;
} }
} }
@ -199,6 +391,11 @@ PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
} }
} }
inline PreallocatedProcessManagerImpl* GetPPMImpl()
{
return PreallocatedProcessManagerImpl::Singleton();
}
} // anonymous namespace } // anonymous namespace
namespace mozilla { namespace mozilla {
@ -206,25 +403,53 @@ namespace mozilla {
/* static */ void /* static */ void
PreallocatedProcessManager::AllocateAfterDelay() PreallocatedProcessManager::AllocateAfterDelay()
{ {
PreallocatedProcessManagerImpl::Singleton()->AllocateAfterDelay(); #ifdef MOZ_NUWA_PROCESS
GetPPMImpl()->ScheduleDelayedNuwaFork();
#else
GetPPMImpl()->AllocateAfterDelay();
#endif
} }
/* static */ void /* static */ void
PreallocatedProcessManager::AllocateOnIdle() PreallocatedProcessManager::AllocateOnIdle()
{ {
PreallocatedProcessManagerImpl::Singleton()->AllocateOnIdle(); GetPPMImpl()->AllocateOnIdle();
} }
/* static */ void /* static */ void
PreallocatedProcessManager::AllocateNow() PreallocatedProcessManager::AllocateNow()
{ {
PreallocatedProcessManagerImpl::Singleton()->AllocateNow(); GetPPMImpl()->AllocateNow();
} }
/* static */ already_AddRefed<ContentParent> /* static */ already_AddRefed<ContentParent>
PreallocatedProcessManager::Take() PreallocatedProcessManager::Take()
{ {
return PreallocatedProcessManagerImpl::Singleton()->Take(); #ifdef MOZ_NUWA_PROCESS
return GetPPMImpl()->GetSpareProcess();
#else
return GetPPMImpl()->Take();
#endif
} }
#ifdef MOZ_NUWA_PROCESS
/* static */ void
PreallocatedProcessManager::PublishSpareProcess(ContentParent* aContent)
{
GetPPMImpl()->PublishSpareProcess(aContent);
}
/* static */ void
PreallocatedProcessManager::MaybeForgetSpare(ContentParent* aContent)
{
GetPPMImpl()->MaybeForgetSpare(aContent);
}
/* static */ void
PreallocatedProcessManager::OnNuwaReady()
{
GetPPMImpl()->OnNuwaReady();
}
#endif
} // namespace mozilla } // namespace mozilla

View File

@ -78,6 +78,12 @@ public:
*/ */
static already_AddRefed<ContentParent> Take(); static already_AddRefed<ContentParent> Take();
#ifdef MOZ_NUWA_PROCESS
static void PublishSpareProcess(ContentParent* aContent);
static void MaybeForgetSpare(ContentParent* aContent);
static void OnNuwaReady();
#endif
private: private:
PreallocatedProcessManager(); PreallocatedProcessManager();
DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager); DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager);