Bug 1334716 - Make process selection a service and implementable in JS. r=krizsa

MozReview-Commit-ID: CViRvZB8nKe
This commit is contained in:
Blake Kaplan 2016-11-01 16:02:43 -07:00
Родитель dbf209197e
Коммит 9fb1ff597d
8 изменённых файлов: 265 добавлений и 17 удалений

Просмотреть файл

@ -520,6 +520,9 @@
@RESPATH@/components/remotebrowserutils.manifest @RESPATH@/components/remotebrowserutils.manifest
@RESPATH@/components/RemoteWebNavigation.js @RESPATH@/components/RemoteWebNavigation.js
@RESPATH@/components/ProcessSelector.js
@RESPATH@/components/ProcessSelector.manifest
@RESPATH@/components/SlowScriptDebug.manifest @RESPATH@/components/SlowScriptDebug.manifest
@RESPATH@/components/SlowScriptDebug.js @RESPATH@/components/SlowScriptDebug.js

Просмотреть файл

@ -0,0 +1,62 @@
/* 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/. */
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import('resource://gre/modules/Services.jsm');
const BASE_PREF = "dom.ipc.processCount"
const PREF_BRANCH = BASE_PREF + ".";
// Utilities:
function getMaxContentParents(processType) {
let maxContentParents = -1;
try {
maxContentParents = Services.prefs.getIntPref(PREF_BRANCH + processType);
} catch (e) {
// Pref probably didn't exist, get the default number of processes.
try {
maxContentParents = Services.prefs.getIntPref(BASE_PREF);
} catch (e) {
// No prefs? That's odd, use only one process.
maxContentParents = 1;
}
}
return maxContentParents;
}
// Fills up aProcesses until max and then selects randomly from the available
// ones.
function RandomSelector() {
}
RandomSelector.prototype = {
classID: Components.ID("{c616fcfd-9737-41f1-aa74-cee72a38f91b}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentProcessProvider]),
provideProcess(aType, aOpener, aProcesses, aCount) {
let maxContentParents = getMaxContentParents(aType);
if (aCount < maxContentParents) {
return Ci.nsIContentProcessProvider.NEW_PROCESS;
}
let startIdx = Math.floor(Math.random() * maxContentParents);
let curIdx = startIdx;
do {
if (aProcesses[curIdx].opener === aOpener) {
return curIdx;
}
curIdx = (curIdx + 1) % maxContentParents;
} while (curIdx !== startIdx);
return Ci.nsIContentProcessProvider.NEW_PROCESS;
},
};
var components = [RandomSelector];
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

Просмотреть файл

@ -0,0 +1,2 @@
component {c616fcfd-9737-41f1-aa74-cee72a38f91b} ProcessSelector.js
contract @mozilla.org/ipc/processselector;1 {c616fcfd-9737-41f1-aa74-cee72a38f91b}

Просмотреть файл

@ -401,6 +401,8 @@ EXTRA_COMPONENTS += [
'contentAreaDropListener.manifest', 'contentAreaDropListener.manifest',
'messageWakeupService.js', 'messageWakeupService.js',
'messageWakeupService.manifest', 'messageWakeupService.manifest',
'ProcessSelector.js',
'ProcessSelector.manifest',
'SlowScriptDebug.js', 'SlowScriptDebug.js',
'SlowScriptDebug.manifest', 'SlowScriptDebug.manifest',
] ]

Просмотреть файл

@ -11,6 +11,7 @@ XPIDL_SOURCES += [
'nsIContentPermissionPrompt.idl', 'nsIContentPermissionPrompt.idl',
'nsIContentPrefService.idl', 'nsIContentPrefService.idl',
'nsIContentPrefService2.idl', 'nsIContentPrefService2.idl',
'nsIContentProcess.idl',
'nsIContentURIGrouper.idl', 'nsIContentURIGrouper.idl',
'nsIDOMChromeWindow.idl', 'nsIDOMChromeWindow.idl',
'nsIDOMClientRect.idl', 'nsIDOMClientRect.idl',

Просмотреть файл

@ -0,0 +1,53 @@
/* 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 "nsISupports.idl"
interface nsIDOMElement;
interface nsIMessageSender;
interface nsIURI;
[scriptable, builtinclass, uuid(456f58be-29dd-4973-885b-95aece1c9a8a)]
interface nsIContentProcessInfo : nsISupports
{
/**
* Is this content process alive?
*/
readonly attribute boolean isAlive;
/**
* The content process's PID.
* Throws if the process is not alive.
*/
readonly attribute int32_t processId;
/**
* This content process's opener.
*/
readonly attribute nsIContentProcessInfo opener;
/**
* The process manager for this ContentParent (so a process message manager
* as opposed to a frame message manager.
*/
readonly attribute nsIMessageSender messageManager;
};
[scriptable, uuid(83ffb063-5f65-4c45-ae07-3f553e0809bb)]
interface nsIContentProcessProvider : nsISupports
{
/**
* Return this from provideProcess to create a new process.
*/
const int32_t NEW_PROCESS = -1;
/**
* Given aAliveProcesses (with an opener aOpener), choose which process of
* aType to use. Return nsIContentProcessProvider.NEW_PROCESS to ask the
* caller to create a new content process.
*/
int32_t provideProcess(in AString aType, in nsIContentProcessInfo aOpener,
[array, size_is(aCount)] in nsIContentProcessInfo aAliveProcesses,
in uint32_t aCount);
};

Просмотреть файл

@ -112,6 +112,7 @@
#include "nsIAlertsService.h" #include "nsIAlertsService.h"
#include "nsIClipboard.h" #include "nsIClipboard.h"
#include "nsContentPermissionHelper.h" #include "nsContentPermissionHelper.h"
#include "nsIContentProcess.h"
#include "nsICycleCollectorListener.h" #include "nsICycleCollectorListener.h"
#include "nsIDocShellTreeOwner.h" #include "nsIDocShellTreeOwner.h"
#include "nsIDocument.h" #include "nsIDocument.h"
@ -435,6 +436,90 @@ ContentParentsMemoryReporter::CollectReports(
} }
nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>* ContentParent::sBrowserContentParents; nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>* ContentParent::sBrowserContentParents;
namespace {
class ScriptableCPInfo final : public nsIContentProcessInfo
{
public:
explicit ScriptableCPInfo(ContentParent* aParent)
: mContentParent(aParent)
{
MOZ_ASSERT(mContentParent);
}
NS_DECL_ISUPPORTS
NS_DECL_NSICONTENTPROCESSINFO
void ProcessDied()
{
mContentParent = nullptr;
}
private:
~ScriptableCPInfo()
{
MOZ_ASSERT(!mContentParent, "must call ProcessDied");
}
ContentParent* mContentParent;
};
NS_IMPL_ISUPPORTS(ScriptableCPInfo, nsIContentProcessInfo)
NS_IMETHODIMP
ScriptableCPInfo::GetIsAlive(bool* aIsAlive)
{
*aIsAlive = mContentParent != nullptr;
return NS_OK;
}
NS_IMETHODIMP
ScriptableCPInfo::GetProcessId(int32_t* aPID)
{
if (!mContentParent) {
*aPID = -1;
return NS_ERROR_NOT_INITIALIZED;
}
*aPID = mContentParent->Pid();
if (*aPID == -1) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
ScriptableCPInfo::GetOpener(nsIContentProcessInfo** aInfo)
{
*aInfo = nullptr;
if (!mContentParent) {
return NS_ERROR_NOT_INITIALIZED;
}
if (ContentParent* opener = mContentParent->Opener()) {
nsCOMPtr<nsIContentProcessInfo> info = opener->ScriptableHelper();
info.forget(aInfo);
}
return NS_OK;
}
NS_IMETHODIMP
ScriptableCPInfo::GetMessageManager(nsIMessageSender** aMessenger)
{
*aMessenger = nullptr;
if (!mContentParent) {
return NS_ERROR_NOT_INITIALIZED;
}
nsCOMPtr<nsIMessageSender> manager = mContentParent->GetMessageManager();
manager.forget(aMessenger);
return NS_OK;
}
} // anonymous namespace
nsTArray<ContentParent*>* ContentParent::sPrivateContent; nsTArray<ContentParent*>* ContentParent::sPrivateContent;
StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents; StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents;
#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX) #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
@ -697,37 +782,64 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
ContentParent* aOpener) ContentParent* aOpener)
{ {
nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType); nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
uint32_t maxContentParents = GetMaxProcessCount(aRemoteType); uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
RefPtr<ContentParent> p;
if (contentParents.Length() >= maxContentParents) {
// We never want to re-use Large-Allocation processes.
if (aRemoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) { if (aRemoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) {
// We never want to re-use Large-Allocation processes.
if (contentParents.Length() >= maxContentParents) {
return GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), return GetNewOrUsedBrowserProcess(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
aPriority, aPriority,
aOpener); aOpener);
} }
} else {
nsTArray<nsIContentProcessInfo*> infos(contentParents.Length());
for (auto* cp : contentParents) {
infos.AppendElement(cp->mScriptableHelper);
}
if ((p = RandomSelect(contentParents, aOpener, maxContentParents))) { nsCOMPtr<nsIContentProcessProvider> cpp =
return p.forget(); do_GetService("@mozilla.org/ipc/processselector;1");
nsIContentProcessInfo* openerInfo = aOpener ? aOpener->mScriptableHelper.get() : nullptr;
int32_t index;
if (cpp &&
NS_SUCCEEDED(cpp->ProvideProcess(aRemoteType, openerInfo,
infos.Elements(), infos.Length(),
&index))) {
// If the provider returned an existing ContentParent, use that one.
if (0 <= index && static_cast<uint32_t>(index) <= maxContentParents) {
RefPtr<ContentParent> retval = contentParents[index];
return retval.forget();
}
} else {
// If there was a problem with the JS chooser, fall back to a random
// selection.
NS_WARNING("nsIContentProcessProvider failed to return a process");
RefPtr<ContentParent> random;
if (contentParents.Length() >= maxContentParents &&
(random = RandomSelect(contentParents, aOpener, maxContentParents))) {
return random.forget();
} }
} }
// Try to take the preallocated process only for the default process type. // Try to take the preallocated process only for the default process type.
if (aRemoteType.Equals(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)) && RefPtr<ContentParent> p;
if (aRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) &&
(p = PreallocatedProcessManager::Take())) { (p = PreallocatedProcessManager::Take())) {
// For pre-allocated process we have not set the opener yet. // For pre-allocated process we have not set the opener yet.
p->mOpener = aOpener; p->mOpener = aOpener;
} else { contentParents.AppendElement(p);
p = new ContentParent(aOpener, aRemoteType); return p.forget();
}
}
// Create a new process from scratch.
RefPtr<ContentParent> p = new ContentParent(aOpener, aRemoteType);
if (!p->LaunchSubprocess(aPriority)) { if (!p->LaunchSubprocess(aPriority)) {
return nullptr; return nullptr;
} }
p->Init(); p->Init();
}
contentParents.AppendElement(p); contentParents.AppendElement(p);
return p.forget(); return p.forget();
@ -1204,6 +1316,8 @@ ContentParent::Init()
RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton()); RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton());
gmps->UpdateContentProcessGMPCapabilities(); gmps->UpdateContentProcessGMPCapabilities();
mScriptableHelper = new ScriptableCPInfo(this);
} }
namespace { namespace {
@ -1274,6 +1388,11 @@ ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority)
void void
ContentParent::ShutDownProcess(ShutDownMethod aMethod) ContentParent::ShutDownProcess(ShutDownMethod aMethod)
{ {
if (mScriptableHelper) {
static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
mScriptableHelper = nullptr;
}
// Shutting down by sending a shutdown message works differently than the // Shutting down by sending a shutdown message works differently than the
// other methods. We first call Shutdown() in the child. After the child is // other methods. We first call Shutdown() in the child. After the child is
// ready, it calls FinishShutdown() on us. Then we close the channel. // ready, it calls FinishShutdown() on us. Then we close the channel.

Просмотреть файл

@ -45,6 +45,7 @@
#define LARGE_ALLOCATION_REMOTE_TYPE "webLargeAllocation" #define LARGE_ALLOCATION_REMOTE_TYPE "webLargeAllocation"
class nsConsoleService; class nsConsoleService;
class nsIContentProcessInfo;
class nsICycleCollectorLogSink; class nsICycleCollectorLogSink;
class nsIDumpGCAndCCLogsCallback; class nsIDumpGCAndCCLogsCallback;
class nsITabParent; class nsITabParent;
@ -371,6 +372,10 @@ public:
{ {
return mOpener; return mOpener;
} }
nsIContentProcessInfo* ScriptableHelper() const
{
return mScriptableHelper;
}
bool NeedsPermissionsUpdate() const bool NeedsPermissionsUpdate() const
{ {
@ -1173,6 +1178,7 @@ private:
RefPtr<nsConsoleService> mConsoleService; RefPtr<nsConsoleService> mConsoleService;
nsConsoleService* GetConsoleService(); nsConsoleService* GetConsoleService();
nsCOMPtr<nsIContentProcessInfo> mScriptableHelper;
nsTArray<nsCOMPtr<nsIObserver>> mIdleListeners; nsTArray<nsCOMPtr<nsIObserver>> mIdleListeners;