Commit 8e7026d2 authored by Etienne Bergeron's avatar Etienne Bergeron Committed by Commit Bot

[2/4] Add ThreadControllerPowerMonitor to manage power states

This CL is introducing a helper class ThreadControllerPowerMonitor
that assist ThreadController for tasks scheduling.

The use of ThreadControllerPowerMonitor is optional and the
power state is assume to not be suspended when unused.
The end goal is to activate it by default after testing on stable
channel.

TBR=nasko@chromium.org

Bug: 1074332
Change-Id: I40c3580ccba0b4755efed19f264e4634e3f13770
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2200421Reviewed-by: default avatarEtienne Bergeron <etienneb@chromium.org>
Commit-Queue: Etienne Bergeron <etienneb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#771619}
parent ad57ee53
...@@ -637,6 +637,8 @@ jumbo_component("base") { ...@@ -637,6 +637,8 @@ jumbo_component("base") {
"task/sequence_manager/thread_controller.h", "task/sequence_manager/thread_controller.h",
"task/sequence_manager/thread_controller_impl.cc", "task/sequence_manager/thread_controller_impl.cc",
"task/sequence_manager/thread_controller_impl.h", "task/sequence_manager/thread_controller_impl.h",
"task/sequence_manager/thread_controller_power_monitor.cc",
"task/sequence_manager/thread_controller_power_monitor.h",
"task/sequence_manager/thread_controller_with_message_pump_impl.cc", "task/sequence_manager/thread_controller_with_message_pump_impl.cc",
"task/sequence_manager/thread_controller_with_message_pump_impl.h", "task/sequence_manager/thread_controller_with_message_pump_impl.h",
"task/sequence_manager/time_domain.cc", "task/sequence_manager/time_domain.cc",
...@@ -2725,6 +2727,7 @@ test("base_unittests") { ...@@ -2725,6 +2727,7 @@ test("base_unittests") {
"task/sequence_manager/task_queue_selector_unittest.cc", "task/sequence_manager/task_queue_selector_unittest.cc",
"task/sequence_manager/task_queue_unittest.cc", "task/sequence_manager/task_queue_unittest.cc",
"task/sequence_manager/test/mock_time_message_pump_unittest.cc", "task/sequence_manager/test/mock_time_message_pump_unittest.cc",
"task/sequence_manager/thread_controller_power_monitor_unittest.cc",
"task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc", "task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc",
"task/sequence_manager/time_domain_unittest.cc", "task/sequence_manager/time_domain_unittest.cc",
"task/sequence_manager/work_deduplicator_unittest.cc", "task/sequence_manager/work_deduplicator_unittest.cc",
......
// 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/task/sequence_manager/thread_controller_power_monitor.h"
#include "base/feature_list.h"
#include "base/power_monitor/power_monitor.h"
#include "base/trace_event/trace_event.h"
namespace base {
namespace sequence_manager {
namespace internal {
namespace {
// Activate the power management events that affect task scheduling.
const Feature kUsePowerMonitorWithThreadController{
"UsePowerMonitorWithThreadController", FEATURE_DISABLED_BY_DEFAULT};
// TODO(1074332): Remove this when the experiment becomes the default.
bool g_use_thread_controller_power_monitor_ = false;
} // namespace
ThreadControllerPowerMonitor::ThreadControllerPowerMonitor() = default;
ThreadControllerPowerMonitor::~ThreadControllerPowerMonitor() {
PowerMonitor::RemoveObserver(this);
}
void ThreadControllerPowerMonitor::BindToCurrentThread() {
#if DCHECK_IS_ON()
DCHECK(!is_observer_registered_);
is_observer_registered_ = true;
#endif
// Register the observer to deliver notifications on the current thread.
PowerMonitor::AddObserver(this);
}
bool ThreadControllerPowerMonitor::IsProcessInPowerSuspendState() {
return is_power_suspended_;
}
// static
void ThreadControllerPowerMonitor::InitializeOnMainThread() {
DCHECK(!g_use_thread_controller_power_monitor_);
g_use_thread_controller_power_monitor_ =
FeatureList::IsEnabled(kUsePowerMonitorWithThreadController);
}
// static
void ThreadControllerPowerMonitor::OverrideUsePowerMonitorForTesting(
bool use_power_monitor) {
g_use_thread_controller_power_monitor_ = use_power_monitor;
}
// static
void ThreadControllerPowerMonitor::ResetForTesting() {
g_use_thread_controller_power_monitor_ = false;
}
void ThreadControllerPowerMonitor::OnSuspend() {
if (!g_use_thread_controller_power_monitor_)
return;
DCHECK(!is_power_suspended_);
TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("base", "ThreadController::Suspended",
this);
is_power_suspended_ = true;
}
void ThreadControllerPowerMonitor::OnResume() {
if (!g_use_thread_controller_power_monitor_)
return;
// It is possible a suspend was already happening before the observer was
// added to the power monitor. Ignoring the resume notification in that case.
if (is_power_suspended_) {
TRACE_EVENT_NESTABLE_ASYNC_END0("base", "ThreadController::Suspended",
this);
is_power_suspended_ = false;
}
}
} // namespace internal
} // namespace sequence_manager
} // namespace base
// 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_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_POWER_MONITOR_H_
#define BASE_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_POWER_MONITOR_H_
#include "base/check.h"
#include "base/macros.h"
#include "base/power_monitor/power_observer.h"
namespace base {
namespace sequence_manager {
namespace internal {
// A helper class that keeps track of the power state and handles power
// notifications. The class register itself to the PowerMonitor and receives
// notifications on the bound thread (see BindToCurrentThread(...)).
class BASE_EXPORT ThreadControllerPowerMonitor : public PowerObserver {
public:
ThreadControllerPowerMonitor();
~ThreadControllerPowerMonitor() override;
ThreadControllerPowerMonitor(const ThreadControllerPowerMonitor&) = delete;
ThreadControllerPowerMonitor& operator=(const ThreadControllerPowerMonitor&) =
delete;
// Register this class to the power monitor to receive notifications on this
// thread. It is safe to call this before PowerMonitor is initialized.
void BindToCurrentThread();
// Returns whether the process is between power suspend and resume
// notifications.
bool IsProcessInPowerSuspendState();
// Initialize the ThreadControllerPowerMonitor. Must be called once on the
// main thread during startup while single-threaded.
static void InitializeOnMainThread();
static void OverrideUsePowerMonitorForTesting(bool use_power_monitor);
static void ResetForTesting();
// base::PowerObserver:
void OnSuspend() override;
void OnResume() override;
private:
// Power state based on notifications delivered to this observer.
bool is_power_suspended_ = false;
#if DCHECK_IS_ON()
// Whether PowerMonitor observer is registered.
bool is_observer_registered_ = false;
#endif
};
} // namespace internal
} // namespace sequence_manager
} // namespace base
#endif // BASE_TASK_SEQUENCE_MANAGER_THREAD_CONTROLLER_POWER_MONITOR_H_
// 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/task/sequence_manager/thread_controller_power_monitor.h"
#include "base/power_monitor/power_monitor.h"
#include "base/power_monitor/power_monitor_source.h"
#include "base/test/power_monitor_test_base.h"
#include "base/test/task_environment.h"
#include "base/test/mock_callback.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace sequence_manager {
namespace internal {
class ThreadControllerPowerMonitorTest : public testing::Test {
public:
void SetUp() override {
power_monitor_source_ = new PowerMonitorTestSource();
PowerMonitor::Initialize(
std::unique_ptr<PowerMonitorSource>(power_monitor_source_));
thread_controller_power_monitor_ =
std::make_unique<ThreadControllerPowerMonitor>();
internal::ThreadControllerPowerMonitor::OverrideUsePowerMonitorForTesting(
true);
}
void TearDown() override {
thread_controller_power_monitor_.reset();
internal::ThreadControllerPowerMonitor::ResetForTesting();
PowerMonitor::ShutdownForTesting();
}
protected:
base::test::SingleThreadTaskEnvironment task_environment_;
PowerMonitorTestSource* power_monitor_source_ = nullptr;
std::unique_ptr<ThreadControllerPowerMonitor>
thread_controller_power_monitor_;
};
TEST_F(ThreadControllerPowerMonitorTest, IsProcessInPowerSuspendState) {
EXPECT_FALSE(
thread_controller_power_monitor_->IsProcessInPowerSuspendState());
// Before the monitor is bound to the thread, the notifications are not
// received.
power_monitor_source_->GenerateSuspendEvent();
EXPECT_FALSE(
thread_controller_power_monitor_->IsProcessInPowerSuspendState());
power_monitor_source_->GenerateResumeEvent();
EXPECT_FALSE(
thread_controller_power_monitor_->IsProcessInPowerSuspendState());
thread_controller_power_monitor_->BindToCurrentThread();
// Ensures notifications are processed.
power_monitor_source_->GenerateSuspendEvent();
EXPECT_TRUE(thread_controller_power_monitor_->IsProcessInPowerSuspendState());
power_monitor_source_->GenerateResumeEvent();
EXPECT_FALSE(
thread_controller_power_monitor_->IsProcessInPowerSuspendState());
}
} // namespace internal
} // namespace sequence_manager
} // namespace base
...@@ -5,10 +5,8 @@ ...@@ -5,10 +5,8 @@
#include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h" #include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h"
#include "base/auto_reset.h" #include "base/auto_reset.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/message_loop/message_pump.h" #include "base/message_loop/message_pump.h"
#include "base/power_monitor/power_monitor.h"
#include "base/threading/hang_watcher.h" #include "base/threading/hang_watcher.h"
#include "base/time/tick_clock.h" #include "base/time/tick_clock.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
...@@ -25,12 +23,6 @@ namespace sequence_manager { ...@@ -25,12 +23,6 @@ namespace sequence_manager {
namespace internal { namespace internal {
namespace { namespace {
// Activate the power management events that affect the tasks scheduling.
const Feature kUsePowerMonitorWithThreadController{
"UsePowerMonitorWithThreadController", FEATURE_DISABLED_BY_DEFAULT};
bool g_use_power_monitor_with_thread_controller = false;
// Returns |next_run_time| capped at 1 day from |lazy_now|. This is used to // Returns |next_run_time| capped at 1 day from |lazy_now|. This is used to
// mitigate https://crbug.com/850450 where some platforms are unhappy with // mitigate https://crbug.com/850450 where some platforms are unhappy with
// delays > 100,000,000 seconds. In practice, a diagnosis metric showed that no // delays > 100,000,000 seconds. In practice, a diagnosis metric showed that no
...@@ -105,6 +97,7 @@ void ThreadControllerWithMessagePumpImpl::BindToCurrentThread( ...@@ -105,6 +97,7 @@ void ThreadControllerWithMessagePumpImpl::BindToCurrentThread(
ShouldScheduleWork::kScheduleImmediate) { ShouldScheduleWork::kScheduleImmediate) {
pump_->ScheduleWork(); pump_->ScheduleWork();
} }
power_monitor_.BindToCurrentThread();
} }
void ThreadControllerWithMessagePumpImpl::SetWorkBatchSize( void ThreadControllerWithMessagePumpImpl::SetWorkBatchSize(
...@@ -368,8 +361,7 @@ bool ThreadControllerWithMessagePumpImpl::DoIdleWork() { ...@@ -368,8 +361,7 @@ bool ThreadControllerWithMessagePumpImpl::DoIdleWork() {
work_id_provider_->IncrementWorkId(); work_id_provider_->IncrementWorkId();
#if defined(OS_WIN) #if defined(OS_WIN)
if (!g_use_power_monitor_with_thread_controller || if (!power_monitor_.IsProcessInPowerSuspendState()) {
!base::PowerMonitor::IsProcessSuspended()) {
// Avoid calling Time::ActivateHighResolutionTimer() between // Avoid calling Time::ActivateHighResolutionTimer() between
// suspend/resume as the system hangs if we do (crbug.com/1074028). // suspend/resume as the system hangs if we do (crbug.com/1074028).
// OnResume() will generate a task on this thread per the // OnResume() will generate a task on this thread per the
...@@ -532,11 +524,5 @@ bool ThreadControllerWithMessagePumpImpl::ShouldQuitRunLoopWhenIdle() { ...@@ -532,11 +524,5 @@ bool ThreadControllerWithMessagePumpImpl::ShouldQuitRunLoopWhenIdle() {
} }
} // namespace internal } // namespace internal
void PostFieldTrialInitialization() {
internal::g_use_power_monitor_with_thread_controller =
FeatureList::IsEnabled(internal::kUsePowerMonitorWithThreadController);
}
} // namespace sequence_manager } // namespace sequence_manager
} // namespace base } // namespace base
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "base/task/sequence_manager/sequence_manager_impl.h" #include "base/task/sequence_manager/sequence_manager_impl.h"
#include "base/task/sequence_manager/sequenced_task_source.h" #include "base/task/sequence_manager/sequenced_task_source.h"
#include "base/task/sequence_manager/thread_controller.h" #include "base/task/sequence_manager/thread_controller.h"
#include "base/task/sequence_manager/thread_controller_power_monitor.h"
#include "base/task/sequence_manager/work_deduplicator.h" #include "base/task/sequence_manager/work_deduplicator.h"
#include "base/thread_annotations.h" #include "base/thread_annotations.h"
#include "base/threading/hang_watcher.h" #include "base/threading/hang_watcher.h"
...@@ -154,6 +155,8 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl ...@@ -154,6 +155,8 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl
WorkDeduplicator work_deduplicator_; WorkDeduplicator work_deduplicator_;
ThreadControllerPowerMonitor power_monitor_;
// Can only be set once (just before calling // Can only be set once (just before calling
// work_deduplicator_.BindToCurrentThread()). After that only read access is // work_deduplicator_.BindToCurrentThread()). After that only read access is
// allowed. // allowed.
...@@ -187,11 +190,6 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl ...@@ -187,11 +190,6 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl
}; };
} // namespace internal } // namespace internal
// Initialize ThreadController features. Called after FeatureList is available
// when the process is still single-threaded.
BASE_EXPORT void PostFieldTrialInitialization();
} // namespace sequence_manager } // namespace sequence_manager
} // namespace base } // namespace base
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include "base/process/process_handle.h" #include "base/process/process_handle.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h" #include "base/task/sequence_manager/thread_controller_power_monitor.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/trace_event/trace_event_impl.h" #include "base/trace_event/trace_event_impl.h"
...@@ -630,7 +630,8 @@ void ChromeMainDelegate::PostFieldTrialInitialization() { ...@@ -630,7 +630,8 @@ void ChromeMainDelegate::PostFieldTrialInitialization() {
#if defined(OS_WIN) #if defined(OS_WIN)
SetUpExtendedCrashReporting(is_browser_process); SetUpExtendedCrashReporting(is_browser_process);
base::Time::ReadMinTimerIntervalLowResMs(); base::Time::ReadMinTimerIntervalLowResMs();
base::sequence_manager::PostFieldTrialInitialization(); base::sequence_manager::internal::ThreadControllerPowerMonitor::
InitializeOnMainThread();
#endif #endif
} }
......
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