| // Copyright 2014 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 "platform/scheduler/base/task_queue_manager.h" |
| |
| #include <stddef.h> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/location.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/test/trace_event_analyzer.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/trace_event/blame_context.h" |
| #include "base/trace_event/trace_buffer.h" |
| #include "components/viz/test/ordered_simple_task_runner.h" |
| #include "platform/scheduler/base/real_time_domain.h" |
| #include "platform/scheduler/base/task_queue_impl.h" |
| #include "platform/scheduler/base/task_queue_manager_delegate_for_test.h" |
| #include "platform/scheduler/base/task_queue_selector.h" |
| #include "platform/scheduler/base/test_count_uses_time_source.h" |
| #include "platform/scheduler/base/test_task_time_observer.h" |
| #include "platform/scheduler/base/test_time_source.h" |
| #include "platform/scheduler/base/virtual_time_domain.h" |
| #include "platform/scheduler/base/work_queue.h" |
| #include "platform/scheduler/base/work_queue_sets.h" |
| #include "platform/scheduler/test/test_task_queue.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| |
| using ::testing::AnyNumber; |
| using ::testing::Contains; |
| using ::testing::ElementsAre; |
| using ::testing::ElementsAreArray; |
| using ::testing::Mock; |
| using ::testing::Not; |
| using ::testing::_; |
| using blink::scheduler::internal::EnqueueOrder; |
| |
| namespace blink { |
| namespace scheduler { |
| // To avoid symbol collisions in jumbo builds. |
| namespace task_queue_manager_unittest { |
| |
| class TaskQueueManagerForTest : public TaskQueueManager { |
| public: |
| explicit TaskQueueManagerForTest( |
| scoped_refptr<TaskQueueManagerDelegate> delegate) |
| : TaskQueueManager(delegate) {} |
| |
| using TaskQueueManager::NextTaskDelay; |
| using TaskQueueManager::ActiveQueuesCount; |
| using TaskQueueManager::QueuesToShutdownCount; |
| using TaskQueueManager::QueuesToDeleteCount; |
| }; |
| |
| class MessageLoopTaskRunner : public TaskQueueManagerDelegateForTest { |
| public: |
| static scoped_refptr<MessageLoopTaskRunner> Create( |
| std::unique_ptr<base::TickClock> tick_clock) { |
| return base::WrapRefCounted( |
| new MessageLoopTaskRunner(std::move(tick_clock))); |
| } |
| |
| // TaskQueueManagerDelegateForTest: |
| bool IsNested() const override { |
| DCHECK(RunsTasksInCurrentSequence()); |
| return base::RunLoop::IsNestedOnCurrentThread(); |
| } |
| |
| void AddNestingObserver(base::RunLoop::NestingObserver* observer) override { |
| base::RunLoop::AddNestingObserverOnCurrentThread(observer); |
| } |
| |
| void RemoveNestingObserver( |
| base::RunLoop::NestingObserver* observer) override { |
| base::RunLoop::RemoveNestingObserverOnCurrentThread(observer); |
| } |
| |
| private: |
| explicit MessageLoopTaskRunner(std::unique_ptr<base::TickClock> tick_clock) |
| : TaskQueueManagerDelegateForTest(base::ThreadTaskRunnerHandle::Get(), |
| std::move(tick_clock)) {} |
| ~MessageLoopTaskRunner() override {} |
| }; |
| |
| class TaskQueueManagerTest : public ::testing::Test { |
| public: |
| TaskQueueManagerTest() {} |
| void DeleteTaskQueueManager() { manager_.reset(); } |
| |
| protected: |
| void TearDown() { manager_.reset(); } |
| |
| scoped_refptr<TestTaskQueue> CreateTaskQueueWithSpec(TaskQueue::Spec spec) { |
| return manager_->CreateTaskQueue<TestTaskQueue>(spec); |
| } |
| |
| scoped_refptr<TestTaskQueue> CreateTaskQueue() { |
| return CreateTaskQueueWithSpec(TaskQueue::Spec("test")); |
| } |
| |
| scoped_refptr<TestTaskQueue> CreateTaskQueueWithMonitoredQuiescence() { |
| return CreateTaskQueueWithSpec( |
| TaskQueue::Spec("test").SetShouldMonitorQuiescence(true)); |
| } |
| |
| void InitializeWithClock(size_t num_queues, |
| std::unique_ptr<base::TickClock> test_time_source) { |
| test_task_runner_ = base::WrapRefCounted( |
| new cc::OrderedSimpleTaskRunner(now_src_.get(), false)); |
| main_task_runner_ = TaskQueueManagerDelegateForTest::Create( |
| test_task_runner_.get(), |
| std::make_unique<TestTimeSource>(now_src_.get())); |
| |
| manager_ = std::make_unique<TaskQueueManagerForTest>(main_task_runner_); |
| |
| for (size_t i = 0; i < num_queues; i++) |
| runners_.push_back(CreateTaskQueue()); |
| } |
| |
| void Initialize(size_t num_queues) { |
| now_src_.reset(new base::SimpleTestTickClock()); |
| now_src_->Advance(base::TimeDelta::FromMicroseconds(1000)); |
| InitializeWithClock(num_queues, |
| std::make_unique<TestTimeSource>(now_src_.get())); |
| } |
| |
| void InitializeWithRealMessageLoop(size_t num_queues) { |
| now_src_.reset(new base::SimpleTestTickClock()); |
| message_loop_.reset(new base::MessageLoop()); |
| // A null clock triggers some assertions. |
| now_src_->Advance(base::TimeDelta::FromMicroseconds(1000)); |
| manager_ = |
| std::make_unique<TaskQueueManagerForTest>(MessageLoopTaskRunner::Create( |
| base::WrapUnique(new TestTimeSource(now_src_.get())))); |
| |
| for (size_t i = 0; i < num_queues; i++) |
| runners_.push_back(CreateTaskQueue()); |
| } |
| |
| void WakeUpReadyDelayedQueues(LazyNow lazy_now) { |
| manager_->WakeUpReadyDelayedQueues(&lazy_now); |
| } |
| |
| using NextTaskDelay = TaskQueueManagerForTest::NextTaskDelay; |
| |
| base::Optional<NextTaskDelay> ComputeDelayTillNextTask(LazyNow* lazy_now) { |
| base::AutoLock lock(manager_->any_thread_lock_); |
| return manager_->ComputeDelayTillNextTaskLocked(lazy_now); |
| } |
| |
| void PostDoWorkContinuation(base::Optional<NextTaskDelay> next_delay, |
| LazyNow* lazy_now) { |
| MoveableAutoLock lock(manager_->any_thread_lock_); |
| return manager_->PostDoWorkContinuationLocked(next_delay, lazy_now, |
| std::move(lock)); |
| } |
| |
| int immediate_do_work_posted_count() const { |
| base::AutoLock lock(manager_->any_thread_lock_); |
| return manager_->any_thread().immediate_do_work_posted_count; |
| } |
| |
| base::TimeTicks next_delayed_do_work_time() const { |
| return manager_->next_delayed_do_work_.run_time(); |
| } |
| |
| EnqueueOrder GetNextSequenceNumber() const { |
| return manager_->GetNextSequenceNumber(); |
| } |
| |
| void MaybeScheduleImmediateWorkLocked(const base::Location& from_here) { |
| MoveableAutoLock lock(manager_->any_thread_lock_); |
| manager_->MaybeScheduleImmediateWorkLocked(from_here, std::move(lock)); |
| } |
| |
| // Runs all immediate tasks until there is no more work to do and advances |
| // time if there is a pending delayed task. |per_run_time_callback| is called |
| // when the clock advances. |
| void RunUntilIdle(base::Closure per_run_time_callback) { |
| for (;;) { |
| // Advance time if we've run out of immediate work to do. |
| if (manager_->selector_.EnabledWorkQueuesEmpty()) { |
| base::TimeTicks run_time; |
| if (manager_->real_time_domain()->NextScheduledRunTime(&run_time)) { |
| now_src_->SetNowTicks(run_time); |
| per_run_time_callback.Run(); |
| } else { |
| break; |
| } |
| } |
| |
| test_task_runner_->RunPendingTasks(); |
| } |
| } |
| |
| base::TimeTicks Now() const { return now_src_->NowTicks(); } |
| |
| std::unique_ptr<base::MessageLoop> message_loop_; |
| std::unique_ptr<base::SimpleTestTickClock> now_src_; |
| scoped_refptr<TaskQueueManagerDelegateForTest> main_task_runner_; |
| scoped_refptr<cc::OrderedSimpleTaskRunner> test_task_runner_; |
| std::unique_ptr<TaskQueueManagerForTest> manager_; |
| std::vector<scoped_refptr<TestTaskQueue>> runners_; |
| TestTaskTimeObserver test_task_time_observer_; |
| }; |
| |
| void PostFromNestedRunloop(base::MessageLoop* message_loop, |
| base::SingleThreadTaskRunner* runner, |
| std::vector<std::pair<base::Closure, bool>>* tasks) { |
| base::MessageLoop::ScopedNestableTaskAllower allow(message_loop); |
| for (std::pair<base::Closure, bool>& pair : *tasks) { |
| if (pair.second) { |
| runner->PostTask(FROM_HERE, pair.first); |
| } else { |
| runner->PostNonNestableTask(FROM_HERE, pair.first); |
| } |
| } |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void NopTask() {} |
| |
| TEST_F(TaskQueueManagerTest, |
| NowCalledMinimumNumberOfTimesToComputeTaskDurations) { |
| message_loop_.reset(new base::MessageLoop()); |
| // This memory is managed by the TaskQueueManager, but we need to hold a |
| // pointer to this object to read out how many times Now was called. |
| TestCountUsesTimeSource* test_count_uses_time_source = |
| new TestCountUsesTimeSource(); |
| |
| manager_ = |
| std::make_unique<TaskQueueManagerForTest>(MessageLoopTaskRunner::Create( |
| base::WrapUnique(test_count_uses_time_source))); |
| manager_->SetWorkBatchSize(6); |
| manager_->AddTaskTimeObserver(&test_task_time_observer_); |
| |
| for (size_t i = 0; i < 3; i++) |
| runners_.push_back(CreateTaskQueue()); |
| |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| runners_[1]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| runners_[1]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| runners_[2]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| runners_[2]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| |
| base::RunLoop().RunUntilIdle(); |
| // Now is called each time a task is queued, when first task is started |
| // running, and when a task is completed. |
| // With 6 tasks that means that 6 + 1 + 6 = 13 calls are expected. |
| EXPECT_EQ(13, test_count_uses_time_source->now_calls_count()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, NowNotCalledForNestedTasks) { |
| message_loop_.reset(new base::MessageLoop()); |
| // This memory is managed by the TaskQueueManager, but we need to hold a |
| // pointer to this object to read out how many times Now was called. |
| TestCountUsesTimeSource* test_count_uses_time_source = |
| new TestCountUsesTimeSource(); |
| |
| manager_ = |
| std::make_unique<TaskQueueManagerForTest>(MessageLoopTaskRunner::Create( |
| base::WrapUnique(test_count_uses_time_source))); |
| manager_->AddTaskTimeObserver(&test_task_time_observer_); |
| |
| runners_.push_back(CreateTaskQueue()); |
| |
| std::vector<std::pair<base::Closure, bool>> tasks_to_post_from_nested_loop; |
| for (int i = 0; i < 7; ++i) { |
| tasks_to_post_from_nested_loop.push_back( |
| std::make_pair(base::Bind(&NopTask), true)); |
| } |
| |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&PostFromNestedRunloop, message_loop_.get(), |
| base::RetainedRef(runners_[0]), |
| base::Unretained(&tasks_to_post_from_nested_loop))); |
| |
| base::RunLoop().RunUntilIdle(); |
| // We need to call Now twice, to measure the start and end of the outermost |
| // task. We shouldn't call it for any of the nested tasks. |
| // Also Now is called when a task is scheduled (8 times). |
| // That brings expected call count for Now() to 2 + 8 = 10 |
| EXPECT_EQ(10, test_count_uses_time_source->now_calls_count()); |
| } |
| |
| void NullTask() {} |
| |
| void TestTask(EnqueueOrder value, std::vector<EnqueueOrder>* out_result) { |
| out_result->push_back(value); |
| } |
| |
| TEST_F(TaskQueueManagerTest, SingleQueuePosting) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, MultiQueuePosting) { |
| Initialize(3u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); |
| runners_[2]->PostTask(FROM_HERE, base::Bind(&TestTask, 5, &run_order)); |
| runners_[2]->PostTask(FROM_HERE, base::Bind(&TestTask, 6, &run_order)); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5, 6)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, NonNestableTaskPosting) { |
| InitializeWithRealMessageLoop(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostNonNestableTask(FROM_HERE, |
| base::Bind(&TestTask, 1, &run_order)); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, NonNestableTaskExecutesInExpectedOrder) { |
| InitializeWithRealMessageLoop(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); |
| runners_[0]->PostNonNestableTask(FROM_HERE, |
| base::Bind(&TestTask, 5, &run_order)); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, NonNestableTaskDoesntExecuteInNestedLoop) { |
| InitializeWithRealMessageLoop(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| |
| std::vector<std::pair<base::Closure, bool>> tasks_to_post_from_nested_loop; |
| tasks_to_post_from_nested_loop.push_back( |
| std::make_pair(base::Bind(&TestTask, 3, &run_order), false)); |
| tasks_to_post_from_nested_loop.push_back( |
| std::make_pair(base::Bind(&TestTask, 4, &run_order), true)); |
| tasks_to_post_from_nested_loop.push_back( |
| std::make_pair(base::Bind(&TestTask, 5, &run_order), true)); |
| |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&PostFromNestedRunloop, message_loop_.get(), |
| base::RetainedRef(runners_[0]), |
| base::Unretained(&tasks_to_post_from_nested_loop))); |
| |
| base::RunLoop().RunUntilIdle(); |
| // Note we expect task 3 to run last because it's non-nestable. |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 4, 5, 3)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, HasPendingImmediateWork_ImmediateTask) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately()); |
| |
| // Move the task into the |immediate_work_queue|. |
| EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->immediate_work_queue()->Empty()); |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_FALSE( |
| runners_[0]->GetTaskQueueImpl()->immediate_work_queue()->Empty()); |
| EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately()); |
| |
| // Run the task, making the queue empty. |
| voter->SetQueueEnabled(true); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, HasPendingImmediateWork_DelayedTask) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| delay); |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| now_src_->Advance(delay); |
| EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately()); |
| |
| // Move the task into the |delayed_work_queue|. |
| WakeUpReadyDelayedQueues(LazyNow(now_src_.get())); |
| EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->delayed_work_queue()->Empty()); |
| EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately()); |
| |
| // Run the task, making the queue empty. |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DelayedTaskPosting) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| delay); |
| EXPECT_EQ(delay, test_task_runner_->DelayToNextTaskTime()); |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| EXPECT_TRUE(run_order.empty()); |
| |
| // The task doesn't run before the delay has completed. |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(9)); |
| EXPECT_TRUE(run_order.empty()); |
| |
| // After the delay has completed, the task runs normally. |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1)); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| } |
| |
| bool MessageLoopTaskCounter(size_t* count) { |
| *count = *count + 1; |
| return true; |
| } |
| |
| TEST_F(TaskQueueManagerTest, DelayedTaskExecutedInOneMessageLoopTask) { |
| Initialize(1u); |
| |
| base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay); |
| |
| size_t task_count = 0; |
| test_task_runner_->RunTasksWhile( |
| base::Bind(&MessageLoopTaskCounter, &task_count)); |
| EXPECT_EQ(1u, task_count); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DelayedTaskPosting_MultipleTasks_DecendingOrder) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| base::TimeDelta::FromMilliseconds(10)); |
| |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), |
| base::TimeDelta::FromMilliseconds(8)); |
| |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), |
| base::TimeDelta::FromMilliseconds(5)); |
| |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(5), |
| test_task_runner_->DelayToNextTaskTime()); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(5)); |
| EXPECT_THAT(run_order, ElementsAre(3)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(3), |
| test_task_runner_->DelayToNextTaskTime()); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(3)); |
| EXPECT_THAT(run_order, ElementsAre(3, 2)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(2), |
| test_task_runner_->DelayToNextTaskTime()); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(2)); |
| EXPECT_THAT(run_order, ElementsAre(3, 2, 1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DelayedTaskPosting_MultipleTasks_AscendingOrder) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| base::TimeDelta::FromMilliseconds(1)); |
| |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), |
| base::TimeDelta::FromMilliseconds(5)); |
| |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), |
| base::TimeDelta::FromMilliseconds(10)); |
| |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1), |
| test_task_runner_->DelayToNextTaskTime()); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1)); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(4), |
| test_task_runner_->DelayToNextTaskTime()); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(4)); |
| EXPECT_THAT(run_order, ElementsAre(1, 2)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(5), |
| test_task_runner_->DelayToNextTaskTime()); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(5)); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, PostDelayedTask_SharesUnderlyingDelayedTasks) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| delay); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), |
| delay); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), |
| delay); |
| |
| EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); |
| } |
| |
| class TestObject { |
| public: |
| ~TestObject() { destructor_count__++; } |
| |
| void Run() { FAIL() << "TestObject::Run should not be called"; } |
| |
| static int destructor_count__; |
| }; |
| |
| int TestObject::destructor_count__ = 0; |
| |
| TEST_F(TaskQueueManagerTest, PendingDelayedTasksRemovedOnShutdown) { |
| Initialize(1u); |
| |
| TestObject::destructor_count__ = 0; |
| |
| base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&TestObject::Run, base::Owned(new TestObject())), |
| delay); |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&TestObject::Run, base::Owned(new TestObject()))); |
| |
| manager_.reset(); |
| |
| EXPECT_EQ(2, TestObject::destructor_count__); |
| } |
| |
| TEST_F(TaskQueueManagerTest, InsertAndRemoveFence) { |
| Initialize(1u); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| |
| std::vector<EnqueueOrder> run_order; |
| // Posting a task when pumping is disabled doesn't result in work getting |
| // posted. |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| EXPECT_FALSE(test_task_runner_->HasPendingTasks()); |
| |
| // However polling still works. |
| EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately()); |
| |
| // After removing the fence the task runs normally. |
| runners_[0]->RemoveFence(); |
| EXPECT_TRUE(test_task_runner_->HasPendingTasks()); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, RemovingFenceForDisabledQueueDoesNotPostDoWork) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| |
| runners_[0]->RemoveFence(); |
| EXPECT_FALSE(test_task_runner_->HasPendingTasks()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, EnablingFencedQueueDoesNotPostDoWork) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| |
| voter->SetQueueEnabled(true); |
| EXPECT_FALSE(test_task_runner_->HasPendingTasks()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DenyRunning_BeforePosting) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| EXPECT_FALSE(test_task_runner_->HasPendingTasks()); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| voter->SetQueueEnabled(true); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DenyRunning_AfterPosting) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| EXPECT_TRUE(test_task_runner_->HasPendingTasks()); |
| voter->SetQueueEnabled(false); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| voter->SetQueueEnabled(true); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DenyRunning_AfterRemovingFence) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| runners_[0]->RemoveFence(); |
| voter->SetQueueEnabled(true); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, RemovingFenceWithDelayedTask) { |
| Initialize(1u); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| |
| std::vector<EnqueueOrder> run_order; |
| // Posting a delayed task when fenced will apply the delay, but won't cause |
| // work to executed afterwards. |
| base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| delay); |
| |
| // The task does not run even though it's delay is up. |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(10)); |
| EXPECT_TRUE(run_order.empty()); |
| |
| // Removing the fence causes the task to run. |
| runners_[0]->RemoveFence(); |
| EXPECT_TRUE(test_task_runner_->HasPendingTasks()); |
| test_task_runner_->RunPendingTasks(); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, RemovingFenceWithMultipleDelayedTasks) { |
| Initialize(1u); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| |
| std::vector<EnqueueOrder> run_order; |
| // Posting a delayed task when fenced will apply the delay, but won't cause |
| // work to executed afterwards. |
| base::TimeDelta delay1(base::TimeDelta::FromMilliseconds(1)); |
| base::TimeDelta delay2(base::TimeDelta::FromMilliseconds(10)); |
| base::TimeDelta delay3(base::TimeDelta::FromMilliseconds(20)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| delay1); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), |
| delay2); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), |
| delay3); |
| |
| now_src_->Advance(base::TimeDelta::FromMilliseconds(15)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| |
| // Removing the fence causes the ready tasks to run. |
| runners_[0]->RemoveFence(); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, InsertFencePreventsDelayedTasksFromRunning) { |
| Initialize(1u); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| |
| std::vector<EnqueueOrder> run_order; |
| base::TimeDelta delay(base::TimeDelta::FromMilliseconds(10)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| delay); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(10)); |
| EXPECT_TRUE(run_order.empty()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, MultipleFences) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2)); |
| |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| // Subsequent tasks should be blocked. |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, InsertFenceThenImmediatlyRemoveDoesNotBlock) { |
| Initialize(1u); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| runners_[0]->RemoveFence(); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, InsertFencePostThenRemoveDoesNotBlock) { |
| Initialize(1u); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| runners_[0]->RemoveFence(); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, MultipleFencesWithInitiallyEmptyQueue) { |
| Initialize(1u); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, BlockedByFence) { |
| Initialize(1u); |
| EXPECT_FALSE(runners_[0]->BlockedByFence()); |
| |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| EXPECT_TRUE(runners_[0]->BlockedByFence()); |
| |
| runners_[0]->RemoveFence(); |
| EXPECT_FALSE(runners_[0]->BlockedByFence()); |
| |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| EXPECT_FALSE(runners_[0]->BlockedByFence()); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_TRUE(runners_[0]->BlockedByFence()); |
| |
| runners_[0]->RemoveFence(); |
| EXPECT_FALSE(runners_[0]->BlockedByFence()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, BlockedByFence_BothTypesOfFence) { |
| Initialize(1u); |
| |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| EXPECT_FALSE(runners_[0]->BlockedByFence()); |
| |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime); |
| EXPECT_TRUE(runners_[0]->BlockedByFence()); |
| } |
| |
| namespace { |
| |
| void RecordTimeTask(std::vector<base::TimeTicks>* run_times, |
| base::SimpleTestTickClock* clock) { |
| run_times->push_back(clock->NowTicks()); |
| } |
| |
| } // namespace |
| |
| TEST_F(TaskQueueManagerTest, DelayedFence_DelayedTasks) { |
| Initialize(1u); |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| std::vector<base::TimeTicks> run_times; |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&RecordTimeTask, &run_times, now_src_.get()), |
| base::TimeDelta::FromMilliseconds(100)); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&RecordTimeTask, &run_times, now_src_.get()), |
| base::TimeDelta::FromMilliseconds(200)); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&RecordTimeTask, &run_times, now_src_.get()), |
| base::TimeDelta::FromMilliseconds(300)); |
| |
| runners_[0]->InsertFenceAt(Now() + base::TimeDelta::FromMilliseconds(250)); |
| EXPECT_FALSE(runners_[0]->HasActiveFence()); |
| |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_TRUE(runners_[0]->HasActiveFence()); |
| EXPECT_THAT( |
| run_times, |
| ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(101), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(201))); |
| run_times.clear(); |
| |
| runners_[0]->RemoveFence(); |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_FALSE(runners_[0]->HasActiveFence()); |
| EXPECT_THAT(run_times, ElementsAre(base::TimeTicks() + |
| base::TimeDelta::FromMilliseconds(301))); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DelayedFence_ImmediateTasks) { |
| Initialize(1u); |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| std::vector<base::TimeTicks> run_times; |
| runners_[0]->InsertFenceAt(Now() + base::TimeDelta::FromMilliseconds(250)); |
| |
| for (int i = 0; i < 5; ++i) { |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&RecordTimeTask, &run_times, now_src_.get())); |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(100)); |
| if (i < 2) { |
| EXPECT_FALSE(runners_[0]->HasActiveFence()); |
| } else { |
| EXPECT_TRUE(runners_[0]->HasActiveFence()); |
| } |
| } |
| |
| EXPECT_THAT( |
| run_times, |
| ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(101), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(201))); |
| run_times.clear(); |
| |
| runners_[0]->RemoveFence(); |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_THAT( |
| run_times, |
| ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(501), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(501))); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DelayedFence_RemovedFenceDoesNotActivate) { |
| Initialize(1u); |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| std::vector<base::TimeTicks> run_times; |
| runners_[0]->InsertFenceAt(Now() + base::TimeDelta::FromMilliseconds(250)); |
| |
| for (int i = 0; i < 3; ++i) { |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&RecordTimeTask, &run_times, now_src_.get())); |
| EXPECT_FALSE(runners_[0]->HasActiveFence()); |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(100)); |
| } |
| |
| EXPECT_TRUE(runners_[0]->HasActiveFence()); |
| runners_[0]->RemoveFence(); |
| |
| for (int i = 0; i < 2; ++i) { |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&RecordTimeTask, &run_times, now_src_.get())); |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(100)); |
| EXPECT_FALSE(runners_[0]->HasActiveFence()); |
| } |
| |
| EXPECT_THAT( |
| run_times, |
| ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(1), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(101), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(201), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(301), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(401))); |
| } |
| |
| namespace { |
| |
| void ReentrantTestTask(scoped_refptr<base::SingleThreadTaskRunner> runner, |
| int countdown, |
| std::vector<EnqueueOrder>* out_result) { |
| out_result->push_back(countdown); |
| if (--countdown) { |
| runner->PostTask(FROM_HERE, |
| Bind(&ReentrantTestTask, runner, countdown, out_result)); |
| } |
| } |
| |
| } // namespace |
| |
| TEST_F(TaskQueueManagerTest, ReentrantPosting) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, |
| Bind(&ReentrantTestTask, runners_[0], 3, &run_order)); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(3, 2, 1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, NoTasksAfterShutdown) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| manager_.reset(); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_TRUE(run_order.empty()); |
| } |
| |
| void PostTaskToRunner(scoped_refptr<base::SingleThreadTaskRunner> runner, |
| std::vector<EnqueueOrder>* run_order) { |
| runner->PostTask(FROM_HERE, base::Bind(&TestTask, 1, run_order)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, PostFromThread) { |
| InitializeWithRealMessageLoop(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| base::Thread thread("TestThread"); |
| thread.Start(); |
| thread.task_runner()->PostTask( |
| FROM_HERE, base::Bind(&PostTaskToRunner, runners_[0], &run_order)); |
| thread.Stop(); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| } |
| |
| void RePostingTestTask(scoped_refptr<base::SingleThreadTaskRunner> runner, |
| int* run_count) { |
| (*run_count)++; |
| runner->PostTask(FROM_HERE, Bind(&RePostingTestTask, |
| base::Unretained(runner.get()), run_count)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DoWorkCantPostItselfMultipleTimes) { |
| Initialize(1u); |
| |
| int run_count = 0; |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&RePostingTestTask, runners_[0], &run_count)); |
| |
| test_task_runner_->RunPendingTasks(); |
| // NOTE without the executing_task_ check in MaybeScheduleDoWork there |
| // will be two tasks here. |
| EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); |
| EXPECT_EQ(1, run_count); |
| } |
| |
| TEST_F(TaskQueueManagerTest, PostFromNestedRunloop) { |
| InitializeWithRealMessageLoop(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| std::vector<std::pair<base::Closure, bool>> tasks_to_post_from_nested_loop; |
| tasks_to_post_from_nested_loop.push_back( |
| std::make_pair(base::Bind(&TestTask, 1, &run_order), true)); |
| |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 0, &run_order)); |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&PostFromNestedRunloop, message_loop_.get(), |
| base::RetainedRef(runners_[0]), |
| base::Unretained(&tasks_to_post_from_nested_loop))); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, ElementsAre(0, 2, 1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, WorkBatching) { |
| Initialize(1u); |
| |
| manager_->SetWorkBatchSize(2); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); |
| |
| // Running one task in the host message loop should cause two posted tasks to |
| // get executed. |
| EXPECT_EQ(test_task_runner_->NumPendingTasks(), 1u); |
| test_task_runner_->RunPendingTasks(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2)); |
| |
| // The second task runs the remaining two posted tasks. |
| EXPECT_EQ(test_task_runner_->NumPendingTasks(), 1u); |
| test_task_runner_->RunPendingTasks(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4)); |
| } |
| |
| class MockTaskObserver : public base::MessageLoop::TaskObserver { |
| public: |
| MOCK_METHOD1(DidProcessTask, void(const base::PendingTask& task)); |
| MOCK_METHOD1(WillProcessTask, void(const base::PendingTask& task)); |
| }; |
| |
| TEST_F(TaskQueueManagerTest, TaskObserverAdding) { |
| InitializeWithRealMessageLoop(1u); |
| MockTaskObserver observer; |
| |
| manager_->SetWorkBatchSize(2); |
| manager_->AddTaskObserver(&observer); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| |
| EXPECT_CALL(observer, WillProcessTask(_)).Times(2); |
| EXPECT_CALL(observer, DidProcessTask(_)).Times(2); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TaskObserverRemoving) { |
| InitializeWithRealMessageLoop(1u); |
| MockTaskObserver observer; |
| manager_->SetWorkBatchSize(2); |
| manager_->AddTaskObserver(&observer); |
| manager_->RemoveTaskObserver(&observer); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| |
| EXPECT_CALL(observer, WillProcessTask(_)).Times(0); |
| EXPECT_CALL(observer, DidProcessTask(_)).Times(0); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void RemoveObserverTask(TaskQueueManager* manager, |
| base::MessageLoop::TaskObserver* observer) { |
| manager->RemoveTaskObserver(observer); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TaskObserverRemovingInsideTask) { |
| InitializeWithRealMessageLoop(1u); |
| MockTaskObserver observer; |
| manager_->SetWorkBatchSize(3); |
| manager_->AddTaskObserver(&observer); |
| |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&RemoveObserverTask, manager_.get(), &observer)); |
| |
| EXPECT_CALL(observer, WillProcessTask(_)).Times(1); |
| EXPECT_CALL(observer, DidProcessTask(_)).Times(0); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, QueueTaskObserverAdding) { |
| InitializeWithRealMessageLoop(2u); |
| MockTaskObserver observer; |
| |
| manager_->SetWorkBatchSize(2); |
| runners_[0]->AddTaskObserver(&observer); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| |
| EXPECT_CALL(observer, WillProcessTask(_)).Times(1); |
| EXPECT_CALL(observer, DidProcessTask(_)).Times(1); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, QueueTaskObserverRemoving) { |
| InitializeWithRealMessageLoop(1u); |
| MockTaskObserver observer; |
| manager_->SetWorkBatchSize(2); |
| runners_[0]->AddTaskObserver(&observer); |
| runners_[0]->RemoveTaskObserver(&observer); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| |
| EXPECT_CALL(observer, WillProcessTask(_)).Times(0); |
| EXPECT_CALL(observer, DidProcessTask(_)).Times(0); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void RemoveQueueObserverTask(scoped_refptr<TaskQueue> queue, |
| base::MessageLoop::TaskObserver* observer) { |
| queue->RemoveTaskObserver(observer); |
| } |
| |
| TEST_F(TaskQueueManagerTest, QueueTaskObserverRemovingInsideTask) { |
| InitializeWithRealMessageLoop(1u); |
| MockTaskObserver observer; |
| runners_[0]->AddTaskObserver(&observer); |
| |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&RemoveQueueObserverTask, runners_[0], &observer)); |
| |
| EXPECT_CALL(observer, WillProcessTask(_)).Times(1); |
| EXPECT_CALL(observer, DidProcessTask(_)).Times(0); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ThreadCheckAfterTermination) { |
| Initialize(1u); |
| EXPECT_TRUE(runners_[0]->RunsTasksInCurrentSequence()); |
| manager_.reset(); |
| EXPECT_TRUE(runners_[0]->RunsTasksInCurrentSequence()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TimeDomain_NextScheduledRunTime) { |
| Initialize(2u); |
| now_src_->Advance(base::TimeDelta::FromMicroseconds(10000)); |
| |
| // With no delayed tasks. |
| base::TimeTicks run_time; |
| EXPECT_FALSE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); |
| |
| // With a non-delayed task. |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| EXPECT_FALSE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); |
| |
| // With a delayed task. |
| base::TimeDelta expected_delay = base::TimeDelta::FromMilliseconds(50); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), expected_delay); |
| EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); |
| EXPECT_EQ(now_src_->NowTicks() + expected_delay, run_time); |
| |
| // With another delayed task in the same queue with a longer delay. |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), |
| base::TimeDelta::FromMilliseconds(100)); |
| EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); |
| EXPECT_EQ(now_src_->NowTicks() + expected_delay, run_time); |
| |
| // 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(&NopTask), expected_delay); |
| EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); |
| EXPECT_EQ(now_src_->NowTicks() + expected_delay, run_time); |
| |
| // 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(&NopTask), expected_delay); |
| EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); |
| EXPECT_EQ(now_src_->NowTicks() + expected_delay, run_time); |
| |
| // Test it updates as time progresses |
| now_src_->Advance(expected_delay); |
| EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); |
| EXPECT_EQ(now_src_->NowTicks(), run_time); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TimeDomain_NextScheduledRunTime_MultipleQueues) { |
| Initialize(3u); |
| |
| base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(50); |
| base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(5); |
| base::TimeDelta delay3 = base::TimeDelta::FromMilliseconds(10); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay1); |
| runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay2); |
| runners_[2]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay3); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| |
| base::TimeTicks run_time; |
| EXPECT_TRUE(manager_->real_time_domain()->NextScheduledRunTime(&run_time)); |
| EXPECT_EQ(now_src_->NowTicks() + delay2, run_time); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DeleteTaskQueueManagerInsideATask) { |
| Initialize(1u); |
| |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&TaskQueueManagerTest::DeleteTaskQueueManager, |
| base::Unretained(this))); |
| |
| // This should not crash, assuming DoWork detects the TaskQueueManager has |
| // been deleted. |
| test_task_runner_->RunUntilIdle(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, GetAndClearSystemIsQuiescentBit) { |
| Initialize(3u); |
| |
| scoped_refptr<TaskQueue> queue0 = CreateTaskQueueWithMonitoredQuiescence(); |
| scoped_refptr<TaskQueue> queue1 = CreateTaskQueueWithMonitoredQuiescence(); |
| scoped_refptr<TaskQueue> queue2 = CreateTaskQueue(); |
| |
| EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); |
| |
| queue0->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_FALSE(manager_->GetAndClearSystemIsQuiescentBit()); |
| EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); |
| |
| queue1->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_FALSE(manager_->GetAndClearSystemIsQuiescentBit()); |
| EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); |
| |
| queue2->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); |
| |
| queue0->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| queue1->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_FALSE(manager_->GetAndClearSystemIsQuiescentBit()); |
| EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, HasPendingImmediateWork) { |
| Initialize(1u); |
| |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(NullTask)); |
| EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately()); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, HasPendingImmediateWork_DelayedTasks) { |
| Initialize(1u); |
| |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(NullTask), |
| base::TimeDelta::FromMilliseconds(12)); |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| |
| // Move time forwards until just before the delayed task should run. |
| now_src_->Advance(base::TimeDelta::FromMilliseconds(10)); |
| WakeUpReadyDelayedQueues(LazyNow(now_src_.get())); |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| |
| // Force the delayed task onto the work queue. |
| now_src_->Advance(base::TimeDelta::FromMilliseconds(2)); |
| WakeUpReadyDelayedQueues(LazyNow(now_src_.get())); |
| EXPECT_TRUE(runners_[0]->HasTaskToRunImmediately()); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_FALSE(runners_[0]->HasTaskToRunImmediately()); |
| } |
| |
| void ExpensiveTestTask(int value, |
| base::SimpleTestTickClock* clock, |
| std::vector<EnqueueOrder>* out_result) { |
| out_result->push_back(value); |
| clock->Advance(base::TimeDelta::FromMilliseconds(1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ImmediateAndDelayedTaskInterleaving) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10); |
| for (int i = 10; i < 19; i++) { |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&ExpensiveTestTask, i, now_src_.get(), &run_order), delay); |
| } |
| |
| test_task_runner_->RunForPeriod(delay); |
| |
| for (int i = 0; i < 9; i++) { |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&ExpensiveTestTask, i, |
| now_src_.get(), &run_order)); |
| } |
| |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| test_task_runner_->RunUntilIdle(); |
| |
| // Delayed tasks are not allowed to starve out immediate work which is why |
| // some of the immediate tasks run out of order. |
| int expected_run_order[] = {10, 11, 12, 13, 0, 14, 15, 16, 1, |
| 17, 18, 2, 3, 4, 5, 6, 7, 8}; |
| EXPECT_THAT(run_order, ElementsAreArray(expected_run_order)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, |
| DelayedTaskDoesNotSkipAHeadOfNonDelayedTask_SameQueue) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| delay); |
| |
| now_src_->Advance(delay * 2); |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, ElementsAre(2, 3, 1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, |
| DelayedTaskDoesNotSkipAHeadOfNonDelayedTask_DifferentQueues) { |
| Initialize(2u); |
| |
| std::vector<EnqueueOrder> run_order; |
| base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10); |
| runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| delay); |
| |
| now_src_->Advance(delay * 2); |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, ElementsAre(2, 3, 1)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DelayedTaskDoesNotSkipAHeadOfShorterDelayedTask) { |
| Initialize(2u); |
| |
| std::vector<EnqueueOrder> run_order; |
| base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10); |
| base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(5); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| delay1); |
| runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), |
| delay2); |
| |
| now_src_->Advance(delay1 * 2); |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, ElementsAre(2, 1)); |
| } |
| |
| void CheckIsNested(bool* is_nested) { |
| *is_nested = base::RunLoop::IsNestedOnCurrentThread(); |
| } |
| |
| void PostAndQuitFromNestedRunloop(base::RunLoop* run_loop, |
| base::SingleThreadTaskRunner* runner, |
| bool* was_nested) { |
| base::MessageLoop::ScopedNestableTaskAllower allow( |
| base::MessageLoop::current()); |
| runner->PostTask(FROM_HERE, run_loop->QuitClosure()); |
| runner->PostTask(FROM_HERE, base::Bind(&CheckIsNested, was_nested)); |
| run_loop->Run(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, QuitWhileNested) { |
| // This test makes sure we don't continue running a work batch after a nested |
| // run loop has been exited in the middle of the batch. |
| InitializeWithRealMessageLoop(1u); |
| manager_->SetWorkBatchSize(2); |
| |
| bool was_nested = true; |
| base::RunLoop run_loop; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&PostAndQuitFromNestedRunloop, |
| base::Unretained(&run_loop), |
| base::RetainedRef(runners_[0]), |
| base::Unretained(&was_nested))); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(was_nested); |
| } |
| |
| class SequenceNumberCapturingTaskObserver |
| : public base::MessageLoop::TaskObserver { |
| public: |
| // MessageLoop::TaskObserver overrides. |
| void WillProcessTask(const base::PendingTask& pending_task) override {} |
| void DidProcessTask(const base::PendingTask& pending_task) override { |
| sequence_numbers_.push_back(pending_task.sequence_num); |
| } |
| |
| const std::vector<EnqueueOrder>& sequence_numbers() const { |
| return sequence_numbers_; |
| } |
| |
| private: |
| std::vector<EnqueueOrder> sequence_numbers_; |
| }; |
| |
| TEST_F(TaskQueueManagerTest, SequenceNumSetWhenTaskIsPosted) { |
| Initialize(1u); |
| |
| SequenceNumberCapturingTaskObserver observer; |
| manager_->AddTaskObserver(&observer); |
| |
| // Register four tasks that will run in reverse order. |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| base::TimeDelta::FromMilliseconds(30)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), |
| base::TimeDelta::FromMilliseconds(20)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), |
| base::TimeDelta::FromMilliseconds(10)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(40)); |
| ASSERT_THAT(run_order, ElementsAre(4, 3, 2, 1)); |
| |
| // The sequence numbers are a one-based monotonically incrememting counter |
| // which should be set when the task is posted rather than when it's enqueued |
| // onto the Incoming queue. This counter starts with 2. |
| EXPECT_THAT(observer.sequence_numbers(), ElementsAre(5, 4, 3, 2)); |
| |
| manager_->RemoveTaskObserver(&observer); |
| } |
| |
| TEST_F(TaskQueueManagerTest, NewTaskQueues) { |
| Initialize(1u); |
| |
| scoped_refptr<TaskQueue> queue1 = CreateTaskQueue(); |
| scoped_refptr<TaskQueue> queue2 = CreateTaskQueue(); |
| scoped_refptr<TaskQueue> queue3 = CreateTaskQueue(); |
| |
| ASSERT_NE(queue1, queue2); |
| ASSERT_NE(queue1, queue3); |
| ASSERT_NE(queue2, queue3); |
| |
| std::vector<EnqueueOrder> run_order; |
| queue1->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| queue2->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| queue3->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ShutdownTaskQueue) { |
| Initialize(1u); |
| |
| scoped_refptr<TaskQueue> queue1 = CreateTaskQueue(); |
| scoped_refptr<TaskQueue> queue2 = CreateTaskQueue(); |
| scoped_refptr<TaskQueue> queue3 = CreateTaskQueue(); |
| |
| ASSERT_NE(queue1, queue2); |
| ASSERT_NE(queue1, queue3); |
| ASSERT_NE(queue2, queue3); |
| |
| std::vector<EnqueueOrder> run_order; |
| queue1->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| queue2->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| queue3->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| |
| queue2->ShutdownTaskQueue(); |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_THAT(run_order, ElementsAre(1, 3)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ShutdownTaskQueue_WithDelayedTasks) { |
| Initialize(2u); |
| |
| // Register three delayed tasks |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| base::TimeDelta::FromMilliseconds(10)); |
| runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), |
| base::TimeDelta::FromMilliseconds(20)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), |
| base::TimeDelta::FromMilliseconds(30)); |
| |
| runners_[1]->ShutdownTaskQueue(); |
| test_task_runner_->RunUntilIdle(); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(40)); |
| ASSERT_THAT(run_order, ElementsAre(1, 3)); |
| } |
| |
| namespace { |
| void ShutdownQueue(scoped_refptr<TaskQueue> queue) { |
| queue->ShutdownTaskQueue(); |
| } |
| } |
| |
| TEST_F(TaskQueueManagerTest, ShutdownTaskQueue_InTasks) { |
| Initialize(3u); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&ShutdownQueue, runners_[1])); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&ShutdownQueue, runners_[2])); |
| runners_[1]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| runners_[2]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| |
| test_task_runner_->RunUntilIdle(); |
| ASSERT_THAT(run_order, ElementsAre(1)); |
| } |
| |
| namespace { |
| |
| class MockObserver : public TaskQueueManager::Observer { |
| public: |
| MOCK_METHOD0(OnTriedToExecuteBlockedTask, void()); |
| MOCK_METHOD0(OnBeginNestedRunLoop, void()); |
| MOCK_METHOD0(OnExitNestedRunLoop, void()); |
| }; |
| |
| } // namespace |
| |
| TEST_F(TaskQueueManagerTest, OnTriedToExecuteBlockedTask) { |
| Initialize(0u); |
| |
| MockObserver observer; |
| manager_->SetObserver(&observer); |
| |
| scoped_refptr<TaskQueue> task_queue = CreateTaskQueueWithSpec( |
| TaskQueue::Spec("test").SetShouldReportWhenExecutionBlocked(true)); |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| task_queue->CreateQueueEnabledVoter(); |
| |
| voter->SetQueueEnabled(false); |
| task_queue->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| |
| // Trick |task_queue| into posting a DoWork. By default PostTask with a |
| // disabled queue won't post a DoWork until we enable the queue. |
| voter->SetQueueEnabled(true); |
| voter->SetQueueEnabled(false); |
| |
| EXPECT_CALL(observer, OnTriedToExecuteBlockedTask()).Times(1); |
| test_task_runner_->RunPendingTasks(); |
| |
| manager_->SetObserver(nullptr); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ExecutedNonBlockedTask) { |
| Initialize(0u); |
| |
| MockObserver observer; |
| manager_->SetObserver(&observer); |
| |
| scoped_refptr<TaskQueue> task_queue = CreateTaskQueueWithSpec( |
| TaskQueue::Spec("test").SetShouldReportWhenExecutionBlocked(true)); |
| task_queue->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| |
| EXPECT_CALL(observer, OnTriedToExecuteBlockedTask()).Times(0); |
| test_task_runner_->RunPendingTasks(); |
| |
| manager_->SetObserver(nullptr); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ShutdownTaskQueueInNestedLoop) { |
| InitializeWithRealMessageLoop(1u); |
| |
| // We retain a reference to the task queue even when the manager has deleted |
| // its reference. |
| scoped_refptr<TaskQueue> task_queue = CreateTaskQueue(); |
| |
| std::vector<bool> log; |
| std::vector<std::pair<base::Closure, bool>> tasks_to_post_from_nested_loop; |
| |
| // Inside a nested run loop, call task_queue->ShutdownTaskQueue, bookended |
| // by calls to HasOneRefTask to make sure the manager doesn't release its |
| // reference until the nested run loop exits. |
| // NB: This first HasOneRefTask is a sanity check. |
| tasks_to_post_from_nested_loop.push_back( |
| std::make_pair(base::Bind(&NopTask), true)); |
| tasks_to_post_from_nested_loop.push_back( |
| std::make_pair(base::Bind(&TaskQueue::ShutdownTaskQueue, |
| base::Unretained(task_queue.get())), |
| true)); |
| tasks_to_post_from_nested_loop.push_back( |
| std::make_pair(base::Bind(&NopTask), true)); |
| runners_[0]->PostTask( |
| FROM_HERE, base::Bind(&PostFromNestedRunloop, message_loop_.get(), |
| base::RetainedRef(runners_[0]), |
| base::Unretained(&tasks_to_post_from_nested_loop))); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Just make sure that we don't crash. |
| } |
| |
| TEST_F(TaskQueueManagerTest, TimeDomainsAreIndependant) { |
| Initialize(2u); |
| |
| base::TimeTicks start_time = manager_->Delegate()->NowTicks(); |
| std::unique_ptr<VirtualTimeDomain> domain_a( |
| new VirtualTimeDomain(start_time)); |
| std::unique_ptr<VirtualTimeDomain> domain_b( |
| new VirtualTimeDomain(start_time)); |
| manager_->RegisterTimeDomain(domain_a.get()); |
| manager_->RegisterTimeDomain(domain_b.get()); |
| runners_[0]->SetTimeDomain(domain_a.get()); |
| runners_[1]->SetTimeDomain(domain_b.get()); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| base::TimeDelta::FromMilliseconds(10)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), |
| base::TimeDelta::FromMilliseconds(20)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), |
| base::TimeDelta::FromMilliseconds(30)); |
| |
| runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order), |
| base::TimeDelta::FromMilliseconds(10)); |
| runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 5, &run_order), |
| base::TimeDelta::FromMilliseconds(20)); |
| runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 6, &run_order), |
| base::TimeDelta::FromMilliseconds(30)); |
| |
| domain_b->AdvanceTo(start_time + base::TimeDelta::FromMilliseconds(50)); |
| manager_->MaybeScheduleImmediateWork(FROM_HERE); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(4, 5, 6)); |
| |
| domain_a->AdvanceTo(start_time + base::TimeDelta::FromMilliseconds(50)); |
| manager_->MaybeScheduleImmediateWork(FROM_HERE); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(4, 5, 6, 1, 2, 3)); |
| |
| runners_[0]->ShutdownTaskQueue(); |
| runners_[1]->ShutdownTaskQueue(); |
| |
| manager_->UnregisterTimeDomain(domain_a.get()); |
| manager_->UnregisterTimeDomain(domain_b.get()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TimeDomainMigration) { |
| Initialize(1u); |
| |
| base::TimeTicks start_time = manager_->Delegate()->NowTicks(); |
| std::unique_ptr<VirtualTimeDomain> domain_a( |
| new VirtualTimeDomain(start_time)); |
| manager_->RegisterTimeDomain(domain_a.get()); |
| runners_[0]->SetTimeDomain(domain_a.get()); |
| |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| base::TimeDelta::FromMilliseconds(10)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), |
| base::TimeDelta::FromMilliseconds(20)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), |
| base::TimeDelta::FromMilliseconds(30)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order), |
| base::TimeDelta::FromMilliseconds(40)); |
| |
| domain_a->AdvanceTo(start_time + base::TimeDelta::FromMilliseconds(20)); |
| manager_->MaybeScheduleImmediateWork(FROM_HERE); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2)); |
| |
| std::unique_ptr<VirtualTimeDomain> domain_b( |
| new VirtualTimeDomain(start_time)); |
| manager_->RegisterTimeDomain(domain_b.get()); |
| runners_[0]->SetTimeDomain(domain_b.get()); |
| |
| domain_b->AdvanceTo(start_time + base::TimeDelta::FromMilliseconds(50)); |
| manager_->MaybeScheduleImmediateWork(FROM_HERE); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4)); |
| |
| runners_[0]->ShutdownTaskQueue(); |
| |
| manager_->UnregisterTimeDomain(domain_a.get()); |
| manager_->UnregisterTimeDomain(domain_b.get()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TimeDomainMigrationWithIncomingImmediateTasks) { |
| Initialize(1u); |
| |
| base::TimeTicks start_time = manager_->Delegate()->NowTicks(); |
| std::unique_ptr<VirtualTimeDomain> domain_a( |
| new VirtualTimeDomain(start_time)); |
| std::unique_ptr<VirtualTimeDomain> domain_b( |
| new VirtualTimeDomain(start_time)); |
| manager_->RegisterTimeDomain(domain_a.get()); |
| manager_->RegisterTimeDomain(domain_b.get()); |
| |
| runners_[0]->SetTimeDomain(domain_a.get()); |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->SetTimeDomain(domain_b.get()); |
| |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_THAT(run_order, ElementsAre(1)); |
| |
| runners_[0]->ShutdownTaskQueue(); |
| |
| manager_->UnregisterTimeDomain(domain_a.get()); |
| manager_->UnregisterTimeDomain(domain_b.get()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, |
| PostDelayedTasksReverseOrderAlternatingTimeDomains) { |
| Initialize(1u); |
| |
| std::vector<EnqueueOrder> run_order; |
| |
| std::unique_ptr<RealTimeDomain> domain_a(new RealTimeDomain()); |
| std::unique_ptr<RealTimeDomain> domain_b(new RealTimeDomain()); |
| manager_->RegisterTimeDomain(domain_a.get()); |
| manager_->RegisterTimeDomain(domain_b.get()); |
| |
| runners_[0]->SetTimeDomain(domain_a.get()); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order), |
| base::TimeDelta::FromMilliseconds(40)); |
| |
| runners_[0]->SetTimeDomain(domain_b.get()); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order), |
| base::TimeDelta::FromMilliseconds(30)); |
| |
| runners_[0]->SetTimeDomain(domain_a.get()); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order), |
| base::TimeDelta::FromMilliseconds(20)); |
| |
| runners_[0]->SetTimeDomain(domain_b.get()); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order), |
| base::TimeDelta::FromMilliseconds(10)); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(40)); |
| EXPECT_THAT(run_order, ElementsAre(4, 3, 2, 1)); |
| |
| runners_[0]->ShutdownTaskQueue(); |
| |
| manager_->UnregisterTimeDomain(domain_a.get()); |
| manager_->UnregisterTimeDomain(domain_b.get()); |
| } |
| |
| namespace { |
| |
| class MockTaskQueueObserver : public TaskQueue::Observer { |
| public: |
| ~MockTaskQueueObserver() override {} |
| |
| MOCK_METHOD2(OnQueueNextWakeUpChanged, void(TaskQueue*, base::TimeTicks)); |
| }; |
| |
| } // namespace |
| |
| TEST_F(TaskQueueManagerTest, TaskQueueObserver_ImmediateTask) { |
| Initialize(1u); |
| |
| MockTaskQueueObserver observer; |
| runners_[0]->SetObserver(&observer); |
| |
| // We should get a notification when a task is posted on an empty queue. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // But not subsequently. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // Unless the immediate work queue is emptied. |
| runners_[0]->GetTaskQueueImpl()->ReloadImmediateWorkQueueIfEmpty(); |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| |
| // Tidy up. |
| runners_[0]->ShutdownTaskQueue(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TaskQueueObserver_DelayedTask) { |
| Initialize(1u); |
| |
| base::TimeTicks start_time = manager_->Delegate()->NowTicks(); |
| base::TimeDelta delay10s(base::TimeDelta::FromSeconds(10)); |
| base::TimeDelta delay100s(base::TimeDelta::FromSeconds(100)); |
| base::TimeDelta delay1s(base::TimeDelta::FromSeconds(1)); |
| |
| MockTaskQueueObserver observer; |
| runners_[0]->SetObserver(&observer); |
| |
| // We should get a notification when a delayed task is posted on an empty |
| // queue. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), |
| start_time + delay10s)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay10s); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // We should not get a notification for a longer delay. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay100s); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // We should get a notification for a shorter delay. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), |
| start_time + delay1s)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay1s); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // When a queue has been enabled, we may get a notification if the |
| // TimeDomain's next scheduled wake-up has changed. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), |
| start_time + delay1s)); |
| voter->SetQueueEnabled(true); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // Tidy up. |
| runners_[0]->ShutdownTaskQueue(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TaskQueueObserver_DelayedTaskMultipleQueues) { |
| Initialize(2u); |
| |
| MockTaskQueueObserver observer; |
| runners_[0]->SetObserver(&observer); |
| runners_[1]->SetObserver(&observer); |
| |
| base::TimeTicks start_time = manager_->Delegate()->NowTicks(); |
| base::TimeDelta delay1s(base::TimeDelta::FromSeconds(1)); |
| base::TimeDelta delay10s(base::TimeDelta::FromSeconds(10)); |
| |
| EXPECT_CALL(observer, |
| OnQueueNextWakeUpChanged(runners_[0].get(), start_time + delay1s)) |
| .Times(1); |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[1].get(), |
| start_time + delay10s)) |
| .Times(1); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay1s); |
| runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay10s); |
| ::testing::Mock::VerifyAndClearExpectations(&observer); |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter0 = |
| runners_[0]->CreateQueueEnabledVoter(); |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter1 = |
| runners_[1]->CreateQueueEnabledVoter(); |
| |
| // Disabling a queue should not trigger a notification. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0); |
| voter0->SetQueueEnabled(false); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // Re-enabling it should should also trigger a notification. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), |
| start_time + delay1s)); |
| voter0->SetQueueEnabled(true); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // Disabling a queue should not trigger a notification. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0); |
| voter1->SetQueueEnabled(false); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // Re-enabling it should should trigger a notification. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[1].get(), |
| start_time + delay10s)); |
| voter1->SetQueueEnabled(true); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // Tidy up. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(AnyNumber()); |
| runners_[0]->ShutdownTaskQueue(); |
| runners_[1]->ShutdownTaskQueue(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TaskQueueObserver_DelayedWorkWhichCanRunNow) { |
| // This test checks that when delayed work becomes available |
| // the notification still fires. This usually happens when time advances |
| // and task becomes available in the middle of the scheduling code. |
| // For this test we rely on the fact that notification dispatching code |
| // is the same in all conditions and just change a time domain to |
| // trigger notification. |
| |
| Initialize(1u); |
| |
| base::TimeDelta delay1s(base::TimeDelta::FromSeconds(1)); |
| base::TimeDelta delay10s(base::TimeDelta::FromSeconds(10)); |
| |
| MockTaskQueueObserver observer; |
| runners_[0]->SetObserver(&observer); |
| |
| // We should get a notification when a delayed task is posted on an empty |
| // queue. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay1s); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| std::unique_ptr<TimeDomain> mock_time_domain = |
| std::make_unique<RealTimeDomain>(); |
| manager_->RegisterTimeDomain(mock_time_domain.get()); |
| |
| now_src_->Advance(delay10s); |
| |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)); |
| runners_[0]->SetTimeDomain(mock_time_domain.get()); |
| Mock::VerifyAndClearExpectations(&observer); |
| |
| // Tidy up. |
| runners_[0]->ShutdownTaskQueue(); |
| } |
| |
| class CancelableTask { |
| public: |
| explicit CancelableTask(base::TickClock* clock) |
| : clock_(clock), weak_factory_(this) {} |
| |
| void RecordTimeTask(std::vector<base::TimeTicks>* run_times) { |
| run_times->push_back(clock_->NowTicks()); |
| } |
| |
| base::TickClock* clock_; |
| base::WeakPtrFactory<CancelableTask> weak_factory_; |
| }; |
| |
| TEST_F(TaskQueueManagerTest, TaskQueueObserver_SweepCanceledDelayedTasks) { |
| Initialize(1u); |
| |
| MockTaskQueueObserver observer; |
| runners_[0]->SetObserver(&observer); |
| |
| base::TimeTicks start_time = manager_->Delegate()->NowTicks(); |
| base::TimeDelta delay1(base::TimeDelta::FromSeconds(5)); |
| base::TimeDelta delay2(base::TimeDelta::FromSeconds(10)); |
| |
| EXPECT_CALL(observer, |
| OnQueueNextWakeUpChanged(runners_[0].get(), start_time + delay1)) |
| .Times(1); |
| |
| CancelableTask task1(now_src_.get()); |
| CancelableTask task2(now_src_.get()); |
| std::vector<base::TimeTicks> run_times; |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&CancelableTask::RecordTimeTask, |
| task1.weak_factory_.GetWeakPtr(), &run_times), |
| delay1); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&CancelableTask::RecordTimeTask, |
| task2.weak_factory_.GetWeakPtr(), &run_times), |
| delay2); |
| |
| task1.weak_factory_.InvalidateWeakPtrs(); |
| |
| // Sweeping away canceled delayed tasks should trigger a notification. |
| EXPECT_CALL(observer, |
| OnQueueNextWakeUpChanged(runners_[0].get(), start_time + delay2)) |
| .Times(1); |
| manager_->SweepCanceledDelayedTasks(); |
| } |
| |
| namespace { |
| void ChromiumRunloopInspectionTask( |
| scoped_refptr<cc::OrderedSimpleTaskRunner> test_task_runner) { |
| EXPECT_EQ(1u, test_task_runner->NumPendingTasks()); |
| } |
| } // namespace |
| |
| TEST_F(TaskQueueManagerTest, NumberOfPendingTasksOnChromiumRunLoop) { |
| Initialize(1u); |
| |
| // NOTE because tasks posted to the chromiumrun loop are not cancellable, we |
| // will end up with a lot more tasks posted if the delayed tasks were posted |
| // in the reverse order. |
| // TODO(alexclarke): Consider talking to the message pump directly. |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| for (int i = 1; i < 100; i++) { |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&ChromiumRunloopInspectionTask, test_task_runner_), |
| base::TimeDelta::FromMilliseconds(i)); |
| } |
| test_task_runner_->RunUntilIdle(); |
| } |
| |
| namespace { |
| |
| class QuadraticTask { |
| public: |
| QuadraticTask(scoped_refptr<TaskQueue> task_queue, |
| base::TimeDelta delay, |
| base::SimpleTestTickClock* now_src) |
| : count_(0), task_queue_(task_queue), delay_(delay), now_src_(now_src) {} |
| |
| void SetShouldExit(base::Callback<bool()> should_exit) { |
| should_exit_ = should_exit; |
| } |
| |
| void Run() { |
| if (should_exit_.Run()) |
| return; |
| count_++; |
| task_queue_->PostDelayedTask( |
| FROM_HERE, base::Bind(&QuadraticTask::Run, base::Unretained(this)), |
| delay_); |
| task_queue_->PostDelayedTask( |
| FROM_HERE, base::Bind(&QuadraticTask::Run, base::Unretained(this)), |
| delay_); |
| now_src_->Advance(base::TimeDelta::FromMilliseconds(5)); |
| } |
| |
| int Count() const { return count_; } |
| |
| private: |
| int count_; |
| scoped_refptr<TaskQueue> task_queue_; |
| base::TimeDelta delay_; |
| base::Callback<bool()> should_exit_; |
| base::SimpleTestTickClock* now_src_; |
| }; |
| |
| class LinearTask { |
| public: |
| LinearTask(scoped_refptr<TaskQueue> task_queue, |
| base::TimeDelta delay, |
| base::SimpleTestTickClock* now_src) |
| : count_(0), task_queue_(task_queue), delay_(delay), now_src_(now_src) {} |
| |
| void SetShouldExit(base::Callback<bool()> should_exit) { |
| should_exit_ = should_exit; |
| } |
| |
| void Run() { |
| if (should_exit_.Run()) |
| return; |
| count_++; |
| task_queue_->PostDelayedTask( |
| FROM_HERE, base::Bind(&LinearTask::Run, base::Unretained(this)), |
| delay_); |
| now_src_->Advance(base::TimeDelta::FromMilliseconds(5)); |
| } |
| |
| int Count() const { return count_; } |
| |
| private: |
| int count_; |
| scoped_refptr<TaskQueue> task_queue_; |
| base::TimeDelta delay_; |
| base::Callback<bool()> should_exit_; |
| base::SimpleTestTickClock* now_src_; |
| }; |
| |
| bool ShouldExit(QuadraticTask* quadratic_task, LinearTask* linear_task) { |
| return quadratic_task->Count() == 1000 || linear_task->Count() == 1000; |
| } |
| |
| } // namespace |
| |
| TEST_F(TaskQueueManagerTest, |
| DelayedTasksDontBadlyStarveNonDelayedWork_SameQueue) { |
| Initialize(1u); |
| |
| QuadraticTask quadratic_delayed_task( |
| runners_[0], base::TimeDelta::FromMilliseconds(10), now_src_.get()); |
| LinearTask linear_immediate_task(runners_[0], base::TimeDelta(), |
| now_src_.get()); |
| base::Callback<bool()> should_exit = |
| base::Bind(ShouldExit, &quadratic_delayed_task, &linear_immediate_task); |
| quadratic_delayed_task.SetShouldExit(should_exit); |
| linear_immediate_task.SetShouldExit(should_exit); |
| |
| quadratic_delayed_task.Run(); |
| linear_immediate_task.Run(); |
| |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| test_task_runner_->RunUntilIdle(); |
| |
| double ratio = static_cast<double>(linear_immediate_task.Count()) / |
| static_cast<double>(quadratic_delayed_task.Count()); |
| |
| EXPECT_GT(ratio, 0.333); |
| EXPECT_LT(ratio, 1.1); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ImmediateWorkCanStarveDelayedTasks_SameQueue) { |
| Initialize(1u); |
| |
| QuadraticTask quadratic_immediate_task(runners_[0], base::TimeDelta(), |
| now_src_.get()); |
| LinearTask linear_delayed_task( |
| runners_[0], base::TimeDelta::FromMilliseconds(10), now_src_.get()); |
| base::Callback<bool()> should_exit = |
| base::Bind(&ShouldExit, &quadratic_immediate_task, &linear_delayed_task); |
| |
| quadratic_immediate_task.SetShouldExit(should_exit); |
| linear_delayed_task.SetShouldExit(should_exit); |
| |
| quadratic_immediate_task.Run(); |
| linear_delayed_task.Run(); |
| |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| test_task_runner_->RunUntilIdle(); |
| |
| double ratio = static_cast<double>(linear_delayed_task.Count()) / |
| static_cast<double>(quadratic_immediate_task.Count()); |
| |
| // This is by design, we want to enforce a strict ordering in task execution |
| // where by delayed tasks can not skip ahead of non-delayed work. |
| EXPECT_GT(ratio, 0.0); |
| EXPECT_LT(ratio, 0.1); |
| } |
| |
| TEST_F(TaskQueueManagerTest, |
| DelayedTasksDontBadlyStarveNonDelayedWork_DifferentQueue) { |
| Initialize(2u); |
| |
| QuadraticTask quadratic_delayed_task( |
| runners_[0], base::TimeDelta::FromMilliseconds(10), now_src_.get()); |
| LinearTask linear_immediate_task(runners_[1], base::TimeDelta(), |
| now_src_.get()); |
| base::Callback<bool()> should_exit = |
| base::Bind(ShouldExit, &quadratic_delayed_task, &linear_immediate_task); |
| quadratic_delayed_task.SetShouldExit(should_exit); |
| linear_immediate_task.SetShouldExit(should_exit); |
| |
| quadratic_delayed_task.Run(); |
| linear_immediate_task.Run(); |
| |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| test_task_runner_->RunUntilIdle(); |
| |
| double ratio = static_cast<double>(linear_immediate_task.Count()) / |
| static_cast<double>(quadratic_delayed_task.Count()); |
| |
| EXPECT_GT(ratio, 0.333); |
| EXPECT_LT(ratio, 1.1); |
| } |
| |
| TEST_F(TaskQueueManagerTest, |
| ImmediateWorkCanStarveDelayedTasks_DifferentQueue) { |
| Initialize(2u); |
| |
| QuadraticTask quadratic_immediate_task(runners_[0], base::TimeDelta(), |
| now_src_.get()); |
| LinearTask linear_delayed_task( |
| runners_[1], base::TimeDelta::FromMilliseconds(10), now_src_.get()); |
| base::Callback<bool()> should_exit = |
| base::Bind(&ShouldExit, &quadratic_immediate_task, &linear_delayed_task); |
| |
| quadratic_immediate_task.SetShouldExit(should_exit); |
| linear_delayed_task.SetShouldExit(should_exit); |
| |
| quadratic_immediate_task.Run(); |
| linear_delayed_task.Run(); |
| |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| test_task_runner_->RunUntilIdle(); |
| |
| double ratio = static_cast<double>(linear_delayed_task.Count()) / |
| static_cast<double>(quadratic_immediate_task.Count()); |
| |
| // This is by design, we want to enforce a strict ordering in task execution |
| // where by delayed tasks can not skip ahead of non-delayed work. |
| EXPECT_GT(ratio, 0.0); |
| EXPECT_LT(ratio, 0.1); |
| } |
| |
| TEST_F(TaskQueueManagerTest, CurrentlyExecutingTaskQueue_NoTaskRunning) { |
| Initialize(1u); |
| |
| EXPECT_EQ(nullptr, manager_->currently_executing_task_queue()); |
| } |
| |
| namespace { |
| void CurrentlyExecutingTaskQueueTestTask( |
| TaskQueueManager* task_queue_manager, |
| std::vector<internal::TaskQueueImpl*>* task_sources) { |
| task_sources->push_back(task_queue_manager->currently_executing_task_queue()); |
| } |
| } |
| |
| TEST_F(TaskQueueManagerTest, CurrentlyExecutingTaskQueue_TaskRunning) { |
| Initialize(2u); |
| |
| TestTaskQueue* queue0 = runners_[0].get(); |
| TestTaskQueue* queue1 = runners_[1].get(); |
| |
| std::vector<internal::TaskQueueImpl*> task_sources; |
| queue0->PostTask(FROM_HERE, base::Bind(&CurrentlyExecutingTaskQueueTestTask, |
| manager_.get(), &task_sources)); |
| queue1->PostTask(FROM_HERE, base::Bind(&CurrentlyExecutingTaskQueueTestTask, |
| manager_.get(), &task_sources)); |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_THAT(task_sources, ElementsAre(queue0->GetTaskQueueImpl(), |
| queue1->GetTaskQueueImpl())); |
| EXPECT_EQ(nullptr, manager_->currently_executing_task_queue()); |
| } |
| |
| namespace { |
| void RunloopCurrentlyExecutingTaskQueueTestTask( |
| base::MessageLoop* message_loop, |
| TaskQueueManager* task_queue_manager, |
| std::vector<internal::TaskQueueImpl*>* task_sources, |
| std::vector<std::pair<base::Closure, TestTaskQueue*>>* tasks) { |
| base::MessageLoop::ScopedNestableTaskAllower allow(message_loop); |
| task_sources->push_back(task_queue_manager->currently_executing_task_queue()); |
| |
| for (std::pair<base::Closure, TestTaskQueue*>& pair : *tasks) { |
| pair.second->PostTask(FROM_HERE, pair.first); |
| } |
| |
| base::RunLoop().RunUntilIdle(); |
| task_sources->push_back(task_queue_manager->currently_executing_task_queue()); |
| } |
| } |
| |
| TEST_F(TaskQueueManagerTest, CurrentlyExecutingTaskQueue_NestedLoop) { |
| InitializeWithRealMessageLoop(3u); |
| |
| TestTaskQueue* queue0 = runners_[0].get(); |
| TestTaskQueue* queue1 = runners_[1].get(); |
| TestTaskQueue* queue2 = runners_[2].get(); |
| |
| std::vector<internal::TaskQueueImpl*> task_sources; |
| std::vector<std::pair<base::Closure, TestTaskQueue*>> |
| tasks_to_post_from_nested_loop; |
| tasks_to_post_from_nested_loop.push_back( |
| std::make_pair(base::Bind(&CurrentlyExecutingTaskQueueTestTask, |
| manager_.get(), &task_sources), |
| queue1)); |
| tasks_to_post_from_nested_loop.push_back( |
| std::make_pair(base::Bind(&CurrentlyExecutingTaskQueueTestTask, |
| manager_.get(), &task_sources), |
| queue2)); |
| |
| queue0->PostTask( |
| FROM_HERE, base::Bind(&RunloopCurrentlyExecutingTaskQueueTestTask, |
| message_loop_.get(), manager_.get(), &task_sources, |
| &tasks_to_post_from_nested_loop)); |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_THAT( |
| task_sources, |
| ElementsAre(queue0->GetTaskQueueImpl(), queue1->GetTaskQueueImpl(), |
| queue2->GetTaskQueueImpl(), queue0->GetTaskQueueImpl())); |
| EXPECT_EQ(nullptr, manager_->currently_executing_task_queue()); |
| } |
| |
| void OnTraceDataCollected(base::Closure quit_closure, |
| base::trace_event::TraceResultBuffer* buffer, |
| const scoped_refptr<base::RefCountedString>& json, |
| bool has_more_events) { |
| buffer->AddFragment(json->data()); |
| if (!has_more_events) |
| quit_closure.Run(); |
| } |
| |
| class TaskQueueManagerTestWithTracing : public TaskQueueManagerTest { |
| public: |
| void StartTracing(); |
| void StopTracing(); |
| std::unique_ptr<trace_analyzer::TraceAnalyzer> CreateTraceAnalyzer(); |
| }; |
| |
| void TaskQueueManagerTestWithTracing::StartTracing() { |
| base::trace_event::TraceLog::GetInstance()->SetEnabled( |
| base::trace_event::TraceConfig("*"), |
| base::trace_event::TraceLog::RECORDING_MODE); |
| } |
| |
| void TaskQueueManagerTestWithTracing::StopTracing() { |
| base::trace_event::TraceLog::GetInstance()->SetDisabled(); |
| } |
| |
| std::unique_ptr<trace_analyzer::TraceAnalyzer> |
| TaskQueueManagerTestWithTracing::CreateTraceAnalyzer() { |
| base::trace_event::TraceResultBuffer buffer; |
| base::trace_event::TraceResultBuffer::SimpleOutput trace_output; |
| buffer.SetOutputCallback(trace_output.GetCallback()); |
| base::RunLoop run_loop; |
| buffer.Start(); |
| base::trace_event::TraceLog::GetInstance()->Flush( |
| Bind(&OnTraceDataCollected, run_loop.QuitClosure(), |
| base::Unretained(&buffer))); |
| run_loop.Run(); |
| buffer.Finish(); |
| |
| return base::WrapUnique( |
| trace_analyzer::TraceAnalyzer::Create(trace_output.json_output)); |
| } |
| |
| TEST_F(TaskQueueManagerTestWithTracing, BlameContextAttribution) { |
| using trace_analyzer::Query; |
| |
| InitializeWithRealMessageLoop(1u); |
| TestTaskQueue* queue = runners_[0].get(); |
| |
| StartTracing(); |
| { |
| base::trace_event::BlameContext blame_context("cat", "name", "type", |
| "scope", 0, nullptr); |
| blame_context.Initialize(); |
| queue->SetBlameContext(&blame_context); |
| queue->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| base::RunLoop().RunUntilIdle(); |
| } |
| StopTracing(); |
| std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer = |
| CreateTraceAnalyzer(); |
| |
| trace_analyzer::TraceEventVector events; |
| Query q = Query::EventPhaseIs(TRACE_EVENT_PHASE_ENTER_CONTEXT) || |
| Query::EventPhaseIs(TRACE_EVENT_PHASE_LEAVE_CONTEXT); |
| analyzer->FindEvents(q, &events); |
| |
| EXPECT_EQ(2u, events.size()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, NoWakeUpsForCanceledDelayedTasks) { |
| Initialize(1u); |
| |
| base::TimeTicks start_time = manager_->Delegate()->NowTicks(); |
| |
| CancelableTask task1(now_src_.get()); |
| CancelableTask task2(now_src_.get()); |
| CancelableTask task3(now_src_.get()); |
| CancelableTask task4(now_src_.get()); |
| base::TimeDelta delay1(base::TimeDelta::FromSeconds(5)); |
| base::TimeDelta delay2(base::TimeDelta::FromSeconds(10)); |
| base::TimeDelta delay3(base::TimeDelta::FromSeconds(15)); |
| base::TimeDelta delay4(base::TimeDelta::FromSeconds(30)); |
| std::vector<base::TimeTicks> run_times; |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task1.weak_factory_.GetWeakPtr(), &run_times), |
| delay1); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task2.weak_factory_.GetWeakPtr(), &run_times), |
| delay2); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task3.weak_factory_.GetWeakPtr(), &run_times), |
| delay3); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task4.weak_factory_.GetWeakPtr(), &run_times), |
| delay4); |
| |
| task2.weak_factory_.InvalidateWeakPtrs(); |
| task3.weak_factory_.InvalidateWeakPtrs(); |
| |
| std::set<base::TimeTicks> wake_up_times; |
| |
| RunUntilIdle(base::Bind( |
| [](std::set<base::TimeTicks>* wake_up_times, |
| base::SimpleTestTickClock* clock) { |
| wake_up_times->insert(clock->NowTicks()); |
| }, |
| &wake_up_times, now_src_.get())); |
| |
| EXPECT_THAT(wake_up_times, |
| ElementsAre(start_time + delay1, start_time + delay4)); |
| EXPECT_THAT(run_times, ElementsAre(start_time + delay1, start_time + delay4)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, NoWakeUpsForCanceledDelayedTasksReversePostOrder) { |
| Initialize(1u); |
| |
| base::TimeTicks start_time = manager_->Delegate()->NowTicks(); |
| |
| CancelableTask task1(now_src_.get()); |
| CancelableTask task2(now_src_.get()); |
| CancelableTask task3(now_src_.get()); |
| CancelableTask task4(now_src_.get()); |
| base::TimeDelta delay1(base::TimeDelta::FromSeconds(5)); |
| base::TimeDelta delay2(base::TimeDelta::FromSeconds(10)); |
| base::TimeDelta delay3(base::TimeDelta::FromSeconds(15)); |
| base::TimeDelta delay4(base::TimeDelta::FromSeconds(30)); |
| std::vector<base::TimeTicks> run_times; |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task4.weak_factory_.GetWeakPtr(), &run_times), |
| delay4); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task3.weak_factory_.GetWeakPtr(), &run_times), |
| delay3); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task2.weak_factory_.GetWeakPtr(), &run_times), |
| delay2); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task1.weak_factory_.GetWeakPtr(), &run_times), |
| delay1); |
| |
| task2.weak_factory_.InvalidateWeakPtrs(); |
| task3.weak_factory_.InvalidateWeakPtrs(); |
| |
| std::set<base::TimeTicks> wake_up_times; |
| |
| RunUntilIdle(base::Bind( |
| [](std::set<base::TimeTicks>* wake_up_times, |
| base::SimpleTestTickClock* clock) { |
| wake_up_times->insert(clock->NowTicks()); |
| }, |
| &wake_up_times, now_src_.get())); |
| |
| EXPECT_THAT(wake_up_times, |
| ElementsAre(start_time + delay1, start_time + delay4)); |
| EXPECT_THAT(run_times, ElementsAre(start_time + delay1, start_time + delay4)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TimeDomainWakeUpOnlyCancelledIfAllUsesCancelled) { |
| Initialize(1u); |
| |
| base::TimeTicks start_time = manager_->Delegate()->NowTicks(); |
| |
| CancelableTask task1(now_src_.get()); |
| CancelableTask task2(now_src_.get()); |
| CancelableTask task3(now_src_.get()); |
| CancelableTask task4(now_src_.get()); |
| base::TimeDelta delay1(base::TimeDelta::FromSeconds(5)); |
| base::TimeDelta delay2(base::TimeDelta::FromSeconds(10)); |
| base::TimeDelta delay3(base::TimeDelta::FromSeconds(15)); |
| base::TimeDelta delay4(base::TimeDelta::FromSeconds(30)); |
| std::vector<base::TimeTicks> run_times; |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task1.weak_factory_.GetWeakPtr(), &run_times), |
| delay1); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task2.weak_factory_.GetWeakPtr(), &run_times), |
| delay2); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task3.weak_factory_.GetWeakPtr(), &run_times), |
| delay3); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task4.weak_factory_.GetWeakPtr(), &run_times), |
| delay4); |
| |
| // Post a non-canceled task with |delay3|. So we should still get a wake-up at |
| // |delay3| even though we cancel |task3|. |
| runners_[0]->PostDelayedTask(FROM_HERE, |
| base::Bind(&CancelableTask::RecordTimeTask, |
| base::Unretained(&task3), &run_times), |
| delay3); |
| |
| task2.weak_factory_.InvalidateWeakPtrs(); |
| task3.weak_factory_.InvalidateWeakPtrs(); |
| task1.weak_factory_.InvalidateWeakPtrs(); |
| |
| std::set<base::TimeTicks> wake_up_times; |
| |
| RunUntilIdle(base::Bind( |
| [](std::set<base::TimeTicks>* wake_up_times, |
| base::SimpleTestTickClock* clock) { |
| wake_up_times->insert(clock->NowTicks()); |
| }, |
| &wake_up_times, now_src_.get())); |
| |
| EXPECT_THAT(wake_up_times, |
| ElementsAre(start_time + delay1, start_time + delay3, |
| start_time + delay4)); |
| |
| EXPECT_THAT(run_times, ElementsAre(start_time + delay3, start_time + delay4)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, TaskQueueVoters) { |
| Initialize(1u); |
| |
| // The task queue should be initially enabled. |
| EXPECT_TRUE(runners_[0]->IsQueueEnabled()); |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter1 = |
| runners_[0]->CreateQueueEnabledVoter(); |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter2 = |
| runners_[0]->CreateQueueEnabledVoter(); |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter3 = |
| runners_[0]->CreateQueueEnabledVoter(); |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter4 = |
| runners_[0]->CreateQueueEnabledVoter(); |
| |
| // Voters should initially vote for the queue to be enabled. |
| EXPECT_TRUE(runners_[0]->IsQueueEnabled()); |
| |
| // If any voter wants to disable, the queue is disabled. |
| voter1->SetQueueEnabled(false); |
| EXPECT_FALSE(runners_[0]->IsQueueEnabled()); |
| |
| // If the voter is deleted then the queue should be re-enabled. |
| voter1.reset(); |
| EXPECT_TRUE(runners_[0]->IsQueueEnabled()); |
| |
| // If any of the remaining voters wants to disable, the queue should be |
| // disabled. |
| voter2->SetQueueEnabled(false); |
| EXPECT_FALSE(runners_[0]->IsQueueEnabled()); |
| |
| // If another queue votes to disable, nothing happens because it's already |
| // disabled. |
| voter3->SetQueueEnabled(false); |
| EXPECT_FALSE(runners_[0]->IsQueueEnabled()); |
| |
| // There are two votes to disable, so one of them voting to enable does |
| // nothing. |
| voter2->SetQueueEnabled(true); |
| EXPECT_FALSE(runners_[0]->IsQueueEnabled()); |
| |
| // IF all queues vote to enable then the queue is enabled. |
| voter3->SetQueueEnabled(true); |
| EXPECT_TRUE(runners_[0]->IsQueueEnabled()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ShutdownQueueBeforeEnabledVoterDeleted) { |
| Initialize(1u); |
| |
| scoped_refptr<TaskQueue> queue = CreateTaskQueue(); |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| queue->CreateQueueEnabledVoter(); |
| |
| voter->SetQueueEnabled(true); // NOP |
| queue->ShutdownTaskQueue(); |
| |
| // This should complete without DCHECKing. |
| voter.reset(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ShutdownQueueBeforeDisabledVoterDeleted) { |
| Initialize(1u); |
| |
| scoped_refptr<TaskQueue> queue = CreateTaskQueue(); |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| queue->CreateQueueEnabledVoter(); |
| |
| voter->SetQueueEnabled(false); |
| queue->ShutdownTaskQueue(); |
| |
| // This should complete without DCHECKing. |
| voter.reset(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, SweepCanceledDelayedTasks) { |
| Initialize(1u); |
| |
| CancelableTask task1(now_src_.get()); |
| CancelableTask task2(now_src_.get()); |
| CancelableTask task3(now_src_.get()); |
| CancelableTask task4(now_src_.get()); |
| base::TimeDelta delay1(base::TimeDelta::FromSeconds(5)); |
| base::TimeDelta delay2(base::TimeDelta::FromSeconds(10)); |
| base::TimeDelta delay3(base::TimeDelta::FromSeconds(15)); |
| base::TimeDelta delay4(base::TimeDelta::FromSeconds(30)); |
| std::vector<base::TimeTicks> run_times; |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task1.weak_factory_.GetWeakPtr(), &run_times), |
| delay1); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task2.weak_factory_.GetWeakPtr(), &run_times), |
| delay2); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task3.weak_factory_.GetWeakPtr(), &run_times), |
| delay3); |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&CancelableTask::RecordTimeTask, |
| task4.weak_factory_.GetWeakPtr(), &run_times), |
| delay4); |
| |
| EXPECT_EQ(4u, runners_[0]->GetNumberOfPendingTasks()); |
| task2.weak_factory_.InvalidateWeakPtrs(); |
| task3.weak_factory_.InvalidateWeakPtrs(); |
| EXPECT_EQ(4u, runners_[0]->GetNumberOfPendingTasks()); |
| |
| manager_->SweepCanceledDelayedTasks(); |
| EXPECT_EQ(2u, runners_[0]->GetNumberOfPendingTasks()); |
| |
| task1.weak_factory_.InvalidateWeakPtrs(); |
| task4.weak_factory_.InvalidateWeakPtrs(); |
| |
| manager_->SweepCanceledDelayedTasks(); |
| EXPECT_EQ(0u, runners_[0]->GetNumberOfPendingTasks()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ComputeDelayTillNextTask) { |
| Initialize(2u); |
| |
| LazyNow lazy_now(now_src_.get()); |
| EXPECT_FALSE(static_cast<bool>(ComputeDelayTillNextTask(&lazy_now))); |
| |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), |
| base::TimeDelta::FromSeconds(10)); |
| |
| EXPECT_EQ(base::TimeDelta::FromSeconds(10), |
| ComputeDelayTillNextTask(&lazy_now)->Delay()); |
| |
| runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), |
| base::TimeDelta::FromSeconds(15)); |
| |
| EXPECT_EQ(base::TimeDelta::FromSeconds(10), |
| ComputeDelayTillNextTask(&lazy_now)->Delay()); |
| |
| runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), |
| base::TimeDelta::FromSeconds(5)); |
| |
| EXPECT_EQ(base::TimeDelta::FromSeconds(5), |
| ComputeDelayTillNextTask(&lazy_now)->Delay()); |
| |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| |
| EXPECT_EQ(base::TimeDelta(), ComputeDelayTillNextTask(&lazy_now)->Delay()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ComputeDelayTillNextTask_Disabled) { |
| Initialize(1u); |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| |
| LazyNow lazy_now(now_src_.get()); |
| EXPECT_FALSE(ComputeDelayTillNextTask(&lazy_now)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ComputeDelayTillNextTask_Fence) { |
| Initialize(1u); |
| |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| |
| LazyNow lazy_now(now_src_.get()); |
| EXPECT_FALSE(ComputeDelayTillNextTask(&lazy_now)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ComputeDelayTillNextTask_FenceUnblocking) { |
| Initialize(1u); |
| |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| |
| LazyNow lazy_now(now_src_.get()); |
| EXPECT_EQ(base::TimeDelta(), ComputeDelayTillNextTask(&lazy_now)->Delay()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ComputeDelayTillNextTask_DelayedTaskReady) { |
| Initialize(1u); |
| |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), |
| base::TimeDelta::FromSeconds(1)); |
| |
| now_src_->Advance(base::TimeDelta::FromSeconds(10)); |
| |
| LazyNow lazy_now(now_src_.get()); |
| EXPECT_EQ(base::TimeDelta(), ComputeDelayTillNextTask(&lazy_now)->Delay()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_NoMoreWork) { |
| Initialize(1u); |
| |
| LazyNow lazy_now(now_src_.get()); |
| PostDoWorkContinuation(base::Optional<NextTaskDelay>(), &lazy_now); |
| |
| EXPECT_EQ(0u, test_task_runner_->NumPendingTasks()); |
| EXPECT_EQ(0, immediate_do_work_posted_count()); |
| EXPECT_TRUE(next_delayed_do_work_time().is_null()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_ImmediateWork) { |
| Initialize(1u); |
| |
| LazyNow lazy_now(now_src_.get()); |
| PostDoWorkContinuation(NextTaskDelay(), &lazy_now); |
| |
| EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); |
| EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime()); |
| EXPECT_EQ(1, immediate_do_work_posted_count()); |
| EXPECT_TRUE(next_delayed_do_work_time().is_null()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_DelayedWorkInThePast) { |
| Initialize(1u); |
| |
| LazyNow lazy_now(now_src_.get()); |
| // Note this isn't supposed to happen in practice. |
| PostDoWorkContinuation( |
| NextTaskDelay(base::TimeDelta::FromSeconds(-1), |
| runners_[0]->GetTimeDomain(), |
| NextTaskDelay::AllowAnyDelayForTesting()), |
| &lazy_now); |
| |
| EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); |
| EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime()); |
| EXPECT_EQ(1, immediate_do_work_posted_count()); |
| EXPECT_TRUE(next_delayed_do_work_time().is_null()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_DelayedWork) { |
| Initialize(1u); |
| |
| LazyNow lazy_now(now_src_.get()); |
| PostDoWorkContinuation(NextTaskDelay(base::TimeDelta::FromSeconds(1), |
| runners_[0]->GetTimeDomain()), |
| &lazy_now); |
| |
| EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(1), |
| test_task_runner_->DelayToNextTaskTime()); |
| EXPECT_EQ(0, immediate_do_work_posted_count()); |
| EXPECT_EQ(lazy_now.Now() + base::TimeDelta::FromSeconds(1), |
| next_delayed_do_work_time()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, |
| PostDoWorkContinuation_DelayedWorkButImmediateDoWorkAlreadyPosted) { |
| Initialize(1u); |
| |
| MaybeScheduleImmediateWorkLocked(FROM_HERE); |
| EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); |
| EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime()); |
| EXPECT_EQ(1, immediate_do_work_posted_count()); |
| |
| LazyNow lazy_now(now_src_.get()); |
| PostDoWorkContinuation(NextTaskDelay(base::TimeDelta::FromSeconds(1), |
| runners_[0]->GetTimeDomain()), |
| &lazy_now); |
| |
| // Test that a delayed task didn't get posted. |
| EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); |
| EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime()); |
| EXPECT_EQ(1, immediate_do_work_posted_count()); |
| EXPECT_TRUE(next_delayed_do_work_time().is_null()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, PostDoWorkContinuation_DelayedWorkTimeChanges) { |
| Initialize(1u); |
| |
| LazyNow lazy_now(now_src_.get()); |
| PostDoWorkContinuation(NextTaskDelay(base::TimeDelta::FromSeconds(1), |
| runners_[0]->GetTimeDomain()), |
| &lazy_now); |
| |
| EXPECT_TRUE(test_task_runner_->HasPendingTasks()); |
| EXPECT_EQ(0, immediate_do_work_posted_count()); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(1), |
| test_task_runner_->DelayToNextTaskTime()); |
| EXPECT_EQ(lazy_now.Now() + base::TimeDelta::FromSeconds(1), |
| next_delayed_do_work_time()); |
| |
| PostDoWorkContinuation(NextTaskDelay(base::TimeDelta::FromSeconds(10), |
| runners_[0]->GetTimeDomain()), |
| &lazy_now); |
| |
| // This should have resulted in the previous task getting canceled and a new |
| // one getting posted. |
| EXPECT_EQ(2u, test_task_runner_->NumPendingTasks()); |
| test_task_runner_->RemoveCancelledTasks(); |
| EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(10), |
| test_task_runner_->DelayToNextTaskTime()); |
| EXPECT_EQ(0, immediate_do_work_posted_count()); |
| EXPECT_EQ(lazy_now.Now() + base::TimeDelta::FromSeconds(10), |
| next_delayed_do_work_time()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, |
| PostDoWorkContinuation_ImmediateWorkButDelayedDoWorkPending) { |
| Initialize(1u); |
| |
| LazyNow lazy_now(now_src_.get()); |
| PostDoWorkContinuation(NextTaskDelay(base::TimeDelta::FromSeconds(1), |
| runners_[0]->GetTimeDomain()), |
| &lazy_now); |
| |
| now_src_->Advance(base::TimeDelta::FromSeconds(1)); |
| lazy_now = LazyNow(now_src_.get()); |
| PostDoWorkContinuation(NextTaskDelay(), &lazy_now); |
| |
| // Because the delayed DoWork was pending we don't expect an immediate DoWork |
| // to get posted. |
| EXPECT_EQ(1u, test_task_runner_->NumPendingTasks()); |
| EXPECT_EQ(base::TimeDelta(), test_task_runner_->DelayToNextTaskTime()); |
| EXPECT_EQ(0, immediate_do_work_posted_count()); |
| EXPECT_EQ(lazy_now.Now(), next_delayed_do_work_time()); |
| } |
| |
| namespace { |
| void MessageLoopTaskWithDelayedQuit(base::MessageLoop* message_loop, |
| base::SimpleTestTickClock* now_src, |
| scoped_refptr<TaskQueue> task_queue) { |
| base::MessageLoop::ScopedNestableTaskAllower allow(message_loop); |
| base::RunLoop run_loop; |
| task_queue->PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), |
| base::TimeDelta::FromMilliseconds(100)); |
| now_src->Advance(base::TimeDelta::FromMilliseconds(200)); |
| run_loop.Run(); |
| } |
| } // namespace |
| |
| TEST_F(TaskQueueManagerTest, DelayedTaskRunsInNestedMessageLoop) { |
| InitializeWithRealMessageLoop(1u); |
| base::RunLoop run_loop; |
| runners_[0]->PostTask( |
| FROM_HERE, |
| base::Bind(&MessageLoopTaskWithDelayedQuit, message_loop_.get(), |
| now_src_.get(), base::RetainedRef(runners_[0]))); |
| run_loop.RunUntilIdle(); |
| } |
| |
| namespace { |
| void MessageLoopTaskWithImmediateQuit(base::MessageLoop* message_loop, |
| base::Closure non_nested_quit_closure, |
| scoped_refptr<TaskQueue> task_queue) { |
| base::MessageLoop::ScopedNestableTaskAllower allow(message_loop); |
| |
| base::RunLoop run_loop; |
| // Needed because entering the nested run loop causes a DoWork to get |
| // posted. |
| task_queue->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| task_queue->PostTask(FROM_HERE, run_loop.QuitClosure()); |
| run_loop.Run(); |
| non_nested_quit_closure.Run(); |
| } |
| } // namespace |
| |
| TEST_F(TaskQueueManagerTest, |
| DelayedNestedMessageLoopDoesntPreventTasksRunning) { |
| InitializeWithRealMessageLoop(1u); |
| base::RunLoop run_loop; |
| runners_[0]->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&MessageLoopTaskWithImmediateQuit, message_loop_.get(), |
| run_loop.QuitClosure(), base::RetainedRef(runners_[0])), |
| base::TimeDelta::FromMilliseconds(100)); |
| |
| now_src_->Advance(base::TimeDelta::FromMilliseconds(200)); |
| run_loop.Run(); |
| } |
| |
| TEST_F(TaskQueueManagerTest, CouldTaskRun_DisableAndReenable) { |
| Initialize(1u); |
| |
| EnqueueOrder enqueue_order = GetNextSequenceNumber(); |
| EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order)); |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order)); |
| |
| voter->SetQueueEnabled(true); |
| EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, CouldTaskRun_Fence) { |
| Initialize(1u); |
| |
| EnqueueOrder enqueue_order = GetNextSequenceNumber(); |
| EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order)); |
| |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order)); |
| |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime); |
| EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order)); |
| |
| runners_[0]->RemoveFence(); |
| EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, CouldTaskRun_FenceBeforeThenAfter) { |
| Initialize(1u); |
| |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| |
| EnqueueOrder enqueue_order = GetNextSequenceNumber(); |
| EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order)); |
| |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kNow); |
| EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->CouldTaskRun(enqueue_order)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DelayedDoWorkNotPostedForDisabledQueue) { |
| Initialize(1u); |
| |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), |
| base::TimeDelta::FromMilliseconds(1)); |
| ASSERT_TRUE(test_task_runner_->HasPendingTasks()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1), |
| test_task_runner_->DelayToNextTaskTime()); |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| |
| EXPECT_TRUE(test_task_runner_->HasPendingTasks()); |
| test_task_runner_->RemoveCancelledTasks(); |
| EXPECT_FALSE(test_task_runner_->HasPendingTasks()); |
| |
| voter->SetQueueEnabled(true); |
| ASSERT_TRUE(test_task_runner_->HasPendingTasks()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1), |
| test_task_runner_->DelayToNextTaskTime()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, DisablingQueuesChangesDelayTillNextDoWork) { |
| Initialize(3u); |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), |
| base::TimeDelta::FromMilliseconds(1)); |
| runners_[1]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), |
| base::TimeDelta::FromMilliseconds(10)); |
| runners_[2]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), |
| base::TimeDelta::FromMilliseconds(100)); |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter0 = |
| runners_[0]->CreateQueueEnabledVoter(); |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter1 = |
| runners_[1]->CreateQueueEnabledVoter(); |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter2 = |
| runners_[2]->CreateQueueEnabledVoter(); |
| |
| ASSERT_TRUE(test_task_runner_->HasPendingTasks()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1), |
| test_task_runner_->DelayToNextTaskTime()); |
| |
| voter0->SetQueueEnabled(false); |
| test_task_runner_->RemoveCancelledTasks(); |
| ASSERT_TRUE(test_task_runner_->HasPendingTasks()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(10), |
| test_task_runner_->DelayToNextTaskTime()); |
| |
| voter1->SetQueueEnabled(false); |
| test_task_runner_->RemoveCancelledTasks(); |
| ASSERT_TRUE(test_task_runner_->HasPendingTasks()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(100), |
| test_task_runner_->DelayToNextTaskTime()); |
| |
| voter2->SetQueueEnabled(false); |
| test_task_runner_->RemoveCancelledTasks(); |
| EXPECT_FALSE(test_task_runner_->HasPendingTasks()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, GetNextScheduledWakeUp) { |
| Initialize(1u); |
| |
| EXPECT_EQ(base::nullopt, runners_[0]->GetNextScheduledWakeUp()); |
| |
| base::TimeTicks start_time = manager_->Delegate()->NowTicks(); |
| base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(10); |
| base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(2); |
| |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay1); |
| EXPECT_EQ(start_time + delay1, runners_[0]->GetNextScheduledWakeUp()); |
| |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), delay2); |
| EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp()); |
| |
| // We don't have wake-ups scheduled for disabled queues. |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| EXPECT_EQ(base::nullopt, runners_[0]->GetNextScheduledWakeUp()); |
| |
| voter->SetQueueEnabled(true); |
| EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp()); |
| |
| // Immediate tasks shouldn't make any difference. |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&NopTask)); |
| EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp()); |
| |
| // Neither should fences. |
| runners_[0]->InsertFence(TaskQueue::InsertFencePosition::kBeginningOfTime); |
| EXPECT_EQ(start_time + delay2, runners_[0]->GetNextScheduledWakeUp()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, SetTimeDomainForDisabledQueue) { |
| Initialize(1u); |
| |
| MockTaskQueueObserver observer; |
| runners_[0]->SetObserver(&observer); |
| |
| runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&NopTask), |
| base::TimeDelta::FromMilliseconds(1)); |
| |
| std::unique_ptr<TaskQueue::QueueEnabledVoter> voter = |
| runners_[0]->CreateQueueEnabledVoter(); |
| voter->SetQueueEnabled(false); |
| |
| // We should not get a notification for a disabled queue. |
| EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0); |
| |
| std::unique_ptr<VirtualTimeDomain> domain( |
| new VirtualTimeDomain(manager_->Delegate()->NowTicks())); |
| manager_->RegisterTimeDomain(domain.get()); |
| runners_[0]->SetTimeDomain(domain.get()); |
| |
| // Tidy up. |
| runners_[0]->ShutdownTaskQueue(); |
| manager_->UnregisterTimeDomain(domain.get()); |
| } |
| |
| namespace { |
| void SetOnTaskHandlers(scoped_refptr<TestTaskQueue> task_queue, |
| int* start_counter, |
| int* complete_counter) { |
| task_queue->GetTaskQueueImpl()->SetOnTaskStartedHandler( |
| base::Bind([](int* counter, const TaskQueue::Task& task, |
| base::TimeTicks start) { ++(*counter); }, |
| start_counter)); |
| task_queue->GetTaskQueueImpl()->SetOnTaskCompletedHandler(base::Bind( |
| [](int* counter, const TaskQueue::Task& task, base::TimeTicks start, |
| base::TimeTicks end) { ++(*counter); }, |
| complete_counter)); |
| } |
| |
| void UnsetOnTaskHandlers(scoped_refptr<TestTaskQueue> task_queue) { |
| task_queue->GetTaskQueueImpl()->SetOnTaskStartedHandler( |
| base::Callback<void(const TaskQueue::Task& task, |
| base::TimeTicks start)>()); |
| task_queue->GetTaskQueueImpl()->SetOnTaskCompletedHandler( |
| base::Callback<void(const TaskQueue::Task& task, base::TimeTicks start, |
| base::TimeTicks end)>()); |
| } |
| } // namespace |
| |
| TEST_F(TaskQueueManagerTest, ProcessTasksWithoutTaskTimeObservers) { |
| Initialize(1u); |
| int start_counter = 0; |
| int complete_counter = 0; |
| std::vector<EnqueueOrder> run_order; |
| SetOnTaskHandlers(runners_[0], &start_counter, &complete_counter); |
| EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming()); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_EQ(start_counter, 3); |
| EXPECT_EQ(complete_counter, 3); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3)); |
| |
| UnsetOnTaskHandlers(runners_[0]); |
| EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming()); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 5, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 6, &run_order)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_EQ(start_counter, 3); |
| EXPECT_EQ(complete_counter, 3); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5, 6)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, ProcessTasksWithTaskTimeObservers) { |
| Initialize(1u); |
| int start_counter = 0; |
| int complete_counter = 0; |
| |
| manager_->AddTaskTimeObserver(&test_task_time_observer_); |
| SetOnTaskHandlers(runners_[0], &start_counter, &complete_counter); |
| EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming()); |
| std::vector<EnqueueOrder> run_order; |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 2, &run_order)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_EQ(start_counter, 2); |
| EXPECT_EQ(complete_counter, 2); |
| EXPECT_THAT(run_order, ElementsAre(1, 2)); |
| |
| UnsetOnTaskHandlers(runners_[0]); |
| EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming()); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 3, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 4, &run_order)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_EQ(start_counter, 2); |
| EXPECT_EQ(complete_counter, 2); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4)); |
| |
| manager_->RemoveTaskTimeObserver(&test_task_time_observer_); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 5, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 6, &run_order)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_EQ(start_counter, 2); |
| EXPECT_EQ(complete_counter, 2); |
| EXPECT_FALSE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming()); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5, 6)); |
| |
| SetOnTaskHandlers(runners_[0], &start_counter, &complete_counter); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 7, &run_order)); |
| runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 8, &run_order)); |
| test_task_runner_->RunUntilIdle(); |
| EXPECT_EQ(start_counter, 4); |
| EXPECT_EQ(complete_counter, 4); |
| EXPECT_TRUE(runners_[0]->GetTaskQueueImpl()->RequiresTaskTiming()); |
| EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4, 5, 6, 7, 8)); |
| } |
| |
| TEST_F(TaskQueueManagerTest, GracefulShutdown) { |
| Initialize(0u); |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| std::vector<base::TimeTicks> run_times; |
| scoped_refptr<TestTaskQueue> main_tq = CreateTaskQueue(); |
| base::WeakPtr<TestTaskQueue> main_tq_weak_ptr = main_tq->GetWeakPtr(); |
| |
| EXPECT_EQ(1u, manager_->ActiveQueuesCount()); |
| EXPECT_EQ(0u, manager_->QueuesToShutdownCount()); |
| EXPECT_EQ(0u, manager_->QueuesToDeleteCount()); |
| |
| for (int i = 1; i <= 5; ++i) { |
| main_tq->PostDelayedTask( |
| FROM_HERE, base::Bind(&RecordTimeTask, &run_times, now_src_.get()), |
| base::TimeDelta::FromMilliseconds(i * 100)); |
| } |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(250)); |
| |
| main_tq = nullptr; |
| // Ensure that task queue went away. |
| EXPECT_FALSE(main_tq_weak_ptr.get()); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1)); |
| |
| EXPECT_EQ(1u, manager_->ActiveQueuesCount()); |
| EXPECT_EQ(1u, manager_->QueuesToShutdownCount()); |
| EXPECT_EQ(0u, manager_->QueuesToDeleteCount()); |
| |
| test_task_runner_->RunUntilIdle(); |
| |
| // Even with TaskQueue gone, tasks are executed. |
| EXPECT_THAT( |
| run_times, |
| ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(101), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(201), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(301), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(401), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(501))); |
| |
| EXPECT_EQ(0u, manager_->ActiveQueuesCount()); |
| EXPECT_EQ(0u, manager_->QueuesToShutdownCount()); |
| EXPECT_EQ(0u, manager_->QueuesToDeleteCount()); |
| } |
| |
| TEST_F(TaskQueueManagerTest, GracefulShutdown_ManagerDeletedInFlight) { |
| Initialize(0u); |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| std::vector<base::TimeTicks> run_times; |
| scoped_refptr<TestTaskQueue> control_tq = CreateTaskQueue(); |
| std::vector<scoped_refptr<TestTaskQueue>> main_tqs; |
| std::vector<base::WeakPtr<TestTaskQueue>> main_tq_weak_ptrs; |
| |
| // There might be a race condition - async task queues should be unregistered |
| // first. Increase the number of task queues to surely detect that. |
| // The problem is that pointers are compared in a set and generally for |
| // a small number of allocations value of the pointers increases |
| // monotonically. 100 is large enough to force allocations from different |
| // pages. |
| const int N = 100; |
| for (int i = 0; i < N; ++i) { |
| scoped_refptr<TestTaskQueue> tq = CreateTaskQueue(); |
| main_tq_weak_ptrs.push_back(tq->GetWeakPtr()); |
| main_tqs.push_back(std::move(tq)); |
| } |
| |
| for (int i = 1; i <= 5; ++i) { |
| main_tqs[0]->PostDelayedTask( |
| FROM_HERE, base::Bind(&RecordTimeTask, &run_times, now_src_.get()), |
| base::TimeDelta::FromMilliseconds(i * 100)); |
| } |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(250)); |
| |
| main_tqs.clear(); |
| // Ensure that task queues went away. |
| for (int i = 0; i < N; ++i) { |
| EXPECT_FALSE(main_tq_weak_ptrs[i].get()); |
| } |
| |
| // No leaks should occur when TQM was destroyed before processing |
| // shutdown task and TaskQueueImpl should be safely deleted on a correct |
| // thread. |
| manager_.reset(); |
| |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_THAT( |
| run_times, |
| ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(101), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(201))); |
| } |
| |
| TEST_F(TaskQueueManagerTest, |
| GracefulShutdown_ManagerDeletedWithQueuesToShutdown) { |
| Initialize(0u); |
| test_task_runner_->SetAutoAdvanceNowToPendingTasks(true); |
| |
| std::vector<base::TimeTicks> run_times; |
| scoped_refptr<TestTaskQueue> main_tq = CreateTaskQueue(); |
| base::WeakPtr<TestTaskQueue> main_tq_weak_ptr = main_tq->GetWeakPtr(); |
| |
| EXPECT_EQ(1u, manager_->ActiveQueuesCount()); |
| EXPECT_EQ(0u, manager_->QueuesToShutdownCount()); |
| EXPECT_EQ(0u, manager_->QueuesToDeleteCount()); |
| |
| for (int i = 1; i <= 5; ++i) { |
| main_tq->PostDelayedTask( |
| FROM_HERE, base::Bind(&RecordTimeTask, &run_times, now_src_.get()), |
| base::TimeDelta::FromMilliseconds(i * 100)); |
| } |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(250)); |
| |
| main_tq = nullptr; |
| // Ensure that task queue went away. |
| EXPECT_FALSE(main_tq_weak_ptr.get()); |
| |
| test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1)); |
| |
| EXPECT_EQ(1u, manager_->ActiveQueuesCount()); |
| EXPECT_EQ(1u, manager_->QueuesToShutdownCount()); |
| EXPECT_EQ(0u, manager_->QueuesToDeleteCount()); |
| |
| // Ensure that all queues-to-gracefully-shutdown are properly unregistered. |
| manager_.reset(); |
| |
| test_task_runner_->RunUntilIdle(); |
| |
| EXPECT_THAT( |
| run_times, |
| ElementsAre(base::TimeTicks() + base::TimeDelta::FromMilliseconds(101), |
| base::TimeTicks() + base::TimeDelta::FromMilliseconds(201))); |
| } |
| |
| } // namespace task_queue_manager_unittest |
| } // namespace scheduler |
| } // namespace blink |