зеркало из https://github.com/mozilla/gecko-dev.git
Bug 836654 - Part 6: If a process is "critical" and holds the "cpu" or "high-priority" wake lock, give it priority FOREGROUND_HIGH. r=cjones
This commit is contained in:
Родитель
ed1dc3281d
Коммит
f44d60c8cc
|
@ -550,7 +550,7 @@ pref("ui.showHideScrollbars", 1);
|
|||
// background.
|
||||
pref("dom.ipc.processPriorityManager.enabled", true);
|
||||
pref("dom.ipc.processPriorityManager.backgroundGracePeriodMS", 1000);
|
||||
pref("dom.ipc.processPriorityManager.temporaryPriorityMS", 5000);
|
||||
pref("dom.ipc.processPriorityManager.temporaryPriorityLockMS", 5000);
|
||||
|
||||
// Kernel parameters for how processes are killed on low-memory.
|
||||
pref("gonk.systemMemoryPressureRecoveryPollMS", 5000);
|
||||
|
|
|
@ -76,36 +76,15 @@ static PRLogModuleInfo* logModule = PR_NewLogModule("ProcessPriorityManager");
|
|||
#define LOG(fmt, ...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get the appropriate backround priority for this process.
|
||||
*/
|
||||
ProcessPriority
|
||||
GetBackgroundPriority()
|
||||
uint64_t
|
||||
GetContentChildID()
|
||||
{
|
||||
AudioChannelService* service = AudioChannelService::GetAudioChannelService();
|
||||
if (service->ContentOrNormalChannelIsActive()) {
|
||||
return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
|
||||
}
|
||||
|
||||
bool isHomescreen = false;
|
||||
|
||||
ContentChild* contentChild = ContentChild::GetSingleton();
|
||||
if (contentChild) {
|
||||
const InfallibleTArray<PBrowserChild*>& browsers =
|
||||
contentChild->ManagedPBrowserChild();
|
||||
for (uint32_t i = 0; i < browsers.Length(); i++) {
|
||||
nsAutoString appType;
|
||||
static_cast<TabChild*>(browsers[i])->GetAppType(appType);
|
||||
if (appType.EqualsLiteral("homescreen")) {
|
||||
isHomescreen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contentChild) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return isHomescreen ?
|
||||
PROCESS_PRIORITY_BACKGROUND_HOMESCREEN :
|
||||
PROCESS_PRIORITY_BACKGROUND;
|
||||
return contentChild->GetID();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,6 +130,7 @@ class ProcessPriorityManager MOZ_FINAL
|
|||
: public nsIObserver
|
||||
, public nsIDOMEventListener
|
||||
, public nsITimerCallback
|
||||
, public WakeLockObserver
|
||||
{
|
||||
public:
|
||||
ProcessPriorityManager();
|
||||
|
@ -160,20 +140,21 @@ public:
|
|||
NS_DECL_NSITIMERCALLBACK
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
void Notify(const WakeLockInformation& aWakeLockInfo);
|
||||
|
||||
ProcessPriority GetPriority() const { return mProcessPriority; }
|
||||
|
||||
/**
|
||||
* If this process is not already in the foreground, move it into the
|
||||
* foreground and set a timer to call ResetPriorityNow() in a few seconds.
|
||||
* This function doesn't do exactly what you might think; see the comment on
|
||||
* ProcessPriorityManager.h::TemporarilyLockProcessPriority().
|
||||
*/
|
||||
void TemporarilySetIsForeground();
|
||||
void TemporarilyLockProcessPriority();
|
||||
|
||||
/**
|
||||
* Recompute this process's priority and apply it, potentially after a brief
|
||||
* delay.
|
||||
*
|
||||
* If the new priority is FOREGROUND, it takes effect immediately.
|
||||
* If the new priority is FOREGROUND*, it takes effect immediately.
|
||||
*
|
||||
* If the new priority is a BACKGROUND* priority and this process's priority
|
||||
* is currently a BACKGROUND* priority, the new priority takes effect
|
||||
|
@ -193,13 +174,30 @@ public:
|
|||
private:
|
||||
void OnContentDocumentGlobalCreated(nsISupports* aOuterWindow);
|
||||
|
||||
/**
|
||||
* Is this process a "critical" process that's holding the "CPU" or
|
||||
* "high-priority" wake lock?
|
||||
*/
|
||||
bool IsCriticalProcessWithWakeLock();
|
||||
|
||||
/**
|
||||
* If this process were in the foreground, what priority would it have?
|
||||
*/
|
||||
ProcessPriority GetForegroundPriority();
|
||||
|
||||
/**
|
||||
* If this process were in the foreground, what priority would it have?
|
||||
*/
|
||||
ProcessPriority GetBackgroundPriority();
|
||||
|
||||
/**
|
||||
* Compute whether this process is in the foreground and return the result.
|
||||
*/
|
||||
bool ComputeIsInForeground();
|
||||
|
||||
/**
|
||||
* Set this process's priority to FOREGROUND immediately.
|
||||
* Set this process's priority to the appropriate FOREGROUND* priority
|
||||
* immediately.
|
||||
*/
|
||||
void SetIsForeground();
|
||||
|
||||
|
@ -217,6 +215,12 @@ private:
|
|||
void
|
||||
ScheduleResetPriority(const char* aTimeoutPref);
|
||||
|
||||
// Tracks whether this process holds the "cpu" lock.
|
||||
bool mHoldsCPUWakeLock;
|
||||
|
||||
// Tracks whether this process holds the "high-priority" lock.
|
||||
bool mHoldsHighPriorityWakeLock;
|
||||
|
||||
// mProcessPriority tracks the priority we've given this process in hal.
|
||||
ProcessPriority mProcessPriority;
|
||||
|
||||
|
@ -229,20 +233,20 @@ private:
|
|||
nsWeakPtr mMemoryMinimizerRunnable;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS3(ProcessPriorityManager, nsIObserver,
|
||||
nsIDOMEventListener, nsITimerCallback)
|
||||
NS_IMPL_ISUPPORTS2(ProcessPriorityManager, nsIObserver, nsIDOMEventListener)
|
||||
|
||||
ProcessPriorityManager::ProcessPriorityManager()
|
||||
: mProcessPriority(ProcessPriority(-1))
|
||||
: mHoldsCPUWakeLock(false)
|
||||
, mHoldsHighPriorityWakeLock(false)
|
||||
, mProcessPriority(ProcessPriority(-1))
|
||||
{
|
||||
// When our parent process forked us, it set our priority either to
|
||||
// FOREGROUND (if our parent launched this process to meet an immediate need)
|
||||
// or one of the BACKGROUND priorities (if our parent launched this process
|
||||
// to meet a future need).
|
||||
// When our parent process forked us, it may have set our process's priority
|
||||
// to one of a few of the process priorities, depending on exactly why this
|
||||
// process was created.
|
||||
//
|
||||
// We don't know which situation we're in, so we set mProcessPriority to -1
|
||||
// so that the next time ResetPriorityNow is run, we'll definitely call into
|
||||
// hal and set our priority.
|
||||
// We don't know which priority we were given, so we set mProcessPriority to
|
||||
// -1 so that the next time ResetPriorityNow is run, we'll definitely call
|
||||
// into hal and set our priority.
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -262,6 +266,20 @@ ProcessPriorityManager::Init()
|
|||
os->AddObserver(this, "inner-window-destroyed", /* ownsWeak = */ false);
|
||||
os->AddObserver(this, "audio-channel-agent-changed", /* ownsWeak = */ false);
|
||||
os->AddObserver(this, "process-priority:reset-now", /* ownsWeak = */ false);
|
||||
|
||||
RegisterWakeLockObserver(this);
|
||||
|
||||
// This process may already hold the CPU lock; for example, our parent may
|
||||
// have acquired it on our behalf.
|
||||
WakeLockInformation info1, info2;
|
||||
GetWakeLockInfo(NS_LITERAL_STRING("cpu"), &info1);
|
||||
mHoldsCPUWakeLock = info1.lockingProcesses().Contains(GetContentChildID());
|
||||
|
||||
GetWakeLockInfo(NS_LITERAL_STRING("high-priority"), &info2);
|
||||
mHoldsHighPriorityWakeLock = info2.lockingProcesses().Contains(GetContentChildID());
|
||||
|
||||
LOG("Done starting up. mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d",
|
||||
mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -284,6 +302,30 @@ ProcessPriorityManager::Observe(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessPriorityManager::Notify(const WakeLockInformation& aInfo)
|
||||
{
|
||||
bool* dest = nullptr;
|
||||
if (aInfo.topic() == NS_LITERAL_STRING("cpu")) {
|
||||
dest = &mHoldsCPUWakeLock;
|
||||
} else if (aInfo.topic() == NS_LITERAL_STRING("high-priority")) {
|
||||
dest = &mHoldsHighPriorityWakeLock;
|
||||
}
|
||||
|
||||
if (dest) {
|
||||
bool thisProcessLocks =
|
||||
aInfo.lockingProcesses().Contains(GetContentChildID());
|
||||
|
||||
if (thisProcessLocks != *dest) {
|
||||
*dest = thisProcessLocks;
|
||||
LOG("Got wake lock changed event. "
|
||||
"Now mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d",
|
||||
mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock);
|
||||
ResetPriority();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ProcessPriorityManager::HandleEvent(
|
||||
nsIDOMEvent* aEvent)
|
||||
|
@ -329,9 +371,75 @@ ProcessPriorityManager::OnContentDocumentGlobalCreated(
|
|||
/* wantsUntrusted = */ false);
|
||||
|
||||
mWindows.AppendElement(weakWin);
|
||||
|
||||
ResetPriority();
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessPriorityManager::IsCriticalProcessWithWakeLock()
|
||||
{
|
||||
if (!(mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ContentChild* contentChild = ContentChild::GetSingleton();
|
||||
if (!contentChild) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const InfallibleTArray<PBrowserChild*>& browsers =
|
||||
contentChild->ManagedPBrowserChild();
|
||||
for (uint32_t i = 0; i < browsers.Length(); i++) {
|
||||
nsAutoString appType;
|
||||
static_cast<TabChild*>(browsers[i])->GetAppType(appType);
|
||||
if (appType.EqualsLiteral("critical")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ProcessPriority
|
||||
ProcessPriorityManager::GetForegroundPriority()
|
||||
{
|
||||
return IsCriticalProcessWithWakeLock() ? PROCESS_PRIORITY_FOREGROUND_HIGH :
|
||||
PROCESS_PRIORITY_FOREGROUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate backround priority for this process.
|
||||
*/
|
||||
ProcessPriority
|
||||
ProcessPriorityManager::GetBackgroundPriority()
|
||||
{
|
||||
AudioChannelService* service = AudioChannelService::GetAudioChannelService();
|
||||
if (service->ContentOrNormalChannelIsActive()) {
|
||||
return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
|
||||
}
|
||||
|
||||
bool isHomescreen = false;
|
||||
|
||||
ContentChild* contentChild = ContentChild::GetSingleton();
|
||||
if (contentChild) {
|
||||
const InfallibleTArray<PBrowserChild*>& browsers =
|
||||
contentChild->ManagedPBrowserChild();
|
||||
for (uint32_t i = 0; i < browsers.Length(); i++) {
|
||||
nsAutoString appType;
|
||||
static_cast<TabChild*>(browsers[i])->GetAppType(appType);
|
||||
if (appType.EqualsLiteral("homescreen")) {
|
||||
isHomescreen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isHomescreen ?
|
||||
PROCESS_PRIORITY_BACKGROUND_HOMESCREEN :
|
||||
PROCESS_PRIORITY_BACKGROUND;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ProcessPriorityManager::ResetPriority()
|
||||
{
|
||||
|
@ -359,6 +467,12 @@ ProcessPriorityManager::ResetPriorityNow()
|
|||
bool
|
||||
ProcessPriorityManager::ComputeIsInForeground()
|
||||
{
|
||||
// Critical processes holding the CPU/high-priority wake lock are always
|
||||
// considered to be in the foreground.
|
||||
if (IsCriticalProcessWithWakeLock()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We could try to be clever and keep a running count of the number of active
|
||||
// docshells, instead of iterating over mWindows every time one window's
|
||||
// visibility state changes. But experience suggests that iterating over the
|
||||
|
@ -383,6 +497,7 @@ ProcessPriorityManager::ComputeIsInForeground()
|
|||
bool isActive = false;
|
||||
docshell->GetIsActive(&isActive);
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
nsAutoCString spec;
|
||||
nsCOMPtr<nsIURI> uri = window->GetDocumentURI();
|
||||
|
@ -405,7 +520,8 @@ ProcessPriorityManager::ComputeIsInForeground()
|
|||
void
|
||||
ProcessPriorityManager::SetIsForeground()
|
||||
{
|
||||
if (mProcessPriority == PROCESS_PRIORITY_FOREGROUND) {
|
||||
ProcessPriority foregroundPriority = GetForegroundPriority();
|
||||
if (foregroundPriority == mProcessPriority) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -416,7 +532,7 @@ ProcessPriorityManager::SetIsForeground()
|
|||
runnable->Cancel();
|
||||
}
|
||||
|
||||
mProcessPriority = PROCESS_PRIORITY_FOREGROUND;
|
||||
mProcessPriority = foregroundPriority;
|
||||
LOG("Setting priority to %s.", ProcessPriorityToString(mProcessPriority));
|
||||
hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_FOREGROUND);
|
||||
}
|
||||
|
@ -455,7 +571,7 @@ void
|
|||
ProcessPriorityManager::ScheduleResetPriority(const char* aTimeoutPref)
|
||||
{
|
||||
if (mResetPriorityTimer) {
|
||||
// The timer is already running.
|
||||
LOG("ScheduleResetPriority bailing; the timer is already running.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -476,20 +592,20 @@ ProcessPriorityManager::Notify(nsITimer* aTimer)
|
|||
}
|
||||
|
||||
void
|
||||
ProcessPriorityManager::TemporarilySetIsForeground()
|
||||
ProcessPriorityManager::TemporarilyLockProcessPriority()
|
||||
{
|
||||
LOG("TemporarilySetIsForeground");
|
||||
SetIsForeground();
|
||||
LOG("TemporarilyLockProcessPriority");
|
||||
|
||||
// Each call to TemporarilySetIsForeground guarantees us temporaryPriorityMS
|
||||
// in the foreground (unless we receive a process-priority:reset-now
|
||||
// notification). So cancel our timer if it's running (which is due to a
|
||||
// previous call to either TemporarilySetIsForeground() or ResetPriority()).
|
||||
// Each call to TemporarilyLockProcessPriority gives us an additional
|
||||
// temporaryPriorityMS at our current priority (unless we receive a
|
||||
// process-priority:reset-now notification). So cancel our timer if it's
|
||||
// running (which is due to a previous call to either
|
||||
// TemporarilyLockProcessPriority() or ResetPriority()).
|
||||
if (mResetPriorityTimer) {
|
||||
mResetPriorityTimer->Cancel();
|
||||
mResetPriorityTimer = nullptr;
|
||||
}
|
||||
ScheduleResetPriority("temporaryPriorityMS");
|
||||
ScheduleResetPriority("temporaryPriorityLockMS");
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
@ -537,12 +653,12 @@ CurrentProcessIsForeground()
|
|||
}
|
||||
|
||||
void
|
||||
TemporarilySetProcessPriorityToForeground()
|
||||
TemporarilyLockProcessPriority()
|
||||
{
|
||||
if (sManager) {
|
||||
sManager->TemporarilySetIsForeground();
|
||||
sManager->TemporarilyLockProcessPriority();
|
||||
} else {
|
||||
LOG("TemporarilySetProcessPriorityToForeground called before "
|
||||
LOG("TemporarilyLockProcessPriority called before "
|
||||
"InitProcessPriorityManager. Bailing.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,15 +34,16 @@ void InitProcessPriorityManager();
|
|||
bool CurrentProcessIsForeground();
|
||||
|
||||
/**
|
||||
* If this process is in the background, temporarily boost its priority to the
|
||||
* foreground. This priority boost will expire after a few seconds
|
||||
* (dom.ipc.processPriorityManager.temporaryPriorityMS).
|
||||
* Calling this function prevents us from changing this process's priority
|
||||
* for a few seconds, if that change in priority would not have taken effect
|
||||
* immediately to begin with.
|
||||
*
|
||||
* You might want to call this function when a process starts loading some
|
||||
* things, but doesn't yet have a foreground window. The hope would be that by
|
||||
* once the timer here expires, the process will have a foreground window.
|
||||
* In practice, this prevents foreground --> background transitions, but not
|
||||
* background --> foreground transitions. It also does not prevent
|
||||
* transitions from an unknown priority (as happens immediately after we're
|
||||
* constructed) to a foreground priority.
|
||||
*/
|
||||
void TemporarilySetProcessPriorityToForeground();
|
||||
void TemporarilyLockProcessPriority();
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace dom
|
||||
|
|
Загрузка…
Ссылка в новой задаче