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"); |