// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/task/sequence_manager/work_queue.h" #include #include #include "base/bind.h" #include "base/task/sequence_manager/real_time_domain.h" #include "base/task/sequence_manager/task_queue_impl.h" #include "base/task/sequence_manager/work_queue_sets.h" #include "testing/gmock/include/gmock/gmock.h" namespace base { namespace sequence_manager { namespace internal { namespace { void NopTask() {} struct Cancelable { Cancelable() : weak_ptr_factory(this) {} void NopTask() {} WeakPtrFactory weak_ptr_factory; }; } // namespace class WorkQueueTest : public testing::Test { public: void SetUp() override { time_domain_.reset(new RealTimeDomain()); task_queue_ = std::make_unique(nullptr, time_domain_.get(), TaskQueue::Spec("test")); work_queue_.reset(new WorkQueue(task_queue_.get(), "test", WorkQueue::QueueType::kImmediate)); work_queue_sets_.reset(new WorkQueueSets(1, "test")); work_queue_sets_->AddQueue(work_queue_.get(), 0); } void TearDown() override { work_queue_sets_->RemoveQueue(work_queue_.get()); } protected: TaskQueueImpl::Task FakeCancelableTaskWithEnqueueOrder( int enqueue_order, WeakPtr weak_ptr) { TaskQueueImpl::Task fake_task( TaskQueue::PostedTask(BindOnce(&Cancelable::NopTask, weak_ptr), FROM_HERE), TimeTicks(), EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_order)); return fake_task; } TaskQueueImpl::Task FakeTaskWithEnqueueOrder(int enqueue_order) { TaskQueueImpl::Task fake_task( TaskQueue::PostedTask(BindOnce(&NopTask), FROM_HERE), TimeTicks(), EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_order)); return fake_task; } TaskQueueImpl::Task FakeNonNestableTaskWithEnqueueOrder(int enqueue_order) { TaskQueueImpl::Task fake_task( TaskQueue::PostedTask(BindOnce(&NopTask), FROM_HERE), TimeTicks(), EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_order)); fake_task.nestable = Nestable::kNonNestable; return fake_task; } std::unique_ptr time_domain_; std::unique_ptr task_queue_; std::unique_ptr work_queue_; std::unique_ptr work_queue_sets_; std::unique_ptr incoming_queue_; }; TEST_F(WorkQueueTest, Empty) { EXPECT_TRUE(work_queue_->Empty()); work_queue_->Push(FakeTaskWithEnqueueOrder(1)); EXPECT_FALSE(work_queue_->Empty()); } TEST_F(WorkQueueTest, Empty_IgnoresFences) { work_queue_->Push(FakeTaskWithEnqueueOrder(1)); work_queue_->InsertFence(EnqueueOrder::blocking_fence()); EXPECT_FALSE(work_queue_->Empty()); } TEST_F(WorkQueueTest, GetFrontTaskEnqueueOrderQueueEmpty) { EnqueueOrder enqueue_order; EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); } TEST_F(WorkQueueTest, GetFrontTaskEnqueueOrder) { work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); EnqueueOrder enqueue_order; EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); EXPECT_EQ(2ull, enqueue_order); } TEST_F(WorkQueueTest, GetFrontTaskQueueEmpty) { EXPECT_EQ(nullptr, work_queue_->GetFrontTask()); } TEST_F(WorkQueueTest, GetFrontTask) { work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); ASSERT_NE(nullptr, work_queue_->GetFrontTask()); EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order()); } TEST_F(WorkQueueTest, GetBackTask_Empty) { EXPECT_EQ(nullptr, work_queue_->GetBackTask()); } TEST_F(WorkQueueTest, GetBackTask) { work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); ASSERT_NE(nullptr, work_queue_->GetBackTask()); EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order()); } TEST_F(WorkQueueTest, Push) { WorkQueue* work_queue; EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); work_queue_->Push(FakeTaskWithEnqueueOrder(2)); EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_EQ(work_queue_.get(), work_queue); } TEST_F(WorkQueueTest, PushAfterFenceHit) { work_queue_->InsertFence(EnqueueOrder::blocking_fence()); WorkQueue* work_queue; EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); work_queue_->Push(FakeTaskWithEnqueueOrder(2)); EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); } TEST_F(WorkQueueTest, PushNonNestableTaskToFront) { WorkQueue* work_queue; EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); work_queue_->PushNonNestableTaskToFront( FakeNonNestableTaskWithEnqueueOrder(3)); EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_EQ(work_queue_.get(), work_queue); work_queue_->PushNonNestableTaskToFront( FakeNonNestableTaskWithEnqueueOrder(2)); EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order()); EXPECT_EQ(3ull, work_queue_->GetBackTask()->enqueue_order()); } TEST_F(WorkQueueTest, PushNonNestableTaskToFrontAfterFenceHit) { work_queue_->InsertFence(EnqueueOrder::blocking_fence()); WorkQueue* work_queue; EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); work_queue_->PushNonNestableTaskToFront( FakeNonNestableTaskWithEnqueueOrder(2)); EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); } TEST_F(WorkQueueTest, PushNonNestableTaskToFrontBeforeFenceHit) { work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(3)); WorkQueue* work_queue; EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); work_queue_->PushNonNestableTaskToFront( FakeNonNestableTaskWithEnqueueOrder(2)); EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); } TEST_F(WorkQueueTest, ReloadEmptyImmediateQueue) { task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(2)); task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(3)); task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(4)); WorkQueue* work_queue; EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_TRUE(work_queue_->Empty()); work_queue_->ReloadEmptyImmediateQueue(); EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_FALSE(work_queue_->Empty()); ASSERT_NE(nullptr, work_queue_->GetFrontTask()); EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order()); ASSERT_NE(nullptr, work_queue_->GetBackTask()); EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order()); } TEST_F(WorkQueueTest, ReloadEmptyImmediateQueueAfterFenceHit) { work_queue_->InsertFence(EnqueueOrder::blocking_fence()); task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(2)); task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(3)); task_queue_->PushImmediateIncomingTaskForTest(FakeTaskWithEnqueueOrder(4)); WorkQueue* work_queue; EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_TRUE(work_queue_->Empty()); work_queue_->ReloadEmptyImmediateQueue(); EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_FALSE(work_queue_->Empty()); ASSERT_NE(nullptr, work_queue_->GetFrontTask()); EXPECT_EQ(2ull, work_queue_->GetFrontTask()->enqueue_order()); ASSERT_NE(nullptr, work_queue_->GetBackTask()); EXPECT_EQ(4ull, work_queue_->GetBackTask()->enqueue_order()); } TEST_F(WorkQueueTest, TakeTaskFromWorkQueue) { work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); WorkQueue* work_queue; EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_FALSE(work_queue_->Empty()); EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_EQ(3ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_EQ(4ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_TRUE(work_queue_->Empty()); } TEST_F(WorkQueueTest, TakeTaskFromWorkQueue_HitFence) { work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(3)); work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); EXPECT_FALSE(work_queue_->BlockedByFence()); WorkQueue* work_queue; EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_FALSE(work_queue_->Empty()); EXPECT_FALSE(work_queue_->BlockedByFence()); EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_FALSE(work_queue_->Empty()); EXPECT_TRUE(work_queue_->BlockedByFence()); } TEST_F(WorkQueueTest, InsertFenceBeforeEnqueueing) { EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::blocking_fence())); EXPECT_TRUE(work_queue_->BlockedByFence()); work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); EnqueueOrder enqueue_order; EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); } TEST_F(WorkQueueTest, InsertFenceAfterEnqueueingNonBlocking) { work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(5))); EXPECT_FALSE(work_queue_->BlockedByFence()); EnqueueOrder enqueue_order; EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); } TEST_F(WorkQueueTest, InsertFenceAfterEnqueueing) { work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); // NB in reality a fence will always be greater than any currently enqueued // tasks. EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::blocking_fence())); EXPECT_TRUE(work_queue_->BlockedByFence()); EnqueueOrder enqueue_order; EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); } TEST_F(WorkQueueTest, InsertNewFence) { work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); work_queue_->Push(FakeTaskWithEnqueueOrder(5)); EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(3))); EXPECT_FALSE(work_queue_->BlockedByFence()); // Note until TakeTaskFromWorkQueue() is called we don't hit the fence. EnqueueOrder enqueue_order; EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); EXPECT_EQ(2ull, enqueue_order); EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); EXPECT_TRUE(work_queue_->BlockedByFence()); // Inserting the new fence should temporarily unblock the queue until the new // one is hit. EXPECT_TRUE(work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(6))); EXPECT_FALSE(work_queue_->BlockedByFence()); EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); EXPECT_EQ(4ull, enqueue_order); EXPECT_EQ(4ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); EXPECT_FALSE(work_queue_->BlockedByFence()); } TEST_F(WorkQueueTest, PushWithNonEmptyQueueDoesNotHitFence) { work_queue_->Push(FakeTaskWithEnqueueOrder(1)); EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(2))); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); EXPECT_FALSE(work_queue_->BlockedByFence()); } TEST_F(WorkQueueTest, RemoveFence) { work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); work_queue_->Push(FakeTaskWithEnqueueOrder(5)); work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(3)); WorkQueue* work_queue; EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_FALSE(work_queue_->Empty()); EXPECT_EQ(2ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_FALSE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_FALSE(work_queue_->Empty()); EXPECT_TRUE(work_queue_->BlockedByFence()); EXPECT_TRUE(work_queue_->RemoveFence()); EXPECT_EQ(4ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_TRUE(work_queue_sets_->GetOldestQueueInSet(0, &work_queue)); EXPECT_FALSE(work_queue_->BlockedByFence()); } TEST_F(WorkQueueTest, RemoveFenceButNoFence) { EXPECT_FALSE(work_queue_->RemoveFence()); } TEST_F(WorkQueueTest, RemoveFenceNothingUnblocked) { EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::blocking_fence())); EXPECT_TRUE(work_queue_->BlockedByFence()); EXPECT_FALSE(work_queue_->RemoveFence()); EXPECT_FALSE(work_queue_->BlockedByFence()); } TEST_F(WorkQueueTest, BlockedByFence) { EXPECT_FALSE(work_queue_->BlockedByFence()); EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::blocking_fence())); EXPECT_TRUE(work_queue_->BlockedByFence()); } TEST_F(WorkQueueTest, BlockedByFencePopBecomesEmpty) { work_queue_->Push(FakeTaskWithEnqueueOrder(1)); EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(2))); EXPECT_FALSE(work_queue_->BlockedByFence()); EXPECT_EQ(1ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_TRUE(work_queue_->BlockedByFence()); } TEST_F(WorkQueueTest, BlockedByFencePop) { work_queue_->Push(FakeTaskWithEnqueueOrder(1)); EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(2))); EXPECT_FALSE(work_queue_->BlockedByFence()); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); EXPECT_FALSE(work_queue_->BlockedByFence()); EXPECT_EQ(1ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_TRUE(work_queue_->BlockedByFence()); } TEST_F(WorkQueueTest, InitiallyEmptyBlockedByFenceNewFenceUnblocks) { EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::blocking_fence())); EXPECT_TRUE(work_queue_->BlockedByFence()); work_queue_->Push(FakeTaskWithEnqueueOrder(2)); EXPECT_TRUE(work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(3))); EXPECT_FALSE(work_queue_->BlockedByFence()); } TEST_F(WorkQueueTest, BlockedByFenceNewFenceUnblocks) { work_queue_->Push(FakeTaskWithEnqueueOrder(1)); EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(2))); EXPECT_FALSE(work_queue_->BlockedByFence()); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); EXPECT_FALSE(work_queue_->BlockedByFence()); EXPECT_EQ(1ull, work_queue_->TakeTaskFromWorkQueue().enqueue_order()); EXPECT_TRUE(work_queue_->BlockedByFence()); EXPECT_TRUE(work_queue_->InsertFence(EnqueueOrder::FromIntForTesting(4))); EXPECT_FALSE(work_queue_->BlockedByFence()); } TEST_F(WorkQueueTest, InsertFenceAfterEnqueuing) { work_queue_->Push(FakeTaskWithEnqueueOrder(2)); work_queue_->Push(FakeTaskWithEnqueueOrder(3)); work_queue_->Push(FakeTaskWithEnqueueOrder(4)); EXPECT_FALSE(work_queue_->BlockedByFence()); EXPECT_FALSE(work_queue_->InsertFence(EnqueueOrder::blocking_fence())); EXPECT_TRUE(work_queue_->BlockedByFence()); EnqueueOrder enqueue_order; EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); } TEST_F(WorkQueueTest, RemoveAllCanceledTasksFromFront) { { Cancelable cancelable; work_queue_->Push(FakeCancelableTaskWithEnqueueOrder( 2, cancelable.weak_ptr_factory.GetWeakPtr())); work_queue_->Push(FakeCancelableTaskWithEnqueueOrder( 3, cancelable.weak_ptr_factory.GetWeakPtr())); work_queue_->Push(FakeCancelableTaskWithEnqueueOrder( 4, cancelable.weak_ptr_factory.GetWeakPtr())); work_queue_->Push(FakeTaskWithEnqueueOrder(5)); } EXPECT_TRUE(work_queue_->RemoveAllCanceledTasksFromFront()); EnqueueOrder enqueue_order; EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); EXPECT_EQ(5ull, enqueue_order); } TEST_F(WorkQueueTest, RemoveAllCanceledTasksFromFrontTasksNotCanceled) { { Cancelable cancelable; work_queue_->Push(FakeCancelableTaskWithEnqueueOrder( 2, cancelable.weak_ptr_factory.GetWeakPtr())); work_queue_->Push(FakeCancelableTaskWithEnqueueOrder( 3, cancelable.weak_ptr_factory.GetWeakPtr())); work_queue_->Push(FakeCancelableTaskWithEnqueueOrder( 4, cancelable.weak_ptr_factory.GetWeakPtr())); work_queue_->Push(FakeTaskWithEnqueueOrder(5)); EXPECT_FALSE(work_queue_->RemoveAllCanceledTasksFromFront()); EnqueueOrder enqueue_order; EXPECT_TRUE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order)); EXPECT_EQ(2ull, enqueue_order); } } } // namespace internal } // namespace sequence_manager } // namespace base