blob: 90f49177a6f3c63716486cf721709ec37f7f88e8 [file] [log] [blame]
// 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 "components/performance_manager/public/frame_priority/max_vote_aggregator.h"
#include <algorithm>
#include <tuple>
namespace performance_manager {
namespace frame_priority {
MaxVoteAggregator::MaxVoteAggregator() : factory_(this) {}
MaxVoteAggregator::~MaxVoteAggregator() = default;
VotingChannel MaxVoteAggregator::GetVotingChannel() {
return factory_.BuildVotingChannel();
void MaxVoteAggregator::SetUpstreamVotingChannel(VotingChannel&& channel) {
channel_ = std::move(channel);
VoteReceipt MaxVoteAggregator::SubmitVote(VoterId voter_id, const Vote& vote) {
// NOTE: We don't currently explicitly worry about having multiple votes for
// the same frame from a single voter, although such logic could be added.
// Add the new vote.
VoteData& vote_data = vote_data_map_[vote.frame_node()];
auto accepted_vote = AcceptedVote(this, voter_id, vote);
auto receipt = accepted_vote.IssueReceipt();
if (vote_data.AddVote(std::move(accepted_vote), next_vote_id_++))
// Finally, return a vote receipt to our voter for the received vote.
return receipt;
VoteReceipt MaxVoteAggregator::ChangeVote(VoteReceipt receipt,
AcceptedVote* old_vote,
const Vote& new_vote) {
VoteData& vote_data = GetVoteData(old_vote)->second;
size_t index = vote_data.GetVoteIndex(old_vote);
// Update the vote directly, then repair the heap.
if (vote_data.UpdateVote(index, next_vote_id_++))
// Return the same receipt back to the user.
return receipt;
void MaxVoteAggregator::VoteInvalidated(AcceptedVote* vote) {
auto it = GetVoteData(vote);
VoteData& vote_data = it->second;
size_t index = vote_data.GetVoteIndex(vote);
// Remove the vote, and upstream if necessary.
if (vote_data.RemoveVote(index))
// If all the votes for this frame have disappeared then remove the entry
// entirely. This will automatically cancel our upstream vote.
if (vote_data.IsEmpty())
MaxVoteAggregator::VoteDataMap::iterator MaxVoteAggregator::GetVoteData(
AcceptedVote* vote) {
// The vote being retrieved should have us as its consumer, and we should
// already have been setup to receive votes before this is called.
DCHECK_EQ(this, vote->consumer());
// Find the votes associated with this frame.
auto it = vote_data_map_.find(vote->vote().frame_node());
DCHECK(it != vote_data_map_.end());
return it;
MaxVoteAggregator::VoteData::VoteData() = default;
MaxVoteAggregator::VoteData::VoteData(VoteData&& rhs) = default;
MaxVoteAggregator::VoteData& MaxVoteAggregator::VoteData::operator=(
VoteData&& rhs) = default;
MaxVoteAggregator::VoteData::~VoteData() = default;
bool MaxVoteAggregator::VoteData::AddVote(AcceptedVote&& vote,
uint32_t vote_id) {
votes_.emplace(std::move(vote), vote_id);
return true;
bool MaxVoteAggregator::VoteData::UpdateVote(size_t index, uint32_t vote_id) {
DCHECK_LE(0u, index);
DCHECK_LT(index, votes_.size());
// Remember the upstream vote as it may change.
const Vote old_root =;
// The AcceptedVote has actually already been changed in place. Remove the
// vote, finish the update, and reinsert it into the heap.
// The vote always needs to be upstreamed if the changed vote was at the root.
// Otherwise, we only need to upstream if the root vote was observed to change
// as part of the heap repair.
const Vote& new_root =;
return index == 0 || old_root != new_root;
bool MaxVoteAggregator::VoteData::RemoveVote(size_t index) {
DCHECK_LE(0u, index);
DCHECK_LT(index, votes_.size());
// A new upstream vote needs to be made if the root was disturbed.
return votes_.size() && index == 0;
size_t MaxVoteAggregator::VoteData::GetVoteIndex(AcceptedVote* vote) {
static_assert(offsetof(StampedVote, vote) == 0,
"AcceptedVote is expected to be at offset 0 of StampedVote");
StampedVote* stamped_vote = reinterpret_cast<StampedVote*>(vote);
DCHECK_NE(0u, votes_.size());
DCHECK_LE(, stamped_vote);
DCHECK_LT(stamped_vote, + votes_.size());
return stamped_vote -;
void MaxVoteAggregator::VoteData::UpstreamVote(VotingChannel* channel) {
DCHECK_NE(0u, votes_.size());
const Vote& vote =;
// Change our existing vote, or create a new one as necessary.
if (receipt_.HasVote()) {
receipt_.ChangeVote(vote.priority(), vote.reason());
} else {
receipt_ = channel->SubmitVote(vote);
} // namespace frame_priority
} // namespace performance_manager