1 // Copyright 2016 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/thread_pool/sequence.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/test/gtest_util.h"
13 #include "base/time/time.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace base {
18 namespace internal {
19 
20 namespace {
21 
22 class MockTask {
23  public:
24   MOCK_METHOD0(Run, void());
25 };
26 
CreateTask(MockTask * mock_task)27 Task CreateTask(MockTask* mock_task) {
28   return Task(FROM_HERE, BindOnce(&MockTask::Run, Unretained(mock_task)),
29               TimeDelta());
30 }
31 
ExpectMockTask(MockTask * mock_task,Task * task)32 void ExpectMockTask(MockTask* mock_task, Task* task) {
33   EXPECT_CALL(*mock_task, Run());
34   std::move(task->task).Run();
35   testing::Mock::VerifyAndClear(mock_task);
36 }
37 
38 }  // namespace
39 
TEST(ThreadPoolSequenceTest,PushTakeRemove)40 TEST(ThreadPoolSequenceTest, PushTakeRemove) {
41   testing::StrictMock<MockTask> mock_task_a;
42   testing::StrictMock<MockTask> mock_task_b;
43   testing::StrictMock<MockTask> mock_task_c;
44   testing::StrictMock<MockTask> mock_task_d;
45   testing::StrictMock<MockTask> mock_task_e;
46 
47   scoped_refptr<Sequence> sequence =
48       MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr,
49                                TaskSourceExecutionMode::kParallel);
50   Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
51 
52   // Push task A in the sequence. PushTask() should return true since it's the
53   // first task->
54   EXPECT_TRUE(sequence_transaction.WillPushTask());
55   sequence_transaction.PushTask(CreateTask(&mock_task_a));
56 
57   // Push task B, C and D in the sequence. PushTask() should return false
58   // since there is already a task in a sequence.
59   EXPECT_FALSE(sequence_transaction.WillPushTask());
60   sequence_transaction.PushTask(CreateTask(&mock_task_b));
61   EXPECT_FALSE(sequence_transaction.WillPushTask());
62   sequence_transaction.PushTask(CreateTask(&mock_task_c));
63   EXPECT_FALSE(sequence_transaction.WillPushTask());
64   sequence_transaction.PushTask(CreateTask(&mock_task_d));
65 
66   // Take the task in front of the sequence. It should be task A.
67   auto registered_task_source =
68       RegisteredTaskSource::CreateForTesting(sequence);
69   registered_task_source.WillRunTask();
70   Optional<Task> task = registered_task_source.TakeTask(&sequence_transaction);
71   ExpectMockTask(&mock_task_a, &task.value());
72   EXPECT_FALSE(task->queue_time.is_null());
73 
74   // Remove the empty slot. Task B should now be in front.
75   EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
76 
77   EXPECT_FALSE(sequence_transaction.WillPushTask());
78   registered_task_source.WillRunTask();
79   task = registered_task_source.TakeTask(&sequence_transaction);
80   ExpectMockTask(&mock_task_b, &task.value());
81   EXPECT_FALSE(task->queue_time.is_null());
82 
83   // Remove the empty slot. Task C should now be in front.
84   EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
85 
86   EXPECT_FALSE(sequence_transaction.WillPushTask());
87   registered_task_source.WillRunTask();
88   task = registered_task_source.TakeTask(&sequence_transaction);
89   ExpectMockTask(&mock_task_c, &task.value());
90   EXPECT_FALSE(task->queue_time.is_null());
91 
92   // Remove the empty slot.
93   EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
94 
95   // Push task E in the sequence.
96   EXPECT_FALSE(sequence_transaction.WillPushTask());
97   sequence_transaction.PushTask(CreateTask(&mock_task_e));
98 
99   // Task D should be in front.
100   registered_task_source.WillRunTask();
101   task = registered_task_source.TakeTask(&sequence_transaction);
102   ExpectMockTask(&mock_task_d, &task.value());
103   EXPECT_FALSE(task->queue_time.is_null());
104 
105   // Remove the empty slot. Task E should now be in front.
106   EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction));
107   EXPECT_FALSE(sequence_transaction.WillPushTask());
108   registered_task_source.WillRunTask();
109   task = registered_task_source.TakeTask(&sequence_transaction);
110   ExpectMockTask(&mock_task_e, &task.value());
111   EXPECT_FALSE(task->queue_time.is_null());
112 
113   // Remove the empty slot. The sequence should now be empty.
114   EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction));
115   EXPECT_TRUE(sequence_transaction.WillPushTask());
116 }
117 
118 // Verifies the sort key of a BEST_EFFORT sequence that contains one task.
TEST(ThreadPoolSequenceTest,GetSortKeyBestEffort)119 TEST(ThreadPoolSequenceTest, GetSortKeyBestEffort) {
120   // Create a BEST_EFFORT sequence with a task.
121   Task best_effort_task(FROM_HERE, DoNothing(), TimeDelta());
122   scoped_refptr<Sequence> best_effort_sequence =
123       MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr,
124                                TaskSourceExecutionMode::kParallel);
125   Sequence::Transaction best_effort_sequence_transaction(
126       best_effort_sequence->BeginTransaction());
127   best_effort_sequence_transaction.PushTask(std::move(best_effort_task));
128 
129   // Get the sort key.
130   const TaskSourceSortKey best_effort_sort_key =
131       best_effort_sequence->GetSortKey();
132 
133   // Take the task from the sequence, so that its sequenced time is available
134   // for the check below.
135   auto best_effort_registered_task_source =
136       RegisteredTaskSource::CreateForTesting(best_effort_sequence);
137   best_effort_registered_task_source.WillRunTask();
138   auto take_best_effort_task = best_effort_registered_task_source.TakeTask(
139       &best_effort_sequence_transaction);
140 
141   // Verify the sort key.
142   EXPECT_EQ(TaskPriority::BEST_EFFORT, best_effort_sort_key.priority());
143   EXPECT_EQ(take_best_effort_task.queue_time,
144             best_effort_sort_key.ready_time());
145 
146   // DidProcessTask for correctness.
147   best_effort_registered_task_source.DidProcessTask(
148       &best_effort_sequence_transaction);
149 }
150 
151 // Same as ThreadPoolSequenceTest.GetSortKeyBestEffort, but with a
152 // USER_VISIBLE sequence.
TEST(ThreadPoolSequenceTest,GetSortKeyForeground)153 TEST(ThreadPoolSequenceTest, GetSortKeyForeground) {
154   // Create a USER_VISIBLE sequence with a task.
155   Task foreground_task(FROM_HERE, DoNothing(), TimeDelta());
156   scoped_refptr<Sequence> foreground_sequence =
157       MakeRefCounted<Sequence>(TaskTraits(TaskPriority::USER_VISIBLE), nullptr,
158                                TaskSourceExecutionMode::kParallel);
159   Sequence::Transaction foreground_sequence_transaction(
160       foreground_sequence->BeginTransaction());
161   foreground_sequence_transaction.PushTask(std::move(foreground_task));
162 
163   // Get the sort key.
164   const TaskSourceSortKey foreground_sort_key =
165       foreground_sequence->GetSortKey();
166 
167   // Take the task from the sequence, so that its sequenced time is available
168   // for the check below.
169   auto foreground_registered_task_source =
170       RegisteredTaskSource::CreateForTesting(foreground_sequence);
171   foreground_registered_task_source.WillRunTask();
172   auto take_foreground_task = foreground_registered_task_source.TakeTask(
173       &foreground_sequence_transaction);
174 
175   // Verify the sort key.
176   EXPECT_EQ(TaskPriority::USER_VISIBLE, foreground_sort_key.priority());
177   EXPECT_EQ(take_foreground_task.queue_time, foreground_sort_key.ready_time());
178 
179   // DidProcessTask for correctness.
180   foreground_registered_task_source.DidProcessTask(
181       &foreground_sequence_transaction);
182 }
183 
184 // Verify that a DCHECK fires if DidProcessTask() is called on a sequence which
185 // didn't return a Task.
TEST(ThreadPoolSequenceTest,DidProcessTaskWithoutWillRunTask)186 TEST(ThreadPoolSequenceTest, DidProcessTaskWithoutWillRunTask) {
187   scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
188       TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel);
189   Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
190   sequence_transaction.PushTask(Task(FROM_HERE, DoNothing(), TimeDelta()));
191 
192   auto registered_task_source =
193       RegisteredTaskSource::CreateForTesting(sequence);
194   EXPECT_DCHECK_DEATH({
195     registered_task_source.DidProcessTask(&sequence_transaction);
196   });
197 }
198 
199 // Verify that a DCHECK fires if TakeTask() is called on a sequence whose front
200 // slot is empty.
TEST(ThreadPoolSequenceTest,TakeEmptyFrontSlot)201 TEST(ThreadPoolSequenceTest, TakeEmptyFrontSlot) {
202   scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
203       TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel);
204   Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
205   sequence_transaction.PushTask(Task(FROM_HERE, DoNothing(), TimeDelta()));
206 
207   auto registered_task_source =
208       RegisteredTaskSource::CreateForTesting(sequence);
209   {
210     registered_task_source.WillRunTask();
211     IgnoreResult(registered_task_source.TakeTask(&sequence_transaction));
212     registered_task_source.DidProcessTask(&sequence_transaction);
213   }
214   EXPECT_DCHECK_DEATH({
215     registered_task_source.WillRunTask();
216     auto task = registered_task_source.TakeTask(&sequence_transaction);
217   });
218 }
219 
220 // Verify that a DCHECK fires if TakeTask() is called on an empty sequence.
TEST(ThreadPoolSequenceTest,TakeEmptySequence)221 TEST(ThreadPoolSequenceTest, TakeEmptySequence) {
222   scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
223       TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel);
224   auto registered_task_source =
225       RegisteredTaskSource::CreateForTesting(sequence);
226   EXPECT_DCHECK_DEATH({
227     registered_task_source.WillRunTask();
228     auto task = registered_task_source.TakeTask();
229   });
230 }
231 
232 }  // namespace internal
233 }  // namespace base
234