Chromium Code Reviews| 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 1dfe379832dd408fb35d0457eb1b44a146a66a0e..9a153d11621e65a42955aad727675eed5549595c 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 |
| @@ -144,10 +144,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(tracing_category_, "TaskQueueThrottler_TaskQueueUnthrottled", |
| "task_queue", task_queue); |
| @@ -178,10 +180,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 |
| @@ -210,9 +211,20 @@ 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->OnTaskQueueHasWork(queue, now, next_wake_up); |
|
Sami
2017/04/26 13:09:08
Could we also call this OnQueueNextWakeUpChanged b
altimin
2017/04/26 14:31:29
Done.
|
| + } |
| + |
| + base::TimeTicks next_allowed_run_time = |
| + GetNextAllowedRunTime(queue, next_wake_up); |
| + // TODO(altimin): Remove after moving to budget pools completely. |
|
Sami
2017/04/26 13:09:08
Could you expand this comment? I'm not sure what i
altimin
2017/04/26 14:31:29
Done.
|
| 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() { |
| @@ -222,50 +234,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( |
| - tracing_category_, |
| - "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); |
| } |
| } |
| @@ -315,6 +289,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) { |
| @@ -326,18 +307,102 @@ 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; |
| + } |
| + |
| + 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 (CanRunTasksUntil(queue, now, next_desired_run_time.value())) { |
|
Sami
2017/04/27 12:12:30
Reading this more, I guess I'm still a bit confuse
altimin
2017/04/27 12:23:50
Yes, but it will be at least as complex as fence i
|
| + // We can run up until the next task uninterrupted. Remove the fence |
| + // to allow new tasks to run immediately. |
|
Sami
2017/04/26 13:09:08
This is a little confusing: the next task doesn't
altimin
2017/04/26 14:31:29
Yes. Also there can be new tasks coming from diffe
Sami
2017/04/27 12:12:30
I see. I wonder if a fence with a timestamp would
altimin
2017/04/27 12:23:51
See comment above.
|
| + queue->RemoveFence(); |
| + |
| + // TaskQueueThrottler does not schedule wake-ups implicitly, we need |
| + // to be explicit. |
| + if (next_desired_run_time.value() != now) { |
| + time_domain_->SetNextTask(next_desired_run_time.value()); |
|
Sami
2017/04/26 13:09:08
SetNextTaskRunTime? (also, why doesn't this just s
altimin
2017/04/26 14:31:29
ThrottledTimeDomain does not schedule wake-ups (do
|
| + } |
| + 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 = |
|
Sami
2017/04/26 13:09:08
What's the difference between next_desired_run_tim
altimin
2017/04/26 14:31:29
next_desired_run_time takes into account tasks tha
|
| + queue->GetNextScheduledWakeUp(); |
| + if (next_wake_up) { |
| + MaybeSchedulePumpThrottledTasks( |
| + FROM_HERE, now, GetNextAllowedRunTime(queue, next_wake_up.value())); |
| + } |
| return; |
| + } |
| + |
| + // Ensure that correct type of a fence is blocking queue which can't run. |
| + base::Optional<QueueBlockType> block_type = GetQueueBlockType(now, queue); |
| + if (block_type == QueueBlockType::kAllTasks) { |
| + queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); |
| + } else if (block_type == QueueBlockType::kNewTasksOnly && |
| + !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); |
| + } |
| + DCHECK(block_type); |
|
Sami
2017/04/26 13:09:08
nit: DCHECK before actually using |block_type|
Sami
2017/04/27 12:12:30
Missed this?
altimin
2017/04/27 12:23:51
Done, forgot to reply.
|
| - queue->InsertFence(TaskQueue::InsertFencePosition::BEGINNING_OF_TIME); |
| - SchedulePumpQueue(FROM_HERE, now, queue); |
| + // Schedule a pump. |
| + base::TimeTicks next_run_time = |
| + GetNextAllowedRunTime(queue, next_desired_run_time.value()); |
| + 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; |
| + // GetQueueBlockType() == QueueBlockType::kNewTasksOnly |
|
Sami
2017/04/26 13:09:08
DCHECK?
altimin
2017/04/26 14:31:29
Done.
|
| + has_new_tasks_only_block = true; |
|
Sami
2017/04/26 13:09:08
break?
altimin
2017/04/26 14:31:29
No, we can still encounter QueueBlockType::kAllTas
Sami
2017/04/27 12:12:30
Ah got it.
|
| + } |
| + } |
| + |
| + if (has_new_tasks_only_block) |
| + return QueueBlockType::kNewTasksOnly; |
| + return base::nullopt; |
| } |
| void TaskQueueThrottler::AsValueInto(base::trace_event::TracedValue* state, |
| @@ -398,43 +463,51 @@ 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 true; |
| + for (BudgetPool* budget_pool : find_it->second.budget_pools) { |
| + if (!budget_pool->CanRunTasksAt(moment, is_wake_up)) |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool TaskQueueThrottler::CanRunTasksUntil(TaskQueue* queue, |
| + base::TimeTicks now, |
| + base::TimeTicks moment) { |
| 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->CanRunTasksUntil(now, moment)) |
| + return false; |
| } |
| - return next_run_time; |
| + return true; |
| } |
| void TaskQueueThrottler::MaybeDeleteQueueMetadata(TaskQueueMap::iterator it) { |
| @@ -483,7 +556,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(tracing_category_, "TaskQueueThrottler_EnableThrottling"); |