mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-24 02:35:41 +00:00
b0e6709e13
This patch initializes some HAL components greedily so that we can get rid of lazy initializers within the code. Observers are still lazily initialized because they can be instanced within content processes but that doesn't always happen and we don't want to pay the memory price for structures we don't use. Shutdown is now happening at a fixed time for all HAL components save WakeLocks. This ensures that we don't destroy an object while still iterating over it, something that could happen before. Finally a workaround for a compiler limitation has been removed. Differential Revision: https://phabricator.services.mozilla.com/D3100 --HG-- extra : moz-landing-system : lando
269 lines
6.8 KiB
C++
269 lines
6.8 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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/. */
|
|
|
|
#include "Hal.h"
|
|
#include "mozilla/HalWakeLock.h"
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "nsAutoPtr.h"
|
|
#include "nsClassHashtable.h"
|
|
#include "nsDataHashtable.h"
|
|
#include "nsHashKeys.h"
|
|
#include "nsIPropertyBag2.h"
|
|
#include "nsIObserverService.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::hal;
|
|
|
|
namespace {
|
|
|
|
struct LockCount {
|
|
LockCount()
|
|
: numLocks(0)
|
|
, numHidden(0)
|
|
{}
|
|
uint32_t numLocks;
|
|
uint32_t numHidden;
|
|
nsTArray<uint64_t> processes;
|
|
};
|
|
|
|
typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
|
|
typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
|
|
|
|
int sActiveListeners = 0;
|
|
StaticAutoPtr<LockTable> sLockTable;
|
|
bool sIsShuttingDown = false;
|
|
|
|
WakeLockInformation
|
|
WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
|
|
{
|
|
nsString topic(aTopic);
|
|
WakeLockInformation info(topic, aLockCount.numLocks, aLockCount.numHidden,
|
|
aLockCount.processes);
|
|
|
|
return info;
|
|
}
|
|
|
|
static void
|
|
CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount)
|
|
{
|
|
for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
|
|
const uint64_t& key = iter.Key();
|
|
LockCount count = iter.UserData();
|
|
|
|
aTotalCount->numLocks += count.numLocks;
|
|
aTotalCount->numHidden += count.numHidden;
|
|
|
|
// This is linear in the number of processes, but that should be small.
|
|
if (!aTotalCount->processes.Contains(key)) {
|
|
aTotalCount->processes.AppendElement(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
class ClearHashtableOnShutdown final : public nsIObserver {
|
|
~ClearHashtableOnShutdown() {}
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIOBSERVER
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
|
|
|
|
NS_IMETHODIMP
|
|
ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
|
|
{
|
|
MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
|
|
|
|
sIsShuttingDown = true;
|
|
sLockTable = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
class CleanupOnContentShutdown final : public nsIObserver {
|
|
~CleanupOnContentShutdown() {}
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIOBSERVER
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
|
|
|
|
NS_IMETHODIMP
|
|
CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
|
|
{
|
|
MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
|
|
|
|
if (sIsShuttingDown) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
|
|
if (!props) {
|
|
NS_WARNING("ipc:content-shutdown message without property bag as subject");
|
|
return NS_OK;
|
|
}
|
|
|
|
uint64_t childID = 0;
|
|
nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
|
|
&childID);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
|
|
nsAutoPtr<ProcessLockTable>& table = iter.Data();
|
|
|
|
if (table->Get(childID, nullptr)) {
|
|
table->Remove(childID);
|
|
|
|
LockCount totalCount;
|
|
CountWakeLocks(table, &totalCount);
|
|
|
|
if (sActiveListeners) {
|
|
NotifyWakeLockChange(WakeLockInfoFromLockCount(iter.Key(),
|
|
totalCount));
|
|
}
|
|
|
|
if (totalCount.numLocks == 0) {
|
|
iter.Remove();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
NS_WARNING("ipc:content-shutdown message without childID property");
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace mozilla {
|
|
|
|
namespace hal {
|
|
|
|
void
|
|
WakeLockInit()
|
|
{
|
|
sLockTable = new LockTable();
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
|
|
obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
|
|
}
|
|
}
|
|
|
|
|
|
WakeLockState
|
|
ComputeWakeLockState(int aNumLocks, int aNumHidden)
|
|
{
|
|
if (aNumLocks == 0) {
|
|
return WAKE_LOCK_STATE_UNLOCKED;
|
|
} else if (aNumLocks == aNumHidden) {
|
|
return WAKE_LOCK_STATE_HIDDEN;
|
|
} else {
|
|
return WAKE_LOCK_STATE_VISIBLE;
|
|
}
|
|
}
|
|
|
|
} // namespace hal
|
|
|
|
namespace hal_impl {
|
|
|
|
void
|
|
EnableWakeLockNotifications()
|
|
{
|
|
sActiveListeners++;
|
|
}
|
|
|
|
void
|
|
DisableWakeLockNotifications()
|
|
{
|
|
sActiveListeners--;
|
|
}
|
|
|
|
void
|
|
ModifyWakeLock(const nsAString& aTopic,
|
|
hal::WakeLockControl aLockAdjust,
|
|
hal::WakeLockControl aHiddenAdjust,
|
|
uint64_t aProcessID)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
|
|
|
|
if (sIsShuttingDown) {
|
|
return;
|
|
}
|
|
|
|
ProcessLockTable* table = sLockTable->Get(aTopic);
|
|
LockCount processCount;
|
|
LockCount totalCount;
|
|
if (!table) {
|
|
table = new ProcessLockTable();
|
|
sLockTable->Put(aTopic, table);
|
|
} else {
|
|
table->Get(aProcessID, &processCount);
|
|
CountWakeLocks(table, &totalCount);
|
|
}
|
|
|
|
MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
|
|
MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
|
|
MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
|
|
MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
|
|
MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
|
|
MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
|
|
|
|
WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
|
|
bool processWasLocked = processCount.numLocks > 0;
|
|
|
|
processCount.numLocks += aLockAdjust;
|
|
processCount.numHidden += aHiddenAdjust;
|
|
|
|
totalCount.numLocks += aLockAdjust;
|
|
totalCount.numHidden += aHiddenAdjust;
|
|
|
|
if (processCount.numLocks) {
|
|
table->Put(aProcessID, processCount);
|
|
} else {
|
|
table->Remove(aProcessID);
|
|
}
|
|
if (!totalCount.numLocks) {
|
|
sLockTable->Remove(aTopic);
|
|
}
|
|
|
|
if (sActiveListeners &&
|
|
(oldState != ComputeWakeLockState(totalCount.numLocks,
|
|
totalCount.numHidden) ||
|
|
processWasLocked != (processCount.numLocks > 0))) {
|
|
|
|
WakeLockInformation info;
|
|
hal::GetWakeLockInfo(aTopic, &info);
|
|
NotifyWakeLockChange(info);
|
|
}
|
|
}
|
|
|
|
void
|
|
GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
|
|
{
|
|
if (sIsShuttingDown) {
|
|
NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
|
|
*aWakeLockInfo = WakeLockInformation();
|
|
return;
|
|
}
|
|
|
|
ProcessLockTable* table = sLockTable->Get(aTopic);
|
|
if (!table) {
|
|
*aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
|
|
return;
|
|
}
|
|
LockCount totalCount;
|
|
CountWakeLocks(table, &totalCount);
|
|
*aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
|
|
}
|
|
|
|
} // namespace hal_impl
|
|
} // namespace mozilla
|