| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 "components/scheduler/base/task_queue_selector.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "base/trace_event/trace_event_argument.h" | |
| 9 #include "components/scheduler/base/task_queue_impl.h" | |
| 10 #include "components/scheduler/base/work_queue.h" | |
| 11 | |
| 12 namespace scheduler { | |
| 13 namespace internal { | |
| 14 | |
| 15 TaskQueueSelector::TaskQueueSelector() | |
| 16 : enabled_selector_(this, "enabled"), | |
| 17 blocked_selector_(this, "blocked"), | |
| 18 immediate_starvation_count_(0), | |
| 19 high_priority_starvation_count_(0), | |
| 20 num_blocked_queues_to_report_(0), | |
| 21 task_queue_selector_observer_(nullptr) {} | |
| 22 | |
| 23 TaskQueueSelector::~TaskQueueSelector() {} | |
| 24 | |
| 25 void TaskQueueSelector::AddQueue(internal::TaskQueueImpl* queue) { | |
| 26 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 27 DCHECK(queue->IsQueueEnabled()); | |
| 28 enabled_selector_.AddQueue(queue, TaskQueue::NORMAL_PRIORITY); | |
| 29 } | |
| 30 | |
| 31 void TaskQueueSelector::RemoveQueue(internal::TaskQueueImpl* queue) { | |
| 32 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 33 if (queue->IsQueueEnabled()) { | |
| 34 enabled_selector_.RemoveQueue(queue); | |
| 35 // The #if DCHECK_IS_ON() shouldn't be necessary but this doesn't compile on | |
| 36 // chromeos bots without it :( | |
| 37 #if DCHECK_IS_ON() | |
| 38 DCHECK(!blocked_selector_.CheckContainsQueueForTest(queue)); | |
| 39 #endif | |
| 40 } else if (queue->should_report_when_execution_blocked()) { | |
| 41 DCHECK_GT(num_blocked_queues_to_report_, 0u); | |
| 42 num_blocked_queues_to_report_--; | |
| 43 blocked_selector_.RemoveQueue(queue); | |
| 44 #if DCHECK_IS_ON() | |
| 45 DCHECK(!enabled_selector_.CheckContainsQueueForTest(queue)); | |
| 46 #endif | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 void TaskQueueSelector::EnableQueue(internal::TaskQueueImpl* queue) { | |
| 51 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 52 DCHECK(queue->IsQueueEnabled()); | |
| 53 if (queue->should_report_when_execution_blocked()) { | |
| 54 DCHECK_GT(num_blocked_queues_to_report_, 0u); | |
| 55 num_blocked_queues_to_report_--; | |
| 56 blocked_selector_.RemoveQueue(queue); | |
| 57 } | |
| 58 enabled_selector_.AddQueue(queue, queue->GetQueuePriority()); | |
| 59 if (task_queue_selector_observer_) | |
| 60 task_queue_selector_observer_->OnTaskQueueEnabled(queue); | |
| 61 } | |
| 62 | |
| 63 void TaskQueueSelector::DisableQueue(internal::TaskQueueImpl* queue) { | |
| 64 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 65 DCHECK(!queue->IsQueueEnabled()); | |
| 66 enabled_selector_.RemoveQueue(queue); | |
| 67 if (queue->should_report_when_execution_blocked()) { | |
| 68 blocked_selector_.AddQueue(queue, queue->GetQueuePriority()); | |
| 69 num_blocked_queues_to_report_++; | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 void TaskQueueSelector::SetQueuePriority(internal::TaskQueueImpl* queue, | |
| 74 TaskQueue::QueuePriority priority) { | |
| 75 DCHECK_LT(priority, TaskQueue::QUEUE_PRIORITY_COUNT); | |
| 76 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 77 if (queue->IsQueueEnabled()) { | |
| 78 enabled_selector_.ChangeSetIndex(queue, priority); | |
| 79 } else if (queue->should_report_when_execution_blocked()) { | |
| 80 blocked_selector_.ChangeSetIndex(queue, priority); | |
| 81 } else { | |
| 82 // Normally blocked_selector_.ChangeSetIndex would assign the queue's | |
| 83 // priority, however if |queue->should_report_when_execution_blocked()| is | |
| 84 // false then the disabled queue is not in any set so we need to do it here. | |
| 85 queue->delayed_work_queue()->AssignSetIndex(priority); | |
| 86 queue->immediate_work_queue()->AssignSetIndex(priority); | |
| 87 } | |
| 88 DCHECK_EQ(priority, queue->GetQueuePriority()); | |
| 89 } | |
| 90 | |
| 91 TaskQueue::QueuePriority TaskQueueSelector::NextPriority( | |
| 92 TaskQueue::QueuePriority priority) { | |
| 93 DCHECK(priority < TaskQueue::QUEUE_PRIORITY_COUNT); | |
| 94 return static_cast<TaskQueue::QueuePriority>(static_cast<int>(priority) + 1); | |
| 95 } | |
| 96 | |
| 97 TaskQueueSelector::PrioritizingSelector::PrioritizingSelector( | |
| 98 TaskQueueSelector* task_queue_selector, | |
| 99 const char* name) | |
| 100 : task_queue_selector_(task_queue_selector), | |
| 101 delayed_work_queue_sets_(TaskQueue::QUEUE_PRIORITY_COUNT, name), | |
| 102 immediate_work_queue_sets_(TaskQueue::QUEUE_PRIORITY_COUNT, name) {} | |
| 103 | |
| 104 void TaskQueueSelector::PrioritizingSelector::AddQueue( | |
| 105 internal::TaskQueueImpl* queue, | |
| 106 TaskQueue::QueuePriority priority) { | |
| 107 #if DCHECK_IS_ON() | |
| 108 DCHECK(!CheckContainsQueueForTest(queue)); | |
| 109 #endif | |
| 110 delayed_work_queue_sets_.AddQueue(queue->delayed_work_queue(), priority); | |
| 111 immediate_work_queue_sets_.AddQueue(queue->immediate_work_queue(), priority); | |
| 112 #if DCHECK_IS_ON() | |
| 113 DCHECK(CheckContainsQueueForTest(queue)); | |
| 114 #endif | |
| 115 } | |
| 116 | |
| 117 void TaskQueueSelector::PrioritizingSelector::ChangeSetIndex( | |
| 118 internal::TaskQueueImpl* queue, | |
| 119 TaskQueue::QueuePriority priority) { | |
| 120 #if DCHECK_IS_ON() | |
| 121 DCHECK(CheckContainsQueueForTest(queue)); | |
| 122 #endif | |
| 123 delayed_work_queue_sets_.ChangeSetIndex(queue->delayed_work_queue(), | |
| 124 priority); | |
| 125 immediate_work_queue_sets_.ChangeSetIndex(queue->immediate_work_queue(), | |
| 126 priority); | |
| 127 #if DCHECK_IS_ON() | |
| 128 DCHECK(CheckContainsQueueForTest(queue)); | |
| 129 #endif | |
| 130 } | |
| 131 | |
| 132 void TaskQueueSelector::PrioritizingSelector::RemoveQueue( | |
| 133 internal::TaskQueueImpl* queue) { | |
| 134 #if DCHECK_IS_ON() | |
| 135 DCHECK(CheckContainsQueueForTest(queue)); | |
| 136 #endif | |
| 137 delayed_work_queue_sets_.RemoveQueue(queue->delayed_work_queue()); | |
| 138 immediate_work_queue_sets_.RemoveQueue(queue->immediate_work_queue()); | |
| 139 | |
| 140 #if DCHECK_IS_ON() | |
| 141 DCHECK(!CheckContainsQueueForTest(queue)); | |
| 142 #endif | |
| 143 } | |
| 144 | |
| 145 bool TaskQueueSelector::PrioritizingSelector:: | |
| 146 ChooseOldestImmediateTaskWithPriority(TaskQueue::QueuePriority priority, | |
| 147 WorkQueue** out_work_queue) const { | |
| 148 return immediate_work_queue_sets_.GetOldestQueueInSet(priority, | |
| 149 out_work_queue); | |
| 150 } | |
| 151 | |
| 152 bool TaskQueueSelector::PrioritizingSelector:: | |
| 153 ChooseOldestDelayedTaskWithPriority(TaskQueue::QueuePriority priority, | |
| 154 WorkQueue** out_work_queue) const { | |
| 155 return delayed_work_queue_sets_.GetOldestQueueInSet(priority, out_work_queue); | |
| 156 } | |
| 157 | |
| 158 bool TaskQueueSelector::PrioritizingSelector:: | |
| 159 ChooseOldestImmediateOrDelayedTaskWithPriority( | |
| 160 TaskQueue::QueuePriority priority, | |
| 161 bool* out_chose_delayed_over_immediate, | |
| 162 WorkQueue** out_work_queue) const { | |
| 163 WorkQueue* immediate_queue; | |
| 164 DCHECK_EQ(*out_chose_delayed_over_immediate, false); | |
| 165 if (immediate_work_queue_sets_.GetOldestQueueInSet(priority, | |
| 166 &immediate_queue)) { | |
| 167 WorkQueue* delayed_queue; | |
| 168 if (delayed_work_queue_sets_.GetOldestQueueInSet(priority, | |
| 169 &delayed_queue)) { | |
| 170 if (immediate_queue->ShouldRunBefore(delayed_queue)) { | |
| 171 *out_work_queue = immediate_queue; | |
| 172 } else { | |
| 173 *out_chose_delayed_over_immediate = true; | |
| 174 *out_work_queue = delayed_queue; | |
| 175 } | |
| 176 } else { | |
| 177 *out_work_queue = immediate_queue; | |
| 178 } | |
| 179 return true; | |
| 180 } | |
| 181 return delayed_work_queue_sets_.GetOldestQueueInSet(priority, out_work_queue); | |
| 182 } | |
| 183 | |
| 184 bool TaskQueueSelector::PrioritizingSelector::ChooseOldestWithPriority( | |
| 185 TaskQueue::QueuePriority priority, | |
| 186 bool* out_chose_delayed_over_immediate, | |
| 187 WorkQueue** out_work_queue) const { | |
| 188 // Select an immediate work queue if we are starving immediate tasks. | |
| 189 if (task_queue_selector_->immediate_starvation_count_ >= | |
| 190 kMaxDelayedStarvationTasks) { | |
| 191 if (ChooseOldestImmediateTaskWithPriority(priority, out_work_queue)) { | |
| 192 return true; | |
| 193 } | |
| 194 if (ChooseOldestDelayedTaskWithPriority(priority, out_work_queue)) { | |
| 195 return true; | |
| 196 } | |
| 197 return false; | |
| 198 } | |
| 199 return ChooseOldestImmediateOrDelayedTaskWithPriority( | |
| 200 priority, out_chose_delayed_over_immediate, out_work_queue); | |
| 201 } | |
| 202 | |
| 203 bool TaskQueueSelector::PrioritizingSelector::SelectWorkQueueToService( | |
| 204 TaskQueue::QueuePriority max_priority, | |
| 205 WorkQueue** out_work_queue, | |
| 206 bool* out_chose_delayed_over_immediate) { | |
| 207 DCHECK(task_queue_selector_->main_thread_checker_.CalledOnValidThread()); | |
| 208 DCHECK_EQ(*out_chose_delayed_over_immediate, false); | |
| 209 | |
| 210 // Always service the control queue if it has any work. | |
| 211 if (max_priority > TaskQueue::CONTROL_PRIORITY && | |
| 212 ChooseOldestWithPriority(TaskQueue::CONTROL_PRIORITY, | |
| 213 out_chose_delayed_over_immediate, | |
| 214 out_work_queue)) { | |
| 215 return true; | |
| 216 } | |
| 217 | |
| 218 // Select from the normal priority queue if we are starving it. | |
| 219 if (max_priority > TaskQueue::NORMAL_PRIORITY && | |
| 220 task_queue_selector_->high_priority_starvation_count_ >= | |
| 221 kMaxHighPriorityStarvationTasks && | |
| 222 ChooseOldestWithPriority(TaskQueue::NORMAL_PRIORITY, | |
| 223 out_chose_delayed_over_immediate, | |
| 224 out_work_queue)) { | |
| 225 return true; | |
| 226 } | |
| 227 // Otherwise choose in priority order. | |
| 228 for (TaskQueue::QueuePriority priority = TaskQueue::HIGH_PRIORITY; | |
| 229 priority < max_priority; priority = NextPriority(priority)) { | |
| 230 if (ChooseOldestWithPriority(priority, out_chose_delayed_over_immediate, | |
| 231 out_work_queue)) { | |
| 232 return true; | |
| 233 } | |
| 234 } | |
| 235 return false; | |
| 236 } | |
| 237 | |
| 238 #if DCHECK_IS_ON() || !defined(NDEBUG) | |
| 239 bool | |
| 240 TaskQueueSelector::PrioritizingSelector::CheckContainsQueueForTest( | |
| 241 const internal::TaskQueueImpl* queue) const { | |
| 242 bool contains_delayed_work_queue = | |
| 243 delayed_work_queue_sets_.ContainsWorkQueueForTest( | |
| 244 queue->delayed_work_queue()); | |
| 245 | |
| 246 bool contains_immediate_work_queue = | |
| 247 immediate_work_queue_sets_.ContainsWorkQueueForTest( | |
| 248 queue->immediate_work_queue()); | |
| 249 | |
| 250 DCHECK_EQ(contains_delayed_work_queue, contains_immediate_work_queue); | |
| 251 return contains_delayed_work_queue; | |
| 252 } | |
| 253 #endif | |
| 254 | |
| 255 bool TaskQueueSelector::SelectWorkQueueToService(WorkQueue** out_work_queue) { | |
| 256 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 257 bool chose_delayed_over_immediate = false; | |
| 258 bool found_queue = enabled_selector_.SelectWorkQueueToService( | |
| 259 TaskQueue::QUEUE_PRIORITY_COUNT, out_work_queue, | |
| 260 &chose_delayed_over_immediate); | |
| 261 if (!found_queue) { | |
| 262 TrySelectingBlockedQueue(); | |
| 263 return false; | |
| 264 } | |
| 265 | |
| 266 TrySelectingBlockedQueueOverEnabledQueue(**out_work_queue); | |
| 267 DidSelectQueueWithPriority( | |
| 268 (*out_work_queue)->task_queue()->GetQueuePriority(), | |
| 269 chose_delayed_over_immediate); | |
| 270 return true; | |
| 271 } | |
| 272 | |
| 273 void TaskQueueSelector::TrySelectingBlockedQueue() { | |
| 274 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 275 if (!num_blocked_queues_to_report_ || !task_queue_selector_observer_) | |
| 276 return; | |
| 277 WorkQueue* chosen_blocked_queue; | |
| 278 bool chose_delayed_over_immediate = false; | |
| 279 // There was nothing unblocked to run, see if we could have run a blocked | |
| 280 // task. | |
| 281 if (blocked_selector_.SelectWorkQueueToService( | |
| 282 TaskQueue::QUEUE_PRIORITY_COUNT, &chosen_blocked_queue, | |
| 283 &chose_delayed_over_immediate)) { | |
| 284 task_queue_selector_observer_->OnTriedToSelectBlockedWorkQueue( | |
| 285 chosen_blocked_queue); | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 void TaskQueueSelector::TrySelectingBlockedQueueOverEnabledQueue( | |
| 290 const WorkQueue& chosen_enabled_queue) { | |
| 291 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 292 if (!num_blocked_queues_to_report_ || !task_queue_selector_observer_) | |
| 293 return; | |
| 294 | |
| 295 TaskQueue::QueuePriority max_priority = | |
| 296 NextPriority(chosen_enabled_queue.task_queue()->GetQueuePriority()); | |
| 297 | |
| 298 WorkQueue* chosen_blocked_queue; | |
| 299 bool chose_delayed_over_immediate = false; | |
| 300 bool found_queue = blocked_selector_.SelectWorkQueueToService( | |
| 301 max_priority, &chosen_blocked_queue, &chose_delayed_over_immediate); | |
| 302 if (!found_queue) | |
| 303 return; | |
| 304 | |
| 305 // Check if the chosen blocked queue has a lower numerical priority than the | |
| 306 // chosen enabled queue. If so we would have chosen the blocked queue (since | |
| 307 // zero is the highest priority). | |
| 308 if (chosen_blocked_queue->task_queue()->GetQueuePriority() < | |
| 309 chosen_enabled_queue.task_queue()->GetQueuePriority()) { | |
| 310 task_queue_selector_observer_->OnTriedToSelectBlockedWorkQueue( | |
| 311 chosen_blocked_queue); | |
| 312 return; | |
| 313 } | |
| 314 DCHECK_EQ(chosen_blocked_queue->task_queue()->GetQueuePriority(), | |
| 315 chosen_enabled_queue.task_queue()->GetQueuePriority()); | |
| 316 // Otherwise there was an enabled and a blocked task with the same priority. | |
| 317 // The one with the older enqueue order wins. | |
| 318 if (chosen_blocked_queue->ShouldRunBefore(&chosen_enabled_queue)) { | |
| 319 task_queue_selector_observer_->OnTriedToSelectBlockedWorkQueue( | |
| 320 chosen_blocked_queue); | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 void TaskQueueSelector::DidSelectQueueWithPriority( | |
| 325 TaskQueue::QueuePriority priority, | |
| 326 bool chose_delayed_over_immediate) { | |
| 327 switch (priority) { | |
| 328 case TaskQueue::CONTROL_PRIORITY: | |
| 329 break; | |
| 330 case TaskQueue::HIGH_PRIORITY: | |
| 331 high_priority_starvation_count_++; | |
| 332 break; | |
| 333 case TaskQueue::NORMAL_PRIORITY: | |
| 334 case TaskQueue::BEST_EFFORT_PRIORITY: | |
| 335 high_priority_starvation_count_ = 0; | |
| 336 break; | |
| 337 default: | |
| 338 NOTREACHED(); | |
| 339 } | |
| 340 if (chose_delayed_over_immediate) { | |
| 341 immediate_starvation_count_++; | |
| 342 } else { | |
| 343 immediate_starvation_count_ = 0; | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 void TaskQueueSelector::AsValueInto( | |
| 348 base::trace_event::TracedValue* state) const { | |
| 349 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 350 state->SetInteger("high_priority_starvation_count", | |
| 351 high_priority_starvation_count_); | |
| 352 state->SetInteger("immediate_starvation_count", immediate_starvation_count_); | |
| 353 state->SetInteger("num_blocked_queues_to_report", | |
| 354 num_blocked_queues_to_report_); | |
| 355 } | |
| 356 | |
| 357 void TaskQueueSelector::SetTaskQueueSelectorObserver(Observer* observer) { | |
| 358 task_queue_selector_observer_ = observer; | |
| 359 } | |
| 360 | |
| 361 bool TaskQueueSelector::EnabledWorkQueuesEmpty() const { | |
| 362 DCHECK(main_thread_checker_.CalledOnValidThread()); | |
| 363 for (TaskQueue::QueuePriority priority = TaskQueue::CONTROL_PRIORITY; | |
| 364 priority < TaskQueue::QUEUE_PRIORITY_COUNT; | |
| 365 priority = NextPriority(priority)) { | |
| 366 if (!enabled_selector_.delayed_work_queue_sets()->IsSetEmpty(priority) || | |
| 367 !enabled_selector_.immediate_work_queue_sets()->IsSetEmpty(priority)) { | |
| 368 return false; | |
| 369 } | |
| 370 } | |
| 371 return true; | |
| 372 } | |
| 373 | |
| 374 void TaskQueueSelector::SetImmediateStarvationCountForTest( | |
| 375 size_t immediate_starvation_count) { | |
| 376 immediate_starvation_count_ = immediate_starvation_count; | |
| 377 } | |
| 378 | |
| 379 } // namespace internal | |
| 380 } // namespace scheduler | |
| OLD | NEW |