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/task_tracker.h" |
| 6 |
| 7 #include <queue> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/macros.h" |
| 12 #include "base/memory/scoped_ptr.h" |
| 13 #include "base/synchronization/waitable_event.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 "testing/gtest/include/gtest/gtest.h" |
| 20 |
| 21 namespace base { |
| 22 namespace internal { |
| 23 |
| 24 namespace { |
| 25 |
| 26 class ThreadCallingShutdown : public SimpleThread { |
| 27 public: |
| 28 explicit ThreadCallingShutdown(TaskTracker* tracker) |
| 29 : SimpleThread("ThreadCallingShutdown"), tracker_(tracker) {} |
| 30 |
| 31 // Returns true once the call to Shutdown() has returned. |
| 32 bool has_returned() const { return has_returned_; } |
| 33 |
| 34 private: |
| 35 void Run() override { |
| 36 tracker_->Shutdown(); |
| 37 has_returned_ = true; |
| 38 } |
| 39 |
| 40 TaskTracker* const tracker_; |
| 41 bool has_returned_ = false; |
| 42 |
| 43 DISALLOW_COPY_AND_ASSIGN(ThreadCallingShutdown); |
| 44 }; |
| 45 |
| 46 class TaskSchedulerTaskTrackerTest |
| 47 : public testing::TestWithParam<TaskShutdownBehavior> { |
| 48 public: |
| 49 TaskSchedulerTaskTrackerTest() = default; |
| 50 |
| 51 void RunNextPostedTaskViaTracker() { |
| 52 ASSERT_FALSE(posted_tasks_.empty()); |
| 53 tracker_.RunTask(posted_tasks_.front().get()); |
| 54 posted_tasks_.pop(); |
| 55 } |
| 56 |
| 57 protected: |
| 58 // Creates a task with |shutdown_behavior|. |
| 59 scoped_ptr<Task> CreateTask(TaskShutdownBehavior shutdown_behavior) { |
| 60 return make_scoped_ptr(new Task( |
| 61 FROM_HERE, |
| 62 Bind(&TaskSchedulerTaskTrackerTest::RunTaskCallback, Unretained(this)), |
| 63 TaskTraits().WithShutdownBehavior(shutdown_behavior))); |
| 64 } |
| 65 |
| 66 // Tries to post |task| via |tracker_|. If |tracker_| approves the operation, |
| 67 // |task| is added to |posted_tasks_|. |
| 68 void PostTaskViaTracker(scoped_ptr<Task> task) { |
| 69 tracker_.PostTask( |
| 70 Bind(&TaskSchedulerTaskTrackerTest::PostTaskCallback, Unretained(this)), |
| 71 std::move(task)); |
| 72 } |
| 73 |
| 74 // Calls tracker_->Shutdown() on a new thread. When this returns, the |
| 75 // Shutdown() method has been entered on the new thread, but it hasn't |
| 76 // necessarily returned. |
| 77 void CallShutdownAsync() { |
| 78 DCHECK(!thread_calling_shutdown_.get()); |
| 79 thread_calling_shutdown_.reset(new ThreadCallingShutdown(&tracker_)); |
| 80 thread_calling_shutdown_->Start(); |
| 81 while (!tracker_.IsShuttingDownForTesting() && |
| 82 !tracker_.shutdown_completed()) { |
| 83 PlatformThread::YieldCurrentThread(); |
| 84 } |
| 85 } |
| 86 |
| 87 void WaitForAsyncShutdownCompleted() { |
| 88 DCHECK(thread_calling_shutdown_.get()); |
| 89 thread_calling_shutdown_->Join(); |
| 90 EXPECT_TRUE(thread_calling_shutdown_->has_returned()); |
| 91 EXPECT_TRUE(tracker_.shutdown_completed()); |
| 92 } |
| 93 |
| 94 void VerifyAsyncShutdownInProgress() { |
| 95 DCHECK(thread_calling_shutdown_.get()); |
| 96 EXPECT_FALSE(thread_calling_shutdown_->has_returned()); |
| 97 EXPECT_FALSE(tracker_.shutdown_completed()); |
| 98 EXPECT_TRUE(tracker_.IsShuttingDownForTesting()); |
| 99 } |
| 100 |
| 101 TaskTracker tracker_; |
| 102 size_t num_tasks_executed_ = 0; |
| 103 std::queue<scoped_ptr<Task>> posted_tasks_; |
| 104 |
| 105 private: |
| 106 void PostTaskCallback(scoped_ptr<Task> task) { |
| 107 posted_tasks_.push(std::move(task)); |
| 108 } |
| 109 |
| 110 void RunTaskCallback() { ++num_tasks_executed_; } |
| 111 |
| 112 scoped_ptr<ThreadCallingShutdown> thread_calling_shutdown_; |
| 113 |
| 114 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerTaskTrackerTest); |
| 115 }; |
| 116 |
| 117 // A thread which calls |
| 118 // TaskSchedulerTaskTrackerTest::RunNextPostedTaskViaTracker() asynchronously. |
| 119 class ThreadRunningNextPostedTask : public SimpleThread { |
| 120 public: |
| 121 explicit ThreadRunningNextPostedTask(TaskSchedulerTaskTrackerTest* test) |
| 122 : SimpleThread("ThreadRunningNextPostedTask"), test_(test) {} |
| 123 |
| 124 private: |
| 125 void Run() override { test_->RunNextPostedTaskViaTracker(); } |
| 126 |
| 127 TaskSchedulerTaskTrackerTest* const test_; |
| 128 |
| 129 DISALLOW_COPY_AND_ASSIGN(ThreadRunningNextPostedTask); |
| 130 }; |
| 131 |
| 132 } // namespace |
| 133 |
| 134 TEST_P(TaskSchedulerTaskTrackerTest, PostAndRunBeforeShutdown) { |
| 135 scoped_ptr<Task> task_to_post(CreateTask(GetParam())); |
| 136 const Task* task_to_post_raw = task_to_post.get(); |
| 137 |
| 138 // Post the task. |
| 139 EXPECT_TRUE(posted_tasks_.empty()); |
| 140 PostTaskViaTracker(std::move(task_to_post)); |
| 141 EXPECT_EQ(1U, posted_tasks_.size()); |
| 142 EXPECT_EQ(task_to_post_raw, posted_tasks_.front().get()); |
| 143 |
| 144 // Run the posted task. |
| 145 EXPECT_EQ(0U, num_tasks_executed_); |
| 146 RunNextPostedTaskViaTracker(); |
| 147 EXPECT_EQ(1U, num_tasks_executed_); |
| 148 |
| 149 // Shutdown() shouldn't block. |
| 150 tracker_.Shutdown(); |
| 151 } |
| 152 |
| 153 TEST_P(TaskSchedulerTaskTrackerTest, PostAndRunLongTaskBeforeShutdown) { |
| 154 // Post a task that will block until |event| is signaled. |
| 155 EXPECT_TRUE(posted_tasks_.empty()); |
| 156 WaitableEvent event(false, false); |
| 157 PostTaskViaTracker(make_scoped_ptr( |
| 158 new Task(FROM_HERE, Bind(&WaitableEvent::Wait, Unretained(&event)), |
| 159 TaskTraits().WithShutdownBehavior(GetParam())))); |
| 160 EXPECT_EQ(1U, posted_tasks_.size()); |
| 161 |
| 162 // Run the task asynchronouly. |
| 163 ThreadRunningNextPostedTask thread(this); |
| 164 thread.Start(); |
| 165 |
| 166 // Initiate shutdown while the task is running. |
| 167 CallShutdownAsync(); |
| 168 |
| 169 if (GetParam() == TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) { |
| 170 // Shutdown should complete even with a CONTINUE_ON_SHUTDOWN in progress. |
| 171 WaitForAsyncShutdownCompleted(); |
| 172 } else { |
| 173 // Shutdown should block with any non CONTINUE_ON_SHUTDOWN task in progress. |
| 174 VerifyAsyncShutdownInProgress(); |
| 175 } |
| 176 |
| 177 // Unblock the task. |
| 178 event.Signal(); |
| 179 thread.Join(); |
| 180 |
| 181 if (GetParam() != TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) |
| 182 WaitForAsyncShutdownCompleted(); |
| 183 } |
| 184 |
| 185 TEST_P(TaskSchedulerTaskTrackerTest, PostBeforeShutdownRunDuringShutdown) { |
| 186 scoped_ptr<Task> task_to_post(CreateTask(GetParam())); |
| 187 const Task* task_to_post_raw = task_to_post.get(); |
| 188 |
| 189 // Post the task. |
| 190 EXPECT_TRUE(posted_tasks_.empty()); |
| 191 PostTaskViaTracker(std::move(task_to_post)); |
| 192 EXPECT_EQ(1U, posted_tasks_.size()); |
| 193 EXPECT_EQ(task_to_post_raw, posted_tasks_.front().get()); |
| 194 |
| 195 // Post a BLOCK_SHUTDOWN task just to block shutdown. |
| 196 PostTaskViaTracker(CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN)); |
| 197 |
| 198 // Call Shutdown() asynchronously. |
| 199 CallShutdownAsync(); |
| 200 VerifyAsyncShutdownInProgress(); |
| 201 |
| 202 // Try to run task posted at the beginning of this test. Only BLOCK_SHUTDOWN |
| 203 // tasks should run, others should be discarded. |
| 204 EXPECT_EQ(0U, num_tasks_executed_); |
| 205 RunNextPostedTaskViaTracker(); |
| 206 EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 1U : 0U, |
| 207 num_tasks_executed_); |
| 208 VerifyAsyncShutdownInProgress(); |
| 209 |
| 210 // Unblock shutdown by running the remaining BLOCK_SHUTDOWN task. |
| 211 RunNextPostedTaskViaTracker(); |
| 212 EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U, |
| 213 num_tasks_executed_); |
| 214 WaitForAsyncShutdownCompleted(); |
| 215 } |
| 216 |
| 217 TEST_P(TaskSchedulerTaskTrackerTest, PostBeforeShutdownRunAfterShutdown) { |
| 218 scoped_ptr<Task> task_to_post(CreateTask(GetParam())); |
| 219 const Task* task_to_post_raw = task_to_post.get(); |
| 220 |
| 221 // Post the task. |
| 222 EXPECT_TRUE(posted_tasks_.empty()); |
| 223 PostTaskViaTracker(std::move(task_to_post)); |
| 224 EXPECT_EQ(1U, posted_tasks_.size()); |
| 225 EXPECT_EQ(task_to_post_raw, posted_tasks_.front().get()); |
| 226 |
| 227 // Call Shutdown() asynchronously. |
| 228 CallShutdownAsync(); |
| 229 EXPECT_EQ(0U, num_tasks_executed_); |
| 230 |
| 231 if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) { |
| 232 VerifyAsyncShutdownInProgress(); |
| 233 |
| 234 // Run the task to unblock shutdown. |
| 235 RunNextPostedTaskViaTracker(); |
| 236 EXPECT_EQ(1U, num_tasks_executed_); |
| 237 WaitForAsyncShutdownCompleted(); |
| 238 |
| 239 // It is not possible to test running after shutdown a BLOCK_SHUTDOWN task |
| 240 // that has been posted before shutdown because Shutdown() won't return if |
| 241 // there are pending BLOCK_SHUTDOWN tasks. |
| 242 |
| 243 // Make sure that a BLOCK_SHUTDOWN task doesn't run after shutdown (the |
| 244 // task has never been posted). |
| 245 EXPECT_DCHECK_DEATH( |
| 246 { |
| 247 tracker_.RunTask( |
| 248 CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN).get()); |
| 249 }, |
| 250 ""); |
| 251 } else { |
| 252 WaitForAsyncShutdownCompleted(); |
| 253 |
| 254 // The task shouldn't be allowed to run after shutdown. |
| 255 RunNextPostedTaskViaTracker(); |
| 256 EXPECT_EQ(0U, num_tasks_executed_); |
| 257 } |
| 258 } |
| 259 |
| 260 TEST_P(TaskSchedulerTaskTrackerTest, PostAndRunDuringShutdown) { |
| 261 // Post a BLOCK_SHUTDOWN task just to block shutdown. |
| 262 PostTaskViaTracker(CreateTask(TaskShutdownBehavior::BLOCK_SHUTDOWN)); |
| 263 scoped_ptr<Task> block_shutdown_task = std::move(posted_tasks_.front()); |
| 264 posted_tasks_.pop(); |
| 265 |
| 266 // Call Shutdown() asynchronously. |
| 267 CallShutdownAsync(); |
| 268 VerifyAsyncShutdownInProgress(); |
| 269 |
| 270 if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) { |
| 271 // Post a task. This should succeed. |
| 272 EXPECT_TRUE(posted_tasks_.empty()); |
| 273 PostTaskViaTracker(CreateTask(GetParam())); |
| 274 EXPECT_EQ(1U, posted_tasks_.size()); |
| 275 |
| 276 // Run the task. This should succeed. |
| 277 EXPECT_EQ(0U, num_tasks_executed_); |
| 278 RunNextPostedTaskViaTracker(); |
| 279 EXPECT_EQ(1U, num_tasks_executed_); |
| 280 } else { |
| 281 // It shouldn't be possible to post a task which isn't BLOCK_SHUTDOWN. |
| 282 PostTaskViaTracker(CreateTask(GetParam())); |
| 283 EXPECT_TRUE(posted_tasks_.empty()); |
| 284 |
| 285 // Don't try to run the task, because it hasn't been posted successfully. |
| 286 } |
| 287 |
| 288 // Unblock shutdown by running the BLOCK_SHUTDOWN task posted at the beginning |
| 289 // of the test. |
| 290 VerifyAsyncShutdownInProgress(); |
| 291 tracker_.RunTask(block_shutdown_task.get()); |
| 292 EXPECT_EQ(GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? 2U : 1U, |
| 293 num_tasks_executed_); |
| 294 WaitForAsyncShutdownCompleted(); |
| 295 } |
| 296 |
| 297 TEST_P(TaskSchedulerTaskTrackerTest, PostAfterShutdown) { |
| 298 // It is not possible to post a task after shutdown. |
| 299 tracker_.Shutdown(); |
| 300 EXPECT_TRUE(posted_tasks_.empty()); |
| 301 |
| 302 if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) |
| 303 EXPECT_DCHECK_DEATH({ PostTaskViaTracker(CreateTask(GetParam())); }, ""); |
| 304 else |
| 305 PostTaskViaTracker(CreateTask(GetParam())); |
| 306 |
| 307 EXPECT_TRUE(posted_tasks_.empty()); |
| 308 } |
| 309 |
| 310 INSTANTIATE_TEST_CASE_P( |
| 311 ContinueOnShutdown, |
| 312 TaskSchedulerTaskTrackerTest, |
| 313 ::testing::Values(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)); |
| 314 INSTANTIATE_TEST_CASE_P( |
| 315 SkipOnShutdown, |
| 316 TaskSchedulerTaskTrackerTest, |
| 317 ::testing::Values(TaskShutdownBehavior::SKIP_ON_SHUTDOWN)); |
| 318 INSTANTIATE_TEST_CASE_P( |
| 319 BlockShutdown, |
| 320 TaskSchedulerTaskTrackerTest, |
| 321 ::testing::Values(TaskShutdownBehavior::BLOCK_SHUTDOWN)); |
| 322 |
| 323 } // namespace internal |
| 324 } // namespace base |
OLD | NEW |