Repo created

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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