Commit 143bfac3 authored by rmcilroy's avatar rmcilroy Committed by Commit bot

[content]: Add support for long idle times in the Blink Scheduler.

Adds support for long idle times in the Blink Scheduler.
A long idle time is initiated
if there are no frames being drawn and we are not in TOUCHSTART policy. Each
long idle period will be a maximum of 50 milliseconds or the time to the next
pending delayed task (whichever is sooner). Long idle periods will continually
be scheduled until a call to EndIdlePeriod is made. If there are no pending
tasks in the idle queue then the callback used to initate the next long idle
task will be posted on the control_task_after_wakeup_runner_ task runner, thus
ensuring that it won't cause the scheduler to wake up until another task
could have posted more idle work.

Currently, the long idle tasks are not enabled due to crbug.com/467655 -
once this is fixed they will be enabled.

Long idle task design doc:
https://docs.google.com/a/chromium.org/document/d/1yBlUdYW8VTIfB-DqhvQqUeP0kf-Ap1W4cao2yQq58Do/edit

BUG=455713,467655

Review URL: https://codereview.chromium.org/968073003

Cr-Commit-Position: refs/heads/master@{#320785}
parent 027195d0
...@@ -52,6 +52,8 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler { ...@@ -52,6 +52,8 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler {
void SetTimeSourceForTesting(scoped_refptr<cc::TestNowSource> time_source); void SetTimeSourceForTesting(scoped_refptr<cc::TestNowSource> time_source);
void SetWorkBatchSizeForTesting(size_t work_batch_size); void SetWorkBatchSizeForTesting(size_t work_batch_size);
// TODO(rmcilroy): Remove this when http://crbug.com/467655 is fixed.
void SetLongIdlePeriodsEnabledForTesting(bool long_idle_periods_enabled);
private: private:
friend class RendererSchedulerImplTest; friend class RendererSchedulerImplTest;
...@@ -69,18 +71,30 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler { ...@@ -69,18 +71,30 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler {
TASK_QUEUE_COUNT, TASK_QUEUE_COUNT,
}; };
// Keep RendererSchedulerImpl::PolicyToString in sync with this enum.
enum class Policy { enum class Policy {
NORMAL, NORMAL,
COMPOSITOR_PRIORITY, COMPOSITOR_PRIORITY,
TOUCHSTART_PRIORITY, TOUCHSTART_PRIORITY,
}; };
// Keep RendererSchedulerImpl::InputStreamStateToString in sync with this
// enum.
enum class InputStreamState { enum class InputStreamState {
INACTIVE, INACTIVE,
ACTIVE, ACTIVE,
ACTIVE_AND_AWAITING_TOUCHSTART_RESPONSE ACTIVE_AND_AWAITING_TOUCHSTART_RESPONSE
}; };
// Keep RendererSchedulerImpl::IdlePeriodStateToString in sync with this enum.
enum class IdlePeriodState {
NOT_IN_IDLE_PERIOD,
IN_SHORT_IDLE_PERIOD,
IN_LONG_IDLE_PERIOD,
IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE,
ENDING_LONG_IDLE_PERIOD
};
class PollableNeedsUpdateFlag { class PollableNeedsUpdateFlag {
public: public:
PollableNeedsUpdateFlag(base::Lock* write_lock); PollableNeedsUpdateFlag(base::Lock* write_lock);
...@@ -105,6 +119,7 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler { ...@@ -105,6 +119,7 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler {
static const char* TaskQueueIdToString(QueueId queue_id); static const char* TaskQueueIdToString(QueueId queue_id);
static const char* PolicyToString(Policy policy); static const char* PolicyToString(Policy policy);
static const char* InputStreamStateToString(InputStreamState state); static const char* InputStreamStateToString(InputStreamState state);
static const char* IdlePeriodStateToString(IdlePeriodState state);
static InputStreamState ComputeNewInputStreamState( static InputStreamState ComputeNewInputStreamState(
InputStreamState current_state, InputStreamState current_state,
...@@ -114,6 +129,12 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler { ...@@ -114,6 +129,12 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler {
// The time we should stay in a priority-escalated mode after an input event. // The time we should stay in a priority-escalated mode after an input event.
static const int kPriorityEscalationAfterInputMillis = 100; static const int kPriorityEscalationAfterInputMillis = 100;
// The maximum length of an idle period.
static const int kMaximumIdlePeriodMillis = 50;
// The minimum delay to wait between retrying to initiate a long idle time.
static const int kRetryInitiateLongIdlePeriodDelayMillis = 1;
// IdleTaskDeadlineSupplier Implementation: // IdleTaskDeadlineSupplier Implementation:
void CurrentIdleTaskDeadlineCallback(base::TimeTicks* deadline_out) const; void CurrentIdleTaskDeadlineCallback(base::TimeTicks* deadline_out) const;
...@@ -135,6 +156,10 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler { ...@@ -135,6 +156,10 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler {
void UpdatePolicy(); void UpdatePolicy();
virtual void UpdatePolicyLocked(); virtual void UpdatePolicyLocked();
// Returns the amount of time left in the current input escalated priority
// policy.
base::TimeDelta TimeLeftInInputEscalatedPolicy(base::TimeTicks now) const;
// Helper for computing the new policy. |new_policy_duration| will be filled // Helper for computing the new policy. |new_policy_duration| will be filled
// with the amount of time after which the policy should be updated again. If // with the amount of time after which the policy should be updated again. If
// the duration is zero, a new policy update will not be scheduled. Must be // the duration is zero, a new policy update will not be scheduled. Must be
...@@ -150,10 +175,24 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler { ...@@ -150,10 +175,24 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler {
// input was processed. // input was processed.
void DidProcessInputEvent(base::TimeTicks begin_frame_time); void DidProcessInputEvent(base::TimeTicks begin_frame_time);
// Returns the new idle period state for the next long idle period. Fills in
// |next_long_idle_period_delay_out| with the next time we should try to
// initiate the next idle period.
IdlePeriodState ComputeNewLongIdlePeriodState(
const base::TimeTicks now,
base::TimeDelta* next_long_idle_period_delay_out);
// Initiate a long idle period.
void InitiateLongIdlePeriod();
void InitiateLongIdlePeriodAfterWakeup();
// Start and end an idle period. // Start and end an idle period.
void StartIdlePeriod(); void StartIdlePeriod(IdlePeriodState new_idle_period_state);
void EndIdlePeriod(); void EndIdlePeriod();
// Returns true if |state| represents being within an idle period state.
static bool IsInIdlePeriod(IdlePeriodState state);
base::TimeTicks Now() const; base::TimeTicks Now() const;
base::ThreadChecker main_thread_checker_; base::ThreadChecker main_thread_checker_;
...@@ -169,11 +208,18 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler { ...@@ -169,11 +208,18 @@ class CONTENT_EXPORT RendererSchedulerImpl : public RendererScheduler {
base::Closure update_policy_closure_; base::Closure update_policy_closure_;
DeadlineTaskRunner delayed_update_policy_runner_; DeadlineTaskRunner delayed_update_policy_runner_;
CancelableClosureHolder end_idle_period_closure_; CancelableClosureHolder end_idle_period_closure_;
CancelableClosureHolder initiate_next_long_idle_period_closure_;
CancelableClosureHolder initiate_next_long_idle_period_after_wakeup_closure_;
// Don't access current_policy_ directly, instead use SchedulerPolicy(). // Don't access current_policy_ directly, instead use SchedulerPolicy().
Policy current_policy_; Policy current_policy_;
IdlePeriodState idle_period_state_;
// TODO(rmcilroy): Remove this when http://crbug.com/467655 is fixed.
bool long_idle_periods_enabled_;
base::TimeTicks estimated_next_frame_begin_; base::TimeTicks estimated_next_frame_begin_;
base::TimeTicks current_policy_expiration_time_;
// The incoming_signals_lock_ mutex protects access to all variables in the // The incoming_signals_lock_ mutex protects access to all variables in the
// (contiguous) block below. // (contiguous) block below.
......
...@@ -57,6 +57,7 @@ class RendererSchedulerImplTest : public testing::Test { ...@@ -57,6 +57,7 @@ class RendererSchedulerImplTest : public testing::Test {
loading_task_runner_(scheduler_->LoadingTaskRunner()), loading_task_runner_(scheduler_->LoadingTaskRunner()),
idle_task_runner_(scheduler_->IdleTaskRunner()) { idle_task_runner_(scheduler_->IdleTaskRunner()) {
scheduler_->SetTimeSourceForTesting(clock_); scheduler_->SetTimeSourceForTesting(clock_);
scheduler_->SetLongIdlePeriodsEnabledForTesting(true);
} }
RendererSchedulerImplTest(base::MessageLoop* message_loop) RendererSchedulerImplTest(base::MessageLoop* message_loop)
...@@ -70,6 +71,7 @@ class RendererSchedulerImplTest : public testing::Test { ...@@ -70,6 +71,7 @@ class RendererSchedulerImplTest : public testing::Test {
loading_task_runner_(scheduler_->LoadingTaskRunner()), loading_task_runner_(scheduler_->LoadingTaskRunner()),
idle_task_runner_(scheduler_->IdleTaskRunner()) { idle_task_runner_(scheduler_->IdleTaskRunner()) {
scheduler_->SetTimeSourceForTesting(clock_); scheduler_->SetTimeSourceForTesting(clock_);
scheduler_->SetLongIdlePeriodsEnabledForTesting(true);
} }
~RendererSchedulerImplTest() override {} ~RendererSchedulerImplTest() override {}
...@@ -158,6 +160,17 @@ class RendererSchedulerImplTest : public testing::Test { ...@@ -158,6 +160,17 @@ class RendererSchedulerImplTest : public testing::Test {
RendererSchedulerImpl::kPriorityEscalationAfterInputMillis); RendererSchedulerImpl::kPriorityEscalationAfterInputMillis);
} }
static base::TimeDelta maximum_idle_period_duration() {
return base::TimeDelta::FromMilliseconds(
RendererSchedulerImpl::kMaximumIdlePeriodMillis);
}
base::TimeTicks CurrentIdleTaskDeadline() {
base::TimeTicks deadline;
scheduler_->CurrentIdleTaskDeadlineCallback(&deadline);
return deadline;
}
scoped_refptr<cc::TestNowSource> clock_; scoped_refptr<cc::TestNowSource> clock_;
// Only one of mock_task_runner_ or message_loop_ will be set. // Only one of mock_task_runner_ or message_loop_ will be set.
scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_; scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
...@@ -496,7 +509,7 @@ TEST_F(RendererSchedulerImplTest, TestTouchstartPolicy) { ...@@ -496,7 +509,7 @@ TEST_F(RendererSchedulerImplTest, TestTouchstartPolicy) {
std::vector<std::string> run_order; std::vector<std::string> run_order;
PostTestTasks(&run_order, "L1 D1 C1 D2 C2"); PostTestTasks(&run_order, "L1 D1 C1 D2 C2");
// Observation of touchstart should defer execution of idle and loading tasks. // Observation of touchstart should defer execution of loading tasks.
scheduler_->DidReceiveInputEventOnCompositorThread( scheduler_->DidReceiveInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchStart)); FakeInputEvent(blink::WebInputEvent::TouchStart));
RunUntilIdle(); RunUntilIdle();
...@@ -1086,4 +1099,151 @@ TEST_F(RendererSchedulerImplWithMessageLoopTest, ...@@ -1086,4 +1099,151 @@ TEST_F(RendererSchedulerImplWithMessageLoopTest,
std::string("3"))); std::string("3")));
} }
TEST_F(RendererSchedulerImplTest, TestLongIdlePeriod) {
base::TimeTicks expected_deadline =
clock_->Now() + maximum_idle_period_duration();
base::TimeTicks deadline_in_task;
int run_count = 0;
idle_task_runner_->PostIdleTask(
FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task));
RunUntilIdle();
EXPECT_EQ(0, run_count); // Shouldn't run yet as no idle period.
scheduler_->BeginFrameNotExpectedSoon();
RunUntilIdle();
EXPECT_EQ(1, run_count); // Should have run in a long idle time.
EXPECT_EQ(expected_deadline, deadline_in_task);
}
TEST_F(RendererSchedulerImplTest, TestLongIdlePeriodWithPendingDelayedTask) {
base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(30);
base::TimeTicks expected_deadline = clock_->Now() + pending_task_delay;
base::TimeTicks deadline_in_task;
int run_count = 0;
idle_task_runner_->PostIdleTask(
FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task));
default_task_runner_->PostDelayedTask(
FROM_HERE, base::Bind(&NullTask), pending_task_delay);
scheduler_->BeginFrameNotExpectedSoon();
RunUntilIdle();
EXPECT_EQ(1, run_count); // Should have run in a long idle time.
EXPECT_EQ(expected_deadline, deadline_in_task);
}
TEST_F(RendererSchedulerImplTest,
TestLongIdlePeriodWithLatePendingDelayedTask) {
base::TimeDelta pending_task_delay = base::TimeDelta::FromMilliseconds(10);
base::TimeTicks deadline_in_task;
int run_count = 0;
default_task_runner_->PostDelayedTask(
FROM_HERE, base::Bind(&NullTask), pending_task_delay);
// Advance clock until after delayed task was meant to be run.
clock_->AdvanceNow(base::TimeDelta::FromMilliseconds(20));
// Post an idle task and BeginFrameNotExpectedSoon to initiate a long idle
// period. Since there is a late pending delayed task this shouldn't actually
// start an idle period.
idle_task_runner_->PostIdleTask(
FROM_HERE, base::Bind(&IdleTestTask, &run_count, &deadline_in_task));
scheduler_->BeginFrameNotExpectedSoon();
RunUntilIdle();
EXPECT_EQ(0, run_count);
// After the delayed task has been run we should trigger an idle period.
clock_->AdvanceNow(maximum_idle_period_duration());
RunUntilIdle();
EXPECT_EQ(1, run_count);
}
TEST_F(RendererSchedulerImplTest, TestLongIdlePeriodRepeating) {
int run_count = 0;
idle_task_runner_->PostIdleTask(
FROM_HERE,
base::Bind(&RepostingIdleTestTask, idle_task_runner_, &run_count));
scheduler_->BeginFrameNotExpectedSoon();
RunUntilIdle();
EXPECT_EQ(1, run_count); // Should only run once per idle period.
// Advance time to start of next long idle period and check task reposted task
// gets run.
clock_->AdvanceNow(maximum_idle_period_duration());
RunUntilIdle();
EXPECT_EQ(2, run_count);
// Advance time to start of next long idle period then end idle period with a
// new BeginMainFrame and check idle task doesn't run.
clock_->AdvanceNow(maximum_idle_period_duration());
scheduler_->WillBeginFrame(cc::BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, clock_->Now(), base::TimeTicks(),
base::TimeDelta::FromMilliseconds(1000), cc::BeginFrameArgs::NORMAL));
RunUntilIdle();
EXPECT_EQ(2, run_count);
}
TEST_F(RendererSchedulerImplTest, TestLongIdlePeriodDoesNotWakeScheduler) {
base::TimeTicks deadline_in_task;
int run_count = 0;
// Start a long idle period and get the time it should end.
scheduler_->BeginFrameNotExpectedSoon();
// The scheduler should not run the initiate_next_long_idle_period task if
// there are no idle tasks and no other task woke up the scheduler, thus
// the idle period deadline shouldn't update at the end of the current long
// idle period.
base::TimeTicks idle_period_deadline = CurrentIdleTaskDeadline();
clock_->AdvanceNow(maximum_idle_period_duration());
RunUntilIdle();
base::TimeTicks new_idle_period_deadline = CurrentIdleTaskDeadline();
EXPECT_EQ(idle_period_deadline, new_idle_period_deadline);
// Posting a after-wakeup idle task also shouldn't wake the scheduler or
// initiate the next long idle period.
idle_task_runner_->PostIdleTaskAfterWakeup(
FROM_HERE,
base::Bind(&IdleTestTask, &run_count, &deadline_in_task));
RunUntilIdle();
new_idle_period_deadline = CurrentIdleTaskDeadline();
EXPECT_EQ(idle_period_deadline, new_idle_period_deadline);
EXPECT_EQ(0, run_count);
// Running a normal task should initiate a new long idle period though.
default_task_runner_->PostTask(FROM_HERE, base::Bind(&NullTask));
RunUntilIdle();
new_idle_period_deadline = CurrentIdleTaskDeadline();
EXPECT_EQ(idle_period_deadline + maximum_idle_period_duration(),
new_idle_period_deadline);
EXPECT_EQ(1, run_count);
}
TEST_F(RendererSchedulerImplTest, TestLongIdlePeriodInTouchStartPolicy) {
base::TimeTicks deadline_in_task;
int run_count = 0;
idle_task_runner_->PostIdleTask(
FROM_HERE,
base::Bind(&IdleTestTask, &run_count, &deadline_in_task));
// Observation of touchstart should defer the start of the long idle period.
scheduler_->DidReceiveInputEventOnCompositorThread(
FakeInputEvent(blink::WebInputEvent::TouchStart));
scheduler_->BeginFrameNotExpectedSoon();
RunUntilIdle();
EXPECT_EQ(0, run_count);
// The long idle period should start after the touchstart policy has finished.
clock_->AdvanceNow(priority_escalation_after_input_duration());
RunUntilIdle();
EXPECT_EQ(1, run_count);
}
} // namespace content } // namespace content
...@@ -43,6 +43,9 @@ class TaskQueue : public base::SingleThreadTaskRunner { ...@@ -43,6 +43,9 @@ class TaskQueue : public base::SingleThreadTaskRunner {
void SetPumpPolicy(TaskQueueManager::PumpPolicy pump_policy); void SetPumpPolicy(TaskQueueManager::PumpPolicy pump_policy);
void PumpQueue(); void PumpQueue();
bool NextPendingDelayedTaskRunTime(
base::TimeTicks* next_pending_delayed_task);
bool UpdateWorkQueue(base::TimeTicks* next_pending_delayed_task, bool UpdateWorkQueue(base::TimeTicks* next_pending_delayed_task,
const base::PendingTask* previous_task); const base::PendingTask* previous_task);
base::PendingTask TakeTaskFromWorkQueue(); base::PendingTask TakeTaskFromWorkQueue();
...@@ -77,6 +80,8 @@ class TaskQueue : public base::SingleThreadTaskRunner { ...@@ -77,6 +80,8 @@ class TaskQueue : public base::SingleThreadTaskRunner {
bool TaskIsOlderThanQueuedTasks(const base::PendingTask* task); bool TaskIsOlderThanQueuedTasks(const base::PendingTask* task);
bool ShouldAutoPumpQueueLocked(const base::PendingTask* previous_task); bool ShouldAutoPumpQueueLocked(const base::PendingTask* previous_task);
void EnqueueTaskLocked(const base::PendingTask& pending_task); void EnqueueTaskLocked(const base::PendingTask& pending_task);
bool NextPendingDelayedTaskRunTimeLocked(
base::TimeTicks* next_pending_delayed_task);
void TraceQueueSize(bool is_locked) const; void TraceQueueSize(bool is_locked) const;
static const char* PumpPolicyToString( static const char* PumpPolicyToString(
...@@ -189,6 +194,23 @@ bool TaskQueue::ShouldAutoPumpQueueLocked( ...@@ -189,6 +194,23 @@ bool TaskQueue::ShouldAutoPumpQueueLocked(
return true; return true;
} }
bool TaskQueue::NextPendingDelayedTaskRunTime(
base::TimeTicks* next_pending_delayed_task) {
base::AutoLock lock(lock_);
return NextPendingDelayedTaskRunTimeLocked(next_pending_delayed_task);
}
bool TaskQueue::NextPendingDelayedTaskRunTimeLocked(
base::TimeTicks* next_pending_delayed_task) {
lock_.AssertAcquired();
if (!delayed_task_run_times_.empty()) {
*next_pending_delayed_task =
std::min(*next_pending_delayed_task, delayed_task_run_times_.top());
return true;
}
return false;
}
bool TaskQueue::UpdateWorkQueue( bool TaskQueue::UpdateWorkQueue(
base::TimeTicks* next_pending_delayed_task, base::TimeTicks* next_pending_delayed_task,
const base::PendingTask* previous_task) { const base::PendingTask* previous_task) {
...@@ -197,10 +219,7 @@ bool TaskQueue::UpdateWorkQueue( ...@@ -197,10 +219,7 @@ bool TaskQueue::UpdateWorkQueue(
{ {
base::AutoLock lock(lock_); base::AutoLock lock(lock_);
if (!delayed_task_run_times_.empty()) { NextPendingDelayedTaskRunTimeLocked(next_pending_delayed_task);
*next_pending_delayed_task =
std::min(*next_pending_delayed_task, delayed_task_run_times_.top());
}
if (!ShouldAutoPumpQueueLocked(previous_task)) if (!ShouldAutoPumpQueueLocked(previous_task))
return false; return false;
work_queue_.Swap(&incoming_queue_); work_queue_.Swap(&incoming_queue_);
...@@ -391,6 +410,24 @@ bool TaskQueueManager::IsQueueEmpty(size_t queue_index) const { ...@@ -391,6 +410,24 @@ bool TaskQueueManager::IsQueueEmpty(size_t queue_index) const {
return queue->IsQueueEmpty(); return queue->IsQueueEmpty();
} }
base::TimeTicks TaskQueueManager::NextPendingDelayedTaskRunTime() {
DCHECK(main_thread_checker_.CalledOnValidThread());
bool found_pending_task = false;
base::TimeTicks next_pending_delayed_task(
base::TimeTicks::FromInternalValue(kMaxTimeTicks));
for (auto& queue : queues_) {
found_pending_task |=
queue->NextPendingDelayedTaskRunTime(&next_pending_delayed_task);
}
if (!found_pending_task)
return base::TimeTicks();
DCHECK_NE(next_pending_delayed_task,
base::TimeTicks::FromInternalValue(kMaxTimeTicks));
return next_pending_delayed_task;
}
void TaskQueueManager::SetPumpPolicy(size_t queue_index, void TaskQueueManager::SetPumpPolicy(size_t queue_index,
PumpPolicy pump_policy) { PumpPolicy pump_policy) {
DCHECK(main_thread_checker_.CalledOnValidThread()); DCHECK(main_thread_checker_.CalledOnValidThread());
......
...@@ -98,6 +98,10 @@ class CONTENT_EXPORT TaskQueueManager { ...@@ -98,6 +98,10 @@ class CONTENT_EXPORT TaskQueueManager {
// lock, so calling it has some overhead. // lock, so calling it has some overhead.
bool IsQueueEmpty(size_t queue_index) const; bool IsQueueEmpty(size_t queue_index) const;
// Returns the time of the next pending delayed task in any queue. Returns
// a null TimeTicks object if no tasks are pending.
base::TimeTicks NextPendingDelayedTaskRunTime();
// Set the name |queue_index| for tracing purposes. |name| must be a pointer // Set the name |queue_index| for tracing purposes. |name| must be a pointer
// to a static string. // to a static string.
void SetQueueName(size_t queue_index, const char* name); void SetQueueName(size_t queue_index, const char* name);
......
...@@ -840,6 +840,54 @@ TEST_F(TaskQueueManagerTest, ThreadCheckAfterTermination) { ...@@ -840,6 +840,54 @@ TEST_F(TaskQueueManagerTest, ThreadCheckAfterTermination) {
EXPECT_TRUE(runner->RunsTasksOnCurrentThread()); EXPECT_TRUE(runner->RunsTasksOnCurrentThread());
} }
TEST_F(TaskQueueManagerTest, NextPendingDelayedTaskRunTime) {
scoped_refptr<cc::TestNowSource> clock(cc::TestNowSource::Create());
Initialize(2u);
manager_->SetTimeSourceForTesting(clock);
scoped_refptr<base::SingleThreadTaskRunner> runners[2] = {
manager_->TaskRunnerForQueue(0), manager_->TaskRunnerForQueue(1)};
// With no delayed tasks.
EXPECT_TRUE(manager_->NextPendingDelayedTaskRunTime().is_null());
// With a non-delayed task.
runners[0]->PostTask(FROM_HERE, base::Bind(&NullTestTask));
EXPECT_TRUE(manager_->NextPendingDelayedTaskRunTime().is_null());
// With a delayed task.
base::TimeDelta expected_delay = base::TimeDelta::FromMilliseconds(50);
runners[0]->PostDelayedTask(
FROM_HERE, base::Bind(&NullTestTask), expected_delay);
EXPECT_EQ(clock->Now() + expected_delay,
manager_->NextPendingDelayedTaskRunTime());
// With another delayed task in the same queue with a longer delay.
runners[0]->PostDelayedTask(
FROM_HERE, base::Bind(&NullTestTask),
base::TimeDelta::FromMilliseconds(100));
EXPECT_EQ(clock->Now() + expected_delay,
manager_->NextPendingDelayedTaskRunTime());
// With another delayed task in the same queue with a shorter delay.
expected_delay = base::TimeDelta::FromMilliseconds(20);
runners[0]->PostDelayedTask(
FROM_HERE, base::Bind(&NullTestTask), expected_delay);
EXPECT_EQ(clock->Now() + expected_delay,
manager_->NextPendingDelayedTaskRunTime());
// With another delayed task in a different queue with a shorter delay.
expected_delay = base::TimeDelta::FromMilliseconds(10);
runners[1]->PostDelayedTask(
FROM_HERE, base::Bind(&NullTestTask), expected_delay);
EXPECT_EQ(clock->Now() + expected_delay,
manager_->NextPendingDelayedTaskRunTime());
// Test it updates as time progresses
clock->AdvanceNow(expected_delay);
EXPECT_EQ(clock->Now(), manager_->NextPendingDelayedTaskRunTime());
}
} // namespace } // namespace
} // namespace content } // namespace content
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