OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/task_scheduler/sequence.h" | 5 #include "base/task_scheduler/sequence.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/macros.h" | 10 #include "base/macros.h" |
11 #include "base/memory/ptr_util.h" | 11 #include "base/memory/ptr_util.h" |
| 12 #include "base/test/gtest_util.h" |
12 #include "base/time/time.h" | 13 #include "base/time/time.h" |
13 #include "testing/gtest/include/gtest/gtest.h" | 14 #include "testing/gtest/include/gtest/gtest.h" |
14 | 15 |
15 namespace base { | 16 namespace base { |
16 namespace internal { | 17 namespace internal { |
17 | 18 |
18 namespace { | 19 namespace { |
19 | 20 |
20 // A class that pushes a Task to a Sequence in its destructor. | |
21 class PushTaskInDestructor { | |
22 public: | |
23 explicit PushTaskInDestructor(scoped_refptr<Sequence> sequence) | |
24 : sequence_(std::move(sequence)) {} | |
25 PushTaskInDestructor(PushTaskInDestructor&&) = default; | |
26 PushTaskInDestructor& operator=(PushTaskInDestructor&&) = default; | |
27 | |
28 ~PushTaskInDestructor() { | |
29 // |sequence_| may be nullptr in a temporary instance of this class. | |
30 if (sequence_) { | |
31 EXPECT_FALSE(sequence_->PeekTask()); | |
32 sequence_->PushTask(WrapUnique( | |
33 new Task(FROM_HERE, Closure(), TaskTraits(), TimeDelta()))); | |
34 } | |
35 } | |
36 | |
37 private: | |
38 scoped_refptr<Sequence> sequence_; | |
39 | |
40 DISALLOW_COPY_AND_ASSIGN(PushTaskInDestructor); | |
41 }; | |
42 | |
43 void DoNothing(const PushTaskInDestructor&) {} | |
44 | 21 |
45 class TaskSchedulerSequenceTest : public testing::Test { | 22 class TaskSchedulerSequenceTest : public testing::Test { |
46 public: | 23 public: |
47 TaskSchedulerSequenceTest() | 24 TaskSchedulerSequenceTest() |
48 : task_a_owned_( | 25 : task_a_owned_( |
49 new Task(FROM_HERE, | 26 new Task(FROM_HERE, |
50 Closure(), | 27 Closure(), |
51 TaskTraits().WithPriority(TaskPriority::BACKGROUND), | 28 TaskTraits().WithPriority(TaskPriority::BACKGROUND), |
52 TimeDelta())), | 29 TimeDelta())), |
53 task_b_owned_( | 30 task_b_owned_( |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 const Task* task_c_; | 69 const Task* task_c_; |
93 const Task* task_d_; | 70 const Task* task_d_; |
94 const Task* task_e_; | 71 const Task* task_e_; |
95 | 72 |
96 private: | 73 private: |
97 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerSequenceTest); | 74 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerSequenceTest); |
98 }; | 75 }; |
99 | 76 |
100 } // namespace | 77 } // namespace |
101 | 78 |
102 TEST_F(TaskSchedulerSequenceTest, PushPopPeek) { | 79 TEST_F(TaskSchedulerSequenceTest, PushTakeRemove) { |
103 scoped_refptr<Sequence> sequence(new Sequence); | 80 scoped_refptr<Sequence> sequence(new Sequence); |
104 | 81 |
105 // Push task A in the sequence. Its sequenced time should be updated and it | 82 // Push task A in the sequence. Its sequenced time should be updated and it |
106 // should be in front of the sequence. | 83 // should be in front of the sequence. |
107 EXPECT_TRUE(sequence->PushTask(std::move(task_a_owned_))); | 84 EXPECT_TRUE(sequence->PushTask(std::move(task_a_owned_))); |
108 EXPECT_FALSE(task_a_->sequenced_time.is_null()); | 85 EXPECT_FALSE(task_a_->sequenced_time.is_null()); |
109 EXPECT_EQ(task_a_, sequence->PeekTask()); | 86 EXPECT_EQ(task_a_->traits.priority(), sequence->PeekTaskTraits().priority()); |
110 | 87 |
111 // Push task B, C and D in the sequence. Their sequenced time should be | 88 // Push task B, C and D in the sequence. Their sequenced time should be |
112 // updated and task A should always remain in front of the sequence. | 89 // updated and task A should always remain in front of the sequence. |
113 EXPECT_FALSE(sequence->PushTask(std::move(task_b_owned_))); | 90 EXPECT_FALSE(sequence->PushTask(std::move(task_b_owned_))); |
114 EXPECT_FALSE(task_b_->sequenced_time.is_null()); | 91 EXPECT_FALSE(task_b_->sequenced_time.is_null()); |
115 EXPECT_EQ(task_a_, sequence->PeekTask()); | 92 EXPECT_EQ(task_a_->traits.priority(), sequence->PeekTaskTraits().priority()); |
116 | 93 |
117 EXPECT_FALSE(sequence->PushTask(std::move(task_c_owned_))); | 94 EXPECT_FALSE(sequence->PushTask(std::move(task_c_owned_))); |
118 EXPECT_FALSE(task_c_->sequenced_time.is_null()); | 95 EXPECT_FALSE(task_c_->sequenced_time.is_null()); |
119 EXPECT_EQ(task_a_, sequence->PeekTask()); | 96 EXPECT_EQ(task_a_->traits.priority(), sequence->PeekTaskTraits().priority()); |
120 | 97 |
121 EXPECT_FALSE(sequence->PushTask(std::move(task_d_owned_))); | 98 EXPECT_FALSE(sequence->PushTask(std::move(task_d_owned_))); |
122 EXPECT_FALSE(task_d_->sequenced_time.is_null()); | 99 EXPECT_FALSE(task_d_->sequenced_time.is_null()); |
123 EXPECT_EQ(task_a_, sequence->PeekTask()); | 100 EXPECT_EQ(task_a_->traits.priority(), sequence->PeekTaskTraits().priority()); |
124 | 101 |
125 // Pop task A. Task B should now be in front. | 102 // Get the task in front of the sequence. It should be task A. |
126 EXPECT_FALSE(sequence->PopTask()); | 103 EXPECT_EQ(task_a_, sequence->TakeTask().get()); |
127 EXPECT_EQ(task_b_, sequence->PeekTask()); | |
128 | 104 |
129 // Pop task B. Task C should now be in front. | 105 // Remove the empty slot. Task B should now be in front. |
130 EXPECT_FALSE(sequence->PopTask()); | 106 EXPECT_FALSE(sequence->Pop()); |
131 EXPECT_EQ(task_c_, sequence->PeekTask()); | 107 EXPECT_EQ(task_b_, sequence->TakeTask().get()); |
132 | 108 |
133 // Pop task C. Task D should now be in front. | 109 // Remove the empty slot. Task C should now be in front. |
134 EXPECT_FALSE(sequence->PopTask()); | 110 EXPECT_FALSE(sequence->Pop()); |
135 EXPECT_EQ(task_d_, sequence->PeekTask()); | 111 EXPECT_EQ(task_c_, sequence->TakeTask().get()); |
136 | 112 |
137 // Push task E in the sequence. Its sequenced time should be updated and | 113 // Remove the empty slot. Task D should now be in front. |
138 // task D should remain in front. | 114 EXPECT_FALSE(sequence->Pop()); |
| 115 EXPECT_EQ(task_d_, sequence->TakeTask().get()); |
| 116 |
| 117 // Push task E in the sequence. Its sequenced time should be updated. |
139 EXPECT_FALSE(sequence->PushTask(std::move(task_e_owned_))); | 118 EXPECT_FALSE(sequence->PushTask(std::move(task_e_owned_))); |
140 EXPECT_FALSE(task_e_->sequenced_time.is_null()); | 119 EXPECT_FALSE(task_e_->sequenced_time.is_null()); |
141 EXPECT_EQ(task_d_, sequence->PeekTask()); | |
142 | 120 |
143 // Pop task D. Task E should now be in front. | 121 // Remove the empty slot. Task E should now be in front. |
144 EXPECT_FALSE(sequence->PopTask()); | 122 EXPECT_FALSE(sequence->Pop()); |
145 EXPECT_EQ(task_e_, sequence->PeekTask()); | 123 EXPECT_EQ(task_e_, sequence->TakeTask().get()); |
146 | 124 |
147 // Pop task E. The sequence should now be empty. | 125 // Remove the empty slot. The sequence should now be empty. |
148 EXPECT_TRUE(sequence->PopTask()); | 126 EXPECT_TRUE(sequence->Pop()); |
149 EXPECT_EQ(nullptr, sequence->PeekTask()); | |
150 } | 127 } |
151 | 128 |
152 TEST_F(TaskSchedulerSequenceTest, GetSortKey) { | 129 TEST_F(TaskSchedulerSequenceTest, GetSortKey) { |
153 scoped_refptr<Sequence> sequence(new Sequence); | 130 scoped_refptr<Sequence> sequence(new Sequence); |
154 | 131 |
155 // Push task A in the sequence. The highest priority is from task A | 132 // Push task A in the sequence. The highest priority is from task A |
156 // (BACKGROUND). Task A is in front of the sequence. | 133 // (BACKGROUND). Task A is in front of the sequence. |
157 sequence->PushTask(std::move(task_a_owned_)); | 134 sequence->PushTask(std::move(task_a_owned_)); |
158 EXPECT_EQ(SequenceSortKey(TaskPriority::BACKGROUND, task_a_->sequenced_time), | 135 EXPECT_EQ(SequenceSortKey(TaskPriority::BACKGROUND, task_a_->sequenced_time), |
159 sequence->GetSortKey()); | 136 sequence->GetSortKey()); |
(...skipping 14 matching lines...) Expand all Loading... |
174 | 151 |
175 // Push task D in the sequence. The highest priority is from tasks C/D | 152 // Push task D in the sequence. The highest priority is from tasks C/D |
176 // (USER_BLOCKING). Task A is still in front of the sequence. | 153 // (USER_BLOCKING). Task A is still in front of the sequence. |
177 sequence->PushTask(std::move(task_d_owned_)); | 154 sequence->PushTask(std::move(task_d_owned_)); |
178 EXPECT_EQ( | 155 EXPECT_EQ( |
179 SequenceSortKey(TaskPriority::USER_BLOCKING, task_a_->sequenced_time), | 156 SequenceSortKey(TaskPriority::USER_BLOCKING, task_a_->sequenced_time), |
180 sequence->GetSortKey()); | 157 sequence->GetSortKey()); |
181 | 158 |
182 // Pop task A. The highest priority is still USER_BLOCKING. The task in front | 159 // Pop task A. The highest priority is still USER_BLOCKING. The task in front |
183 // of the sequence is now task B. | 160 // of the sequence is now task B. |
184 sequence->PopTask(); | 161 sequence->TakeTask(); |
| 162 sequence->Pop(); |
185 EXPECT_EQ( | 163 EXPECT_EQ( |
186 SequenceSortKey(TaskPriority::USER_BLOCKING, task_b_->sequenced_time), | 164 SequenceSortKey(TaskPriority::USER_BLOCKING, task_b_->sequenced_time), |
187 sequence->GetSortKey()); | 165 sequence->GetSortKey()); |
188 | 166 |
189 // Pop task B. The highest priority is still USER_BLOCKING. The task in front | 167 // Pop task B. The highest priority is still USER_BLOCKING. The task in front |
190 // of the sequence is now task C. | 168 // of the sequence is now task C. |
191 sequence->PopTask(); | 169 sequence->TakeTask(); |
| 170 sequence->Pop(); |
192 EXPECT_EQ( | 171 EXPECT_EQ( |
193 SequenceSortKey(TaskPriority::USER_BLOCKING, task_c_->sequenced_time), | 172 SequenceSortKey(TaskPriority::USER_BLOCKING, task_c_->sequenced_time), |
194 sequence->GetSortKey()); | 173 sequence->GetSortKey()); |
195 | 174 |
196 // Pop task C. The highest priority is still USER_BLOCKING. The task in front | 175 // Pop task C. The highest priority is still USER_BLOCKING. The task in front |
197 // of the sequence is now task D. | 176 // of the sequence is now task D. |
198 sequence->PopTask(); | 177 sequence->TakeTask(); |
| 178 sequence->Pop(); |
199 EXPECT_EQ( | 179 EXPECT_EQ( |
200 SequenceSortKey(TaskPriority::USER_BLOCKING, task_d_->sequenced_time), | 180 SequenceSortKey(TaskPriority::USER_BLOCKING, task_d_->sequenced_time), |
201 sequence->GetSortKey()); | 181 sequence->GetSortKey()); |
202 | 182 |
203 // Push task E in the sequence. The highest priority is still USER_BLOCKING. | 183 // Push task E in the sequence. The highest priority is still USER_BLOCKING. |
204 // The task in front of the sequence is still task D. | 184 // The task in front of the sequence is still task D. |
205 sequence->PushTask(std::move(task_e_owned_)); | 185 sequence->PushTask(std::move(task_e_owned_)); |
206 EXPECT_EQ( | 186 EXPECT_EQ( |
207 SequenceSortKey(TaskPriority::USER_BLOCKING, task_d_->sequenced_time), | 187 SequenceSortKey(TaskPriority::USER_BLOCKING, task_d_->sequenced_time), |
208 sequence->GetSortKey()); | 188 sequence->GetSortKey()); |
209 | 189 |
210 // Pop task D. The highest priority is now from task E (BACKGROUND). The | 190 // Pop task D. The highest priority is now from task E (BACKGROUND). The |
211 // task in front of the sequence is now task E. | 191 // task in front of the sequence is now task E. |
212 sequence->PopTask(); | 192 sequence->TakeTask(); |
| 193 sequence->Pop(); |
213 EXPECT_EQ(SequenceSortKey(TaskPriority::BACKGROUND, task_e_->sequenced_time), | 194 EXPECT_EQ(SequenceSortKey(TaskPriority::BACKGROUND, task_e_->sequenced_time), |
214 sequence->GetSortKey()); | 195 sequence->GetSortKey()); |
215 } | 196 } |
216 | 197 |
217 TEST_F(TaskSchedulerSequenceTest, CanPushTaskInTaskDestructor) { | 198 // Verify that a DCHECK fires if Pop() is called on a sequence whose front slot |
| 199 // isn't empty. |
| 200 TEST_F(TaskSchedulerSequenceTest, PopNonEmptyFrontSlot) { |
218 scoped_refptr<Sequence> sequence(new Sequence); | 201 scoped_refptr<Sequence> sequence(new Sequence); |
219 sequence->PushTask(MakeUnique<Task>( | 202 sequence->PushTask( |
220 FROM_HERE, Bind(&DoNothing, PushTaskInDestructor(sequence)), TaskTraits(), | 203 MakeUnique<Task>(FROM_HERE, Bind(&DoNothing), TaskTraits(), TimeDelta())); |
221 TimeDelta())); | |
222 | 204 |
223 // PushTask() is invoked on |sequence| when the popped Task is destroyed. If | 205 EXPECT_DCHECK_DEATH({ sequence->Pop(); }); |
224 // PopTask() destroys the Task outside the scope of its lock as expected, no | 206 } |
225 // deadlock will occur when PushTask() tries to acquire the Sequence's lock. | |
226 sequence->PopTask(); | |
227 | 207 |
228 // Verify that |sequence| contains exactly one Task. | 208 // Verify that a DCHECK fires if TakeTask() is called on a sequence whose front |
229 EXPECT_TRUE(sequence->PeekTask()); | 209 // slot is empty. |
230 EXPECT_TRUE(sequence->PopTask()); | 210 TEST_F(TaskSchedulerSequenceTest, TakeEmptyFrontSlot) { |
| 211 scoped_refptr<Sequence> sequence(new Sequence); |
| 212 sequence->PushTask( |
| 213 MakeUnique<Task>(FROM_HERE, Bind(&DoNothing), TaskTraits(), TimeDelta())); |
| 214 |
| 215 EXPECT_TRUE(sequence->TakeTask()); |
| 216 EXPECT_DCHECK_DEATH({ sequence->TakeTask(); }); |
| 217 } |
| 218 |
| 219 // Verify that a DCHECK fires if TakeTask() is called on an empty sequence. |
| 220 TEST_F(TaskSchedulerSequenceTest, TakeEmptySequence) { |
| 221 scoped_refptr<Sequence> sequence(new Sequence); |
| 222 EXPECT_DCHECK_DEATH({ sequence->TakeTask(); }); |
231 } | 223 } |
232 | 224 |
233 } // namespace internal | 225 } // namespace internal |
234 } // namespace base | 226 } // namespace base |
OLD | NEW |