// Copyright 2013 The Flutter 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 FLUTTER_FML_MESSAGE_LOOP_TASK_QUEUES_H_ #define FLUTTER_FML_MESSAGE_LOOP_TASK_QUEUES_H_ #include #include #include #include #include #include "flutter/fml/closure.h" #include "flutter/fml/delayed_task.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/ref_counted.h" #include "flutter/fml/synchronization/shared_mutex.h" #include "flutter/fml/task_queue_id.h" #include "flutter/fml/task_source.h" #include "flutter/fml/wakeable.h" namespace fml { static const TaskQueueId _kUnmerged = TaskQueueId(TaskQueueId::kUnmerged); /// A collection of tasks and observers associated with one TaskQueue. /// /// Often a TaskQueue has a one-to-one relationship with a fml::MessageLoop, /// this isn't the case when TaskQueues are merged via /// \p fml::MessageLoopTaskQueues::Merge. class TaskQueueEntry { public: using TaskObservers = std::map; Wakeable* wakeable; TaskObservers task_observers; std::unique_ptr task_source; /// Set of the TaskQueueIds which is owned by this TaskQueue. If the set is /// empty, this TaskQueue does not own any other TaskQueues. std::set owner_of; /// Identifies the TaskQueue that subsumes this TaskQueue. If it is _kUnmerged /// it indicates that this TaskQueue is not owned by any other TaskQueue. TaskQueueId subsumed_by; TaskQueueId created_for; explicit TaskQueueEntry(TaskQueueId created_for); private: FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TaskQueueEntry); }; enum class FlushType { kSingle, kAll, }; /// A singleton container for all tasks and observers associated with all /// fml::MessageLoops. /// /// This also wakes up the loop at the required times. /// \see fml::MessageLoop /// \see fml::Wakeable class MessageLoopTaskQueues { public: // Lifecycle. static MessageLoopTaskQueues* GetInstance(); TaskQueueId CreateTaskQueue(); void Dispose(TaskQueueId queue_id); void DisposeTasks(TaskQueueId queue_id); // Tasks methods. void RegisterTask(TaskQueueId queue_id, const fml::closure& task, fml::TimePoint target_time, fml::TaskSourceGrade task_source_grade = fml::TaskSourceGrade::kUnspecified); bool HasPendingTasks(TaskQueueId queue_id) const; fml::closure GetNextTaskToRun(TaskQueueId queue_id, fml::TimePoint from_time); size_t GetNumPendingTasks(TaskQueueId queue_id) const; static TaskSourceGrade GetCurrentTaskSourceGrade(); // Observers methods. void AddTaskObserver(TaskQueueId queue_id, intptr_t key, const fml::closure& callback); void RemoveTaskObserver(TaskQueueId queue_id, intptr_t key); std::vector GetObserversToNotify(TaskQueueId queue_id) const; // Misc. void SetWakeable(TaskQueueId queue_id, fml::Wakeable* wakeable); // Invariants for merge and un-merge // 1. RegisterTask will always submit to the queue_id that is passed // to it. It is not aware of whether a queue is merged or not. Same with // task observers. // 2. When we get the tasks to run now, we look at both the queue_ids // for the owner and the subsumed task queues. // 3. One TaskQueue can subsume multiple other TaskQueues. A TaskQueue can be // in exactly one of the following three states: // a. Be an owner of multiple other TaskQueues. // b. Be subsumed by a TaskQueue (an owner can never be subsumed). // c. Be independent, i.e, neither owner nor be subsumed. // // Methods currently aware of the merged state of the queues: // HasPendingTasks, GetNextTaskToRun, GetNumPendingTasks bool Merge(TaskQueueId owner, TaskQueueId subsumed); // Will return false if the owner has not been merged before, or owner was // subsumed by others, or subsumed wasn't subsumed by others, or owner // didn't own the given subsumed queue id. bool Unmerge(TaskQueueId owner, TaskQueueId subsumed); /// Returns \p true if \p owner owns the \p subsumed task queue. bool Owns(TaskQueueId owner, TaskQueueId subsumed) const; // Returns the subsumed task queue if any or |TaskQueueId::kUnmerged| // otherwise. std::set GetSubsumedTaskQueueId(TaskQueueId owner) const; void PauseSecondarySource(TaskQueueId queue_id); void ResumeSecondarySource(TaskQueueId queue_id); private: class MergedQueuesRunner; MessageLoopTaskQueues(); ~MessageLoopTaskQueues(); void WakeUpUnlocked(TaskQueueId queue_id, fml::TimePoint time) const; bool HasPendingTasksUnlocked(TaskQueueId queue_id) const; TaskSource::TopTask PeekNextTaskUnlocked(TaskQueueId owner) const; fml::TimePoint GetNextWakeTimeUnlocked(TaskQueueId queue_id) const; mutable std::mutex queue_mutex_; std::map> queue_entries_; size_t task_queue_id_counter_; std::atomic_int order_; FML_DISALLOW_COPY_ASSIGN_AND_MOVE(MessageLoopTaskQueues); }; } // namespace fml #endif // FLUTTER_FML_MESSAGE_LOOP_TASK_QUEUES_H_