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/priority_queue.h" | 5 #include "base/task_scheduler/priority_queue.h" |
6 | 6 |
7 #include <memory> | 7 #include <memory> |
8 | 8 |
9 #include "base/bind.h" | |
10 #include "base/bind_helpers.h" | |
11 #include "base/macros.h" | 9 #include "base/macros.h" |
12 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
13 #include "base/memory/ref_counted.h" | 11 #include "base/memory/ref_counted.h" |
14 #include "base/synchronization/waitable_event.h" | 12 #include "base/synchronization/waitable_event.h" |
15 #include "base/task_scheduler/sequence.h" | 13 #include "base/task_scheduler/sequence.h" |
16 #include "base/task_scheduler/task.h" | 14 #include "base/task_scheduler/task.h" |
17 #include "base/task_scheduler/task_traits.h" | 15 #include "base/task_scheduler/task_traits.h" |
18 #include "base/task_scheduler/test_utils.h" | 16 #include "base/task_scheduler/test_utils.h" |
19 #include "base/threading/platform_thread.h" | 17 #include "base/threading/platform_thread.h" |
20 #include "base/threading/simple_thread.h" | 18 #include "base/threading/simple_thread.h" |
21 #include "base/time/time.h" | 19 #include "base/time/time.h" |
22 #include "testing/gmock/include/gmock/gmock.h" | |
23 #include "testing/gtest/include/gtest/gtest.h" | 20 #include "testing/gtest/include/gtest/gtest.h" |
24 | 21 |
25 namespace base { | 22 namespace base { |
26 namespace internal { | 23 namespace internal { |
27 | 24 |
28 namespace { | 25 namespace { |
29 | 26 |
30 class PriorityQueueCallbackMock { | |
31 public: | |
32 PriorityQueueCallbackMock() = default; | |
33 MOCK_METHOD0(WakeUp, void()); | |
34 | |
35 private: | |
36 DISALLOW_COPY_AND_ASSIGN(PriorityQueueCallbackMock); | |
37 }; | |
38 | |
39 class ThreadBeginningTransaction : public SimpleThread { | 27 class ThreadBeginningTransaction : public SimpleThread { |
40 public: | 28 public: |
41 explicit ThreadBeginningTransaction(PriorityQueue* priority_queue) | 29 explicit ThreadBeginningTransaction(PriorityQueue* priority_queue) |
42 : SimpleThread("ThreadBeginningTransaction"), | 30 : SimpleThread("ThreadBeginningTransaction"), |
43 priority_queue_(priority_queue), | 31 priority_queue_(priority_queue), |
44 transaction_began_(true, false) {} | 32 transaction_began_(true, false) {} |
45 | 33 |
46 // SimpleThread: | 34 // SimpleThread: |
47 void Run() override { | 35 void Run() override { |
48 std::unique_ptr<PriorityQueue::Transaction> transaction = | 36 std::unique_ptr<PriorityQueue::Transaction> transaction = |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
101 TaskTraits().WithPriority(TaskPriority::USER_BLOCKING)))); | 89 TaskTraits().WithPriority(TaskPriority::USER_BLOCKING)))); |
102 SequenceSortKey sort_key_c = sequence_c->GetSortKey(); | 90 SequenceSortKey sort_key_c = sequence_c->GetSortKey(); |
103 | 91 |
104 scoped_refptr<Sequence> sequence_d(new Sequence); | 92 scoped_refptr<Sequence> sequence_d(new Sequence); |
105 sequence_d->PushTask(WrapUnique( | 93 sequence_d->PushTask(WrapUnique( |
106 new Task(FROM_HERE, Closure(), | 94 new Task(FROM_HERE, Closure(), |
107 TaskTraits().WithPriority(TaskPriority::BACKGROUND)))); | 95 TaskTraits().WithPriority(TaskPriority::BACKGROUND)))); |
108 SequenceSortKey sort_key_d = sequence_d->GetSortKey(); | 96 SequenceSortKey sort_key_d = sequence_d->GetSortKey(); |
109 | 97 |
110 // Create a PriorityQueue and a Transaction. | 98 // Create a PriorityQueue and a Transaction. |
111 testing::StrictMock<PriorityQueueCallbackMock> mock; | 99 PriorityQueue pq; |
112 PriorityQueue pq(Bind(&PriorityQueueCallbackMock::WakeUp, Unretained(&mock))); | 100 auto transaction(pq.BeginTransaction()); |
113 std::unique_ptr<PriorityQueue::Transaction> transaction( | |
114 pq.BeginTransaction()); | |
115 EXPECT_SEQUENCE_AND_SORT_KEY_EQ(PriorityQueue::SequenceAndSortKey(), | 101 EXPECT_SEQUENCE_AND_SORT_KEY_EQ(PriorityQueue::SequenceAndSortKey(), |
116 transaction->Peek()); | 102 transaction->Peek()); |
117 | 103 |
118 // Push |sequence_a| in the PriorityQueue. It becomes the sequence with the | 104 // Push |sequence_a| in the PriorityQueue. It becomes the sequence with the |
119 // highest priority. | 105 // highest priority. |
120 transaction->Push(WrapUnique( | 106 transaction->Push(WrapUnique( |
121 new PriorityQueue::SequenceAndSortKey(sequence_a, sort_key_a))); | 107 new PriorityQueue::SequenceAndSortKey(sequence_a, sort_key_a))); |
122 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( | 108 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( |
123 PriorityQueue::SequenceAndSortKey(sequence_a, sort_key_a), | 109 PriorityQueue::SequenceAndSortKey(sequence_a, sort_key_a), |
124 transaction->Peek()); | 110 transaction->Peek()); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
165 // with the highest priority. | 151 // with the highest priority. |
166 transaction->Pop(); | 152 transaction->Pop(); |
167 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( | 153 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( |
168 PriorityQueue::SequenceAndSortKey(sequence_d, sort_key_d), | 154 PriorityQueue::SequenceAndSortKey(sequence_d, sort_key_d), |
169 transaction->Peek()); | 155 transaction->Peek()); |
170 | 156 |
171 // Pop |sequence_d| from the PriorityQueue. It is now empty. | 157 // Pop |sequence_d| from the PriorityQueue. It is now empty. |
172 transaction->Pop(); | 158 transaction->Pop(); |
173 EXPECT_SEQUENCE_AND_SORT_KEY_EQ(PriorityQueue::SequenceAndSortKey(), | 159 EXPECT_SEQUENCE_AND_SORT_KEY_EQ(PriorityQueue::SequenceAndSortKey(), |
174 transaction->Peek()); | 160 transaction->Peek()); |
175 | |
176 // Expect 4 calls to mock.WakeUp() when the Transaction is destroyed. | |
177 EXPECT_CALL(mock, WakeUp()).Times(4); | |
178 transaction.reset(); | |
179 } | |
180 | |
181 TEST(TaskSchedulerPriorityQueueTest, PushNoWakeUpPopPeek) { | |
182 // Create test sequences. | |
183 scoped_refptr<Sequence> sequence_a(new Sequence); | |
184 sequence_a->PushTask(WrapUnique( | |
185 new Task(FROM_HERE, Closure(), | |
186 TaskTraits().WithPriority(TaskPriority::USER_VISIBLE)))); | |
187 SequenceSortKey sort_key_a = sequence_a->GetSortKey(); | |
188 | |
189 scoped_refptr<Sequence> sequence_b(new Sequence); | |
190 sequence_b->PushTask(WrapUnique( | |
191 new Task(FROM_HERE, Closure(), | |
192 TaskTraits().WithPriority(TaskPriority::USER_BLOCKING)))); | |
193 SequenceSortKey sort_key_b = sequence_b->GetSortKey(); | |
194 | |
195 scoped_refptr<Sequence> sequence_c(new Sequence); | |
196 sequence_c->PushTask(WrapUnique( | |
197 new Task(FROM_HERE, Closure(), | |
198 TaskTraits().WithPriority(TaskPriority::USER_BLOCKING)))); | |
199 SequenceSortKey sort_key_c = sequence_c->GetSortKey(); | |
200 | |
201 scoped_refptr<Sequence> sequence_d(new Sequence); | |
202 sequence_d->PushTask(WrapUnique( | |
203 new Task(FROM_HERE, Closure(), | |
204 TaskTraits().WithPriority(TaskPriority::BACKGROUND)))); | |
205 SequenceSortKey sort_key_d = sequence_d->GetSortKey(); | |
206 | |
207 // Create a PriorityQueue and a Transaction. | |
208 testing::StrictMock<PriorityQueueCallbackMock> mock; | |
209 PriorityQueue pq(Bind(&PriorityQueueCallbackMock::WakeUp, Unretained(&mock))); | |
210 std::unique_ptr<PriorityQueue::Transaction> transaction( | |
211 pq.BeginTransaction()); | |
212 EXPECT_SEQUENCE_AND_SORT_KEY_EQ(PriorityQueue::SequenceAndSortKey(), | |
213 transaction->Peek()); | |
214 | |
215 // Push |sequence_a| in the PriorityQueue. It becomes the sequence with the | |
216 // highest priority. | |
217 transaction->PushNoWakeUp(WrapUnique( | |
218 new PriorityQueue::SequenceAndSortKey(sequence_a, sort_key_a))); | |
219 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( | |
220 PriorityQueue::SequenceAndSortKey(sequence_a, sort_key_a), | |
221 transaction->Peek()); | |
222 | |
223 // Push |sequence_b| in the PriorityQueue. It becomes the sequence with the | |
224 // highest priority. | |
225 transaction->PushNoWakeUp(WrapUnique( | |
226 new PriorityQueue::SequenceAndSortKey(sequence_b, sort_key_b))); | |
227 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( | |
228 PriorityQueue::SequenceAndSortKey(sequence_b, sort_key_b), | |
229 transaction->Peek()); | |
230 | |
231 // Push |sequence_c| in the PriorityQueue. |sequence_b| is still the sequence | |
232 // with the highest priority. | |
233 transaction->PushNoWakeUp(WrapUnique( | |
234 new PriorityQueue::SequenceAndSortKey(sequence_c, sort_key_c))); | |
235 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( | |
236 PriorityQueue::SequenceAndSortKey(sequence_b, sort_key_b), | |
237 transaction->Peek()); | |
238 | |
239 // Push |sequence_d| in the PriorityQueue. |sequence_b| is still the sequence | |
240 // with the highest priority. | |
241 transaction->PushNoWakeUp(WrapUnique( | |
242 new PriorityQueue::SequenceAndSortKey(sequence_d, sort_key_d))); | |
243 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( | |
244 PriorityQueue::SequenceAndSortKey(sequence_b, sort_key_b), | |
245 transaction->Peek()); | |
246 | |
247 // Pop |sequence_b| from the PriorityQueue. |sequence_c| becomes the sequence | |
248 // with the highest priority. | |
249 transaction->Pop(); | |
250 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( | |
251 PriorityQueue::SequenceAndSortKey(sequence_c, sort_key_c), | |
252 transaction->Peek()); | |
253 | |
254 // Pop |sequence_c| from the PriorityQueue. |sequence_a| becomes the sequence | |
255 // with the highest priority. | |
256 transaction->Pop(); | |
257 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( | |
258 PriorityQueue::SequenceAndSortKey(sequence_a, sort_key_a), | |
259 transaction->Peek()); | |
260 | |
261 // Pop |sequence_a| from the PriorityQueue. |sequence_d| becomes the sequence | |
262 // with the highest priority. | |
263 transaction->Pop(); | |
264 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( | |
265 PriorityQueue::SequenceAndSortKey(sequence_d, sort_key_d), | |
266 transaction->Peek()); | |
267 | |
268 // Pop |sequence_d| from the PriorityQueue. It is now empty. | |
269 transaction->Pop(); | |
270 EXPECT_SEQUENCE_AND_SORT_KEY_EQ(PriorityQueue::SequenceAndSortKey(), | |
271 transaction->Peek()); | |
272 | |
273 // Expect no call to mock.WakeUp() when the Transaction is destroyed. | |
274 EXPECT_CALL(mock, WakeUp()).Times(0); | |
275 transaction.reset(); | |
276 } | 161 } |
277 | 162 |
278 // Check that creating Transactions on the same thread for 2 unrelated | 163 // Check that creating Transactions on the same thread for 2 unrelated |
279 // PriorityQueues causes a crash. | 164 // PriorityQueues causes a crash. |
280 TEST(TaskSchedulerPriorityQueueTest, IllegalTwoTransactionsSameThread) { | 165 TEST(TaskSchedulerPriorityQueueTest, IllegalTwoTransactionsSameThread) { |
281 PriorityQueue pq_a(Bind(&DoNothing)); | 166 PriorityQueue pq_a; |
282 PriorityQueue pq_b(Bind(&DoNothing)); | 167 PriorityQueue pq_b; |
283 | 168 |
284 EXPECT_DCHECK_DEATH( | 169 EXPECT_DCHECK_DEATH( |
285 { | 170 { |
286 std::unique_ptr<PriorityQueue::Transaction> transaction_a = | 171 std::unique_ptr<PriorityQueue::Transaction> transaction_a = |
287 pq_a.BeginTransaction(); | 172 pq_a.BeginTransaction(); |
288 std::unique_ptr<PriorityQueue::Transaction> transaction_b = | 173 std::unique_ptr<PriorityQueue::Transaction> transaction_b = |
289 pq_b.BeginTransaction(); | 174 pq_b.BeginTransaction(); |
290 }, | 175 }, |
291 ""); | 176 ""); |
292 } | 177 } |
293 | 178 |
294 // Check that there is no crash when Transactions are created on the same thread | 179 // Check that there is no crash when Transactions are created on the same thread |
295 // for 2 PriorityQueues which have a predecessor relationship. | 180 // for 2 PriorityQueues which have a predecessor relationship. |
296 TEST(TaskSchedulerPriorityQueueTest, LegalTwoTransactionsSameThread) { | 181 TEST(TaskSchedulerPriorityQueueTest, LegalTwoTransactionsSameThread) { |
297 PriorityQueue pq_a(Bind(&DoNothing)); | 182 PriorityQueue pq_a; |
298 PriorityQueue pq_b(Bind(&DoNothing), &pq_a); | 183 PriorityQueue pq_b(&pq_a); |
299 | 184 |
300 // This shouldn't crash. | 185 // This shouldn't crash. |
301 std::unique_ptr<PriorityQueue::Transaction> transaction_a = | 186 std::unique_ptr<PriorityQueue::Transaction> transaction_a = |
302 pq_a.BeginTransaction(); | 187 pq_a.BeginTransaction(); |
303 std::unique_ptr<PriorityQueue::Transaction> transaction_b = | 188 std::unique_ptr<PriorityQueue::Transaction> transaction_b = |
304 pq_b.BeginTransaction(); | 189 pq_b.BeginTransaction(); |
305 } | 190 } |
306 | 191 |
307 // Check that it is possible to begin multiple Transactions for the same | 192 // Check that it is possible to begin multiple Transactions for the same |
308 // PriorityQueue on different threads. The call to BeginTransaction() on the | 193 // PriorityQueue on different threads. The call to BeginTransaction() on the |
309 // second thread should block until the Transaction has ended on the first | 194 // second thread should block until the Transaction has ended on the first |
310 // thread. | 195 // thread. |
311 TEST(TaskSchedulerPriorityQueueTest, TwoTransactionsTwoThreads) { | 196 TEST(TaskSchedulerPriorityQueueTest, TwoTransactionsTwoThreads) { |
312 PriorityQueue pq(Bind(&DoNothing)); | 197 PriorityQueue pq; |
313 | 198 |
314 // Call BeginTransaction() on this thread and keep the Transaction alive. | 199 // Call BeginTransaction() on this thread and keep the Transaction alive. |
315 std::unique_ptr<PriorityQueue::Transaction> transaction = | 200 std::unique_ptr<PriorityQueue::Transaction> transaction = |
316 pq.BeginTransaction(); | 201 pq.BeginTransaction(); |
317 | 202 |
318 // Call BeginTransaction() on another thread. | 203 // Call BeginTransaction() on another thread. |
319 ThreadBeginningTransaction thread_beginning_transaction(&pq); | 204 ThreadBeginningTransaction thread_beginning_transaction(&pq); |
320 thread_beginning_transaction.Start(); | 205 thread_beginning_transaction.Start(); |
321 | 206 |
322 // After a few milliseconds, the call to BeginTransaction() on the other | 207 // After a few milliseconds, the call to BeginTransaction() on the other |
323 // thread should not have returned. | 208 // thread should not have returned. |
324 thread_beginning_transaction.ExpectTransactionDoesNotBegin(); | 209 thread_beginning_transaction.ExpectTransactionDoesNotBegin(); |
325 | 210 |
326 // End the Transaction on the current thread. | 211 // End the Transaction on the current thread. |
327 transaction.reset(); | 212 transaction.reset(); |
328 | 213 |
329 // The other thread should exit after its call to BeginTransaction() returns. | 214 // The other thread should exit after its call to BeginTransaction() returns. |
330 thread_beginning_transaction.Join(); | 215 thread_beginning_transaction.Join(); |
331 } | 216 } |
332 | 217 |
333 TEST(TaskSchedulerPriorityQueueTest, SequenceAndSortKeyIsNull) { | 218 TEST(TaskSchedulerPriorityQueueTest, SequenceAndSortKeyIsNull) { |
334 EXPECT_TRUE(PriorityQueue::SequenceAndSortKey().is_null()); | 219 EXPECT_TRUE(PriorityQueue::SequenceAndSortKey().is_null()); |
335 | 220 |
336 const PriorityQueue::SequenceAndSortKey non_null_sequence_andsort_key( | 221 const PriorityQueue::SequenceAndSortKey non_null_sequence_and_sort_key( |
337 make_scoped_refptr(new Sequence), | 222 make_scoped_refptr(new Sequence), |
338 SequenceSortKey(TaskPriority::USER_VISIBLE, TimeTicks())); | 223 SequenceSortKey(TaskPriority::USER_VISIBLE, TimeTicks())); |
339 EXPECT_FALSE(non_null_sequence_andsort_key.is_null()); | 224 EXPECT_FALSE(non_null_sequence_and_sort_key.is_null()); |
340 } | 225 } |
341 | 226 |
342 } // namespace internal | 227 } // namespace internal |
343 } // namespace base | 228 } // namespace base |
OLD | NEW |