Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
3
TMessagesProj/jni/voip/webrtc/base/util/OWNERS
Normal file
3
TMessagesProj/jni/voip/webrtc/base/util/OWNERS
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
ajwong@chromium.org
|
||||
jdoerrie@chromium.org
|
||||
wez@chromium.org
|
||||
121
TMessagesProj/jni/voip/webrtc/base/util/README.md
Normal file
121
TMessagesProj/jni/voip/webrtc/base/util/README.md
Normal 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`
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
chrisha@chromium.org
|
||||
fdoray@chromium.org
|
||||
sebmarchand@chromium.org
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
lukasza@chromium.org
|
||||
mpawlowski@opera.com
|
||||
|
|
@ -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_
|
||||
|
|
@ -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_
|
||||
|
|
@ -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_
|
||||
2
TMessagesProj/jni/voip/webrtc/base/util/values/OWNERS
Normal file
2
TMessagesProj/jni/voip/webrtc/base/util/values/OWNERS
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
alancutter@chromium.org
|
||||
jdoerrie@chromium.org
|
||||
|
|
@ -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
|
||||
36
TMessagesProj/jni/voip/webrtc/base/util/values/values_util.h
Normal file
36
TMessagesProj/jni/voip/webrtc/base/util/values/values_util.h
Normal 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_
|
||||
Loading…
Add table
Add a link
Reference in a new issue