| 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/scheduler_worker_thread.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <memory> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include "base/bind.h" | |
| 13 #include "base/bind_helpers.h" | |
| 14 #include "base/macros.h" | |
| 15 #include "base/memory/ptr_util.h" | |
| 16 #include "base/synchronization/condition_variable.h" | |
| 17 #include "base/task_scheduler/scheduler_lock.h" | |
| 18 #include "base/task_scheduler/sequence.h" | |
| 19 #include "base/task_scheduler/task.h" | |
| 20 #include "base/task_scheduler/task_tracker.h" | |
| 21 #include "base/time/time.h" | |
| 22 #include "testing/gtest/include/gtest/gtest.h" | |
| 23 | |
| 24 namespace base { | |
| 25 namespace internal { | |
| 26 namespace { | |
| 27 | |
| 28 const size_t kNumSequencesPerTest = 150; | |
| 29 | |
| 30 // The test parameter is the number of Tasks per Sequence returned by GetWork(). | |
| 31 class TaskSchedulerWorkerThreadTest : public testing::TestWithParam<size_t> { | |
| 32 protected: | |
| 33 TaskSchedulerWorkerThreadTest() | |
| 34 : main_entry_called_(WaitableEvent::ResetPolicy::MANUAL, | |
| 35 WaitableEvent::InitialState::NOT_SIGNALED), | |
| 36 num_get_work_cv_(lock_.CreateConditionVariable()), | |
| 37 worker_thread_set_(WaitableEvent::ResetPolicy::MANUAL, | |
| 38 WaitableEvent::InitialState::NOT_SIGNALED) {} | |
| 39 | |
| 40 void SetUp() override { | |
| 41 worker_thread_ = SchedulerWorkerThread::Create( | |
| 42 ThreadPriority::NORMAL, | |
| 43 WrapUnique(new TestSchedulerWorkerThreadDelegate(this)), | |
| 44 &task_tracker_); | |
| 45 ASSERT_TRUE(worker_thread_); | |
| 46 worker_thread_set_.Signal(); | |
| 47 main_entry_called_.Wait(); | |
| 48 } | |
| 49 | |
| 50 void TearDown() override { | |
| 51 worker_thread_->JoinForTesting(); | |
| 52 } | |
| 53 | |
| 54 size_t TasksPerSequence() const { return GetParam(); } | |
| 55 | |
| 56 // Wait until GetWork() has been called |num_get_work| times. | |
| 57 void WaitForNumGetWork(size_t num_get_work) { | |
| 58 AutoSchedulerLock auto_lock(lock_); | |
| 59 while (num_get_work_ < num_get_work) | |
| 60 num_get_work_cv_->Wait(); | |
| 61 } | |
| 62 | |
| 63 void SetMaxGetWork(size_t max_get_work) { | |
| 64 AutoSchedulerLock auto_lock(lock_); | |
| 65 max_get_work_ = max_get_work; | |
| 66 } | |
| 67 | |
| 68 void SetNumSequencesToCreate(size_t num_sequences_to_create) { | |
| 69 AutoSchedulerLock auto_lock(lock_); | |
| 70 EXPECT_EQ(0U, num_sequences_to_create_); | |
| 71 num_sequences_to_create_ = num_sequences_to_create; | |
| 72 } | |
| 73 | |
| 74 size_t NumRunTasks() { | |
| 75 AutoSchedulerLock auto_lock(lock_); | |
| 76 return num_run_tasks_; | |
| 77 } | |
| 78 | |
| 79 std::vector<scoped_refptr<Sequence>> CreatedSequences() { | |
| 80 AutoSchedulerLock auto_lock(lock_); | |
| 81 return created_sequences_; | |
| 82 } | |
| 83 | |
| 84 std::vector<scoped_refptr<Sequence>> EnqueuedSequences() { | |
| 85 AutoSchedulerLock auto_lock(lock_); | |
| 86 return re_enqueued_sequences_; | |
| 87 } | |
| 88 | |
| 89 std::unique_ptr<SchedulerWorkerThread> worker_thread_; | |
| 90 | |
| 91 private: | |
| 92 class TestSchedulerWorkerThreadDelegate | |
| 93 : public SchedulerWorkerThread::Delegate { | |
| 94 public: | |
| 95 TestSchedulerWorkerThreadDelegate(TaskSchedulerWorkerThreadTest* outer) | |
| 96 : outer_(outer) {} | |
| 97 | |
| 98 // SchedulerWorkerThread::Delegate: | |
| 99 void OnMainEntry(SchedulerWorkerThread* worker_thread) override { | |
| 100 outer_->worker_thread_set_.Wait(); | |
| 101 EXPECT_EQ(outer_->worker_thread_.get(), worker_thread); | |
| 102 | |
| 103 // Without synchronization, OnMainEntry() could be called twice without | |
| 104 // generating an error. | |
| 105 AutoSchedulerLock auto_lock(outer_->lock_); | |
| 106 EXPECT_FALSE(outer_->main_entry_called_.IsSignaled()); | |
| 107 outer_->main_entry_called_.Signal(); | |
| 108 } | |
| 109 | |
| 110 scoped_refptr<Sequence> GetWork( | |
| 111 SchedulerWorkerThread* worker_thread) override { | |
| 112 EXPECT_EQ(outer_->worker_thread_.get(), worker_thread); | |
| 113 | |
| 114 { | |
| 115 AutoSchedulerLock auto_lock(outer_->lock_); | |
| 116 | |
| 117 // Increment the number of times that this method has been called. | |
| 118 ++outer_->num_get_work_; | |
| 119 outer_->num_get_work_cv_->Signal(); | |
| 120 | |
| 121 // Verify that this method isn't called more times than expected. | |
| 122 EXPECT_LE(outer_->num_get_work_, outer_->max_get_work_); | |
| 123 | |
| 124 // Check if a Sequence should be returned. | |
| 125 if (outer_->num_sequences_to_create_ == 0) | |
| 126 return nullptr; | |
| 127 --outer_->num_sequences_to_create_; | |
| 128 } | |
| 129 | |
| 130 // Create a Sequence with TasksPerSequence() Tasks. | |
| 131 scoped_refptr<Sequence> sequence(new Sequence); | |
| 132 for (size_t i = 0; i < outer_->TasksPerSequence(); ++i) { | |
| 133 std::unique_ptr<Task> task(new Task( | |
| 134 FROM_HERE, Bind(&TaskSchedulerWorkerThreadTest::RunTaskCallback, | |
| 135 Unretained(outer_)), | |
| 136 TaskTraits(), TimeDelta())); | |
| 137 EXPECT_TRUE(outer_->task_tracker_.WillPostTask(task.get())); | |
| 138 sequence->PushTask(std::move(task)); | |
| 139 } | |
| 140 | |
| 141 { | |
| 142 // Add the Sequence to the vector of created Sequences. | |
| 143 AutoSchedulerLock auto_lock(outer_->lock_); | |
| 144 outer_->created_sequences_.push_back(sequence); | |
| 145 } | |
| 146 | |
| 147 return sequence; | |
| 148 } | |
| 149 | |
| 150 // This override verifies that |sequence| contains the expected number of | |
| 151 // Tasks and adds it to |enqueued_sequences_|. Unlike a normal | |
| 152 // EnqueueSequence implementation, it doesn't reinsert |sequence| into a | |
| 153 // queue for further execution. | |
| 154 void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override { | |
| 155 EXPECT_GT(outer_->TasksPerSequence(), 1U); | |
| 156 | |
| 157 // Verify that |sequence| contains TasksPerSequence() - 1 Tasks. | |
| 158 for (size_t i = 0; i < outer_->TasksPerSequence() - 1; ++i) { | |
| 159 EXPECT_TRUE(sequence->PeekTask()); | |
| 160 sequence->PopTask(); | |
| 161 } | |
| 162 EXPECT_FALSE(sequence->PeekTask()); | |
| 163 | |
| 164 // Add |sequence| to |re_enqueued_sequences_|. | |
| 165 AutoSchedulerLock auto_lock(outer_->lock_); | |
| 166 outer_->re_enqueued_sequences_.push_back(std::move(sequence)); | |
| 167 EXPECT_LE(outer_->re_enqueued_sequences_.size(), | |
| 168 outer_->created_sequences_.size()); | |
| 169 } | |
| 170 | |
| 171 TimeDelta GetSleepTimeout() override { | |
| 172 return TimeDelta::Max(); | |
| 173 } | |
| 174 | |
| 175 private: | |
| 176 TaskSchedulerWorkerThreadTest* outer_; | |
| 177 }; | |
| 178 | |
| 179 void RunTaskCallback() { | |
| 180 AutoSchedulerLock auto_lock(lock_); | |
| 181 ++num_run_tasks_; | |
| 182 EXPECT_LE(num_run_tasks_, created_sequences_.size()); | |
| 183 } | |
| 184 | |
| 185 TaskTracker task_tracker_; | |
| 186 | |
| 187 // Synchronizes access to all members below. | |
| 188 mutable SchedulerLock lock_; | |
| 189 | |
| 190 // Signaled once OnMainEntry() has been called. | |
| 191 WaitableEvent main_entry_called_; | |
| 192 | |
| 193 // Number of Sequences that should be created by GetWork(). When this | |
| 194 // is 0, GetWork() returns nullptr. | |
| 195 size_t num_sequences_to_create_ = 0; | |
| 196 | |
| 197 // Number of times that GetWork() has been called. | |
| 198 size_t num_get_work_ = 0; | |
| 199 | |
| 200 // Maximum number of times that GetWork() can be called. | |
| 201 size_t max_get_work_ = 0; | |
| 202 | |
| 203 // Condition variable signaled when |num_get_work_| is incremented. | |
| 204 std::unique_ptr<ConditionVariable> num_get_work_cv_; | |
| 205 | |
| 206 // Sequences created by GetWork(). | |
| 207 std::vector<scoped_refptr<Sequence>> created_sequences_; | |
| 208 | |
| 209 // Sequences passed to EnqueueSequence(). | |
| 210 std::vector<scoped_refptr<Sequence>> re_enqueued_sequences_; | |
| 211 | |
| 212 // Number of times that RunTaskCallback() has been called. | |
| 213 size_t num_run_tasks_ = 0; | |
| 214 | |
| 215 // Signaled after |worker_thread_| is set. | |
| 216 WaitableEvent worker_thread_set_; | |
| 217 | |
| 218 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerThreadTest); | |
| 219 }; | |
| 220 | |
| 221 // Verify that when GetWork() continuously returns Sequences, all Tasks in these | |
| 222 // Sequences run successfully. The test wakes up the SchedulerWorkerThread once. | |
| 223 TEST_P(TaskSchedulerWorkerThreadTest, ContinuousWork) { | |
| 224 // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to | |
| 225 // return nullptr. | |
| 226 SetNumSequencesToCreate(kNumSequencesPerTest); | |
| 227 | |
| 228 // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a | |
| 229 // Sequence and one call in which its returns nullptr. | |
| 230 const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1; | |
| 231 SetMaxGetWork(kExpectedNumGetWork); | |
| 232 | |
| 233 // Wake up |worker_thread_| and wait until GetWork() has been invoked the | |
| 234 // expected amount of times. | |
| 235 worker_thread_->WakeUp(); | |
| 236 WaitForNumGetWork(kExpectedNumGetWork); | |
| 237 | |
| 238 // All tasks should have run. | |
| 239 EXPECT_EQ(kNumSequencesPerTest, NumRunTasks()); | |
| 240 | |
| 241 // If Sequences returned by GetWork() contain more than one Task, they aren't | |
| 242 // empty after the worker thread pops Tasks from them and thus should be | |
| 243 // returned to EnqueueSequence(). | |
| 244 if (TasksPerSequence() > 1) | |
| 245 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); | |
| 246 else | |
| 247 EXPECT_TRUE(EnqueuedSequences().empty()); | |
| 248 } | |
| 249 | |
| 250 // Verify that when GetWork() alternates between returning a Sequence and | |
| 251 // returning nullptr, all Tasks in the returned Sequences run successfully. The | |
| 252 // test wakes up the SchedulerWorkerThread once for each Sequence. | |
| 253 TEST_P(TaskSchedulerWorkerThreadTest, IntermittentWork) { | |
| 254 for (size_t i = 0; i < kNumSequencesPerTest; ++i) { | |
| 255 // Set GetWork() to return 1 Sequence before starting to return | |
| 256 // nullptr. | |
| 257 SetNumSequencesToCreate(1); | |
| 258 | |
| 259 // Expect |i + 1| calls to GetWork() in which it returns a Sequence and | |
| 260 // |i + 1| calls in which it returns nullptr. | |
| 261 const size_t expected_num_get_work = 2 * (i + 1); | |
| 262 SetMaxGetWork(expected_num_get_work); | |
| 263 | |
| 264 // Wake up |worker_thread_| and wait until GetWork() has been invoked | |
| 265 // the expected amount of times. | |
| 266 worker_thread_->WakeUp(); | |
| 267 WaitForNumGetWork(expected_num_get_work); | |
| 268 | |
| 269 // The Task should have run | |
| 270 EXPECT_EQ(i + 1, NumRunTasks()); | |
| 271 | |
| 272 // If Sequences returned by GetWork() contain more than one Task, they | |
| 273 // aren't empty after the worker thread pops Tasks from them and thus should | |
| 274 // be returned to EnqueueSequence(). | |
| 275 if (TasksPerSequence() > 1) | |
| 276 EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); | |
| 277 else | |
| 278 EXPECT_TRUE(EnqueuedSequences().empty()); | |
| 279 } | |
| 280 } | |
| 281 | |
| 282 INSTANTIATE_TEST_CASE_P(OneTaskPerSequence, | |
| 283 TaskSchedulerWorkerThreadTest, | |
| 284 ::testing::Values(1)); | |
| 285 INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence, | |
| 286 TaskSchedulerWorkerThreadTest, | |
| 287 ::testing::Values(2)); | |
| 288 | |
| 289 } // namespace | |
| 290 } // namespace internal | |
| 291 } // namespace base | |
| OLD | NEW |