Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 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 "platform/scheduler/renderer/task_queue_throttler.h" | |
| 6 | |
| 7 #include <cstdint> | |
| 8 | |
| 9 #include "base/format_macros.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/memory/ptr_util.h" | |
| 12 #include "base/optional.h" | |
| 13 #include "base/strings/stringprintf.h" | |
| 14 #include "platform/scheduler/base/real_time_domain.h" | |
| 15 #include "platform/scheduler/child/scheduler_tqm_delegate.h" | |
| 16 #include "platform/scheduler/renderer/auto_advancing_virtual_time_domain.h" | |
| 17 #include "platform/scheduler/renderer/renderer_scheduler_impl.h" | |
| 18 #include "platform/scheduler/renderer/throttled_time_domain.h" | |
| 19 #include "platform/scheduler/renderer/web_frame_scheduler_impl.h" | |
| 20 #include "public/platform/WebFrameScheduler.h" | |
| 21 | |
| 22 namespace blink { | |
| 23 namespace scheduler { | |
| 24 | |
| 25 namespace { | |
| 26 const int kMaxBudgetLevelInSeconds = 1; | |
| 27 | |
| 28 base::Optional<base::TimeTicks> NextTaskRunTime(LazyNow* lazy_now, | |
| 29 TaskQueue* queue) { | |
| 30 if (queue->HasPendingImmediateWork()) | |
| 31 return lazy_now->Now(); | |
| 32 return queue->GetNextScheduledWakeUp(); | |
| 33 } | |
| 34 } | |
|
Sami
2016/09/22 12:34:07
} // namespace
altimin
2016/09/22 13:45:59
Done.
| |
| 35 | |
| 36 TaskQueueThrottler::TimeBudgetPool::TimeBudgetPool( | |
| 37 const char* name, | |
| 38 TaskQueueThrottler* task_queue_throttler, | |
| 39 base::TimeTicks now) | |
| 40 : name_(name), | |
| 41 task_queue_throttler_(task_queue_throttler), | |
| 42 max_budget_level_(base::TimeDelta::FromSeconds(kMaxBudgetLevelInSeconds)), | |
| 43 last_checkpoint_(now), | |
| 44 cpu_percentage_(1), | |
| 45 is_enabled_(true) {} | |
| 46 | |
| 47 TaskQueueThrottler::TimeBudgetPool::~TimeBudgetPool() {} | |
| 48 | |
| 49 void TaskQueueThrottler::TimeBudgetPool::SetTimeBudget(base::TimeTicks now, | |
| 50 double cpu_percentage) { | |
| 51 Advance(now); | |
| 52 cpu_percentage_ = cpu_percentage; | |
| 53 } | |
| 54 | |
| 55 void TaskQueueThrottler::TimeBudgetPool::AddQueue(base::TimeTicks now, | |
| 56 TaskQueue* queue) { | |
| 57 Metadata& metadata = task_queue_throttler_->queue_details_[queue]; | |
| 58 DCHECK(!metadata.time_budget_pool); | |
| 59 metadata.time_budget_pool = this; | |
| 60 | |
| 61 associated_task_queues_.insert(queue); | |
| 62 | |
| 63 if (metadata.IsThrottled() == 0) | |
|
Sami
2016/09/22 12:34:07
nit: !metadata.IsThrottled()
altimin
2016/09/22 13:45:59
Ooops. Thanks!
| |
| 64 return; | |
| 65 | |
| 66 queue->SetQueueEnabled(false); | |
| 67 | |
| 68 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | |
| 69 GetNextAllowedRunTime()); | |
| 70 } | |
| 71 | |
| 72 void TaskQueueThrottler::TimeBudgetPool::RemoveQueue(base::TimeTicks now, | |
| 73 TaskQueue* queue) { | |
| 74 auto find_it = task_queue_throttler_->queue_details_.find(queue); | |
| 75 DCHECK(find_it != task_queue_throttler_->queue_details_.end() && | |
| 76 find_it->second.time_budget_pool == this); | |
| 77 find_it->second.time_budget_pool = nullptr; | |
| 78 bool is_throttled = find_it->second.IsThrottled(); | |
| 79 | |
| 80 task_queue_throttler_->MaybeDeleteQueueMetadata(find_it); | |
| 81 associated_task_queues_.erase(queue); | |
| 82 | |
| 83 if (is_throttled) | |
| 84 return; | |
| 85 | |
| 86 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | |
| 87 base::nullopt); | |
| 88 } | |
| 89 | |
| 90 void TaskQueueThrottler::TimeBudgetPool::EnableThrottling(LazyNow* lazy_now) { | |
| 91 if (is_enabled_) | |
| 92 return; | |
| 93 is_enabled_ = true; | |
| 94 | |
| 95 for (TaskQueue* queue : associated_task_queues_) { | |
|
Sami
2016/09/22 12:34:07
Could we just call BlockQueues() here?
altimin
2016/09/22 13:45:58
Done.
| |
| 96 if (!task_queue_throttler_->IsThrottled(queue)) | |
| 97 continue; | |
| 98 | |
| 99 queue->SetQueueEnabled(false); | |
| 100 | |
| 101 task_queue_throttler_->MaybeSchedulePumpQueue( | |
| 102 FROM_HERE, lazy_now->Now(), queue, GetNextAllowedRunTime()); | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 void TaskQueueThrottler::TimeBudgetPool::DisableThrottling(LazyNow* lazy_now) { | |
| 107 if (!is_enabled_) | |
| 108 return; | |
| 109 is_enabled_ = false; | |
| 110 | |
| 111 for (TaskQueue* queue : associated_task_queues_) { | |
| 112 if (!task_queue_throttler_->IsThrottled(queue)) | |
| 113 continue; | |
| 114 | |
| 115 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, lazy_now->Now(), | |
| 116 queue, base::nullopt); | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 bool TaskQueueThrottler::TimeBudgetPool::IsThrottlingEnabled() const { | |
| 121 return is_enabled_; | |
| 122 } | |
| 123 | |
| 124 void TaskQueueThrottler::TimeBudgetPool::Close() { | |
| 125 DCHECK_EQ(0u, associated_task_queues_.size()); | |
| 126 | |
| 127 task_queue_throttler_->time_budget_pools_.erase(this); | |
| 128 } | |
| 129 | |
| 130 bool TaskQueueThrottler::TimeBudgetPool::HasEnoughBudgetToRun( | |
| 131 base::TimeTicks now) { | |
| 132 Advance(now); | |
| 133 return !is_enabled_ || current_budget_level_.InMicroseconds() >= 0; | |
| 134 } | |
| 135 | |
| 136 base::TimeTicks TaskQueueThrottler::TimeBudgetPool::GetNextAllowedRunTime() { | |
| 137 if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) { | |
| 138 return last_checkpoint_; | |
| 139 } else { | |
| 140 // Subtract because current_budget is negative. | |
| 141 return last_checkpoint_ - current_budget_level_ / cpu_percentage_; | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 void TaskQueueThrottler::TimeBudgetPool::RecordTaskRunTime( | |
| 146 base::TimeDelta task_run_time) { | |
| 147 if (is_enabled_) { | |
|
Sami
2016/09/22 12:34:07
nit: no {}
altimin
2016/09/22 13:45:59
Done.
| |
| 148 current_budget_level_ -= task_run_time; | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 const char* TaskQueueThrottler::TimeBudgetPool::Name() const { | |
| 153 return name_; | |
| 154 } | |
| 155 | |
| 156 void TaskQueueThrottler::TimeBudgetPool::AsValueInto( | |
| 157 base::trace_event::TracedValue* state, | |
| 158 base::TimeTicks now) const { | |
| 159 state->BeginDictionary(); | |
| 160 | |
| 161 state->SetString("name", name_); | |
| 162 state->SetDouble("time_budget", cpu_percentage_); | |
| 163 state->SetDouble("time_budget_level_in_seconds", | |
| 164 current_budget_level_.InSecondsF()); | |
| 165 state->SetDouble("last_checkpoint_seconds_ago", | |
| 166 (now - last_checkpoint_).InSecondsF()); | |
|
caseq
2016/09/22 00:52:34
perhaps just dump last_checkpoint_?
altimin
2016/09/22 13:45:59
I believe that delta is easier to read than random
| |
| 167 | |
| 168 state->BeginArray("task_queues"); | |
| 169 for (TaskQueue* queue : associated_task_queues_) { | |
| 170 state->AppendString(base::StringPrintf( | |
| 171 "%" PRIx64, static_cast<uint64_t>(reinterpret_cast<uintptr_t>(queue)))); | |
| 172 } | |
| 173 state->EndArray(); | |
| 174 | |
| 175 state->EndDictionary(); | |
| 176 } | |
| 177 | |
| 178 void TaskQueueThrottler::TimeBudgetPool::Advance(base::TimeTicks now) { | |
| 179 if (now > last_checkpoint_) { | |
| 180 if (is_enabled_) { | |
| 181 current_budget_level_ = std::min( | |
| 182 current_budget_level_ + cpu_percentage_ * (now - last_checkpoint_), | |
| 183 max_budget_level_); | |
| 184 } | |
| 185 last_checkpoint_ = now; | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 void TaskQueueThrottler::TimeBudgetPool::BlockQueues(base::TimeTicks now) { | |
| 190 for (TaskQueue* queue : associated_task_queues_) { | |
| 191 if (!task_queue_throttler_->IsThrottled(queue)) | |
| 192 continue; | |
| 193 | |
| 194 queue->SetQueueEnabled(false); | |
| 195 task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue, | |
| 196 base::nullopt); | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 TaskQueueThrottler::TaskQueueThrottler( | |
| 201 RendererSchedulerImpl* renderer_scheduler, | |
| 202 const char* tracing_category) | |
|
caseq
2016/09/22 01:25:26
Ouch -- just noticed this! Trace macros expect sta
Sami
2016/09/22 12:34:07
Good point. We might want to fix this separately s
altimin
2016/09/22 13:45:59
I'll add a TODO and address it in a next patch.
| |
| 203 : task_runner_(renderer_scheduler->ControlTaskRunner()), | |
| 204 renderer_scheduler_(renderer_scheduler), | |
| 205 tick_clock_(renderer_scheduler->tick_clock()), | |
| 206 tracing_category_(tracing_category), | |
| 207 time_domain_(new ThrottledTimeDomain(this, tracing_category)), | |
| 208 virtual_time_(false), | |
| 209 weak_factory_(this) { | |
| 210 pump_throttled_tasks_closure_.Reset(base::Bind( | |
| 211 &TaskQueueThrottler::PumpThrottledTasks, weak_factory_.GetWeakPtr())); | |
| 212 forward_immediate_work_callback_ = | |
| 213 base::Bind(&TaskQueueThrottler::OnTimeDomainHasImmediateWork, | |
| 214 weak_factory_.GetWeakPtr()); | |
| 215 | |
| 216 renderer_scheduler_->RegisterTimeDomain(time_domain_.get()); | |
| 217 } | |
| 218 | |
| 219 TaskQueueThrottler::~TaskQueueThrottler() { | |
| 220 // It's possible for queues to be still throttled, so we need to tidy up | |
| 221 // before unregistering the time domain. | |
| 222 for (const TaskQueueMap::value_type& map_entry : queue_details_) { | |
| 223 if (map_entry.second.IsThrottled()) { | |
| 224 TaskQueue* task_queue = map_entry.first; | |
| 225 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); | |
| 226 task_queue->RemoveFence(); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 renderer_scheduler_->UnregisterTimeDomain(time_domain_.get()); | |
| 231 } | |
| 232 | |
| 233 void TaskQueueThrottler::SetQueueEnabled(TaskQueue* task_queue, bool enabled) { | |
| 234 TaskQueueMap::iterator find_it = queue_details_.find(task_queue); | |
| 235 | |
| 236 if (find_it == queue_details_.end()) { | |
| 237 task_queue->SetQueueEnabled(enabled); | |
| 238 return; | |
| 239 } | |
| 240 | |
| 241 find_it->second.enabled = enabled; | |
| 242 | |
| 243 if (!find_it->second.IsThrottled()) | |
| 244 return; | |
| 245 | |
| 246 // We don't enable the queue here because it's throttled and there might be | |
| 247 // tasks in it's work queue that would execute immediatly rather than after | |
| 248 // PumpThrottledTasks runs. | |
| 249 if (!enabled) { | |
| 250 task_queue->SetQueueEnabled(false); | |
| 251 MaybeSchedulePumpQueue(FROM_HERE, tick_clock_->NowTicks(), task_queue, | |
| 252 base::nullopt); | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 void TaskQueueThrottler::IncreaseThrottleRefCount(TaskQueue* task_queue) { | |
| 257 DCHECK_NE(task_queue, task_runner_.get()); | |
| 258 | |
| 259 if (virtual_time_) | |
| 260 return; | |
| 261 | |
| 262 std::pair<TaskQueueMap::iterator, bool> insert_result = | |
| 263 queue_details_.insert(std::make_pair(task_queue, Metadata())); | |
| 264 | |
| 265 if (!insert_result.first->second.IsThrottled()) { | |
| 266 // The insert was successful so we need to throttle the queue. | |
| 267 insert_result.first->second.enabled = task_queue->IsQueueEnabled(); | |
| 268 | |
| 269 task_queue->SetTimeDomain(time_domain_.get()); | |
| 270 task_queue->RemoveFence(); | |
| 271 task_queue->SetQueueEnabled(false); | |
| 272 | |
| 273 if (!task_queue->IsEmpty()) { | |
| 274 if (task_queue->HasPendingImmediateWork()) { | |
| 275 OnTimeDomainHasImmediateWork(task_queue); | |
| 276 } else { | |
| 277 OnTimeDomainHasDelayedWork(task_queue); | |
| 278 } | |
| 279 } | |
| 280 | |
| 281 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueThrottled", | |
| 282 "task_queue", task_queue); | |
| 283 } | |
| 284 | |
| 285 insert_result.first->second.throttling_ref_count++; | |
| 286 } | |
| 287 | |
| 288 void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) { | |
| 289 if (virtual_time_) | |
| 290 return; | |
| 291 | |
| 292 TaskQueueMap::iterator iter = queue_details_.find(task_queue); | |
| 293 | |
| 294 if (iter != queue_details_.end() && | |
| 295 --iter->second.throttling_ref_count == 0) { | |
| 296 bool enabled = iter->second.enabled; | |
| 297 | |
| 298 MaybeDeleteQueueMetadata(iter); | |
| 299 | |
| 300 task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain()); | |
| 301 task_queue->RemoveFence(); | |
| 302 task_queue->SetQueueEnabled(enabled); | |
| 303 | |
| 304 TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUntrottled", | |
| 305 "task_queue", task_queue); | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 bool TaskQueueThrottler::IsThrottled(TaskQueue* task_queue) const { | |
| 310 auto find_it = queue_details_.find(task_queue); | |
| 311 if (find_it == queue_details_.end()) | |
| 312 return false; | |
| 313 return find_it->second.IsThrottled(); | |
| 314 } | |
| 315 | |
| 316 void TaskQueueThrottler::UnregisterTaskQueue(TaskQueue* task_queue) { | |
| 317 LazyNow lazy_now(tick_clock_); | |
| 318 auto find_it = queue_details_.find(task_queue); | |
| 319 | |
| 320 if (find_it == queue_details_.end()) | |
| 321 return; | |
| 322 | |
| 323 if (find_it->second.time_budget_pool) | |
| 324 find_it->second.time_budget_pool->RemoveQueue(lazy_now.Now(), task_queue); | |
| 325 | |
| 326 queue_details_.erase(find_it); | |
| 327 } | |
| 328 | |
| 329 void TaskQueueThrottler::OnTimeDomainHasImmediateWork(TaskQueue* queue) { | |
| 330 // Forward to the main thread if called from another thread | |
| 331 if (!task_runner_->RunsTasksOnCurrentThread()) { | |
| 332 task_runner_->PostTask(FROM_HERE, | |
| 333 base::Bind(forward_immediate_work_callback_, queue)); | |
| 334 return; | |
| 335 } | |
| 336 TRACE_EVENT0(tracing_category_, | |
| 337 "TaskQueueThrottler::OnTimeDomainHasImmediateWork"); | |
| 338 | |
| 339 base::TimeTicks now = tick_clock_->NowTicks(); | |
| 340 base::TimeTicks next_allowed_run_time = GetNextAllowedRunTime(now, queue); | |
| 341 MaybeSchedulePumpThrottledTasks(FROM_HERE, now, next_allowed_run_time); | |
| 342 } | |
| 343 | |
| 344 void TaskQueueThrottler::OnTimeDomainHasDelayedWork(TaskQueue* queue) { | |
| 345 TRACE_EVENT0(tracing_category_, | |
| 346 "TaskQueueThrottler::OnTimeDomainHasDelayedWork"); | |
| 347 base::TimeTicks now = tick_clock_->NowTicks(); | |
| 348 LazyNow lazy_now(now); | |
| 349 | |
| 350 base::Optional<base::TimeTicks> next_scheduled_delayed_task = | |
| 351 NextTaskRunTime(&lazy_now, queue); | |
| 352 DCHECK(next_scheduled_delayed_task); | |
| 353 MaybeSchedulePumpThrottledTasks(FROM_HERE, now, | |
| 354 next_scheduled_delayed_task.value()); | |
| 355 } | |
| 356 | |
| 357 namespace { | |
| 358 | |
| 359 template <class T> | |
|
Sami
2016/09/22 12:34:08
Could you move these into the anonymous namespace
altimin
2016/09/22 13:45:59
Done.
| |
| 360 T Min(const base::Optional<T>& optional, const T& value) { | |
| 361 if (!optional) { | |
| 362 return value; | |
| 363 } | |
| 364 return std::min(optional.value(), value); | |
| 365 } | |
| 366 | |
| 367 template <class T> | |
| 368 base::Optional<T> Min(const base::Optional<T>& a, const base::Optional<T>& b) { | |
| 369 if (!b) | |
| 370 return a; | |
| 371 if (!a) | |
| 372 return b; | |
| 373 return std::min(a.value(), b.value()); | |
| 374 } | |
| 375 | |
| 376 template <class T> | |
| 377 T Max(const base::Optional<T>& optional, const T& value) { | |
| 378 if (!optional) | |
| 379 return value; | |
| 380 return std::max(optional.value(), value); | |
| 381 } | |
| 382 | |
| 383 template <class T> | |
| 384 base::Optional<T> Max(const base::Optional<T>& a, const base::Optional<T>& b) { | |
| 385 if (!b) | |
| 386 return a; | |
| 387 if (!a) | |
| 388 return b; | |
| 389 return std::max(a.value(), b.value()); | |
| 390 } | |
| 391 | |
| 392 } // namespace | |
| 393 | |
| 394 void TaskQueueThrottler::PumpThrottledTasks() { | |
| 395 TRACE_EVENT0(tracing_category_, "TaskQueueThrottler::PumpThrottledTasks"); | |
| 396 pending_pump_throttled_tasks_runtime_.reset(); | |
| 397 | |
| 398 LazyNow lazy_now(tick_clock_); | |
| 399 base::Optional<base::TimeTicks> next_scheduled_delayed_task; | |
| 400 | |
| 401 for (const TaskQueueMap::value_type& map_entry : queue_details_) { | |
| 402 TaskQueue* task_queue = map_entry.first; | |
| 403 if (!map_entry.second.enabled || task_queue->IsEmpty() || | |
| 404 !map_entry.second.IsThrottled()) | |
| 405 continue; | |
| 406 | |
| 407 // Don't enable queues whose budget pool doesn't allow them to run now. | |
| 408 base::TimeTicks next_allowed_run_time = | |
| 409 GetNextAllowedRunTime(lazy_now.Now(), task_queue); | |
| 410 base::Optional<base::TimeTicks> next_desired_run_time = | |
| 411 NextTaskRunTime(&lazy_now, task_queue); | |
| 412 | |
| 413 if (next_desired_run_time && | |
| 414 next_allowed_run_time > next_desired_run_time.value()) { | |
| 415 TRACE_EVENT1( | |
| 416 tracing_category_, | |
| 417 "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled", | |
| 418 "throttle_time_in_seconds", | |
| 419 (next_allowed_run_time - next_desired_run_time.value()).InSecondsF()); | |
| 420 | |
| 421 // Schedule a pump for queue which was disabled because of time budget. | |
| 422 next_scheduled_delayed_task = | |
| 423 Min(next_scheduled_delayed_task, next_allowed_run_time); | |
| 424 | |
| 425 continue; | |
| 426 } | |
| 427 | |
| 428 next_scheduled_delayed_task = | |
| 429 Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp()); | |
| 430 | |
| 431 if (next_allowed_run_time > lazy_now.Now()) | |
| 432 continue; | |
| 433 | |
| 434 task_queue->SetQueueEnabled(true); | |
| 435 task_queue->InsertFence(); | |
| 436 } | |
| 437 | |
| 438 // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is | |
| 439 // a pending delayed task or a throttled task ready to run. | |
| 440 // NOTE: posting a non-delayed task in the future will result in | |
| 441 // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called. | |
| 442 if (next_scheduled_delayed_task) { | |
| 443 MaybeSchedulePumpThrottledTasks(FROM_HERE, lazy_now.Now(), | |
| 444 *next_scheduled_delayed_task); | |
| 445 } | |
| 446 } | |
| 447 | |
| 448 /* static */ | |
| 449 base::TimeTicks TaskQueueThrottler::AlignedThrottledRunTime( | |
| 450 base::TimeTicks unthrottled_runtime) { | |
| 451 const base::TimeDelta one_second = base::TimeDelta::FromSeconds(1); | |
| 452 return unthrottled_runtime + one_second - | |
| 453 ((unthrottled_runtime - base::TimeTicks()) % one_second); | |
| 454 } | |
| 455 | |
| 456 void TaskQueueThrottler::MaybeSchedulePumpThrottledTasks( | |
| 457 const tracked_objects::Location& from_here, | |
| 458 base::TimeTicks now, | |
| 459 base::TimeTicks unaligned_runtime) { | |
| 460 if (virtual_time_) | |
| 461 return; | |
| 462 | |
| 463 base::TimeTicks runtime = | |
| 464 std::max(now, AlignedThrottledRunTime(unaligned_runtime)); | |
| 465 | |
| 466 // If there is a pending call to PumpThrottledTasks and it's sooner than | |
| 467 // |runtime| then return. | |
| 468 if (pending_pump_throttled_tasks_runtime_ && | |
| 469 runtime >= pending_pump_throttled_tasks_runtime_.value()) { | |
| 470 return; | |
| 471 } | |
| 472 | |
| 473 pending_pump_throttled_tasks_runtime_ = runtime; | |
| 474 | |
| 475 pump_throttled_tasks_closure_.Cancel(); | |
| 476 | |
| 477 base::TimeDelta delay = pending_pump_throttled_tasks_runtime_.value() - now; | |
| 478 TRACE_EVENT1(tracing_category_, | |
| 479 "TaskQueueThrottler::MaybeSchedulePumpThrottledTasks", | |
| 480 "delay_till_next_pump_ms", delay.InMilliseconds()); | |
| 481 task_runner_->PostDelayedTask( | |
| 482 from_here, pump_throttled_tasks_closure_.callback(), delay); | |
| 483 } | |
| 484 | |
| 485 void TaskQueueThrottler::EnableVirtualTime() { | |
| 486 virtual_time_ = true; | |
| 487 | |
| 488 pump_throttled_tasks_closure_.Cancel(); | |
| 489 | |
| 490 for (auto it = queue_details_.begin(); it != queue_details_.end();) { | |
| 491 TaskQueue* task_queue = it->first; | |
| 492 bool enabled = it->second.enabled; | |
| 493 | |
| 494 if (!it->second.time_budget_pool) { | |
| 495 it = queue_details_.erase(it); | |
| 496 } else { | |
| 497 // Fall back to default values. | |
| 498 it->second.throttling_ref_count = 0; | |
| 499 it->second.enabled = false; | |
| 500 it++; | |
| 501 } | |
| 502 | |
| 503 task_queue->SetTimeDomain(renderer_scheduler_->GetVirtualTimeDomain()); | |
| 504 task_queue->RemoveFence(); | |
| 505 task_queue->SetQueueEnabled(enabled); | |
| 506 } | |
| 507 } | |
| 508 | |
| 509 TaskQueueThrottler::TimeBudgetPool* TaskQueueThrottler::CreateTimeBudgetPool( | |
| 510 const char* name) { | |
| 511 TimeBudgetPool* time_budget_pool = | |
| 512 new TimeBudgetPool(name, this, tick_clock_->NowTicks()); | |
| 513 time_budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool); | |
| 514 return time_budget_pool; | |
| 515 } | |
| 516 | |
| 517 void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue, | |
| 518 base::TimeTicks start_time, | |
| 519 base::TimeTicks end_time) { | |
| 520 if (!IsThrottled(task_queue)) | |
| 521 return; | |
| 522 | |
| 523 TimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(task_queue); | |
| 524 if (!time_budget_pool) | |
| 525 return; | |
| 526 | |
| 527 time_budget_pool->RecordTaskRunTime(end_time - start_time); | |
| 528 if (!time_budget_pool->HasEnoughBudgetToRun(end_time)) | |
| 529 time_budget_pool->BlockQueues(end_time); | |
| 530 } | |
| 531 | |
| 532 void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state, | |
| 533 base::TimeTicks now) const { | |
| 534 if (pending_pump_throttled_tasks_runtime_) { | |
| 535 state->SetDouble( | |
| 536 "next_throttled_tasks_pump_in_seconds", | |
| 537 (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF()); | |
| 538 } | |
| 539 | |
| 540 state->BeginDictionary("time_budget_pools"); | |
| 541 | |
| 542 for (const auto& map_entry : time_budget_pools_) { | |
| 543 TaskQueueThrottler::TimeBudgetPool* pool = map_entry.first; | |
| 544 pool->AsValueInto(state, now); | |
| 545 } | |
| 546 | |
| 547 state->EndDictionary(); | |
| 548 } | |
| 549 | |
| 550 TaskQueueThrottler::TimeBudgetPool* | |
| 551 TaskQueueThrottler::GetTimeBudgetPoolForQueue(TaskQueue* queue) { | |
| 552 auto find_it = queue_details_.find(queue); | |
| 553 if (find_it == queue_details_.end()) { | |
| 554 return nullptr; | |
| 555 } else { | |
|
caseq
2016/09/22 01:25:26
style: we don't use else after return
altimin
2016/09/22 13:45:59
Done.
| |
| 556 return find_it->second.time_budget_pool; | |
| 557 } | |
| 558 } | |
| 559 | |
| 560 void TaskQueueThrottler::MaybeSchedulePumpQueue( | |
| 561 const tracked_objects::Location& from_here, | |
| 562 base::TimeTicks now, | |
| 563 TaskQueue* queue, | |
| 564 base::Optional<base::TimeTicks> next_possible_run_time) { | |
| 565 LazyNow lazy_now(now); | |
| 566 base::Optional<base::TimeTicks> next_run_time = | |
| 567 Max(NextTaskRunTime(&lazy_now, queue), next_possible_run_time); | |
| 568 | |
| 569 if (next_run_time) { | |
| 570 MaybeSchedulePumpThrottledTasks(from_here, now, next_run_time.value()); | |
| 571 } | |
| 572 } | |
| 573 | |
| 574 base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(base::TimeTicks now, | |
| 575 TaskQueue* queue) { | |
| 576 TimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(queue); | |
| 577 if (!time_budget_pool) { | |
| 578 return now; | |
| 579 } else { | |
|
caseq
2016/09/22 01:25:26
ditto.
altimin
2016/09/22 13:45:59
Done.
| |
| 580 return std::max(now, time_budget_pool->GetNextAllowedRunTime()); | |
| 581 } | |
| 582 } | |
| 583 | |
| 584 void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) { | |
| 585 if (!it->second.IsThrottled() && !it->second.time_budget_pool) | |
| 586 queue_details_.erase(it); | |
| 587 } | |
| 588 | |
| 589 } // namespace scheduler | |
| 590 } // namespace blink | |
| OLD | NEW |