2012-08-05 09:09:39 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set sw=2 ts=8 et ft=cpp : */
|
|
|
|
/* 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/. */
|
|
|
|
|
2013-01-04 03:39:25 +04:00
|
|
|
#include "mozilla/ClearOnShutdown.h"
|
2012-08-05 09:09:39 +04:00
|
|
|
#include "mozilla/dom/ipc/ProcessPriorityManager.h"
|
2012-12-19 06:37:35 +04:00
|
|
|
#include "mozilla/dom/ContentChild.h"
|
|
|
|
#include "mozilla/dom/TabChild.h"
|
2012-08-05 09:09:39 +04:00
|
|
|
#include "mozilla/Hal.h"
|
|
|
|
#include "mozilla/Preferences.h"
|
|
|
|
#include "mozilla/Services.h"
|
|
|
|
#include "mozilla/HalTypes.h"
|
|
|
|
#include "mozilla/TimeStamp.h"
|
2013-01-05 09:03:51 +04:00
|
|
|
#include "AudioChannelService.h"
|
2012-08-05 09:09:39 +04:00
|
|
|
#include "prlog.h"
|
2013-01-27 01:14:01 +04:00
|
|
|
#include "nsPrintfCString.h"
|
2012-08-05 09:09:39 +04:00
|
|
|
#include "nsWeakPtr.h"
|
|
|
|
#include "nsXULAppAPI.h"
|
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
|
|
#include "nsITimer.h"
|
|
|
|
#include "nsIObserver.h"
|
|
|
|
#include "nsIObserverService.h"
|
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsIDOMEventListener.h"
|
|
|
|
#include "nsIDOMWindow.h"
|
|
|
|
#include "nsIDOMEvent.h"
|
|
|
|
#include "nsIDOMEventTarget.h"
|
|
|
|
#include "nsIDOMDocument.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
2013-01-04 03:39:25 +04:00
|
|
|
#include "StaticPtr.h"
|
2012-08-05 09:09:39 +04:00
|
|
|
|
|
|
|
#ifdef XP_WIN
|
|
|
|
#include <process.h>
|
|
|
|
#define getpid _getpid
|
|
|
|
#else
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
using namespace mozilla::hal;
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
namespace ipc {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
static bool sInitialized = false;
|
2013-01-04 03:39:25 +04:00
|
|
|
class ProcessPriorityManager;
|
|
|
|
static StaticRefPtr<ProcessPriorityManager> sManager;
|
2012-08-05 09:09:39 +04:00
|
|
|
|
|
|
|
// Some header defines a LOG macro, but we don't want it here.
|
|
|
|
#ifdef LOG
|
|
|
|
#undef LOG
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Enable logging by setting
|
|
|
|
//
|
|
|
|
// NSPR_LOG_MODULES=ProcessPriorityManager:5
|
|
|
|
//
|
2013-02-08 18:32:23 +04:00
|
|
|
// in your environment. Or just comment out the "&& 0" below, if you're on
|
|
|
|
// Android/B2G.
|
2012-08-05 09:09:39 +04:00
|
|
|
|
2013-02-08 18:32:23 +04:00
|
|
|
#if defined(ANDROID) && 0
|
|
|
|
#include <android/log.h>
|
|
|
|
#define LOG(fmt, ...) \
|
|
|
|
__android_log_print(ANDROID_LOG_INFO, \
|
|
|
|
"Gecko:ProcessPriorityManager", \
|
|
|
|
fmt, ## __VA_ARGS__)
|
|
|
|
#elif defined(PR_LOGGING)
|
2012-10-30 03:32:10 +04:00
|
|
|
static PRLogModuleInfo*
|
|
|
|
GetPPMLog()
|
|
|
|
{
|
|
|
|
static PRLogModuleInfo *sLog;
|
|
|
|
if (!sLog)
|
|
|
|
sLog = PR_NewLogModule("ProcessPriorityManager");
|
|
|
|
return sLog;
|
|
|
|
}
|
2012-08-05 09:09:39 +04:00
|
|
|
#define LOG(fmt, ...) \
|
2012-10-30 03:32:10 +04:00
|
|
|
PR_LOG(GetPPMLog(), PR_LOG_DEBUG, \
|
2012-08-05 09:09:39 +04:00
|
|
|
("[%d] ProcessPriorityManager - " fmt, getpid(), ##__VA_ARGS__))
|
|
|
|
#else
|
|
|
|
#define LOG(fmt, ...)
|
|
|
|
#endif
|
|
|
|
|
2013-02-15 00:41:30 +04:00
|
|
|
uint64_t
|
|
|
|
GetContentChildID()
|
2012-12-19 06:37:35 +04:00
|
|
|
{
|
|
|
|
ContentChild* contentChild = ContentChild::GetSingleton();
|
2013-02-15 00:41:30 +04:00
|
|
|
if (!contentChild) {
|
|
|
|
return 0;
|
2012-12-19 06:37:35 +04:00
|
|
|
}
|
|
|
|
|
2013-02-15 00:41:30 +04:00
|
|
|
return contentChild->GetID();
|
2012-12-19 06:37:35 +04:00
|
|
|
}
|
|
|
|
|
2013-01-05 09:03:51 +04:00
|
|
|
/**
|
|
|
|
* Determine if the priority is a backround priority.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
IsBackgroundPriority(ProcessPriority aPriority)
|
|
|
|
{
|
|
|
|
return (aPriority == PROCESS_PRIORITY_BACKGROUND ||
|
|
|
|
aPriority == PROCESS_PRIORITY_BACKGROUND_HOMESCREEN ||
|
|
|
|
aPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE);
|
|
|
|
}
|
|
|
|
|
2012-08-05 09:09:39 +04:00
|
|
|
/**
|
2013-02-15 00:41:29 +04:00
|
|
|
* This class listens to various Gecko events and asks the hal back-end to
|
|
|
|
* change this process's priority when it transitions between various states of
|
|
|
|
* "importance".
|
2012-08-05 09:09:39 +04:00
|
|
|
*
|
2013-02-15 00:41:29 +04:00
|
|
|
* The process's priority determines its CPU priority and also how likely it is
|
|
|
|
* to be killed when the system is running out of memory.
|
2012-08-05 09:09:39 +04:00
|
|
|
*
|
2013-02-15 00:41:29 +04:00
|
|
|
* The most basic dichotomy in the ProcessPriorityManager is between
|
|
|
|
* "foreground" processes, which usually have at least one active docshell, and
|
|
|
|
* "background" processes.
|
2012-08-05 09:09:39 +04:00
|
|
|
*
|
2013-02-15 00:41:29 +04:00
|
|
|
* An important heuristic here is that we don't always mark a process as having
|
|
|
|
* "background" priority until it's met the requisite criteria for some amount
|
|
|
|
* of time.
|
2012-08-05 09:09:39 +04:00
|
|
|
*
|
2013-02-15 00:41:29 +04:00
|
|
|
* We do this because otherwise there are cases where we'd thrash a process
|
|
|
|
* between foreground and background priorities; for example, Gaia sometimes
|
|
|
|
* releases and re-acquires CPU wake locks in quick succession.
|
|
|
|
*
|
|
|
|
* On the other hand, when the embedder of an <iframe mozbrowser> calls
|
|
|
|
* setVisible(false) on an iframe, we immediately send the relevant process to
|
|
|
|
* the background, if it has no other foreground docshells. This is necessary
|
|
|
|
* to ensure that when we load an app, the embedder first has a chance to send
|
|
|
|
* the previous app into the background. This ensures that there's only one
|
|
|
|
* foreground app at a time, thus ensuring that we kill the right process if we
|
|
|
|
* come under memory pressure.
|
2012-08-05 09:09:39 +04:00
|
|
|
*/
|
|
|
|
class ProcessPriorityManager MOZ_FINAL
|
|
|
|
: public nsIObserver
|
|
|
|
, public nsIDOMEventListener
|
2013-01-27 01:14:01 +04:00
|
|
|
, public nsITimerCallback
|
2013-02-15 00:41:30 +04:00
|
|
|
, public WakeLockObserver
|
2012-08-05 09:09:39 +04:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
ProcessPriorityManager();
|
|
|
|
void Init();
|
|
|
|
|
|
|
|
NS_DECL_ISUPPORTS
|
2013-01-27 01:14:01 +04:00
|
|
|
NS_DECL_NSITIMERCALLBACK
|
2012-08-05 09:09:39 +04:00
|
|
|
NS_DECL_NSIOBSERVER
|
|
|
|
NS_DECL_NSIDOMEVENTLISTENER
|
2013-02-15 00:41:30 +04:00
|
|
|
void Notify(const WakeLockInformation& aWakeLockInfo);
|
2012-08-05 09:09:39 +04:00
|
|
|
|
2013-01-04 03:39:25 +04:00
|
|
|
ProcessPriority GetPriority() const { return mProcessPriority; }
|
|
|
|
|
2013-01-27 01:14:01 +04:00
|
|
|
/**
|
2013-02-15 00:41:30 +04:00
|
|
|
* This function doesn't do exactly what you might think; see the comment on
|
|
|
|
* ProcessPriorityManager.h::TemporarilyLockProcessPriority().
|
2013-01-27 01:14:01 +04:00
|
|
|
*/
|
2013-02-15 00:41:30 +04:00
|
|
|
void TemporarilyLockProcessPriority();
|
2013-01-27 01:14:01 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Recompute this process's priority and apply it, potentially after a brief
|
|
|
|
* delay.
|
|
|
|
*
|
2013-02-15 00:41:30 +04:00
|
|
|
* If the new priority is FOREGROUND*, it takes effect immediately.
|
2013-01-27 01:14:01 +04:00
|
|
|
*
|
|
|
|
* If the new priority is a BACKGROUND* priority and this process's priority
|
|
|
|
* is currently a BACKGROUND* priority, the new priority takes effect
|
|
|
|
* immediately.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
void ResetPriority();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recompute this process's priority and apply it immediately.
|
|
|
|
*/
|
|
|
|
void ResetPriorityNow();
|
|
|
|
|
2012-08-05 09:09:39 +04:00
|
|
|
private:
|
|
|
|
void OnContentDocumentGlobalCreated(nsISupports* aOuterWindow);
|
2013-01-27 01:14:01 +04:00
|
|
|
|
2013-02-15 00:41:30 +04:00
|
|
|
/**
|
|
|
|
* 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();
|
|
|
|
|
2013-01-27 01:14:01 +04:00
|
|
|
/**
|
|
|
|
* Compute whether this process is in the foreground and return the result.
|
|
|
|
*/
|
|
|
|
bool ComputeIsInForeground();
|
|
|
|
|
|
|
|
/**
|
2013-02-15 00:41:30 +04:00
|
|
|
* Set this process's priority to the appropriate FOREGROUND* priority
|
|
|
|
* immediately.
|
2013-01-27 01:14:01 +04:00
|
|
|
*/
|
|
|
|
void SetIsForeground();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set this process's priority to the appropriate BACKGROUND* priority
|
|
|
|
* immediately.
|
|
|
|
*/
|
|
|
|
void SetIsBackgroundNow();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If mResetPriorityTimer is null (i.e., not running), create a timer and set
|
|
|
|
* it to invoke ResetPriorityNow() after
|
|
|
|
* dom.ipc.processPriorityManager.aTimeoutPref ms.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ScheduleResetPriority(const char* aTimeoutPref);
|
|
|
|
|
2013-02-15 00:41:30 +04:00
|
|
|
// Tracks whether this process holds the "cpu" lock.
|
|
|
|
bool mHoldsCPUWakeLock;
|
|
|
|
|
|
|
|
// Tracks whether this process holds the "high-priority" lock.
|
|
|
|
bool mHoldsHighPriorityWakeLock;
|
|
|
|
|
2013-01-27 01:14:01 +04:00
|
|
|
// mProcessPriority tracks the priority we've given this process in hal.
|
2012-08-05 09:09:39 +04:00
|
|
|
ProcessPriority mProcessPriority;
|
|
|
|
|
|
|
|
nsTArray<nsWeakPtr> mWindows;
|
2013-01-27 01:14:01 +04:00
|
|
|
|
|
|
|
// When this timer expires, we set mResetPriorityTimer to null and run
|
|
|
|
// ResetPriorityNow().
|
|
|
|
nsCOMPtr<nsITimer> mResetPriorityTimer;
|
|
|
|
|
2012-12-11 22:13:29 +04:00
|
|
|
nsWeakPtr mMemoryMinimizerRunnable;
|
2012-08-05 09:09:39 +04:00
|
|
|
};
|
|
|
|
|
2013-02-15 00:41:30 +04:00
|
|
|
NS_IMPL_ISUPPORTS2(ProcessPriorityManager, nsIObserver, nsIDOMEventListener)
|
2012-08-05 09:09:39 +04:00
|
|
|
|
|
|
|
ProcessPriorityManager::ProcessPriorityManager()
|
2013-02-15 00:41:30 +04:00
|
|
|
: mHoldsCPUWakeLock(false)
|
|
|
|
, mHoldsHighPriorityWakeLock(false)
|
|
|
|
, mProcessPriority(ProcessPriority(-1))
|
2012-08-05 09:09:39 +04:00
|
|
|
{
|
2013-02-15 00:41:30 +04:00
|
|
|
// 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.
|
2013-01-27 01:14:01 +04:00
|
|
|
//
|
2013-02-15 00:41:30 +04:00
|
|
|
// 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.
|
2012-08-05 09:09:39 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessPriorityManager::Init()
|
|
|
|
{
|
|
|
|
LOG("Starting up.");
|
|
|
|
|
|
|
|
// We can't do this in the constructor because we need to hold a strong ref
|
|
|
|
// to |this| before calling these methods.
|
2013-02-15 00:41:29 +04:00
|
|
|
//
|
|
|
|
// 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.
|
2012-08-05 09:09:39 +04:00
|
|
|
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
|
|
|
os->AddObserver(this, "content-document-global-created", /* ownsWeak = */ false);
|
|
|
|
os->AddObserver(this, "inner-window-destroyed", /* ownsWeak = */ false);
|
2013-01-05 09:03:51 +04:00
|
|
|
os->AddObserver(this, "audio-channel-agent-changed", /* ownsWeak = */ false);
|
2013-02-15 00:41:29 +04:00
|
|
|
os->AddObserver(this, "process-priority:reset-now", /* ownsWeak = */ false);
|
2013-02-15 00:41:30 +04:00
|
|
|
|
|
|
|
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);
|
2012-08-05 09:09:39 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
ProcessPriorityManager::Observe(
|
|
|
|
nsISupports* aSubject,
|
|
|
|
const char* aTopic,
|
|
|
|
const PRUnichar* aData)
|
|
|
|
{
|
|
|
|
if (!strcmp(aTopic, "content-document-global-created")) {
|
|
|
|
OnContentDocumentGlobalCreated(aSubject);
|
2013-01-27 01:14:01 +04:00
|
|
|
} else if (!strcmp(aTopic, "inner-window-destroyed") ||
|
|
|
|
!strcmp(aTopic, "audio-channel-agent-changed")) {
|
|
|
|
ResetPriority();
|
2013-02-15 00:41:29 +04:00
|
|
|
} else if (!strcmp(aTopic, "process-priority:reset-now")) {
|
|
|
|
LOG("Got process-priority:reset-now notification.");
|
|
|
|
ResetPriorityNow();
|
2012-08-05 09:09:39 +04:00
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(false);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2013-02-15 00:41:30 +04:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-05 09:09:39 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
ProcessPriorityManager::HandleEvent(
|
|
|
|
nsIDOMEvent* aEvent)
|
|
|
|
{
|
|
|
|
LOG("Got visibilitychange.");
|
2013-01-27 01:14:01 +04:00
|
|
|
ResetPriority();
|
2012-08-05 09:09:39 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ProcessPriorityManager::OnContentDocumentGlobalCreated(
|
|
|
|
nsISupports* aOuterWindow)
|
|
|
|
{
|
2013-01-27 01:14:01 +04:00
|
|
|
LOG("DocumentGlobalCreated");
|
2012-08-05 09:09:39 +04:00
|
|
|
// Get the inner window (the topic of content-document-global-created is
|
|
|
|
// the /outer/ window!).
|
|
|
|
nsCOMPtr<nsPIDOMWindow> outerWindow = do_QueryInterface(aOuterWindow);
|
2012-09-14 14:00:31 +04:00
|
|
|
NS_ENSURE_TRUE_VOID(outerWindow);
|
2012-08-05 09:09:39 +04:00
|
|
|
nsCOMPtr<nsPIDOMWindow> innerWindow = outerWindow->GetCurrentInnerWindow();
|
2012-09-14 14:00:31 +04:00
|
|
|
NS_ENSURE_TRUE_VOID(innerWindow);
|
2012-08-05 09:09:39 +04:00
|
|
|
|
|
|
|
// We're only interested in top-level windows.
|
|
|
|
nsCOMPtr<nsIDOMWindow> parentOuterWindow;
|
|
|
|
innerWindow->GetScriptableParent(getter_AddRefs(parentOuterWindow));
|
2012-09-14 14:00:31 +04:00
|
|
|
NS_ENSURE_TRUE_VOID(parentOuterWindow);
|
2012-08-05 09:09:39 +04:00
|
|
|
if (parentOuterWindow != outerWindow) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(innerWindow);
|
2012-09-14 14:00:31 +04:00
|
|
|
NS_ENSURE_TRUE_VOID(target);
|
2012-08-05 09:09:39 +04:00
|
|
|
|
|
|
|
nsWeakPtr weakWin = do_GetWeakReference(innerWindow);
|
2012-09-14 14:00:31 +04:00
|
|
|
NS_ENSURE_TRUE_VOID(weakWin);
|
2012-08-05 09:09:39 +04:00
|
|
|
|
|
|
|
if (mWindows.Contains(weakWin)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-17 02:22:56 +04:00
|
|
|
target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
2012-08-05 09:09:39 +04:00
|
|
|
this,
|
|
|
|
/* useCapture = */ false,
|
|
|
|
/* wantsUntrusted = */ false);
|
|
|
|
|
|
|
|
mWindows.AppendElement(weakWin);
|
2013-02-15 00:41:30 +04:00
|
|
|
|
2013-01-27 01:14:01 +04:00
|
|
|
ResetPriority();
|
2012-08-05 09:09:39 +04:00
|
|
|
}
|
|
|
|
|
2013-02-15 00:41:30 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-05 09:09:39 +04:00
|
|
|
void
|
2013-01-27 01:14:01 +04:00
|
|
|
ProcessPriorityManager::ResetPriority()
|
2012-08-05 09:09:39 +04:00
|
|
|
{
|
2013-01-27 01:14:01 +04:00
|
|
|
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");
|
|
|
|
}
|
2012-08-05 09:09:39 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-01-27 01:14:01 +04:00
|
|
|
ProcessPriorityManager::ResetPriorityNow()
|
|
|
|
{
|
|
|
|
if (ComputeIsInForeground()) {
|
|
|
|
SetIsForeground();
|
|
|
|
} else {
|
|
|
|
SetIsBackgroundNow();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ProcessPriorityManager::ComputeIsInForeground()
|
2012-08-05 09:09:39 +04:00
|
|
|
{
|
2013-02-15 00:41:30 +04:00
|
|
|
// Critical processes holding the CPU/high-priority wake lock are always
|
|
|
|
// considered to be in the foreground.
|
|
|
|
if (IsCriticalProcessWithWakeLock()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-02-15 00:41:29 +04:00
|
|
|
// 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
|
|
|
|
// windows is prone to fewer errors (and one mistake doesn't mess you up for
|
|
|
|
// the entire session). Moreover, mWindows should be a very short list,
|
|
|
|
// since it contains only top-level content windows.
|
2012-08-05 09:09:39 +04:00
|
|
|
|
|
|
|
bool allHidden = true;
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t i = 0; i < mWindows.Length(); i++) {
|
2013-02-15 00:41:29 +04:00
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindows[i]);
|
2012-08-05 09:09:39 +04:00
|
|
|
if (!window) {
|
|
|
|
mWindows.RemoveElementAt(i);
|
|
|
|
i--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-02-15 00:41:29 +04:00
|
|
|
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(window);
|
|
|
|
if (!docshell) {
|
2012-08-05 09:09:39 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-02-15 00:41:29 +04:00
|
|
|
bool isActive = false;
|
|
|
|
docshell->GetIsActive(&isActive);
|
|
|
|
|
2013-02-15 00:41:30 +04:00
|
|
|
|
2012-08-05 09:09:39 +04:00
|
|
|
#ifdef DEBUG
|
2013-02-15 00:41:29 +04:00
|
|
|
nsAutoCString spec;
|
|
|
|
nsCOMPtr<nsIURI> uri = window->GetDocumentURI();
|
|
|
|
if (uri) {
|
|
|
|
uri->GetSpec(spec);
|
|
|
|
}
|
|
|
|
LOG("Docshell at %s has visibility %d.", spec.get(), isActive);
|
2012-08-05 09:09:39 +04:00
|
|
|
#endif
|
|
|
|
|
2013-02-15 00:41:29 +04:00
|
|
|
allHidden = allHidden && !isActive;
|
2012-08-05 09:09:39 +04:00
|
|
|
|
|
|
|
// We could break out early from this loop if
|
2013-02-15 00:41:29 +04:00
|
|
|
// isActive && mProcessPriority == BACKGROUND,
|
2012-08-05 09:09:39 +04:00
|
|
|
// but then we might not clean up all the weak refs.
|
|
|
|
}
|
|
|
|
|
2013-01-27 01:14:01 +04:00
|
|
|
return !allHidden;
|
2012-08-05 09:09:39 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-01-27 01:14:01 +04:00
|
|
|
ProcessPriorityManager::SetIsForeground()
|
2012-08-05 09:09:39 +04:00
|
|
|
{
|
2013-02-15 00:41:30 +04:00
|
|
|
ProcessPriority foregroundPriority = GetForegroundPriority();
|
|
|
|
if (foregroundPriority == mProcessPriority) {
|
2012-08-05 09:09:39 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-27 01:14:01 +04:00
|
|
|
// Cancel the memory minimization procedure we might have started.
|
|
|
|
nsCOMPtr<nsICancelableRunnable> runnable =
|
|
|
|
do_QueryReferent(mMemoryMinimizerRunnable);
|
|
|
|
if (runnable) {
|
|
|
|
runnable->Cancel();
|
2012-08-05 09:09:39 +04:00
|
|
|
}
|
2013-01-27 01:14:01 +04:00
|
|
|
|
2013-02-15 00:41:30 +04:00
|
|
|
mProcessPriority = foregroundPriority;
|
2013-02-08 18:32:23 +04:00
|
|
|
LOG("Setting priority to %s.", ProcessPriorityToString(mProcessPriority));
|
2013-02-15 17:40:01 +04:00
|
|
|
hal::SetProcessPriority(getpid(), mProcessPriority);
|
2012-08-05 09:09:39 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-01-27 01:14:01 +04:00
|
|
|
ProcessPriorityManager::SetIsBackgroundNow()
|
2012-08-05 09:09:39 +04:00
|
|
|
{
|
2013-01-27 01:14:01 +04:00
|
|
|
ProcessPriority backgroundPriority = GetBackgroundPriority();
|
|
|
|
if (mProcessPriority == backgroundPriority) {
|
|
|
|
return;
|
|
|
|
}
|
2012-08-05 09:09:39 +04:00
|
|
|
|
2013-01-27 01:14:01 +04:00
|
|
|
mProcessPriority = backgroundPriority;
|
2013-02-08 18:32:23 +04:00
|
|
|
LOG("Setting priority to %s", ProcessPriorityToString(mProcessPriority));
|
2012-12-19 06:37:35 +04:00
|
|
|
hal::SetProcessPriority(getpid(), mProcessPriority);
|
2012-10-22 23:40:19 +04:00
|
|
|
|
|
|
|
// We're in the background; dump as much memory as we can.
|
|
|
|
nsCOMPtr<nsIMemoryReporterManager> mgr =
|
|
|
|
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
|
|
|
if (mgr) {
|
2012-12-11 22:13:29 +04:00
|
|
|
nsCOMPtr<nsICancelableRunnable> runnable =
|
|
|
|
do_QueryReferent(mMemoryMinimizerRunnable);
|
|
|
|
|
|
|
|
// Cancel the previous task if it's still pending
|
|
|
|
if (runnable) {
|
|
|
|
runnable->Cancel();
|
|
|
|
}
|
|
|
|
|
|
|
|
mgr->MinimizeMemoryUsage(/* callback = */ nullptr,
|
|
|
|
getter_AddRefs(runnable));
|
|
|
|
mMemoryMinimizerRunnable = do_GetWeakReference(runnable);
|
2012-10-22 23:40:19 +04:00
|
|
|
}
|
2012-08-05 09:09:39 +04:00
|
|
|
}
|
|
|
|
|
2013-01-27 01:14:01 +04:00
|
|
|
void
|
|
|
|
ProcessPriorityManager::ScheduleResetPriority(const char* aTimeoutPref)
|
|
|
|
{
|
|
|
|
if (mResetPriorityTimer) {
|
2013-02-15 00:41:30 +04:00
|
|
|
LOG("ScheduleResetPriority bailing; the timer is already running.");
|
2013-01-27 01:14:01 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t timeout = Preferences::GetUint(
|
|
|
|
nsPrintfCString("dom.ipc.processPriorityManager.%s", aTimeoutPref).get());
|
|
|
|
LOG("Scheduling reset timer to fire in %dms.", timeout);
|
|
|
|
mResetPriorityTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
|
|
mResetPriorityTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
ProcessPriorityManager::Notify(nsITimer* aTimer)
|
|
|
|
{
|
|
|
|
LOG("Reset priority timer callback; about to ResetPriorityNow.");
|
|
|
|
ResetPriorityNow();
|
|
|
|
mResetPriorityTimer = nullptr;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2013-02-15 00:41:30 +04:00
|
|
|
ProcessPriorityManager::TemporarilyLockProcessPriority()
|
2013-01-27 01:14:01 +04:00
|
|
|
{
|
2013-02-15 00:41:30 +04:00
|
|
|
LOG("TemporarilyLockProcessPriority");
|
2013-01-27 01:14:01 +04:00
|
|
|
|
2013-02-15 00:41:30 +04:00
|
|
|
// 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()).
|
2013-01-27 01:14:01 +04:00
|
|
|
if (mResetPriorityTimer) {
|
|
|
|
mResetPriorityTimer->Cancel();
|
|
|
|
mResetPriorityTimer = nullptr;
|
|
|
|
}
|
2013-02-15 00:41:30 +04:00
|
|
|
ScheduleResetPriority("temporaryPriorityLockMS");
|
2013-01-27 01:14:01 +04:00
|
|
|
}
|
|
|
|
|
2012-08-05 09:09:39 +04:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
void
|
|
|
|
InitProcessPriorityManager()
|
|
|
|
{
|
|
|
|
if (sInitialized) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If IPC tabs aren't enabled at startup, don't bother with any of this.
|
|
|
|
if (!Preferences::GetBool("dom.ipc.processPriorityManager.enabled") ||
|
|
|
|
Preferences::GetBool("dom.ipc.tabs.disabled")) {
|
2013-01-27 01:14:01 +04:00
|
|
|
LOG("InitProcessPriorityManager bailing due to prefs.");
|
2012-08-05 09:09:39 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sInitialized = true;
|
|
|
|
|
|
|
|
// If we're the master process, mark ourselves as such and don't create a
|
|
|
|
// ProcessPriorityManager (we never want to mark the master process as
|
|
|
|
// backgrounded).
|
|
|
|
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
|
|
|
LOG("This is the master process.");
|
|
|
|
hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_MASTER);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-04 03:39:25 +04:00
|
|
|
sManager = new ProcessPriorityManager();
|
|
|
|
sManager->Init();
|
|
|
|
ClearOnShutdown(&sManager);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
CurrentProcessIsForeground()
|
|
|
|
{
|
2013-01-07 12:44:22 +04:00
|
|
|
// The process priority manager is the only thing which changes our priority,
|
|
|
|
// so if the manager does not exist, then we must be in the foreground.
|
|
|
|
if (!sManager) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-01-04 03:39:25 +04:00
|
|
|
return sManager->GetPriority() >= PROCESS_PRIORITY_FOREGROUND;
|
2012-08-05 09:09:39 +04:00
|
|
|
}
|
|
|
|
|
2013-01-27 01:14:01 +04:00
|
|
|
void
|
2013-02-15 00:41:30 +04:00
|
|
|
TemporarilyLockProcessPriority()
|
2013-01-27 01:14:01 +04:00
|
|
|
{
|
|
|
|
if (sManager) {
|
2013-02-15 00:41:30 +04:00
|
|
|
sManager->TemporarilyLockProcessPriority();
|
2013-01-27 01:14:01 +04:00
|
|
|
} else {
|
2013-02-15 00:41:30 +04:00
|
|
|
LOG("TemporarilyLockProcessPriority called before "
|
2013-01-27 01:14:01 +04:00
|
|
|
"InitProcessPriorityManager. Bailing.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-05 09:09:39 +04:00
|
|
|
} // namespace ipc
|
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|