--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -149,25 +149,31 @@ public:
* ProcessPriorityManager.h::TemporarilyLockProcessPriority().
*/
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 we are transitioning to a priority that is "lower" than the current
+ * priority (as defined below), that transition happens after a grace period.
+ * Otherwise the transition happens immediately.
+ *
+ * For the purposes of deciding whether to apply a grace period, the
+ * hierarchy of priorities is
*
- * If the new priority is a BACKGROUND* priority and this process's priority
- * is currently a BACKGROUND* priority, the new priority takes effect
- * immediately.
+ * - UNKNOWN
+ * - FOREGROUND_HIGH
+ * - FOREGROUND
+ * - BACKGROUND*
*
- * But if the new priority is a BACKGROUND* priority and this process is not
- * currently in the background, we schedule a timer and run
- * ResetPriorityNow() after a short period of time.
+ * So for example, a transition between any two BACKGROUND* priorites happens
+ * immediately, but a transition from UNKNOWN to FOREGROUND_HIGH happens
+ * after a grace period.
*/
void ResetPriority();
/**
* Recompute this process's priority and apply it immediately.
*/
void ResetPriorityNow();
@@ -190,21 +196,29 @@ private:
*/
ProcessPriority GetBackgroundPriority();
/**
* Compute whether this process is in the foreground and return the result.
*/
bool ComputeIsInForeground();
+
+ /**
+ * Set this process's priority to the appropriate FOREGROUND* priority
+ * immediately if we're upgrading its priority, and after a grace period if
+ * we're downgrading it or if the current priority is unknown.
+ */
+ void SetIsForeground();
+
/**
* Set this process's priority to the appropriate FOREGROUND* priority
* immediately.
*/
- void SetIsForeground();
+ void SetIsForegroundNow();
/**
* Set this process's priority to the appropriate BACKGROUND* priority
* immediately.
*/
void SetIsBackgroundNow();
/**
@@ -219,31 +233,36 @@ private:
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;
+ // Have we seen at least one tab-child-created event yet? Until this is
+ // true, ResetPriority() and ResetPriorityNow() do nothing.
+ bool mObservedTabChildCreated;
+
nsTArray<nsWeakPtr> mWindows;
// When this timer expires, we set mResetPriorityTimer to null and run
// ResetPriorityNow().
nsCOMPtr<nsITimer> mResetPriorityTimer;
nsWeakPtr mMemoryMinimizerRunnable;
};
NS_IMPL_ISUPPORTS2(ProcessPriorityManager, nsIObserver, nsIDOMEventListener)
ProcessPriorityManager::ProcessPriorityManager()
: mHoldsCPUWakeLock(false)
, mHoldsHighPriorityWakeLock(false)
, mProcessPriority(ProcessPriority(-1))
+ , mObservedTabChildCreated(false)
{
// 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 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.
@@ -257,16 +276,17 @@ ProcessPriorityManager::Init()
// We can't do this in the constructor because we need to hold a strong ref
// to |this| before calling these methods.
//
// Notice that we track /window/ creation and destruction even though our
// notion of "is-foreground" is tied to /docshell/ activity. We do this
// because docshells don't fire an event when their visibility changes, but
// windows do.
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ os->AddObserver(this, "tab-child-created", /* ownsWeak = */ false);
os->AddObserver(this, "content-document-global-created", /* ownsWeak = */ false);
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
@@ -283,17 +303,20 @@ ProcessPriorityManager::Init()
}
NS_IMETHODIMP
ProcessPriorityManager::Observe(
nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
- if (!strcmp(aTopic, "content-document-global-created")) {
+ if (!strcmp(aTopic, "tab-child-created")) {
+ mObservedTabChildCreated = true;
+ ResetPriority();
+ } else if (!strcmp(aTopic, "content-document-global-created")) {
OnContentDocumentGlobalCreated(aSubject);
} else if (!strcmp(aTopic, "inner-window-destroyed") ||
!strcmp(aTopic, "audio-channel-agent-changed")) {
ResetPriority();
} else if (!strcmp(aTopic, "process-priority:reset-now")) {
LOG("Got process-priority:reset-now notification.");
ResetPriorityNow();
} else {
@@ -438,32 +461,44 @@ ProcessPriorityManager::GetBackgroundPri
PROCESS_PRIORITY_BACKGROUND_HOMESCREEN :
PROCESS_PRIORITY_BACKGROUND;
}
void
ProcessPriorityManager::ResetPriority()
{
+ if (!mObservedTabChildCreated) {
+ LOG("ResetPriority bailing because we haven't observed "
+ "a tab-child-created event.");
+ return;
+ }
+
if (ComputeIsInForeground()) {
SetIsForeground();
} else if (IsBackgroundPriority(mProcessPriority)) {
// If we're already in the background, recompute our background priority
// and set it immediately.
SetIsBackgroundNow();
} else {
ScheduleResetPriority("backgroundGracePeriodMS");
}
}
void
ProcessPriorityManager::ResetPriorityNow()
{
+ if (!mObservedTabChildCreated) {
+ LOG("ResetPriorityNow bailing because we haven't observed "
+ "a tab-child-created event.");
+ return;
+ }
+
if (ComputeIsInForeground()) {
- SetIsForeground();
+ SetIsForegroundNow();
} else {
SetIsBackgroundNow();
}
}
bool
ProcessPriorityManager::ComputeIsInForeground()
{
@@ -520,16 +555,32 @@ ProcessPriorityManager::ComputeIsInForeg
return !allHidden;
}
void
ProcessPriorityManager::SetIsForeground()
{
ProcessPriority foregroundPriority = GetForegroundPriority();
+
+ if (mProcessPriority == PROCESS_PRIORITY_UNKNOWN ||
+ foregroundPriority < mProcessPriority) {
+ LOG("Giving grace period to %s -> %s transition.",
+ ProcessPriorityToString(mProcessPriority),
+ ProcessPriorityToString(foregroundPriority));
+ ScheduleResetPriority("backgroundGracePeriodMS");
+ } else {
+ SetIsForegroundNow();
+ }
+}
+
+void
+ProcessPriorityManager::SetIsForegroundNow()
+{
+ ProcessPriority foregroundPriority = GetForegroundPriority();
if (foregroundPriority == mProcessPriority) {
return;
}
// Cancel the memory minimization procedure we might have started.
nsCOMPtr<nsICancelableRunnable> runnable =
do_QueryReferent(mMemoryMinimizerRunnable);
if (runnable) {