Chromium Code Reviews| 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/worker_thread.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/callback_forward.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/memory/scoped_ptr.h" | |
| 12 #include "base/synchronization/condition_variable.h" | |
| 13 #include "base/synchronization/waitable_event.h" | |
| 14 #include "base/task_scheduler/priority_queue.h" | |
| 15 #include "base/task_scheduler/scheduler_lock.h" | |
| 16 #include "base/task_scheduler/task_tracker.h" | |
| 17 #include "base/task_scheduler/utils.h" | |
| 18 #include "testing/gtest/include/gtest/gtest.h" | |
| 19 | |
| 20 namespace base { | |
| 21 namespace internal { | |
| 22 | |
| 23 class TaskSchedulerWorkerThreadTest : public testing::Test { | |
| 24 protected: | |
| 25 TaskSchedulerWorkerThreadTest() | |
| 26 : shared_priority_queue_(Bind(&DoNothing)), | |
| 27 run_task_cv_(lock_.CreateConditionVariable()), | |
| 28 becomes_idle_callback_cv_(lock_.CreateConditionVariable()), | |
| 29 num_becomes_idle_callback_invocations_(0), | |
| 30 last_posted_task_index_(0), | |
| 31 last_run_task_index_(0), | |
| 32 ran_task_that_should_not_run_(false), | |
| 33 ran_tasks_in_wrong_order_(true) { | |
| 34 worker_thread_ = WorkerThread::CreateWorkerThread( | |
| 35 ThreadPriority::NORMAL, | |
| 36 &shared_priority_queue_, | |
| 37 Bind(&TaskSchedulerWorkerThreadTest::ReinsertSequenceCallback, | |
| 38 Unretained(this)), | |
| 39 Bind(&TaskSchedulerWorkerThreadTest::BecomesIdleCallback, | |
| 40 Unretained(this)), | |
| 41 &task_tracker_); | |
| 42 } | |
| 43 | |
| 44 Closure GetTaskThatShouldRunClosure() { | |
| 45 ++last_posted_task_index_; | |
| 46 return Bind(&TaskSchedulerWorkerThreadTest::RunTaskThatShouldRun, | |
| 47 Unretained(this), last_posted_task_index_); | |
| 48 } | |
| 49 | |
| 50 Closure GetTaskThatShouldNotRunClosure() { | |
| 51 return Bind(&TaskSchedulerWorkerThreadTest::RunTaskThatShouldNotRun, | |
| 52 Unretained(this)); | |
| 53 } | |
| 54 | |
| 55 void WaitUntilLastPostedTaskHasRun() { | |
|
gab
2016/03/21 19:11:55
s/HasRun/Ran/
(here and below)
fdoray
2016/03/24 19:21:11
Done.
| |
| 56 AutoSchedulerLock auto_lock(lock_); | |
| 57 while (last_posted_task_index_ != last_run_task_index_) | |
| 58 run_task_cv_->Wait(); | |
| 59 } | |
| 60 | |
| 61 void ExpectLastPostedTaskHasRun() { | |
| 62 AutoSchedulerLock auto_lock(lock_); | |
| 63 EXPECT_EQ(last_posted_task_index_, last_run_task_index_); | |
| 64 } | |
| 65 | |
| 66 void WaitUntilNumBecomesIdleCallbackInvocations( | |
| 67 size_t expected_num_becomes_idle_callback_invocations) { | |
| 68 AutoSchedulerLock auto_lock(lock_); | |
| 69 while (num_becomes_idle_callback_invocations_ != | |
| 70 expected_num_becomes_idle_callback_invocations) { | |
| 71 EXPECT_LT(num_becomes_idle_callback_invocations_, | |
| 72 expected_num_becomes_idle_callback_invocations); | |
| 73 becomes_idle_callback_cv_->Wait(); | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 bool ran_task_that_should_not_run() const { | |
| 78 return ran_task_that_should_not_run_; | |
| 79 } | |
| 80 | |
| 81 protected: | |
| 82 PriorityQueue shared_priority_queue_; | |
| 83 TaskTracker task_tracker_; | |
| 84 scoped_ptr<WorkerThread> worker_thread_; | |
| 85 | |
| 86 private: | |
| 87 void ReinsertSequenceCallback(scoped_refptr<Sequence> sequence, | |
| 88 const WorkerThread* worker_thread) { | |
| 89 const SequenceSortKey sort_key = sequence->GetSortKey(); | |
| 90 shared_priority_queue_.BeginTransaction()->Push(make_scoped_ptr( | |
| 91 new PriorityQueue::SequenceAndSortKey(std::move(sequence), sort_key))); | |
| 92 } | |
| 93 | |
| 94 void BecomesIdleCallback(WorkerThread* worker_thread) { | |
| 95 AutoSchedulerLock auto_lock(lock_); | |
| 96 ++num_becomes_idle_callback_invocations_; | |
| 97 becomes_idle_callback_cv_->Signal(); | |
| 98 } | |
| 99 | |
| 100 void RunTaskThatShouldRun(size_t index) { | |
| 101 AutoSchedulerLock auto_lock(lock_); | |
| 102 | |
| 103 if (index != last_run_task_index_ + 1) | |
| 104 ran_tasks_in_wrong_order_ = true; | |
|
gab
2016/03/21 19:11:55
ADD_FAILURE here too instead of maintaining extra
fdoray
2016/03/24 19:21:10
Done.
| |
| 105 | |
| 106 last_run_task_index_ = index; | |
| 107 run_task_cv_->Signal(); | |
| 108 } | |
| 109 | |
| 110 void RunTaskThatShouldNotRun() { ran_task_that_should_not_run_ = true; } | |
|
gab
2016/03/21 19:11:55
Instead of having extra state here and checking fo
fdoray
2016/03/24 19:21:11
Done.
| |
| 111 | |
| 112 // Lock protecting |run_task_cv_|. | |
|
gab
2016/03/21 19:11:55
Hmm, it does more than that it seems?
fdoray
2016/03/24 19:21:11
Done.
| |
| 113 SchedulerLock lock_; | |
| 114 | |
| 115 // Condition variable signaled each time a task completes its execution. | |
| 116 scoped_ptr<ConditionVariable> run_task_cv_; | |
| 117 | |
| 118 // Condition variable signaled when BecomesIdleCallback() is invoked. | |
| 119 scoped_ptr<ConditionVariable> becomes_idle_callback_cv_; | |
| 120 | |
| 121 // Number of times that BecomesIdleCallback() has been called. | |
| 122 size_t num_becomes_idle_callback_invocations_; | |
| 123 | |
| 124 // Index of the last posted task. | |
| 125 size_t last_posted_task_index_; | |
| 126 | |
| 127 // Index of the last run task. | |
| 128 size_t last_run_task_index_; | |
| 129 | |
| 130 // True if a task that shouldn't run has run. | |
| 131 bool ran_task_that_should_not_run_; | |
| 132 | |
| 133 // True if tasks were run in the wrong order. | |
| 134 bool ran_tasks_in_wrong_order_; | |
| 135 | |
| 136 }; | |
| 137 | |
| 138 TEST_F(TaskSchedulerWorkerThreadTest, PostOneSingleThreadedTask) { | |
| 139 ASSERT_NE(nullptr, worker_thread_.get()); | |
|
gab
2016/03/21 19:11:55
ASSERT_TRUE(worker_thread_);
fdoray
2016/03/24 19:21:11
Done.
| |
| 140 WaitUntilNumBecomesIdleCallbackInvocations(1); | |
|
gab
2016/03/21 19:11:55
Move these two lines to constructor instead of at
fdoray
2016/03/24 19:21:11
Done.
| |
| 141 | |
| 142 worker_thread_->CreateTaskRunnerWithTraits(TaskTraits()) | |
| 143 ->PostTask(FROM_HERE, GetTaskThatShouldRunClosure()); | |
| 144 | |
| 145 WaitUntilNumBecomesIdleCallbackInvocations(2); | |
|
gab
2016/03/21 19:11:55
Since WaitUntilNumBecomesIdleCallbackInvocations(1
fdoray
2016/03/24 19:21:10
With WaitForNextBecomesIdleCallbackInvocation(), i
| |
| 146 ExpectLastPostedTaskHasRun(); | |
| 147 worker_thread_->JoinForTesting(); | |
|
gab
2016/03/21 19:11:55
Since the thread is created by the fixture I think
gab
2016/03/21 19:11:55
In fact I think TearDown() should do both:
Expect
fdoray
2016/03/24 19:21:11
Done.
fdoray
2016/03/24 19:21:11
ExpectLastPostedTaskHasRun() is not done in the de
| |
| 148 } | |
| 149 | |
| 150 TEST_F(TaskSchedulerWorkerThreadTest, | |
| 151 PostMultipleSingleThreadedTasksNoWaitBetweenPosts) { | |
| 152 ASSERT_NE(nullptr, worker_thread_.get()); | |
| 153 WaitUntilNumBecomesIdleCallbackInvocations(1); | |
| 154 | |
| 155 auto task_runner = worker_thread_->CreateTaskRunnerWithTraits(TaskTraits()); | |
| 156 | |
| 157 for (size_t i = 0; i < 100; ++i) | |
| 158 task_runner->PostTask(FROM_HERE, GetTaskThatShouldRunClosure()); | |
| 159 | |
| 160 WaitUntilLastPostedTaskHasRun(); | |
| 161 worker_thread_->JoinForTesting(); | |
| 162 } | |
| 163 | |
| 164 TEST_F(TaskSchedulerWorkerThreadTest, | |
| 165 PostMultipleSingleThreadedTasksWaitBetweenPosts) { | |
| 166 ASSERT_NE(nullptr, worker_thread_.get()); | |
| 167 WaitUntilNumBecomesIdleCallbackInvocations(1); | |
| 168 | |
| 169 auto task_runner = worker_thread_->CreateTaskRunnerWithTraits(TaskTraits()); | |
| 170 | |
| 171 for (size_t i = 0; i < 100; ++i) { | |
| 172 task_runner->PostTask(FROM_HERE, GetTaskThatShouldRunClosure()); | |
| 173 WaitUntilLastPostedTaskHasRun(); | |
| 174 WaitUntilNumBecomesIdleCallbackInvocations(2 + i); | |
| 175 } | |
| 176 | |
| 177 worker_thread_->JoinForTesting(); | |
| 178 } | |
| 179 | |
| 180 TEST_F(TaskSchedulerWorkerThreadTest, | |
| 181 PostMultipleTasksTwoSingleThreadedTaskRunners) { | |
| 182 ASSERT_NE(nullptr, worker_thread_.get()); | |
| 183 WaitUntilNumBecomesIdleCallbackInvocations(1); | |
| 184 | |
| 185 auto task_runner_a = worker_thread_->CreateTaskRunnerWithTraits(TaskTraits()); | |
| 186 auto task_runner_b = worker_thread_->CreateTaskRunnerWithTraits(TaskTraits()); | |
| 187 | |
| 188 for (size_t i = 0; i < 100; ++i) { | |
| 189 task_runner_a->PostTask(FROM_HERE, GetTaskThatShouldRunClosure()); | |
| 190 task_runner_b->PostTask(FROM_HERE, GetTaskThatShouldRunClosure()); | |
| 191 } | |
| 192 | |
| 193 WaitUntilLastPostedTaskHasRun(); | |
| 194 worker_thread_->JoinForTesting(); | |
| 195 } | |
| 196 | |
| 197 TEST_F(TaskSchedulerWorkerThreadTest, PostOneTaskInSharedPriorityQueue) { | |
| 198 ASSERT_NE(nullptr, worker_thread_.get()); | |
| 199 WaitUntilNumBecomesIdleCallbackInvocations(1); | |
| 200 | |
| 201 PostTaskHelper(make_scoped_ptr(new Task( | |
| 202 FROM_HERE, GetTaskThatShouldRunClosure(), TaskTraits())), | |
| 203 new Sequence, &shared_priority_queue_, &task_tracker_); | |
|
gab
2016/03/21 19:11:55
make_scoped_refptr(new Sequence)
here and below (
fdoray
2016/03/24 19:21:11
Done.
| |
| 204 EXPECT_TRUE(worker_thread_->WakeUp()); | |
| 205 | |
| 206 WaitUntilNumBecomesIdleCallbackInvocations(2); | |
| 207 ExpectLastPostedTaskHasRun(); | |
| 208 worker_thread_->JoinForTesting(); | |
| 209 } | |
| 210 | |
| 211 TEST_F(TaskSchedulerWorkerThreadTest, PostSharedAndSingleThreadedTasks) { | |
| 212 ASSERT_NE(nullptr, worker_thread_.get()); | |
| 213 WaitUntilNumBecomesIdleCallbackInvocations(1); | |
| 214 | |
| 215 // Post a task in the shared priority queue. | |
| 216 PostTaskHelper(make_scoped_ptr(new Task( | |
| 217 FROM_HERE, GetTaskThatShouldRunClosure(), TaskTraits())), | |
| 218 new Sequence, &shared_priority_queue_, &task_tracker_); | |
| 219 | |
| 220 // Post a task in the single-threaded priority queue. The TaskRunner will wake | |
| 221 // up the WorkerThread. | |
| 222 worker_thread_->CreateTaskRunnerWithTraits(TaskTraits()) | |
| 223 ->PostTask(FROM_HERE, GetTaskThatShouldRunClosure()); | |
| 224 | |
| 225 WaitUntilNumBecomesIdleCallbackInvocations(2); | |
| 226 ExpectLastPostedTaskHasRun(); | |
| 227 worker_thread_->JoinForTesting(); | |
| 228 } | |
| 229 | |
| 230 TEST_F(TaskSchedulerWorkerThreadTest, WakeUpIdleThread) { | |
| 231 ASSERT_NE(nullptr, worker_thread_.get()); | |
| 232 WaitUntilNumBecomesIdleCallbackInvocations(1); | |
| 233 | |
| 234 EXPECT_TRUE(worker_thread_->WakeUp()); | |
| 235 WaitUntilNumBecomesIdleCallbackInvocations(2); | |
| 236 | |
| 237 worker_thread_->JoinForTesting(); | |
| 238 } | |
| 239 | |
| 240 TEST_F(TaskSchedulerWorkerThreadTest, WakeUpBusyThread) { | |
| 241 ASSERT_NE(nullptr, worker_thread_.get()); | |
| 242 WaitUntilNumBecomesIdleCallbackInvocations(1); | |
| 243 | |
| 244 // Post a task. | |
| 245 WaitableEvent event(false, false); | |
| 246 worker_thread_->CreateTaskRunnerWithTraits(TaskTraits()) | |
| 247 ->PostTask(FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&event))); | |
| 248 | |
| 249 // Because the thread is busy, WakeUp() should return false. | |
| 250 EXPECT_FALSE(worker_thread_->WakeUp()); | |
| 251 | |
| 252 event.Signal(); | |
| 253 WaitUntilNumBecomesIdleCallbackInvocations(2); | |
| 254 worker_thread_->JoinForTesting(); | |
| 255 } | |
|
gab
2016/03/21 19:11:55
A good chunk of ThreadMain is about handling race
fdoray
2016/03/24 19:21:11
Done.
| |
| 256 | |
| 257 } // namespace internal | |
| 258 } // namespace base | |
| OLD | NEW |