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 |
| 16 const char kQueueFunctionName[] = "base::PostTask"; |
| 17 |
| 18 // Upper bound for the |
| 19 // TaskScheduler.BlockShutdownTasksPostedDuringShutdown histogram. |
| 20 const HistogramBase::Sample kMaxBlockShutdownTasksPostedDuringShutdown = 1000; |
| 21 |
| 22 void RecordNumBlockShutdownTasksPostedDuringShutdown( |
| 23 HistogramBase::Sample value) { |
| 24 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 25 "TaskScheduler.BlockShutdownTasksPostedDuringShutdown", value, 1, |
| 26 kMaxBlockShutdownTasksPostedDuringShutdown, 50); |
| 27 } |
| 28 |
| 29 } // namespace |
| 30 |
| 31 TaskTracker::TaskTracker() = default; |
| 32 TaskTracker::~TaskTracker() = default; |
| 33 |
| 34 void TaskTracker::Shutdown() { |
| 35 AutoSchedulerLock auto_lock(lock_); |
| 36 |
| 37 // This method should only be called once. |
| 38 DCHECK(!shutdown_completed_); |
| 39 DCHECK(!shutdown_cv_); |
| 40 |
| 41 shutdown_cv_ = lock_.CreateConditionVariable(); |
| 42 |
| 43 // Wait until the number of tasks blocking shutdown is zero. |
| 44 while (num_tasks_blocking_shutdown_ != 0) |
| 45 shutdown_cv_->Wait(); |
| 46 |
| 47 shutdown_cv_.reset(); |
| 48 shutdown_completed_ = true; |
| 49 |
| 50 // Record the TaskScheduler.BlockShutdownTasksPostedDuringShutdown if less |
| 51 // than |kMaxBlockShutdownTasksPostedDuringShutdown| BLOCK_SHUTDOWN tasks were |
| 52 // posted during shutdown. Otherwise, the histogram has already been recorded |
| 53 // in BeforePostTask(). |
| 54 if (num_block_shutdown_tasks_posted_during_shutdown_ < |
| 55 kMaxBlockShutdownTasksPostedDuringShutdown) { |
| 56 RecordNumBlockShutdownTasksPostedDuringShutdown( |
| 57 num_block_shutdown_tasks_posted_during_shutdown_); |
| 58 } |
| 59 } |
| 60 |
| 61 void TaskTracker::PostTask( |
| 62 const Callback<void(scoped_ptr<Task>)>& post_task_callback, |
| 63 scoped_ptr<Task> task) { |
| 64 DCHECK(!post_task_callback.is_null()); |
| 65 DCHECK(task); |
| 66 |
| 67 if (!BeforePostTask(task->traits.shutdown_behavior())) |
| 68 return; |
| 69 |
| 70 debug::TaskAnnotator task_annotator; |
| 71 task_annotator.DidQueueTask(kQueueFunctionName, *task); |
| 72 |
| 73 post_task_callback.Run(std::move(task)); |
| 74 } |
| 75 |
| 76 void TaskTracker::RunTask(const Task* task) { |
| 77 DCHECK(task); |
| 78 |
| 79 const TaskShutdownBehavior shutdown_behavior = |
| 80 task->traits.shutdown_behavior(); |
| 81 if (!BeforeRunTask(shutdown_behavior)) |
| 82 return; |
| 83 |
| 84 debug::TaskAnnotator task_annotator; |
| 85 task_annotator.RunTask(kQueueFunctionName, *task); |
| 86 |
| 87 AfterRunTask(shutdown_behavior); |
| 88 } |
| 89 |
| 90 bool TaskTracker::IsShuttingDownForTesting() const { |
| 91 AutoSchedulerLock auto_lock(lock_); |
| 92 return !!shutdown_cv_; |
| 93 } |
| 94 |
| 95 bool TaskTracker::BeforePostTask(TaskShutdownBehavior shutdown_behavior) { |
| 96 AutoSchedulerLock auto_lock(lock_); |
| 97 |
| 98 if (shutdown_completed_) { |
| 99 // A BLOCK_SHUTDOWN task posted after shutdown has completed is an ordering |
| 100 // bug. This DCHECK aims to catch those early. |
| 101 DCHECK_NE(shutdown_behavior, TaskShutdownBehavior::BLOCK_SHUTDOWN); |
| 102 |
| 103 // No task is allowed to be posted after shutdown. |
| 104 return false; |
| 105 } |
| 106 |
| 107 if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN) { |
| 108 // BLOCK_SHUTDOWN tasks block shutdown between the moment they are posted |
| 109 // and the moment they complete their execution. |
| 110 ++num_tasks_blocking_shutdown_; |
| 111 |
| 112 if (shutdown_cv_) { |
| 113 ++num_block_shutdown_tasks_posted_during_shutdown_; |
| 114 |
| 115 if (num_block_shutdown_tasks_posted_during_shutdown_ == |
| 116 kMaxBlockShutdownTasksPostedDuringShutdown) { |
| 117 // Record the TaskScheduler.BlockShutdownTasksPostedDuringShutdown |
| 118 // histogram as soon as its upper bound is hit. That way, a value will |
| 119 // be recorded even if an infinite number of BLOCK_SHUTDOWN tasks are |
| 120 // posted, preventing shutdown to complete. |
| 121 RecordNumBlockShutdownTasksPostedDuringShutdown( |
| 122 num_block_shutdown_tasks_posted_during_shutdown_); |
| 123 } |
| 124 } |
| 125 |
| 126 // A BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't |
| 127 // completed. |
| 128 return true; |
| 129 } |
| 130 |
| 131 // A non BLOCK_SHUTDOWN task is allowed to be posted iff shutdown hasn't |
| 132 // started. |
| 133 return !shutdown_cv_; |
| 134 } |
| 135 |
| 136 bool TaskTracker::BeforeRunTask(TaskShutdownBehavior shutdown_behavior) { |
| 137 AutoSchedulerLock auto_lock(lock_); |
| 138 |
| 139 if (shutdown_completed_) { |
| 140 // Trying to run a BLOCK_SHUTDOWN task after shutdown has completed is |
| 141 // unexpected as it either shouldn't have been posted if shutdown completed |
| 142 // or should be blocking shutdown if it was posted before it did. |
| 143 DCHECK_NE(shutdown_behavior, TaskShutdownBehavior::BLOCK_SHUTDOWN); |
| 144 |
| 145 // A WorkerThread might extract a non BLOCK_SHUTDOWN task from a |
| 146 // PriorityQueue after shutdown. It shouldn't be allowed to run it. |
| 147 return false; |
| 148 } |
| 149 |
| 150 switch (shutdown_behavior) { |
| 151 case TaskShutdownBehavior::BLOCK_SHUTDOWN: |
| 152 DCHECK_GT(num_tasks_blocking_shutdown_, 0U); |
| 153 return true; |
| 154 |
| 155 case TaskShutdownBehavior::SKIP_ON_SHUTDOWN: |
| 156 if (shutdown_cv_) |
| 157 return false; |
| 158 |
| 159 // SKIP_ON_SHUTDOWN tasks block shutdown while they are running. |
| 160 ++num_tasks_blocking_shutdown_; |
| 161 return true; |
| 162 |
| 163 case TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN: |
| 164 return !shutdown_cv_; |
| 165 } |
| 166 |
| 167 NOTREACHED(); |
| 168 return false; |
| 169 } |
| 170 |
| 171 void TaskTracker::AfterRunTask(TaskShutdownBehavior shutdown_behavior) { |
| 172 if (shutdown_behavior == TaskShutdownBehavior::BLOCK_SHUTDOWN || |
| 173 shutdown_behavior == TaskShutdownBehavior::SKIP_ON_SHUTDOWN) { |
| 174 AutoSchedulerLock auto_lock(lock_); |
| 175 DCHECK_GT(num_tasks_blocking_shutdown_, 0U); |
| 176 --num_tasks_blocking_shutdown_; |
| 177 if (num_tasks_blocking_shutdown_ == 0 && shutdown_cv_) |
| 178 shutdown_cv_->Signal(); |
| 179 } |
| 180 } |
| 181 |
| 182 } // namespace internal |
| 183 } // namespace base |
OLD | NEW |