Bug 1364849 - Recycle short lived content processes. r=mrbkap

To prevent addons or other code to rapidly create and destroy content processes
under various conditions, we let the preallocated process manager to reuse short
lived content processes.
This commit is contained in:
Gabor Krizsanits 2017-05-30 13:46:41 +02:00
parent f3ac40becf
commit 98fc89bfe4
4 changed files with 78 additions and 3 deletions

View File

@ -862,12 +862,17 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
}
// Try to take the preallocated process only for the default process type.
// The preallocated process manager might not had the chance yet to release the process
// after a very recent ShutDownProcess, let's make sure we don't try to reuse a process
// that is being shut down.
RefPtr<ContentParent> p;
if (aRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) &&
(p = PreallocatedProcessManager::Take())) {
(p = PreallocatedProcessManager::Take()) &&
!p->mShutdownPending) {
// For pre-allocated process we have not set the opener yet.
p->mOpener = aOpener;
contentParents.AppendElement(p);
p->mActivateTS = TimeStamp::Now();
return p.forget();
}
}
@ -882,6 +887,7 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
p->Init();
contentParents.AppendElement(p);
p->mActivateTS = TimeStamp::Now();
return p.forget();
}
@ -1522,7 +1528,7 @@ ContentParent::ShutDownMessageManager()
}
void
ContentParent::MarkAsTroubled()
ContentParent::RemoveFromList()
{
if (IsForJSPlugin()) {
if (sJSPluginContentParents) {
@ -1554,6 +1560,12 @@ ContentParent::MarkAsTroubled()
sPrivateContent = nullptr;
}
}
}
void
ContentParent::MarkAsTroubled()
{
RemoveFromList();
mIsAvailable = false;
}
@ -1854,6 +1866,26 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
#endif
}
bool
ContentParent::TryToRecycle()
{
// This life time check should be replaced by a memory health check (memory usage + fragmentation).
const double kMaxLifeSpan = 5;
if (mShutdownPending ||
mCalledKillHard ||
!IsAvailable() ||
!mRemoteType.EqualsLiteral(DEFAULT_REMOTE_TYPE) ||
(TimeStamp::Now() - mActivateTS).ToSeconds() > kMaxLifeSpan ||
!PreallocatedProcessManager::Provide(this)) {
return false;
}
// The PreallocatedProcessManager took over the ownership let's not keep a reference to it,
// until we don't take it back.
RemoveFromList();
return true;
}
bool
ContentParent::ShouldKeepProcessAlive() const
{
@ -1916,6 +1948,10 @@ ContentParent::NotifyTabDestroying(const TabId& aTabId,
return;
}
if (cp->TryToRecycle()) {
return;
}
// We're dying now, so prevent this content process from being
// recycled during its shutdown procedure.
cp->MarkAsDead();
@ -1965,7 +2001,7 @@ ContentParent::NotifyTabDestroyed(const TabId& aTabId,
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(this->ChildID());
if (tabIds.Length() == 1 && !ShouldKeepProcessAlive()) {
if (tabIds.Length() == 1 && !ShouldKeepProcessAlive() && !TryToRecycle()) {
// In the case of normal shutdown, send a shutdown message to child to
// allow it to perform shutdown tasks.
MessageLoop::current()->PostTask(NewRunnableMethod
@ -2103,6 +2139,7 @@ ContentParent::ContentParent(ContentParent* aOpener,
: nsIContentParent()
, mSubprocess(nullptr)
, mLaunchTS(TimeStamp::Now())
, mActivateTS(TimeStamp::Now())
, mOpener(aOpener)
, mRemoteType(aRemoteType)
, mChildID(gContentChildID++)

View File

@ -750,6 +750,17 @@ private:
// called after the process has been transformed to browser.
void ForwardKnownInfo();
/**
* We might want to reuse barely used content processes if certain criteria are met.
*/
bool TryToRecycle();
/**
* Removing it from the static array so it won't be returned for new tabs in
* GetNewOrUsedBrowserProcess.
*/
void RemoveFromList();
/**
* Decide whether the process should be kept alive even when it would normally
* be shut down, for example when all its tabs are closed.
@ -1182,6 +1193,7 @@ private:
GeckoChildProcessHost* mSubprocess;
const TimeStamp mLaunchTS; // used to calculate time to start content process
TimeStamp mActivateTS;
ContentParent* mOpener;
nsString mRemoteType;

View File

@ -43,6 +43,7 @@ public:
void AllocateOnIdle();
void AllocateNow();
already_AddRefed<ContentParent> Take();
bool Provide(ContentParent* aParent);
private:
static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
@ -163,9 +164,26 @@ PreallocatedProcessManagerImpl::RereadPrefs()
already_AddRefed<ContentParent>
PreallocatedProcessManagerImpl::Take()
{
if (!mEnabled || mShutdown) {
return nullptr;
}
return mPreallocatedProcess.forget();
}
bool
PreallocatedProcessManagerImpl::Provide(ContentParent* aParent)
{
if (mEnabled && !mShutdown && !mPreallocatedProcess) {
mPreallocatedProcess = aParent;
}
// We might get a call from both NotifyTabDestroying and NotifyTabDestroyed with the same
// ContentParent. Returning true here for both calls is important to avoid the cached process
// to be destroyed.
return aParent == mPreallocatedProcess;
}
void
PreallocatedProcessManagerImpl::Enable()
{
@ -282,4 +300,10 @@ PreallocatedProcessManager::Take()
return GetPPMImpl()->Take();
}
/* static */ bool
PreallocatedProcessManager::Provide(ContentParent* aParent)
{
return GetPPMImpl()->Provide(aParent);
}
} // namespace mozilla

View File

@ -72,6 +72,8 @@ public:
*/
static already_AddRefed<ContentParent> Take();
static bool Provide(ContentParent* aParent);
private:
PreallocatedProcessManager();
DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager);