| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_TASK_QUEUE_THROTTLER_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_TASK_QUEUE_THROTTLER_H_ |
| |
| #include <set> |
| #include <unordered_map> |
| |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/optional.h" |
| #include "base/threading/thread_checker.h" |
| #include "third_party/blink/renderer/platform/platform_export.h" |
| #include "third_party/blink/renderer/platform/scheduler/base/task_queue.h" |
| #include "third_party/blink/renderer/platform/scheduler/base/time_domain.h" |
| #include "third_party/blink/renderer/platform/scheduler/common/cancelable_closure_holder.h" |
| #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h" |
| #include "third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.h" |
| #include "third_party/blink/renderer/platform/scheduler/common/throttling/wake_up_budget_pool.h" |
| #include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h" |
| |
| namespace base { |
| namespace trace_event { |
| class TracedValue; |
| } |
| } // namespace base |
| |
| namespace blink { |
| namespace scheduler { |
| |
| class BudgetPool; |
| class ThreadSchedulerImpl; |
| class ThrottledTimeDomain; |
| class CPUTimeBudgetPool; |
| class WakeUpBudgetPool; |
| |
| // kNewTasksOnly prevents new tasks from running (old tasks can run normally), |
| // kAllTasks block queue completely. |
| // kAllTasks-type block always blocks the queue completely. |
| // kNewTasksOnly-type block does nothing when queue is already blocked by |
| // kAllTasks, and overrides previous kNewTasksOnly block if any, which may |
| // unblock some tasks. |
| enum class QueueBlockType { kAllTasks, kNewTasksOnly }; |
| |
| // Interface for BudgetPool to interact with TaskQueueThrottler. |
| class PLATFORM_EXPORT BudgetPoolController { |
| public: |
| virtual ~BudgetPoolController() = default; |
| |
| // To be used by BudgetPool only, use BudgetPool::{Add,Remove}Queue |
| // methods instead. |
| virtual void AddQueueToBudgetPool(base::sequence_manager::TaskQueue* queue, |
| BudgetPool* budget_pool) = 0; |
| virtual void RemoveQueueFromBudgetPool( |
| base::sequence_manager::TaskQueue* queue, |
| BudgetPool* budget_pool) = 0; |
| |
| // Deletes the budget pool. |
| virtual void UnregisterBudgetPool(BudgetPool* budget_pool) = 0; |
| |
| // Ensure that an appropriate type of the fence is installed and schedule |
| // a pump for this queue when needed. |
| virtual void UpdateQueueSchedulingLifecycleState( |
| base::TimeTicks now, |
| base::sequence_manager::TaskQueue* queue) = 0; |
| |
| // Returns true if the |queue| is throttled (i.e. added to TaskQueueThrottler |
| // and throttling is not disabled). |
| virtual bool IsThrottled(base::sequence_manager::TaskQueue* queue) const = 0; |
| }; |
| |
| // The job of the TaskQueueThrottler is to control when tasks posted on |
| // throttled queues get run. The TaskQueueThrottler: |
| // - runs throttled tasks once per second, |
| // - controls time budget for task queues grouped in CPUTimeBudgetPools. |
| // |
| // This is done by disabling throttled queues and running |
| // a special "heart beat" function |PumpThrottledTasks| which when run |
| // temporarily enables throttled queues and inserts a fence to ensure tasks |
| // posted from a throttled task run next time the queue is pumped. |
| // |
| // Of course the TaskQueueThrottler isn't the only sub-system that wants to |
| // enable or disable queues. E.g. ThreadSchedulerImpl also does this for |
| // policy reasons. To prevent the systems from fighting, clients of |
| // TaskQueueThrottler must use SetQueueEnabled rather than calling the function |
| // directly on the queue. |
| // |
| // There may be more than one system that wishes to throttle a queue (e.g. |
| // renderer suspension vs tab level suspension) so the TaskQueueThrottler keeps |
| // a count of the number of systems that wish a queue to be throttled. |
| // See IncreaseThrottleRefCount & DecreaseThrottleRefCount. |
| // |
| // This class is main-thread only. |
| class PLATFORM_EXPORT TaskQueueThrottler |
| : public base::sequence_manager::TaskQueue::Observer, |
| public BudgetPoolController { |
| public: |
| // We use tracing controller from ThreadSchedulerImpl because an instance |
| // of this class is always its member, so has the same lifetime. |
| TaskQueueThrottler(ThreadSchedulerImpl* thread_scheduler, |
| TraceableVariableController* tracing_controller); |
| |
| ~TaskQueueThrottler() override; |
| |
| // TaskQueue::Observer implementation: |
| void OnQueueNextWakeUpChanged(base::sequence_manager::TaskQueue* queue, |
| base::TimeTicks wake_up) override; |
| |
| // BudgetPoolController implementation: |
| void AddQueueToBudgetPool(base::sequence_manager::TaskQueue* queue, |
| BudgetPool* budget_pool) override; |
| void RemoveQueueFromBudgetPool(base::sequence_manager::TaskQueue* queue, |
| BudgetPool* budget_pool) override; |
| void UnregisterBudgetPool(BudgetPool* budget_pool) override; |
| void UpdateQueueSchedulingLifecycleState( |
| base::TimeTicks now, |
| base::sequence_manager::TaskQueue* queue) override; |
| bool IsThrottled(base::sequence_manager::TaskQueue* queue) const override; |
| |
| // Increments the throttled refcount and causes |task_queue| to be throttled |
| // if its not already throttled. |
| void IncreaseThrottleRefCount(base::sequence_manager::TaskQueue* task_queue); |
| |
| // If the refcouint is non-zero it's decremented. If the throttled refcount |
| // becomes zero then |task_queue| is unthrottled. If the refcount was already |
| // zero this function does nothing. |
| void DecreaseThrottleRefCount(base::sequence_manager::TaskQueue* task_queue); |
| |
| // Removes |task_queue| from |queue_details| and from appropriate budget pool. |
| void ShutdownTaskQueue(base::sequence_manager::TaskQueue* task_queue); |
| |
| // Disable throttling for all queues, this setting takes precedence over |
| // all other throttling settings. Designed to be used when a global event |
| // disabling throttling happens (e.g. audio is playing). |
| void DisableThrottling(); |
| |
| // Enable back global throttling. |
| void EnableThrottling(); |
| |
| const ThrottledTimeDomain* time_domain() const { return time_domain_.get(); } |
| |
| // TODO(altimin): Remove it. |
| static base::TimeTicks AlignedThrottledRunTime( |
| base::TimeTicks unthrottled_runtime); |
| |
| // Returned object is owned by |TaskQueueThrottler|. |
| CPUTimeBudgetPool* CreateCPUTimeBudgetPool(const char* name); |
| WakeUpBudgetPool* CreateWakeUpBudgetPool(const char* name); |
| |
| // Accounts for given task for cpu-based throttling needs. |
| void OnTaskRunTimeReported(base::sequence_manager::TaskQueue* task_queue, |
| base::TimeTicks start_time, |
| base::TimeTicks end_time); |
| |
| void AsValueInto(base::trace_event::TracedValue* state, |
| base::TimeTicks now) const; |
| |
| private: |
| struct Metadata { |
| Metadata() : throttling_ref_count(0) {} |
| |
| size_t throttling_ref_count; |
| |
| std::unordered_set<BudgetPool*> budget_pools; |
| }; |
| using TaskQueueMap = |
| std::unordered_map<base::sequence_manager::TaskQueue*, Metadata>; |
| |
| void PumpThrottledTasks(); |
| |
| // Note |unthrottled_runtime| might be in the past. When this happens we |
| // compute the delay to the next runtime based on now rather than |
| // unthrottled_runtime. |
| void MaybeSchedulePumpThrottledTasks(const base::Location& from_here, |
| base::TimeTicks now, |
| base::TimeTicks runtime); |
| |
| // Return next possible time when queue is allowed to run in accordance |
| // with throttling policy. |
| base::TimeTicks GetNextAllowedRunTime( |
| base::sequence_manager::TaskQueue* queue, |
| base::TimeTicks desired_run_time); |
| |
| bool CanRunTasksAt(base::sequence_manager::TaskQueue* queue, |
| base::TimeTicks moment, |
| bool is_wake_up); |
| |
| base::Optional<base::TimeTicks> GetTimeTasksCanRunUntil( |
| base::sequence_manager::TaskQueue* queue, |
| base::TimeTicks now, |
| bool is_wake_up) const; |
| |
| void MaybeDeleteQueueMetadata(TaskQueueMap::iterator it); |
| |
| void UpdateQueueSchedulingLifecycleStateInternal( |
| base::TimeTicks now, |
| base::sequence_manager::TaskQueue* queue, |
| bool is_wake_up); |
| |
| base::Optional<QueueBlockType> GetQueueBlockType( |
| base::TimeTicks now, |
| base::sequence_manager::TaskQueue* queue); |
| |
| TaskQueueMap queue_details_; |
| base::RepeatingCallback<void(base::sequence_manager::TaskQueue*, |
| base::TimeTicks)> |
| forward_immediate_work_callback_; |
| scoped_refptr<base::SingleThreadTaskRunner> control_task_runner_; |
| ThreadSchedulerImpl* thread_scheduler_; // NOT OWNED |
| TraceableVariableController* tracing_controller_; // NOT OWNED |
| const base::TickClock* tick_clock_; // NOT OWNED |
| std::unique_ptr<ThrottledTimeDomain> time_domain_; |
| |
| CancelableClosureHolder pump_throttled_tasks_closure_; |
| base::Optional<base::TimeTicks> pending_pump_throttled_tasks_runtime_; |
| bool allow_throttling_; |
| |
| std::unordered_map<BudgetPool*, std::unique_ptr<BudgetPool>> budget_pools_; |
| |
| base::WeakPtrFactory<TaskQueueThrottler> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TaskQueueThrottler); |
| }; |
| |
| } // namespace scheduler |
| } // namespace blink |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_THROTTLING_TASK_QUEUE_THROTTLER_H_ |