| // Copyright 2018 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_H_ | |
| #define BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_H_ | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <type_traits> | |
| #include <utility> | |
| #include <vector> | |
| #include "base/base_export.h" | |
| #include "base/compiler_specific.h" | |
| #include "base/dcheck_is_on.h" | |
| #include "base/memory/raw_ptr.h" | |
| #include "base/message_loop/message_pump_type.h" | |
| #include "base/task/sequence_manager/task_queue_impl.h" | |
| #include "base/task/sequence_manager/task_time_observer.h" | |
| #include "base/task/sequenced_task_runner.h" | |
| #include "base/task/single_thread_task_runner.h" | |
| #include "base/task/thread_type.h" | |
| #include "base/time/default_tick_clock.h" | |
| namespace base { | |
| class MessagePump; | |
| class TaskObserver; | |
| namespace sequence_manager { | |
| class TimeDomain; | |
| // SequenceManager manages TaskQueues which have different properties | |
| // (e.g. priority, common task type) multiplexing all posted tasks into | |
| // a single backing sequence (currently bound to a single thread, which is | |
| // refererred as *main thread* in the comments below). SequenceManager | |
| // implementation can be used in a various ways to apply scheduling logic. | |
| class BASE_EXPORT SequenceManager { | |
| public: | |
| class Observer { | |
| public: | |
| virtual ~Observer() = default; | |
| // Called back on the main thread. | |
| virtual void OnBeginNestedRunLoop() = 0; | |
| virtual void OnExitNestedRunLoop() = 0; | |
| }; | |
| class BASE_EXPORT PrioritySettings { | |
| public: | |
| using ThreadTypeMapping = ThreadType (*)(TaskQueue::QueuePriority); | |
| // This limit is based on an implementation detail of `TaskQueueSelector`'s | |
| // `ActivePriorityTracker`, which can be refactored if more priorities are | |
| // needed. | |
| static constexpr size_t kMaxPriorities = sizeof(size_t) * 8 - 1; | |
| static PrioritySettings CreateDefault(); | |
| static ThreadType DefaultTaskPriorityToThreadType( | |
| TaskQueue::QueuePriority priority); | |
| template <typename T> | |
| requires(std::is_enum_v<T>) | |
| PrioritySettings(T priority_count, T default_priority) | |
| : PrioritySettings( | |
| static_cast<TaskQueue::QueuePriority>(priority_count), | |
| static_cast<TaskQueue::QueuePriority>(default_priority)) { | |
| static_assert( | |
| std::is_same_v<std::underlying_type_t<T>, TaskQueue::QueuePriority>, | |
| "Enumerated priorites must have the same underlying type as " | |
| "TaskQueue::QueuePriority"); | |
| } | |
| PrioritySettings(TaskQueue::QueuePriority priority_count, | |
| TaskQueue::QueuePriority default_priority); | |
| ~PrioritySettings(); | |
| PrioritySettings(PrioritySettings&&) noexcept; | |
| PrioritySettings& operator=(PrioritySettings&&); | |
| TaskQueue::QueuePriority priority_count() const { return priority_count_; } | |
| TaskQueue::QueuePriority default_priority() const { | |
| return default_priority_; | |
| } | |
| void SetProtoPriorityConverter( | |
| perfetto::protos::pbzero::SequenceManagerTask::Priority ( | |
| *proto_priority_converter)(TaskQueue::QueuePriority)) { | |
| proto_priority_converter_ = proto_priority_converter; | |
| } | |
| // Sets a mapping functions from custom priority to ThreadType, which | |
| // will be returned by internal::GetCurrentTaskImportance(). | |
| void SetThreadTypeMapping(ThreadTypeMapping thread_type_mapping) { | |
| thread_type_mapping_ = thread_type_mapping; | |
| } | |
| perfetto::protos::pbzero::SequenceManagerTask::Priority TaskPriorityToProto( | |
| TaskQueue::QueuePriority priority) const; | |
| // Returns the ThreadType corresponding to `priority`, using the mapping | |
| // provided via SetThreadTypeMapping() if applicable. | |
| ThreadType TaskPriorityToThreadType( | |
| TaskQueue::QueuePriority priority) const; | |
| private: | |
| TaskQueue::QueuePriority priority_count_; | |
| TaskQueue::QueuePriority default_priority_; | |
| perfetto::protos::pbzero::SequenceManagerTask::Priority ( | |
| *proto_priority_converter_)(TaskQueue::QueuePriority) = nullptr; | |
| ThreadTypeMapping thread_type_mapping_ = &DefaultTaskPriorityToThreadType; | |
| }; | |
| // Settings defining the desired SequenceManager behaviour. | |
| struct BASE_EXPORT Settings { | |
| class Builder; | |
| Settings(); | |
| Settings(const Settings&) = delete; | |
| Settings& operator=(const Settings&) = delete; | |
| // In the future MessagePump (which is move-only) will also be a setting, | |
| // so we are making Settings move-only in preparation. | |
| Settings(Settings&& move_from) noexcept; | |
| ~Settings(); | |
| MessagePumpType message_loop_type = MessagePumpType::DEFAULT; | |
| // Whether or not CPU time should be sampled for a fixed percentage of | |
| // tasks. | |
| bool sample_cpu_time = false; | |
| raw_ptr<const TickClock, DanglingUntriaged> clock = | |
| DefaultTickClock::GetInstance(); | |
| // Whether or not queueing timestamp will be added to tasks. | |
| bool add_queue_time_to_tasks = false; | |
| // Whether many tasks may run between each check for native work. | |
| bool can_run_tasks_by_batches = false; | |
| PrioritySettings priority_settings = PrioritySettings::CreateDefault(); | |
| // Whether this sequence manager represents the main thread of the process. | |
| // This is only used to set | |
| // base::SingleThreadTaskRunner::GetMainThreadDefault(). | |
| bool is_main_thread = false; | |
| // If true, tasks posted to this sequence manager should be delayed when a | |
| // Scoped(*)ExecutionFence covering the task type exists. For example | |
| // best-effort tasks would be delayed by ScopedBestEffortExecutionFence. | |
| // (Note: ScopedThreadPoolExecutionFence only ever affects the ThreadPool.) | |
| bool should_block_on_scoped_fences = false; | |
| #if DCHECK_IS_ON() | |
| // If not zero this seeds a PRNG used by the task selection logic to choose | |
| // a random TaskQueue for a given priority rather than the TaskQueue with | |
| // the oldest EnqueueOrder. | |
| uint64_t random_task_selection_seed = 0; | |
| #endif // DCHECK_IS_ON() | |
| }; | |
| virtual ~SequenceManager() = default; | |
| // Binds the SequenceManager and its TaskQueues to the current thread. Should | |
| // only be called once. Note that CreateSequenceManagerOnCurrentThread() | |
| // performs this initialization automatically. | |
| virtual void BindToCurrentThread() = 0; | |
| // Finishes the initialization for a SequenceManager created via | |
| // CreateUnboundSequenceManager(). Must not be called in any other | |
| // circumstances. The ownership of the pump is transferred to SequenceManager. | |
| virtual void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) = 0; | |
| // Gets a pointer to the message pump that this sequence manager is bound to, | |
| // if any. | |
| virtual MessagePump* GetMessagePump() const = 0; | |
| // Must be called on the main thread. | |
| // Can be called only once, before creating TaskQueues. | |
| // Observer must outlive the SequenceManager. | |
| virtual void SetObserver(Observer* observer) = 0; | |
| // Must be called on the main thread. | |
| virtual void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) = 0; | |
| virtual void RemoveTaskTimeObserver(TaskTimeObserver* task_time_observer) = 0; | |
| // Sets `time_domain` to be used by this scheduler and associated task queues. | |
| // Only one time domain can be set at a time. `time_domain` must outlive this | |
| // SequenceManager, even if ResetTimeDomain() is called. This has no effect on | |
| // previously scheduled tasks and it is recommended that `time_domain` be set | |
| // before posting any task to avoid inconsistencies in time. Otherwise, | |
| // replacing `time_domain` is very subtle and should be reserved for developer | |
| // only use cases (e.g. virtual time in devtools) where any flakiness caused | |
| // by a racy time update isn't surprising. | |
| virtual void SetTimeDomain(TimeDomain* time_domain) = 0; | |
| // Disassociates the current `time_domain` and reverts to using | |
| // RealTimeDomain. | |
| virtual void ResetTimeDomain() = 0; | |
| virtual const TickClock* GetTickClock() const = 0; | |
| virtual TimeTicks NowTicks() const = 0; | |
| // Returns a wake-up for the next delayed task which is not ripe for | |
| // execution. If there are no such tasks (immediate tasks don't count), | |
| // returns nullopt. | |
| virtual std::optional<WakeUp> GetNextDelayedWakeUp() const = 0; | |
| // Sets the TaskQueue whose task runner will be returned by | |
| // SingleThreadTaskRunner::GetCurrentDefault on the main thread. | |
| virtual void SetDefaultTaskQueue(TaskQueue* task_queue) = 0; | |
| // Directly sets the SingleThreadTaskRunner that will be returned by | |
| // SingleThreadTaskRunner::GetCurrentDefault on the main thread. Use this only | |
| // when setting a task runner that's not a TaskQueue's default, e.g. one with | |
| // a custom task type. | |
| virtual void SetDefaultTaskRunner( | |
| scoped_refptr<SingleThreadTaskRunner> task_runner, | |
| TaskQueue::QueuePriority priority) = 0; | |
| // Removes all canceled delayed tasks, and considers resizing to fit all | |
| // internal queues. | |
| virtual void ReclaimMemory() = 0; | |
| // Returns true if no tasks were executed in TaskQueues that monitor | |
| // quiescence since the last call to this method. | |
| virtual bool GetAndClearSystemIsQuiescentBit() = 0; | |
| // Set the number of tasks executed in a single SequenceManager invocation. | |
| // Increasing this number reduces the overhead of the tasks dispatching | |
| // logic at the cost of a potentially worse latency. 1 by default. | |
| virtual void SetWorkBatchSize(int work_batch_size) = 0; | |
| // Enables crash keys that can be set in the scope of a task which help | |
| // to identify the culprit if upcoming work results in a crash. | |
| // Key names must be thread-specific to avoid races and corrupted crash dumps. | |
| virtual void EnableCrashKeys(const char* async_stack_crash_key) = 0; | |
| virtual TaskQueue::QueuePriority GetPriorityCount() const = 0; | |
| // Returns all TaskQueues with the priority used for "best-effort" tasks. This | |
| // is the largest value (lowest priority) defined by the PrioritySettings, | |
| // unless that's the default priority, in which case there's no "best-effort" | |
| // priority and the returned list is empty. | |
| virtual std::vector<TaskQueue*> GetBestEffortTaskQueues() = 0; | |
| // Creates a `TaskQueue` and returns a `TaskQueue::Handle`for it. The queue is | |
| // owned by the handle and shut down when the handle is destroyed. Must be | |
| // called on the main thread. | |
| virtual TaskQueue::Handle CreateTaskQueue(const TaskQueue::Spec& spec) = 0; | |
| // Returns true iff this SequenceManager has no immediate work to do. I.e. | |
| // there are no pending non-delayed tasks or delayed tasks that are due to | |
| // run. This method ignores any pending delayed tasks that might have become | |
| // eligible to run since the last task was executed. This is important because | |
| // if it did tests would become flaky depending on the exact timing of this | |
| // call. This is moderately expensive. | |
| virtual bool IsIdleForTesting() = 0; | |
| // The total number of posted tasks that haven't executed yet. | |
| virtual size_t GetPendingTaskCountForTesting() const = 0; | |
| // Returns a JSON string which describes all pending tasks. | |
| virtual std::string DescribeAllPendingTasks() const = 0; | |
| // Adds an observer which reports task execution. Can only be called on the | |
| // same thread that `this` is running on. | |
| virtual void AddTaskObserver(TaskObserver* task_observer) = 0; | |
| // Removes an observer which reports task execution. Can only be called on the | |
| // same thread that `this` is running on. | |
| virtual void RemoveTaskObserver(TaskObserver* task_observer) = 0; | |
| }; | |
| class BASE_EXPORT SequenceManager::Settings::Builder { | |
| public: | |
| Builder(); | |
| ~Builder(); | |
| Builder(Builder&& move_from) noexcept = default; | |
| // Sets the MessagePumpType which is used to create a MessagePump. | |
| Builder& SetMessagePumpType(MessagePumpType message_loop_type); | |
| // Whether or not CPU time will be sampled for tasks at a fixed sampling | |
| // ratio. | |
| Builder& SetShouldSampleCPUTime(bool enable); | |
| // Sets the TickClock the SequenceManager uses to obtain Now. | |
| Builder& SetTickClock(const TickClock* clock); | |
| // Whether or not queueing timestamp will be added to tasks. | |
| Builder& SetAddQueueTimeToTasks(bool add_queue_time_to_tasks); | |
| // Whether many tasks may run between each check for native work. | |
| Builder& SetCanRunTasksByBatches(bool can_run_tasks_by_batches); | |
| Builder& SetPrioritySettings(PrioritySettings settings); | |
| Builder& SetIsMainThread(bool is_main_thread); | |
| // Whether tasks posted to this sequence manager should be delayed when a | |
| // Scoped(*)ExecutionFence covering the task type exists. | |
| Builder& SetShouldBlockOnScopedFences(bool enable); | |
| #if DCHECK_IS_ON() | |
| // If not zero this seeds a PRNG used by the task selection logic to choose a | |
| // random TaskQueue for a given priority rather than the TaskQueue with the | |
| // oldest EnqueueOrder. | |
| Builder& SetRandomTaskSelectionSeed(uint64_t random_task_selection_seed); | |
| #endif // DCHECK_IS_ON() | |
| Settings Build(); | |
| private: | |
| Settings settings_; | |
| }; | |
| // Create a SequenceManager using the given MessagePump on the current thread. | |
| // MessagePump instances can be created with | |
| // MessagePump::CreateMessagePumpForType(). | |
| BASE_EXPORT std::unique_ptr<SequenceManager> | |
| CreateSequenceManagerOnCurrentThreadWithPump( | |
| std::unique_ptr<MessagePump> message_pump, | |
| SequenceManager::Settings settings = SequenceManager::Settings()); | |
| // Create an unbound SequenceManager (typically for a future thread or because | |
| // additional setup is required before binding). The SequenceManager can be | |
| // initialized on the current thread and then needs to be bound and initialized | |
| // on the target thread by calling one of the Bind*() methods. | |
| BASE_EXPORT std::unique_ptr<SequenceManager> CreateUnboundSequenceManager( | |
| SequenceManager::Settings settings = SequenceManager::Settings()); | |
| // Wrapper around SequenceManager::Settings. | |
| // | |
| // If you need `SequenceManager::Settings` in a header file, forward declare | |
| // this `SequenceManagerSettings` instead of including the full | |
| // `sequence_manager.h` header file. This helps avoid increasing compile size. | |
| // For an example of its usage, see base/thread.h. | |
| struct BASE_EXPORT SequenceManagerSettings { | |
| explicit SequenceManagerSettings(SequenceManager::Settings settings); | |
| SequenceManagerSettings(const SequenceManagerSettings&) = delete; | |
| SequenceManagerSettings& operator=(const SequenceManagerSettings&) = delete; | |
| SequenceManager::Settings settings; | |
| }; | |
| } // namespace sequence_manager | |
| } // namespace base | |
| #endif // BASE_TASK_SEQUENCE_MANAGER_SEQUENCE_MANAGER_H_ |