| OLD | NEW | 
|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "base/test/scoped_task_environment.h" | 5 #include "base/test/scoped_task_environment.h" | 
| 6 | 6 | 
|  | 7 #include "base/bind_helpers.h" | 
|  | 8 #include "base/logging.h" | 
| 7 #include "base/run_loop.h" | 9 #include "base/run_loop.h" | 
| 8 #include "base/task_scheduler/scheduler_worker_pool_params.h" | 10 #include "base/synchronization/condition_variable.h" | 
|  | 11 #include "base/synchronization/lock.h" | 
|  | 12 #include "base/task_scheduler/post_task.h" | 
| 9 #include "base/task_scheduler/task_scheduler.h" | 13 #include "base/task_scheduler/task_scheduler.h" | 
|  | 14 #include "base/task_scheduler/task_scheduler_impl.h" | 
|  | 15 #include "base/threading/thread_task_runner_handle.h" | 
| 10 #include "base/time/time.h" | 16 #include "base/time/time.h" | 
| 11 | 17 | 
| 12 namespace base { | 18 namespace base { | 
| 13 namespace test { | 19 namespace test { | 
| 14 | 20 | 
| 15 namespace { | 21 namespace { | 
| 16 | 22 | 
| 17 class TaskObserver : public MessageLoop::TaskObserver { | 23 class TaskObserver : public MessageLoop::TaskObserver { | 
| 18  public: | 24  public: | 
| 19   TaskObserver() = default; | 25   TaskObserver() = default; | 
| 20 | 26 | 
| 21   // MessageLoop::TaskObserver: | 27   // MessageLoop::TaskObserver: | 
| 22   void WillProcessTask(const PendingTask& pending_task) override {} | 28   void WillProcessTask(const PendingTask& pending_task) override {} | 
| 23   void DidProcessTask(const PendingTask& pending_task) override { | 29   void DidProcessTask(const PendingTask& pending_task) override { | 
| 24     ran_task_ = true; | 30     ++task_count_; | 
| 25   } | 31   } | 
| 26 | 32 | 
| 27   bool ran_task() const { return ran_task_; } | 33   int task_count() const { return task_count_; } | 
| 28 | 34 | 
| 29  private: | 35  private: | 
| 30   bool ran_task_ = false; | 36   int task_count_ = 0; | 
|  | 37 | 
| 31   DISALLOW_COPY_AND_ASSIGN(TaskObserver); | 38   DISALLOW_COPY_AND_ASSIGN(TaskObserver); | 
| 32 }; | 39 }; | 
| 33 | 40 | 
| 34 }  // namespace | 41 }  // namespace | 
| 35 | 42 | 
| 36 ScopedTaskEnvironment::ScopedTaskEnvironment(MainThreadType main_thread_type) | 43 class ScopedTaskEnvironment::TestTaskTracker | 
| 37     : message_loop_(main_thread_type == MainThreadType::DEFAULT | 44     : public internal::TaskSchedulerImpl::TaskTrackerImpl { | 
|  | 45  public: | 
|  | 46   TestTaskTracker(); | 
|  | 47 | 
|  | 48   void RegisterOnQueueEmptyClosure(OnceClosure queue_empty_closure); | 
|  | 49   void AssertOnQueueEmptyClosureIsNull(); | 
|  | 50 | 
|  | 51   // Allow running tasks. | 
|  | 52   void AllowRunRask(); | 
|  | 53 | 
|  | 54   // Disallow running tasks. No-ops and returns false if a task is running. | 
|  | 55   bool DisallowRunTasks(); | 
|  | 56 | 
|  | 57  private: | 
|  | 58   friend class ScopedTaskEnvironment; | 
|  | 59 | 
|  | 60   // internal::TaskSchedulerImpl::TaskTrackerImpl: | 
|  | 61   void PerformRunTask(std::unique_ptr<internal::Task> task, | 
|  | 62                       const SequenceToken& sequence_token) override; | 
|  | 63 | 
|  | 64   // Synchronizes accesses to members below. | 
|  | 65   Lock lock_; | 
|  | 66 | 
|  | 67   // Closure posted to the main thread when the task queue becomes empty. | 
|  | 68   OnceClosure queue_empty_closure_; | 
|  | 69 | 
|  | 70   // True if running tasks is allowed. | 
|  | 71   bool can_run_tasks_ = true; | 
|  | 72 | 
|  | 73   // Signaled when |can_run_tasks_| becomes true. | 
|  | 74   ConditionVariable can_run_tasks_cv_; | 
|  | 75 | 
|  | 76   // Number of tasks that are currently running. | 
|  | 77   int num_tasks_running_ = 0; | 
|  | 78 | 
|  | 79   DISALLOW_COPY_AND_ASSIGN(TestTaskTracker); | 
|  | 80 }; | 
|  | 81 | 
|  | 82 ScopedTaskEnvironment::ScopedTaskEnvironment( | 
|  | 83     MainThreadType main_thread_type, | 
|  | 84     ExecutionMode execution_control_mode) | 
|  | 85     : execution_control_mode_(execution_control_mode), | 
|  | 86       message_loop_(main_thread_type == MainThreadType::DEFAULT | 
| 38                         ? MessageLoop::TYPE_DEFAULT | 87                         ? MessageLoop::TYPE_DEFAULT | 
| 39                         : (main_thread_type == MainThreadType::UI | 88                         : (main_thread_type == MainThreadType::UI | 
| 40                                ? MessageLoop::TYPE_UI | 89                                ? MessageLoop::TYPE_UI | 
| 41                                : MessageLoop::TYPE_IO)) { | 90                                : MessageLoop::TYPE_IO)), | 
| 42   DCHECK(!TaskScheduler::GetInstance()); | 91       task_tracker_(new TestTaskTracker()) { | 
|  | 92   CHECK(!TaskScheduler::GetInstance()); | 
| 43 | 93 | 
| 44   // Instantiate a TaskScheduler with 1 thread in each of its 4 pools. Threads | 94   // Instantiate a TaskScheduler with 1 thread in each of its 4 pools. Threads | 
| 45   // stay alive even when they don't have work. | 95   // stay alive even when they don't have work. | 
| 46   constexpr int kMaxThreads = 1; | 96   constexpr int kMaxThreads = 1; | 
| 47   const TimeDelta kSuggestedReclaimTime = TimeDelta::Max(); | 97   const TimeDelta kSuggestedReclaimTime = TimeDelta::Max(); | 
| 48   const SchedulerWorkerPoolParams worker_pool_params( | 98   const SchedulerWorkerPoolParams worker_pool_params( | 
| 49       SchedulerWorkerPoolParams::StandbyThreadPolicy::ONE, kMaxThreads, | 99       SchedulerWorkerPoolParams::StandbyThreadPolicy::ONE, kMaxThreads, | 
| 50       kSuggestedReclaimTime); | 100       kSuggestedReclaimTime); | 
| 51   TaskScheduler::Create("ScopedTaskEnvironment"); | 101   TaskScheduler::SetInstance(MakeUnique<internal::TaskSchedulerImpl>( | 
|  | 102       "ScopedTaskEnvironment", WrapUnique(task_tracker_))); | 
| 52   task_scheduler_ = TaskScheduler::GetInstance(); | 103   task_scheduler_ = TaskScheduler::GetInstance(); | 
| 53   TaskScheduler::GetInstance()->Start({worker_pool_params, worker_pool_params, | 104   TaskScheduler::GetInstance()->Start({worker_pool_params, worker_pool_params, | 
| 54                                        worker_pool_params, worker_pool_params}); | 105                                        worker_pool_params, worker_pool_params}); | 
|  | 106 | 
|  | 107   if (execution_control_mode_ == ExecutionMode::QUEUED) | 
|  | 108     CHECK(task_tracker_->DisallowRunTasks()); | 
| 55 } | 109 } | 
| 56 | 110 | 
| 57 ScopedTaskEnvironment::~ScopedTaskEnvironment() { | 111 ScopedTaskEnvironment::~ScopedTaskEnvironment() { | 
| 58   // Ideally this would RunLoop().RunUntilIdle() here to catch any errors or | 112   // Ideally this would RunLoop().RunUntilIdle() here to catch any errors or | 
| 59   // infinite post loop in the remaining work but this isn't possible right now | 113   // infinite post loop in the remaining work but this isn't possible right now | 
| 60   // because base::~MessageLoop() didn't use to do this and adding it here would | 114   // because base::~MessageLoop() didn't use to do this and adding it here would | 
| 61   // make the migration away from MessageLoop that much harder. | 115   // make the migration away from MessageLoop that much harder. | 
| 62 | 116   CHECK_EQ(TaskScheduler::GetInstance(), task_scheduler_); | 
| 63   DCHECK_EQ(TaskScheduler::GetInstance(), task_scheduler_); |  | 
| 64   // Without FlushForTesting(), DeleteSoon() and ReleaseSoon() tasks could be | 117   // Without FlushForTesting(), DeleteSoon() and ReleaseSoon() tasks could be | 
| 65   // skipped, resulting in memory leaks. | 118   // skipped, resulting in memory leaks. | 
|  | 119   task_tracker_->AllowRunRask(); | 
| 66   TaskScheduler::GetInstance()->FlushForTesting(); | 120   TaskScheduler::GetInstance()->FlushForTesting(); | 
| 67   TaskScheduler::GetInstance()->Shutdown(); | 121   TaskScheduler::GetInstance()->Shutdown(); | 
| 68   TaskScheduler::GetInstance()->JoinForTesting(); | 122   TaskScheduler::GetInstance()->JoinForTesting(); | 
| 69   TaskScheduler::SetInstance(nullptr); | 123   TaskScheduler::SetInstance(nullptr); | 
| 70 } | 124 } | 
| 71 | 125 | 
| 72 scoped_refptr<base::SingleThreadTaskRunner> | 126 scoped_refptr<base::SingleThreadTaskRunner> | 
| 73 ScopedTaskEnvironment::GetMainThreadTaskRunner() { | 127 ScopedTaskEnvironment::GetMainThreadTaskRunner() { | 
| 74   return message_loop_.task_runner(); | 128   return message_loop_.task_runner(); | 
| 75 } | 129 } | 
| 76 | 130 | 
| 77 void ScopedTaskEnvironment::RunUntilIdle() { | 131 void ScopedTaskEnvironment::RunUntilIdle() { | 
| 78   for (;;) { | 132   for (;;) { | 
| 79     TaskScheduler::GetInstance()->FlushForTesting(); | 133     RunLoop run_loop; | 
| 80 | 134 | 
|  | 135     // Register a closure to stop running tasks on the main thread when the | 
|  | 136     // TaskScheduler queue becomes empty. | 
|  | 137     task_tracker_->RegisterOnQueueEmptyClosure(run_loop.QuitWhenIdleClosure()); | 
|  | 138 | 
|  | 139     // The closure registered above may never run if the TaskScheduler queue | 
|  | 140     // starts empty. Post a TaskScheduler tasks to make sure that the queue | 
|  | 141     // doesn't start empty. | 
|  | 142     PostTask(FROM_HERE, BindOnce(&DoNothing)); | 
|  | 143 | 
|  | 144     // Run main thread and TaskScheduler tasks. | 
|  | 145     task_tracker_->AllowRunRask(); | 
| 81     TaskObserver task_observer; | 146     TaskObserver task_observer; | 
| 82     MessageLoop::current()->AddTaskObserver(&task_observer); | 147     MessageLoop::current()->AddTaskObserver(&task_observer); | 
| 83     RunLoop().RunUntilIdle(); | 148     run_loop.Run(); | 
| 84     MessageLoop::current()->RemoveTaskObserver(&task_observer); | 149     MessageLoop::current()->RemoveTaskObserver(&task_observer); | 
| 85 | 150 | 
| 86     if (!task_observer.ran_task()) | 151     task_tracker_->AssertOnQueueEmptyClosureIsNull(); | 
| 87       return; | 152 | 
|  | 153     // If tasks other than the QuitWhenIdle closure ran on the main thread, they | 
|  | 154     // may have posted TaskScheduler tasks that didn't run yet. Another | 
|  | 155     // iteration is required to run them. | 
|  | 156     // | 
|  | 157     // If the ExecutionMode is QUEUED and DisallowRunTasks() fails, | 
|  | 158     // another iteration is required to make sure that RunUntilIdle() doesn't | 
|  | 159     // return while TaskScheduler tasks are still allowed to run. | 
|  | 160     // | 
|  | 161     // Note: DisallowRunTasks() fails when a TaskScheduler task is running. A | 
|  | 162     // TaskScheduler task may be running after the TaskScheduler queue became | 
|  | 163     // empty even if no tasks ran on the main thread in these cases: | 
|  | 164     // - An undelayed task became ripe for execution. | 
|  | 165     // - A task was posted from an external thread. | 
|  | 166     if (task_observer.task_count() == 1 && | 
|  | 167         (execution_control_mode_ != ExecutionMode::QUEUED || | 
|  | 168          task_tracker_->DisallowRunTasks())) { | 
|  | 169       break; | 
|  | 170     } | 
| 88   } | 171   } | 
| 89 } | 172 } | 
| 90 | 173 | 
|  | 174 ScopedTaskEnvironment::TestTaskTracker::TestTaskTracker() | 
|  | 175     : can_run_tasks_cv_(&lock_) {} | 
|  | 176 | 
|  | 177 void ScopedTaskEnvironment::TestTaskTracker::RegisterOnQueueEmptyClosure( | 
|  | 178     OnceClosure queue_empty_closure) { | 
|  | 179   AutoLock auto_lock(lock_); | 
|  | 180   CHECK(!queue_empty_closure_); | 
|  | 181   queue_empty_closure_ = std::move(queue_empty_closure); | 
|  | 182 } | 
|  | 183 | 
|  | 184 void ScopedTaskEnvironment::TestTaskTracker::AssertOnQueueEmptyClosureIsNull() { | 
|  | 185   AutoLock auto_lock(lock_); | 
|  | 186   CHECK(!queue_empty_closure_); | 
|  | 187 } | 
|  | 188 | 
|  | 189 void ScopedTaskEnvironment::TestTaskTracker::AllowRunRask() { | 
|  | 190   AutoLock auto_lock(lock_); | 
|  | 191   can_run_tasks_ = true; | 
|  | 192   can_run_tasks_cv_.Broadcast(); | 
|  | 193 } | 
|  | 194 | 
|  | 195 bool ScopedTaskEnvironment::TestTaskTracker::DisallowRunTasks() { | 
|  | 196   AutoLock auto_lock(lock_); | 
|  | 197 | 
|  | 198   // Can't disallow run task if there are tasks running. | 
|  | 199   if (num_tasks_running_ > 0) | 
|  | 200     return false; | 
|  | 201 | 
|  | 202   can_run_tasks_ = false; | 
|  | 203   return true; | 
|  | 204 } | 
|  | 205 | 
|  | 206 void ScopedTaskEnvironment::TestTaskTracker::PerformRunTask( | 
|  | 207     std::unique_ptr<internal::Task> task, | 
|  | 208     const SequenceToken& sequence_token) { | 
|  | 209   { | 
|  | 210     AutoLock auto_lock(lock_); | 
|  | 211 | 
|  | 212     while (!can_run_tasks_) | 
|  | 213       can_run_tasks_cv_.Wait(); | 
|  | 214 | 
|  | 215     ++num_tasks_running_; | 
|  | 216   } | 
|  | 217 | 
|  | 218   internal::TaskSchedulerImpl::TaskTrackerImpl::PerformRunTask(std::move(task), | 
|  | 219                                                                sequence_token); | 
|  | 220 | 
|  | 221   { | 
|  | 222     AutoLock auto_lock(lock_); | 
|  | 223 | 
|  | 224     CHECK_GT(num_tasks_running_, 0); | 
|  | 225     CHECK(can_run_tasks_); | 
|  | 226 | 
|  | 227     // Notify the main thread when no task other than the current one is running | 
|  | 228     // or queued. | 
|  | 229     if (num_tasks_running_ == 1 && | 
|  | 230         GetNumPendingUndelayedTasksForTesting() == 1 && queue_empty_closure_) { | 
|  | 231       std::move(queue_empty_closure_).Run(); | 
|  | 232     } | 
|  | 233 | 
|  | 234     --num_tasks_running_; | 
|  | 235   } | 
|  | 236 } | 
|  | 237 | 
| 91 }  // namespace test | 238 }  // namespace test | 
| 92 }  // namespace base | 239 }  // namespace base | 
| OLD | NEW | 
|---|