Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,3 @@
ajwong@chromium.org
jdoerrie@chromium.org
wez@chromium.org

View file

@ -0,0 +1,121 @@
# What is this
Contains a written down set of principles and other information on //base/util.
Please add to it!
## About //base/util:
This directory is meant to house common utilities that can be shared across the
whole Chromium codebase. //base is similar, but due to the way //base grew over
time, it has not been well organized to allow for fine-grained ownership. Also,
there is a mixture of commonly useful utility code and extremely subtle code
with performance and security implications. The combination of the two lead to
a small number of //base OWNERS enforcing a very high quality bar over a
diverse bag of code. The goal of //base/util is to avoid both these issues.
Over time, functionality in //base proper will sorted into //base/util and
one of two things will occur:
1. //base becomes empty and subdirs in //base/util get moved up to //base
2. A distinct "core" //base module is distilled out.
We will iterate on this purpose as we add more functionality into this
directory.
## Structure of //base/util:
* No files other than README.md, and OWNERS are allowed in the top-level
directory.
* Subdirectories should be named for a class of functionality; it should
be relatively easy to browse the directory and find a utility.
* There is no top-level BUILD.gn. Subdirectories will have their own and
are allowed to reference other subdirectories.
## Responsibilities of //base/util/OWNERS
* Creating new categories.
* Helping dedupe and organize code being added.
* Ensuring the directory is somewhat browseable.
It is specifically NOT the OWNERS job to gatekeep what is a "good pattern"
for Chromium code. Obviously they can still object, but the objection
should be considered similar to any other Chromium dev objection.
There will be cases when a utility is deemed to be more negative than
positive after it has been landed. The //base/util/OWNERS may aide in
coordinating and tracking removal, but responsibility for actually
deleting the code and its uses falls to the *category* OWNERS.
## Guidelines for adding code to //base/util/{category}
* You will be added to an OWNERS file under //base/util/category and be
responsible for maintaining your addition.
* A //base/util/OWNER must approve of the location of your code.
* Code must be needed in at least 2 places in Chrome and have no "higher
layered" directory (eg "//ui/base, //media/base, etc.), that could
facilitate sharing.
* Code must have unittests, and coverage should be > 95%.
* Code must have clear usage documentation for all the APIs.
* Public APIs must be in `::util` namespace. All implementation details
should be in `::util::internal`. Macros, which are not namespaceable,
are permitted but should be used sparingly and cause a small pang of guilt.
* Implementation and expected usage must be understandable by another OWNER
in your subdirectory; if creating a new subdirectory you must find a
co-OWNER.
* New subdirectories should have their own BUILD.gn files.
## Why not just put new utilities in //base/FooDir?
At some point, //base/util directories could get moved back into //base.
Until then, //base/util will
1. make a distinct separation between "common useful utility code" from the
"extremely subtle code with performance and security implications" that //base
also houses.
2. remove //base/OWNERS as a bottleneck.
The boundary is still a work-in-progress, but should clear itself up with time
after some of the more obviously "utility-esque" classes are moved.
## How does this differ from //components
Both //components and //base/util contain subdirectories that are (a) intended
for reuse. In addition, //components imposes no global layering in Chromium, so
a subdirectory placed in //components can be used from most-to-all layers in the
codebase, subject to the dependencies that that subdirectory itself holds.
In spite of these similarities, there are *conceptual* differences: //components
contains things are closer to full features or subsystems (eg autofill, heap
profiler, cloud devices, visited link tracker) that are not really intended for
large scale reuse.
There is some overlap and at some point it will become a judgment call, but
in general, //components are a better fit if the code in question is a feature,
module, or subsystem. //base/util is better if it is a more narrow construct
such as a data structure, coding primitive, etc.
## Why not the "Rule-of-3?"
The [Rule-of-3](https://en.wikipedia.org/wiki/Rule_of_three_%28computer_programming%29)
is a simple guidance on when it makes sense to extract common functionality out
versus duplicating it. It has commonly been used in //base as a way of measuring
"how general" is this functionality.
Unfortunately, there are reasons for wanting to share code beyond just
cleanliness. For example, if you need to guarantee exact behavior across
two modules, duplication is not proper even if there will ever only be 2
users.
Furthermore, there is a chicken-and-egg problem that prevents incremental
adoption of a utility. For example, someone introduces ThingerDoer in
//foo/bar. Later, ThingerDoer is wanted in //foo/qux, but this still fails
the rule-of-3 test, MyDoer is created. When //foo/waldo wants it, it's
completely a game of chance whether or not the CL author manages to find
both classes, wants to spend the time to determine if they've diverged,
and then tries to abstract it.
As such, the rule-of-3 runs contrary to the goal of sharing that
//base/util is designed to facilitate.
## Tips
* if doing a mass-move of code, look at `//tools/git/mass-rename.py`

View file

@ -0,0 +1,3 @@
chrisha@chromium.org
fdoray@chromium.org
sebmarchand@chromium.org

View file

@ -0,0 +1,33 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/util/memory_pressure/fake_memory_pressure_monitor.h"
namespace util {
namespace test {
FakeMemoryPressureMonitor::FakeMemoryPressureMonitor()
: MultiSourceMemoryPressureMonitor(),
memory_pressure_level_(MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {}
FakeMemoryPressureMonitor::~FakeMemoryPressureMonitor() {}
void FakeMemoryPressureMonitor::SetAndNotifyMemoryPressure(
MemoryPressureLevel level) {
memory_pressure_level_ = level;
base::MemoryPressureListener::SimulatePressureNotification(level);
}
base::MemoryPressureMonitor::MemoryPressureLevel
FakeMemoryPressureMonitor::GetCurrentPressureLevel() const {
return memory_pressure_level_;
}
void FakeMemoryPressureMonitor::SetDispatchCallback(
const DispatchCallback& callback) {
LOG(ERROR) << "FakeMemoryPressureMonitor::SetDispatchCallback";
}
} // namespace test
} // namespace util

View file

@ -0,0 +1,40 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_MEMORY_PRESSURE_FAKE_MEMORY_PRESSURE_MONITOR_H_
#define BASE_UTIL_MEMORY_PRESSURE_FAKE_MEMORY_PRESSURE_MONITOR_H_
#include "base/macros.h"
#include "base/util/memory_pressure/multi_source_memory_pressure_monitor.h"
namespace util {
namespace test {
class FakeMemoryPressureMonitor
: public ::util::MultiSourceMemoryPressureMonitor {
public:
using MemoryPressureLevel =
::util::MultiSourceMemoryPressureMonitor::MemoryPressureLevel;
using DispatchCallback =
::util::MultiSourceMemoryPressureMonitor::DispatchCallback;
FakeMemoryPressureMonitor();
~FakeMemoryPressureMonitor() override;
void SetAndNotifyMemoryPressure(MemoryPressureLevel level);
// base::MemoryPressureMonitor overrides:
MemoryPressureLevel GetCurrentPressureLevel() const override;
void SetDispatchCallback(const DispatchCallback& callback) override;
private:
MemoryPressureLevel memory_pressure_level_;
DISALLOW_COPY_AND_ASSIGN(FakeMemoryPressureMonitor);
};
} // namespace test
} // namespace util
#endif // BASE_UTIL_MEMORY_PRESSURE_FAKE_MEMORY_PRESSURE_MONITOR_H_

View file

@ -0,0 +1,145 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/util/memory_pressure/memory_pressure_voter.h"
#include <numeric>
#include "base/stl_util.h"
#include "base/trace_event/trace_event.h"
namespace util {
class MemoryPressureVoterImpl : public MemoryPressureVoter {
public:
explicit MemoryPressureVoterImpl(MemoryPressureVoteAggregator* aggregator)
: aggregator_(aggregator) {}
~MemoryPressureVoterImpl() override {
// Remove this voter's vote.
if (vote_)
aggregator_->OnVote(vote_, base::nullopt);
}
MemoryPressureVoterImpl(MemoryPressureVoterImpl&&) = delete;
MemoryPressureVoterImpl& operator=(MemoryPressureVoterImpl&&) = delete;
void SetVote(base::MemoryPressureListener::MemoryPressureLevel level,
bool notify_listeners) override {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto old_vote = vote_;
vote_ = level;
aggregator_->OnVote(old_vote, vote_);
if (notify_listeners)
aggregator_->NotifyListeners();
}
private:
// This is the aggregator to which this voter's votes will be cast.
MemoryPressureVoteAggregator* const aggregator_;
// Optional<> is used here as the vote will be null until the voter's
// first vote calculation.
base::Optional<base::MemoryPressureListener::MemoryPressureLevel> vote_;
SEQUENCE_CHECKER(sequence_checker_);
};
MemoryPressureVoteAggregator::MemoryPressureVoteAggregator(Delegate* delegate)
: delegate_(delegate) {}
MemoryPressureVoteAggregator::~MemoryPressureVoteAggregator() {
DCHECK_EQ(std::accumulate(votes_.begin(), votes_.end(), 0), 0);
}
std::unique_ptr<MemoryPressureVoter>
MemoryPressureVoteAggregator::CreateVoter() {
return std::make_unique<MemoryPressureVoterImpl>(this);
}
void MemoryPressureVoteAggregator::OnVoteForTesting(
base::Optional<MemoryPressureLevel> old_vote,
base::Optional<MemoryPressureLevel> new_vote) {
OnVote(old_vote, new_vote);
}
void MemoryPressureVoteAggregator::NotifyListenersForTesting() {
NotifyListeners();
}
base::MemoryPressureListener::MemoryPressureLevel
MemoryPressureVoteAggregator::EvaluateVotesForTesting() {
return EvaluateVotes();
}
void MemoryPressureVoteAggregator::OnVote(
base::Optional<MemoryPressureLevel> old_vote,
base::Optional<MemoryPressureLevel> new_vote) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(old_vote || new_vote);
if (old_vote) {
DCHECK_LT(0u, votes_[old_vote.value()]);
votes_[old_vote.value()]--;
}
if (new_vote)
votes_[new_vote.value()]++;
auto old_pressure_level = current_pressure_level_;
// If the pressure level is not None then an asynchronous event will have been
// started below, it needs to be ended.
// Note that we record this event every time we receive a new vote to ensure
// that the begin event doesn't get dropped during long pressure sessions.
if (old_pressure_level ==
MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL) {
TRACE_EVENT_NESTABLE_ASYNC_END0("base", "MemoryPressure::CriticalPressure",
this);
} else if (old_pressure_level ==
MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE) {
TRACE_EVENT_NESTABLE_ASYNC_END0("base", "MemoryPressure::ModeratePressure",
this);
}
current_pressure_level_ = EvaluateVotes();
// Start an asynchronous tracing event to record this pressure session.
if (current_pressure_level_ ==
MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL) {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("base",
"MemoryPressure::CriticalPressure", this);
} else if (current_pressure_level_ ==
MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE) {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("base",
"MemoryPressure::ModeratePressure", this);
}
if (old_pressure_level != current_pressure_level_)
delegate_->OnMemoryPressureLevelChanged(current_pressure_level_);
}
void MemoryPressureVoteAggregator::NotifyListeners() {
delegate_->OnNotifyListenersRequested();
}
base::MemoryPressureListener::MemoryPressureLevel
MemoryPressureVoteAggregator::EvaluateVotes() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
static_assert(
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL == 2,
"Ensure that each memory pressure level is handled by this method.");
if (votes_[2])
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
if (votes_[1])
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
void MemoryPressureVoteAggregator::SetVotesForTesting(size_t none_votes,
size_t moderate_votes,
size_t critical_votes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
votes_[0] = none_votes;
votes_[1] = moderate_votes;
votes_[2] = critical_votes;
}
} // namespace util

View file

@ -0,0 +1,119 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_MEMORY_PRESSURE_MEMORY_PRESSURE_VOTER_H_
#define BASE_UTIL_MEMORY_PRESSURE_MEMORY_PRESSURE_VOTER_H_
#include <array>
#include "base/callback.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
namespace util {
// Interface used by code which actually monitors memory pressure, to inform
// a MemoryPressureAggregator when the pressure they observe changes, or they
// want to trigger a (re-)notification of clients of the current level.
// Voters must be used only from the same sequence as the Aggregator to which
// they are attached.
class MemoryPressureVoter {
public:
virtual ~MemoryPressureVoter() {}
// Called to set a vote / change a vote.
virtual void SetVote(base::MemoryPressureListener::MemoryPressureLevel level,
bool notify_listeners) = 0;
};
// Collects votes from MemoryPressureVoters and evaluates them to determine the
// pressure level for the MultiSourceMemoryPressureMonitor, which will own
// and outlive the aggregator. The pressure level is calculated as the most
// critical of all votes collected. This class is not thread safe and should be
// used from a single sequence.
class MemoryPressureVoteAggregator {
public:
class Delegate;
using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel;
explicit MemoryPressureVoteAggregator(Delegate* delegate);
~MemoryPressureVoteAggregator();
// Creates a MemoryPressureVoter attached to this Aggregator. The returned
// Voter must not out-live the Aggregator.
std::unique_ptr<MemoryPressureVoter> CreateVoter();
void OnVoteForTesting(base::Optional<MemoryPressureLevel> old_vote,
base::Optional<MemoryPressureLevel> new_vote);
void NotifyListenersForTesting();
base::MemoryPressureListener::MemoryPressureLevel EvaluateVotesForTesting();
void SetVotesForTesting(size_t none_votes,
size_t moderate_votes,
size_t critical_votes);
private:
friend class MemoryPressureVoterImpl;
// Invoked by MemoryPressureVoter as it calculates its vote. Optional is
// used so a voter can pass null as |old_vote| if this is their first vote, or
// null as |new_vote| if they are removing their vote (e.g. when the voter is
// being destroyed). |old_vote| and |new_vote| should never both be null.
void OnVote(base::Optional<MemoryPressureLevel> old_vote,
base::Optional<MemoryPressureLevel> new_vote);
// Triggers a notification of the MemoryPressureMonitor's current pressure
// level, allowing each of the various sources of input on MemoryPressureLevel
// to maintain their own signalling behavior.
// TODO(991361): Remove this behavior and standardize across platforms.
void NotifyListeners();
// Returns the highest index of |votes_| with a non-zero value, as a
// MemoryPressureLevel.
MemoryPressureLevel EvaluateVotes() const;
MemoryPressureLevel current_pressure_level_ =
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
Delegate* const delegate_;
// Array with one bucket for each potential MemoryPressureLevel. The overall
// MemoryPressureLevel is calculated as the highest index of a non-zero
// bucket.
// MEMORY_PRESSURE_LEVEL_CRITICAL + 1 is used in place of adding a kCount
// value to the MemoryPressureLevel enum as adding another value would require
// changing every instance of switch(MemoryPressureLevel) in Chromium, and the
// MemoryPressureLevel system will be changing soon regardless.
std::array<size_t,
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL + 1>
votes_ = {};
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(MemoryPressureVoteAggregator);
};
// Interface used to notify MemoryPressureVoteAggregator's owner of changes to
// vote aggregation.
class MemoryPressureVoteAggregator::Delegate {
public:
Delegate() = default;
virtual ~Delegate() = default;
// Invoked when the aggregate vote has changed.
virtual void OnMemoryPressureLevelChanged(
base::MemoryPressureListener::MemoryPressureLevel level) = 0;
// Invoked when a voter has determined that a notification of the current
// pressure level is necessary.
virtual void OnNotifyListenersRequested() = 0;
};
} // namespace util
#endif // BASE_UTIL_MEMORY_PRESSURE_MEMORY_PRESSURE_VOTER_H_

View file

@ -0,0 +1,152 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/util/memory_pressure/multi_source_memory_pressure_monitor.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/util/memory_pressure/system_memory_pressure_evaluator.h"
namespace util {
MultiSourceMemoryPressureMonitor::MultiSourceMemoryPressureMonitor()
: current_pressure_level_(
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
dispatch_callback_(base::BindRepeating(
&base::MemoryPressureListener::NotifyMemoryPressure)),
aggregator_(this) {
}
MultiSourceMemoryPressureMonitor::~MultiSourceMemoryPressureMonitor() {
// Destroy system evaluator early while the remaining members of this class
// still exist. MultiSourceMemoryPressureMonitor implements
// MemoryPressureVoteAggregator::Delegate, and
// delegate_->OnMemoryPressureLevelChanged() gets indirectly called during
// ~SystemMemoryPressureEvaluator().
system_evaluator_.reset();
}
void MultiSourceMemoryPressureMonitor::Start() {
system_evaluator_ =
SystemMemoryPressureEvaluator::CreateDefaultSystemEvaluator(this);
StartMetricsTimer();
}
void MultiSourceMemoryPressureMonitor::StartMetricsTimer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Unretained is safe here since this task is running on a timer owned by this
// object.
metric_timer_.Start(
FROM_HERE, MemoryPressureMonitor::kUMAMemoryPressureLevelPeriod,
BindRepeating(
&MultiSourceMemoryPressureMonitor::RecordCurrentPressureLevel,
base::Unretained(this)));
}
void MultiSourceMemoryPressureMonitor::StopMetricsTimer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
metric_timer_.Stop();
}
void MultiSourceMemoryPressureMonitor::RecordCurrentPressureLevel() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
RecordMemoryPressure(GetCurrentPressureLevel(), /* ticks = */ 1);
}
base::MemoryPressureListener::MemoryPressureLevel
MultiSourceMemoryPressureMonitor::GetCurrentPressureLevel() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return current_pressure_level_;
}
std::unique_ptr<MemoryPressureVoter>
MultiSourceMemoryPressureMonitor::CreateVoter() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return aggregator_.CreateVoter();
}
void MultiSourceMemoryPressureMonitor::SetDispatchCallback(
const DispatchCallback& callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
dispatch_callback_ = callback;
}
void MultiSourceMemoryPressureMonitor::OnMemoryPressureLevelChanged(
base::MemoryPressureListener::MemoryPressureLevel level) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(current_pressure_level_, level);
TRACE_EVENT_INSTANT1(
"base", "MultiSourceMemoryPressureMonitor::OnMemoryPressureLevelChanged",
TRACE_EVENT_SCOPE_THREAD, "level", level);
// Records the duration of the latest pressure session, there are 4
// transitions of interest:
// - Moderate -> None
// - Moderate -> Critical
// - Critical -> Moderate
// - Critical -> None
base::TimeTicks now = base::TimeTicks::Now();
if (current_pressure_level_ !=
MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {
DCHECK(!last_pressure_change_timestamp_.is_null());
std::string histogram_name = "Memory.PressureWindowDuration.";
switch (current_pressure_level_) {
// From:
case MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE: {
// To:
if (level == MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {
histogram_name += "ModerateToNone";
} else { // MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL
histogram_name += "ModerateToCritical";
}
break;
}
// From:
case MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL: {
// To:
if (level == MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {
histogram_name += "CriticalToNone";
} else { // MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE
histogram_name += "CriticalToModerate";
}
break;
}
case MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE:
default:
break;
}
base::UmaHistogramCustomTimes(
histogram_name, now - last_pressure_change_timestamp_,
base::TimeDelta::FromSeconds(1), base::TimeDelta::FromMinutes(10), 50);
}
last_pressure_change_timestamp_ = now;
current_pressure_level_ = level;
}
void MultiSourceMemoryPressureMonitor::OnNotifyListenersRequested() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
dispatch_callback_.Run(current_pressure_level_);
}
void MultiSourceMemoryPressureMonitor::ResetSystemEvaluatorForTesting() {
system_evaluator_.reset();
}
void MultiSourceMemoryPressureMonitor::SetSystemEvaluator(
std::unique_ptr<SystemMemoryPressureEvaluator> evaluator) {
DCHECK(!system_evaluator_);
system_evaluator_ = std::move(evaluator);
}
} // namespace util

View file

@ -0,0 +1,86 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_MEMORY_PRESSURE_MULTI_SOURCE_MEMORY_PRESSURE_MONITOR_H_
#define BASE_UTIL_MEMORY_PRESSURE_MULTI_SOURCE_MEMORY_PRESSURE_MONITOR_H_
#include "base/memory/memory_pressure_monitor.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/util/memory_pressure/memory_pressure_voter.h"
namespace util {
class SystemMemoryPressureEvaluator;
// This is a specialization of a MemoryPressureMonitor that relies on a set of
// MemoryPressureVoters to determine the memory pressure state. The
// MemoryPressureVoteAggregator is in charge of receiving votes from these
// voters and notifying MemoryPressureListeners of the MemoryPressureLevel via
// the monitor's |dispatch_callback_|. The pressure level is calculated as the
// most critical of all votes collected.
// This class is not thread safe and should be used from a single sequence.
class MultiSourceMemoryPressureMonitor
: public base::MemoryPressureMonitor,
public MemoryPressureVoteAggregator::Delegate {
public:
using MemoryPressureLevel = base::MemoryPressureMonitor::MemoryPressureLevel;
using DispatchCallback = base::MemoryPressureMonitor::DispatchCallback;
MultiSourceMemoryPressureMonitor();
~MultiSourceMemoryPressureMonitor() override;
// Start monitoring memory pressure using the platform-specific voter.
void Start();
// MemoryPressureMonitor implementation.
MemoryPressureLevel GetCurrentPressureLevel() const override;
void SetDispatchCallback(const DispatchCallback& callback) override;
// Creates a MemoryPressureVoter to be owned/used by a source that wishes to
// have input on the overall memory pressure level.
std::unique_ptr<MemoryPressureVoter> CreateVoter();
MemoryPressureVoteAggregator* aggregator_for_testing() {
return &aggregator_;
}
void ResetSystemEvaluatorForTesting();
void SetSystemEvaluator(
std::unique_ptr<SystemMemoryPressureEvaluator> evaluator);
protected:
void StartMetricsTimer();
void StopMetricsTimer();
private:
// Delegate implementation.
void OnMemoryPressureLevelChanged(MemoryPressureLevel level) override;
void OnNotifyListenersRequested() override;
void RecordCurrentPressureLevel();
MemoryPressureLevel current_pressure_level_;
DispatchCallback dispatch_callback_;
MemoryPressureVoteAggregator aggregator_;
std::unique_ptr<SystemMemoryPressureEvaluator> system_evaluator_;
// A periodic timer to record UMA metrics.
base::RepeatingTimer metric_timer_;
// The timestamp of the last pressure change event.
base::TimeTicks last_pressure_change_timestamp_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(MultiSourceMemoryPressureMonitor);
};
} // namespace util
#endif // BASE_UTIL_MEMORY_PRESSURE_MULTI_SOURCE_MEMORY_PRESSURE_MONITOR_H_

View file

@ -0,0 +1,64 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/util/memory_pressure/system_memory_pressure_evaluator.h"
#include "build/build_config.h"
#if defined(OS_CHROMEOS)
#include "base/util/memory_pressure/system_memory_pressure_evaluator_chromeos.h"
#elif defined(OS_FUCHSIA)
#include "base/util/memory_pressure/system_memory_pressure_evaluator_fuchsia.h"
#elif defined(OS_MACOSX) && !defined(OS_IOS)
#include "base/util/memory_pressure/system_memory_pressure_evaluator_mac.h"
#elif defined(OS_WIN)
#include "base/util/memory_pressure/system_memory_pressure_evaluator_win.h"
#endif
namespace util {
// static
std::unique_ptr<SystemMemoryPressureEvaluator>
SystemMemoryPressureEvaluator::CreateDefaultSystemEvaluator(
MultiSourceMemoryPressureMonitor* monitor) {
#if defined(OS_CHROMEOS)
if (util::chromeos::SystemMemoryPressureEvaluator::
SupportsKernelNotifications()) {
return std::make_unique<util::chromeos::SystemMemoryPressureEvaluator>(
monitor->CreateVoter());
}
LOG(ERROR) << "No MemoryPressureMonitor created because the kernel does "
"not support notifications.";
#elif defined(OS_FUCHSIA)
return std::make_unique<util::SystemMemoryPressureEvaluatorFuchsia>(
monitor->CreateVoter());
#elif defined(OS_MACOSX) && !defined(OS_IOS)
return std::make_unique<util::mac::SystemMemoryPressureEvaluator>(
monitor->CreateVoter());
#elif defined(OS_WIN)
return std::make_unique<util::win::SystemMemoryPressureEvaluator>(
monitor->CreateVoter());
#endif
return nullptr;
}
SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
std::unique_ptr<MemoryPressureVoter> voter)
: current_vote_(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE),
voter_(std::move(voter)) {}
SystemMemoryPressureEvaluator::~SystemMemoryPressureEvaluator() = default;
void SystemMemoryPressureEvaluator::SetCurrentVote(
base::MemoryPressureListener::MemoryPressureLevel level) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
current_vote_ = level;
}
void SystemMemoryPressureEvaluator::SendCurrentVote(bool notify) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
voter_->SetVote(current_vote_, notify);
}
} // namespace util

View file

@ -0,0 +1,56 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_H_
#define BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_H_
#include "base/memory/memory_pressure_listener.h"
#include "base/util/memory_pressure/memory_pressure_voter.h"
#include "base/util/memory_pressure/multi_source_memory_pressure_monitor.h"
namespace util {
// Base class for the platform SystemMemoryPressureEvaluators, which use
// MemoryPressureVoters to cast their vote on the overall MemoryPressureLevel.
class SystemMemoryPressureEvaluator {
public:
// Used by the MemoryPressureMonitor to create the correct Evaluator for the
// platform in use.
static std::unique_ptr<SystemMemoryPressureEvaluator>
CreateDefaultSystemEvaluator(MultiSourceMemoryPressureMonitor* monitor);
virtual ~SystemMemoryPressureEvaluator();
base::MemoryPressureListener::MemoryPressureLevel current_vote() const {
return current_vote_;
}
protected:
explicit SystemMemoryPressureEvaluator(
std::unique_ptr<MemoryPressureVoter> voter);
// Sets the Evaluator's |current_vote_| member without casting vote to the
// MemoryPressureVoteAggregator.
void SetCurrentVote(base::MemoryPressureListener::MemoryPressureLevel level);
// Uses the Evaluators' |voter_| to cast/update its vote on memory pressure
// level. The MemoryPressureListeners will only be notified of the newly
// calculated pressure level if |notify| is true.
void SendCurrentVote(bool notify) const;
private:
base::MemoryPressureListener::MemoryPressureLevel current_vote_;
// In charge of forwarding votes from here to the
// MemoryPressureVoteAggregator.
std::unique_ptr<MemoryPressureVoter> voter_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(SystemMemoryPressureEvaluator);
};
} // namespace util
#endif // BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_H_

View file

@ -0,0 +1,467 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/util/memory_pressure/system_memory_pressure_evaluator_chromeos.h"
#include <fcntl.h>
#include <sys/poll.h>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/posix/eintr_wrapper.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
namespace util {
namespace chromeos {
const base::Feature kCrOSUserSpaceLowMemoryNotification{
"CrOSUserSpaceLowMemoryNotification", base::FEATURE_DISABLED_BY_DEFAULT};
namespace {
// Pointer to the SystemMemoryPressureEvaluator used by TabManagerDelegate for
// chromeos to need to call into ScheduleEarlyCheck.
SystemMemoryPressureEvaluator* g_system_evaluator = nullptr;
// We try not to re-notify on moderate too frequently, this time
// controls how frequently we will notify after our first notification.
constexpr base::TimeDelta kModerateMemoryPressureCooldownTime =
base::TimeDelta::FromSeconds(10);
// The margin mem file contains the two memory levels, the first is the
// critical level and the second is the moderate level. Note, this
// file may contain more values but only the first two are used for
// memory pressure notifications in chromeos.
constexpr char kMarginMemFile[] = "/sys/kernel/mm/chromeos-low_mem/margin";
// The available memory file contains the available memory as determined
// by the kernel.
constexpr char kAvailableMemFile[] =
"/sys/kernel/mm/chromeos-low_mem/available";
// The reserved file cache.
constexpr char kMinFilelist[] = "/proc/sys/vm/min_filelist_kbytes";
// The estimation of how well zram based swap is compressed.
constexpr char kRamVsSwapWeight[] =
"/sys/kernel/mm/chromeos-low_mem/ram_vs_swap_weight";
// Converts an available memory value in MB to a memory pressure level.
base::MemoryPressureListener::MemoryPressureLevel
GetMemoryPressureLevelFromAvailable(int available_mb,
int moderate_avail_mb,
int critical_avail_mb) {
if (available_mb < critical_avail_mb)
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
if (available_mb < moderate_avail_mb)
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
uint64_t ReadFileToUint64(const base::FilePath& file) {
std::string file_contents;
if (!ReadFileToString(file, &file_contents))
return 0;
TrimWhitespaceASCII(file_contents, base::TRIM_ALL, &file_contents);
uint64_t file_contents_uint64 = 0;
if (!base::StringToUint64(file_contents, &file_contents_uint64))
return 0;
return file_contents_uint64;
}
uint64_t ReadAvailableMemoryMB(int available_fd) {
// Read the available memory.
char buf[32] = {};
// kernfs/file.c:
// "Once poll/select indicates that the value has changed, you
// need to close and re-open the file, or seek to 0 and read again.
ssize_t bytes_read = HANDLE_EINTR(pread(available_fd, buf, sizeof(buf), 0));
PCHECK(bytes_read != -1);
std::string mem_str(buf, bytes_read);
uint64_t available = std::numeric_limits<uint64_t>::max();
CHECK(base::StringToUint64(
base::TrimWhitespaceASCII(mem_str, base::TrimPositions::TRIM_ALL),
&available));
return available;
}
// This function will wait until the /sys/kernel/mm/chromeos-low_mem/available
// file becomes readable and then read the latest value. This file will only
// become readable once the available memory cross through one of the margin
// values specified in /sys/kernel/mm/chromeos-low_mem/margin, for more
// details see https://crrev.com/c/536336.
bool WaitForMemoryPressureChanges(int available_fd) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::WILL_BLOCK);
pollfd pfd = {available_fd, POLLPRI | POLLERR, 0};
int res = HANDLE_EINTR(poll(&pfd, 1, -1)); // Wait indefinitely.
PCHECK(res != -1);
if (pfd.revents != (POLLPRI | POLLERR)) {
// If we didn't receive POLLPRI | POLLERR it means we likely received
// POLLNVAL because the fd has been closed we will only log an error in
// other situations.
LOG_IF(ERROR, pfd.revents != POLLNVAL)
<< "WaitForMemoryPressureChanges received unexpected revents: "
<< pfd.revents;
// We no longer want to wait for a kernel notification if the fd has been
// closed.
return false;
}
return true;
}
} // namespace
SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
std::unique_ptr<MemoryPressureVoter> voter)
: SystemMemoryPressureEvaluator(
kMarginMemFile,
kAvailableMemFile,
base::BindRepeating(&WaitForMemoryPressureChanges),
/*disable_timer_for_testing*/ false,
base::FeatureList::IsEnabled(
chromeos::kCrOSUserSpaceLowMemoryNotification),
std::move(voter)) {}
SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
const std::string& margin_file,
const std::string& available_file,
base::RepeatingCallback<bool(int)> kernel_waiting_callback,
bool disable_timer_for_testing,
bool is_user_space_notify,
std::unique_ptr<MemoryPressureVoter> voter)
: util::SystemMemoryPressureEvaluator(std::move(voter)),
is_user_space_notify_(is_user_space_notify),
weak_ptr_factory_(this) {
DCHECK(g_system_evaluator == nullptr);
g_system_evaluator = this;
std::vector<int> margin_parts =
SystemMemoryPressureEvaluator::GetMarginFileParts(margin_file);
// This class SHOULD have verified kernel support by calling
// SupportsKernelNotifications() before creating a new instance of this.
// Therefore we will check fail if we don't have multiple margin values.
CHECK_LE(2u, margin_parts.size());
critical_pressure_threshold_mb_ = margin_parts[0];
moderate_pressure_threshold_mb_ = margin_parts[1];
UpdateMemoryParameters();
if (!is_user_space_notify_) {
kernel_waiting_callback_ = base::BindRepeating(
std::move(kernel_waiting_callback), available_mem_file_.get());
available_mem_file_ =
base::ScopedFD(HANDLE_EINTR(open(available_file.c_str(), O_RDONLY)));
CHECK(available_mem_file_.is_valid());
ScheduleWaitForKernelNotification();
}
if (!disable_timer_for_testing) {
// We will check the memory pressure and report the metric
// (ChromeOS.MemoryPressureLevel) every 1 second.
checking_timer_.Start(
FROM_HERE, base::TimeDelta::FromSeconds(1),
base::BindRepeating(&SystemMemoryPressureEvaluator::
CheckMemoryPressureAndRecordStatistics,
weak_ptr_factory_.GetWeakPtr()));
}
}
SystemMemoryPressureEvaluator::~SystemMemoryPressureEvaluator() {
DCHECK(g_system_evaluator);
g_system_evaluator = nullptr;
}
// static
SystemMemoryPressureEvaluator* SystemMemoryPressureEvaluator::Get() {
return g_system_evaluator;
}
std::vector<int> SystemMemoryPressureEvaluator::GetMarginFileParts() {
static const base::NoDestructor<std::vector<int>> margin_file_parts(
GetMarginFileParts(kMarginMemFile));
return *margin_file_parts;
}
std::vector<int> SystemMemoryPressureEvaluator::GetMarginFileParts(
const std::string& file) {
std::vector<int> margin_values;
std::string margin_contents;
if (base::ReadFileToString(base::FilePath(file), &margin_contents)) {
std::vector<std::string> margins =
base::SplitString(margin_contents, base::kWhitespaceASCII,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (const auto& v : margins) {
int value = -1;
if (!base::StringToInt(v, &value)) {
// If any of the values weren't parseable as an int we return
// nothing as the file format is unexpected.
LOG(ERROR) << "Unable to parse margin file contents as integer: " << v;
return std::vector<int>();
}
margin_values.push_back(value);
}
} else {
LOG(ERROR) << "Unable to read margin file: " << kMarginMemFile;
}
return margin_values;
}
// CalculateReservedFreeKB() calculates the reserved free memory in KiB from
// /proc/zoneinfo. Reserved pages are free pages reserved for emergent kernel
// allocation and are not available to the user space. It's the sum of high
// watermarks and max protection pages of memory zones. It implements the same
// reserved pages calculation in linux kernel calculate_totalreserve_pages().
//
// /proc/zoneinfo example:
// ...
// Node 0, zone DMA32
// pages free 422432
// min 16270
// low 20337
// high 24404
// ...
// protection: (0, 0, 1953, 1953)
//
// The high field is the high watermark for this zone. The protection field is
// the protected pages for lower zones. See the lowmem_reserve_ratio section in
// https://www.kernel.org/doc/Documentation/sysctl/vm.txt.
uint64_t SystemMemoryPressureEvaluator::CalculateReservedFreeKB(
const std::string& zoneinfo) {
constexpr uint64_t kPageSizeKB = 4;
uint64_t num_reserved_pages = 0;
for (const base::StringPiece& line : base::SplitStringPiece(
zoneinfo, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
std::vector<base::StringPiece> tokens = base::SplitStringPiece(
line, base::kWhitespaceASCII, base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
// Skip the line if there are not enough tokens.
if (tokens.size() < 2) {
continue;
}
if (tokens[0] == "high") {
// Parse the high watermark.
uint64_t high = 0;
if (base::StringToUint64(tokens[1], &high)) {
num_reserved_pages += high;
} else {
LOG(ERROR) << "Couldn't parse the high field in /proc/zoneinfo: "
<< tokens[1];
}
} else if (tokens[0] == "protection:") {
// Parse the protection pages.
uint64_t max = 0;
for (size_t i = 1; i < tokens.size(); ++i) {
uint64_t num = 0;
base::StringPiece entry;
if (i == 1) {
// Exclude the leading '(' and the trailing ','.
entry = tokens[i].substr(1, tokens[i].size() - 2);
} else {
// Exclude the trailing ',' or ')'.
entry = tokens[i].substr(0, tokens[i].size() - 1);
}
if (base::StringToUint64(entry, &num)) {
max = std::max(max, num);
} else {
LOG(ERROR)
<< "Couldn't parse the protection field in /proc/zoneinfo: "
<< entry;
}
}
num_reserved_pages += max;
}
}
return num_reserved_pages * kPageSizeKB;
}
uint64_t SystemMemoryPressureEvaluator::GetReservedMemoryKB() {
std::string file_contents;
if (!ReadFileToString(base::FilePath("/proc/zoneinfo"), &file_contents)) {
LOG(ERROR) << "Couldn't get /proc/zoneinfo";
return 0;
}
return CalculateReservedFreeKB(file_contents);
}
// CalculateAvailableMemoryUserSpaceKB implements the same available memory
// calculation as kernel function get_available_mem_adj(). The available memory
// consists of 3 parts: the free memory, the file cache, and the swappable
// memory. The available free memory is free memory minus reserved free memory.
// The available file cache is the total file cache minus reserved file cache
// (min_filelist). Because swapping is prohibited if there is no anonymous
// memory or no swap free, the swappable memory is the minimal of anonymous
// memory and swap free. As swapping memory is more costly than dropping file
// cache, only a fraction (1 / ram_swap_weight) of the swappable memory
// contributes to the available memory.
uint64_t SystemMemoryPressureEvaluator::CalculateAvailableMemoryUserSpaceKB(
const base::SystemMemoryInfoKB& info,
uint64_t reserved_free,
uint64_t min_filelist,
uint64_t ram_swap_weight) {
const uint64_t free = info.free;
const uint64_t anon = info.active_anon + info.inactive_anon;
const uint64_t file = info.active_file + info.inactive_file;
const uint64_t dirty = info.dirty;
const uint64_t swap_free = info.swap_free;
uint64_t available = (free > reserved_free) ? free - reserved_free : 0;
available += (file > dirty + min_filelist) ? file - dirty - min_filelist : 0;
available += std::min<uint64_t>(anon, swap_free) / ram_swap_weight;
return available;
}
uint64_t SystemMemoryPressureEvaluator::GetAvailableMemoryKB() {
if (is_user_space_notify_) {
base::SystemMemoryInfoKB info;
CHECK(base::GetSystemMemoryInfo(&info));
return CalculateAvailableMemoryUserSpaceKB(info, reserved_free_,
min_filelist_, ram_swap_weight_);
} else {
const uint64_t available_mem_mb =
ReadAvailableMemoryMB(available_mem_file_.get());
return available_mem_mb * 1024;
}
}
bool SystemMemoryPressureEvaluator::SupportsKernelNotifications() {
// Unfortunately at the moment the only way to determine if the chromeos
// kernel supports polling on the available file is to observe two values
// in the margin file, if the critical and moderate levels are specified
// there then we know the kernel must support polling on available.
return SystemMemoryPressureEvaluator::GetMarginFileParts().size() >= 2;
}
// CheckMemoryPressure will get the current memory pressure level by reading
// the available file.
void SystemMemoryPressureEvaluator::CheckMemoryPressure() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto old_vote = current_vote();
uint64_t mem_avail_mb = GetAvailableMemoryKB() / 1024;
SetCurrentVote(GetMemoryPressureLevelFromAvailable(
mem_avail_mb, moderate_pressure_threshold_mb_,
critical_pressure_threshold_mb_));
bool notify = true;
if (current_vote() ==
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE) {
last_moderate_notification_ = base::TimeTicks();
notify = false;
}
// In the case of MODERATE memory pressure we may be in this state for quite
// some time so we limit the rate at which we dispatch notifications.
else if (current_vote() ==
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE) {
if (old_vote == current_vote()) {
if (base::TimeTicks::Now() - last_moderate_notification_ <
kModerateMemoryPressureCooldownTime) {
notify = false;
} else if (old_vote ==
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) {
// Reset the moderate notification time if we just crossed back.
last_moderate_notification_ = base::TimeTicks::Now();
notify = false;
}
}
if (notify)
last_moderate_notification_ = base::TimeTicks::Now();
}
VLOG(1) << "SystemMemoryPressureEvaluator::CheckMemoryPressure dispatching "
"at level: "
<< current_vote();
SendCurrentVote(notify);
}
void SystemMemoryPressureEvaluator::HandleKernelNotification(bool result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If WaitForKernelNotification returned false then the FD has been closed and
// we just exit without waiting again.
if (!result) {
return;
}
CheckMemoryPressure();
// Now we need to schedule back our blocking task to wait for more
// kernel notifications.
ScheduleWaitForKernelNotification();
}
void SystemMemoryPressureEvaluator::CheckMemoryPressureAndRecordStatistics() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Note: If we support notifications of memory pressure changes in both
// directions we will not have to update the cached value as it will always
// be correct.
CheckMemoryPressure();
// Record UMA histogram statistics for the current memory pressure level, it
// would seem that only Memory.PressureLevel would be necessary.
constexpr int kNumberPressureLevels = 3;
UMA_HISTOGRAM_ENUMERATION("ChromeOS.MemoryPressureLevel", current_vote(),
kNumberPressureLevels);
}
void SystemMemoryPressureEvaluator::ScheduleEarlyCheck() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&SystemMemoryPressureEvaluator::CheckMemoryPressure,
weak_ptr_factory_.GetWeakPtr()));
}
void SystemMemoryPressureEvaluator::UpdateMemoryParameters() {
if (is_user_space_notify_) {
reserved_free_ = GetReservedMemoryKB();
min_filelist_ = ReadFileToUint64(base::FilePath(kMinFilelist));
ram_swap_weight_ = ReadFileToUint64(base::FilePath(kRamVsSwapWeight));
}
}
void SystemMemoryPressureEvaluator::ScheduleWaitForKernelNotification() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(kernel_waiting_callback_),
base::BindOnce(&SystemMemoryPressureEvaluator::HandleKernelNotification,
weak_ptr_factory_.GetWeakPtr()));
}
} // namespace chromeos
} // namespace util

View file

@ -0,0 +1,153 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_CHROMEOS_H_
#define BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_CHROMEOS_H_
#include <vector>
#include "base/base_export.h"
#include "base/feature_list.h"
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/weak_ptr.h"
#include "base/process/process_metrics.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/util/memory_pressure/memory_pressure_voter.h"
#include "base/util/memory_pressure/system_memory_pressure_evaluator.h"
namespace util {
namespace chromeos {
// A feature which controls user space low memory notification.
extern const base::Feature kCrOSUserSpaceLowMemoryNotification;
////////////////////////////////////////////////////////////////////////////////
// SystemMemoryPressureEvaluator
//
// A class to handle the observation of our free memory. It notifies the
// MemoryPressureListener of memory fill level changes, so that it can take
// action to reduce memory resources accordingly.
class SystemMemoryPressureEvaluator
: public util::SystemMemoryPressureEvaluator {
public:
// The SystemMemoryPressureEvaluator reads the pressure levels from the
// /sys/kernel/mm/chromeos-low_mem/margin and does not need to be configured.
//
// NOTE: You should check that the kernel supports notifications by calling
// SupportsKernelNotifications() before constructing a new instance of this
// class.
explicit SystemMemoryPressureEvaluator(
std::unique_ptr<MemoryPressureVoter> voter);
~SystemMemoryPressureEvaluator() override;
// GetMarginFileParts returns a vector of the configured margin file values.
// The margin file contains two or more values, but we're only concerned with
// the first two. The first represents critical memory pressure, the second
// is moderate memory pressure level.
static std::vector<int> GetMarginFileParts();
// GetAvailableMemoryKB returns the available memory in KiB.
uint64_t GetAvailableMemoryKB();
// SupportsKernelNotifications will return true if the kernel supports and is
// configured for notifications on memory availability changes.
static bool SupportsKernelNotifications();
// ScheduleEarlyCheck is used by the ChromeOS tab manager delegate to force it
// to quickly recheck pressure levels after a tab discard or some other
// action.
void ScheduleEarlyCheck();
// Returns the moderate pressure threshold as read from the margin file.
int ModeratePressureThresholdMBForTesting() const {
return moderate_pressure_threshold_mb_;
}
// Returns the critical pressure threshold as read from the margin file.
int CriticalPressureThresholdMBForTesting() const {
return critical_pressure_threshold_mb_;
}
// The memory parameters are saved for optimization. If these memory
// parameters are changed, call this function to update the saved values.
void UpdateMemoryParameters();
// Returns the current system memory pressure evaluator.
static SystemMemoryPressureEvaluator* Get();
protected:
// This constructor is only used for testing.
SystemMemoryPressureEvaluator(
const std::string& margin_file,
const std::string& available_file,
base::RepeatingCallback<bool(int)> kernel_waiting_callback,
bool disable_timer_for_testing,
bool is_user_space_notify,
std::unique_ptr<MemoryPressureVoter> voter);
static std::vector<int> GetMarginFileParts(const std::string& margin_file);
static uint64_t CalculateReservedFreeKB(const std::string& zoneinfo);
static uint64_t GetReservedMemoryKB();
static uint64_t CalculateAvailableMemoryUserSpaceKB(
const base::SystemMemoryInfoKB& info,
uint64_t reserved_free,
uint64_t min_filelist,
uint64_t ram_swap_weight);
void CheckMemoryPressure();
private:
void HandleKernelNotification(bool result);
void ScheduleWaitForKernelNotification();
void CheckMemoryPressureAndRecordStatistics();
int moderate_pressure_threshold_mb_ = 0;
int critical_pressure_threshold_mb_ = 0;
// We keep track of how long it has been since we last notified at the
// moderate level.
base::TimeTicks last_moderate_notification_;
// We keep track of how long it's been since we notified on the
// Memory.PressureLevel metric.
base::TimeTicks last_pressure_level_report_;
// File descriptor used to read and poll(2) available memory from sysfs,
// In /sys/kernel/mm/chromeos-low_mem/available.
base::ScopedFD available_mem_file_;
// A timer to check the memory pressure and to report an UMA metric
// periodically.
base::RepeatingTimer checking_timer_;
// Kernel waiting callback which is responsible for blocking on the
// available file until it receives a kernel notification, this is
// configurable to make testing easier.
base::RepeatingCallback<bool()> kernel_waiting_callback_;
// User space low memory notification mode.
const bool is_user_space_notify_;
// Values saved for user space available memory calculation. The value of
// |reserved_free_| should not change unless min_free_kbytes or
// lowmem_reserve_ratio change. The value of |min_filelist_| and
// |ram_swap_weight_| should not change unless the user sets them manually.
uint64_t reserved_free_;
uint64_t min_filelist_;
uint64_t ram_swap_weight_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<SystemMemoryPressureEvaluator> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(SystemMemoryPressureEvaluator);
};
} // namespace chromeos
} // namespace util
#endif // BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_CHROMEOS_H_

View file

@ -0,0 +1,80 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/util/memory_pressure/system_memory_pressure_evaluator_fuchsia.h"
#include <lib/sys/cpp/component_context.h>
#include "base/fuchsia/default_context.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/util/memory_pressure/memory_pressure_voter.h"
namespace util {
namespace {
base::MemoryPressureListener::MemoryPressureLevel
FuchsiaToBaseMemoryPressureLevel(fuchsia::memorypressure::Level level) {
switch (level) {
case fuchsia::memorypressure::Level::NORMAL:
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
case fuchsia::memorypressure::Level::WARNING:
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
case fuchsia::memorypressure::Level::CRITICAL:
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
};
}
} // namespace
SystemMemoryPressureEvaluatorFuchsia::SystemMemoryPressureEvaluatorFuchsia(
std::unique_ptr<util::MemoryPressureVoter> voter)
: util::SystemMemoryPressureEvaluator(std::move(voter)), binding_(this) {
binding_.set_error_handler([](zx_status_t status) {
// TODO(https://crbug.com/1020698): Update fuchsia.web docs to make this a
// required service, and make this a FATAL log.
ZX_LOG(WARNING, status) << "fuchsia.memorypressure.Provider disconnected.";
});
DVLOG(1) << "Registering for memory pressure updates.";
auto provider = base::fuchsia::ComponentContextForCurrentProcess()
->svc()
->Connect<fuchsia::memorypressure::Provider>();
provider->RegisterWatcher(binding_.NewBinding());
}
SystemMemoryPressureEvaluatorFuchsia::~SystemMemoryPressureEvaluatorFuchsia() =
default;
void SystemMemoryPressureEvaluatorFuchsia::OnLevelChanged(
fuchsia::memorypressure::Level level,
OnLevelChangedCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << "OnLevelChanged: level=" << static_cast<uint32_t>(level);
base::MemoryPressureListener::MemoryPressureLevel new_level =
FuchsiaToBaseMemoryPressureLevel(level);
VLOG(1) << "MemoryPressureLevel: " << new_level;
// Set the new vote, and determine whether to notify listeners.
SetCurrentVote(new_level);
switch (new_level) {
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
// By convention no notifications are sent when returning to NONE level.
SendCurrentVote(false);
break;
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
SendCurrentVote(true);
break;
}
callback();
}
} // namespace util

View file

@ -0,0 +1,45 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_FUCHSIA_H_
#define BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_FUCHSIA_H_
#include <fuchsia/memorypressure/cpp/fidl.h>
#include <lib/fidl/cpp/binding.h>
#include "base/sequence_checker.h"
#include "base/util/memory_pressure/system_memory_pressure_evaluator.h"
namespace util {
class MemoryPressureVoter;
// Registers with the fuchsia.memorypressure.Provider to be notified of changes
// to the system memory pressure level.
class SystemMemoryPressureEvaluatorFuchsia
: public SystemMemoryPressureEvaluator,
public fuchsia::memorypressure::Watcher {
public:
explicit SystemMemoryPressureEvaluatorFuchsia(
std::unique_ptr<util::MemoryPressureVoter> voter);
~SystemMemoryPressureEvaluatorFuchsia() override;
SystemMemoryPressureEvaluatorFuchsia(
const SystemMemoryPressureEvaluatorFuchsia&) = delete;
SystemMemoryPressureEvaluatorFuchsia& operator=(
const SystemMemoryPressureEvaluatorFuchsia&) = delete;
private:
// fuchsia::memorypressure::Watcher implementation.
void OnLevelChanged(fuchsia::memorypressure::Level level,
OnLevelChangedCallback callback) override;
fidl::Binding<fuchsia::memorypressure::Watcher> binding_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace util
#endif // BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_FUCHSIA_H_

View file

@ -0,0 +1,116 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/util/memory_pressure/system_memory_pressure_evaluator_mac.h"
#include <CoreFoundation/CoreFoundation.h>
#include <dlfcn.h>
#include <stddef.h>
#include <sys/sysctl.h>
#include <cmath>
#include "base/bind.h"
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/memory/memory_pressure_monitor.h"
#include "base/threading/sequenced_task_runner_handle.h"
// Redeclare for partial 10.9 availability.
DISPATCH_EXPORT const struct dispatch_source_type_s
_dispatch_source_type_memorypressure;
namespace util {
namespace mac {
base::MemoryPressureListener::MemoryPressureLevel
SystemMemoryPressureEvaluator::MemoryPressureLevelForMacMemoryPressureLevel(
int mac_memory_pressure_level) {
switch (mac_memory_pressure_level) {
case DISPATCH_MEMORYPRESSURE_NORMAL:
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
case DISPATCH_MEMORYPRESSURE_WARN:
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
case DISPATCH_MEMORYPRESSURE_CRITICAL:
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
}
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
std::unique_ptr<MemoryPressureVoter> voter)
: util::SystemMemoryPressureEvaluator(std::move(voter)),
memory_level_event_source_(dispatch_source_create(
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE,
0,
DISPATCH_MEMORYPRESSURE_WARN | DISPATCH_MEMORYPRESSURE_CRITICAL |
DISPATCH_MEMORYPRESSURE_NORMAL,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))),
weak_ptr_factory_(this) {
// WeakPtr needed because there is no guarantee that |this| is still be alive
// when the task posted to the TaskRunner or event handler runs.
base::WeakPtr<SystemMemoryPressureEvaluator> weak_this =
weak_ptr_factory_.GetWeakPtr();
scoped_refptr<base::TaskRunner> task_runner =
base::SequencedTaskRunnerHandle::Get();
// Attach an event handler to the memory pressure event source.
if (memory_level_event_source_.get()) {
dispatch_source_set_event_handler(memory_level_event_source_, ^{
task_runner->PostTask(
FROM_HERE,
base::BindRepeating(
&SystemMemoryPressureEvaluator::OnMemoryPressureChanged,
weak_this));
});
// Start monitoring the event source.
dispatch_resume(memory_level_event_source_);
}
}
SystemMemoryPressureEvaluator::~SystemMemoryPressureEvaluator() {
// Remove the memory pressure event source.
if (memory_level_event_source_.get()) {
dispatch_source_cancel(memory_level_event_source_);
}
}
int SystemMemoryPressureEvaluator::GetMacMemoryPressureLevel() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Get the raw memory pressure level from macOS.
int mac_memory_pressure_level;
size_t length = sizeof(int);
sysctlbyname("kern.memorystatus_vm_pressure_level",
&mac_memory_pressure_level, &length, nullptr, 0);
return mac_memory_pressure_level;
}
void SystemMemoryPressureEvaluator::UpdatePressureLevel() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Get the current macOS pressure level and convert to the corresponding
// Chrome pressure level.
SetCurrentVote(MemoryPressureLevelForMacMemoryPressureLevel(
GetMacMemoryPressureLevel()));
}
void SystemMemoryPressureEvaluator::OnMemoryPressureChanged() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The OS has sent a notification that the memory pressure level has changed.
// Go through the normal memory pressure level checking mechanism so that
// |current_vote_| and UMA get updated to the current value.
UpdatePressureLevel();
// Run the callback that's waiting on memory pressure change notifications.
// The convention is to not send notifiations on memory pressure returning to
// normal.
bool notify = current_vote() !=
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
SendCurrentVote(notify);
}
} // namespace mac
} // namespace util

View file

@ -0,0 +1,62 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_MAC_H_
#define BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_MAC_H_
#include <CoreFoundation/CFDate.h>
#include <dispatch/dispatch.h>
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_dispatch_object.h"
#include "base/macros.h"
#include "base/message_loop/message_pump_mac.h"
#include "base/sequence_checker.h"
#include "base/util/memory_pressure/memory_pressure_voter.h"
#include "base/util/memory_pressure/system_memory_pressure_evaluator.h"
namespace util {
namespace mac {
class TestSystemMemoryPressureEvaluator;
// Declares the interface for the Mac SystemMemoryPressureEvaluator, which
// reports memory pressure events and status.
class SystemMemoryPressureEvaluator
: public util::SystemMemoryPressureEvaluator {
public:
explicit SystemMemoryPressureEvaluator(
std::unique_ptr<MemoryPressureVoter> voter);
~SystemMemoryPressureEvaluator() override;
private:
friend TestSystemMemoryPressureEvaluator;
static base::MemoryPressureListener::MemoryPressureLevel
MemoryPressureLevelForMacMemoryPressureLevel(int mac_memory_pressure_level);
// Returns the raw memory pressure level from the macOS. Exposed for
// unit testing.
virtual int GetMacMemoryPressureLevel();
// Updates |last_pressure_level_| with the current memory pressure level.
void UpdatePressureLevel();
// Run |dispatch_callback| on memory pressure notifications from the OS.
void OnMemoryPressureChanged();
// The dispatch source that generates memory pressure change notifications.
base::ScopedDispatchObject<dispatch_source_t> memory_level_event_source_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<SystemMemoryPressureEvaluator> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(SystemMemoryPressureEvaluator);
};
} // namespace mac
} // namespace util
#endif // BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_MAC_H_

View file

@ -0,0 +1,210 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/util/memory_pressure/system_memory_pressure_evaluator_win.h"
#include <windows.h>
#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "base/util/memory_pressure/multi_source_memory_pressure_monitor.h"
namespace util {
namespace win {
namespace {
static const DWORDLONG kMBBytes = 1024 * 1024;
} // namespace
// The following constants have been lifted from similar values in the ChromeOS
// memory pressure monitor. The values were determined experimentally to ensure
// sufficient responsiveness of the memory pressure subsystem, and minimal
// overhead.
const int SystemMemoryPressureEvaluator::kModeratePressureCooldownMs = 10000;
// TODO(chrisha): Explore the following constants further with an experiment.
// A system is considered 'high memory' if it has more than 1.5GB of system
// memory available for use by the memory manager (not reserved for hardware
// and drivers). This is a fuzzy version of the ~2GB discussed below.
const int SystemMemoryPressureEvaluator::kLargeMemoryThresholdMb = 1536;
// These are the default thresholds used for systems with < ~2GB of physical
// memory. Such systems have been observed to always maintain ~100MB of
// available memory, paging until that is the case. To try to avoid paging a
// threshold slightly above this is chosen. The moderate threshold is slightly
// less grounded in reality and chosen as 2.5x critical.
const int
SystemMemoryPressureEvaluator::kSmallMemoryDefaultModerateThresholdMb = 500;
const int
SystemMemoryPressureEvaluator::kSmallMemoryDefaultCriticalThresholdMb = 200;
// These are the default thresholds used for systems with >= ~2GB of physical
// memory. Such systems have been observed to always maintain ~300MB of
// available memory, paging until that is the case.
const int
SystemMemoryPressureEvaluator::kLargeMemoryDefaultModerateThresholdMb =
1000;
const int
SystemMemoryPressureEvaluator::kLargeMemoryDefaultCriticalThresholdMb = 400;
SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
std::unique_ptr<MemoryPressureVoter> voter)
: util::SystemMemoryPressureEvaluator(std::move(voter)),
moderate_threshold_mb_(0),
critical_threshold_mb_(0),
moderate_pressure_repeat_count_(0) {
InferThresholds();
StartObserving();
}
SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
int moderate_threshold_mb,
int critical_threshold_mb,
std::unique_ptr<MemoryPressureVoter> voter)
: util::SystemMemoryPressureEvaluator(std::move(voter)),
moderate_threshold_mb_(moderate_threshold_mb),
critical_threshold_mb_(critical_threshold_mb),
moderate_pressure_repeat_count_(0) {
DCHECK_GE(moderate_threshold_mb_, critical_threshold_mb_);
DCHECK_LE(0, critical_threshold_mb_);
StartObserving();
}
SystemMemoryPressureEvaluator::~SystemMemoryPressureEvaluator() {
StopObserving();
}
void SystemMemoryPressureEvaluator::CheckMemoryPressureSoon() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, BindOnce(&SystemMemoryPressureEvaluator::CheckMemoryPressure,
weak_ptr_factory_.GetWeakPtr()));
}
void SystemMemoryPressureEvaluator::InferThresholds() {
// Default to a 'high' memory situation, which uses more conservative
// thresholds.
bool high_memory = true;
MEMORYSTATUSEX mem_status = {};
if (GetSystemMemoryStatus(&mem_status)) {
static const DWORDLONG kLargeMemoryThresholdBytes =
static_cast<DWORDLONG>(kLargeMemoryThresholdMb) * kMBBytes;
high_memory = mem_status.ullTotalPhys >= kLargeMemoryThresholdBytes;
}
if (high_memory) {
moderate_threshold_mb_ = kLargeMemoryDefaultModerateThresholdMb;
critical_threshold_mb_ = kLargeMemoryDefaultCriticalThresholdMb;
} else {
moderate_threshold_mb_ = kSmallMemoryDefaultModerateThresholdMb;
critical_threshold_mb_ = kSmallMemoryDefaultCriticalThresholdMb;
}
}
void SystemMemoryPressureEvaluator::StartObserving() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
timer_.Start(
FROM_HERE, base::MemoryPressureMonitor::kUMAMemoryPressureLevelPeriod,
BindRepeating(&SystemMemoryPressureEvaluator::CheckMemoryPressure,
weak_ptr_factory_.GetWeakPtr()));
}
void SystemMemoryPressureEvaluator::StopObserving() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If StartObserving failed, StopObserving will still get called.
timer_.Stop();
weak_ptr_factory_.InvalidateWeakPtrs();
}
void SystemMemoryPressureEvaluator::CheckMemoryPressure() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Get the previous pressure level and update the current one.
MemoryPressureLevel old_vote = current_vote();
SetCurrentVote(CalculateCurrentPressureLevel());
// |notify| will be set to true if MemoryPressureListeners need to be
// notified of a memory pressure level state change.
bool notify = false;
switch (current_vote()) {
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
break;
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
if (old_vote != current_vote()) {
// This is a new transition to moderate pressure so notify.
moderate_pressure_repeat_count_ = 0;
notify = true;
} else {
// Already in moderate pressure, only notify if sustained over the
// cooldown period.
const int kModeratePressureCooldownCycles =
kModeratePressureCooldownMs /
base::MemoryPressureMonitor::kUMAMemoryPressureLevelPeriod
.InMilliseconds();
if (++moderate_pressure_repeat_count_ ==
kModeratePressureCooldownCycles) {
moderate_pressure_repeat_count_ = 0;
notify = true;
}
}
break;
case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
// Always notify of critical pressure levels.
notify = true;
break;
}
SendCurrentVote(notify);
}
base::MemoryPressureListener::MemoryPressureLevel
SystemMemoryPressureEvaluator::CalculateCurrentPressureLevel() {
MEMORYSTATUSEX mem_status = {};
if (!GetSystemMemoryStatus(&mem_status))
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
// How much system memory is actively available for use right now, in MBs.
int phys_free = static_cast<int>(mem_status.ullAvailPhys / kMBBytes);
// TODO(chrisha): This should eventually care about address space pressure,
// but the browser process (where this is running) effectively never runs out
// of address space. Renderers occasionally do, but it does them no good to
// have the browser process monitor address space pressure. Long term,
// renderers should run their own address space pressure monitors and act
// accordingly, with the browser making cross-process decisions based on
// system memory pressure.
// Determine if the physical memory is under critical memory pressure.
if (phys_free <= critical_threshold_mb_)
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
// Determine if the physical memory is under moderate memory pressure.
if (phys_free <= moderate_threshold_mb_)
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
// No memory pressure was detected.
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
}
bool SystemMemoryPressureEvaluator::GetSystemMemoryStatus(
MEMORYSTATUSEX* mem_status) {
DCHECK(mem_status != nullptr);
mem_status->dwLength = sizeof(*mem_status);
if (!::GlobalMemoryStatusEx(mem_status))
return false;
return true;
}
} // namespace win
} // namespace util

View file

@ -0,0 +1,132 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_WIN_H_
#define BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_WIN_H_
#include "base/base_export.h"
#include "base/macros.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/timer/timer.h"
#include "base/util/memory_pressure/memory_pressure_voter.h"
#include "base/util/memory_pressure/system_memory_pressure_evaluator.h"
// To not pull in windows.h.
typedef struct _MEMORYSTATUSEX MEMORYSTATUSEX;
namespace util {
namespace win {
// Windows memory pressure voter. Because there is no OS provided signal this
// polls at a low frequency, and applies internal hysteresis.
class SystemMemoryPressureEvaluator
: public util::SystemMemoryPressureEvaluator {
public:
using MemoryPressureLevel = base::MemoryPressureListener::MemoryPressureLevel;
// Constants governing the polling and hysteresis behaviour of the observer.
// The time which should pass between 2 successive moderate memory pressure
// signals, in milliseconds.
static const int kModeratePressureCooldownMs;
// Constants governing the memory pressure level detection.
// The amount of total system memory beyond which a system is considered to be
// a large-memory system.
static const int kLargeMemoryThresholdMb;
// Default minimum free memory thresholds for small-memory systems, in MB.
static const int kSmallMemoryDefaultModerateThresholdMb;
static const int kSmallMemoryDefaultCriticalThresholdMb;
// Default minimum free memory thresholds for large-memory systems, in MB.
static const int kLargeMemoryDefaultModerateThresholdMb;
static const int kLargeMemoryDefaultCriticalThresholdMb;
// Default constructor. Will choose thresholds automatically based on the
// actual amount of system memory.
explicit SystemMemoryPressureEvaluator(
std::unique_ptr<MemoryPressureVoter> voter);
// Constructor with explicit memory thresholds. These represent the amount of
// free memory below which the applicable memory pressure state engages.
// For testing purposes.
SystemMemoryPressureEvaluator(int moderate_threshold_mb,
int critical_threshold_mb,
std::unique_ptr<MemoryPressureVoter> voter);
~SystemMemoryPressureEvaluator() override;
// Schedules a memory pressure check to run soon. This must be called on the
// same sequence where the monitor was instantiated.
void CheckMemoryPressureSoon();
// Returns the moderate pressure level free memory threshold, in MB.
int moderate_threshold_mb() const { return moderate_threshold_mb_; }
// Returns the critical pressure level free memory threshold, in MB.
int critical_threshold_mb() const { return critical_threshold_mb_; }
protected:
// Internals are exposed for unittests.
// Automatically infers threshold values based on system memory. This invokes
// GetMemoryStatus so it can be mocked in unittests.
void InferThresholds();
// Starts observing the memory fill level. Calls to StartObserving should
// always be matched with calls to StopObserving.
void StartObserving();
// Stop observing the memory fill level. May be safely called if
// StartObserving has not been called. Must be called from the same thread on
// which the monitor was instantiated.
void StopObserving();
// Checks memory pressure, storing the current level, applying any hysteresis
// and emitting memory pressure level change signals as necessary. This
// function is called periodically while the monitor is observing memory
// pressure. Must be called from the same thread on which the monitor was
// instantiated.
void CheckMemoryPressure();
// Calculates the current instantaneous memory pressure level. This does not
// use any hysteresis and simply returns the result at the current moment. Can
// be called on any thread.
MemoryPressureLevel CalculateCurrentPressureLevel();
// Gets system memory status. This is virtual as a unittesting hook. Returns
// true if the system call succeeds, false otherwise. Can be called on any
// thread.
virtual bool GetSystemMemoryStatus(MEMORYSTATUSEX* mem_status);
private:
// Threshold amounts of available memory that trigger pressure levels. See
// memory_pressure_monitor.cc for a discussion of reasonable values for these.
int moderate_threshold_mb_;
int critical_threshold_mb_;
// A periodic timer to check for memory pressure changes.
base::RepeatingTimer timer_;
// To slow down the amount of moderate pressure event calls, this gets used to
// count the number of events since the last event occurred. This is used by
// |CheckMemoryPressure| to apply hysteresis on the raw results of
// |CalculateCurrentPressureLevel|.
int moderate_pressure_repeat_count_;
// Ensures that this object is used from a single sequence.
SEQUENCE_CHECKER(sequence_checker_);
// Weak pointer factory to ourself used for scheduling calls to
// CheckMemoryPressure/CheckMemoryPressureAndRecordStatistics via |timer_|.
base::WeakPtrFactory<SystemMemoryPressureEvaluator> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(SystemMemoryPressureEvaluator);
};
} // namespace win
} // namespace util
#endif // BASE_UTIL_MEMORY_PRESSURE_SYSTEM_MEMORY_PRESSURE_EVALUATOR_WIN_H_

View file

@ -0,0 +1,2 @@
lukasza@chromium.org
mpawlowski@opera.com

View file

@ -0,0 +1,99 @@
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_TYPE_SAFETY_ID_TYPE_H_
#define BASE_UTIL_TYPE_SAFETY_ID_TYPE_H_
#include <cstdint>
#include "base/util/type_safety/strong_alias.h"
namespace util {
// A specialization of StrongAlias for integer-based identifiers.
//
// IdType32<>, IdType64<>, etc. wrap an integer id in a custom, type-safe type.
//
// IdType32<Foo> is an alternative to int, for a class Foo with methods like:
//
// int GetId() { return id_; };
// static Foo* FromId(int id) { return g_all_foos_by_id[id]; }
//
// Such methods are a standard means of safely referring to objects across
// thread and process boundaries. But if a nearby class Bar also represents
// its IDs as a bare int, horrific mixups are possible -- one example, of many,
// is http://crrev.com/365437. IdType<> offers compile-time protection against
// such mishaps, since IdType32<Foo> is incompatible with IdType32<Bar>, even
// though both just compile down to an int32_t.
//
// Templates in this file:
// IdType32<T> / IdTypeU32<T>: Signed / unsigned 32-bit IDs
// IdType64<T> / IdTypeU64<T>: Signed / unsigned 64-bit IDs
// IdType<>: For when you need a different underlying type or
// a default/null value other than zero.
//
// IdType32<Foo> behaves just like an int32_t in the following aspects:
// - it can be used as a key in std::map and/or std::unordered_map;
// - it can be used as an argument to DCHECK_EQ or streamed to LOG(ERROR);
// - it has the same memory footprint and runtime overhead as int32_t;
// - it can be copied by memcpy.
// - it can be used in IPC messages.
//
// IdType32<Foo> has the following differences from a bare int32_t:
// - it forces coercions to go through the explicit constructor and value()
// getter;
// - it restricts the set of available operations (i.e. no multiplication);
// - it default-constructs to a null value and allows checking against the null
// value via is_null method.
template <typename TypeMarker, typename WrappedType, WrappedType kInvalidValue>
class IdType : public StrongAlias<TypeMarker, WrappedType> {
public:
static_assert(kInvalidValue <= 0,
"The invalid value should be negative or equal to zero to "
"avoid overflow issues.");
using StrongAlias<TypeMarker, WrappedType>::StrongAlias;
// This class can be used to generate unique IdTypes. It keeps an internal
// counter that is continually increased by one every time an ID is generated.
class Generator {
public:
Generator() = default;
~Generator() = default;
// Generates the next unique ID.
IdType GenerateNextId() { return FromUnsafeValue(next_id_++); }
// Non-copyable.
Generator(const Generator&) = delete;
Generator& operator=(const Generator&) = delete;
private:
WrappedType next_id_ = kInvalidValue + 1;
};
// Default-construct in the null state.
IdType() : StrongAlias<TypeMarker, WrappedType>::StrongAlias(kInvalidValue) {}
bool is_null() const { return this->value() == kInvalidValue; }
// TODO(mpawlowski) Replace these with constructor/value() getter. The
// conversions are safe as long as they're explicit (which is taken care of by
// StrongAlias).
static IdType FromUnsafeValue(WrappedType value) { return IdType(value); }
WrappedType GetUnsafeValue() const { return this->value(); }
};
// Type aliases for convenience:
template <typename TypeMarker>
using IdType32 = IdType<TypeMarker, std::int32_t, 0>;
template <typename TypeMarker>
using IdTypeU32 = IdType<TypeMarker, std::uint32_t, 0>;
template <typename TypeMarker>
using IdType64 = IdType<TypeMarker, std::int64_t, 0>;
template <typename TypeMarker>
using IdTypeU64 = IdType<TypeMarker, std::uint64_t, 0>;
} // namespace util
#endif // BASE_UTIL_TYPE_SAFETY_ID_TYPE_H_

View file

@ -0,0 +1,48 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_TYPE_SAFETY_PASS_KEY_H_
#define BASE_UTIL_TYPE_SAFETY_PASS_KEY_H_
namespace util {
// util::PassKey can be used to restrict access to functions to an authorized
// caller. The primary use case is restricting the construction of an object in
// situations where the constructor needs to be public, which may be the case
// if the object must be constructed through a helper function, such as
// blink::MakeGarbageCollected.
//
// For example, to limit the creation of 'Foo' to the 'Manager' class:
//
// class Foo {
// public:
// Foo(util::PassKey<Manager>);
// };
//
// class Manager {
// public:
// using PassKey = util::PassKey<Manager>;
// Manager() : foo_(blink::MakeGarbageCollected<Foo>(PassKey())) {}
// void Trace(blink::Visitor* visitor) { visitor->Trace(foo_); }
// Foo* GetFooSingleton() { foo_; }
//
// private:
// blink::Member<Foo> foo_;
// };
//
// In the above example, the 'Foo' constructor requires an instance of
// util::PassKey<Manager>. Only Manager is allowed to create such instances,
// making the constructor unusable elsewhere.
template <typename T>
class PassKey {
private:
// Avoid =default to disallow creation by uniform initialization.
PassKey() {}
friend T;
};
} // namespace util
#endif // BASE_UTIL_TYPE_SAFETY_PASS_KEY_H_

View file

@ -0,0 +1,123 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_TYPE_SAFETY_STRONG_ALIAS_H_
#define BASE_UTIL_TYPE_SAFETY_STRONG_ALIAS_H_
#include <ostream>
#include <utility>
namespace util {
// A type-safe alternative for a typedef or a 'using' directive.
//
// C++ currently does not support type-safe typedefs, despite multiple proposals
// (ex. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3515.pdf). The
// next best thing is to try and emulate them in library code.
//
// The motivation is to disallow several classes of errors:
//
// using Orange = int;
// using Apple = int;
// Apple apple(2);
// Orange orange = apple; // Orange should not be able to become an Apple.
// Orange x = orange + apple; // Shouldn't add Oranges and Apples.
// if (orange > apple); // Shouldn't compare Apples to Oranges.
// void foo(Orange);
// void foo(Apple); // Redefinition.
// etc.
//
// StrongAlias may instead be used as follows:
//
// using Orange = StrongAlias<class OrangeTag, int>;
// using Apple = StrongAlias<class AppleTag, int>;
// Apple apple(2);
// Orange orange = apple; // Does not compile.
// Orange other_orange = orange; // Compiles, types match.
// Orange x = orange + apple; // Does not compile.
// Orange y = Orange(orange.value() + apple.value()); // Compiles.
// if (orange > apple); // Does not compile.
// if (orange > other_orange); // Compiles.
// void foo(Orange);
// void foo(Apple); // Compiles into separate overload.
//
// StrongAlias is a zero-cost abstraction, it's compiled away.
//
// TagType is an empty tag class (also called "phantom type") that only serves
// the type system to differentiate between different instantiations of the
// template.
// UnderlyingType may be almost any value type. Note that some methods of the
// StrongAlias may be unavailable (ie. produce elaborate compilation errors when
// used) if UnderlyingType doesn't support them.
//
// StrongAlias only directly exposes comparison operators (for convenient use in
// ordered containers) and a hash function (for unordered_map/set). It's
// impossible, without reflection, to expose all methods of the UnderlyingType
// in StrongAlias's interface. It's also potentially unwanted (ex. you don't
// want to be able to add two StrongAliases that represent socket handles).
// A getter is provided in case you need to access the UnderlyingType.
//
// See also
// - //styleguide/c++/blink-c++.md which provides recommendation and examples of
// using StrongAlias<Tag, bool> instead of a bare bool.
// - util::IdType<...> which provides helpers for specializing
// StrongAlias to be used as an id.
template <typename TagType, typename UnderlyingType>
class StrongAlias {
public:
StrongAlias() = default;
explicit StrongAlias(const UnderlyingType& v) : value_(v) {}
explicit StrongAlias(UnderlyingType&& v) : value_(std::move(v)) {}
~StrongAlias() = default;
StrongAlias(const StrongAlias& other) = default;
StrongAlias& operator=(const StrongAlias& other) = default;
StrongAlias(StrongAlias&& other) = default;
StrongAlias& operator=(StrongAlias&& other) = default;
const UnderlyingType& value() const { return value_; }
explicit operator UnderlyingType() const { return value_; }
bool operator==(const StrongAlias& other) const {
return value_ == other.value_;
}
bool operator!=(const StrongAlias& other) const {
return value_ != other.value_;
}
bool operator<(const StrongAlias& other) const {
return value_ < other.value_;
}
bool operator<=(const StrongAlias& other) const {
return value_ <= other.value_;
}
bool operator>(const StrongAlias& other) const {
return value_ > other.value_;
}
bool operator>=(const StrongAlias& other) const {
return value_ >= other.value_;
}
// Hasher to use in std::unordered_map, std::unordered_set, etc.
struct Hasher {
using argument_type = StrongAlias;
using result_type = std::size_t;
result_type operator()(const argument_type& id) const {
return std::hash<UnderlyingType>()(id.value());
}
};
protected:
UnderlyingType value_;
};
// Stream operator for convenience, streams the UnderlyingType.
template <typename TagType, typename UnderlyingType>
std::ostream& operator<<(std::ostream& stream,
const StrongAlias<TagType, UnderlyingType>& alias) {
return stream << alias.value();
}
} // namespace util
#endif // BASE_UTIL_TYPE_SAFETY_STRONG_ALIAS_H_

View file

@ -0,0 +1,2 @@
alancutter@chromium.org
jdoerrie@chromium.org

View file

@ -0,0 +1,60 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/util/values/values_util.h"
#include "base/strings/string_number_conversions.h"
namespace util {
base::Value Int64ToValue(int64_t integer) {
return base::Value(base::NumberToString(integer));
}
base::Optional<int64_t> ValueToInt64(const base::Value* value) {
return value ? ValueToInt64(*value) : base::nullopt;
}
base::Optional<int64_t> ValueToInt64(const base::Value& value) {
if (!value.is_string())
return base::nullopt;
int64_t integer;
if (!base::StringToInt64(value.GetString(), &integer))
return base::nullopt;
return integer;
}
base::Value TimeDeltaToValue(base::TimeDelta time_delta) {
return Int64ToValue(time_delta.InMicroseconds());
}
base::Optional<base::TimeDelta> ValueToTimeDelta(const base::Value* value) {
return value ? ValueToTimeDelta(*value) : base::nullopt;
}
base::Optional<base::TimeDelta> ValueToTimeDelta(const base::Value& value) {
base::Optional<int64_t> integer = ValueToInt64(value);
if (!integer)
return base::nullopt;
return base::TimeDelta::FromMicroseconds(*integer);
}
base::Value TimeToValue(base::Time time) {
return TimeDeltaToValue(time.ToDeltaSinceWindowsEpoch());
}
base::Optional<base::Time> ValueToTime(const base::Value* value) {
return value ? ValueToTime(*value) : base::nullopt;
}
base::Optional<base::Time> ValueToTime(const base::Value& value) {
base::Optional<base::TimeDelta> time_delta = ValueToTimeDelta(value);
if (!time_delta)
return base::nullopt;
return base::Time::FromDeltaSinceWindowsEpoch(*time_delta);
}
} // namespace util

View file

@ -0,0 +1,36 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_UTIL_VALUES_VALUES_UTIL_H_
#define BASE_UTIL_VALUES_VALUES_UTIL_H_
#include "base/optional.h"
#include "base/time/time.h"
#include "base/values.h"
namespace util {
// Simple helper functions for converting int64_t, base::TimeDelta and
// base::Time to numeric string base::Values.
// Because base::TimeDelta and base::Time share the same internal representation
// as int64_t they are stored using the exact same numeric string format.
// Stores the int64_t as a string.
base::Value Int64ToValue(int64_t integer);
base::Optional<int64_t> ValueToInt64(const base::Value* value);
base::Optional<int64_t> ValueToInt64(const base::Value& value);
// Converts the TimeDelta to an int64_t of microseconds.
base::Value TimeDeltaToValue(base::TimeDelta time_delta);
base::Optional<base::TimeDelta> ValueToTimeDelta(const base::Value* value);
base::Optional<base::TimeDelta> ValueToTimeDelta(const base::Value& value);
// Converts the Time to a TimeDelta from the Windows epoch.
base::Value TimeToValue(base::Time time);
base::Optional<base::Time> ValueToTime(const base::Value* value);
base::Optional<base::Time> ValueToTime(const base::Value& value);
} // namespace util
#endif // BASE_UTIL_VALUES_VALUES_UTIL_H_