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/thread_pool.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/callback_forward.h" |
| 9 #include "base/synchronization/condition_variable.h" |
| 10 #include "base/task_scheduler/scheduler_lock.h" |
| 11 #include "base/task_scheduler/shutdown_manager.h" |
| 12 #include "base/task_scheduler/worker_thread.h" |
| 13 #include "base/threading/platform_thread.h" |
| 14 #include "testing/gtest/include/gtest/gtest.h" |
| 15 |
| 16 namespace base { |
| 17 namespace internal { |
| 18 |
| 19 namespace { |
| 20 class SingleThreadChecker { |
| 21 public: |
| 22 SingleThreadChecker(const SchedulerLock* predecessor) |
| 23 : ran_all_tasks_on_same_thread_(true), |
| 24 lock_(predecessor) {} |
| 25 |
| 26 void RunTask() { |
| 27 AutoSchedulerLock auto_lock(lock_); |
| 28 |
| 29 if (!last_thread_handle_.is_null() && |
| 30 !PlatformThread::CurrentHandle().is_equal(last_thread_handle_)) { |
| 31 ran_all_tasks_on_same_thread_ = false; |
| 32 } |
| 33 last_thread_handle_ = PlatformThread::CurrentHandle(); |
| 34 } |
| 35 |
| 36 bool ran_all_tasks_on_same_thread() const { |
| 37 return ran_all_tasks_on_same_thread_; |
| 38 } |
| 39 |
| 40 private: |
| 41 PlatformThreadHandle last_thread_handle_; |
| 42 bool ran_all_tasks_on_same_thread_; |
| 43 SchedulerLock lock_; |
| 44 }; |
| 45 } // namespace |
| 46 |
| 47 class TaskSchedulerThreadPoolTest : public testing::Test { |
| 48 protected: |
| 49 TaskSchedulerThreadPoolTest() |
| 50 : thread_pool_(ThreadPool::CreateThreadPool( |
| 51 ThreadPriority::NORMAL, |
| 52 4, |
| 53 Bind(&TaskSchedulerThreadPoolTest::ReinsertSequenceCallback, |
| 54 Unretained(this)), |
| 55 &shutdown_manager_)), |
| 56 cv_(lock_.RawLockForConditionVariable()), |
| 57 last_posted_task_index_(0), |
| 58 last_run_task_index_(0), |
| 59 ran_task_that_should_not_run_(false) {} |
| 60 |
| 61 Closure GetTaskThatShouldRunClosure( |
| 62 SingleThreadChecker* single_thread_checker) { |
| 63 ++last_posted_task_index_; |
| 64 return Bind(&TaskSchedulerThreadPoolTest::RunTaskThatShouldRun, |
| 65 Unretained(this), last_posted_task_index_, |
| 66 single_thread_checker); |
| 67 } |
| 68 |
| 69 Closure GetTaskThatShouldNotRunClosure() { |
| 70 return Bind(&TaskSchedulerThreadPoolTest::RunTaskThatShouldNotRun, |
| 71 Unretained(this)); |
| 72 } |
| 73 |
| 74 void WaitUntilLastPostedTaskHasRun() { |
| 75 AutoSchedulerLock auto_lock(lock_); |
| 76 while (last_posted_task_index_ != last_run_task_index_) |
| 77 cv_.Wait(); |
| 78 } |
| 79 |
| 80 bool ran_task_that_should_not_run() const { |
| 81 return ran_task_that_should_not_run_; |
| 82 } |
| 83 |
| 84 const SchedulerLock* lock() { |
| 85 return &lock_; |
| 86 } |
| 87 |
| 88 ShutdownManager shutdown_manager_; |
| 89 scoped_ptr<ThreadPool> thread_pool_; |
| 90 |
| 91 private: |
| 92 void ReinsertSequenceCallback(scoped_refptr<Sequence> sequence, |
| 93 const WorkerThread* worker_thread) { |
| 94 thread_pool_->ReinsertSequence(sequence, sequence->GetSortKey(), |
| 95 worker_thread); |
| 96 } |
| 97 |
| 98 void RunTaskThatShouldRun(size_t index, |
| 99 SingleThreadChecker* single_thread_checker) { |
| 100 AutoSchedulerLock auto_lock(lock_); |
| 101 |
| 102 if (single_thread_checker) |
| 103 single_thread_checker->RunTask(); |
| 104 |
| 105 // Wait until the task with index (|index| - 1) has run. If tasks are |
| 106 // executed in the wrong order, this can block forever and make the test |
| 107 // fail. |
| 108 // |
| 109 // Note: It isn't correct to assume that this condition will always be |
| 110 // immediatly true if tasks are popped from the priority queue in the right |
| 111 // order. Indeed, a thread A could start running task #2 before a thread B |
| 112 // starts running task #1 despite the fact that thread B has popped task #1 |
| 113 // before thread A has popped task #2. |
| 114 while (last_run_task_index_ != index - 1) |
| 115 cv_.Wait(); |
| 116 |
| 117 ++last_run_task_index_; |
| 118 cv_.Broadcast(); |
| 119 } |
| 120 |
| 121 void RunTaskThatShouldNotRun() { ran_task_that_should_not_run_ = true; } |
| 122 |
| 123 // Lock protecting |cv_|. |
| 124 SchedulerLock lock_; |
| 125 |
| 126 // Condition variable signaled each time a task completes its execution. |
| 127 ConditionVariable cv_; |
| 128 |
| 129 // Index of the last posted task. |
| 130 size_t last_posted_task_index_; |
| 131 |
| 132 // Index of the last run task. |
| 133 size_t last_run_task_index_; |
| 134 |
| 135 // True if a task that shouldn't run has run. |
| 136 bool ran_task_that_should_not_run_; |
| 137 }; |
| 138 |
| 139 TEST_F(TaskSchedulerThreadPoolTest, PostSingleParallelTask) { |
| 140 ASSERT_TRUE(thread_pool_.get()); |
| 141 |
| 142 thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(), |
| 143 ExecutionMode::PARALLEL) |
| 144 ->PostTask(FROM_HERE, GetTaskThatShouldRunClosure(nullptr)); |
| 145 |
| 146 WaitUntilLastPostedTaskHasRun(); |
| 147 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 148 } |
| 149 |
| 150 TEST_F(TaskSchedulerThreadPoolTest, PostSingleSequencedTask) { |
| 151 ASSERT_TRUE(thread_pool_.get()); |
| 152 |
| 153 thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(), |
| 154 ExecutionMode::SEQUENCED) |
| 155 ->PostTask(FROM_HERE, GetTaskThatShouldRunClosure(nullptr)); |
| 156 |
| 157 WaitUntilLastPostedTaskHasRun(); |
| 158 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 159 } |
| 160 |
| 161 TEST_F(TaskSchedulerThreadPoolTest, PostSingleSingleThreadedTask) { |
| 162 ASSERT_TRUE(thread_pool_.get()); |
| 163 |
| 164 thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(), |
| 165 ExecutionMode::SINGLE_THREADED) |
| 166 ->PostTask(FROM_HERE, GetTaskThatShouldRunClosure(nullptr)); |
| 167 |
| 168 WaitUntilLastPostedTaskHasRun(); |
| 169 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 170 } |
| 171 |
| 172 TEST_F(TaskSchedulerThreadPoolTest, PostMultipleTasksNoWaitBetweenPosts) { |
| 173 ASSERT_TRUE(thread_pool_.get()); |
| 174 |
| 175 for (size_t i = 0; i < 100; ++i) { |
| 176 thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(), |
| 177 ExecutionMode::PARALLEL) |
| 178 ->PostTask(FROM_HERE, GetTaskThatShouldRunClosure(nullptr)); |
| 179 } |
| 180 |
| 181 WaitUntilLastPostedTaskHasRun(); |
| 182 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 183 } |
| 184 |
| 185 TEST_F(TaskSchedulerThreadPoolTest, PostMultipleTasksWaitBetweenPosts) { |
| 186 ASSERT_TRUE(thread_pool_.get()); |
| 187 |
| 188 for (size_t i = 0; i < 100; ++i) { |
| 189 thread_pool_->CreateTaskRunnerWithTraits(TaskTraits(), |
| 190 ExecutionMode::PARALLEL) |
| 191 ->PostTask(FROM_HERE, GetTaskThatShouldRunClosure(nullptr)); |
| 192 WaitUntilLastPostedTaskHasRun(); |
| 193 } |
| 194 |
| 195 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 196 } |
| 197 |
| 198 TEST_F(TaskSchedulerThreadPoolTest, PostMultipleTasksInSameSequence) { |
| 199 ASSERT_TRUE(thread_pool_.get()); |
| 200 |
| 201 auto task_runner = thread_pool_->CreateTaskRunnerWithTraits( |
| 202 TaskTraits(), ExecutionMode::SEQUENCED); |
| 203 |
| 204 for (size_t i = 0; i < 100; ++i) |
| 205 task_runner->PostTask(FROM_HERE, GetTaskThatShouldRunClosure(nullptr)); |
| 206 |
| 207 WaitUntilLastPostedTaskHasRun(); |
| 208 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 209 } |
| 210 |
| 211 TEST_F(TaskSchedulerThreadPoolTest, PostMultipleTasksInTwoSequences) { |
| 212 ASSERT_TRUE(thread_pool_.get()); |
| 213 |
| 214 auto task_runner_a = thread_pool_->CreateTaskRunnerWithTraits( |
| 215 TaskTraits(), ExecutionMode::SEQUENCED); |
| 216 auto task_runner_b = thread_pool_->CreateTaskRunnerWithTraits( |
| 217 TaskTraits(), ExecutionMode::SEQUENCED); |
| 218 |
| 219 for (size_t i = 0; i < 100; ++i) { |
| 220 task_runner_a->PostTask(FROM_HERE, GetTaskThatShouldRunClosure(nullptr)); |
| 221 task_runner_b->PostTask(FROM_HERE, GetTaskThatShouldRunClosure(nullptr)); |
| 222 } |
| 223 |
| 224 WaitUntilLastPostedTaskHasRun(); |
| 225 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 226 } |
| 227 |
| 228 TEST_F(TaskSchedulerThreadPoolTest, PostMultipleSingleThreadedTasks) { |
| 229 ASSERT_TRUE(thread_pool_.get()); |
| 230 |
| 231 auto task_runner = thread_pool_->CreateTaskRunnerWithTraits( |
| 232 TaskTraits(), ExecutionMode::SINGLE_THREADED); |
| 233 SingleThreadChecker single_thread_checker(lock()); |
| 234 |
| 235 for (size_t i = 0; i < 100; ++i) { |
| 236 task_runner->PostTask(FROM_HERE, |
| 237 GetTaskThatShouldRunClosure(&single_thread_checker)); |
| 238 } |
| 239 |
| 240 WaitUntilLastPostedTaskHasRun(); |
| 241 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 242 EXPECT_TRUE(single_thread_checker.ran_all_tasks_on_same_thread()); |
| 243 } |
| 244 |
| 245 TEST_F(TaskSchedulerThreadPoolTest, PostParallelDelayedTasks) { |
| 246 ASSERT_TRUE(thread_pool_.get()); |
| 247 |
| 248 auto task_runner = thread_pool_->CreateTaskRunnerWithTraits( |
| 249 TaskTraits(), ExecutionMode::PARALLEL); |
| 250 |
| 251 for (size_t i = 0; i < 10; ++i) { |
| 252 task_runner->PostDelayedTask(FROM_HERE, |
| 253 GetTaskThatShouldRunClosure(nullptr), |
| 254 TimeDelta::FromMilliseconds(i * 50)); |
| 255 } |
| 256 |
| 257 WaitUntilLastPostedTaskHasRun(); |
| 258 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 259 } |
| 260 |
| 261 TEST_F(TaskSchedulerThreadPoolTest, PostSequencedDelayedTasks) { |
| 262 ASSERT_TRUE(thread_pool_.get()); |
| 263 |
| 264 auto task_runner = thread_pool_->CreateTaskRunnerWithTraits( |
| 265 TaskTraits(), ExecutionMode::SEQUENCED); |
| 266 |
| 267 for (size_t i = 0; i < 10; ++i) { |
| 268 task_runner->PostDelayedTask(FROM_HERE, |
| 269 GetTaskThatShouldRunClosure(nullptr), |
| 270 TimeDelta::FromMilliseconds(i * 50)); |
| 271 } |
| 272 |
| 273 WaitUntilLastPostedTaskHasRun(); |
| 274 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 275 } |
| 276 |
| 277 TEST_F(TaskSchedulerThreadPoolTest, ShutdownBehavior) { |
| 278 ASSERT_TRUE(thread_pool_.get()); |
| 279 |
| 280 shutdown_manager_.set_is_shutting_down_for_testing(true); |
| 281 |
| 282 // Post tasks with different shutdown behaviors. |
| 283 thread_pool_->CreateTaskRunnerWithTraits( |
| 284 TaskTraits().WithShutdownBehavior( |
| 285 TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN), |
| 286 ExecutionMode::PARALLEL) |
| 287 ->PostTask(FROM_HERE, GetTaskThatShouldNotRunClosure()); |
| 288 thread_pool_->CreateTaskRunnerWithTraits( |
| 289 TaskTraits().WithShutdownBehavior( |
| 290 TaskShutdownBehavior::SKIP_ON_SHUTDOWN), |
| 291 ExecutionMode::PARALLEL) |
| 292 ->PostTask(FROM_HERE, GetTaskThatShouldNotRunClosure()); |
| 293 thread_pool_->CreateTaskRunnerWithTraits( |
| 294 TaskTraits().WithShutdownBehavior( |
| 295 TaskShutdownBehavior::BLOCK_SHUTDOWN), |
| 296 ExecutionMode::PARALLEL) |
| 297 ->PostTask(FROM_HERE, GetTaskThatShouldRunClosure(nullptr)); |
| 298 |
| 299 WaitUntilLastPostedTaskHasRun(); |
| 300 |
| 301 shutdown_manager_.set_is_shutting_down_for_testing(false); |
| 302 thread_pool_->ShutdownAndJoinAllThreadsForTesting(); |
| 303 |
| 304 EXPECT_FALSE(ran_task_that_should_not_run()); |
| 305 } |
| 306 |
| 307 } // namespace internal |
| 308 } // namespace base |
OLD | NEW |