Commit 4e292920 authored by Francois Doray's avatar Francois Doray Committed by Commit Bot

[blink scheduler] Support interval change at any time in WakeUpBudgetPool.

Before this CL, changing the wake up interval in WakeUpBudgetPool did
not have an immediate effect. With this CL, decreasing the wake up
interval immediately reschedules the next wake up. This change is
required to support the intensive wake up experiment, which will
change the wake up interval from 1 second to 1 minute after 5 minutes
in background.

Bug: 1075553
Change-Id: I4ca1589a1ca810a4109cffc694db8ae1d89293dc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2220840
Commit-Queue: François Doray <fdoray@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#774651}
parent b3d60081
......@@ -61,7 +61,7 @@ void BudgetPool::EnableThrottling(base::sequence_manager::LazyNow* lazy_now) {
TRACE_EVENT0("renderer.scheduler", "BudgetPool_EnableThrottling");
BlockThrottledQueues(lazy_now->Now());
UpdateThrottlingStateForAllQueues(lazy_now->Now());
}
void BudgetPool::DisableThrottling(base::sequence_manager::LazyNow* lazy_now) {
......@@ -90,7 +90,7 @@ void BudgetPool::Close() {
budget_pool_controller_->UnregisterBudgetPool(this);
}
void BudgetPool::BlockThrottledQueues(base::TimeTicks now) {
void BudgetPool::UpdateThrottlingStateForAllQueues(base::TimeTicks now) {
for (TaskQueue* queue : associated_task_queues_)
budget_pool_controller_->UpdateQueueSchedulingLifecycleState(now, queue);
}
......
......@@ -103,8 +103,10 @@ class PLATFORM_EXPORT BudgetPool {
// All queues should be removed before calling Close().
void Close();
// Block all associated queues and schedule them to run when appropriate.
void BlockThrottledQueues(base::TimeTicks now);
// Ensures that a pump is scheduled and that a fence is installed for all
// queues in this pool, based on state of those queues and latest values from
// CanRunTasksAt/GetTimeTasksCanRunUntil/GetNextAllowedRunTime.
void UpdateThrottlingStateForAllQueues(base::TimeTicks now);
protected:
BudgetPool(const char* name, BudgetPoolController* budget_pool_controller);
......
......@@ -132,7 +132,7 @@ TEST_F(BudgetPoolTest, WakeUpBudgetPool) {
scheduler_->NewTimerTaskQueue(
MainThreadTaskQueue::QueueType::kFrameThrottleable, nullptr);
pool->SetWakeUpInterval(base::TimeDelta::FromSeconds(10));
pool->SetWakeUpInterval(base::TimeTicks(), base::TimeDelta::FromSeconds(10));
pool->SetWakeUpDuration(base::TimeDelta::FromMilliseconds(10));
// Can't run tasks until a wake-up.
......
......@@ -116,7 +116,7 @@ void CPUTimeBudgetPool::RecordTaskRunTime(TaskQueue* queue,
}
if (current_budget_level_->InSecondsF() < 0)
BlockThrottledQueues(end_time);
UpdateThrottlingStateForAllQueues(end_time);
}
void CPUTimeBudgetPool::OnQueueNextWakeUpChanged(
......
......@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/test/bind_test_util.h"
#include "base/test/test_mock_time_task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -1329,7 +1330,8 @@ TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottling_EnableDisableThrottling) {
constexpr base::TimeDelta kDelay = base::TimeDelta::FromSeconds(10);
constexpr base::TimeDelta kTimeBetweenWakeUps =
base::TimeDelta::FromMinutes(1);
wake_up_budget_pool_->SetWakeUpInterval(kTimeBetweenWakeUps);
wake_up_budget_pool_->SetWakeUpInterval(base::TimeTicks(),
kTimeBetweenWakeUps);
wake_up_budget_pool_->SetWakeUpDuration(base::TimeDelta::FromMilliseconds(1));
Vector<base::TimeTicks> run_times;
......@@ -1375,6 +1377,138 @@ TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottling_EnableDisableThrottling) {
base::TimeTicks() + base::TimeDelta::FromSeconds(300)));
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_IncreaseWakeUpIntervalBeforeWakeUp) {
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
// Post 2 delayed tasks when the wake up interval is 1 minute. The delay of
// the 2nd task is such that it won't be ready when the 1st task completes.
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(1));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMinutes(2));
// Update the wake up interval to 1 hour.
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromHours(1));
// The 1st delayed task should run after 1 minute, because increasing the wake
// up interval does not affect already scheduled wake ups. The 2nd task is
// scheduled according to the 1-hour wake up interval.
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1),
base::TimeTicks() + base::TimeDelta::FromHours(1)));
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_DecreaseWakeUpIntervalBeforeWakeUp) {
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
// Post a delayed task when the wake up interval is 1 hour.
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromHours(1));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(1));
// Update the wake up interval to 1 minute.
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
// The delayed task should run after 1 minute, which is the most up to date
// wake up interval.
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1)));
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_IncreaseWakeUpIntervalDuringWakeUp) {
wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
// Post a 1st delayed task when the wake up interval is 1 minute.
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
TestTask(&run_times, test_task_runner_);
// Post a 2nd delayed task when the wake up interval is still 1 minute.
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
TestTask(&run_times, test_task_runner_);
// Post a 3rd task when the wake up interval is 1 hour.
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(1));
}),
base::TimeDelta::FromSeconds(1));
// Increase the wake up interval. Since the wake up for the 2nd task is
// already scheduled, it isn't affected by this.
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromHours(1));
}),
base::TimeDelta::FromSeconds(1));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1),
base::TimeTicks() + base::TimeDelta::FromMinutes(2),
base::TimeTicks() + base::TimeDelta::FromHours(1)));
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_DecreaseWakeUpIntervalDuringWakeUp) {
wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
// Post a 1st delayed task when the wake up interval is 1 hour.
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromHours(1));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
TestTask(&run_times, test_task_runner_);
// Post a 2nd delayed task when the wake up interval is still 1 hour.
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
TestTask(&run_times, test_task_runner_);
// Post a 3rd task when the wake up interval is 1 minute.
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(1));
}),
base::TimeDelta::FromSeconds(1));
// Decrease the wake up interval. This immediately reschedules the wake
// up for the 2nd task.
wake_up_budget_pool_->SetWakeUpInterval(
test_task_runner_->NowTicks(), base::TimeDelta::FromMinutes(1));
}),
base::TimeDelta::FromSeconds(1));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromHours(1),
base::TimeTicks() + base::TimeDelta::FromHours(1) +
base::TimeDelta::FromMinutes(1),
base::TimeTicks() + base::TimeDelta::FromHours(1) +
base::TimeDelta::FromMinutes(2)));
}
TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottlingWithCPUBudgetThrottling) {
wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
......
......@@ -26,8 +26,10 @@ QueueBlockType WakeUpBudgetPool::GetBlockType() const {
return QueueBlockType::kNewTasksOnly;
}
void WakeUpBudgetPool::SetWakeUpInterval(base::TimeDelta interval) {
void WakeUpBudgetPool::SetWakeUpInterval(base::TimeTicks now,
base::TimeDelta interval) {
wake_up_interval_ = interval;
UpdateThrottlingStateForAllQueues(now);
}
void WakeUpBudgetPool::SetWakeUpDuration(base::TimeDelta duration) {
......
......@@ -13,8 +13,8 @@
namespace blink {
namespace scheduler {
// WakeUpBudgetPool represents a collection of task queues which share a limit
// on total cpu time.
// WakeUpBudgetPool represents a collection of task queues which run for a
// limited time at regular intervals.
class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool {
public:
WakeUpBudgetPool(const char* name,
......@@ -22,12 +22,14 @@ class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool {
base::TimeTicks now);
~WakeUpBudgetPool() override;
// Note: this does not have an immediate effect and should be called only
// during initialization of a WakeUpBudgetPool.
void SetWakeUpInterval(base::TimeDelta interval);
// Sets the interval between wake ups. This can be invoked at any time. If a
// next wake up is already scheduled, it is rescheduled only if the new
// |interval| is smaller than the old interval. Wake ups after that will be
// scheduled according to |interval|.
void SetWakeUpInterval(base::TimeTicks now, base::TimeDelta interval);
// Note: this does not have an immediate effect and should be called only
// during initialization of a WakeUpBudgetPool.
// Sets the duration of wake ups. This does not have an immediate effect and
// should be called only during initialization of a WakeUpBudgetPool.
void SetWakeUpDuration(base::TimeDelta duration);
// BudgetPool implementation:
......
......@@ -591,7 +591,8 @@ void PageSchedulerImpl::MaybeInitializeWakeUpBudgetPool(
main_thread_scheduler_->task_queue_throttler()->CreateWakeUpBudgetPool(
"Page Wake Up Throttling");
wake_up_budget_pool_->SetWakeUpInterval(kThrottledWakeUpInterval);
wake_up_budget_pool_->SetWakeUpInterval(lazy_now->Now(),
kThrottledWakeUpInterval);
wake_up_budget_pool_->SetWakeUpDuration(kThrottledWakeUpDuration);
}
......
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