1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/task/sequence_manager/task_queue_selector.h"
6
7 #include <stddef.h>
8
9 #include <map>
10 #include <memory>
11 #include <set>
12 #include <utility>
13 #include <vector>
14
15 #include "base/functional/bind.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/pending_task.h"
18 #include "base/task/sequence_manager/enqueue_order_generator.h"
19 #include "base/task/sequence_manager/task_queue_impl.h"
20 #include "base/task/sequence_manager/test/mock_time_domain.h"
21 #include "base/task/sequence_manager/work_queue.h"
22 #include "base/task/sequence_manager/work_queue_sets.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 using testing::_;
27 using testing::ElementsAre;
28 using testing::NotNull;
29
30 namespace base {
31 namespace sequence_manager {
32 namespace internal {
33 // To avoid symbol collisions in jumbo builds.
34 namespace task_queue_selector_unittest {
35
36 namespace {
37 const TaskQueue::QueuePriority kHighestPriority = 0;
38 const TaskQueue::QueuePriority kHighPriority = 4;
39 const TaskQueue::QueuePriority kDefaultPriority = 5;
40 const TaskQueue::QueuePriority kPriorityCount = 10;
41 const size_t kTaskQueueCount = kPriorityCount;
42
43 // Tests assume high priority is higher than default priority, but nothing about
44 // their values.
45 static_assert(kHighPriority < kDefaultPriority);
46 } // namespace
47
48 class MockObserver : public TaskQueueSelector::Observer {
49 public:
50 MockObserver() = default;
51 MockObserver(const MockObserver&) = delete;
52 MockObserver& operator=(const MockObserver&) = delete;
53 ~MockObserver() override = default;
54
55 MOCK_METHOD1(OnTaskQueueEnabled, void(internal::TaskQueueImpl*));
56 };
57
58 class TaskQueueSelectorForTest : public TaskQueueSelector {
59 public:
60 using TaskQueueSelector::ActivePriorityTracker;
61 using TaskQueueSelector::ChooseWithPriority;
62 using TaskQueueSelector::delayed_work_queue_sets;
63 using TaskQueueSelector::immediate_work_queue_sets;
64 using TaskQueueSelector::SetImmediateStarvationCountForTest;
65 using TaskQueueSelector::SetOperationOldest;
66
TaskQueueSelectorForTest(scoped_refptr<AssociatedThreadId> associated_thread)67 explicit TaskQueueSelectorForTest(
68 scoped_refptr<AssociatedThreadId> associated_thread)
69 : TaskQueueSelector(
70 associated_thread,
71 SequenceManager::Settings::Builder()
72 .SetPrioritySettings(
73 SequenceManager::PrioritySettings(kPriorityCount,
74 kDefaultPriority))
75 .Build()) {}
76 };
77
78 class TaskQueueSelectorTest : public testing::Test {
79 public:
TaskQueueSelectorTest()80 TaskQueueSelectorTest()
81 : test_closure_(BindRepeating(&TaskQueueSelectorTest::TestFunction)),
82 associated_thread_(AssociatedThreadId::CreateBound()),
83 selector_(associated_thread_) {}
84 ~TaskQueueSelectorTest() override = default;
85
PushTasks(const size_t queue_indices[],size_t num_tasks)86 void PushTasks(const size_t queue_indices[], size_t num_tasks) {
87 EnqueueOrderGenerator enqueue_order_generator;
88 for (size_t i = 0; i < num_tasks; i++) {
89 task_queues_[queue_indices[i]]->immediate_work_queue()->Push(
90 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
91 enqueue_order_generator.GenerateNext()));
92 }
93 }
94
PushTasksWithEnqueueOrder(const size_t queue_indices[],const size_t enqueue_orders[],size_t num_tasks)95 void PushTasksWithEnqueueOrder(const size_t queue_indices[],
96 const size_t enqueue_orders[],
97 size_t num_tasks) {
98 for (size_t i = 0; i < num_tasks; i++) {
99 task_queues_[queue_indices[i]]->immediate_work_queue()->Push(
100 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
101 EnqueueOrder::FromIntForTesting(enqueue_orders[i])));
102 }
103 }
104
PushTask(const size_t queue_index,const size_t enqueue_order)105 void PushTask(const size_t queue_index, const size_t enqueue_order) {
106 task_queues_[queue_index]->immediate_work_queue()->Push(
107 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
108 EnqueueOrder::FromIntForTesting(enqueue_order)));
109 }
110
PopTasksAndReturnQueueIndices()111 std::vector<size_t> PopTasksAndReturnQueueIndices() {
112 std::vector<size_t> order;
113 while (WorkQueue* chosen_work_queue =
114 selector_.SelectWorkQueueToService()) {
115 size_t chosen_queue_index =
116 queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
117 order.push_back(chosen_queue_index);
118 chosen_work_queue->PopTaskForTesting();
119 chosen_work_queue->work_queue_sets()->OnPopMinQueueInSet(
120 chosen_work_queue);
121 }
122 return order;
123 }
124
TestFunction()125 static void TestFunction() {}
126
127 protected:
SetUp()128 void SetUp() final {
129 for (size_t i = 0; i < kTaskQueueCount; i++) {
130 std::unique_ptr<TaskQueueImpl> task_queue =
131 std::make_unique<TaskQueueImpl>(nullptr, nullptr,
132 TaskQueue::Spec(QueueName::TEST_TQ));
133 selector_.AddQueue(task_queue.get(), kDefaultPriority);
134 task_queues_.push_back(std::move(task_queue));
135 }
136 for (size_t i = 0; i < kTaskQueueCount; i++) {
137 EXPECT_EQ(kDefaultPriority, task_queues_[i]->GetQueuePriority()) << i;
138 queue_to_index_map_.insert(std::make_pair(task_queues_[i].get(), i));
139 }
140 }
141
TearDown()142 void TearDown() final {
143 for (std::unique_ptr<TaskQueueImpl>& task_queue : task_queues_) {
144 // Note since this test doesn't have a SequenceManager we need to
145 // manually remove |task_queue| from the |selector_|. Normally
146 // UnregisterTaskQueue would do that.
147 selector_.RemoveQueue(task_queue.get());
148 task_queue->UnregisterTaskQueue();
149 }
150 }
151
NewTaskQueueWithBlockReporting()152 std::unique_ptr<TaskQueueImpl> NewTaskQueueWithBlockReporting() {
153 return std::make_unique<TaskQueueImpl>(nullptr, nullptr,
154 TaskQueue::Spec(QueueName::TEST_TQ));
155 }
156
157 RepeatingClosure test_closure_;
158 scoped_refptr<AssociatedThreadId> associated_thread_;
159 TaskQueueSelectorForTest selector_;
160 std::vector<std::unique_ptr<TaskQueueImpl>> task_queues_;
161 std::map<TaskQueueImpl*, size_t> queue_to_index_map_;
162 };
163
TEST_F(TaskQueueSelectorTest,TestDefaultPriority)164 TEST_F(TaskQueueSelectorTest, TestDefaultPriority) {
165 size_t queue_order[] = {4, 3, 2, 1, 0};
166 PushTasks(queue_order, 5);
167 EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(4, 3, 2, 1, 0));
168 }
169
TEST_F(TaskQueueSelectorTest,TestPriorities)170 TEST_F(TaskQueueSelectorTest, TestPriorities) {
171 for (size_t priority = 0; priority < kDefaultPriority; ++priority) {
172 size_t queue_order[] = {0, 1, 2, 3, 4};
173 PushTasks(queue_order, 5);
174 if (priority != kDefaultPriority) {
175 selector_.SetQueuePriority(task_queues_[2].get(), priority);
176 }
177 EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(2, 0, 1, 3, 4));
178 }
179 }
180
TEST_F(TaskQueueSelectorTest,TestMultiplePriorities)181 TEST_F(TaskQueueSelectorTest, TestMultiplePriorities) {
182 size_t reverse_priority_order[kPriorityCount];
183 for (size_t priority = 0; priority < kPriorityCount; ++priority) {
184 reverse_priority_order[(kPriorityCount - 1) - priority] = priority;
185 }
186
187 PushTasks(reverse_priority_order, kPriorityCount);
188
189 for (size_t i = 0; i < kPriorityCount; ++i) {
190 TaskQueue::QueuePriority priority = reverse_priority_order[i];
191 if (priority != kDefaultPriority) {
192 selector_.SetQueuePriority(task_queues_[i].get(), priority);
193 }
194 EXPECT_EQ(task_queues_[i]->GetQueuePriority(), priority);
195 }
196
197 EXPECT_THAT(PopTasksAndReturnQueueIndices(),
198 ElementsAre(9, 8, 7, 6, 5, 4, 3, 2, 1, 0));
199 }
200
TEST_F(TaskQueueSelectorTest,TestObserverWithEnabledQueue)201 TEST_F(TaskQueueSelectorTest, TestObserverWithEnabledQueue) {
202 task_queues_[1]->SetQueueEnabled(false);
203 selector_.DisableQueue(task_queues_[1].get());
204 {
205 MockObserver mock_observer;
206 selector_.SetTaskQueueSelectorObserver(&mock_observer);
207 EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1);
208 task_queues_[1]->SetQueueEnabled(true);
209 selector_.EnableQueue(task_queues_[1].get());
210
211 // Clear observer before it goes out of scope.
212 selector_.SetTaskQueueSelectorObserver(nullptr);
213 }
214 }
215
TEST_F(TaskQueueSelectorTest,TestObserverWithSetQueuePriorityAndQueueAlreadyEnabled)216 TEST_F(TaskQueueSelectorTest,
217 TestObserverWithSetQueuePriorityAndQueueAlreadyEnabled) {
218 selector_.SetQueuePriority(task_queues_[1].get(), kHighPriority);
219 {
220 MockObserver mock_observer;
221 selector_.SetTaskQueueSelectorObserver(&mock_observer);
222 EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(0);
223 selector_.SetQueuePriority(task_queues_[1].get(), kDefaultPriority);
224
225 // Clear observer before it goes out of scope.
226 selector_.SetTaskQueueSelectorObserver(nullptr);
227 }
228 }
229
TEST_F(TaskQueueSelectorTest,TestDisableEnable)230 TEST_F(TaskQueueSelectorTest, TestDisableEnable) {
231 MockObserver mock_observer;
232 selector_.SetTaskQueueSelectorObserver(&mock_observer);
233
234 size_t queue_order[] = {0, 1, 2, 3, 4};
235 PushTasks(queue_order, 5);
236 task_queues_[2]->SetQueueEnabled(false);
237 selector_.DisableQueue(task_queues_[2].get());
238 task_queues_[4]->SetQueueEnabled(false);
239 selector_.DisableQueue(task_queues_[4].get());
240 // Disabling a queue should not affect its priority.
241 EXPECT_EQ(kDefaultPriority, task_queues_[2]->GetQueuePriority());
242 EXPECT_EQ(kDefaultPriority, task_queues_[4]->GetQueuePriority());
243 EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(0, 1, 3));
244
245 EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2);
246 task_queues_[2]->SetQueueEnabled(true);
247 selector_.EnableQueue(task_queues_[2].get());
248 selector_.SetQueuePriority(task_queues_[2].get(), kPriorityCount - 1);
249 EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(2));
250 task_queues_[4]->SetQueueEnabled(true);
251 selector_.EnableQueue(task_queues_[4].get());
252 EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(4));
253
254 // Clear observer before it goes out of scope.
255 selector_.SetTaskQueueSelectorObserver(nullptr);
256 }
257
TEST_F(TaskQueueSelectorTest,TestDisableChangePriorityThenEnable)258 TEST_F(TaskQueueSelectorTest, TestDisableChangePriorityThenEnable) {
259 EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty());
260 EXPECT_TRUE(task_queues_[2]->immediate_work_queue()->Empty());
261
262 task_queues_[2]->SetQueueEnabled(false);
263 selector_.SetQueuePriority(task_queues_[2].get(), kHighPriority);
264
265 size_t queue_order[] = {0, 1, 2, 3, 4};
266 PushTasks(queue_order, 5);
267
268 EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty());
269 EXPECT_FALSE(task_queues_[2]->immediate_work_queue()->Empty());
270 task_queues_[2]->SetQueueEnabled(true);
271
272 EXPECT_EQ(kHighPriority, task_queues_[2]->GetQueuePriority());
273 EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(2, 0, 1, 3, 4));
274 }
275
TEST_F(TaskQueueSelectorTest,TestEmptyQueues)276 TEST_F(TaskQueueSelectorTest, TestEmptyQueues) {
277 EXPECT_EQ(nullptr, selector_.SelectWorkQueueToService());
278
279 // Test only disabled queues.
280 size_t queue_order[] = {0};
281 PushTasks(queue_order, 1);
282 task_queues_[0]->SetQueueEnabled(false);
283 selector_.DisableQueue(task_queues_[0].get());
284 EXPECT_EQ(nullptr, selector_.SelectWorkQueueToService());
285
286 // These tests are unusual since there's no TQM. To avoid a later DCHECK when
287 // deleting the task queue, we re-enable the queue here so the selector
288 // doesn't get out of sync.
289 task_queues_[0]->SetQueueEnabled(true);
290 selector_.EnableQueue(task_queues_[0].get());
291 }
292
TEST_F(TaskQueueSelectorTest,TestAge)293 TEST_F(TaskQueueSelectorTest, TestAge) {
294 size_t enqueue_order[] = {10, 1, 2, 9, 4};
295 size_t queue_order[] = {0, 1, 2, 3, 4};
296 PushTasksWithEnqueueOrder(queue_order, enqueue_order, 5);
297 EXPECT_THAT(PopTasksAndReturnQueueIndices(), ElementsAre(1, 2, 4, 3, 0));
298 }
299
300 class TaskQueueSelectorStarvationTest : public TaskQueueSelectorTest {
301 public:
302 TaskQueueSelectorStarvationTest() = default;
303
304 protected:
TestPriorityOrder(const size_t queue_order[],size_t num_tasks)305 void TestPriorityOrder(const size_t queue_order[], size_t num_tasks) {
306 for (size_t i = 0; i < kTaskQueueCount; i++) {
307 // Setting the queue priority to its current value causes a check to fail.
308 if (task_queues_[i]->GetQueuePriority() !=
309 static_cast<TaskQueue::QueuePriority>(i)) {
310 selector_.SetQueuePriority(task_queues_[i].get(),
311 static_cast<TaskQueue::QueuePriority>(i));
312 }
313 }
314
315 ASSERT_EQ(num_tasks, kTaskQueueCount);
316 PushTasks(queue_order, kTaskQueueCount);
317
318 for (size_t priority = 0; priority < kTaskQueueCount; priority++) {
319 for (int i = 0; i < 100; i++) {
320 WorkQueue* chosen_work_queue = selector_.SelectWorkQueueToService();
321 ASSERT_THAT(chosen_work_queue, NotNull());
322 EXPECT_EQ(task_queues_[priority].get(),
323 chosen_work_queue->task_queue());
324 // Don't remove task from queue to simulate all queues still being full.
325 }
326
327 // Simulate the highest priority queue becoming empty.
328 WorkQueue* chosen_work_queue = selector_.SelectWorkQueueToService();
329 chosen_work_queue->PopTaskForTesting();
330 chosen_work_queue->work_queue_sets()->OnPopMinQueueInSet(
331 chosen_work_queue);
332 }
333 }
334 };
335
TEST_F(TaskQueueSelectorStarvationTest,HigherPriorityWorkStarvesLowerPriorityWork)336 TEST_F(TaskQueueSelectorStarvationTest,
337 HigherPriorityWorkStarvesLowerPriorityWork) {
338 size_t queue_order[kTaskQueueCount];
339 for (size_t i = 0; i < kTaskQueueCount; i++)
340 queue_order[i] = i;
341 TestPriorityOrder(queue_order, kTaskQueueCount);
342 }
343
TEST_F(TaskQueueSelectorStarvationTest,NewHigherPriorityTasksStarveOldLowerPriorityTasks)344 TEST_F(TaskQueueSelectorStarvationTest,
345 NewHigherPriorityTasksStarveOldLowerPriorityTasks) {
346 // Enqueue tasks in order from lowest to highest priority, and check that they
347 // still run in order from highest to lowest priority.
348 size_t queue_order[kTaskQueueCount];
349 for (size_t i = 0; i < kTaskQueueCount; i++)
350 queue_order[i] = (kTaskQueueCount - i) - 1;
351 TestPriorityOrder(queue_order, kTaskQueueCount);
352 }
353
TEST_F(TaskQueueSelectorTest,GetHighestPendingPriority)354 TEST_F(TaskQueueSelectorTest, GetHighestPendingPriority) {
355 EXPECT_FALSE(selector_.GetHighestPendingPriority().has_value());
356 size_t queue_order[] = {0, 1};
357 PushTasks(queue_order, 2);
358
359 selector_.SetQueuePriority(task_queues_[1].get(), kHighPriority);
360
361 EXPECT_EQ(kHighPriority, *selector_.GetHighestPendingPriority());
362 PopTasksAndReturnQueueIndices();
363 EXPECT_FALSE(selector_.GetHighestPendingPriority().has_value());
364
365 PushTasks(queue_order, 1);
366 EXPECT_EQ(kDefaultPriority, *selector_.GetHighestPendingPriority());
367 PopTasksAndReturnQueueIndices();
368 EXPECT_FALSE(selector_.GetHighestPendingPriority().has_value());
369 }
370
TEST_F(TaskQueueSelectorTest,ChooseWithPriority_Empty)371 TEST_F(TaskQueueSelectorTest, ChooseWithPriority_Empty) {
372 EXPECT_EQ(
373 nullptr,
374 selector_
375 .ChooseWithPriority<TaskQueueSelectorForTest::SetOperationOldest>(
376 kDefaultPriority));
377 }
378
TEST_F(TaskQueueSelectorTest,ChooseWithPriority_OnlyDelayed)379 TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyDelayed) {
380 task_queues_[0]->delayed_work_queue()->Push(
381 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
382 EnqueueOrder::FromIntForTesting(2)));
383
384 EXPECT_EQ(
385 task_queues_[0]->delayed_work_queue(),
386 selector_
387 .ChooseWithPriority<TaskQueueSelectorForTest::SetOperationOldest>(
388 kDefaultPriority));
389 }
390
TEST_F(TaskQueueSelectorTest,ChooseWithPriority_OnlyImmediate)391 TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyImmediate) {
392 task_queues_[0]->immediate_work_queue()->Push(
393 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
394 EnqueueOrder::FromIntForTesting(2)));
395
396 EXPECT_EQ(
397 task_queues_[0]->immediate_work_queue(),
398 selector_
399 .ChooseWithPriority<TaskQueueSelectorForTest::SetOperationOldest>(
400 kDefaultPriority));
401 }
402
TEST_F(TaskQueueSelectorTest,SelectWorkQueueToServiceImmediateOnlyWithoutImmediateTask)403 TEST_F(TaskQueueSelectorTest,
404 SelectWorkQueueToServiceImmediateOnlyWithoutImmediateTask) {
405 task_queues_[0]->delayed_work_queue()->Push(
406 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
407 EnqueueOrder::FromIntForTesting(2)));
408
409 EXPECT_EQ(nullptr,
410 selector_.SelectWorkQueueToService(
411 TaskQueueSelector::SelectTaskOption::kSkipDelayedTask));
412 EXPECT_EQ(task_queues_[0]->delayed_work_queue(),
413 selector_.SelectWorkQueueToService());
414 }
415
TEST_F(TaskQueueSelectorTest,SelectWorkQueueToServiceImmediateOnlyWithDelayedTasks)416 TEST_F(TaskQueueSelectorTest,
417 SelectWorkQueueToServiceImmediateOnlyWithDelayedTasks) {
418 task_queues_[0]->delayed_work_queue()->Push(
419 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
420 EnqueueOrder::FromIntForTesting(1)));
421 task_queues_[0]->immediate_work_queue()->Push(
422 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
423 EnqueueOrder::FromIntForTesting(2)));
424
425 EXPECT_EQ(task_queues_[0]->immediate_work_queue(),
426 selector_.SelectWorkQueueToService(
427 TaskQueueSelector::SelectTaskOption::kSkipDelayedTask));
428 EXPECT_EQ(task_queues_[0]->delayed_work_queue(),
429 selector_.SelectWorkQueueToService());
430 }
431
TEST_F(TaskQueueSelectorTest,SelectWorkQueueToServiceImmediateOnlyWithDisabledQueues)432 TEST_F(TaskQueueSelectorTest,
433 SelectWorkQueueToServiceImmediateOnlyWithDisabledQueues) {
434 task_queues_[0]->delayed_work_queue()->Push(
435 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
436 EnqueueOrder::FromIntForTesting(1)));
437 task_queues_[0]->immediate_work_queue()->Push(
438 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
439 EnqueueOrder::FromIntForTesting(2)));
440 task_queues_[1]->delayed_work_queue()->Push(
441 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
442 EnqueueOrder::FromIntForTesting(3)));
443 task_queues_[2]->immediate_work_queue()->Push(
444 Task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
445 EnqueueOrder::FromIntForTesting(4)));
446
447 EXPECT_EQ(task_queues_[0]->delayed_work_queue(),
448 selector_.SelectWorkQueueToService());
449 EXPECT_EQ(task_queues_[0]->immediate_work_queue(),
450 selector_.SelectWorkQueueToService(
451 TaskQueueSelector::SelectTaskOption::kSkipDelayedTask));
452
453 task_queues_[0]->SetQueueEnabled(false);
454 selector_.DisableQueue(task_queues_[0].get());
455
456 EXPECT_EQ(task_queues_[1]->delayed_work_queue(),
457 selector_.SelectWorkQueueToService());
458 EXPECT_EQ(task_queues_[2]->immediate_work_queue(),
459 selector_.SelectWorkQueueToService(
460 TaskQueueSelector::SelectTaskOption::kSkipDelayedTask));
461
462 task_queues_[1]->SetQueueEnabled(false);
463 selector_.DisableQueue(task_queues_[1].get());
464
465 EXPECT_EQ(task_queues_[2]->immediate_work_queue(),
466 selector_.SelectWorkQueueToService(
467 TaskQueueSelector::SelectTaskOption::kSkipDelayedTask));
468 EXPECT_EQ(task_queues_[2]->immediate_work_queue(),
469 selector_.SelectWorkQueueToService());
470 }
471
TEST_F(TaskQueueSelectorTest,TestObserverWithOneBlockedQueue)472 TEST_F(TaskQueueSelectorTest, TestObserverWithOneBlockedQueue) {
473 MockObserver mock_observer; // Must outlive `selector`
474 TaskQueueSelectorForTest selector(associated_thread_);
475 selector.SetTaskQueueSelectorObserver(&mock_observer);
476
477 EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1);
478
479 std::unique_ptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting());
480 selector.AddQueue(task_queue.get(), kDefaultPriority);
481
482 task_queue->SetQueueEnabled(false);
483 selector.DisableQueue(task_queue.get());
484
485 Task task(PostedTask(nullptr, test_closure_, FROM_HERE), EnqueueOrder(),
486 EnqueueOrder::FromIntForTesting(2));
487 task_queue->immediate_work_queue()->Push(std::move(task));
488
489 EXPECT_EQ(nullptr, selector.SelectWorkQueueToService());
490
491 task_queue->SetQueueEnabled(true);
492 selector.EnableQueue(task_queue.get());
493 selector.RemoveQueue(task_queue.get());
494 task_queue->UnregisterTaskQueue();
495 }
496
TEST_F(TaskQueueSelectorTest,TestObserverWithTwoBlockedQueues)497 TEST_F(TaskQueueSelectorTest, TestObserverWithTwoBlockedQueues) {
498 MockObserver mock_observer; // Must outlive `selector`
499 TaskQueueSelectorForTest selector(associated_thread_);
500 selector.SetTaskQueueSelectorObserver(&mock_observer);
501
502 std::unique_ptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting());
503 std::unique_ptr<TaskQueueImpl> task_queue2(NewTaskQueueWithBlockReporting());
504 selector.AddQueue(task_queue.get(), kDefaultPriority);
505 selector.AddQueue(task_queue2.get(), kDefaultPriority);
506
507 task_queue->SetQueueEnabled(false);
508 task_queue2->SetQueueEnabled(false);
509 selector.DisableQueue(task_queue.get());
510 selector.DisableQueue(task_queue2.get());
511
512 selector.SetQueuePriority(task_queue2.get(), kHighestPriority);
513
514 Task task1(PostedTask(nullptr, test_closure_, FROM_HERE),
515 EnqueueOrder::FromIntForTesting(2),
516 EnqueueOrder::FromIntForTesting(2));
517 Task task2(PostedTask(nullptr, test_closure_, FROM_HERE),
518 EnqueueOrder::FromIntForTesting(3),
519 EnqueueOrder::FromIntForTesting(3));
520 task_queue->immediate_work_queue()->Push(std::move(task1));
521 task_queue2->immediate_work_queue()->Push(std::move(task2));
522 EXPECT_EQ(nullptr, selector.SelectWorkQueueToService());
523 testing::Mock::VerifyAndClearExpectations(&mock_observer);
524
525 EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2);
526
527 task_queue->SetQueueEnabled(true);
528 selector.EnableQueue(task_queue.get());
529
530 selector.RemoveQueue(task_queue.get());
531 task_queue->UnregisterTaskQueue();
532 EXPECT_EQ(nullptr, selector.SelectWorkQueueToService());
533
534 task_queue2->SetQueueEnabled(true);
535 selector.EnableQueue(task_queue2.get());
536 selector.RemoveQueue(task_queue2.get());
537 task_queue2->UnregisterTaskQueue();
538 }
539
TEST_F(TaskQueueSelectorTest,CollectSkippedOverLowerPriorityTasks)540 TEST_F(TaskQueueSelectorTest, CollectSkippedOverLowerPriorityTasks) {
541 size_t queue_order[] = {0, 1, 2, 3, 2, 1, 0};
542 PushTasks(queue_order, 7);
543 selector_.SetQueuePriority(task_queues_[3].get(), kHighPriority);
544
545 std::vector<const Task*> result;
546 selector_.CollectSkippedOverLowerPriorityTasks(
547 task_queues_[3]->immediate_work_queue(), &result);
548
549 ASSERT_EQ(3u, result.size());
550 EXPECT_EQ(2u, result[0]->enqueue_order()); // The order here isn't important.
551 EXPECT_EQ(3u, result[1]->enqueue_order());
552 EXPECT_EQ(4u, result[2]->enqueue_order());
553 }
554
555 struct ChooseWithPriorityTestParam {
556 int delayed_task_enqueue_order;
557 int immediate_task_enqueue_order;
558 int immediate_starvation_count;
559 const char* expected_work_queue_name;
560 };
561
562 static const ChooseWithPriorityTestParam kChooseWithPriorityTestCases[] = {
563 {1, 2, 0, "delayed"}, {1, 2, 1, "delayed"}, {1, 2, 2, "delayed"},
564 {1, 2, 3, "immediate"}, {1, 2, 4, "immediate"}, {2, 1, 4, "immediate"},
565 {2, 1, 4, "immediate"},
566 };
567
568 class ChooseWithPriorityTest
569 : public TaskQueueSelectorTest,
570 public testing::WithParamInterface<ChooseWithPriorityTestParam> {};
571
TEST_P(ChooseWithPriorityTest,RoundRobinTest)572 TEST_P(ChooseWithPriorityTest, RoundRobinTest) {
573 task_queues_[0]->immediate_work_queue()->Push(Task(
574 PostedTask(nullptr, test_closure_, FROM_HERE),
575 EnqueueOrder::FromIntForTesting(GetParam().immediate_task_enqueue_order),
576 EnqueueOrder::FromIntForTesting(
577 GetParam().immediate_task_enqueue_order)));
578
579 task_queues_[0]->delayed_work_queue()->Push(Task(
580 PostedTask(nullptr, test_closure_, FROM_HERE),
581 EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order),
582 EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order)));
583
584 selector_.SetImmediateStarvationCountForTest(
585 GetParam().immediate_starvation_count);
586
587 WorkQueue* chosen_work_queue =
588 selector_
589 .ChooseWithPriority<TaskQueueSelectorForTest::SetOperationOldest>(
590 kDefaultPriority);
591 EXPECT_EQ(chosen_work_queue->task_queue(), task_queues_[0].get());
592 EXPECT_STREQ(chosen_work_queue->name(), GetParam().expected_work_queue_name);
593 }
594
595 INSTANTIATE_TEST_SUITE_P(ChooseWithPriorityTest,
596 ChooseWithPriorityTest,
597 testing::ValuesIn(kChooseWithPriorityTestCases));
598
599 class ActivePriorityTrackerTest : public testing::Test {
600 protected:
601 TaskQueueSelectorForTest::ActivePriorityTracker active_priority_tracker_;
602 };
603
TEST_F(ActivePriorityTrackerTest,SetPriorityActiveAndInactive)604 TEST_F(ActivePriorityTrackerTest, SetPriorityActiveAndInactive) {
605 EXPECT_FALSE(active_priority_tracker_.HasActivePriority());
606 EXPECT_FALSE(active_priority_tracker_.IsActive(kDefaultPriority));
607
608 active_priority_tracker_.SetActive(kDefaultPriority, true);
609
610 EXPECT_TRUE(active_priority_tracker_.HasActivePriority());
611 EXPECT_TRUE(active_priority_tracker_.IsActive(kDefaultPriority));
612
613 active_priority_tracker_.SetActive(kDefaultPriority, false);
614
615 EXPECT_FALSE(active_priority_tracker_.HasActivePriority());
616 EXPECT_FALSE(active_priority_tracker_.IsActive(kDefaultPriority));
617 }
618
TEST_F(ActivePriorityTrackerTest,HighestActivePriority)619 TEST_F(ActivePriorityTrackerTest, HighestActivePriority) {
620 EXPECT_FALSE(active_priority_tracker_.HasActivePriority());
621
622 for (size_t i = 0; i < SequenceManager::PrioritySettings::kMaxPriorities;
623 i++) {
624 TaskQueue::QueuePriority priority =
625 static_cast<TaskQueue::QueuePriority>(i);
626 EXPECT_FALSE(active_priority_tracker_.IsActive(priority));
627 active_priority_tracker_.SetActive(priority, true);
628 EXPECT_TRUE(active_priority_tracker_.IsActive(priority));
629 }
630
631 for (size_t i = 0; i < SequenceManager::PrioritySettings::kMaxPriorities;
632 i++) {
633 EXPECT_TRUE(active_priority_tracker_.HasActivePriority());
634 TaskQueue::QueuePriority priority =
635 static_cast<TaskQueue::QueuePriority>(i);
636 EXPECT_EQ(active_priority_tracker_.HighestActivePriority(), priority);
637 active_priority_tracker_.SetActive(priority, false);
638 }
639
640 EXPECT_FALSE(active_priority_tracker_.HasActivePriority());
641 }
642
643 } // namespace task_queue_selector_unittest
644 } // namespace internal
645 } // namespace sequence_manager
646 } // namespace base
647