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

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

Issue 2778123003: [scheduler] Add WakeupBudgetPool. (Closed)
Patch Set: Addressed comments Created 3 years, 7 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/task_queue_throttler.cc b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
index 7191ec4ad9eea251c68ae1096dc226c94eb8b0de..1c3cd00bfe7a0303b2cde56241e11283888b371b 100644
--- a/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
+++ b/third_party/WebKit/Source/platform/scheduler/renderer/task_queue_throttler.cc
@@ -142,10 +142,12 @@ void TaskQueueThrottler::IncreaseThrottleRefCount(TaskQueue* task_queue) {
void TaskQueueThrottler::DecreaseThrottleRefCount(TaskQueue* task_queue) {
TaskQueueMap::iterator iter = queue_details_.find(task_queue);
- if (iter == queue_details_.end() ||
- --iter->second.throttling_ref_count != 0) {
+ if (iter == queue_details_.end())
+ return;
+ if (iter->second.throttling_ref_count == 0)
+ return;
+ if (--iter->second.throttling_ref_count != 0)
return;
- }
TRACE_EVENT1("renderer.scheduler", "TaskQueueThrottler_TaskQueueUnthrottled",
"task_queue", task_queue);
@@ -176,10 +178,9 @@ void TaskQueueThrottler::UnregisterTaskQueue(TaskQueue* task_queue) {
if (find_it == queue_details_.end())
return;
- LazyNow lazy_now(tick_clock_);
std::unordered_set<BudgetPool*> budget_pools = find_it->second.budget_pools;
for (BudgetPool* budget_pool : budget_pools) {
- budget_pool->RemoveQueue(lazy_now.Now(), task_queue);
+ budget_pool->UnregisterQueue(task_queue);
}
// Iterator may have been deleted by BudgetPool::RemoveQueue, so don't
@@ -208,9 +209,21 @@ void TaskQueueThrottler::OnQueueNextWakeUpChanged(
return;
base::TimeTicks now = tick_clock_->NowTicks();
+
+ auto find_it = queue_details_.find(queue);
+ if (find_it == queue_details_.end())
+ return;
+
+ for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+ budget_pool->OnQueueNextWakeUpChanged(queue, now, next_wake_up);
+ }
+
+ // TODO(altimin): This probably can be removed —- budget pools should
+ // schedule this.
+ base::TimeTicks next_allowed_run_time =
+ GetNextAllowedRunTime(queue, next_wake_up);
MaybeSchedulePumpThrottledTasks(
- FROM_HERE, now,
- std::max(GetNextAllowedRunTime(now, queue), next_wake_up));
+ FROM_HERE, now, std::max(next_wake_up, next_allowed_run_time));
}
void TaskQueueThrottler::PumpThrottledTasks() {
@@ -220,50 +233,12 @@ void TaskQueueThrottler::PumpThrottledTasks() {
LazyNow lazy_now(tick_clock_);
base::Optional<base::TimeTicks> next_scheduled_delayed_task;
+ for (const auto& pair : budget_pools_)
+ pair.first->OnWakeUp(lazy_now.Now());
+
for (const TaskQueueMap::value_type& map_entry : queue_details_) {
TaskQueue* task_queue = map_entry.first;
- if (task_queue->IsEmpty() || !IsThrottled(task_queue))
- continue;
-
- // Don't enable queues whose budget pool doesn't allow them to run now.
- base::TimeTicks next_allowed_run_time =
- 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",
- "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled",
- "throttle_time_in_seconds",
- (next_allowed_run_time - next_desired_run_time.value()).InSecondsF());
-
- // Schedule a pump for queue which was disabled because of time budget.
- next_scheduled_delayed_task =
- Min(next_scheduled_delayed_task, next_allowed_run_time);
-
- continue;
- }
-
- next_scheduled_delayed_task =
- Min(next_scheduled_delayed_task, task_queue->GetNextScheduledWakeUp());
-
- if (next_allowed_run_time > lazy_now.Now())
- continue;
-
- // Remove previous fence and install a new one, allowing all tasks posted
- // on |task_queue| up until this point to run and block all further tasks.
- task_queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
- }
-
- // 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);
+ UpdateQueueThrottlingStateInternal(lazy_now.Now(), task_queue, true);
}
}
@@ -313,6 +288,13 @@ CPUTimeBudgetPool* TaskQueueThrottler::CreateCPUTimeBudgetPool(
return time_budget_pool;
}
+WakeUpBudgetPool* TaskQueueThrottler::CreateWakeUpBudgetPool(const char* name) {
+ WakeUpBudgetPool* wake_up_budget_pool =
+ new WakeUpBudgetPool(name, this, tick_clock_->NowTicks());
+ budget_pools_[wake_up_budget_pool] = base::WrapUnique(wake_up_budget_pool);
+ return wake_up_budget_pool;
+}
+
void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue,
base::TimeTicks start_time,
base::TimeTicks end_time) {
@@ -324,18 +306,120 @@ void TaskQueueThrottler::OnTaskRunTimeReported(TaskQueue* task_queue,
return;
for (BudgetPool* budget_pool : find_it->second.budget_pools) {
- budget_pool->RecordTaskRunTime(start_time, end_time);
- if (!budget_pool->HasEnoughBudgetToRun(end_time))
- budget_pool->BlockThrottledQueues(end_time);
+ budget_pool->RecordTaskRunTime(task_queue, start_time, end_time);
}
}
-void TaskQueueThrottler::BlockQueue(base::TimeTicks now, TaskQueue* queue) {
- if (!IsThrottled(queue))
+void TaskQueueThrottler::UpdateQueueThrottlingState(base::TimeTicks now,
+ TaskQueue* queue) {
+ UpdateQueueThrottlingStateInternal(now, queue, false);
+}
+
+void TaskQueueThrottler::UpdateQueueThrottlingStateInternal(base::TimeTicks now,
+ TaskQueue* queue,
+ bool is_wake_up) {
+ if (!queue->IsQueueEnabled() || !IsThrottled(queue)) {
return;
+ }
- queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
- SchedulePumpQueue(FROM_HERE, now, queue);
+ LazyNow lazy_now(now);
+
+ base::Optional<base::TimeTicks> next_desired_run_time =
+ NextTaskRunTime(&lazy_now, queue);
+
+ if (!next_desired_run_time) {
+ // This queue is empty. Given that new task can arrive at any moment,
+ // block the queue completely and update the state upon the notification
+ // about a new task.
+ queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
+ return;
+ }
+
+ if (CanRunTasksAt(queue, now, false) &&
+ CanRunTasksAt(queue, next_desired_run_time.value(), false)) {
+ // We can run up until the next task uninterrupted unless something changes.
+ // Remove the fence to allow new tasks to run immediately and handle
+ // the situation change in the notification about the said change.
+ queue->RemoveFence();
+
+ // TaskQueueThrottler does not schedule wake-ups implicitly, we need
+ // to be explicit.
+ if (next_desired_run_time.value() != now) {
+ time_domain_->SetNextTaskRunTime(next_desired_run_time.value());
+ }
+ return;
+ }
+
+ if (CanRunTasksAt(queue, now, is_wake_up)) {
+ // We can run task now, but we can't run until the next scheduled task.
+ // Insert a fresh fence to unblock queue and schedule a pump for the
+ // next wake-up.
+ queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
+
+ base::Optional<base::TimeTicks> next_wake_up =
+ queue->GetNextScheduledWakeUp();
+ if (next_wake_up) {
+ MaybeSchedulePumpThrottledTasks(
+ FROM_HERE, now, GetNextAllowedRunTime(queue, next_wake_up.value()));
+ }
+ return;
+ }
+
+ base::TimeTicks next_run_time =
+ GetNextAllowedRunTime(queue, next_desired_run_time.value());
+
+ // Insert a fence of an approriate type.
+ base::Optional<QueueBlockType> block_type = GetQueueBlockType(now, queue);
+ DCHECK(block_type);
+
+ switch (block_type.value()) {
+ case QueueBlockType::kAllTasks:
+ queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
+
+ {
+ // Braces limit the scope for a declared variable. Does not compile
+ // otherwise.
+ TRACE_EVENT1(
+ "renderer.scheduler",
+ "TaskQueueThrottler::PumpThrottledTasks_ExpensiveTaskThrottled",
+ "throttle_time_in_seconds",
+ (next_run_time - next_desired_run_time.value()).InSecondsF());
+ }
+ break;
+ case QueueBlockType::kNewTasksOnly:
+ if (!queue->HasFence()) {
+ // Insert a new non-fully blocking fence only when there is no fence
+ // already in order avoid undesired unblocking of old tasks.
+ queue->InsertFence(TaskQueue::InsertFencePosition::NOW);
+ }
+ break;
+ }
+
+ // Schedule a pump.
+ MaybeSchedulePumpThrottledTasks(FROM_HERE, now, next_run_time);
+}
+
+base::Optional<QueueBlockType> TaskQueueThrottler::GetQueueBlockType(
+ base::TimeTicks now,
+ TaskQueue* queue) {
+ auto find_it = queue_details_.find(queue);
+ if (find_it == queue_details_.end())
+ return base::nullopt;
+
+ bool has_new_tasks_only_block = false;
+
+ for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+ if (!budget_pool->CanRunTasksAt(now, false)) {
+ if (budget_pool->GetBlockType() == QueueBlockType::kAllTasks)
+ return QueueBlockType::kAllTasks;
+ DCHECK_EQ(budget_pool->GetBlockType(), QueueBlockType::kNewTasksOnly);
+ has_new_tasks_only_block = true;
+ }
+ }
+
+ if (has_new_tasks_only_block)
+ return QueueBlockType::kNewTasksOnly;
+ return base::nullopt;
}
void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state,
@@ -396,43 +480,36 @@ void TaskQueueThrottler::UnregisterBudgetPool(BudgetPool* budget_pool) {
budget_pools_.erase(budget_pool);
}
-void TaskQueueThrottler::UnblockQueue(base::TimeTicks now, TaskQueue* queue) {
- SchedulePumpQueue(FROM_HERE, now, queue);
-}
-
-void TaskQueueThrottler::SchedulePumpQueue(
- const tracked_objects::Location& from_here,
- base::TimeTicks now,
- TaskQueue* queue) {
- if (!IsThrottled(queue))
- return;
+base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(
+ TaskQueue* queue,
+ base::TimeTicks desired_run_time) {
+ base::TimeTicks next_run_time = desired_run_time;
- LazyNow lazy_now(now);
- base::Optional<base::TimeTicks> next_desired_run_time =
- NextTaskRunTime(&lazy_now, queue);
- if (!next_desired_run_time)
- return;
+ auto find_it = queue_details_.find(queue);
+ if (find_it == queue_details_.end())
+ return next_run_time;
- base::Optional<base::TimeTicks> next_run_time =
- Max(next_desired_run_time, GetNextAllowedRunTime(now, queue));
+ for (BudgetPool* budget_pool : find_it->second.budget_pools) {
+ next_run_time = std::max(
+ next_run_time, budget_pool->GetNextAllowedRunTime(desired_run_time));
+ }
- MaybeSchedulePumpThrottledTasks(from_here, now, next_run_time.value());
+ return next_run_time;
}
-base::TimeTicks TaskQueueThrottler::GetNextAllowedRunTime(base::TimeTicks now,
- TaskQueue* queue) {
- base::TimeTicks next_run_time = now;
-
+bool TaskQueueThrottler::CanRunTasksAt(TaskQueue* queue,
+ base::TimeTicks moment,
+ bool is_wake_up) {
auto find_it = queue_details_.find(queue);
if (find_it == queue_details_.end())
- return next_run_time;
+ return true;
for (BudgetPool* budget_pool : find_it->second.budget_pools) {
- next_run_time =
- std::max(next_run_time, budget_pool->GetNextAllowedRunTime());
+ if (!budget_pool->CanRunTasksAt(moment, is_wake_up))
+ return false;
}
- return next_run_time;
+ return true;
}
void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) {
@@ -481,7 +558,7 @@ void TaskQueueThrottler::EnableThrottling() {
// to enforce task alignment.
queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME);
queue->SetTimeDomain(time_domain_.get());
- SchedulePumpQueue(FROM_HERE, lazy_now.Now(), queue);
+ UpdateQueueThrottlingState(lazy_now.Now(), queue);
}
TRACE_EVENT0("renderer.scheduler", "TaskQueueThrottler_EnableThrottling");

Powered by Google App Engine
This is Rietveld 408576698