зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1324428 - Simplified preallocated process mechanism. r=billm
This commit is contained in:
Родитель
38c45ee10a
Коммит
c0ff5e5593
|
@ -13,5 +13,6 @@ support-files =
|
|||
|
||||
[test_Simple.html]
|
||||
[test_HighPriority.html]
|
||||
[test_Preallocated.html]
|
||||
[test_WebGLContextLost.html]
|
||||
disabled = bug 865844
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test that the preallocated process starts up.
|
||||
-->
|
||||
<head>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="../browserElementTestHelpers.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript;version=1.7">
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function expectProcessCreated() {
|
||||
return new Promise(resolve => {
|
||||
function parentExpectProcessCreated() {
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
let topic = "ipc:content-created";
|
||||
let obs = { observe: function() {
|
||||
Services.obs.removeObserver(obs, topic);
|
||||
sendAsyncMessage('process-created');
|
||||
}}
|
||||
Services.obs.addObserver(obs, topic, /* weak = */ false);
|
||||
}
|
||||
|
||||
let helper = SpecialPowers.loadChromeScript(parentExpectProcessCreated);
|
||||
SimpleTest.registerCleanupFunction(function() { helper.destroy() });
|
||||
helper.addMessageListener('process-created', resolve);
|
||||
});
|
||||
}
|
||||
|
||||
expectProcessCreated().then(() => {
|
||||
ok(true, "Process creation detected.");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
// Kill existing preallocated process.
|
||||
SpecialPowers.pushPrefEnv({'set':[["dom.ipc.processPrelaunch.enabled", false]]}).then(() => {
|
||||
// Make sure we have the capacity to launch preallocated process.
|
||||
SpecialPowers.pushPrefEnv({'set':[["dom.ipc.processCount", 100]]}).then(() => {
|
||||
// Enable preallocated process and run the test.
|
||||
SpecialPowers.pushPrefEnv({'set':[["dom.ipc.processPrelaunch.enabled", true]]});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1538,6 +1538,15 @@ ContentChild::RecvBidiKeyboardNotify(const bool& aIsLangRTL,
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
static StaticRefPtr<CancelableRunnable> gFirstIdleTask;
|
||||
|
||||
static void
|
||||
FirstIdle(void)
|
||||
{
|
||||
MOZ_ASSERT(gFirstIdleTask);
|
||||
gFirstIdleTask = nullptr;
|
||||
ContentChild::GetSingleton()->SendFirstIdle();
|
||||
}
|
||||
|
||||
mozilla::jsipc::PJavaScriptChild *
|
||||
ContentChild::AllocPJavaScriptChild()
|
||||
|
@ -1591,6 +1600,16 @@ ContentChild::RecvPBrowserConstructor(PBrowserChild* aActor,
|
|||
const ContentParentId& aCpID,
|
||||
const bool& aIsForBrowser)
|
||||
{
|
||||
static bool hasRunOnce = false;
|
||||
if (!hasRunOnce) {
|
||||
hasRunOnce = true;
|
||||
|
||||
MOZ_ASSERT(!gFirstIdleTask);
|
||||
RefPtr<CancelableRunnable> firstIdleTask = NewCancelableRunnableFunction(FirstIdle);
|
||||
gFirstIdleTask = firstIdleTask;
|
||||
NS_IdleDispatchToCurrentThread(firstIdleTask.forget());
|
||||
}
|
||||
|
||||
return nsIContentChild::RecvPBrowserConstructor(aActor, aTabId, aContext,
|
||||
aChromeFlags, aCpID, aIsForBrowser);
|
||||
}
|
||||
|
@ -2117,6 +2136,9 @@ ContentChild::ActorDestroy(ActorDestroyReason why)
|
|||
// keep persistent state.
|
||||
ProcessChild::QuickExit();
|
||||
#else
|
||||
if (gFirstIdleTask) {
|
||||
gFirstIdleTask->Cancel();
|
||||
}
|
||||
|
||||
nsHostObjectProtocolHandler::RemoveDataEntries();
|
||||
|
||||
|
|
|
@ -157,6 +157,7 @@
|
|||
#include "nsThreadUtils.h"
|
||||
#include "nsToolkitCompsCID.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "PreallocatedProcessManager.h"
|
||||
#include "ProcessPriorityManager.h"
|
||||
#include "SandboxHal.h"
|
||||
#include "ScreenManagerParent.h"
|
||||
|
@ -542,6 +543,23 @@ static const char* sObserverTopics[] = {
|
|||
"cacheservice:empty-cache",
|
||||
};
|
||||
|
||||
// PreallocateProcess is called by the PreallocatedProcessManager.
|
||||
// ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
ContentParent::PreallocateProcess()
|
||||
{
|
||||
RefPtr<ContentParent> process =
|
||||
new ContentParent(/* aOpener = */ nullptr,
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
|
||||
|
||||
if (!process->LaunchSubprocess(PROCESS_PRIORITY_PREALLOC)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
process->Init();
|
||||
return process.forget();
|
||||
}
|
||||
|
||||
/*static*/ void
|
||||
ContentParent::StartUp()
|
||||
{
|
||||
|
@ -575,6 +593,9 @@ ContentParent::StartUp()
|
|||
|
||||
BackgroundChild::Startup();
|
||||
|
||||
// Try to preallocate a process that we can use later.
|
||||
PreallocatedProcessManager::AllocateAfterDelay();
|
||||
|
||||
sDisableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS");
|
||||
|
||||
#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
|
||||
|
@ -641,28 +662,37 @@ ContentParent::JoinAllSubprocesses()
|
|||
sCanLaunchSubprocesses = false;
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
|
||||
ProcessPriority aPriority,
|
||||
ContentParent* aOpener,
|
||||
bool aLargeAllocationProcess)
|
||||
/*static*/ uint32_t
|
||||
ContentParent::GetPoolSize(const nsAString& aContentProcessType)
|
||||
{
|
||||
if (!sBrowserContentParents) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsTArray<ContentParent*>* parents =
|
||||
sBrowserContentParents->Get(aContentProcessType);
|
||||
|
||||
return parents ? parents->Length() : 0;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ nsTArray<ContentParent*>&
|
||||
ContentParent::GetOrCreatePool(const nsAString& aContentProcessType)
|
||||
{
|
||||
if (!sBrowserContentParents) {
|
||||
sBrowserContentParents =
|
||||
new nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>;
|
||||
}
|
||||
|
||||
// Decide which pool of content parents we are going to be pulling from based
|
||||
// on the aRemoteType and aLargeAllocationProcess flag.
|
||||
nsAutoString contentProcessType(aLargeAllocationProcess
|
||||
? NS_LITERAL_STRING(LARGE_ALLOCATION_REMOTE_TYPE)
|
||||
: aRemoteType);
|
||||
nsTArray<ContentParent*>* contentParents =
|
||||
sBrowserContentParents->LookupOrAdd(contentProcessType);
|
||||
return *sBrowserContentParents->LookupOrAdd(aContentProcessType);
|
||||
}
|
||||
|
||||
/*static*/ uint32_t
|
||||
ContentParent::GetMaxProcessCount(const nsAString& aContentProcessType)
|
||||
{
|
||||
int32_t maxContentParents;
|
||||
nsAutoCString processCountPref("dom.ipc.processCount.");
|
||||
processCountPref.Append(NS_ConvertUTF16toUTF8(contentProcessType));
|
||||
processCountPref.Append(NS_ConvertUTF16toUTF8(aContentProcessType));
|
||||
if (NS_FAILED(Preferences::GetInt(processCountPref.get(), &maxContentParents))) {
|
||||
maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1);
|
||||
}
|
||||
|
@ -671,30 +701,73 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
|
|||
maxContentParents = 1;
|
||||
}
|
||||
|
||||
if (contentParents->Length() >= uint32_t(maxContentParents)) {
|
||||
uint32_t maxSelectable = std::min(static_cast<uint32_t>(contentParents->Length()),
|
||||
static_cast<uint32_t>(maxContentParents));
|
||||
uint32_t startIdx = rand() % maxSelectable;
|
||||
uint32_t currIdx = startIdx;
|
||||
do {
|
||||
RefPtr<ContentParent> p = (*contentParents)[currIdx];
|
||||
NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sBrowserContentParents?");
|
||||
if (p->mOpener == aOpener) {
|
||||
return p.forget();
|
||||
}
|
||||
currIdx = (currIdx + 1) % maxSelectable;
|
||||
} while (currIdx != startIdx);
|
||||
return static_cast<uint32_t>(maxContentParents);
|
||||
}
|
||||
|
||||
/*static*/ bool
|
||||
ContentParent::IsMaxProcessCountReached(const nsAString& aContentProcessType)
|
||||
{
|
||||
return GetPoolSize(aContentProcessType) >= GetMaxProcessCount(aContentProcessType);
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
ContentParent::RandomSelect(const nsTArray<ContentParent*>& aContentParents,
|
||||
ContentParent* aOpener, int32_t aMaxContentParents)
|
||||
{
|
||||
uint32_t maxSelectable = std::min(static_cast<uint32_t>(aContentParents.Length()),
|
||||
static_cast<uint32_t>(aMaxContentParents));
|
||||
uint32_t startIdx = rand() % maxSelectable;
|
||||
uint32_t currIdx = startIdx;
|
||||
do {
|
||||
RefPtr<ContentParent> p = aContentParents[currIdx];
|
||||
NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sBrowserContentParents?");
|
||||
if (p->mOpener == aOpener) {
|
||||
return p.forget();
|
||||
}
|
||||
currIdx = (currIdx + 1) % maxSelectable;
|
||||
} while (currIdx != startIdx);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*static*/ already_AddRefed<ContentParent>
|
||||
ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
|
||||
ProcessPriority aPriority,
|
||||
ContentParent* aOpener,
|
||||
bool aLargeAllocationProcess)
|
||||
{
|
||||
// Decide which pool of content parents we are going to be pulling from based
|
||||
// on the aRemoteType and aLargeAllocationProcess flag.
|
||||
nsAutoString contentProcessType(aLargeAllocationProcess
|
||||
? NS_LITERAL_STRING(LARGE_ALLOCATION_REMOTE_TYPE)
|
||||
: aRemoteType);
|
||||
|
||||
nsTArray<ContentParent*>& contentParents = GetOrCreatePool(contentProcessType);
|
||||
|
||||
uint32_t maxContentParents = GetMaxProcessCount(contentProcessType);
|
||||
|
||||
RefPtr<ContentParent> p;
|
||||
if (contentParents.Length() >= uint32_t(maxContentParents) &&
|
||||
(p = RandomSelect(contentParents, aOpener, maxContentParents))) {
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
RefPtr<ContentParent> p = new ContentParent(aOpener, contentProcessType);
|
||||
// Try to take the preallocated process only for the default process type.
|
||||
if (contentProcessType.Equals(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)) &&
|
||||
(p = PreallocatedProcessManager::Take())) {
|
||||
// For pre-allocated process we have not set the opener yet.
|
||||
p->mOpener = aOpener;
|
||||
} else {
|
||||
p = new ContentParent(aOpener, contentProcessType);
|
||||
|
||||
if (!p->LaunchSubprocess(aPriority)) {
|
||||
return nullptr;
|
||||
if (!p->LaunchSubprocess(aPriority)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p->Init();
|
||||
}
|
||||
|
||||
p->Init();
|
||||
|
||||
contentParents->AppendElement(p);
|
||||
contentParents.AppendElement(p);
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
|
@ -2272,6 +2345,17 @@ ContentParent::RecvGetShowPasswordSetting(bool* showPassword)
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvFirstIdle()
|
||||
{
|
||||
// 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 IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
|
||||
const bool& aHidden)
|
||||
|
|
|
@ -117,6 +117,11 @@ public:
|
|||
|
||||
virtual bool IsContentParent() const override { return true; }
|
||||
|
||||
/**
|
||||
* Create a subprocess suitable for use later as a content process.
|
||||
*/
|
||||
static already_AddRefed<ContentParent> PreallocateProcess();
|
||||
|
||||
/**
|
||||
* Start up the content-process machinery. This might include
|
||||
* scheduling pre-launch tasks.
|
||||
|
@ -134,6 +139,22 @@ public:
|
|||
*/
|
||||
static void JoinAllSubprocesses();
|
||||
|
||||
static uint32_t GetPoolSize(const nsAString& aContentProcessType);
|
||||
|
||||
static uint32_t GetMaxProcessCount(const nsAString& aContentProcessType);
|
||||
|
||||
static bool IsMaxProcessCountReached(const nsAString& aContentProcessType);
|
||||
|
||||
/**
|
||||
* Picks a random content parent from |aContentParents| with a given |aOpener|
|
||||
* respecting the index limit set by |aMaxContentParents|.
|
||||
* Returns null if non available.
|
||||
*/
|
||||
static already_AddRefed<ContentParent>
|
||||
RandomSelect(const nsTArray<ContentParent*>& aContentParents,
|
||||
ContentParent* aOpener,
|
||||
int32_t maxContentParents);
|
||||
|
||||
/**
|
||||
* Get or create a content process for:
|
||||
* 1. browser iframe
|
||||
|
@ -713,6 +734,12 @@ private:
|
|||
AllocPBackgroundParent(Transport* aTransport, ProcessId aOtherProcess)
|
||||
override;
|
||||
|
||||
/**
|
||||
* Get or create the corresponding content parent array to |aContentProcessType|.
|
||||
*/
|
||||
static nsTArray<ContentParent*>&
|
||||
GetOrCreatePool(const nsAString& aContentProcessType);
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvGetProcessAttributes(ContentParentId* aCpId,
|
||||
bool* aIsForBrowser) override;
|
||||
|
||||
|
@ -968,6 +995,8 @@ private:
|
|||
|
||||
virtual mozilla::ipc::IPCResult RecvPrivateDocShellsExist(const bool& aExist) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvFirstIdle() override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
|
||||
const bool& aHidden) override;
|
||||
|
||||
|
|
|
@ -907,6 +907,9 @@ parent:
|
|||
// Notify the parent of the presence or absence of private docshells
|
||||
async PrivateDocShellsExist(bool aExist);
|
||||
|
||||
// Tell the parent that the child has gone idle for the first time.
|
||||
async FirstIdle();
|
||||
|
||||
async AudioChannelServiceStatus(bool aActiveTelephonyChannel,
|
||||
bool aContentOrNormalChannel,
|
||||
bool aAnyActiveChannel);
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/PreallocatedProcessManager.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsIPropertyBag2.h"
|
||||
#include "ProcessPriorityManager.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
// 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
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::hal;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* This singleton class implements the static methods on
|
||||
* PreallocatedProcessManager.
|
||||
*/
|
||||
class PreallocatedProcessManagerImpl final
|
||||
: public nsIObserver
|
||||
{
|
||||
public:
|
||||
static PreallocatedProcessManagerImpl* Singleton();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
// See comments on PreallocatedProcessManager for these methods.
|
||||
void AllocateAfterDelay();
|
||||
void AllocateOnIdle();
|
||||
void AllocateNow();
|
||||
already_AddRefed<ContentParent> Take();
|
||||
|
||||
private:
|
||||
static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
|
||||
|
||||
PreallocatedProcessManagerImpl();
|
||||
~PreallocatedProcessManagerImpl() {}
|
||||
DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl);
|
||||
|
||||
void Init();
|
||||
|
||||
void RereadPrefs();
|
||||
void Enable();
|
||||
void Disable();
|
||||
void CloseProcess();
|
||||
|
||||
void ObserveProcessShutdown(nsISupports* aSubject);
|
||||
|
||||
bool mEnabled;
|
||||
bool mShutdown;
|
||||
RefPtr<ContentParent> mPreallocatedProcess;
|
||||
};
|
||||
|
||||
/* static */ StaticRefPtr<PreallocatedProcessManagerImpl>
|
||||
PreallocatedProcessManagerImpl::sSingleton;
|
||||
|
||||
/* static */ PreallocatedProcessManagerImpl*
|
||||
PreallocatedProcessManagerImpl::Singleton()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sSingleton) {
|
||||
sSingleton = new PreallocatedProcessManagerImpl();
|
||||
sSingleton->Init();
|
||||
ClearOnShutdown(&sSingleton);
|
||||
}
|
||||
|
||||
// First time when we init sSingleton, the pref database might not be in a
|
||||
// reliable state (we are too early), so despite dom.ipc.processPrelaunch.enabled
|
||||
// is set to true Preferences::GetBool will return false (it cannot find the pref).
|
||||
// Since Init() above will be called only once, and the pref will not be changed,
|
||||
// the manger will stay disabled. To prevent that let's re-read the pref each time
|
||||
// someone accessing the manager singleton. This is a hack but this is not a hot code
|
||||
// so it should be fine.
|
||||
sSingleton->RereadPrefs();
|
||||
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver)
|
||||
|
||||
PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
|
||||
: mEnabled(false)
|
||||
, mShutdown(false)
|
||||
{}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::Init()
|
||||
{
|
||||
Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled");
|
||||
// We have to respect processCount at all time. This is especially important
|
||||
// for testing.
|
||||
Preferences::AddStrongObserver(this, "dom.ipc.processCount");
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
os->AddObserver(this, "ipc:content-shutdown",
|
||||
/* weakRef = */ false);
|
||||
os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
|
||||
/* weakRef = */ false);
|
||||
}
|
||||
RereadPrefs();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
if (!strcmp("ipc:content-shutdown", aTopic)) {
|
||||
ObserveProcessShutdown(aSubject);
|
||||
} else if (!strcmp("nsPref:changed", aTopic)) {
|
||||
// The only other observer we registered was for our prefs.
|
||||
RereadPrefs();
|
||||
} else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
|
||||
Preferences::RemoveObserver(this, "dom.ipc.processPrelaunch.enabled");
|
||||
Preferences::RemoveObserver(this, "dom.ipc.processCount");
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
if (os) {
|
||||
os->RemoveObserver(this, "ipc:content-shutdown");
|
||||
os->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::RereadPrefs()
|
||||
{
|
||||
if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
|
||||
Enable();
|
||||
} else {
|
||||
Disable();
|
||||
}
|
||||
|
||||
if (ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) {
|
||||
CloseProcess();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<ContentParent>
|
||||
PreallocatedProcessManagerImpl::Take()
|
||||
{
|
||||
return mPreallocatedProcess.forget();
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::Enable()
|
||||
{
|
||||
if (mEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEnabled = true;
|
||||
AllocateAfterDelay();
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::AllocateAfterDelay()
|
||||
{
|
||||
if (!mEnabled || mPreallocatedProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Originally AllocateOnIdle() was post here, but since the gecko parent
|
||||
// message loop in practice never goes idle, that didn't work out well.
|
||||
// Let's just launch the process after the delay.
|
||||
NS_DelayedDispatchToCurrentThread(
|
||||
NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow),
|
||||
Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
|
||||
DEFAULT_ALLOCATE_DELAY));
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::AllocateOnIdle()
|
||||
{
|
||||
if (!mEnabled || mPreallocatedProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_IdleDispatchToCurrentThread(NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow));
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::AllocateNow()
|
||||
{
|
||||
if (!mEnabled || mPreallocatedProcess ||
|
||||
ContentParent::IsMaxProcessCountReached(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPreallocatedProcess = ContentParent::PreallocateProcess();
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::Disable()
|
||||
{
|
||||
if (!mEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEnabled = false;
|
||||
CloseProcess();
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::CloseProcess()
|
||||
{
|
||||
if (mPreallocatedProcess) {
|
||||
mPreallocatedProcess->Close();
|
||||
mPreallocatedProcess = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
|
||||
{
|
||||
if (!mPreallocatedProcess) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
|
||||
NS_ENSURE_TRUE_VOID(props);
|
||||
|
||||
uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
|
||||
props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
|
||||
NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
|
||||
|
||||
if (childID == mPreallocatedProcess->ChildID()) {
|
||||
mPreallocatedProcess = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline PreallocatedProcessManagerImpl* GetPPMImpl()
|
||||
{
|
||||
return PreallocatedProcessManagerImpl::Singleton();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/* static */ void
|
||||
PreallocatedProcessManager::AllocateAfterDelay()
|
||||
{
|
||||
GetPPMImpl()->AllocateAfterDelay();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PreallocatedProcessManager::AllocateOnIdle()
|
||||
{
|
||||
GetPPMImpl()->AllocateOnIdle();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
PreallocatedProcessManager::AllocateNow()
|
||||
{
|
||||
GetPPMImpl()->AllocateNow();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<ContentParent>
|
||||
PreallocatedProcessManager::Take()
|
||||
{
|
||||
return GetPPMImpl()->Take();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,82 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_PreallocatedProcessManager_h
|
||||
#define mozilla_PreallocatedProcessManager_h
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class ContentParent;
|
||||
} // namespace dom
|
||||
|
||||
/**
|
||||
* This class manages a ContentParent that it starts up ahead of any particular
|
||||
* need. You can then call Take() to get this process and use it. Since we
|
||||
* already started it up, it should be ready for use faster than if you'd
|
||||
* created the process when you needed it.
|
||||
*
|
||||
* This class watches the dom.ipc.processPrelaunch.enabled pref. If it changes
|
||||
* from false to true, it preallocates a process. If it changes from true to
|
||||
* false, it kills the preallocated process, if any.
|
||||
*
|
||||
* We don't expect this pref to flip between true and false in production, but
|
||||
* flipping the pref is important for tests.
|
||||
*/
|
||||
class PreallocatedProcessManager final
|
||||
{
|
||||
typedef mozilla::dom::ContentParent ContentParent;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a process after a delay. We wait for a period of time (specified
|
||||
* by the dom.ipc.processPrelaunch.delayMs pref), then wait for this process
|
||||
* to go idle, then allocate the new process.
|
||||
*
|
||||
* If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
|
||||
* have a preallocated process, this function does nothing.
|
||||
*/
|
||||
static void AllocateAfterDelay();
|
||||
|
||||
/**
|
||||
* Create a process once this process goes idle.
|
||||
*
|
||||
* If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
|
||||
* have a preallocated process, this function does nothing.
|
||||
*/
|
||||
static void AllocateOnIdle();
|
||||
|
||||
/**
|
||||
* Create a process right now.
|
||||
*
|
||||
* If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
|
||||
* have a preallocated process, this function does nothing.
|
||||
*/
|
||||
static void AllocateNow();
|
||||
|
||||
/**
|
||||
* Take the preallocated process, if we have one. If we don't have one, this
|
||||
* returns null.
|
||||
*
|
||||
* If you call Take() twice in a row, the second call is guaranteed to return
|
||||
* null.
|
||||
*
|
||||
* After you Take() the preallocated process, you need to call one of the
|
||||
* Allocate* functions (or change the dom.ipc.processPrelaunch pref from
|
||||
* false to true) before we'll create a new process.
|
||||
*/
|
||||
static already_AddRefed<ContentParent> Take();
|
||||
|
||||
private:
|
||||
PreallocatedProcessManager();
|
||||
DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // defined mozilla_PreallocatedProcessManager_h
|
|
@ -38,6 +38,7 @@ EXPORTS.mozilla.dom += [
|
|||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'PreallocatedProcessManager.h',
|
||||
'ProcessHangMonitor.h',
|
||||
'ProcessHangMonitorIPC.h',
|
||||
'ProcessPriorityManager.h',
|
||||
|
@ -56,6 +57,7 @@ UNIFIED_SOURCES += [
|
|||
'nsIContentChild.cpp',
|
||||
'nsIContentParent.cpp',
|
||||
'PermissionMessageUtils.cpp',
|
||||
'PreallocatedProcessManager.cpp',
|
||||
'ProcessPriorityManager.cpp',
|
||||
'ScreenManagerParent.cpp',
|
||||
'StructuredCloneData.cpp',
|
||||
|
|
|
@ -915,6 +915,8 @@ ProcessPriorityToString(ProcessPriority aPriority)
|
|||
switch (aPriority) {
|
||||
case PROCESS_PRIORITY_MASTER:
|
||||
return "MASTER";
|
||||
case PROCESS_PRIORITY_PREALLOC:
|
||||
return "PREALLOC";
|
||||
case PROCESS_PRIORITY_FOREGROUND_HIGH:
|
||||
return "FOREGROUND_HIGH";
|
||||
case PROCESS_PRIORITY_FOREGROUND:
|
||||
|
|
|
@ -60,6 +60,9 @@ enum ProcessPriority {
|
|||
PROCESS_PRIORITY_BACKGROUND,
|
||||
PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE,
|
||||
PROCESS_PRIORITY_FOREGROUND_KEYBOARD,
|
||||
// The special class for the preallocated process, high memory priority but
|
||||
// low CPU priority.
|
||||
PROCESS_PRIORITY_PREALLOC,
|
||||
// Any priority greater than or equal to FOREGROUND is considered
|
||||
// "foreground" for the purposes of priority testing, for example
|
||||
// CurrentProcessIsForeground().
|
||||
|
|
Загрузка…
Ссылка в новой задаче