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