-
Notifications
You must be signed in to change notification settings - Fork 6.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create classes for Memory Pressure Voting system.
This CL creates the MultiSourceMemoryPressureMonitor, MemoryPressureVoteAggregator, and MemoryPressureVoter classes described here: https://docs.google.com/document/d/1W3FPDyjIAKBcFGNYsHA3EKR1FHrJlbBaqT4_RUnxzq0/edit#. The aggregator class collects votes from voter instances via the OnVote method. A MemoryPressureVoteAggregator object will be owned by the MultiSourceMemoryPressureMonitor class, which will be merged with the MemoryPressureMonitor class after subsequent CL's which will migrate the OS-specific MemoryPressureMonitors to own a Voter and use that Voter to inform the Monitor of their votes. Bug: 980965 Change-Id: Ib84aadca9a4cf74b3c1f8a42787d63a96f5dfd17 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1719109 Commit-Queue: Ryan Powell <ryanpow@google.com> Reviewed-by: François Doray <fdoray@chromium.org> Reviewed-by: Sébastien Marchand <sebmarchand@chromium.org> Cr-Commit-Position: refs/heads/master@{#687282}
- Loading branch information
Ryan Powell
authored and
Commit Bot
committed
Aug 15, 2019
1 parent
b932662
commit 30287d4
Showing
10 changed files
with
558 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# 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. | ||
|
||
source_set("memory_pressure") { | ||
sources = [ | ||
"memory_pressure_voter.cc", | ||
"memory_pressure_voter.h", | ||
"multi_source_memory_pressure_monitor.cc", | ||
"multi_source_memory_pressure_monitor.h", | ||
] | ||
|
||
deps = [ | ||
"//base", | ||
] | ||
} | ||
|
||
source_set("unittests") { | ||
testonly = true | ||
sources = [ | ||
"memory_pressure_voter_unittest.cc", | ||
"multi_source_memory_pressure_monitor_unittest.cc", | ||
] | ||
|
||
deps = [ | ||
":memory_pressure", | ||
"//base", | ||
"//base/test:test_support", | ||
"//testing/gtest", | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
chrisha@chromium.org | ||
fdoray@chromium.org | ||
sebmarchand@chromium.org |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// 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" | ||
|
||
namespace util { | ||
|
||
MemoryPressureVoteAggregator::MemoryPressureVoteAggregator(Delegate* delegate) | ||
: current_pressure_level_( | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE), | ||
delegate_(delegate), | ||
votes_() {} | ||
|
||
MemoryPressureVoteAggregator::~MemoryPressureVoteAggregator() { | ||
DCHECK_EQ(std::accumulate(votes_.begin(), votes_.end(), 0), 0); | ||
} | ||
|
||
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_; | ||
current_pressure_level_ = EvaluateVotes(); | ||
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; | ||
} | ||
|
||
MemoryPressureVoter::MemoryPressureVoter( | ||
MemoryPressureVoteAggregator* aggregator) | ||
: aggregator_(aggregator) {} | ||
|
||
MemoryPressureVoter::~MemoryPressureVoter() { | ||
// Remove this voter's vote. | ||
if (vote_) | ||
aggregator_->OnVote(vote_, base::nullopt); | ||
} | ||
|
||
void MemoryPressureVoter::SetVote( | ||
base::MemoryPressureListener::MemoryPressureLevel level, | ||
bool notify_listeners) { | ||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | ||
auto old_vote = vote_; | ||
vote_ = level; | ||
aggregator_->OnVote(old_vote, vote_); | ||
if (notify_listeners) | ||
aggregator_->NotifyListeners(); | ||
} | ||
|
||
} // namespace util |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// 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 { | ||
|
||
// 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(); | ||
|
||
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 MemoryPressureVoter; | ||
|
||
// 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_; | ||
|
||
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; | ||
}; | ||
|
||
// Handles the forwarding of votes to the MemoryPressureVoteAggregator. Any | ||
// source which should have input on the overall MemoryPressureLevel will | ||
// calculate their vote on their own period, and use their Voter to inform the | ||
// Aggregator whenever their vote has changed or they want to trigger a | ||
// notification to the MemoryPressureListeners. This class is not thread safe | ||
// and should be used from a single sequence. | ||
class MemoryPressureVoter { | ||
public: | ||
// The aggregator should outlive this voter, and both should live on the same | ||
// sequence. | ||
explicit MemoryPressureVoter(MemoryPressureVoteAggregator* aggregator); | ||
~MemoryPressureVoter(); | ||
|
||
// Called to set a vote / change a vote. | ||
void SetVote(base::MemoryPressureListener::MemoryPressureLevel level, | ||
bool notify_listeners); | ||
|
||
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_); | ||
|
||
DISALLOW_COPY_AND_ASSIGN(MemoryPressureVoter); | ||
}; | ||
|
||
} // namespace util | ||
|
||
#endif // BASE_UTIL_MEMORY_PRESSURE_MEMORY_PRESSURE_VOTER_H_ |
119 changes: 119 additions & 0 deletions
119
base/util/memory_pressure/memory_pressure_voter_unittest.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
|
||
#include "base/util/memory_pressure/memory_pressure_voter.h" | ||
|
||
#include "base/bind.h" | ||
#include "base/macros.h" | ||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
namespace util { | ||
|
||
namespace { | ||
|
||
class TestDelegate : public util::MemoryPressureVoteAggregator::Delegate { | ||
private: | ||
void OnMemoryPressureLevelChanged( | ||
base::MemoryPressureListener::MemoryPressureLevel level) override {} | ||
void OnNotifyListenersRequested() override {} | ||
}; | ||
|
||
} // namespace | ||
|
||
TEST(MemoryPressureVoterTest, EvaluateVotes) { | ||
TestDelegate delegate; | ||
MemoryPressureVoteAggregator aggregator(&delegate); | ||
|
||
aggregator.SetVotesForTesting(1, 2, 3); | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | ||
|
||
aggregator.SetVotesForTesting(1, 20, 1); | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | ||
|
||
aggregator.SetVotesForTesting(0, 0, 0); | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); | ||
|
||
aggregator.SetVotesForTesting(0, 2, 0); | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); | ||
|
||
// Reset votes so destructor doesn't think there are loose voters. | ||
aggregator.SetVotesForTesting(0, 0, 0); | ||
} | ||
|
||
TEST(MemoryPressureVoterTest, OnVote) { | ||
TestDelegate delegate; | ||
MemoryPressureVoteAggregator aggregator(&delegate); | ||
|
||
// vote count = 0,0,0 | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); | ||
|
||
aggregator.OnVoteForTesting( | ||
base::nullopt, base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); | ||
// vote count = 1,0,0 | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); | ||
|
||
aggregator.OnVoteForTesting( | ||
base::nullopt, | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | ||
// vote count = 1,0,1 | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | ||
|
||
aggregator.OnVoteForTesting( | ||
base::nullopt, | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); | ||
// vote count = 1,1,1 | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | ||
|
||
aggregator.OnVoteForTesting( | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); | ||
// vote count = 1,2,0 | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); | ||
|
||
aggregator.OnVoteForTesting( | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, | ||
base::nullopt); | ||
// vote count = 1,1,0 | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); | ||
|
||
// Reset votes so destructor doesn't think there are loose voters. | ||
aggregator.SetVotesForTesting(0, 0, 0); | ||
} | ||
|
||
TEST(MemoryPressureVoterTest, SetVote) { | ||
TestDelegate delegate; | ||
MemoryPressureVoteAggregator aggregator(&delegate); | ||
auto voter_critical = std::make_unique<MemoryPressureVoter>(&aggregator); | ||
auto voter_moderate = std::make_unique<MemoryPressureVoter>(&aggregator); | ||
|
||
voter_critical->SetVote( | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL, false); | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | ||
|
||
voter_moderate->SetVote( | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE, false); | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL); | ||
|
||
voter_critical.reset(); | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE); | ||
|
||
voter_moderate.reset(); | ||
EXPECT_EQ(aggregator.EvaluateVotesForTesting(), | ||
base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE); | ||
} | ||
|
||
} // namespace util |
Oops, something went wrong.