1 // Copyright 2015 The Chromium Authors. All rights reserved.
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 <memory>
10
11 #include "base/bind.h"
12 #include "base/macros.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/pending_task.h"
15 #include "base/task/sequence_manager/task_queue_impl.h"
16 #include "base/task/sequence_manager/test/mock_time_domain.h"
17 #include "base/task/sequence_manager/work_queue.h"
18 #include "base/task/sequence_manager/work_queue_sets.h"
19 #include "base/test/metrics/histogram_tester.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22
23 using testing::_;
24
25 namespace base {
26 namespace sequence_manager {
27 namespace internal {
28 // To avoid symbol collisions in jumbo builds.
29 namespace task_queue_selector_unittest {
30
31 class MockObserver : public TaskQueueSelector::Observer {
32 public:
33 MockObserver() = default;
34 ~MockObserver() override = default;
35
36 MOCK_METHOD1(OnTaskQueueEnabled, void(internal::TaskQueueImpl*));
37
38 private:
39 DISALLOW_COPY_AND_ASSIGN(MockObserver);
40 };
41
42 class TaskQueueSelectorForTest : public TaskQueueSelector {
43 public:
44 using TaskQueueSelector::prioritizing_selector_for_test;
45 using TaskQueueSelector::PrioritizingSelector;
46 using TaskQueueSelector::SetImmediateStarvationCountForTest;
47
48 // Returns the number of highest priority tasks needed to starve high priority
49 // task.
NumberOfHighestPriorityToStarveHighPriority()50 static constexpr size_t NumberOfHighestPriorityToStarveHighPriority() {
51 return (kMaxHighPriorityStarvationScore +
52 kSmallScoreIncrementForHighPriorityStarvation - 1) /
53 kSmallScoreIncrementForHighPriorityStarvation;
54 }
55
56 // Returns the number of highest priority tasks needed to starve normal
57 // priority tasks.
NumberOfHighestPriorityToStarveNormalPriority()58 static constexpr size_t NumberOfHighestPriorityToStarveNormalPriority() {
59 return (kMaxNormalPriorityStarvationScore +
60 kSmallScoreIncrementForNormalPriorityStarvation - 1) /
61 kSmallScoreIncrementForNormalPriorityStarvation;
62 }
63
64 // Returns the number of high priority tasks needed to starve normal priority
65 // tasks.
NumberOfHighPriorityToStarveNormalPriority()66 static constexpr size_t NumberOfHighPriorityToStarveNormalPriority() {
67 return (kMaxNormalPriorityStarvationScore +
68 kLargeScoreIncrementForNormalPriorityStarvation - 1) /
69 kLargeScoreIncrementForNormalPriorityStarvation;
70 }
71
72 // Returns the number of highest priority tasks needed to starve low priority
73 // ones.
NumberOfHighestPriorityToStarveLowPriority()74 static constexpr size_t NumberOfHighestPriorityToStarveLowPriority() {
75 return (kMaxLowPriorityStarvationScore +
76 kSmallScoreIncrementForLowPriorityStarvation - 1) /
77 kSmallScoreIncrementForLowPriorityStarvation;
78 }
79
80 // Returns the number of high/normal priority tasks needed to starve low
81 // priority ones.
NumberOfHighAndNormalPriorityToStarveLowPriority()82 static constexpr size_t NumberOfHighAndNormalPriorityToStarveLowPriority() {
83 return (kMaxLowPriorityStarvationScore +
84 kLargeScoreIncrementForLowPriorityStarvation - 1) /
85 kLargeScoreIncrementForLowPriorityStarvation;
86 }
87 };
88
89 class TaskQueueSelectorTest : public testing::Test {
90 public:
TaskQueueSelectorTest()91 TaskQueueSelectorTest()
92 : test_closure_(BindRepeating(&TaskQueueSelectorTest::TestFunction)) {}
93 ~TaskQueueSelectorTest() override = default;
94
prioritizing_selector()95 TaskQueueSelectorForTest::PrioritizingSelector* prioritizing_selector() {
96 return selector_.prioritizing_selector_for_test();
97 }
98
delayed_work_queue_sets()99 WorkQueueSets* delayed_work_queue_sets() {
100 return prioritizing_selector()->delayed_work_queue_sets();
101 }
immediate_work_queue_sets()102 WorkQueueSets* immediate_work_queue_sets() {
103 return prioritizing_selector()->immediate_work_queue_sets();
104 }
105
PushTasks(const size_t queue_indices[],size_t num_tasks)106 void PushTasks(const size_t queue_indices[], size_t num_tasks) {
107 std::set<size_t> changed_queue_set;
108 EnqueueOrder::Generator enqueue_order_generator;
109 for (size_t i = 0; i < num_tasks; i++) {
110 changed_queue_set.insert(queue_indices[i]);
111 task_queues_[queue_indices[i]]->immediate_work_queue()->Push(
112 TaskQueueImpl::Task(TaskQueue::PostedTask(test_closure_, FROM_HERE),
113 TimeTicks(), EnqueueOrder(),
114 enqueue_order_generator.GenerateNext()));
115 }
116 }
117
PushTasksWithEnqueueOrder(const size_t queue_indices[],const size_t enqueue_orders[],size_t num_tasks)118 void PushTasksWithEnqueueOrder(const size_t queue_indices[],
119 const size_t enqueue_orders[],
120 size_t num_tasks) {
121 std::set<size_t> changed_queue_set;
122 for (size_t i = 0; i < num_tasks; i++) {
123 changed_queue_set.insert(queue_indices[i]);
124 task_queues_[queue_indices[i]]->immediate_work_queue()->Push(
125 TaskQueueImpl::Task(
126 TaskQueue::PostedTask(test_closure_, FROM_HERE), TimeTicks(),
127 EnqueueOrder(),
128 EnqueueOrder::FromIntForTesting(enqueue_orders[i])));
129 }
130 }
131
PopTasks()132 std::vector<size_t> PopTasks() {
133 std::vector<size_t> order;
134 WorkQueue* chosen_work_queue;
135 while (selector_.SelectWorkQueueToService(&chosen_work_queue)) {
136 size_t chosen_queue_index =
137 queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
138 order.push_back(chosen_queue_index);
139 chosen_work_queue->PopTaskForTesting();
140 immediate_work_queue_sets()->OnPopQueue(chosen_work_queue);
141 }
142 return order;
143 }
144
TestFunction()145 static void TestFunction() {}
146
147 protected:
SetUp()148 void SetUp() final {
149 time_domain_ = std::make_unique<MockTimeDomain>(TimeTicks() +
150 TimeDelta::FromSeconds(1));
151 for (size_t i = 0; i < kTaskQueueCount; i++) {
152 std::unique_ptr<TaskQueueImpl> task_queue =
153 std::make_unique<TaskQueueImpl>(nullptr, time_domain_.get(),
154 TaskQueue::Spec("test"));
155 selector_.AddQueue(task_queue.get());
156 task_queues_.push_back(std::move(task_queue));
157 }
158 for (size_t i = 0; i < kTaskQueueCount; i++) {
159 EXPECT_EQ(TaskQueue::kNormalPriority, task_queues_[i]->GetQueuePriority())
160 << i;
161 queue_to_index_map_.insert(std::make_pair(task_queues_[i].get(), i));
162 }
163 histogram_tester_.reset(new HistogramTester());
164 }
165
TearDown()166 void TearDown() final {
167 for (std::unique_ptr<TaskQueueImpl>& task_queue : task_queues_) {
168 // Note since this test doesn't have a SequenceManager we need to
169 // manually remove |task_queue| from the |selector_|. Normally
170 // UnregisterTaskQueue would do that.
171 selector_.RemoveQueue(task_queue.get());
172 task_queue->UnregisterTaskQueue();
173 }
174 }
175
NewTaskQueueWithBlockReporting()176 std::unique_ptr<TaskQueueImpl> NewTaskQueueWithBlockReporting() {
177 return std::make_unique<TaskQueueImpl>(nullptr, time_domain_.get(),
178 TaskQueue::Spec("test"));
179 }
180
181 const size_t kTaskQueueCount = 5;
182 RepeatingClosure test_closure_;
183 TaskQueueSelectorForTest selector_;
184 std::unique_ptr<TimeDomain> time_domain_;
185 std::vector<std::unique_ptr<TaskQueueImpl>> task_queues_;
186 std::map<TaskQueueImpl*, size_t> queue_to_index_map_;
187 std::unique_ptr<HistogramTester> histogram_tester_;
188 };
189
TEST_F(TaskQueueSelectorTest,TestDefaultPriority)190 TEST_F(TaskQueueSelectorTest, TestDefaultPriority) {
191 size_t queue_order[] = {4, 3, 2, 1, 0};
192 PushTasks(queue_order, 5);
193 EXPECT_THAT(PopTasks(), testing::ElementsAre(4, 3, 2, 1, 0));
194 EXPECT_EQ(histogram_tester_->GetBucketCount(
195 "TaskQueueSelector.TaskServicedPerSelectorLogic",
196 static_cast<int>(TaskQueueSelectorLogic::kNormalPriorityLogic)),
197 5);
198 }
199
TEST_F(TaskQueueSelectorTest,TestHighestPriority)200 TEST_F(TaskQueueSelectorTest, TestHighestPriority) {
201 size_t queue_order[] = {0, 1, 2, 3, 4};
202 PushTasks(queue_order, 5);
203 selector_.SetQueuePriority(task_queues_[2].get(),
204 TaskQueue::kHighestPriority);
205 EXPECT_THAT(PopTasks(), ::testing::ElementsAre(2, 0, 1, 3, 4));
206 EXPECT_EQ(
207 histogram_tester_->GetBucketCount(
208 "TaskQueueSelector.TaskServicedPerSelectorLogic",
209 static_cast<int>(TaskQueueSelectorLogic::kHighestPriorityLogic)),
210 1);
211 }
212
TEST_F(TaskQueueSelectorTest,TestHighPriority)213 TEST_F(TaskQueueSelectorTest, TestHighPriority) {
214 size_t queue_order[] = {0, 1, 2, 3, 4};
215 PushTasks(queue_order, 5);
216 selector_.SetQueuePriority(task_queues_[2].get(),
217 TaskQueue::kHighestPriority);
218 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
219 selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kLowPriority);
220 EXPECT_THAT(PopTasks(), ::testing::ElementsAre(2, 1, 3, 4, 0));
221 EXPECT_EQ(histogram_tester_->GetBucketCount(
222 "TaskQueueSelector.TaskServicedPerSelectorLogic",
223 static_cast<int>(TaskQueueSelectorLogic::kHighPriorityLogic)),
224 1);
225 }
226
TEST_F(TaskQueueSelectorTest,TestLowPriority)227 TEST_F(TaskQueueSelectorTest, TestLowPriority) {
228 size_t queue_order[] = {0, 1, 2, 3, 4};
229 PushTasks(queue_order, 5);
230 selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::kLowPriority);
231 EXPECT_THAT(PopTasks(), testing::ElementsAre(0, 1, 3, 4, 2));
232 EXPECT_EQ(histogram_tester_->GetBucketCount(
233 "TaskQueueSelector.TaskServicedPerSelectorLogic",
234 static_cast<int>(TaskQueueSelectorLogic::kLowPriorityLogic)),
235 1);
236 }
237
TEST_F(TaskQueueSelectorTest,TestBestEffortPriority)238 TEST_F(TaskQueueSelectorTest, TestBestEffortPriority) {
239 size_t queue_order[] = {0, 1, 2, 3, 4};
240 PushTasks(queue_order, 5);
241 selector_.SetQueuePriority(task_queues_[0].get(),
242 TaskQueue::kBestEffortPriority);
243 selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::kLowPriority);
244 selector_.SetQueuePriority(task_queues_[3].get(),
245 TaskQueue::kHighestPriority);
246 EXPECT_THAT(PopTasks(), ::testing::ElementsAre(3, 1, 4, 2, 0));
247 EXPECT_EQ(
248 histogram_tester_->GetBucketCount(
249 "TaskQueueSelector.TaskServicedPerSelectorLogic",
250 static_cast<int>(TaskQueueSelectorLogic::kBestEffortPriorityLogic)),
251 1);
252 }
253
TEST_F(TaskQueueSelectorTest,TestControlPriority)254 TEST_F(TaskQueueSelectorTest, TestControlPriority) {
255 size_t queue_order[] = {0, 1, 2, 3, 4};
256 PushTasks(queue_order, 5);
257 selector_.SetQueuePriority(task_queues_[4].get(),
258 TaskQueue::kControlPriority);
259 EXPECT_EQ(TaskQueue::kControlPriority, task_queues_[4]->GetQueuePriority());
260 selector_.SetQueuePriority(task_queues_[2].get(),
261 TaskQueue::kHighestPriority);
262 EXPECT_EQ(TaskQueue::kHighestPriority, task_queues_[2]->GetQueuePriority());
263 EXPECT_THAT(PopTasks(), ::testing::ElementsAre(4, 2, 0, 1, 3));
264 EXPECT_EQ(
265 histogram_tester_->GetBucketCount(
266 "TaskQueueSelector.TaskServicedPerSelectorLogic",
267 static_cast<int>(TaskQueueSelectorLogic::kControlPriorityLogic)),
268 1);
269 }
270
TEST_F(TaskQueueSelectorTest,TestObserverWithEnabledQueue)271 TEST_F(TaskQueueSelectorTest, TestObserverWithEnabledQueue) {
272 task_queues_[1]->SetQueueEnabledForTest(false);
273 selector_.DisableQueue(task_queues_[1].get());
274 MockObserver mock_observer;
275 selector_.SetTaskQueueSelectorObserver(&mock_observer);
276 EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1);
277 task_queues_[1]->SetQueueEnabledForTest(true);
278 selector_.EnableQueue(task_queues_[1].get());
279 }
280
TEST_F(TaskQueueSelectorTest,TestObserverWithSetQueuePriorityAndQueueAlreadyEnabled)281 TEST_F(TaskQueueSelectorTest,
282 TestObserverWithSetQueuePriorityAndQueueAlreadyEnabled) {
283 selector_.SetQueuePriority(task_queues_[1].get(),
284 TaskQueue::kHighestPriority);
285 MockObserver mock_observer;
286 selector_.SetTaskQueueSelectorObserver(&mock_observer);
287 EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(0);
288 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kNormalPriority);
289 }
290
TEST_F(TaskQueueSelectorTest,TestDisableEnable)291 TEST_F(TaskQueueSelectorTest, TestDisableEnable) {
292 MockObserver mock_observer;
293 selector_.SetTaskQueueSelectorObserver(&mock_observer);
294
295 size_t queue_order[] = {0, 1, 2, 3, 4};
296 PushTasks(queue_order, 5);
297 task_queues_[2]->SetQueueEnabledForTest(false);
298 selector_.DisableQueue(task_queues_[2].get());
299 task_queues_[4]->SetQueueEnabledForTest(false);
300 selector_.DisableQueue(task_queues_[4].get());
301 // Disabling a queue should not affect its priority.
302 EXPECT_EQ(TaskQueue::kNormalPriority, task_queues_[2]->GetQueuePriority());
303 EXPECT_EQ(TaskQueue::kNormalPriority, task_queues_[4]->GetQueuePriority());
304 EXPECT_THAT(PopTasks(), testing::ElementsAre(0, 1, 3));
305
306 EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2);
307 task_queues_[2]->SetQueueEnabledForTest(true);
308 selector_.EnableQueue(task_queues_[2].get());
309 selector_.SetQueuePriority(task_queues_[2].get(),
310 TaskQueue::kBestEffortPriority);
311 EXPECT_THAT(PopTasks(), testing::ElementsAre(2));
312 task_queues_[4]->SetQueueEnabledForTest(true);
313 selector_.EnableQueue(task_queues_[4].get());
314 EXPECT_THAT(PopTasks(), testing::ElementsAre(4));
315 }
316
TEST_F(TaskQueueSelectorTest,TestDisableChangePriorityThenEnable)317 TEST_F(TaskQueueSelectorTest, TestDisableChangePriorityThenEnable) {
318 EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty());
319 EXPECT_TRUE(task_queues_[2]->immediate_work_queue()->Empty());
320
321 task_queues_[2]->SetQueueEnabledForTest(false);
322 selector_.SetQueuePriority(task_queues_[2].get(),
323 TaskQueue::kHighestPriority);
324
325 size_t queue_order[] = {0, 1, 2, 3, 4};
326 PushTasks(queue_order, 5);
327
328 EXPECT_TRUE(task_queues_[2]->delayed_work_queue()->Empty());
329 EXPECT_FALSE(task_queues_[2]->immediate_work_queue()->Empty());
330 task_queues_[2]->SetQueueEnabledForTest(true);
331
332 EXPECT_EQ(TaskQueue::kHighestPriority, task_queues_[2]->GetQueuePriority());
333 EXPECT_THAT(PopTasks(), ::testing::ElementsAre(2, 0, 1, 3, 4));
334 }
335
TEST_F(TaskQueueSelectorTest,TestEmptyQueues)336 TEST_F(TaskQueueSelectorTest, TestEmptyQueues) {
337 WorkQueue* chosen_work_queue = nullptr;
338 EXPECT_FALSE(selector_.SelectWorkQueueToService(&chosen_work_queue));
339
340 // Test only disabled queues.
341 size_t queue_order[] = {0};
342 PushTasks(queue_order, 1);
343 task_queues_[0]->SetQueueEnabledForTest(false);
344 selector_.DisableQueue(task_queues_[0].get());
345 EXPECT_FALSE(selector_.SelectWorkQueueToService(&chosen_work_queue));
346
347 // These tests are unusual since there's no TQM. To avoid a later DCHECK when
348 // deleting the task queue, we re-enable the queue here so the selector
349 // doesn't get out of sync.
350 task_queues_[0]->SetQueueEnabledForTest(true);
351 selector_.EnableQueue(task_queues_[0].get());
352 }
353
TEST_F(TaskQueueSelectorTest,TestAge)354 TEST_F(TaskQueueSelectorTest, TestAge) {
355 size_t enqueue_order[] = {10, 1, 2, 9, 4};
356 size_t queue_order[] = {0, 1, 2, 3, 4};
357 PushTasksWithEnqueueOrder(queue_order, enqueue_order, 5);
358 EXPECT_THAT(PopTasks(), testing::ElementsAre(1, 2, 4, 3, 0));
359 }
360
TEST_F(TaskQueueSelectorTest,TestControlStarvesOthers)361 TEST_F(TaskQueueSelectorTest, TestControlStarvesOthers) {
362 size_t queue_order[] = {0, 1, 2, 3};
363 PushTasks(queue_order, 4);
364 selector_.SetQueuePriority(task_queues_[3].get(),
365 TaskQueue::kControlPriority);
366 selector_.SetQueuePriority(task_queues_[2].get(),
367 TaskQueue::kHighestPriority);
368 selector_.SetQueuePriority(task_queues_[1].get(),
369 TaskQueue::kBestEffortPriority);
370 for (int i = 0; i < 100; i++) {
371 WorkQueue* chosen_work_queue = nullptr;
372 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
373 EXPECT_EQ(task_queues_[3].get(), chosen_work_queue->task_queue());
374 // Don't remove task from queue to simulate all queues still being full.
375 }
376 }
377
TEST_F(TaskQueueSelectorTest,TestHighestPriorityDoesNotStarveHigh)378 TEST_F(TaskQueueSelectorTest, TestHighestPriorityDoesNotStarveHigh) {
379 size_t queue_order[] = {0, 1};
380 PushTasks(queue_order, 2);
381 selector_.SetQueuePriority(task_queues_[0].get(),
382 TaskQueue::kHighestPriority);
383 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
384
385 size_t counts[] = {0, 0};
386 for (int i = 0; i < 100; i++) {
387 WorkQueue* chosen_work_queue = nullptr;
388 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
389 size_t chosen_queue_index =
390 queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
391 counts[chosen_queue_index]++;
392 // Don't remove task from queue to simulate all queues still being full.
393 }
394 EXPECT_GT(counts[1], 0ul); // Check highest doesn't starve high.
395 EXPECT_GT(counts[0], counts[1]); // Check highest gets more chance to run.
396 }
397
TEST_F(TaskQueueSelectorTest,TestHighestPriorityDoesNotStarveHighOrNormal)398 TEST_F(TaskQueueSelectorTest, TestHighestPriorityDoesNotStarveHighOrNormal) {
399 size_t queue_order[] = {0, 1, 2};
400 PushTasks(queue_order, 3);
401 selector_.SetQueuePriority(task_queues_[0].get(),
402 TaskQueue::kHighestPriority);
403 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
404
405 size_t counts[] = {0, 0, 0};
406 for (int i = 0; i < 100; i++) {
407 WorkQueue* chosen_work_queue = nullptr;
408 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
409 size_t chosen_queue_index =
410 queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
411 counts[chosen_queue_index]++;
412 // Don't remove task from queue to simulate all queues still being full.
413 }
414
415 // Check highest runs more frequently then high.
416 EXPECT_GT(counts[0], counts[1]);
417
418 // Check high runs at least as frequently as normal.
419 EXPECT_GE(counts[1], counts[2]);
420
421 // Check normal isn't starved.
422 EXPECT_GT(counts[2], 0ul);
423 }
424
TEST_F(TaskQueueSelectorTest,TestHighestPriorityDoesNotStarveHighOrNormalOrLow)425 TEST_F(TaskQueueSelectorTest,
426 TestHighestPriorityDoesNotStarveHighOrNormalOrLow) {
427 size_t queue_order[] = {0, 1, 2, 3};
428 PushTasks(queue_order, 4);
429 selector_.SetQueuePriority(task_queues_[0].get(),
430 TaskQueue::kHighestPriority);
431 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
432 selector_.SetQueuePriority(task_queues_[3].get(), TaskQueue::kLowPriority);
433
434 size_t counts[] = {0, 0, 0, 0};
435 for (int i = 0; i < 100; i++) {
436 WorkQueue* chosen_work_queue = nullptr;
437 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
438 size_t chosen_queue_index =
439 queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
440 counts[chosen_queue_index]++;
441 // Don't remove task from queue to simulate all queues still being full.
442 }
443
444 // Check highest runs more frequently then high.
445 EXPECT_GT(counts[0], counts[1]);
446
447 // Check high runs at least as frequently as normal.
448 EXPECT_GE(counts[1], counts[2]);
449
450 // Check normal runs more frequently than low.
451 EXPECT_GT(counts[2], counts[3]);
452
453 // Check low isn't starved.
454 EXPECT_GT(counts[3], 0ul);
455 }
456
TEST_F(TaskQueueSelectorTest,TestHighPriorityDoesNotStarveNormal)457 TEST_F(TaskQueueSelectorTest, TestHighPriorityDoesNotStarveNormal) {
458 size_t queue_order[] = {0, 1};
459 PushTasks(queue_order, 2);
460
461 selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kHighPriority);
462
463 size_t counts[] = {0, 0, 0, 0};
464 for (int i = 0; i < 100; i++) {
465 WorkQueue* chosen_work_queue = nullptr;
466 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
467 size_t chosen_queue_index =
468 queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
469 counts[chosen_queue_index]++;
470 // Don't remove task from queue to simulate all queues still being full.
471 }
472
473 // Check high runs more frequently then normal.
474 EXPECT_GT(counts[0], counts[1]);
475
476 // Check low isn't starved.
477 EXPECT_GT(counts[1], 0ul);
478 }
479
TEST_F(TaskQueueSelectorTest,TestHighPriorityDoesNotStarveNormalOrLow)480 TEST_F(TaskQueueSelectorTest, TestHighPriorityDoesNotStarveNormalOrLow) {
481 size_t queue_order[] = {0, 1, 2};
482 PushTasks(queue_order, 3);
483 selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kHighPriority);
484 selector_.SetQueuePriority(task_queues_[2].get(), TaskQueue::kLowPriority);
485
486 size_t counts[] = {0, 0, 0};
487 for (int i = 0; i < 100; i++) {
488 WorkQueue* chosen_work_queue = nullptr;
489 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
490 size_t chosen_queue_index =
491 queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
492 counts[chosen_queue_index]++;
493 // Don't remove task from queue to simulate all queues still being full.
494 }
495
496 // Check high runs more frequently than normal.
497 EXPECT_GT(counts[0], counts[1]);
498
499 // Check normal runs more frequently than low.
500 EXPECT_GT(counts[1], counts[2]);
501
502 // Check low isn't starved.
503 EXPECT_GT(counts[2], 0ul);
504 }
505
TEST_F(TaskQueueSelectorTest,TestNormalPriorityDoesNotStarveLow)506 TEST_F(TaskQueueSelectorTest, TestNormalPriorityDoesNotStarveLow) {
507 size_t queue_order[] = {0, 1, 2};
508 PushTasks(queue_order, 3);
509 selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kLowPriority);
510 selector_.SetQueuePriority(task_queues_[1].get(),
511 TaskQueue::kBestEffortPriority);
512 size_t counts[] = {0, 0, 0};
513 for (int i = 0; i < 100; i++) {
514 WorkQueue* chosen_work_queue = nullptr;
515 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
516 size_t chosen_queue_index =
517 queue_to_index_map_.find(chosen_work_queue->task_queue())->second;
518 counts[chosen_queue_index]++;
519 // Don't remove task from queue to simulate all queues still being full.
520 }
521 EXPECT_GT(counts[0], 0ul); // Check normal doesn't starve low.
522 EXPECT_GT(counts[2], counts[0]); // Check normal gets more chance to run.
523 EXPECT_EQ(0ul, counts[1]); // Check best effort is starved.
524 }
525
TEST_F(TaskQueueSelectorTest,TestBestEffortGetsStarved)526 TEST_F(TaskQueueSelectorTest, TestBestEffortGetsStarved) {
527 size_t queue_order[] = {0, 1};
528 PushTasks(queue_order, 2);
529 selector_.SetQueuePriority(task_queues_[0].get(),
530 TaskQueue::kBestEffortPriority);
531 EXPECT_EQ(TaskQueue::kNormalPriority, task_queues_[1]->GetQueuePriority());
532
533 // Check that normal priority tasks starve best effort.
534 WorkQueue* chosen_work_queue = nullptr;
535 for (int i = 0; i < 100; i++) {
536 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
537 EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue());
538 // Don't remove task from queue to simulate all queues still being full.
539 }
540
541 // Check that highest priority tasks starve best effort.
542 selector_.SetQueuePriority(task_queues_[1].get(),
543 TaskQueue::kHighestPriority);
544 for (int i = 0; i < 100; i++) {
545 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
546 EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue());
547 // Don't remove task from queue to simulate all queues still being full.
548 }
549
550 // Check that high priority tasks starve best effort.
551 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
552 for (int i = 0; i < 100; i++) {
553 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
554 EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue());
555 // Don't remove task from queue to simulate all queues still being full.
556 }
557
558 // Check that low priority tasks starve best effort.
559 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kLowPriority);
560 for (int i = 0; i < 100; i++) {
561 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
562 EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue());
563 // Don't remove task from queue to simulate all queues still being full.
564 }
565
566 // Check that control priority tasks starve best effort.
567 selector_.SetQueuePriority(task_queues_[1].get(),
568 TaskQueue::kControlPriority);
569 for (int i = 0; i < 100; i++) {
570 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
571 EXPECT_EQ(task_queues_[1].get(), chosen_work_queue->task_queue());
572 // Don't remove task from queue to simulate all queues still being full.
573 }
574 }
575
TEST_F(TaskQueueSelectorTest,TestHighPriorityStarvationScoreIncreasedOnlyWhenTasksArePresent)576 TEST_F(TaskQueueSelectorTest,
577 TestHighPriorityStarvationScoreIncreasedOnlyWhenTasksArePresent) {
578 size_t queue_order[] = {0, 1};
579 PushTasks(queue_order, 2);
580 selector_.SetQueuePriority(task_queues_[0].get(),
581 TaskQueue::kHighestPriority);
582 selector_.SetQueuePriority(task_queues_[1].get(),
583 TaskQueue::kHighestPriority);
584
585 // Run a number of highest priority tasks needed to starve high priority
586 // tasks (when present).
587 for (size_t num_tasks = 0;
588 num_tasks <=
589 TaskQueueSelectorForTest::NumberOfHighestPriorityToStarveHighPriority();
590 num_tasks++) {
591 WorkQueue* chosen_work_queue = nullptr;
592 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
593 // Don't remove task from queue to simulate the queue is still full.
594 }
595
596 // Post a high priority task.
597 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
598 WorkQueue* chosen_work_queue = nullptr;
599 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
600
601 // Check that the high priority task is not considered starved, and thus isn't
602 // processed.
603 EXPECT_NE(
604 static_cast<int>(
605 queue_to_index_map_.find(chosen_work_queue->task_queue())->second),
606 1);
607 }
608
TEST_F(TaskQueueSelectorTest,TestNormalPriorityStarvationScoreIncreasedOnllWhenTasksArePresent)609 TEST_F(TaskQueueSelectorTest,
610 TestNormalPriorityStarvationScoreIncreasedOnllWhenTasksArePresent) {
611 size_t queue_order[] = {0, 1};
612 PushTasks(queue_order, 2);
613 selector_.SetQueuePriority(task_queues_[0].get(),
614 TaskQueue::kHighestPriority);
615 selector_.SetQueuePriority(task_queues_[1].get(),
616 TaskQueue::kHighestPriority);
617
618 // Run a number of highest priority tasks needed to starve normal priority
619 // tasks (when present).
620 for (size_t num_tasks = 0;
621 num_tasks <= TaskQueueSelectorForTest::
622 NumberOfHighestPriorityToStarveNormalPriority();
623 num_tasks++) {
624 WorkQueue* chosen_work_queue = nullptr;
625 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
626 // Don't remove task from queue to simulate the queue is still full.
627 }
628
629 selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kHighPriority);
630 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kHighPriority);
631
632 // Run a number of high priority tasks needed to starve normal priority
633 // tasks (when present).
634 for (size_t num_tasks = 0;
635 num_tasks <=
636 TaskQueueSelectorForTest::NumberOfHighPriorityToStarveNormalPriority();
637 num_tasks++) {
638 WorkQueue* chosen_work_queue = nullptr;
639 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
640 // Don't remove task from queue to simulate the queue is still full.
641 }
642
643 // Post a normal priority task.
644 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kNormalPriority);
645 WorkQueue* chosen_work_queue = nullptr;
646 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
647
648 // Check that the normal priority task is not considered starved, and thus
649 // isn't processed.
650 EXPECT_NE(
651 static_cast<int>(
652 queue_to_index_map_.find(chosen_work_queue->task_queue())->second),
653 1);
654 }
655
TEST_F(TaskQueueSelectorTest,TestLowPriorityTaskStarvationOnlyIncreasedWhenTasksArePresent)656 TEST_F(TaskQueueSelectorTest,
657 TestLowPriorityTaskStarvationOnlyIncreasedWhenTasksArePresent) {
658 size_t queue_order[] = {0, 1};
659 PushTasks(queue_order, 2);
660 selector_.SetQueuePriority(task_queues_[0].get(),
661 TaskQueue::kHighestPriority);
662 selector_.SetQueuePriority(task_queues_[1].get(),
663 TaskQueue::kHighestPriority);
664
665 // Run a number of highest priority tasks needed to starve low priority
666 // tasks (when present).
667 for (size_t num_tasks = 0;
668 num_tasks <=
669 TaskQueueSelectorForTest::NumberOfHighestPriorityToStarveLowPriority();
670 num_tasks++) {
671 WorkQueue* chosen_work_queue = nullptr;
672 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
673 // Don't remove task from queue to simulate the queue is still full.
674 }
675
676 selector_.SetQueuePriority(task_queues_[0].get(), TaskQueue::kHighPriority);
677 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kNormalPriority);
678
679 // Run a number of high/normal priority tasks needed to starve low priority
680 // tasks (when present).
681 for (size_t num_tasks = 0;
682 num_tasks <= TaskQueueSelectorForTest::
683 NumberOfHighAndNormalPriorityToStarveLowPriority();
684 num_tasks++) {
685 WorkQueue* chosen_work_queue = nullptr;
686 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
687 // Don't remove task from queue to simulate the queue is still full.
688 }
689
690 // Post a low priority task.
691 selector_.SetQueuePriority(task_queues_[1].get(), TaskQueue::kLowPriority);
692 WorkQueue* chosen_work_queue = nullptr;
693 ASSERT_TRUE(selector_.SelectWorkQueueToService(&chosen_work_queue));
694
695 // Check that the low priority task is not considered starved, and thus
696 // isn't processed.
697 EXPECT_NE(
698 static_cast<int>(
699 queue_to_index_map_.find(chosen_work_queue->task_queue())->second),
700 1);
701 }
702
TEST_F(TaskQueueSelectorTest,AllEnabledWorkQueuesAreEmpty)703 TEST_F(TaskQueueSelectorTest, AllEnabledWorkQueuesAreEmpty) {
704 EXPECT_TRUE(selector_.AllEnabledWorkQueuesAreEmpty());
705 size_t queue_order[] = {0, 1};
706 PushTasks(queue_order, 2);
707
708 EXPECT_FALSE(selector_.AllEnabledWorkQueuesAreEmpty());
709 PopTasks();
710 EXPECT_TRUE(selector_.AllEnabledWorkQueuesAreEmpty());
711 }
712
TEST_F(TaskQueueSelectorTest,AllEnabledWorkQueuesAreEmpty_ControlPriority)713 TEST_F(TaskQueueSelectorTest, AllEnabledWorkQueuesAreEmpty_ControlPriority) {
714 size_t queue_order[] = {0};
715 PushTasks(queue_order, 1);
716
717 selector_.SetQueuePriority(task_queues_[0].get(),
718 TaskQueue::kControlPriority);
719
720 EXPECT_FALSE(selector_.AllEnabledWorkQueuesAreEmpty());
721 }
722
TEST_F(TaskQueueSelectorTest,ChooseOldestWithPriority_Empty)723 TEST_F(TaskQueueSelectorTest, ChooseOldestWithPriority_Empty) {
724 WorkQueue* chosen_work_queue = nullptr;
725 bool chose_delayed_over_immediate = false;
726 EXPECT_FALSE(prioritizing_selector()->ChooseOldestWithPriority(
727 TaskQueue::kNormalPriority, &chose_delayed_over_immediate,
728 &chosen_work_queue));
729 EXPECT_FALSE(chose_delayed_over_immediate);
730 }
731
TEST_F(TaskQueueSelectorTest,ChooseOldestWithPriority_OnlyDelayed)732 TEST_F(TaskQueueSelectorTest, ChooseOldestWithPriority_OnlyDelayed) {
733 task_queues_[0]->delayed_work_queue()->Push(TaskQueueImpl::Task(
734 TaskQueue::PostedTask(test_closure_, FROM_HERE), TimeTicks(),
735 EnqueueOrder(), EnqueueOrder::FromIntForTesting(2)));
736
737 WorkQueue* chosen_work_queue = nullptr;
738 bool chose_delayed_over_immediate = false;
739 EXPECT_TRUE(prioritizing_selector()->ChooseOldestWithPriority(
740 TaskQueue::kNormalPriority, &chose_delayed_over_immediate,
741 &chosen_work_queue));
742 EXPECT_EQ(chosen_work_queue, task_queues_[0]->delayed_work_queue());
743 EXPECT_FALSE(chose_delayed_over_immediate);
744 }
745
TEST_F(TaskQueueSelectorTest,ChooseOldestWithPriority_OnlyImmediate)746 TEST_F(TaskQueueSelectorTest, ChooseOldestWithPriority_OnlyImmediate) {
747 task_queues_[0]->immediate_work_queue()->Push(TaskQueueImpl::Task(
748 TaskQueue::PostedTask(test_closure_, FROM_HERE), TimeTicks(),
749 EnqueueOrder(), EnqueueOrder::FromIntForTesting(2)));
750
751 WorkQueue* chosen_work_queue = nullptr;
752 bool chose_delayed_over_immediate = false;
753 EXPECT_TRUE(prioritizing_selector()->ChooseOldestWithPriority(
754 TaskQueue::kNormalPriority, &chose_delayed_over_immediate,
755 &chosen_work_queue));
756 EXPECT_EQ(chosen_work_queue, task_queues_[0]->immediate_work_queue());
757 EXPECT_FALSE(chose_delayed_over_immediate);
758 }
759
TEST_F(TaskQueueSelectorTest,TestObserverWithOneBlockedQueue)760 TEST_F(TaskQueueSelectorTest, TestObserverWithOneBlockedQueue) {
761 TaskQueueSelectorForTest selector;
762 MockObserver mock_observer;
763 selector.SetTaskQueueSelectorObserver(&mock_observer);
764
765 EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(1);
766
767 std::unique_ptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting());
768 selector.AddQueue(task_queue.get());
769
770 task_queue->SetQueueEnabledForTest(false);
771 selector.DisableQueue(task_queue.get());
772
773 TaskQueueImpl::Task task(TaskQueue::PostedTask(test_closure_, FROM_HERE),
774 TimeTicks(), EnqueueOrder(),
775 EnqueueOrder::FromIntForTesting(2));
776 task_queue->immediate_work_queue()->Push(std::move(task));
777
778 WorkQueue* chosen_work_queue;
779 EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue));
780
781 task_queue->SetQueueEnabledForTest(true);
782 selector.EnableQueue(task_queue.get());
783 selector.RemoveQueue(task_queue.get());
784 task_queue->UnregisterTaskQueue();
785 }
786
TEST_F(TaskQueueSelectorTest,TestObserverWithTwoBlockedQueues)787 TEST_F(TaskQueueSelectorTest, TestObserverWithTwoBlockedQueues) {
788 TaskQueueSelectorForTest selector;
789 MockObserver mock_observer;
790 selector.SetTaskQueueSelectorObserver(&mock_observer);
791
792 std::unique_ptr<TaskQueueImpl> task_queue(NewTaskQueueWithBlockReporting());
793 std::unique_ptr<TaskQueueImpl> task_queue2(NewTaskQueueWithBlockReporting());
794 selector.AddQueue(task_queue.get());
795 selector.AddQueue(task_queue2.get());
796
797 task_queue->SetQueueEnabledForTest(false);
798 task_queue2->SetQueueEnabledForTest(false);
799 selector.DisableQueue(task_queue.get());
800 selector.DisableQueue(task_queue2.get());
801
802 selector.SetQueuePriority(task_queue2.get(), TaskQueue::kControlPriority);
803
804 TaskQueueImpl::Task task1(TaskQueue::PostedTask(test_closure_, FROM_HERE),
805 TimeTicks(), EnqueueOrder::FromIntForTesting(2),
806 EnqueueOrder::FromIntForTesting(2));
807 TaskQueueImpl::Task task2(TaskQueue::PostedTask(test_closure_, FROM_HERE),
808 TimeTicks(), EnqueueOrder::FromIntForTesting(3),
809 EnqueueOrder::FromIntForTesting(3));
810 task_queue->immediate_work_queue()->Push(std::move(task1));
811 task_queue2->immediate_work_queue()->Push(std::move(task2));
812
813 WorkQueue* chosen_work_queue;
814 EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue));
815 testing::Mock::VerifyAndClearExpectations(&mock_observer);
816
817 EXPECT_CALL(mock_observer, OnTaskQueueEnabled(_)).Times(2);
818
819 task_queue->SetQueueEnabledForTest(true);
820 selector.EnableQueue(task_queue.get());
821
822 selector.RemoveQueue(task_queue.get());
823 task_queue->UnregisterTaskQueue();
824 EXPECT_FALSE(selector.SelectWorkQueueToService(&chosen_work_queue));
825
826 task_queue2->SetQueueEnabledForTest(true);
827 selector.EnableQueue(task_queue2.get());
828 selector.RemoveQueue(task_queue2.get());
829 task_queue2->UnregisterTaskQueue();
830 }
831
832 struct ChooseOldestWithPriorityTestParam {
833 int delayed_task_enqueue_order;
834 int immediate_task_enqueue_order;
835 int immediate_starvation_count;
836 const char* expected_work_queue_name;
837 bool expected_did_starve_immediate_queue;
838 };
839
840 static const ChooseOldestWithPriorityTestParam
841 kChooseOldestWithPriorityTestCases[] = {
842 {1, 2, 0, "delayed", true}, {1, 2, 1, "delayed", true},
843 {1, 2, 2, "delayed", true}, {1, 2, 3, "immediate", false},
844 {1, 2, 4, "immediate", false}, {2, 1, 4, "immediate", false},
845 {2, 1, 4, "immediate", false},
846 };
847
848 class ChooseOldestWithPriorityTest
849 : public TaskQueueSelectorTest,
850 public testing::WithParamInterface<ChooseOldestWithPriorityTestParam> {};
851
TEST_P(ChooseOldestWithPriorityTest,RoundRobinTest)852 TEST_P(ChooseOldestWithPriorityTest, RoundRobinTest) {
853 task_queues_[0]->immediate_work_queue()->Push(TaskQueueImpl::Task(
854 TaskQueue::PostedTask(test_closure_, FROM_HERE), TimeTicks(),
855 EnqueueOrder::FromIntForTesting(GetParam().immediate_task_enqueue_order),
856 EnqueueOrder::FromIntForTesting(
857 GetParam().immediate_task_enqueue_order)));
858
859 task_queues_[0]->delayed_work_queue()->Push(TaskQueueImpl::Task(
860 TaskQueue::PostedTask(test_closure_, FROM_HERE), TimeTicks(),
861 EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order),
862 EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order)));
863
864 selector_.SetImmediateStarvationCountForTest(
865 GetParam().immediate_starvation_count);
866
867 WorkQueue* chosen_work_queue = nullptr;
868 bool chose_delayed_over_immediate = false;
869 EXPECT_TRUE(prioritizing_selector()->ChooseOldestWithPriority(
870 TaskQueue::kNormalPriority, &chose_delayed_over_immediate,
871 &chosen_work_queue));
872 EXPECT_EQ(chosen_work_queue->task_queue(), task_queues_[0].get());
873 EXPECT_STREQ(chosen_work_queue->name(), GetParam().expected_work_queue_name);
874 EXPECT_EQ(chose_delayed_over_immediate,
875 GetParam().expected_did_starve_immediate_queue);
876 }
877
878 INSTANTIATE_TEST_CASE_P(ChooseOldestWithPriorityTest,
879 ChooseOldestWithPriorityTest,
880 testing::ValuesIn(kChooseOldestWithPriorityTestCases));
881
882 } // namespace task_queue_selector_unittest
883 } // namespace internal
884 } // namespace sequence_manager
885 } // namespace base
886