Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Unified Diff: third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc

Issue 2258133002: [scheduler] Implement time-based cpu throttling. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Renamed Enable/Disable to Enable/DisableThrottling. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
diff --git a/third_party/WebKit/Source/platform/scheduler/renderer/throttling_helper.cc b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
similarity index 27%
rename from third_party/WebKit/Source/platform/scheduler/renderer/throttling_helper.cc
rename to third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
index 20ff3a1116f6628a01002daf48055ed181c184e0..8f934212a6b811e746416207f69baeb178747056 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/throttling_helper.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
@@ -2,9 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "platform/scheduler/renderer/throttling_helper.h"
+#include "platform/scheduler/renderer/task_queue_throttler.h"
+#include <cstdint>
+
+#include "base/format_macros.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/optional.h"
+#include "base/strings/stringprintf.h"
#include "platform/scheduler/base/real_time_domain.h"
#include "platform/scheduler/child/scheduler_tqm_delegate.h"
#include "platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
@@ -16,8 +22,167 @@
namespace blink {
namespace scheduler {
-ThrottlingHelper::ThrottlingHelper(RendererSchedulerImpl* renderer_scheduler,
- const char* tracing_category)
+namespace {
+const int kMaxBudgetLevelInSeconds = 1;
+
+base::Optional<base::TimeTicks> NextTaskRunTime(LazyNow* lazy_now,
+ TaskQueue* queue) {
+ if (queue->HasPendingImmediateWork())
+ return lazy_now->Now();
+ return queue->GetNextScheduledWakeUp();
+}
+}
+
+TaskQueueThrottler::TimeBudgetPool::TimeBudgetPool(
+ const char* name,
+ TaskQueueThrottler* task_queue_throttler,
+ base::TimeTicks now)
+ : name_(name),
+ task_queue_throttler_(task_queue_throttler),
+ max_budget_level_(base::TimeDelta::FromSeconds(kMaxBudgetLevelInSeconds)),
+ last_checkpoint_(now),
+ cpu_percentage_(1),
+ is_enabled_(true) {}
+
+TaskQueueThrottler::TimeBudgetPool::~TimeBudgetPool() {}
+
+void TaskQueueThrottler::TimeBudgetPool::SetTimeBudget(base::TimeTicks now,
+ double cpu_percentage) {
+ Advance(now);
+ cpu_percentage_ = cpu_percentage;
+}
+
+void TaskQueueThrottler::TimeBudgetPool::AddQueue(base::TimeTicks now,
+ TaskQueue* queue) {
+ DCHECK(task_queue_throttler_->time_budget_pool_for_queue_.find(queue) ==
+ task_queue_throttler_->time_budget_pool_for_queue_.end());
+ task_queue_throttler_->time_budget_pool_for_queue_[queue] = this;
+
+ associated_task_queues_.insert(queue);
+
+ if (!task_queue_throttler_->IsThrottled(queue))
alex clarke (OOO till 29th) 2016/09/15 12:19:35 Note: if |time_budget_pool_for_queue_| gets rolled
altimin 2016/09/15 15:52:11 It's not feasible to do this, because queue can be
alex clarke (OOO till 29th) 2016/09/15 16:18:40 That's not true. Just because |throttled_queues_|
altimin 2016/09/16 13:38:48 Done.
alex clarke (OOO till 29th) 2016/09/16 14:36:18 I'm not seeing this change, did you forget up load
altimin 2016/09/16 14:46:59 Sorry, fixed now.
+ return;
+
+ queue->SetQueueEnabled(false);
+
+ task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue,
+ GetNextAllowedRunTime());
+}
+
+void TaskQueueThrottler::TimeBudgetPool::RemoveQueue(base::TimeTicks now,
+ TaskQueue* queue) {
+ DCHECK_EQ(task_queue_throttler_->time_budget_pool_for_queue_[queue], this);
+ task_queue_throttler_->time_budget_pool_for_queue_.erase(queue);
+
+ associated_task_queues_.erase(queue);
+
+ if (!task_queue_throttler_->IsThrottled(queue))
+ return;
+
+ task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, now, queue);
+}
+
+void TaskQueueThrottler::TimeBudgetPool::EnableThrottling(LazyNow* lazy_now) {
+ if (is_enabled_)
+ return;
+ is_enabled_ = true;
+
+ for (TaskQueue* queue : associated_task_queues_) {
+ if (!task_queue_throttler_->IsThrottled(queue))
+ continue;
+
+ queue->SetQueueEnabled(false);
+
+ task_queue_throttler_->MaybeSchedulePumpQueue(
+ FROM_HERE, lazy_now->Now(), queue, GetNextAllowedRunTime());
+ }
+}
+
+void TaskQueueThrottler::TimeBudgetPool::DisableThrottling(LazyNow* lazy_now) {
+ if (!is_enabled_)
+ return;
+ is_enabled_ = false;
+
+ for (TaskQueue* queue : associated_task_queues_) {
+ if (!task_queue_throttler_->IsThrottled(queue))
+ continue;
+
+ task_queue_throttler_->MaybeSchedulePumpQueue(FROM_HERE, lazy_now->Now(),
+ queue);
+ }
+}
+
+bool TaskQueueThrottler::TimeBudgetPool::IsThrottlingEnabled() const {
+ return is_enabled_;
+}
+
+void TaskQueueThrottler::TimeBudgetPool::Close() {
+ DCHECK_EQ(0u, associated_task_queues_.size());
+
+ task_queue_throttler_->time_budget_pools_.erase(this);
+}
+
+bool TaskQueueThrottler::TimeBudgetPool::IsAllowedToRun(base::TimeTicks now) {
alex clarke (OOO till 29th) 2016/09/15 12:19:35 I wonder if HasEnoughBudgetToRun is a better name,
altimin 2016/09/15 15:52:11 Done.
+ Advance(now);
+ return !is_enabled_ || current_budget_level_.InMicroseconds() >= 0;
+}
+
+base::TimeTicks TaskQueueThrottler::TimeBudgetPool::GetNextAllowedRunTime() {
+ if (!is_enabled_ || current_budget_level_.InMicroseconds() >= 0) {
+ return last_checkpoint_;
+ } else {
+ // Subtract because current_budget is negative.
+ return last_checkpoint_ - current_budget_level_ / cpu_percentage_;
+ }
+}
+
+void TaskQueueThrottler::TimeBudgetPool::RecordTaskRunTime(
+ base::TimeDelta task_run_time) {
+ if (is_enabled_) {
+ current_budget_level_ -= task_run_time;
+ }
+}
+
+const char* TaskQueueThrottler::TimeBudgetPool::Name() const {
+ return name_;
+}
+
+void TaskQueueThrottler::TimeBudgetPool::AsValueInto(
+ base::trace_event::TracedValue* state,
+ base::TimeTicks now) const {
+ state->BeginDictionary();
+
+ state->SetString("name", name_);
+ state->SetDouble("time_budget", cpu_percentage_);
+ state->SetDouble("time_budget_level_in_seconds",
+ current_budget_level_.InSecondsF());
+ state->SetDouble("last_checkpoint_seconds_ago",
+ (now - last_checkpoint_).InSecondsF());
+
+ state->BeginArray("task_queues");
+ for (TaskQueue* queue : associated_task_queues_) {
+ state->AppendString(base::StringPrintf(
+ "%" PRIx64, static_cast<uint64_t>(reinterpret_cast<uintptr_t>(queue))));
+ }
+ state->EndArray();
+
+ state->EndDictionary();
+}
+
+void TaskQueueThrottler::TimeBudgetPool::Advance(base::TimeTicks now) {
+ if (now > last_checkpoint_) {
+ if (is_enabled_) {
+ current_budget_level_ = std::min(
+ current_budget_level_ + cpu_percentage_ * (now - last_checkpoint_),
+ max_budget_level_);
+ }
+ last_checkpoint_ = now;
+ }
+}
+
+TaskQueueThrottler::TaskQueueThrottler(
+ RendererSchedulerImpl* renderer_scheduler,
+ const char* tracing_category)
: task_runner_(renderer_scheduler->ControlTaskRunner()),
renderer_scheduler_(renderer_scheduler),
tick_clock_(renderer_scheduler->tick_clock()),
@@ -26,15 +191,15 @@ ThrottlingHelper::ThrottlingHelper(RendererSchedulerImpl* renderer_scheduler,
virtual_time_(false),
weak_factory_(this) {
pump_throttled_tasks_closure_.Reset(base::Bind(
- &ThrottlingHelper::PumpThrottledTasks, weak_factory_.GetWeakPtr()));
- forward_immediate_work_closure_ =
- base::Bind(&ThrottlingHelper::OnTimeDomainHasImmediateWork,
+ &TaskQueueThrottler::PumpThrottledTasks, weak_factory_.GetWeakPtr()));
+ forward_immediate_work_callback_ =
+ base::Bind(&TaskQueueThrottler::OnTimeDomainHasImmediateWork,
weak_factory_.GetWeakPtr());
renderer_scheduler_->RegisterTimeDomain(time_domain_.get());
}
-ThrottlingHelper::~ThrottlingHelper() {
+TaskQueueThrottler::~TaskQueueThrottler() {
// It's possible for queues to be still throttled, so we need to tidy up
// before unregistering the time domain.
for (const TaskQueueMap::value_type& map_entry : throttled_queues_) {
@@ -46,7 +211,7 @@ ThrottlingHelper::~ThrottlingHelper() {
renderer_scheduler_->UnregisterTimeDomain(time_domain_.get());
}
-void ThrottlingHelper::SetQueueEnabled(TaskQueue* task_queue, bool enabled) {
+void TaskQueueThrottler::SetQueueEnabled(TaskQueue* task_queue, bool enabled) {
TaskQueueMap::iterator find_it = throttled_queues_.find(task_queue);
if (find_it == throttled_queues_.end()) {
@@ -63,7 +228,7 @@ void ThrottlingHelper::SetQueueEnabled(TaskQueue* task_queue, bool enabled) {
task_queue->SetQueueEnabled(false);
}
-void ThrottlingHelper::IncreaseThrottleRefCount(TaskQueue* task_queue) {
+void TaskQueueThrottler::IncreaseThrottleRefCount(TaskQueue* task_queue) {
DCHECK_NE(task_queue, task_runner_.get());
if (virtual_time_)
@@ -74,25 +239,28 @@ void ThrottlingHelper::IncreaseThrottleRefCount(TaskQueue* task_queue) {
task_queue, Metadata(1, task_queue->IsQueueEnabled())));
if (insert_result.second) {
- // The insert was succesful so we need to throttle the queue.
+ // The insert was successful so we need to throttle the queue.
task_queue->SetTimeDomain(time_domain_.get());
task_queue->RemoveFence();
task_queue->SetQueueEnabled(false);
if (!task_queue->IsEmpty()) {
if (task_queue->HasPendingImmediateWork()) {
- OnTimeDomainHasImmediateWork();
+ OnTimeDomainHasImmediateWork(task_queue);
} else {
- OnTimeDomainHasDelayedWork();
+ OnTimeDomainHasDelayedWork(task_queue);
}
}
+
+ TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueThrottled",
+ "task_queue", task_queue);
} else {
// An entry already existed in the map so we need to increment the refcount.
insert_result.first->second.throttling_ref_count++;
}
}
-void ThrottlingHelper::DecreaseThrottleRefCount(TaskQueue* task_queue) {
+void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) {
if (virtual_time_)
return;
@@ -107,104 +275,170 @@ void ThrottlingHelper::DecreaseThrottleRefCount(TaskQueue* task_queue) {
task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
task_queue->RemoveFence();
task_queue->SetQueueEnabled(enabled);
+
+ TRACE_EVENT1(tracing_category_, "TaskQueueThrottler_TaskQueueUntrottled",
+ "task_queue", task_queue);
}
}
-bool ThrottlingHelper::IsThrottled(TaskQueue* task_queue) const {
+bool TaskQueueThrottler::IsThrottled(TaskQueue* task_queue) const {
return throttled_queues_.find(task_queue) != throttled_queues_.end();
}
-void ThrottlingHelper::UnregisterTaskQueue(TaskQueue* task_queue) {
+void TaskQueueThrottler::UnregisterTaskQueue(TaskQueue* task_queue) {
throttled_queues_.erase(task_queue);
}
-void ThrottlingHelper::OnTimeDomainHasImmediateWork() {
- // Forward to the main thread if called from another thread.
+void TaskQueueThrottler::OnTimeDomainHasImmediateWork(TaskQueue* queue) {
+ // Forward to the main thread if called from another thread
if (!task_runner_->RunsTasksOnCurrentThread()) {
- task_runner_->PostTask(FROM_HERE, forward_immediate_work_closure_);
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(forward_immediate_work_callback_, queue));
return;
}
TRACE_EVENT0(tracing_category_,
- "ThrottlingHelper::OnTimeDomainHasImmediateWork");
+ "TaskQueueThrottler::OnTimeDomainHasImmediateWork");
+
base::TimeTicks now = tick_clock_->NowTicks();
- MaybeSchedulePumpThrottledTasksLocked(FROM_HERE, now, now);
+ base::TimeTicks next_allowed_run_time = GetNextAllowedRunTime(now, queue);
+ MaybeSchedulePumpThrottledTasks(FROM_HERE, now, next_allowed_run_time);
}
-void ThrottlingHelper::OnTimeDomainHasDelayedWork() {
+void TaskQueueThrottler::OnTimeDomainHasDelayedWork(TaskQueue* queue) {
TRACE_EVENT0(tracing_category_,
- "ThrottlingHelper::OnTimeDomainHasDelayedWork");
- base::TimeTicks next_scheduled_delayed_task;
- bool has_delayed_task =
- time_domain_->NextScheduledRunTime(&next_scheduled_delayed_task);
- DCHECK(has_delayed_task);
+ "TaskQueueThrottler::OnTimeDomainHasDelayedWork");
base::TimeTicks now = tick_clock_->NowTicks();
- MaybeSchedulePumpThrottledTasksLocked(FROM_HERE, now,
- next_scheduled_delayed_task);
+ LazyNow lazy_now(now);
+
+ base::Optional<base::TimeTicks> next_scheduled_delayed_task =
+ NextTaskRunTime(&lazy_now, queue);
+ DCHECK(next_scheduled_delayed_task);
+ MaybeSchedulePumpThrottledTasks(FROM_HERE, now,
+ next_scheduled_delayed_task.value());
+}
+
+namespace {
+
+template <class T>
+T Min(const base::Optional<T>& optional, const T& value) {
+ if (!optional) {
+ return value;
+ }
+ return std::min(optional.value(), value);
+}
+
+template <class T>
+base::Optional<T> Min(const base::Optional<T>& a, const base::Optional<T>& b) {
+ if (!b)
+ return a;
+ if (!a)
+ return b;
+ return std::min(a.value(), b.value());
+}
+
+template <class T>
+T Max(const base::Optional<T>& optional, const T& value) {
+ if (!optional)
+ return value;
+ return std::max(optional.value(), value);
+}
+
+template <class T>
+base::Optional<T> Max(const base::Optional<T>& a, const base::Optional<T>& b) {
+ if (!b)
+ return a;
+ if (!a)
+ return b;
+ return std::max(a.value(), b.value());
}
-void ThrottlingHelper::PumpThrottledTasks() {
- TRACE_EVENT0(tracing_category_, "ThrottlingHelper::PumpThrottledTasks");
- pending_pump_throttled_tasks_runtime_ = base::TimeTicks();
+} // namespace
+
+void TaskQueueThrottler::PumpThrottledTasks() {
+ TRACE_EVENT0("renderer.scheduler", "TaskQueueThrottler::PumpThrottledTasks");
alex clarke (OOO till 29th) 2016/09/15 12:19:35 Why change tracing_category_?
altimin 2016/09/15 15:52:10 Done.
+ pending_pump_throttled_tasks_runtime_.reset();
+
+ LazyNow lazy_now(tick_clock_);
+ base::Optional<base::TimeTicks> next_scheduled_delayed_task;
- LazyNow lazy_low(tick_clock_);
for (const TaskQueueMap::value_type& map_entry : throttled_queues_) {
TaskQueue* task_queue = map_entry.first;
if (!map_entry.second.enabled || task_queue->IsEmpty())
continue;
- task_queue->SetQueueEnabled(true);
- task_queue->InsertFence();
+ base::TimeTicks next_allowed_run_time =
alex clarke (OOO till 29th) 2016/09/15 12:19:35 Please add a comment here saying: Don't pump queue
altimin 2016/09/15 15:52:10 Done.
+ GetNextAllowedRunTime(lazy_now.Now(), task_queue);
+ base::Optional<base::TimeTicks> next_desired_run_time =
+ NextTaskRunTime(&lazy_now, task_queue);
+
+ if (next_desired_run_time &&
+ next_allowed_run_time > next_desired_run_time.value()) {
+ TRACE_EVENT1(
+ "renderer.scheduler",
alex clarke (OOO till 29th) 2016/09/15 12:19:35 tracing_category_?
altimin 2016/09/15 15:52:11 Done.
+ "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled",
+ "throttle_time_in_seconds",
+ (next_allowed_run_time - next_desired_run_time.value()).InSecondsF());
+
+ next_scheduled_delayed_task =
+ Min(next_scheduled_delayed_task, next_allowed_run_time);
+ }
alex clarke (OOO till 29th) 2016/09/15 12:19:35 can we put continue; in there for clarity?
altimin 2016/09/15 15:52:10 Done.
+
+ next_scheduled_delayed_task =
+ Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp());
+
+ if (next_allowed_run_time == lazy_now.Now()) {
alex clarke (OOO till 29th) 2016/09/15 12:19:35 For readability I would be tempted instead to writ
altimin 2016/09/15 15:52:11 Done.
+ task_queue->SetQueueEnabled(true);
+ task_queue->InsertFence();
+ }
}
- // Make sure NextScheduledRunTime gives us an up-to date result.
- time_domain_->ClearExpiredWakeups();
- base::TimeTicks next_scheduled_delayed_task;
- // Maybe schedule a call to ThrottlingHelper::PumpThrottledTasks if there is
- // a pending delayed task. NOTE posting a non-delayed task in the future will
- // result in ThrottlingHelper::OnTimeDomainHasImmediateWork being called.
- if (time_domain_->NextScheduledRunTime(&next_scheduled_delayed_task)) {
- MaybeSchedulePumpThrottledTasksLocked(FROM_HERE, lazy_low.Now(),
- next_scheduled_delayed_task);
+ // Maybe schedule a call to TaskQueueThrottler::PumpThrottledTasks if there is
+ // a pending delayed task or a throttled task ready to run.
+ // NOTE: posting a non-delayed task in the future will result in
+ // TaskQueueThrottler::OnTimeDomainHasImmediateWork being called.
+ if (next_scheduled_delayed_task) {
+ MaybeSchedulePumpThrottledTasks(FROM_HERE, lazy_now.Now(),
+ *next_scheduled_delayed_task);
}
}
/* static */
-base::TimeTicks ThrottlingHelper::ThrottledRunTime(
+base::TimeTicks TaskQueueThrottler::AlignedThrottledRunTime(
base::TimeTicks unthrottled_runtime) {
const base::TimeDelta one_second = base::TimeDelta::FromSeconds(1);
return unthrottled_runtime + one_second -
((unthrottled_runtime - base::TimeTicks()) % one_second);
}
-void ThrottlingHelper::MaybeSchedulePumpThrottledTasksLocked(
+void TaskQueueThrottler::MaybeSchedulePumpThrottledTasks(
const tracked_objects::Location& from_here,
base::TimeTicks now,
- base::TimeTicks unthrottled_runtime) {
+ base::TimeTicks runtime) {
alex clarke (OOO till 29th) 2016/09/15 12:19:35 unaligned_runtiume
altimin 2016/09/15 15:52:11 Done.
if (virtual_time_)
return;
- base::TimeTicks throttled_runtime =
- ThrottledRunTime(std::max(now, unthrottled_runtime));
+ runtime = std::max(now, AlignedThrottledRunTime(runtime));
+
// If there is a pending call to PumpThrottledTasks and it's sooner than
- // |unthrottled_runtime| then return.
- if (!pending_pump_throttled_tasks_runtime_.is_null() &&
- throttled_runtime >= pending_pump_throttled_tasks_runtime_) {
+ // |runtime| then return.
+ if (pending_pump_throttled_tasks_runtime_ &&
+ runtime >= pending_pump_throttled_tasks_runtime_.value()) {
return;
}
- pending_pump_throttled_tasks_runtime_ = throttled_runtime;
+ pending_pump_throttled_tasks_runtime_ = runtime;
pump_throttled_tasks_closure_.Cancel();
- base::TimeDelta delay = pending_pump_throttled_tasks_runtime_ - now;
+ base::TimeDelta delay = pending_pump_throttled_tasks_runtime_.value() - now;
TRACE_EVENT1(tracing_category_,
- "ThrottlingHelper::MaybeSchedulePumpThrottledTasksLocked",
+ "TaskQueueThrottler::MaybeSchedulePumpThrottledTasks",
"delay_till_next_pump_ms", delay.InMilliseconds());
task_runner_->PostDelayedTask(
from_here, pump_throttled_tasks_closure_.callback(), delay);
}
-void ThrottlingHelper::EnableVirtualTime() {
+void TaskQueueThrottler::EnableVirtualTime() {
virtual_time_ = true;
pump_throttled_tasks_closure_.Cancel();
@@ -221,5 +455,90 @@ void ThrottlingHelper::EnableVirtualTime() {
}
}
+TaskQueueThrottler::TimeBudgetPool* TaskQueueThrottler::CreateTimeBudgetPool(
+ const char* name) {
+ TimeBudgetPool* time_budget_pool =
+ new TimeBudgetPool(name, this, tick_clock_->NowTicks());
+ time_budget_pools_[time_budget_pool] = base::WrapUnique(time_budget_pool);
+ return time_budget_pool;
+}
+
+void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue,
+ base::TimeTicks start_time,
+ base::TimeTicks end_time) {
+ if (!IsThrottled(task_queue))
+ return;
+
+ TimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(task_queue);
+ if (time_budget_pool) {
alex clarke (OOO till 29th) 2016/09/15 12:19:35 For readability I think we should prefer: if (!ti
altimin 2016/09/15 15:52:10 Done.
+ time_budget_pool->RecordTaskRunTime(end_time - start_time);
+ if (!time_budget_pool->IsAllowedToRun(end_time)) {
+ // This task was too expensive and all following tasks are throttled
+ // until explicitly allowed.
+ task_queue->SetQueueEnabled(false);
+
+ if (task_queue->HasPendingImmediateWork()) {
+ MaybeSchedulePumpThrottledTasks(
+ FROM_HERE, end_time,
+ std::max(end_time, time_budget_pool->GetNextAllowedRunTime()));
+ }
+ }
+ }
+}
+
+void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state,
+ base::TimeTicks now) const {
+ if (pending_pump_throttled_tasks_runtime_) {
+ state->SetDouble(
+ "next_throttled_tasks_pump_in_seconds",
+ (pending_pump_throttled_tasks_runtime_.value() - now).InSecondsF());
+ }
+
+ state->BeginDictionary("time_budget_pools");
+
+ for (const auto& map_entry : time_budget_pools_) {
+ TaskQueueThrottler::TimeBudgetPool* pool = map_entry.first;
+ pool->AsValueInto(state, now);
+ }
+
+ state->EndDictionary();
+}
+
+TaskQueueThrottler::TimeBudgetPool*
+TaskQueueThrottler::GetTimeBudgetPoolForQueue(TaskQueue* queue) {
+ auto find_it = time_budget_pool_for_queue_.find(queue);
+ if (find_it == time_budget_pool_for_queue_.end()) {
+ return nullptr;
+ } else {
+ TimeBudgetPool* result = find_it->second;
+ DCHECK(result);
+ return result;
+ }
+}
+
+void TaskQueueThrottler::MaybeSchedulePumpQueue(
+ const tracked_objects::Location& from_here,
+ base::TimeTicks now,
+ TaskQueue* queue,
+ base::Optional<base::TimeTicks> next_possible_run_time) {
+ LazyNow lazy_now(now);
+ base::Optional<base::TimeTicks> next_run_time =
+ Max(NextTaskRunTime(&lazy_now, queue), next_possible_run_time);
+
+ if (next_run_time) {
alex clarke (OOO till 29th) 2016/09/15 12:19:35 Can this ever actually be false? Should we DCHECK
altimin 2016/09/15 15:52:10 Yes. We're calling MaybeSchedulePumpQueue when we'
+ MaybeSchedulePumpThrottledTasks(from_here, now, next_run_time.value());
+ }
+}
+
+base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(base::TimeTicks now,
+ TaskQueue* queue) {
+ TimeBudgetPool* time_budget_pool = GetTimeBudgetPoolForQueue(queue);
+ if (!time_budget_pool) {
+ return now;
+ } else {
+ return std::max(now, time_budget_pool->GetNextAllowedRunTime());
+ }
+}
+
} // namespace scheduler
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698