Commit d10d7333 authored by Patrick Monette's avatar Patrick Monette Committed by Commit Bot

WakeUpBudgetPool: Add AllowUnalignedWakeUpIfNoRecentWakeUp

This is a new mode for the intensive wake up throttling
that allows well behaving page/workers to retain the
precision on their timers.

Bug: 1075553
Change-Id: I9b92dee2f9c3fd43ee48e27c728eeb3ad9de0f96
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2249377
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#780435}
parent c2dca7b4
...@@ -132,6 +132,13 @@ class TaskQueueThrottlerTest : public testing::Test { ...@@ -132,6 +132,13 @@ class TaskQueueThrottlerTest : public testing::Test {
return task_queue->BlockedByFence(); return task_queue->BlockedByFence();
} }
void ForwardTimeToNextMinute() {
test_task_runner_->FastForwardBy(
test_task_runner_->NowTicks().SnappedToNextTick(
base::TimeTicks(), base::TimeDelta::FromMinutes(1)) -
test_task_runner_->NowTicks());
}
protected: protected:
virtual const base::TickClock* GetTickClock() const { virtual const base::TickClock* GetTickClock() const {
return test_task_runner_->GetMockTickClock(); return test_task_runner_->GetMockTickClock();
...@@ -1377,6 +1384,75 @@ TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottling_EnableDisableThrottling) { ...@@ -1377,6 +1384,75 @@ TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottling_EnableDisableThrottling) {
base::TimeTicks() + base::TimeDelta::FromSeconds(300))); base::TimeTicks() + base::TimeDelta::FromSeconds(300)));
} }
TEST_F(TaskQueueThrottlerTest, WakeUpBasedThrottling_UnalignedWakeUps) {
// All throttled wake ups are aligned on 1-second intervals by
// TaskQueueThrottler, irrespective of BudgetPools. Start the test at a time
// aligned on a 1-minute interval, to simplify expectations.
ForwardTimeToNextMinute();
const base::TimeTicks start_time = test_task_runner_->NowTicks();
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
wake_up_budget_pool_->AllowUnalignedWakeUpIfNoRecentWakeUp();
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(90));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(start_time + base::TimeDelta::FromSeconds(90)));
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_UnalignedWakeUps_MultipleTasks) {
// Start at a 1-minute aligned time to simplify expectations.
ForwardTimeToNextMinute();
const base::TimeTicks initial_time = test_task_runner_->NowTicks();
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
wake_up_budget_pool_->AllowUnalignedWakeUpIfNoRecentWakeUp();
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(timer_queue_.get());
// Task delay: Expected run time: Reason:
// 30 seconds 30 seconds >= 60 seconds after last wake up
// 80 seconds 90 seconds >= 60 seconds after last wake up
// 95 seconds 120 seconds Aligned
// 100 seconds 120 seconds Aligned
// 130 seconds 180 seconds Aligned
// 251 seconds 251 seconds >= 60 seconds after last wake up
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(30));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(80));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(95));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(100));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(130));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(251));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(initial_time + base::TimeDelta::FromSeconds(30),
initial_time + base::TimeDelta::FromSeconds(90),
initial_time + base::TimeDelta::FromSeconds(120),
initial_time + base::TimeDelta::FromSeconds(120),
initial_time + base::TimeDelta::FromSeconds(180),
initial_time + base::TimeDelta::FromSeconds(251)));
}
TEST_F(TaskQueueThrottlerTest, TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_IncreaseWakeUpIntervalBeforeWakeUp) { WakeUpBasedThrottling_IncreaseWakeUpIntervalBeforeWakeUp) {
Vector<base::TimeTicks> run_times; Vector<base::TimeTicks> run_times;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h"
#include <algorithm>
#include <cstdint> #include <cstdint>
#include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h" #include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
...@@ -36,6 +37,10 @@ void WakeUpBudgetPool::SetWakeUpDuration(base::TimeDelta duration) { ...@@ -36,6 +37,10 @@ void WakeUpBudgetPool::SetWakeUpDuration(base::TimeDelta duration) {
wake_up_duration_ = duration; wake_up_duration_ = duration;
} }
void WakeUpBudgetPool::AllowUnalignedWakeUpIfNoRecentWakeUp() {
allow_unaligned_wake_up_is_no_recent_wake_up_ = true;
}
void WakeUpBudgetPool::RecordTaskRunTime(TaskQueue* queue, void WakeUpBudgetPool::RecordTaskRunTime(TaskQueue* queue,
base::TimeTicks start_time, base::TimeTicks start_time,
base::TimeTicks end_time) { base::TimeTicks end_time) {
...@@ -73,13 +78,35 @@ base::TimeTicks WakeUpBudgetPool::GetNextAllowedRunTime( ...@@ -73,13 +78,35 @@ base::TimeTicks WakeUpBudgetPool::GetNextAllowedRunTime(
base::TimeTicks desired_run_time) const { base::TimeTicks desired_run_time) const {
if (!is_enabled_) if (!is_enabled_)
return desired_run_time; return desired_run_time;
if (!last_wake_up_) {
return desired_run_time.SnappedToNextTick(base::TimeTicks(), // Do not throttle if the desired run time is still within the duration of the
wake_up_interval_); // last wake up.
} if (last_wake_up_.has_value() &&
if (desired_run_time < last_wake_up_.value() + wake_up_duration_) desired_run_time < last_wake_up_.value() + wake_up_duration_) {
return desired_run_time; return desired_run_time;
DCHECK_GE(desired_run_time, last_wake_up_.value()); }
// Do not throttle if there hasn't been a wake up in the last wake up
// interval.
if (allow_unaligned_wake_up_is_no_recent_wake_up_) {
// If unaligned wake ups are allowed, the first wake up can happen at any
// point.
if (!last_wake_up_.has_value())
return desired_run_time;
// Unaligned wake ups can happen at most every |wake_up_interval_| after the
// last wake up.
auto next_unaligned_wake_up =
std::max(desired_run_time, last_wake_up_.value() + wake_up_interval_);
// Aligned wake ups happen every |wake_up_interval_|, snapped to the minute.
auto next_aligned_wake_up = desired_run_time.SnappedToNextTick(
base::TimeTicks(), wake_up_interval_);
// Pick the earliest of the two allowed run times.
return std::min(next_unaligned_wake_up, next_aligned_wake_up);
}
return desired_run_time.SnappedToNextTick(base::TimeTicks(), return desired_run_time.SnappedToNextTick(base::TimeTicks(),
wake_up_interval_); wake_up_interval_);
} }
......
...@@ -32,6 +32,13 @@ class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool { ...@@ -32,6 +32,13 @@ class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool {
// should be called only during initialization of a WakeUpBudgetPool. // should be called only during initialization of a WakeUpBudgetPool.
void SetWakeUpDuration(base::TimeDelta duration); void SetWakeUpDuration(base::TimeDelta duration);
// If called, the budget pool allows an unaligned wake up when there hasn't
// been a wake up in the last |wake_up_interval_|.
//
// This does not have an immediate effect and should be called only during
// initialization of a WakeUpBudgetPool.
void AllowUnalignedWakeUpIfNoRecentWakeUp();
// BudgetPool implementation: // BudgetPool implementation:
void RecordTaskRunTime(base::sequence_manager::TaskQueue* queue, void RecordTaskRunTime(base::sequence_manager::TaskQueue* queue,
base::TimeTicks start_time, base::TimeTicks start_time,
...@@ -55,6 +62,8 @@ class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool { ...@@ -55,6 +62,8 @@ class PLATFORM_EXPORT WakeUpBudgetPool : public BudgetPool {
base::TimeDelta wake_up_interval_; base::TimeDelta wake_up_interval_;
base::TimeDelta wake_up_duration_; base::TimeDelta wake_up_duration_;
bool allow_unaligned_wake_up_is_no_recent_wake_up_ = false;
base::Optional<base::TimeTicks> last_wake_up_; base::Optional<base::TimeTicks> last_wake_up_;
DISALLOW_COPY_AND_ASSIGN(WakeUpBudgetPool); DISALLOW_COPY_AND_ASSIGN(WakeUpBudgetPool);
......
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