OLD | NEW |
(Empty) | |
| 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_scheduler/priority_queue.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" |
| 9 #include "base/macros.h" |
| 10 #include "base/memory/ref_counted.h" |
| 11 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/synchronization/waitable_event.h" |
| 13 #include "base/task_scheduler/sequence.h" |
| 14 #include "base/task_scheduler/task.h" |
| 15 #include "base/task_scheduler/task_traits.h" |
| 16 #include "base/task_scheduler/test_utils.h" |
| 17 #include "base/threading/platform_thread.h" |
| 18 #include "base/threading/simple_thread.h" |
| 19 #include "base/time/time.h" |
| 20 #include "testing/gmock/include/gmock/gmock.h" |
| 21 #include "testing/gtest/include/gtest/gtest.h" |
| 22 |
| 23 namespace base { |
| 24 namespace internal { |
| 25 |
| 26 namespace { |
| 27 |
| 28 class PriorityQueueCallbackMock { |
| 29 public: |
| 30 PriorityQueueCallbackMock() = default; |
| 31 MOCK_METHOD0(SequenceInsertedInPriorityQueue, void()); |
| 32 |
| 33 private: |
| 34 DISALLOW_COPY_AND_ASSIGN(PriorityQueueCallbackMock); |
| 35 }; |
| 36 |
| 37 class ThreadBeginningTransaction : public SimpleThread { |
| 38 public: |
| 39 explicit ThreadBeginningTransaction(PriorityQueue* priority_queue) |
| 40 : SimpleThread("ThreadBeginningTransaction"), |
| 41 priority_queue_(priority_queue), |
| 42 transaction_began_(true, false) {} |
| 43 |
| 44 // SimpleThread: |
| 45 void Run() override { |
| 46 scoped_ptr<PriorityQueue::Transaction> transaction = |
| 47 priority_queue_->BeginTransaction(); |
| 48 transaction_began_.Signal(); |
| 49 } |
| 50 |
| 51 void ExpectTransactionDoesNotBegin() { |
| 52 // After a few milliseconds, the call to BeginTransaction() should not have |
| 53 // returned. |
| 54 EXPECT_FALSE( |
| 55 transaction_began_.TimedWait(TimeDelta::FromMilliseconds(250))); |
| 56 } |
| 57 |
| 58 private: |
| 59 PriorityQueue* const priority_queue_; |
| 60 WaitableEvent transaction_began_; |
| 61 |
| 62 DISALLOW_COPY_AND_ASSIGN(ThreadBeginningTransaction); |
| 63 }; |
| 64 |
| 65 void ExpectSequenceAndSortKeyEq( |
| 66 const PriorityQueue::SequenceAndSortKey& expected, |
| 67 const PriorityQueue::SequenceAndSortKey& actual) { |
| 68 EXPECT_EQ(expected.sequence, actual.sequence); |
| 69 EXPECT_EQ(expected.sort_key.priority, actual.sort_key.priority); |
| 70 EXPECT_EQ(expected.sort_key.next_task_sequenced_time, |
| 71 actual.sort_key.next_task_sequenced_time); |
| 72 } |
| 73 |
| 74 #define EXPECT_SEQUENCE_AND_SORT_KEY_EQ(expected, actual) \ |
| 75 do { \ |
| 76 SCOPED_TRACE(""); \ |
| 77 ExpectSequenceAndSortKeyEq(expected, actual); \ |
| 78 } while (false) |
| 79 |
| 80 } // namespace |
| 81 |
| 82 TEST(TaskSchedulerPriorityQueueTest, PushPopPeek) { |
| 83 // Create test sequences. |
| 84 scoped_refptr<Sequence> sequence_a(new Sequence); |
| 85 sequence_a->PushTask(make_scoped_ptr( |
| 86 new Task(FROM_HERE, Closure(), |
| 87 TaskTraits().WithPriority(TaskPriority::USER_VISIBLE)))); |
| 88 SequenceSortKey sort_key_a = sequence_a->GetSortKey(); |
| 89 |
| 90 scoped_refptr<Sequence> sequence_b(new Sequence); |
| 91 sequence_b->PushTask(make_scoped_ptr( |
| 92 new Task(FROM_HERE, Closure(), |
| 93 TaskTraits().WithPriority(TaskPriority::USER_BLOCKING)))); |
| 94 SequenceSortKey sort_key_b = sequence_b->GetSortKey(); |
| 95 |
| 96 scoped_refptr<Sequence> sequence_c(new Sequence); |
| 97 sequence_c->PushTask(make_scoped_ptr( |
| 98 new Task(FROM_HERE, Closure(), |
| 99 TaskTraits().WithPriority(TaskPriority::USER_BLOCKING)))); |
| 100 SequenceSortKey sort_key_c = sequence_c->GetSortKey(); |
| 101 |
| 102 scoped_refptr<Sequence> sequence_d(new Sequence); |
| 103 sequence_d->PushTask(make_scoped_ptr( |
| 104 new Task(FROM_HERE, Closure(), |
| 105 TaskTraits().WithPriority(TaskPriority::BACKGROUND)))); |
| 106 SequenceSortKey sort_key_d = sequence_d->GetSortKey(); |
| 107 |
| 108 // Create a PriorityQueue and a Transaction. |
| 109 testing::StrictMock<PriorityQueueCallbackMock> mock; |
| 110 PriorityQueue pq( |
| 111 Bind(&PriorityQueueCallbackMock::SequenceInsertedInPriorityQueue, |
| 112 Unretained(&mock))); |
| 113 scoped_ptr<PriorityQueue::Transaction> transaction(pq.BeginTransaction()); |
| 114 EXPECT_SEQUENCE_AND_SORT_KEY_EQ(PriorityQueue::SequenceAndSortKey(), |
| 115 transaction->Peek()); |
| 116 |
| 117 // Push |sequence_a| in the PriorityQueue. It becomes the sequence with the |
| 118 // highest priority. |
| 119 transaction->Push(make_scoped_ptr( |
| 120 new PriorityQueue::SequenceAndSortKey(sequence_a, sort_key_a))); |
| 121 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( |
| 122 PriorityQueue::SequenceAndSortKey(sequence_a, sort_key_a), |
| 123 transaction->Peek()); |
| 124 |
| 125 // Push |sequence_b| in the PriorityQueue. It becomes the sequence with the |
| 126 // highest priority. |
| 127 transaction->Push(make_scoped_ptr( |
| 128 new PriorityQueue::SequenceAndSortKey(sequence_b, sort_key_b))); |
| 129 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( |
| 130 PriorityQueue::SequenceAndSortKey(sequence_b, sort_key_b), |
| 131 transaction->Peek()); |
| 132 |
| 133 // Push |sequence_c| in the PriorityQueue. |sequence_b| is still the sequence |
| 134 // with the highest priority. |
| 135 transaction->Push(make_scoped_ptr( |
| 136 new PriorityQueue::SequenceAndSortKey(sequence_c, sort_key_c))); |
| 137 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( |
| 138 PriorityQueue::SequenceAndSortKey(sequence_b, sort_key_b), |
| 139 transaction->Peek()); |
| 140 |
| 141 // Push |sequence_d| in the PriorityQueue. |sequence_b| is still the sequence |
| 142 // with the highest priority. |
| 143 transaction->Push(make_scoped_ptr( |
| 144 new PriorityQueue::SequenceAndSortKey(sequence_d, sort_key_d))); |
| 145 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( |
| 146 PriorityQueue::SequenceAndSortKey(sequence_b, sort_key_b), |
| 147 transaction->Peek()); |
| 148 |
| 149 // Pop |sequence_b| from the PriorityQueue. |sequence_c| becomes the sequence |
| 150 // with the highest priority. |
| 151 transaction->Pop(); |
| 152 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( |
| 153 PriorityQueue::SequenceAndSortKey(sequence_c, sort_key_c), |
| 154 transaction->Peek()); |
| 155 |
| 156 // Pop |sequence_c| from the PriorityQueue. |sequence_a| becomes the sequence |
| 157 // with the highest priority. |
| 158 transaction->Pop(); |
| 159 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( |
| 160 PriorityQueue::SequenceAndSortKey(sequence_a, sort_key_a), |
| 161 transaction->Peek()); |
| 162 |
| 163 // Pop |sequence_a| from the PriorityQueue. |sequence_d| becomes the sequence |
| 164 // with the highest priority. |
| 165 transaction->Pop(); |
| 166 EXPECT_SEQUENCE_AND_SORT_KEY_EQ( |
| 167 PriorityQueue::SequenceAndSortKey(sequence_d, sort_key_d), |
| 168 transaction->Peek()); |
| 169 |
| 170 // Pop |sequence_d| from the PriorityQueue. It is now empty. |
| 171 transaction->Pop(); |
| 172 EXPECT_SEQUENCE_AND_SORT_KEY_EQ(PriorityQueue::SequenceAndSortKey(), |
| 173 transaction->Peek()); |
| 174 |
| 175 // Expect 4 calls to mock.SequenceInsertedInPriorityQueue() when the |
| 176 // Transaction is destroyed. |
| 177 EXPECT_CALL(mock, SequenceInsertedInPriorityQueue()).Times(4); |
| 178 transaction.reset(); |
| 179 } |
| 180 |
| 181 // Check that creating Transactions on the same thread for 2 unrelated |
| 182 // PriorityQueues causes a crash. |
| 183 TEST(TaskSchedulerPriorityQueueTest, IllegalTwoTransactionsSameThread) { |
| 184 PriorityQueue pq_a(Bind(&DoNothing)); |
| 185 PriorityQueue pq_b(Bind(&DoNothing)); |
| 186 |
| 187 EXPECT_DCHECK_DEATH( |
| 188 { |
| 189 scoped_ptr<PriorityQueue::Transaction> transaction_a = |
| 190 pq_a.BeginTransaction(); |
| 191 scoped_ptr<PriorityQueue::Transaction> transaction_b = |
| 192 pq_b.BeginTransaction(); |
| 193 }, |
| 194 ""); |
| 195 } |
| 196 |
| 197 // Check that there is no crash when Transactions are created on the same thread |
| 198 // for 2 PriorityQueues which have a predecessor relationship. |
| 199 TEST(TaskSchedulerPriorityQueueTest, LegalTwoTransactionsSameThread) { |
| 200 PriorityQueue pq_a(Bind(&DoNothing)); |
| 201 PriorityQueue pq_b(Bind(&DoNothing), &pq_a); |
| 202 |
| 203 // This shouldn't crash. |
| 204 scoped_ptr<PriorityQueue::Transaction> transaction_a = |
| 205 pq_a.BeginTransaction(); |
| 206 scoped_ptr<PriorityQueue::Transaction> transaction_b = |
| 207 pq_b.BeginTransaction(); |
| 208 } |
| 209 |
| 210 // Check that it is possible to begin multiple Transactions for the same |
| 211 // PriorityQueue on different threads. The call to BeginTransaction() on the |
| 212 // second thread should block until the Transaction has ended on the first |
| 213 // thread. |
| 214 TEST(TaskSchedulerPriorityQueueTest, TwoTransactionsTwoThreads) { |
| 215 PriorityQueue pq(Bind(&DoNothing)); |
| 216 |
| 217 // Call BeginTransaction() on this thread and keep the Transaction alive. |
| 218 scoped_ptr<PriorityQueue::Transaction> transaction = pq.BeginTransaction(); |
| 219 |
| 220 // Call BeginTransaction() on another thread. |
| 221 ThreadBeginningTransaction thread_beginning_transaction(&pq); |
| 222 thread_beginning_transaction.Start(); |
| 223 |
| 224 // After a few milliseconds, the call to BeginTransaction() on the other |
| 225 // thread should not have returned. |
| 226 thread_beginning_transaction.ExpectTransactionDoesNotBegin(); |
| 227 |
| 228 // End the Transaction on the current thread. |
| 229 transaction.reset(); |
| 230 |
| 231 // The other thread should exit after its call to BeginTransaction() returns. |
| 232 thread_beginning_transaction.Join(); |
| 233 } |
| 234 |
| 235 TEST(TaskSchedulerPriorityQueueTest, SequenceAndSortKeyIsNull) { |
| 236 EXPECT_TRUE(PriorityQueue::SequenceAndSortKey().is_null()); |
| 237 |
| 238 const PriorityQueue::SequenceAndSortKey non_null_sequence_andsort_key( |
| 239 make_scoped_refptr(new Sequence), |
| 240 SequenceSortKey(TaskPriority::USER_VISIBLE, TimeTicks())); |
| 241 EXPECT_FALSE(non_null_sequence_andsort_key.is_null()); |
| 242 } |
| 243 |
| 244 } // namespace internal |
| 245 } // namespace base |
OLD | NEW |