| 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 "base/callback.h" |
| 8 #include "base/debug/task_annotator.h" |
| 9 #include "base/metrics/histogram_macros.h" |
| 10 |
| 11 namespace base { |
| 12 namespace internal { |
| 13 |
| 14 namespace { |
| 15 const char kQueueFunctionName[] = "base::PostTask"; |
| 16 } // namespace |
| 17 |
| 18 TaskTracker::TaskTracker() = default; |
| 19 TaskTracker::~TaskTracker() = default; |
| 20 |
| 21 void TaskTracker::Shutdown() { |
| 22 size_t num_block_shutdown_tasks_posted_during_shutdown_copy; |
| 23 |
| 24 { |
| 25 AutoSchedulerLock auto_lock(lock_); |
| 26 |
| 27 DCHECK(!shutdown_completed_ && !shutdown_cv_) |
| 28 << "TaskTracker::Shutdown() should only be invoked once."; |
| 29 |
| 30 shutdown_cv_ = lock_.CreateConditionVariable(); |
| 31 |
| 32 // Wait until the number of tasks blocking shutdown is zero. |
| 33 while (num_tasks_blocking_shutdown_ != 0) |
| 34 shutdown_cv_->Wait(); |
| 35 |
| 36 shutdown_cv_.reset(); |
| 37 shutdown_completed_ = true; |
| 38 |
| 39 num_block_shutdown_tasks_posted_during_shutdown_copy = |
| 40 num_block_shutdown_tasks_posted_during_shutdown_; |
| 41 } |
| 42 |
| 43 UMA_HISTOGRAM_COUNTS_100( |
| 44 "TaskScheduler.BlockShutdownTasksPostedDuringShutdown", |
| 45 num_block_shutdown_tasks_posted_during_shutdown_copy); |
| 46 } |
| 47 |
| 48 void TaskTracker::PostTask( |
| 49 const Callback<void(scoped_ptr<Task>)>& post_task_callback, |
| 50 scoped_ptr<Task> task) { |
| 51 DCHECK(!post_task_callback.is_null()); |
| 52 DCHECK(task); |
| 53 |
| 54 if (!BeforePostTask(task->traits.shutdown_behavior())) |
| 55 return; |
| 56 |
| 57 debug::TaskAnnotator task_annotator; |
| 58 task_annotator.DidQueueTask(kQueueFunctionName, *task); |
| 59 |
| 60 post_task_callback.Run(std::move(task)); |
| 61 } |
| 62 |
| 63 void TaskTracker::RunTask(const Task* task) { |
| 64 DCHECK(task); |
| 65 |
| 66 const TaskShutdownBehavior shutdown_behavior = |
| 67 task->traits.shutdown_behavior(); |
| 68 |
| 69 DCHECK(shutdown_behavior != TaskShutdownBehavior::BLOCK_SHUTDOWN || |
| 70 num_tasks_blocking_shutdown_ > 0U); |
| 71 |
| 72 if (!BeforeRunTask(shutdown_behavior)) |
| 73 return; |
| 74 |
| 75 debug::TaskAnnotator task_annotator; |
| 76 task_annotator.RunTask(kQueueFunctionName, *task); |
| 77 |
| 78 AfterRunTask(shutdown_behavior); |
| 79 } |
| 80 |
| 81 bool TaskTracker::IsShuttingDownForTesting() const { |
| 82 AutoSchedulerLock auto_lock(lock_); |
| 83 return !!shutdown_cv_; |
| 84 } |
| 85 |
| 86 bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) { |
| 87 AutoSchedulerLock auto_lock(lock_); |
| 88 |
| 89 if (shutdown_completed_) { |
| 90 // A BLOCK_SHUTDOWN task posted after shutdown has completed is an ordering |
| 91 // bug. This DCHECK aims to catch those early. |
| 92 DCHECK_NE(TaskShutdownBehavior::BLOCK_SHUTDOWN, shutdown_behavior); |
| 93 |
| 94 // No task is allowed to be posted after shutdown. |
| 95 return false; |
| 96 } |
| 97 |
| 98 if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN) { |
| 99 // BLOCK_SHUTDOWN tasks block shutdown between the moment they are posted |
| 100 // and the moment they complete their execution. |
| 101 ++num_tasks_blocking_shutdown_; |
| 102 |
| 103 if (shutdown_cv_) |
| 104 ++num_block_shutdown_tasks_posted_during_shutdown_; |
| 105 |
| 106 // A BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't |
| 107 // completed. |
| 108 return true; |
| 109 } |
| 110 |
| 111 // A non BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't |
| 112 // started. |
| 113 return !shutdown_cv_; |
| 114 } |
| 115 |
| 116 bool TaskTracker::BeforeRunTask(TaskShutdownBehavior shutdown_behavior) { |
| 117 AutoSchedulerLock auto_lock(lock_); |
| 118 |
| 119 if (shutdown_completed_) { |
| 120 // A BLOCK_SHUTDOWN task run after shutdown has completed is an ordering |
| 121 // bug. This DCHECK aims to catch those early. |
| 122 DCHECK_NE(TaskShutdownBehavior::BLOCK_SHUTDOWN, shutdown_behavior); |
| 123 return false; |
| 124 } |
| 125 |
| 126 switch (shutdown_behavior) { |
| 127 case TaskShutdownBehavior::BLOCK_SHUTDOWN: |
| 128 return true; |
| 129 |
| 130 case TaskShutdownBehavior::SKIP_ON_SHUTDOWN: |
| 131 if (shutdown_cv_) |
| 132 return false; |
| 133 |
| 134 // SKIP_ON_SHUTDOWN tasks block shutdown while they are running. |
| 135 ++num_tasks_blocking_shutdown_; |
| 136 return true; |
| 137 |
| 138 case TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN: |
| 139 return !shutdown_cv_; |
| 140 } |
| 141 |
| 142 NOTREACHED(); |
| 143 return false; |
| 144 } |
| 145 |
| 146 void TaskTracker::AfterRunTask(TaskShutdownBehavior shutdown_behavior) { |
| 147 if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN || |
| 148 shutdown_behavior == TaskShutdownBehavior::SKIP_ON_SHUTDOWN) { |
| 149 AutoSchedulerLock auto_lock(lock_); |
| 150 DCHECK_GT(num_tasks_blocking_shutdown_, 0U); |
| 151 --num_tasks_blocking_shutdown_; |
| 152 if (num_tasks_blocking_shutdown_ == 0 && shutdown_cv_) |
| 153 shutdown_cv_->Signal(); |
| 154 } |
| 155 } |
| 156 |
| 157 } // namespace internal |
| 158 } // namespace base |
| OLD | NEW |