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