blob: ca36958663c3c048743cddf3b5cfb257cb0ce92e [file] [log] [blame]
// Copyright 2015 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 "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
#include <stddef.h>
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#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.h"
#include "base/test/test_mock_time_task_runner.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/wtf/deque.h"
namespace blink {
namespace scheduler {
// To avoid symbol collisions in jumbo builds.
namespace task_queue_throttler_unittest {
using base::TestMockTimeTaskRunner;
using base::TimeDelta;
using base::TimeTicks;
using base::sequence_manager::LazyNow;
using base::sequence_manager::TaskQueue;
using testing::ElementsAre;
class MainThreadSchedulerImplForTest : public MainThreadSchedulerImpl {
public:
using MainThreadSchedulerImpl::ControlTaskQueue;
MainThreadSchedulerImplForTest(
std::unique_ptr<base::sequence_manager::SequenceManager> manager,
base::Optional<base::Time> initial_virtual_time)
: MainThreadSchedulerImpl(std::move(manager), initial_virtual_time) {}
};
void NopTask() {}
void AddOneTask(size_t* count) {
(*count)++;
}
void RunTenTimesTask(size_t* count, scoped_refptr<TaskQueue> timer_queue) {
if (++(*count) < 10) {
timer_queue->task_runner()->PostDelayedTask(
FROM_HERE, base::BindOnce(&RunTenTimesTask, count, timer_queue),
base::TimeDelta::FromMilliseconds(1));
}
}
Deque<base::TimeDelta> MakeTaskDurations(wtf_size_t size,
base::TimeDelta duration) {
Deque<base::TimeDelta> task_durations;
for (wtf_size_t i = 0; i < size; ++i)
task_durations.push_back(duration);
return task_durations;
}
class TaskQueueThrottlerTest : public testing::Test {
public:
TaskQueueThrottlerTest()
: test_task_runner_(base::MakeRefCounted<TestMockTimeTaskRunner>()) {}
~TaskQueueThrottlerTest() override = default;
void SetUp() override {
// A null clock triggers some assertions.
test_task_runner_->AdvanceMockTickClock(
base::TimeDelta::FromMilliseconds(5));
scheduler_.reset(new MainThreadSchedulerImplForTest(
base::sequence_manager::SequenceManagerForTest::Create(
nullptr, test_task_runner_, GetTickClock()),
base::nullopt));
task_queue_throttler_ = scheduler_->task_queue_throttler();
wake_up_budget_pool_ =
task_queue_throttler_->CreateWakeUpBudgetPool("Wake Up Budget Pool");
wake_up_budget_pool_->SetWakeUpDuration(base::TimeDelta());
timer_queue_ = scheduler_->NewTaskQueue(
MainThreadTaskQueue::QueueCreationParams(
MainThreadTaskQueue::QueueType::kFrameThrottleable)
.SetCanBeThrottled(true));
wake_up_budget_pool_->AddQueue(base::TimeTicks(),
timer_queue_->GetTaskQueueForTest());
timer_task_runner_ = timer_queue_->GetTaskRunnerWithDefaultTaskType();
}
void TearDown() override {
wake_up_budget_pool_->RemoveQueue(test_task_runner_->NowTicks(),
timer_queue_->GetTaskQueueForTest());
wake_up_budget_pool_->Close();
scheduler_->Shutdown();
scheduler_.reset();
}
void ExpectThrottled(scoped_refptr<TaskQueue> timer_queue) {
size_t count = 0;
timer_queue->task_runner()->PostDelayedTask(
FROM_HERE, base::BindOnce(&RunTenTimesTask, &count, timer_queue),
base::TimeDelta::FromMilliseconds(1));
test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(11));
EXPECT_EQ(count, 0u);
// Make sure the rest of the tasks run or we risk a UAF on |count|.
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(count, 10u);
}
void ExpectUnthrottled(scoped_refptr<TaskQueue> timer_queue) {
size_t count = 0;
timer_queue->task_runner()->PostDelayedTask(
FROM_HERE, base::BindOnce(&RunTenTimesTask, &count, timer_queue),
base::TimeDelta::FromMilliseconds(1));
test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(11));
EXPECT_EQ(count, 10u);
}
bool IsQueueBlocked(TaskQueue* task_queue) {
if (!task_queue->IsQueueEnabled())
return true;
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());
}
TaskQueue* GetTaskQueue(MainThreadTaskQueue* queue) {
return queue->GetTaskQueueForTest();
}
protected:
virtual const base::TickClock* GetTickClock() const {
return test_task_runner_->GetMockTickClock();
}
scoped_refptr<TestMockTimeTaskRunner> test_task_runner_;
std::unique_ptr<MainThreadSchedulerImplForTest> scheduler_;
// A queue that is subject to |wake_up_budget_pool_|.
scoped_refptr<MainThreadTaskQueue> timer_queue_;
scoped_refptr<base::SingleThreadTaskRunner> timer_task_runner_;
TaskQueueThrottler* task_queue_throttler_ = nullptr;
WakeUpBudgetPool* wake_up_budget_pool_ = nullptr;
private:
DISALLOW_COPY_AND_ASSIGN(TaskQueueThrottlerTest);
};
// Advances mock clock every time we call NowTicks() from the scheduler.
class AutoAdvancingProxyClock : public base::TickClock {
public:
AutoAdvancingProxyClock(scoped_refptr<TestMockTimeTaskRunner> task_runner)
: task_runner_(task_runner) {}
~AutoAdvancingProxyClock() override = default;
void SetAutoAdvanceInterval(base::TimeDelta interval) {
advance_interval_ = interval;
}
base::TimeTicks NowTicks() const override {
if (!advance_interval_.is_zero())
task_runner_->AdvanceMockTickClock(advance_interval_);
return task_runner_->NowTicks();
}
private:
scoped_refptr<TestMockTimeTaskRunner> task_runner_;
base::TimeDelta advance_interval_;
};
class TaskQueueThrottlerWithAutoAdvancingTimeTest
: public TaskQueueThrottlerTest,
public testing::WithParamInterface<bool> {
public:
TaskQueueThrottlerWithAutoAdvancingTimeTest()
: proxy_clock_(test_task_runner_) {}
~TaskQueueThrottlerWithAutoAdvancingTimeTest() override = default;
void SetUp() override {
TaskQueueThrottlerTest::SetUp();
if (GetParam()) {
// Will advance the time by this value after running each task.
proxy_clock_.SetAutoAdvanceInterval(
base::TimeDelta::FromMicroseconds(10));
}
}
protected:
const base::TickClock* GetTickClock() const override { return &proxy_clock_; }
private:
AutoAdvancingProxyClock proxy_clock_;
DISALLOW_COPY_AND_ASSIGN(TaskQueueThrottlerWithAutoAdvancingTimeTest);
};
INSTANTIATE_TEST_SUITE_P(All,
TaskQueueThrottlerWithAutoAdvancingTimeTest,
testing::Bool());
TEST_F(TaskQueueThrottlerTest, ThrottledTasksReportRealTime) {
EXPECT_EQ(GetTaskQueue(timer_queue_.get())->GetTimeDomain()->Now(),
test_task_runner_->NowTicks());
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
EXPECT_EQ(GetTaskQueue(timer_queue_.get())->GetTimeDomain()->Now(),
test_task_runner_->NowTicks());
test_task_runner_->AdvanceMockTickClock(
base::TimeDelta::FromMilliseconds(250));
// Make sure the throttled time domain's Now() reports the same as the
// underlying clock.
EXPECT_EQ(GetTaskQueue(timer_queue_.get())->GetTimeDomain()->Now(),
test_task_runner_->NowTicks());
}
TEST_F(TaskQueueThrottlerTest, AlignedThrottledRunTime) {
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(0.0)));
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(0.1)));
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(0.2)));
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(0.5)));
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(0.8)));
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0),
TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(0.9)));
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(2.0),
TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0)));
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(2.0),
TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(1.1)));
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(9.0),
TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(8.0)));
EXPECT_EQ(base::TimeTicks() + base::TimeDelta::FromSecondsD(9.0),
TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks() + base::TimeDelta::FromSecondsD(8.1)));
}
namespace {
// Round up time to milliseconds to deal with autoadvancing time.
// TODO(altimin): round time only when autoadvancing time is enabled.
base::TimeDelta RoundTimeToMilliseconds(base::TimeDelta time) {
return time - time % base::TimeDelta::FromMilliseconds(1);
}
base::TimeTicks RoundTimeToMilliseconds(base::TimeTicks time) {
return base::TimeTicks() + RoundTimeToMilliseconds(time - base::TimeTicks());
}
void TestTask(Vector<base::TimeTicks>* run_times,
scoped_refptr<TestMockTimeTaskRunner> task_runner) {
run_times->push_back(RoundTimeToMilliseconds(task_runner->NowTicks()));
// FIXME No auto-advancing
}
void ExpensiveTestTask(Vector<base::TimeTicks>* run_times,
scoped_refptr<TestMockTimeTaskRunner> task_runner) {
run_times->push_back(RoundTimeToMilliseconds(task_runner->NowTicks()));
task_runner->AdvanceMockTickClock(base::TimeDelta::FromMilliseconds(250));
// FIXME No auto-advancing
}
void RecordThrottling(Vector<base::TimeDelta>* reported_throttling_times,
base::TimeDelta throttling_duration) {
reported_throttling_times->push_back(
RoundTimeToMilliseconds(throttling_duration));
}
} // namespace
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, TimerAlignment) {
Vector<base::TimeTicks> run_times;
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200.0));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(800.0));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(1200.0));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(8300.0));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
test_task_runner_->FastForwardUntilNoTasksRemain();
// Times are aligned to a multiple of 1000 milliseconds.
EXPECT_THAT(
run_times,
ElementsAre(
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000.0),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000.0),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000.0),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(9000.0)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
TimerAlignment_Unthrottled) {
Vector<base::TimeTicks> run_times;
base::TimeTicks start_time = test_task_runner_->NowTicks();
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200.0));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(800.0));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(1200.0));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(8300.0));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
test_task_runner_->FastForwardUntilNoTasksRemain();
// Times are not aligned.
EXPECT_THAT(
run_times,
ElementsAre(RoundTimeToMilliseconds(
start_time + base::TimeDelta::FromMilliseconds(200.0)),
RoundTimeToMilliseconds(
start_time + base::TimeDelta::FromMilliseconds(800.0)),
RoundTimeToMilliseconds(
start_time + base::TimeDelta::FromMilliseconds(1200.0)),
RoundTimeToMilliseconds(
start_time + base::TimeDelta::FromMilliseconds(8300.0))));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, Refcount) {
ExpectUnthrottled(GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
ExpectThrottled(GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
ExpectThrottled(GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
ExpectThrottled(GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
ExpectUnthrottled(GetTaskQueue(timer_queue_.get()));
// Should be a NOP.
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
ExpectUnthrottled(GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
ExpectThrottled(GetTaskQueue(timer_queue_.get()));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
ThrotlingAnEmptyQueueDoesNotPostPumpThrottledTasksLocked) {
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
EXPECT_TRUE(GetTaskQueue(scheduler_->ControlTaskQueue().get())->IsEmpty());
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
OnTimeDomainHasImmediateWork_EnabledQueue) {
task_queue_throttler_->OnQueueNextWakeUpChanged(
GetTaskQueue(timer_queue_.get()), base::TimeTicks());
// Check PostPumpThrottledTasksLocked was called.
EXPECT_FALSE(GetTaskQueue(scheduler_->ControlTaskQueue().get())->IsEmpty());
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
OnTimeDomainHasImmediateWork_DisabledQueue) {
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
GetTaskQueue(timer_queue_.get())->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
task_queue_throttler_->OnQueueNextWakeUpChanged(
GetTaskQueue(timer_queue_.get()), base::TimeTicks());
// Check PostPumpThrottledTasksLocked was not called.
EXPECT_TRUE(GetTaskQueue(scheduler_->ControlTaskQueue().get())->IsEmpty());
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
ThrottlingADisabledQueueDoesNotPostPumpThrottledTasks) {
timer_task_runner_->PostTask(FROM_HERE, base::BindOnce(&NopTask));
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
GetTaskQueue(timer_queue_.get())->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
EXPECT_TRUE(GetTaskQueue(scheduler_->ControlTaskQueue().get())->IsEmpty());
// Enabling it should trigger a call to PostPumpThrottledTasksLocked.
voter->SetVoteToEnable(true);
EXPECT_FALSE(GetTaskQueue(scheduler_->ControlTaskQueue().get())->IsEmpty());
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
ThrottlingADisabledQueueDoesNotPostPumpThrottledTasks_DelayedTask) {
timer_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
base::TimeDelta::FromMilliseconds(1));
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
GetTaskQueue(timer_queue_.get())->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
EXPECT_TRUE(GetTaskQueue(scheduler_->ControlTaskQueue().get())->IsEmpty());
// Enabling it should trigger a call to PostPumpThrottledTasksLocked.
voter->SetVoteToEnable(true);
EXPECT_FALSE(GetTaskQueue(scheduler_->ControlTaskQueue().get())->IsEmpty());
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, WakeUpForNonDelayedTask) {
Vector<base::TimeTicks> run_times;
// Nothing is posted on timer_queue_ so PumpThrottledTasks will not tick.
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
// Posting a task should trigger the pump.
timer_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(1000.0)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, WakeUpForDelayedTask) {
Vector<base::TimeTicks> run_times;
// Nothing is posted on timer_queue_ so PumpThrottledTasks will not tick.
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
// Posting a task should trigger the pump.
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(1200.0));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(2000.0)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
SingleThrottledTaskPumpedAndRunWithNoExtraneousMessageLoopTasks) {
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10));
timer_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
delay);
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
SingleFutureThrottledTaskPumpedAndRunWithNoExtraneousMessageLoopTasks) {
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
base::TimeDelta delay(base::TimeDelta::FromSecondsD(15.5));
timer_task_runner_->PostDelayedTask(FROM_HERE, base::BindOnce(&NopTask),
delay);
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
TwoFutureThrottledTaskPumpedAndRunWithNoExtraneousMessageLoopTasks) {
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
Vector<base::TimeTicks> run_times;
base::TimeDelta delay(base::TimeDelta::FromSecondsD(15.5));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
delay);
base::TimeDelta delay2(base::TimeDelta::FromSecondsD(5.5));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
delay2);
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
test_task_runner_->FastForwardBy(test_task_runner_->NextPendingTaskDelay());
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
test_task_runner_->FastForwardBy(test_task_runner_->NextPendingTaskDelay());
EXPECT_EQ(0u, test_task_runner_->GetPendingTaskCount());
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(6),
base::TimeTicks() + base::TimeDelta::FromSeconds(16)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
TaskDelayIsBasedOnRealTime) {
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
// Post an initial task that should run at the first aligned time period.
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(900.0));
test_task_runner_->FastForwardUntilNoTasksRemain();
// Advance realtime.
test_task_runner_->AdvanceMockTickClock(
base::TimeDelta::FromMilliseconds(250));
// Post a task that due to real time + delay must run in the third aligned
// time period.
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(900.0));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000.0),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000.0)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, TaskQueueDisabledTillPump) {
size_t count = 0;
timer_task_runner_->PostTask(FROM_HERE, base::BindOnce(&AddOneTask, &count));
EXPECT_FALSE(IsQueueBlocked(GetTaskQueue(timer_queue_.get())));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
EXPECT_TRUE(IsQueueBlocked(GetTaskQueue(timer_queue_.get())));
test_task_runner_->FastForwardUntilNoTasksRemain(); // Wait until the pump.
EXPECT_EQ(1u, count); // The task got run.
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
DoubleIncrementDoubleDecrement) {
timer_task_runner_->PostTask(FROM_HERE, base::BindOnce(&NopTask));
EXPECT_FALSE(IsQueueBlocked(GetTaskQueue(timer_queue_.get())));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
EXPECT_TRUE(IsQueueBlocked(GetTaskQueue(timer_queue_.get())));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
EXPECT_FALSE(IsQueueBlocked(GetTaskQueue(timer_queue_.get())));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
EnableVirtualTimeThenIncrement) {
timer_task_runner_->PostTask(FROM_HERE, base::BindOnce(&NopTask));
scheduler_->EnableVirtualTime(
MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
EXPECT_EQ(GetTaskQueue(timer_queue_.get())->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
EXPECT_FALSE(IsQueueBlocked(GetTaskQueue(timer_queue_.get())));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
EXPECT_FALSE(IsQueueBlocked(GetTaskQueue(timer_queue_.get())));
EXPECT_EQ(GetTaskQueue(timer_queue_.get())->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
IncrementThenEnableVirtualTime) {
timer_task_runner_->PostTask(FROM_HERE, base::BindOnce(&NopTask));
EXPECT_FALSE(IsQueueBlocked(GetTaskQueue(timer_queue_.get())));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
EXPECT_TRUE(IsQueueBlocked(GetTaskQueue(timer_queue_.get())));
scheduler_->EnableVirtualTime(
MainThreadSchedulerImpl::BaseTimeOverridePolicy::DO_NOT_OVERRIDE);
EXPECT_FALSE(IsQueueBlocked(GetTaskQueue(timer_queue_.get())));
EXPECT_EQ(GetTaskQueue(timer_queue_.get())->GetTimeDomain(),
scheduler_->GetVirtualTimeDomain());
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, TimeBasedThrottling) {
Vector<base::TimeTicks> run_times;
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
// Submit two tasks. They should be aligned, and second one should be
// throttled.
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(3)));
pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(timer_queue_.get()));
run_times.clear();
// Queue was removed from CPUTimeBudgetPool, only timer alignment should be
// active now.
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(4000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(4250)));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
pool->Close();
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
EnableAndDisableCPUTimeBudgetPool) {
Vector<base::TimeTicks> run_times;
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
EXPECT_TRUE(pool->IsThrottlingEnabled());
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
// Post an expensive task. Pool is now throttled.
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(1000)));
run_times.clear();
LazyNow lazy_now_1(test_task_runner_->GetMockTickClock());
pool->DisableThrottling(&lazy_now_1);
EXPECT_FALSE(pool->IsThrottlingEnabled());
// Pool should not be throttled now.
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(2000)));
run_times.clear();
LazyNow lazy_now_2(test_task_runner_->GetMockTickClock());
pool->EnableThrottling(&lazy_now_2);
EXPECT_TRUE(pool->IsThrottlingEnabled());
// Because time pool was disabled, time budget level did not replenish
// and queue is throttled.
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(4000)));
run_times.clear();
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(timer_queue_.get()));
pool->Close();
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
ImmediateTasksTimeBudgetThrottling) {
Vector<base::TimeTicks> run_times;
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
// Submit two tasks. They should be aligned, and second one should be
// throttled.
timer_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_));
timer_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(3)));
pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(timer_queue_.get()));
run_times.clear();
// Queue was removed from CPUTimeBudgetPool, only timer alignment should be
// active now.
timer_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_));
timer_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(4000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(4250)));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
pool->Close();
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
TwoQueuesTimeBudgetThrottling) {
Vector<base::TimeTicks> run_times;
scoped_refptr<MainThreadTaskQueue> second_queue = scheduler_->NewTaskQueue(
MainThreadTaskQueue::QueueCreationParams(
MainThreadTaskQueue::QueueType::kFrameThrottleable)
.SetCanBeThrottled(true));
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
pool->AddQueue(base::TimeTicks(), GetTaskQueue(second_queue.get()));
wake_up_budget_pool_->AddQueue(base::TimeTicks(),
GetTaskQueue(second_queue.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(second_queue.get()));
timer_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_));
second_queue->GetTaskRunnerWithDefaultTaskType()->PostTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(3)));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(second_queue.get()));
pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(timer_queue_.get()));
pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(second_queue.get()));
wake_up_budget_pool_->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(second_queue.get()));
pool->Close();
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
DisabledTimeBudgetDoesNotAffectThrottledQueues) {
Vector<base::TimeTicks> run_times;
LazyNow lazy_now(test_task_runner_->GetMockTickClock());
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetTimeBudgetRecoveryRate(lazy_now.Now(), 0.1);
pool->DisableThrottling(&lazy_now);
pool->AddQueue(lazy_now.Now(), GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(100));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(100));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1250)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
TimeBudgetThrottlingDoesNotAffectUnthrottledQueues) {
Vector<base::TimeTicks> run_times;
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
LazyNow lazy_now(test_task_runner_->GetMockTickClock());
pool->DisableThrottling(&lazy_now);
pool->AddQueue(test_task_runner_->NowTicks(),
GetTaskQueue(timer_queue_.get()));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(100));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(100));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(105),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(355)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, MaxThrottlingDelay) {
Vector<base::TimeTicks> run_times;
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetMaxThrottlingDelay(base::TimeTicks(),
base::TimeDelta::FromMinutes(1));
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.001);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
for (int i = 0; i < 5; ++i) {
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
}
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(62),
base::TimeTicks() + base::TimeDelta::FromSeconds(123),
base::TimeTicks() + base::TimeDelta::FromSeconds(184),
base::TimeTicks() + base::TimeDelta::FromSeconds(245)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
EnableAndDisableThrottling) {
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(295));
// Disable throttling - task should run immediately.
task_queue_throttler_->DisableThrottling();
test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(200));
EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(300)));
run_times.clear();
// Schedule a task at 900ms. It should proceed as normal.
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(400));
// Schedule a task at 1200ms. It should proceed as normal.
// PumpThrottledTasks was scheduled at 1000ms, so it needs to be checked
// that it was cancelled and it does not interfere with tasks posted before
// 1s mark and scheduled to run after 1s mark.
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(700));
test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(800));
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(900),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1200)));
run_times.clear();
// Schedule a task at 1500ms. It should be throttled because of enabled
// throttling.
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
// Throttling is enabled and new task should be aligned.
task_queue_throttler_->EnableThrottling();
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(2000)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, ReportThrottling) {
Vector<base::TimeTicks> run_times;
Vector<base::TimeDelta> reported_throttling_times;
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
pool->SetReportingCallback(
base::BindRepeating(&RecordThrottling, &reported_throttling_times));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(3)));
EXPECT_THAT(reported_throttling_times,
ElementsAre(base::TimeDelta::FromMilliseconds(1255),
base::TimeDelta::FromMilliseconds(1755)));
pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
pool->Close();
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, GrantAdditionalBudget) {
Vector<base::TimeTicks> run_times;
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
pool->GrantAdditionalBudget(base::TimeTicks(),
base::TimeDelta::FromMilliseconds(500));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
// Submit five tasks. First three will not be throttled because they have
// budget to run.
for (int i = 0; i < 5; ++i) {
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
}
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1250),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1500),
base::TimeTicks() + base::TimeDelta::FromSeconds(3),
base::TimeTicks() + base::TimeDelta::FromSeconds(6)));
pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
pool->Close();
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
EnableAndDisableThrottlingAndTimeBudgets) {
// This test checks that if time budget pool is enabled when throttling
// is disabled, it does not throttle the queue.
Vector<base::TimeTicks> run_times;
task_queue_throttler_->DisableThrottling();
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
LazyNow lazy_now_1(test_task_runner_->GetMockTickClock());
pool->DisableThrottling(&lazy_now_1);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(95));
LazyNow lazy_now_2(test_task_runner_->GetMockTickClock());
pool->EnableThrottling(&lazy_now_2);
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(300)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
AddQueueToBudgetPoolWhenThrottlingDisabled) {
// This test checks that a task queue is added to time budget pool
// when throttling is disabled, is does not throttle queue.
Vector<base::TimeTicks> run_times;
task_queue_throttler_->DisableThrottling();
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(95));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(300)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
DisabledQueueThenEnabledQueue) {
Vector<base::TimeTicks> run_times;
scoped_refptr<MainThreadTaskQueue> second_queue = scheduler_->NewTaskQueue(
MainThreadTaskQueue::QueueCreationParams(
MainThreadTaskQueue::QueueType::kFrameThrottleable)
.SetCanBeThrottled(true));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(second_queue.get()));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(100));
second_queue->GetTaskRunnerWithDefaultTaskType()->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
std::unique_ptr<TaskQueue::QueueEnabledVoter> voter =
GetTaskQueue(timer_queue_.get())->CreateQueueEnabledVoter();
voter->SetVoteToEnable(false);
test_task_runner_->AdvanceMockTickClock(
base::TimeDelta::FromMilliseconds(250));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(1000)));
// Advance time passed the 1-second aligned wake up. The next task will run on
// the next 1-second aligned wake up.
test_task_runner_->AdvanceMockTickClock(
base::TimeDelta::FromMilliseconds(10));
voter->SetVoteToEnable(true);
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest, TwoBudgetPools) {
Vector<base::TimeTicks> run_times;
scoped_refptr<MainThreadTaskQueue> second_queue = scheduler_->NewTaskQueue(
MainThreadTaskQueue::QueueCreationParams(
MainThreadTaskQueue::QueueType::kFrameThrottleable)
.SetCanBeThrottled(true));
wake_up_budget_pool_->AddQueue(base::TimeTicks(),
GetTaskQueue(second_queue.get()));
CPUTimeBudgetPool* pool1 =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool1->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
pool1->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
pool1->AddQueue(base::TimeTicks(), GetTaskQueue(second_queue.get()));
CPUTimeBudgetPool* pool2 =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool2->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.01);
pool2->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(second_queue.get()));
timer_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_));
second_queue->GetTaskRunnerWithDefaultTaskType()->PostTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_));
timer_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_));
second_queue->GetTaskRunnerWithDefaultTaskType()->PostTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(26000)));
wake_up_budget_pool_->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(second_queue.get()));
}
namespace {
void RunChainedTask(Deque<base::TimeDelta> task_durations,
scoped_refptr<MainThreadTaskQueue> queue,
scoped_refptr<TestMockTimeTaskRunner> task_runner,
Vector<base::TimeTicks>* run_times,
base::TimeDelta delay) {
if (task_durations.empty())
return;
// FIXME No auto-advancing.
run_times->push_back(RoundTimeToMilliseconds(task_runner->NowTicks()));
task_runner->AdvanceMockTickClock(task_durations.front());
task_durations.pop_front();
queue->GetTaskRunnerWithDefaultTaskType()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&RunChainedTask, std::move(task_durations), queue,
task_runner, run_times, delay),
delay);
}
} // namespace
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
WakeUpBasedThrottling_ChainedTasks_Instantaneous) {
wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&RunChainedTask, MakeTaskDurations(10, base::TimeDelta()),
timer_queue_, test_task_runner_, &run_times,
base::TimeDelta()),
base::TimeDelta::FromMilliseconds(100));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(1),
base::TimeTicks() + base::TimeDelta::FromSeconds(1)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
WakeUpBasedThrottling_ImmediateTasks_Fast) {
wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(
&RunChainedTask,
MakeTaskDurations(10, base::TimeDelta::FromMilliseconds(3)),
timer_queue_, test_task_runner_, &run_times, base::TimeDelta()),
base::TimeDelta::FromMilliseconds(100));
test_task_runner_->FastForwardUntilNoTasksRemain();
// TODO(altimin): Add fence mechanism to block immediate tasks.
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1003),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1006),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1009),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2003),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2006),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2009),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3003)));
}
TEST_P(TaskQueueThrottlerWithAutoAdvancingTimeTest,
WakeUpBasedThrottling_DelayedTasks) {
wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&RunChainedTask, MakeTaskDurations(10, base::TimeDelta()),
timer_queue_, test_task_runner_, &run_times,
base::TimeDelta::FromMilliseconds(3)),
base::TimeDelta::FromMilliseconds(100));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1003),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1006),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1009),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2003),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2006),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2009),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3003)));
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_MultiplePoolsWithDifferentIntervalsOneEmpty) {
// Have |wake_up_budget_pool_| control |task_queue| with a wake-up inteval
// of one-minute.
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
WakeUpBudgetPool* one_minute_pool = wake_up_budget_pool_;
scoped_refptr<base::SingleThreadTaskRunner> one_minute_task_runner =
timer_task_runner_;
// Create another TaskQueue, throttled by another WakeUpBudgetPool with
// a wake-up interval of two minutes.
WakeUpBudgetPool* two_minutes_pool =
task_queue_throttler_->CreateWakeUpBudgetPool(
"Two Minutes Interval Pool");
two_minutes_pool->SetWakeUpDuration(base::TimeDelta());
two_minutes_pool->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(2));
scoped_refptr<MainThreadTaskQueue> two_minutes_queue =
scheduler_->NewTaskQueue(
MainThreadTaskQueue::QueueCreationParams(
MainThreadTaskQueue::QueueType::kFrameThrottleable)
.SetCanBeThrottled(true));
two_minutes_pool->AddQueue(base::TimeTicks(),
GetTaskQueue(two_minutes_queue.get()));
scoped_refptr<base::SingleThreadTaskRunner> two_minutes_task_runner =
two_minutes_queue->GetTaskRunnerWithDefaultTaskType();
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(two_minutes_queue.get()));
// Post a task with a short delay to the first queue.
constexpr base::TimeDelta kShortDelay = base::TimeDelta::FromSeconds(1);
Vector<base::TimeTicks> run_times;
one_minute_task_runner->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
kShortDelay);
// Pools did not observe wake ups yet whether they had pending tasks or not.
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(), base::nullopt);
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(), base::nullopt);
// The first task should run after 1 minute, which is the wake up interval of
// |one_minute_pool|.
test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(1));
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
base::TimeTicks() + base::TimeDelta::FromMinutes(1));
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1)));
// The second pool should not have woken up since it had no tasks.
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(), base::nullopt);
// No new task execution or wake-ups for the first queue since it did not
// get new tasks executed.
test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(1));
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
base::TimeTicks() + base::TimeDelta::FromMinutes(1));
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1)));
// The second pool should not have woken up since it had no tasks.
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(), base::nullopt);
// Still no new executions so no update on the wake-up for the queues.
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
base::TimeTicks() + base::TimeDelta::FromMinutes(1));
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(), base::nullopt);
// Clean up.
two_minutes_pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(two_minutes_queue.get()));
two_minutes_pool->Close();
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_MultiplePoolsWithDifferentIntervals) {
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
WakeUpBudgetPool* one_minute_pool = wake_up_budget_pool_;
scoped_refptr<base::SingleThreadTaskRunner> one_minute_task_runner =
timer_task_runner_;
// Create another TaskQueue, throttled by another WakeUpBudgetPool.
WakeUpBudgetPool* two_minutes_pool =
task_queue_throttler_->CreateWakeUpBudgetPool(
"Two Minutes Interval Pool");
two_minutes_pool->SetWakeUpDuration(base::TimeDelta());
two_minutes_pool->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(2));
scoped_refptr<MainThreadTaskQueue> two_minutes_queue =
scheduler_->NewTaskQueue(
MainThreadTaskQueue::QueueCreationParams(
MainThreadTaskQueue::QueueType::kFrameThrottleable)
.SetCanBeThrottled(true));
two_minutes_pool->AddQueue(base::TimeTicks(),
GetTaskQueue(two_minutes_queue.get()));
scoped_refptr<base::SingleThreadTaskRunner> two_minutes_task_runner =
two_minutes_queue->GetTaskRunnerWithDefaultTaskType();
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(two_minutes_queue.get()));
// Post tasks with a short delay to both queues.
constexpr base::TimeDelta kShortDelay = base::TimeDelta::FromSeconds(1);
Vector<base::TimeTicks> run_times;
one_minute_task_runner->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
kShortDelay);
two_minutes_task_runner->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
kShortDelay);
// Pools do not observe wake ups yet.
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(), base::nullopt);
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(), base::nullopt);
// The first task should run after 1 minute, which is the wake up interval of
// |one_minute_pool|. The second task should run after 2 minutes, which is the
// wake up interval of |two_minutes_pool|.
test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(1));
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
base::TimeTicks() + base::TimeDelta::FromMinutes(1));
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(), base::nullopt);
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1)));
test_task_runner_->FastForwardBy(base::TimeDelta::FromMinutes(1));
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
base::TimeTicks() + base::TimeDelta::FromMinutes(1));
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
base::TimeTicks() + base::TimeDelta::FromMinutes(2));
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMinutes(1),
base::TimeTicks() + base::TimeDelta::FromMinutes(2)));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
base::TimeTicks() + base::TimeDelta::FromMinutes(1));
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
base::TimeTicks() + base::TimeDelta::FromMinutes(2));
// Clean up.
two_minutes_pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(two_minutes_queue.get()));
two_minutes_pool->Close();
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_MultiplePoolsWithUnalignedWakeUps) {
// Snap the time to the next minute to simplify expectations.
ForwardTimeToNextMinute();
const base::TimeTicks start_time = test_task_runner_->NowTicks();
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
wake_up_budget_pool_->AllowUnalignedWakeUpIfNoRecentWakeUp();
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
WakeUpBudgetPool* one_minute_pool = wake_up_budget_pool_;
scoped_refptr<base::SingleThreadTaskRunner> one_minute_task_runner =
timer_task_runner_;
// Create another TaskQueue, throttled by another WakeUpBudgetPool.
WakeUpBudgetPool* two_minutes_pool =
task_queue_throttler_->CreateWakeUpBudgetPool(
"Two Minutes Interval Pool");
two_minutes_pool->SetWakeUpDuration(base::TimeDelta());
two_minutes_pool->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
two_minutes_pool->AllowUnalignedWakeUpIfNoRecentWakeUp();
scoped_refptr<MainThreadTaskQueue> two_minutes_queue =
scheduler_->NewTaskQueue(
MainThreadTaskQueue::QueueCreationParams(
MainThreadTaskQueue::QueueType::kFrameThrottleable)
.SetCanBeThrottled(true));
two_minutes_pool->AddQueue(base::TimeTicks(),
GetTaskQueue(two_minutes_queue.get()));
scoped_refptr<base::SingleThreadTaskRunner> two_minutes_task_runner =
two_minutes_queue->GetTaskRunnerWithDefaultTaskType();
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(two_minutes_queue.get()));
// Post tasks with short delays to both queues. They should run unaligned. The
// wake up in |one_minute_pool| should not be taken into account when
// evaluating whether there was a recent wake up in
// |two_minutes_pool_|.
Vector<base::TimeTicks> run_times;
one_minute_task_runner->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(2));
two_minutes_task_runner->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(3));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(2));
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(), base::nullopt);
EXPECT_THAT(run_times,
ElementsAre(start_time + base::TimeDelta::FromSeconds(2)));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(2));
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(3));
EXPECT_THAT(run_times,
ElementsAre(start_time + base::TimeDelta::FromSeconds(2),
start_time + base::TimeDelta::FromSeconds(3)));
// Post extra tasks with long unaligned wake ups. They should run unaligned,
// since their desired run time is more than 1 minute after the last wake up
// in their respective pools.
run_times.clear();
one_minute_task_runner->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(602));
two_minutes_task_runner->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(603));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(601));
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(2));
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(3));
EXPECT_THAT(run_times, ElementsAre());
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(605));
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(3));
EXPECT_THAT(run_times,
ElementsAre(start_time + base::TimeDelta::FromSeconds(605)));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(605));
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(606));
EXPECT_THAT(run_times,
ElementsAre(start_time + base::TimeDelta::FromSeconds(605),
start_time + base::TimeDelta::FromSeconds(606)));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(one_minute_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(605));
EXPECT_EQ(two_minutes_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(606));
// Clean up.
two_minutes_pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(two_minutes_queue.get()));
two_minutes_pool->Close();
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_MultiplePoolsWithAllignedAndUnalignedWakeUps) {
// Snap the time to the next minute to simplify expectations.
ForwardTimeToNextMinute();
const base::TimeTicks start_time = test_task_runner_->NowTicks();
// The 1st WakeUpBudgetPool doesn't allow unaligned wake ups.
wake_up_budget_pool_->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
WakeUpBudgetPool* aligned_pool = wake_up_budget_pool_;
scoped_refptr<base::SingleThreadTaskRunner> aligned_task_runner =
timer_task_runner_;
// Create another TaskQueue, throttled by another WakeUpBudgetPool. This 2nd
// WakeUpBudgetPool allows unaligned wake ups.
WakeUpBudgetPool* unaligned_pool =
task_queue_throttler_->CreateWakeUpBudgetPool(
"Other Wake Up Budget Pool");
unaligned_pool->SetWakeUpDuration(base::TimeDelta());
unaligned_pool->SetWakeUpInterval(test_task_runner_->NowTicks(),
base::TimeDelta::FromMinutes(1));
unaligned_pool->AllowUnalignedWakeUpIfNoRecentWakeUp();
scoped_refptr<MainThreadTaskQueue> unaligned_queue = scheduler_->NewTaskQueue(
MainThreadTaskQueue::QueueCreationParams(
MainThreadTaskQueue::QueueType::kFrameThrottleable)
.SetCanBeThrottled(true));
unaligned_pool->AddQueue(base::TimeTicks(),
GetTaskQueue(unaligned_queue.get()));
scoped_refptr<base::SingleThreadTaskRunner> unaligned_task_runner =
unaligned_queue->GetTaskRunnerWithDefaultTaskType();
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(unaligned_queue.get()));
// Post tasks with short delays to both queues. The 1st task should run
// aligned, while the 2nd task should run unaligned.
Vector<base::TimeTicks> run_times;
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(2));
unaligned_task_runner->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(3));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
EXPECT_EQ(aligned_pool->last_wake_up_for_testing(), base::nullopt);
EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(), base::nullopt);
EXPECT_THAT(run_times, ElementsAre());
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
EXPECT_EQ(aligned_pool->last_wake_up_for_testing(), base::nullopt);
EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(3));
EXPECT_THAT(run_times,
ElementsAre(start_time + base::TimeDelta::FromSeconds(3)));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(57));
EXPECT_EQ(aligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromMinutes(1));
EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(3));
EXPECT_THAT(run_times,
ElementsAre(start_time + base::TimeDelta::FromSeconds(3),
start_time + base::TimeDelta::FromMinutes(1)));
// Post extra tasks with long unaligned wake ups. The 1st task should run
// aligned, while the 2nd task should run unaligned.
run_times.clear();
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(602));
unaligned_task_runner->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromSeconds(603));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(601));
EXPECT_EQ(aligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromMinutes(1));
EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(3));
EXPECT_THAT(run_times, ElementsAre());
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(2));
EXPECT_EQ(aligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromMinutes(1));
EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(663));
EXPECT_THAT(run_times,
ElementsAre(start_time + base::TimeDelta::FromSeconds(663)));
test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(117));
EXPECT_EQ(aligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromMinutes(12));
EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(663));
EXPECT_THAT(run_times,
ElementsAre(start_time + base::TimeDelta::FromSeconds(663),
start_time + base::TimeDelta::FromMinutes(12)));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_EQ(aligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromMinutes(12));
EXPECT_EQ(unaligned_pool->last_wake_up_for_testing(),
start_time + base::TimeDelta::FromSeconds(663));
// Clean up.
unaligned_pool->RemoveQueue(test_task_runner_->NowTicks(),
GetTaskQueue(unaligned_queue.get()));
unaligned_pool->Close();
}
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(base::TimeTicks(),
kTimeBetweenWakeUps);
wake_up_budget_pool_->SetWakeUpDuration(base::TimeDelta::FromMilliseconds(1));
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&RunChainedTask, MakeTaskDurations(10, base::TimeDelta()),
timer_queue_, test_task_runner_, &run_times, kDelay),
kDelay);
// Throttling is enabled. Only 1 task runs per |kTimeBetweenWakeUps|.
test_task_runner_->FastForwardBy(kTimeBetweenWakeUps);
EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() +
base::TimeDelta::FromSeconds(60)));
run_times.clear();
// Disable throttling. All tasks can run.
LazyNow lazy_now_1(test_task_runner_->GetMockTickClock());
wake_up_budget_pool_->DisableThrottling(&lazy_now_1);
test_task_runner_->FastForwardBy(5 * kDelay);
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(70),
base::TimeTicks() + base::TimeDelta::FromSeconds(80),
base::TimeTicks() + base::TimeDelta::FromSeconds(90),
base::TimeTicks() + base::TimeDelta::FromSeconds(100),
base::TimeTicks() + base::TimeDelta::FromSeconds(110)));
run_times.clear();
// Throttling is enabled. Only 1 task runs per |kTimeBetweenWakeUps|.
LazyNow lazy_now_2(test_task_runner_->GetMockTickClock());
wake_up_budget_pool_->EnableThrottling(&lazy_now_2);
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromSeconds(120),
base::TimeTicks() + base::TimeDelta::FromSeconds(180),
base::TimeTicks() + base::TimeDelta::FromSeconds(240),
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(
GetTaskQueue(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(
GetTaskQueue(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,
WakeUpBasedThrottling_IncreaseWakeUpIntervalBeforeWakeUp) {
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(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));
// Tasks run after 1 hour, which is the most up to date wake up interval.
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromHours(1),
base::TimeTicks() + base::TimeDelta::FromHours(1)));
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_DecreaseWakeUpIntervalBeforeWakeUp) {
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(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(
GetTaskQueue(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. This should affect the 2nd and 3rd
// tasks, which haven't run yet.
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::FromHours(1),
base::TimeTicks() + base::TimeDelta::FromHours(2)));
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottling_DecreaseWakeUpIntervalDuringWakeUp) {
wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(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));
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
Deque<base::TimeDelta> task_durations =
MakeTaskDurations(9, base::TimeDelta());
task_durations[0] = base::TimeDelta::FromMilliseconds(250);
task_durations[3] = base::TimeDelta::FromMilliseconds(250);
task_durations[6] = base::TimeDelta::FromMilliseconds(250);
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&RunChainedTask, std::move(task_durations), timer_queue_,
test_task_runner_, &run_times, base::TimeDelta()),
base::TimeDelta::FromMilliseconds(100));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(
run_times,
ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(8000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(8000)));
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottlingWithCPUBudgetThrottling_OnAndOff) {
wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.1);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
Vector<base::TimeTicks> run_times;
bool is_throttled = false;
for (int i = 0; i < 5; ++i) {
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&ExpensiveTestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(200));
timer_task_runner_->PostDelayedTask(
FROM_HERE, base::BindOnce(&TestTask, &run_times, test_task_runner_),
base::TimeDelta::FromMilliseconds(300));
if (is_throttled) {
task_queue_throttler_->DecreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
is_throttled = false;
} else {
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
is_throttled = true;
}
test_task_runner_->FastForwardUntilNoTasksRemain();
}
EXPECT_THAT(run_times,
ElementsAre(
// Throttled due to cpu budget.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
// Unthrottled.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3200),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3450),
// Throttled due to wake-up budget. Old tasks still run.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(5000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(5250),
// Unthrottled.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(6200),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(6450),
// Throttled due to wake-up budget. Old tasks still run.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(8000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(8250)));
}
TEST_F(TaskQueueThrottlerTest,
WakeUpBasedThrottlingWithCPUBudgetThrottling_ChainedFastTasks) {
// This test checks that a new task should run during the wake-up window
// when time budget allows that and should be blocked when time budget is
// exhausted.
wake_up_budget_pool_->SetWakeUpDuration(
base::TimeDelta::FromMilliseconds(10));
CPUTimeBudgetPool* pool =
task_queue_throttler_->CreateCPUTimeBudgetPool("test");
pool->SetTimeBudgetRecoveryRate(base::TimeTicks(), 0.01);
pool->AddQueue(base::TimeTicks(), GetTaskQueue(timer_queue_.get()));
Vector<base::TimeTicks> run_times;
task_queue_throttler_->IncreaseThrottleRefCount(
GetTaskQueue(timer_queue_.get()));
timer_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(
&RunChainedTask,
MakeTaskDurations(10, base::TimeDelta::FromMilliseconds(7)),
timer_queue_, test_task_runner_, &run_times, base::TimeDelta()),
base::TimeDelta::FromMilliseconds(100));
test_task_runner_->FastForwardUntilNoTasksRemain();
EXPECT_THAT(run_times,
ElementsAre(
// Time budget is ~10ms and we can run two 7ms tasks.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(1007),
// Time budget is ~6ms and we can run one 7ms task.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000),
// Time budget is ~8ms and we can run two 7ms tasks.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(3007),
// Time budget is ~5ms and we can run one 7ms task.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(4000),
// Time budget is ~8ms and we can run two 7ms tasks.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(5000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(5007),
// Time budget is ~4ms and we can run one 7ms task.
base::TimeTicks() + base::TimeDelta::FromMilliseconds(6000),
base::TimeTicks() + base::TimeDelta::FromMilliseconds(7000)));
}
} // namespace task_queue_throttler_unittest
} // namespace scheduler
} // namespace blink