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"
#endif
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
#endif
#include "JavaScriptParent.h"
#ifdef MOZ_B2G_FM
@ -140,8 +136,6 @@ using namespace mozilla::system;
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
static const char* sClipboardTextFlavors[] = { kUnicodeMime };
#define NUWA_FORK_WAIT_DURATION_MS 2000 // 2 seconds.
using base::ChildPrivileges;
using base::KillProcess;
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.
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
// actually for any app in particular. Use a magic manifest URL.
// Can't be a static constant.
#define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}")
void
/* static */ already_AddRefed<ContentParent>
ContentParent::RunNuwaProcess()
{
MOZ_ASSERT(NS_IsMainThread());
if (sNuwaProcess) {
NS_RUNTIMEABORT("sNuwaProcess is created twice.");
}
sNuwaProcess =
nsRefPtr<ContentParent> nuwaProcess =
new ContentParent(/* aApp = */ nullptr,
/* aIsForBrowser = */ false,
/* aIsForPreallocated = */ true,
@ -307,109 +285,10 @@ ContentParent::RunNuwaProcess()
base::PRIVILEGES_INHERIT,
PROCESS_PRIORITY_BACKGROUND,
/* 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.
// ContentParent then takes this process back within
// MaybeTakePreallocatedAppProcess.
@ -433,11 +312,7 @@ ContentParent::MaybeTakePreallocatedAppProcess(const nsAString& aAppManifestURL,
ChildPrivileges aPrivs,
ProcessPriority aInitialPriority)
{
#ifdef MOZ_NUWA_PROCESS
nsRefPtr<ContentParent> process = GetSpareProcess();
#else
nsRefPtr<ContentParent> process = PreallocatedProcessManager::Take();
#endif
if (!process) {
return nullptr;
}
@ -464,17 +339,8 @@ ContentParent::StartUp()
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.
PreallocatedProcessManager::AllocateAfterDelay();
#endif
}
/*static*/ void
@ -1027,25 +893,13 @@ ContentParent::MarkAsDead()
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
ContentParent::OnChannelError()
{
nsRefPtr<ContentParent> content(this);
PContentParent::OnChannelError();
#ifdef MOZ_NUWA_PROCESS
MaybeForgetSpare(this);
PreallocatedProcessManager::MaybeForgetSpare(this);
#endif
}
@ -1801,19 +1655,12 @@ ContentParent::RecvGetShowPasswordSetting(bool* showPassword)
bool
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
// 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
// child process and slowing it down.
PreallocatedProcessManager::AllocateAfterDelay();
return true;
#endif
}
bool
@ -1897,34 +1744,16 @@ ContentParent::RecvBroadcastVolume(const nsString& aVolumeName)
#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
ContentParent::RecvNuwaReady()
{
NS_ASSERTION(!sNuwaReady, "Multiple Nuwa processes created!");
ProcessPriorityManager::SetProcessPriority(sNuwaProcess,
hal::PROCESS_PRIORITY_FOREGROUND);
sNuwaReady = true;
SendNuwaFork();
#ifdef MOZ_NUWA_PROCESS
PreallocatedProcessManager::OnNuwaReady();
return true;
#else
NS_ERROR("ContentParent::RecvNuwaReady() not implemented!");
return false;
#endif
}
bool
@ -1939,7 +1768,7 @@ ContentParent::RecvAddNewProcess(const uint32_t& aPid,
aFds,
base::PRIVILEGES_DEFAULT);
content->Init();
PublishSpareProcess(content);
PreallocatedProcessManager::PublishSpareProcess(content);
return true;
#else
NS_ERROR("ContentParent::RecvAddNewProcess() not implemented!");

View File

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

View File

@ -9,6 +9,19 @@
#include "mozilla/Preferences.h"
#include "mozilla/dom/ContentParent.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::hal;
@ -35,6 +48,31 @@ public:
void AllocateNow();
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:
static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
@ -72,6 +110,10 @@ NS_IMPL_ISUPPORTS1(PreallocatedProcessManagerImpl, nsIObserver)
PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
: mEnabled(false)
#ifdef MOZ_NUWA_PROCESS
, mPreallocateAppProcessTask(nullptr)
, mIsNuwaReady(false)
#endif
{}
void
@ -127,7 +169,11 @@ PreallocatedProcessManagerImpl::Enable()
}
mEnabled = true;
#ifdef MOZ_NUWA_PROCESS
ScheduleDelayedNuwaFork();
#else
AllocateAfterDelay();
#endif
}
void
@ -140,7 +186,8 @@ PreallocatedProcessManagerImpl::AllocateAfterDelay()
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle),
Preferences::GetUint("dom.ipc.processPrelaunch.delayMs", 1000));
Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
DEFAULT_ALLOCATE_DELAY));
}
void
@ -165,6 +212,135 @@ PreallocatedProcessManagerImpl::AllocateNow()
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
PreallocatedProcessManagerImpl::Disable()
{
@ -174,8 +350,24 @@ PreallocatedProcessManagerImpl::Disable()
mEnabled = false;
#ifdef MOZ_NUWA_PROCESS
// Cancel pending fork.
if (mPreallocateAppProcessTask) {
mPreallocateAppProcessTask->Cancel();
mPreallocateAppProcessTask = nullptr;
}
#endif
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;
}
}
@ -199,6 +391,11 @@ PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
}
}
inline PreallocatedProcessManagerImpl* GetPPMImpl()
{
return PreallocatedProcessManagerImpl::Singleton();
}
} // anonymous namespace
namespace mozilla {
@ -206,25 +403,53 @@ namespace mozilla {
/* static */ void
PreallocatedProcessManager::AllocateAfterDelay()
{
PreallocatedProcessManagerImpl::Singleton()->AllocateAfterDelay();
#ifdef MOZ_NUWA_PROCESS
GetPPMImpl()->ScheduleDelayedNuwaFork();
#else
GetPPMImpl()->AllocateAfterDelay();
#endif
}
/* static */ void
PreallocatedProcessManager::AllocateOnIdle()
{
PreallocatedProcessManagerImpl::Singleton()->AllocateOnIdle();
GetPPMImpl()->AllocateOnIdle();
}
/* static */ void
PreallocatedProcessManager::AllocateNow()
{
PreallocatedProcessManagerImpl::Singleton()->AllocateNow();
GetPPMImpl()->AllocateNow();
}
/* static */ already_AddRefed<ContentParent>
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

View File

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