Commit ce45a81e authored by Eric Seckler's avatar Eric Seckler Committed by Chromium LUCI CQ

power_scheduler: PowerMode voting & arbitration framework

Adds a power_scheduler component and the foundational framework to vote
on and determine a process-wide PowerMode, which will later be used as
input to the yet-to-be-implemented power scheduler.

We will instrument different Chromium components to provide inputs to
power mode detection. Each instrumentation site will instantiate a
PowerModeVoter, which votes on the process's power mode. The votes are
collected by a PowerModeArbiter, which decides the overall mode for the
process based on a prioritization mechanism.

Internal design doc: http://go/chromium-process-power-scheduler
(sorry, no public docs yet.)

Bug: 1166695
Change-Id: Idc70c8c5202d45ba44cddffa3fba9a5da41257fc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2628958Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Commit-Queue: Eric Seckler <eseckler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#844867}
parent caf14779
......@@ -125,6 +125,7 @@ test("components_unittests") {
"//components/payments/core:unit_tests",
"//components/policy/core/browser:unit_tests",
"//components/policy/core/common:unit_tests",
"//components/power_scheduler:unit_tests",
"//components/prefs:unit_tests",
"//components/previews/core:unit_tests",
"//components/proxy_config:unit_tests",
......
# Copyright 2021 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.
component("power_scheduler") {
sources = [
"power_mode.cc",
"power_mode.h",
"power_mode_arbiter.cc",
"power_mode_arbiter.h",
"power_mode_tracer.cc",
"power_mode_tracer.h",
"power_mode_voter.cc",
"power_mode_voter.h",
]
defines = [ "IS_POWER_SCHEDULER_IMPL" ]
deps = [ "//base" ]
}
source_set("unit_tests") {
testonly = true
sources = [ "power_mode_arbiter_unittest.cc" ]
deps = [
":power_scheduler",
"//base/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
}
monorail: {
component: "Internals>Power"
}
eseckler@chromium.org
khokhlov@google.com
oksamyt@chromium.org
skyostil@chromium.org
// Copyright 2021 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/power_scheduler/power_mode.h"
#include "base/trace_event/trace_event.h"
namespace power_scheduler {
const char* PowerModeToString(PowerMode mode) {
switch (mode) {
case PowerMode::kIdle:
return "Idle";
case PowerMode::kAudible:
return "Audible";
case PowerMode::kLoading:
return "Loading";
case PowerMode::kAnimation:
return "Animation";
case PowerMode::kResponse:
return "Response";
case PowerMode::kNonWebActivity:
return "NonWebActivity";
case PowerMode::kBackground:
return "Background";
case PowerMode::kCharging:
return "Charging";
}
}
} // namespace power_scheduler
// Copyright 2021 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 COMPONENTS_POWER_SCHEDULER_POWER_MODE_H_
#define COMPONENTS_POWER_SCHEDULER_POWER_MODE_H_
#include "base/component_export.h"
namespace power_scheduler {
// Power modes are "use cases" for the power scheduler. This enum is used both
// as a PowerModeVoter's input to PowerModeArbiter and as the arbiter's output,
// i.e. as the process's current power mode.
enum class PowerMode {
// Values in ascending priority order. See
// PowerModeArbiter::ComputeActiveModeLocked.
// Default mode: none of the other use cases were detected.
kIdle,
// The process is playing audio.
kAudible,
// A page or tab associated with the process is loading.
kLoading,
// A surface rendered by the process is animating.
kAnimation,
// The process is responding to user input.
kResponse,
// The (Android) app is showing an uninstrumented activity (e.g., Chromium's
// settings activity) for which we can't determine a more specific use case.
// Only valid in Chromium's browser process.
kNonWebActivity,
// All pages and tabs associated with the process are backgrounded, or the app
// itself is backgrounded.
kBackground,
// The device is connected to an external power source.
kCharging,
kMaxValue = kCharging,
};
COMPONENT_EXPORT(POWER_SCHEDULER) const char* PowerModeToString(PowerMode);
} // namespace power_scheduler
#endif // COMPONENTS_POWER_SCHEDULER_POWER_MODE_H_
// Copyright 2021 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/power_scheduler/power_mode_arbiter.h"
#include <map>
#include <memory>
#include "base/no_destructor.h"
#include "base/synchronization/lock.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/trace_log.h"
#include "components/power_scheduler/power_mode.h"
#include "components/power_scheduler/power_mode_tracer.h"
#include "components/power_scheduler/power_mode_voter.h"
namespace power_scheduler {
PowerModeArbiter::Observer::~Observer() = default;
// static
PowerModeArbiter* PowerModeArbiter::GetInstance() {
static base::NoDestructor<PowerModeArbiter> arbiter;
return arbiter.get();
}
PowerModeArbiter::PowerModeArbiter()
: observers_(new base::ObserverListThreadSafe<Observer>()),
active_mode_("PowerModeArbiter", this) {
base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this);
}
PowerModeArbiter::~PowerModeArbiter() {
base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this);
}
void PowerModeArbiter::OnThreadPoolAvailable() {
DCHECK(!task_runner_);
// Currently only used for the delayed votes.
task_runner_ = base::ThreadPool::CreateTaskRunner(
{base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
// Check if we need to post a task to update pending votes.
base::TimeTicks next_effective_time;
{
base::AutoLock lock(lock_);
for (const auto& entry : pending_resets_) {
base::TimeTicks effective_time = entry.second;
if (next_effective_time.is_null() ||
effective_time < next_effective_time) {
next_effective_time = effective_time;
}
}
next_pending_vote_update_time_ = next_effective_time;
}
if (!next_effective_time.is_null()) {
base::TimeTicks now = base::TimeTicks::Now();
if (next_effective_time < now)
next_effective_time = now;
task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&PowerModeArbiter::UpdatePendingResets,
base::Unretained(this)),
next_effective_time - now);
}
}
std::unique_ptr<PowerModeVoter> PowerModeArbiter::NewVoter(const char* name) {
std::unique_ptr<PowerModeVoter> voter(new PowerModeVoter(this));
{
base::AutoLock lock(lock_);
votes_.emplace(voter.get(), TracedPowerMode(name, voter.get()));
}
return voter;
}
void PowerModeArbiter::AddObserver(Observer* observer) {
base::AutoLock lock(lock_);
observer->OnPowerModeChanged(PowerMode::kIdle, active_mode_.mode());
observers_->AddObserver(observer);
}
void PowerModeArbiter::RemoveObserver(Observer* observer) {
observers_->RemoveObserver(observer);
}
void PowerModeArbiter::OnVoterDestroyed(PowerModeVoter* voter) {
{
base::AutoLock lock(lock_);
votes_.erase(voter);
pending_resets_.erase(voter);
}
OnVotesUpdated();
}
void PowerModeArbiter::SetVote(PowerModeVoter* voter, PowerMode mode) {
bool did_change = false;
{
base::AutoLock lock(lock_);
auto it = votes_.find(voter);
DCHECK(it != votes_.end());
TracedPowerMode& voter_mode = it->second;
did_change |= voter_mode.mode() != mode;
voter_mode.SetMode(mode);
pending_resets_.erase(voter);
}
if (did_change)
OnVotesUpdated();
}
void PowerModeArbiter::ResetVoteAfterTimeout(PowerModeVoter* voter,
base::TimeDelta timeout) {
bool should_post_update_task = false;
{
base::AutoLock lock(lock_);
base::TimeTicks scheduled_time = base::TimeTicks::Now() + timeout;
pending_resets_[voter] = scheduled_time;
// Only post a new task if there isn't one scheduled to run earlier yet.
// This reduces the number of posted callbacks in situations where the
// pending vote is cleared soon after UpdateVoteAfterTimeout() by SetVote().
if (task_runner_ && (next_pending_vote_update_time_.is_null() ||
scheduled_time < next_pending_vote_update_time_)) {
next_pending_vote_update_time_ = scheduled_time;
should_post_update_task = true;
}
}
if (should_post_update_task) {
task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&PowerModeArbiter::UpdatePendingResets,
base::Unretained(this)),
timeout);
}
}
void PowerModeArbiter::UpdatePendingResets() {
// Note: This method may run at any point. Do not assume that there are any
// resets that have expired, or that any other UpdatePendingResets() task is
// scheduled.
bool did_change = false;
base::TimeTicks now = base::TimeTicks::Now();
base::TimeTicks next_task_time;
{
base::AutoLock lock(lock_);
for (auto it = pending_resets_.begin(); it != pending_resets_.end();) {
base::TimeTicks task_time = it->second;
if (task_time <= now) {
PowerModeVoter* voter = it->first;
auto votes_it = votes_.find(voter);
DCHECK(votes_it != votes_.end());
TracedPowerMode& voter_mode = votes_it->second;
did_change |= voter_mode.mode() != PowerMode::kIdle;
voter_mode.SetMode(PowerMode::kIdle);
it = pending_resets_.erase(it);
} else {
if (next_task_time.is_null() || task_time < next_task_time)
next_task_time = task_time;
++it;
}
}
next_pending_vote_update_time_ = next_task_time;
}
if (!next_task_time.is_null()) {
task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&PowerModeArbiter::UpdatePendingResets,
base::Unretained(this)),
next_task_time - now);
}
if (did_change)
OnVotesUpdated();
}
void PowerModeArbiter::OnVotesUpdated() {
base::AutoLock lock(lock_);
PowerMode old_mode = active_mode_.mode();
PowerMode new_mode = ComputeActiveModeLocked();
active_mode_.SetMode(new_mode);
if (old_mode == new_mode)
return;
// Notify while holding |lock| to avoid out-of-order observer updates.
observers_->Notify(FROM_HERE, &Observer::OnPowerModeChanged, old_mode,
new_mode);
}
PowerMode PowerModeArbiter::ComputeActiveModeLocked() {
PowerMode mode = PowerMode::kIdle;
bool is_audible = false;
for (const auto& voter_and_vote : votes_) {
PowerMode vote = voter_and_vote.second.mode();
if (vote > mode)
mode = vote;
if (vote == PowerMode::kAudible)
is_audible = true;
}
// In background, audible overrides.
if (mode == PowerMode::kBackground && is_audible)
return PowerMode::kAudible;
return mode;
}
PowerMode PowerModeArbiter::GetActiveModeForTesting() {
base::AutoLock lock(lock_);
return active_mode_.mode();
}
void PowerModeArbiter::OnTraceLogEnabled() {
base::AutoLock lock(lock_);
for (const auto& voter_and_vote : votes_)
voter_and_vote.second.OnTraceLogEnabled();
active_mode_.OnTraceLogEnabled();
}
void PowerModeArbiter::OnTraceLogDisabled() {}
} // namespace power_scheduler
// Copyright 2021 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 COMPONENTS_POWER_SCHEDULER_POWER_MODE_ARBITER_H_
#define COMPONENTS_POWER_SCHEDULER_POWER_MODE_ARBITER_H_
#include <map>
#include <memory>
#include "base/component_export.h"
#include "base/memory/scoped_refptr.h"
#include "base/no_destructor.h"
#include "base/observer_list_threadsafe.h"
#include "base/synchronization/lock.h"
#include "base/task_runner.h"
#include "base/thread_annotations.h"
#include "base/time/time.h"
#include "base/trace_event/trace_log.h"
#include "components/power_scheduler/power_mode.h"
#include "components/power_scheduler/power_mode_tracer.h"
#include "components/power_scheduler/power_mode_voter.h"
namespace power_scheduler {
// Decides the active PowerMode for a process. To do this, it collects votes
// from various instrumentation points in Chromium.
//
// Each instrumentation point can instantiate a PowerModeVoter and submit votes:
// auto voter = PowerModeArbiter::GetInstance()->NewVoter("MyVoter");
// voter->VoteFor(PowerMode::kCharging);
//
// The active PowerMode is decided via a prioritization mechanism, see
// ComputeActiveModeLocked().
class COMPONENT_EXPORT(POWER_SCHEDULER) PowerModeArbiter
: public base::trace_event::TraceLog::EnabledStateObserver,
public PowerModeVoter::Delegate {
public:
class COMPONENT_EXPORT(POWER_SCHEDULER) Observer {
public:
virtual ~Observer();
virtual void OnPowerModeChanged(PowerMode old_mode, PowerMode new_mode) = 0;
};
static PowerModeArbiter* GetInstance();
// Public for testing.
PowerModeArbiter();
~PowerModeArbiter() override;
PowerModeArbiter(const PowerModeArbiter&) = delete;
PowerModeArbiter& operator=(const PowerModeArbiter&) = delete;
// Instantiates a new Voter for an instrumentation site. |name| must be a
// static string and is used for tracing instrumentation.
std::unique_ptr<PowerModeVoter> NewVoter(const char* name);
// Adds/removes an observer that is notified for every change of the
// process-wide mode. The observer is notified on the task sequence it
// registered on.
void AddObserver(Observer*);
void RemoveObserver(Observer*);
// Should be called by the embedder during process startup, once the thread
// pool is available.
void OnThreadPoolAvailable();
// Returns the currently active PowerMode. Public for testing.
PowerMode GetActiveModeForTesting();
private:
// PowerModeVoter::Delegate implementation:
void OnVoterDestroyed(PowerModeVoter*) override;
void SetVote(PowerModeVoter*, PowerMode) override;
void ResetVoteAfterTimeout(PowerModeVoter*, base::TimeDelta timeout) override;
void UpdatePendingResets();
void OnVotesUpdated();
PowerMode ComputeActiveModeLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_);
// trace_event::TraceLog::EnabledStateObserver implementation:
void OnTraceLogEnabled() override;
void OnTraceLogDisabled() override;
scoped_refptr<base::TaskRunner> task_runner_;
scoped_refptr<base::ObserverListThreadSafe<Observer>> observers_;
base::Lock lock_;
std::map<PowerModeVoter*, TracedPowerMode> votes_ GUARDED_BY(lock_);
std::map<PowerModeVoter*, base::TimeTicks /*effective_time*/> pending_resets_
GUARDED_BY(lock_);
base::TimeTicks next_pending_vote_update_time_ GUARDED_BY(lock_);
TracedPowerMode active_mode_ GUARDED_BY(lock_);
};
} // namespace power_scheduler
#endif // COMPONENTS_POWER_SCHEDULER_POWER_MODE_ARBITER_H_
// Copyright 2021 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/power_scheduler/power_mode_arbiter.h"
#include "base/test/task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace power_scheduler {
TEST(PowerModeArbiterTest, SingleVote) {
PowerModeArbiter arbiter;
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
std::unique_ptr<PowerModeVoter> voter1 = arbiter.NewVoter("voter1");
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
for (PowerMode mode = PowerMode::kIdle; mode <= PowerMode::kMaxValue;) {
mode = static_cast<PowerMode>(static_cast<int>(mode) + 1);
voter1->VoteFor(mode);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), mode);
}
voter1.reset();
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
}
TEST(PowerModeArbiterTest, MultipleVotes) {
PowerModeArbiter arbiter;
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
std::unique_ptr<PowerModeVoter> voter1 = arbiter.NewVoter("voter1");
std::unique_ptr<PowerModeVoter> voter2 = arbiter.NewVoter("voter2");
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
auto vote_and_expect = [&](PowerMode vote1, PowerMode vote2,
PowerMode expected) {
voter1->VoteFor(vote1);
voter2->VoteFor(vote2);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), expected)
<< "vote1: " << PowerModeToString(vote1)
<< ", vote2: " << PowerModeToString(vote2)
<< ", expected: " << PowerModeToString(expected)
<< ", actual: " << PowerModeToString(arbiter.GetActiveModeForTesting());
};
// Two votes for the same mode result in that mode.
for (PowerMode mode = PowerMode::kIdle; mode <= PowerMode::kMaxValue;) {
vote_and_expect(mode, mode, mode);
mode = static_cast<PowerMode>(static_cast<int>(mode) + 1);
}
// Charging trumps anything.
vote_and_expect(PowerMode::kCharging, PowerMode::kIdle, PowerMode::kCharging);
vote_and_expect(PowerMode::kCharging, PowerMode::kAudible,
PowerMode::kCharging);
vote_and_expect(PowerMode::kCharging, PowerMode::kLoading,
PowerMode::kCharging);
vote_and_expect(PowerMode::kCharging, PowerMode::kAnimation,
PowerMode::kCharging);
vote_and_expect(PowerMode::kCharging, PowerMode::kResponse,
PowerMode::kCharging);
vote_and_expect(PowerMode::kCharging, PowerMode::kBackground,
PowerMode::kCharging);
// Background trumps remaining modes, but not audible.
vote_and_expect(PowerMode::kBackground, PowerMode::kIdle,
PowerMode::kBackground);
vote_and_expect(PowerMode::kBackground, PowerMode::kAudible,
PowerMode::kAudible);
vote_and_expect(PowerMode::kBackground, PowerMode::kLoading,
PowerMode::kBackground);
vote_and_expect(PowerMode::kBackground, PowerMode::kAnimation,
PowerMode::kBackground);
vote_and_expect(PowerMode::kBackground, PowerMode::kResponse,
PowerMode::kBackground);
// Response trumps remaining modes.
vote_and_expect(PowerMode::kResponse, PowerMode::kIdle, PowerMode::kResponse);
vote_and_expect(PowerMode::kResponse, PowerMode::kAudible,
PowerMode::kResponse);
vote_and_expect(PowerMode::kResponse, PowerMode::kLoading,
PowerMode::kResponse);
vote_and_expect(PowerMode::kResponse, PowerMode::kAnimation,
PowerMode::kResponse);
// Animation trumps remaining modes.
vote_and_expect(PowerMode::kAnimation, PowerMode::kIdle,
PowerMode::kAnimation);
vote_and_expect(PowerMode::kAnimation, PowerMode::kAudible,
PowerMode::kAnimation);
vote_and_expect(PowerMode::kAnimation, PowerMode::kLoading,
PowerMode::kAnimation);
// Loading trumps remaining modes.
vote_and_expect(PowerMode::kLoading, PowerMode::kIdle, PowerMode::kLoading);
vote_and_expect(PowerMode::kLoading, PowerMode::kAudible,
PowerMode::kLoading);
// Audible trumps idle.
vote_and_expect(PowerMode::kAudible, PowerMode::kIdle, PowerMode::kAudible);
}
namespace {
class MockObserver : public PowerModeArbiter::Observer {
public:
~MockObserver() override = default;
MOCK_METHOD(void,
OnPowerModeChanged,
(PowerMode old_mode, PowerMode new_mode),
(override));
};
} // namespace
TEST(PowerModeArbiterTest, Observer) {
base::test::TaskEnvironment env;
PowerModeArbiter arbiter;
MockObserver observer;
// Observer is notified of initial mode right away.
EXPECT_CALL(observer, OnPowerModeChanged(PowerMode::kIdle, PowerMode::kIdle));
arbiter.AddObserver(&observer);
testing::Mock::VerifyAndClearExpectations(&observer);
std::unique_ptr<PowerModeVoter> voter1 = arbiter.NewVoter("voter1");
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
EXPECT_CALL(observer,
OnPowerModeChanged(PowerMode::kIdle, PowerMode::kAnimation));
voter1->VoteFor(PowerMode::kAnimation);
env.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&observer);
EXPECT_CALL(observer,
OnPowerModeChanged(PowerMode::kAnimation, PowerMode::kIdle));
voter1.reset();
env.RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(&observer);
arbiter.RemoveObserver(&observer);
}
TEST(PowerModeArbiterTest, ResetVoteAfterTimeout) {
base::test::TaskEnvironment env(
base::test::TaskEnvironment::TimeSource::MOCK_TIME);
PowerModeArbiter arbiter;
base::TimeDelta delta1s = base::TimeDelta::FromSeconds(1);
base::TimeDelta delta2s = base::TimeDelta::FromSeconds(2);
std::unique_ptr<PowerModeVoter> voter1 = arbiter.NewVoter("voter1");
voter1->VoteFor(PowerMode::kAnimation);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
// Reset requests before the thread pool is available are queued and executed
// on thread pool becoming available.
voter1->ResetVoteAfterTimeout(delta1s);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
env.FastForwardBy(delta1s); // Advance the time before the task is queued.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
arbiter.OnThreadPoolAvailable();
env.RunUntilIdle(); // Execute the (non-delayed) reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
// If VoteFor() is not called before the task executes, the mode is reset.
voter1->VoteFor(PowerMode::kAnimation);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
voter1->ResetVoteAfterTimeout(delta1s);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
env.FastForwardBy(delta1s); // Execute the reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
// If the VoteFor() is called before the task executes, the mode is not reset.
voter1->VoteFor(PowerMode::kAnimation);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
voter1->ResetVoteAfterTimeout(delta1s);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
voter1->VoteFor(PowerMode::kAnimation);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
env.FastForwardBy(delta1s); // Execute the reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
// Handles multiple pending resets.
voter1->VoteFor(PowerMode::kAnimation);
std::unique_ptr<PowerModeVoter> voter2 = arbiter.NewVoter("voter2");
voter2->VoteFor(PowerMode::kCharging);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
voter2->ResetVoteAfterTimeout(delta1s);
voter1->ResetVoteAfterTimeout(delta2s);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
env.FastForwardBy(delta1s); // Execute the first reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
env.FastForwardBy(delta1s); // Execute the second reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
// Same thing, with reset requests scheduled in reverse order.
voter1->VoteFor(PowerMode::kAnimation);
voter2->VoteFor(PowerMode::kCharging);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
voter1->ResetVoteAfterTimeout(delta2s);
voter2->ResetVoteAfterTimeout(delta1s);
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
env.FastForwardBy(delta1s); // Execute the first reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kAnimation);
env.FastForwardBy(delta1s); // Execute the second reset task.
EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
// If the voter is destroyed, the task doesn't cause crashes.
voter1->VoteFor(PowerMode::kAnimation);
voter1->ResetVoteAfterTimeout(delta1s);
voter1.reset();
env.FastForwardBy(delta1s); // Execute the reset task.
}
} // namespace power_scheduler
\ No newline at end of file
// Copyright 2021 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/power_scheduler/power_mode_tracer.h"
#include "base/trace_event/trace_event.h"
#include "components/power_scheduler/power_mode.h"
namespace power_scheduler {
TracedPowerMode::TracedPowerMode(const char* name, const void* trace_id)
: name_(name), trace_id_(trace_id), mode_(PowerMode::kIdle) {
OnTraceLogEnabled(); // In case it's already enabled.
}
TracedPowerMode::~TracedPowerMode() {
TRACE_EVENT_NESTABLE_ASYNC_END0("power", PowerModeToString(mode_), trace_id_);
TRACE_EVENT_NESTABLE_ASYNC_END0("power", name_, trace_id_);
}
void TracedPowerMode::OnTraceLogEnabled() const {
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("power", name_, trace_id_);
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("power", PowerModeToString(mode_),
trace_id_);
}
void TracedPowerMode::SetMode(PowerMode mode) {
if (mode_ == mode)
return;
TRACE_EVENT_NESTABLE_ASYNC_END0("power", PowerModeToString(mode_), trace_id_);
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("power", PowerModeToString(mode),
trace_id_);
mode_ = mode;
}
} // namespace power_scheduler
// Copyright 2021 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 COMPONENTS_POWER_SCHEDULER_POWER_MODE_TRACER_H_
#define COMPONENTS_POWER_SCHEDULER_POWER_MODE_TRACER_H_
#include <atomic>
#include "base/trace_event/trace_event.h"
#include "components/power_scheduler/power_mode.h"
namespace power_scheduler {
// Keeps track of a PowerMode and traces its value transitions. Not thread-safe.
class TracedPowerMode {
public:
explicit TracedPowerMode(const char* name, const void* trace_id);
~TracedPowerMode();
void OnTraceLogEnabled() const;
void SetMode(PowerMode);
PowerMode mode() const { return mode_; }
private:
const char* name_;
const void* trace_id_;
PowerMode mode_;
};
} // namespace power_scheduler
#endif // COMPONENTS_POWER_SCHEDULER_POWER_MODE_TRACER_H_
// Copyright 2021 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/power_scheduler/power_mode_voter.h"
namespace power_scheduler {
PowerModeVoter::Delegate::~Delegate() = default;
PowerModeVoter::PowerModeVoter(Delegate* delegate) : delegate_(delegate) {}
PowerModeVoter::~PowerModeVoter() {
delegate_->OnVoterDestroyed(this);
}
void PowerModeVoter::VoteFor(PowerMode mode) {
delegate_->SetVote(this, mode);
}
void PowerModeVoter::ResetVoteAfterTimeout(base::TimeDelta timeout) {
delegate_->ResetVoteAfterTimeout(this, timeout);
}
} // namespace power_scheduler
// Copyright 2021 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 COMPONENTS_POWER_SCHEDULER_POWER_MODE_VOTER_H_
#define COMPONENTS_POWER_SCHEDULER_POWER_MODE_VOTER_H_
#include "base/component_export.h"
#include "base/time/time.h"
#include "components/power_scheduler/power_mode.h"
namespace power_scheduler {
class PowerModeArbiter;
// PowerModeVoters should be instantiated at instrumentation points in Chromium
// via PowerModeArbiter::GetInstance()->NewVoter("MyVoter") to vote on a
// process's PowerMode.
class COMPONENT_EXPORT(POWER_SCHEDULER) PowerModeVoter {
public:
class Delegate {
public:
virtual ~Delegate();
virtual void OnVoterDestroyed(PowerModeVoter*) = 0;
virtual void SetVote(PowerModeVoter*, PowerMode) = 0;
virtual void ResetVoteAfterTimeout(PowerModeVoter*,
base::TimeDelta timeout) = 0;
};
~PowerModeVoter();
PowerModeVoter(const PowerModeVoter&) = delete;
PowerModeVoter& operator=(const PowerModeVoter&) = delete;
// Set the voter's vote to the provided PowerMode. Cancels any previously
// scheduled ResetVoteAfterTimeout().
void VoteFor(PowerMode);
// Resets the vote to the PowerMode::kIdle after |timeout|, provided VoteFor()
// is not called again before then.
void ResetVoteAfterTimeout(base::TimeDelta timeout);
private:
friend class PowerModeArbiter;
explicit PowerModeVoter(Delegate* delegate);
Delegate* delegate_;
};
} // namespace power_scheduler
#endif // COMPONENTS_POWER_SCHEDULER_POWER_MODE_VOTER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment