Bug 1144132 - Adjust the priority of an activity opener so that it's above other background applications. r=fabrice

--HG--
rename : dom/browser-element/mochitest/priority/test_Background.html => dom/browser-element/mochitest/priority/test_Activity.html
rename : dom/browser-element/mochitest/priority/test_ForegroundLRU.html => dom/browser-element/mochitest/priority/test_MultipleActivities.html
This commit is contained in:
Gabriele Svelto 2015-06-13 15:16:00 +02:00
parent 8fcf73b164
commit c693511438
12 changed files with 239 additions and 115 deletions

View File

@ -711,7 +711,7 @@ pref("dom.ipc.processPriorityManager.temporaryPriorityLockMS", 5000);
// processes. We use these different levels to force the low-memory killer to
// kill processes in a LRU order.
pref("dom.ipc.processPriorityManager.BACKGROUND.LRUPoolLevels", 5);
pref("dom.ipc.processPriorityManager.FOREGROUND.LRUPoolLevels", 3);
pref("dom.ipc.processPriorityManager.BACKGROUND_PERCEIVABLE.LRUPoolLevels", 4);
// Kernel parameters for process priorities. These affect how processes are
// killed on low-memory and their relative CPU priorities.

View File

@ -270,7 +270,7 @@ let Activities = {
.getService(Ci.nsISystemMessagesInternal);
if (!sysmm) {
// System message is not present, what should we do?
delete self.callers[aMsg.id];
self.removeCaller(aMsg.id);
return;
}
@ -334,7 +334,7 @@ let Activities = {
"id": aMsg.id,
"result": results
});
delete Activities.callers[aMsg.id];
self.removeCaller(aMsg.id);
});
} else {
let glue = Cc["@mozilla.org/dom/activities/ui-glue;1"]
@ -383,7 +383,7 @@ let Activities = {
try {
this.callers[aId].mm.sendAsyncMessage(aName, aPayload);
} finally {
delete this.callers[aId];
this.removeCaller(aId);
}
},
@ -409,8 +409,10 @@ let Activities = {
switch(aMessage.name) {
case "Activity:Start":
Services.obs.notifyObservers(null, "activity-opened", msg.childID);
this.callers[msg.id] = { mm: mm,
manifestURL: msg.manifestURL,
childID: msg.childID,
pageURL: msg.pageURL };
this.startActivity(msg);
break;
@ -458,6 +460,12 @@ let Activities = {
}
break;
}
},
removeCaller: function activities_removeCaller(id) {
Services.obs.notifyObservers(null, "activity-closed",
this.callers[id].childID);
delete this.callers[id];
}
}

View File

@ -6,6 +6,7 @@
#include "Activity.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/ContentChild.h"
#include "nsContentUtils.h"
#include "nsDOMClassInfo.h"
#include "nsIConsoleService.h"
@ -81,7 +82,11 @@ Activity::Initialize(nsPIDOMWindow* aWindow,
ok = JS_WrapValue(aCx, &optionsValue);
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
mProxy->StartActivity(static_cast<nsIDOMDOMRequest*>(this), optionsValue, aWindow);
ContentChild *cpc = ContentChild::GetSingleton();
uint64_t childID = cpc ? cpc->GetID() : 0;
mProxy->StartActivity(static_cast<nsIDOMDOMRequest*>(this), optionsValue,
aWindow, childID);
return NS_OK;
}

View File

@ -36,7 +36,8 @@ function ActivityProxy() {
}
ActivityProxy.prototype = {
startActivity: function actProxy_startActivity(aActivity, aOptions, aWindow) {
startActivity: function actProxy_startActivity(aActivity, aOptions, aWindow,
aChildID) {
debug("startActivity");
this.window = aWindow;
@ -97,7 +98,8 @@ ActivityProxy.prototype = {
},
getFilterResults: aOptions.getFilterResults,
manifestURL: manifestURL,
pageURL: aWindow.document.location.href });
pageURL: aWindow.document.location.href,
childID: aChildID });
},
receiveMessage: function actProxy_receiveMessage(aMessage) {

View File

@ -9,11 +9,12 @@ interface nsIDOMWindow;
/**
* Implemented by @mozilla.org/dom/activities/proxy;1
*/
[scriptable, uuid(60adef0f-c1a7-4dc5-bc0d-4b879f1e59ca)]
[scriptable, uuid(e04c0bbc-ab7d-41ba-b801-1068dd58660b)]
interface nsIActivityProxy : nsISupports
{
void startActivity(in nsISupports /* MozActivity */ activity,
in jsval options,
in nsIDOMWindow window);
in nsIDOMWindow window,
in unsigned long long childID);
void cleanup();
};

View File

@ -37,4 +37,6 @@ LOCAL_INCLUDES += [
'/dom/base',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View File

@ -51,7 +51,7 @@ const browserElementTestHelpers = {
enableProcessPriorityManager: function() {
this._setPrefs(
['dom.ipc.processPriorityManager.BACKGROUND.LRUPoolLevels', 2],
['dom.ipc.processPriorityManager.FOREGROUND.LRUPoolLevels', 2],
['dom.ipc.processPriorityManager.BACKGROUND_PERCEIVABLE.LRUPoolLevels', 2],
['dom.ipc.processPriorityManager.testMode', true],
['dom.ipc.processPriorityManager.enabled', true]
);

View File

@ -12,10 +12,11 @@ skip-if = toolkit != "gtk2" || ((buildapp =='mulet' || buildapp == 'b2g') && (to
support-files = file_HighPriority.html
[test_Background.html]
[test_BackgroundLRU.html]
[test_Activity.html]
[test_Audio.html]
support-files = file_Audio.html silence.ogg
[test_ForegroundLRU.html]
[test_Keyboard.html]
[test_MultipleActivities.html]
[test_MultipleFrames.html]
support-files = file_MultipleFrames.html
[test_Preallocated.html]

View File

@ -0,0 +1,63 @@
<!DOCTYPE HTML>
<html>
<!--
Test that calling setVisible("false") on an iframe that has an open activity
causes its priority to change.
-->
<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();
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
function runTest() {
var os = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
.getService(SpecialPowers.Ci.nsIObserverService);
var iframe = document.createElement("iframe");
iframe.setAttribute("mozbrowser", true);
iframe.src = browserElementTestHelpers.emptyPage1;
var childID = null;
Promise.all([
expectOnlyOneProcessCreated("FOREGROUND").then(function(chid) {
childID = chid;
}),
expectMozbrowserEvent(iframe, "loadend")
]).then(function() {
var p = expectPriorityChange(childID, "BACKGROUND_PERCEIVABLE");
// We simulate opening an activity
os.notifyObservers(null, "activity-opened", childID);
// We wait until mozbrowserloadend before calling setVisible, because
// setVisible isn't available until mozbrowser has loaded. In practice, that
// means we can call setVisible once we've gotten /any/ mozbrowser event.
iframe.setVisible(false);
return p;
}).then(function() {
var p = expectPriorityChange(childID, "BACKGROUND");
// Now we simulate closing an activity
os.notifyObservers(null, "activity-closed", childID);
return p;
}).then(SimpleTest.finish);
document.body.appendChild(iframe);
}
addEventListener("testready", runTest);
</script>
</body>
</html>

View File

@ -1,80 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
Test that creating three foreground processes causes the LRU value of the
oldest one to be increased by one. Also test that the LRU value is decreased
again when the younger processes go into the background.
-->
<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();
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
SpecialPowers.addPermission("embed-apps", true, document);
function runTest() {
var iframe1 = document.createElement('iframe');
iframe1.setAttribute('mozbrowser', true);
iframe1.src = 'file_MultipleFrames.html';
var iframe2 = null;
var childID = null;
// Wait for the first process to be created.
Promise.all([
expectProcessCreated('FOREGROUND').then(function(chid) {
childID = chid;
}),
expectMozbrowserEvent(iframe1, 'openwindow')
]).then(function() {
// Then wait for the second one.
var p = expectProcessCreated('FOREGROUND');
iframe2 = document.createElement('iframe');
iframe2.setAttribute('mozbrowser', true);
iframe2.setAttribute('mozapp', 'http://example.org/manifest.webapp');
iframe2.src = browserElementTestHelpers.emptyPage1;
document.body.appendChild(iframe2);
return p;
}).then(function() {
// Then wait for the third one and for the first one's LRU value to be
// increased by one.
var p = Promise.all([
expectProcessCreated('FOREGROUND'),
expectPriorityWithLRUSet(childID, 'FOREGROUND', '1')
]);
document.body.appendChild(iframe2);
return p;
}).then(function() {
// Now hide the second and third processes, this will send them into the
// background and make the first process LRU value to be decreased.
var p = expectPriorityWithLRUSet(childID, 'FOREGROUND', '0')
iframe2.setVisible(false);
return p;
}).then(function() {
SimpleTest.finish();
});
document.body.appendChild(iframe1);
}
addEventListener('testready', runTest);
</script>
</body>
</html>

View File

@ -0,0 +1,89 @@
<!DOCTYPE HTML>
<html>
<!--
Test that when two activity openers are in the background the oldest one's LRU
value will be increased by one. Also test that the LRU value drops again to
zero when the youngest iframe becomes visible again.
-->
<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();
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
browserElementTestHelpers.enableProcessPriorityManager();
SpecialPowers.addPermission("embed-apps", true, document);
function runTest() {
var os = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
.getService(SpecialPowers.Ci.nsIObserverService);
var iframe1 = document.createElement("iframe");
iframe1.setAttribute("mozbrowser", true);
iframe1.src = "file_MultipleFrames.html";
var iframe2 = null;
var childID = null;
var childID2 = null;
// Wait for the first process to be created.
Promise.all([
expectProcessCreated("FOREGROUND").then(function(chid) {
childID = chid;
}),
expectMozbrowserEvent(iframe1, "openwindow")
]).then(function() {
var p = expectPriorityChange(childID, "BACKGROUND_PERCEIVABLE");
// Simulate opening an activity from the first frame
os.notifyObservers(null, "activity-opened", childID);
iframe1.setVisible(false);
return p;
}).then(function() {
var p = expectProcessCreated("FOREGROUND");
iframe2 = document.createElement("iframe");
iframe2.setAttribute("mozbrowser", true);
iframe2.setAttribute("mozapp", "http://example.org/manifest.webapp");
iframe2.src = browserElementTestHelpers.emptyPage1;
document.body.appendChild(iframe2);
return p;
}).then(function(chid) {
childID2 = chid;
/* Simulate opening an activity from the second frame and send it in the
* background, this should cause the first frame LRU value to be increased
* by one. */
var p = expectPriorityWithLRUSet(childID, "BACKGROUND_PERCEIVABLE", "1");
os.notifyObservers(null, "activity-opened", childID2);
iframe2.setVisible(false);
return p;
}).then(function() {
/* Now make the second frame become visible again, this will cause the
* first frame LRU value to be decreased to 0. */
var p = expectPriorityWithLRUSet(childID, "BACKGROUND_PERCEIVABLE", "0")
iframe2.setVisible(true);
return p;
}).then(function() {
SimpleTest.finish();
});
document.body.appendChild(iframe1);
}
addEventListener("testready", runTest);
</script>
</body>
</html>

View File

@ -26,6 +26,7 @@
#include "nsITimer.h"
#include "nsIPropertyBag2.h"
#include "nsComponentManagerUtils.h"
#include "nsCRT.h"
#ifdef XP_WIN
#include <process.h>
@ -100,7 +101,7 @@ public:
/**
* Creates a new process LRU pool for the specified priority.
*/
ProcessLRUPool(ProcessPriority aPriority, uint32_t aBias);
ProcessLRUPool(ProcessPriority aPriority);
/**
* Used to remove a particular process priority manager from the LRU pool
@ -118,7 +119,6 @@ private:
ProcessPriority mPriority;
uint32_t mLRUPoolLevels;
uint32_t mLRUPoolSize;
uint32_t mBias;
nsTArray<ParticularProcessPriorityManager*> mLRUPool;
uint32_t CalculateLRULevel(uint32_t aLRUPoolIndex);
@ -235,8 +235,8 @@ private:
/** Contains a pseudo-LRU list of background processes */
ProcessLRUPool mBackgroundLRUPool;
/** Contains a pseudo-LRU list of foreground processes */
ProcessLRUPool mForegroundLRUPool;
/** Contains a pseudo-LRU list of background-perceivable processes */
ProcessLRUPool mBackgroundPerceivableLRUPool;
};
/**
@ -312,6 +312,8 @@ public:
void OnRemoteBrowserFrameShown(nsISupports* aSubject);
void OnTabParentDestroyed(nsISupports* aSubject);
void OnFrameloaderVisibleChanged(nsISupports* aSubject);
void OnActivityOpened(const char16_t* aData);
void OnActivityClosed(const char16_t* aData);
ProcessPriority CurrentPriority();
ProcessPriority ComputePriority();
@ -340,6 +342,7 @@ private:
uint32_t mLRU;
bool mHoldsCPUWakeLock;
bool mHoldsHighPriorityWakeLock;
bool mIsActivityOpener;
bool mFrozen;
/**
@ -421,8 +424,8 @@ ProcessPriorityManagerImpl::GetSingleton()
ProcessPriorityManagerImpl::ProcessPriorityManagerImpl()
: mHighPriority(false)
, mBackgroundLRUPool(PROCESS_PRIORITY_BACKGROUND, 1)
, mForegroundLRUPool(PROCESS_PRIORITY_FOREGROUND, 0)
, mBackgroundLRUPool(PROCESS_PRIORITY_BACKGROUND)
, mBackgroundPerceivableLRUPool(PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
RegisterWakeLockObserver(this);
@ -536,7 +539,7 @@ ProcessPriorityManagerImpl::ObserveContentParentDestroyed(nsISupports* aSubject)
if (pppm) {
// Unconditionally remove the manager from the pools
mBackgroundLRUPool.Remove(pppm);
mForegroundLRUPool.Remove(pppm);
mBackgroundPerceivableLRUPool.Remove(pppm);
pppm->ShutDown();
@ -606,12 +609,12 @@ ProcessPriorityManagerImpl::NotifyProcessPriorityChanged(
mBackgroundLRUPool.Remove(aParticularManager);
}
if (newPriority == PROCESS_PRIORITY_FOREGROUND &&
aOldPriority != PROCESS_PRIORITY_FOREGROUND) {
mForegroundLRUPool.Add(aParticularManager);
} else if (newPriority != PROCESS_PRIORITY_FOREGROUND &&
aOldPriority == PROCESS_PRIORITY_FOREGROUND) {
mForegroundLRUPool.Remove(aParticularManager);
if (newPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE &&
aOldPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
mBackgroundPerceivableLRUPool.Add(aParticularManager);
} else if (newPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE &&
aOldPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
mBackgroundPerceivableLRUPool.Remove(aParticularManager);
}
if (newPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH &&
@ -654,6 +657,7 @@ ParticularProcessPriorityManager::ParticularProcessPriorityManager(
, mLRU(0)
, mHoldsCPUWakeLock(false)
, mHoldsHighPriorityWakeLock(false)
, mIsActivityOpener(false)
, mFrozen(aFrozen)
{
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
@ -671,6 +675,8 @@ ParticularProcessPriorityManager::Init()
os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true);
os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true);
os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true);
os->AddObserver(this, "activity-opened", /* ownsWeak */ true);
os->AddObserver(this, "activity-closed", /* ownsWeak */ true);
}
// This process may already hold the CPU lock; for example, our parent may
@ -746,6 +752,10 @@ ParticularProcessPriorityManager::Observe(nsISupports* aSubject,
OnTabParentDestroyed(aSubject);
} else if (topic.EqualsLiteral("frameloader-visible-changed")) {
OnFrameloaderVisibleChanged(aSubject);
} else if (topic.EqualsLiteral("activity-opened")) {
OnActivityOpened(aData);
} else if (topic.EqualsLiteral("activity-closed")) {
OnActivityClosed(aData);
} else {
MOZ_ASSERT(false);
}
@ -884,6 +894,30 @@ ParticularProcessPriorityManager::OnFrameloaderVisibleChanged(nsISupports* aSubj
ResetPriorityNow();
}
void
ParticularProcessPriorityManager::OnActivityOpened(const char16_t* aData)
{
uint64_t childID = nsCRT::atoll(NS_ConvertUTF16toUTF8(aData).get());
if (ChildID() == childID) {
LOGP("Marking as activity opener");
mIsActivityOpener = true;
ResetPriority();
}
}
void
ParticularProcessPriorityManager::OnActivityClosed(const char16_t* aData)
{
uint64_t childID = nsCRT::atoll(NS_ConvertUTF16toUTF8(aData).get());
if (ChildID() == childID) {
LOGP("Unmarking as activity opener");
mIsActivityOpener = false;
ResetPriority();
}
}
void
ParticularProcessPriorityManager::ResetPriority()
{
@ -1012,7 +1046,8 @@ ParticularProcessPriorityManager::ComputePriority()
return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
}
return PROCESS_PRIORITY_BACKGROUND;
return mIsActivityOpener ? PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE
: PROCESS_PRIORITY_BACKGROUND;
}
void
@ -1231,10 +1266,9 @@ ProcessPriorityManagerChild::CurrentProcessIsHighPriority()
mCachedPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH;
}
ProcessLRUPool::ProcessLRUPool(ProcessPriority aPriority, uint32_t aBias)
ProcessLRUPool::ProcessLRUPool(ProcessPriority aPriority)
: mPriority(aPriority)
, mLRUPoolLevels(1)
, mBias(aBias)
{
// We set mLRUPoolLevels according to our pref.
// This value is used to set background process LRU pool
@ -1246,11 +1280,12 @@ ProcessLRUPool::ProcessLRUPool(ProcessPriority aPriority, uint32_t aBias)
// GonkHal defines OOM_ADJUST_MAX is 15 and b2g.js defines
// PROCESS_PRIORITY_BACKGROUND's oom_score_adj is 667 and oom_adj is 10.
// This means we can only have at most (15 -10 + 1) = 6 background LRU levels.
// Similarly we can have at most 4 foreground LRU levels. We should really be
// getting rid of oom_adj and just rely on oom_score_adj only which would
// lift this constraint.
// Similarly we can have at most 4 background perceivable LRU levels. We
// should really be getting rid of oom_adj and just rely on oom_score_adj
// only which would lift this constraint.
MOZ_ASSERT(aPriority != PROCESS_PRIORITY_BACKGROUND || mLRUPoolLevels <= 6);
MOZ_ASSERT(aPriority != PROCESS_PRIORITY_FOREGROUND || mLRUPoolLevels <= 4);
MOZ_ASSERT(aPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE ||
mLRUPoolLevels <= 4);
// LRU pool size = 2 ^ (number of background LRU pool levels) - 1
mLRUPoolSize = (1 << mLRUPoolLevels) - 1;
@ -1273,8 +1308,6 @@ ProcessLRUPool::CalculateLRULevel(uint32_t aLRU)
// Priority+L-1: 2^(number of LRU pool levels - 1)
// (End of buffer)
// Biasing the input can be used to shift the assignment
int exp;
unused << frexp(static_cast<double>(aLRU), &exp);
uint32_t level = std::max(exp - 1, 0);
@ -1311,7 +1344,7 @@ ProcessLRUPool::AdjustLRUValues(
nsTArray<ParticularProcessPriorityManager*>::index_type aStart,
bool removed)
{
uint32_t adj = (removed ? 1 : 0) + mBias;
uint32_t adj = (removed ? 2 : 1);
for (nsTArray<ParticularProcessPriorityManager*>::index_type i = aStart;
i < mLRUPool.Length();
@ -1321,7 +1354,7 @@ ProcessLRUPool::AdjustLRUValues(
* depending on the direction and the bias this test will pick different
* elements. */
if (((i + adj) & (i + adj - 1)) == 0) {
mLRUPool[i]->SetPriorityNow(mPriority, CalculateLRULevel(i + mBias));
mLRUPool[i]->SetPriorityNow(mPriority, CalculateLRULevel(i + 1));
}
}
}