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