1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef FLUTTER_FML_MESSAGE_LOOP_TASK_QUEUES_H_
6#define FLUTTER_FML_MESSAGE_LOOP_TASK_QUEUES_H_
7
8#include <map>
9#include <mutex>
10#include <vector>
11
12#include "flutter/fml/closure.h"
13#include "flutter/fml/delayed_task.h"
14#include "flutter/fml/macros.h"
15#include "flutter/fml/memory/ref_counted.h"
16#include "flutter/fml/synchronization/shared_mutex.h"
17#include "flutter/fml/wakeable.h"
18
19namespace fml {
20
21class TaskQueueId {
22 public:
23 static const size_t kUnmerged;
24
25 explicit TaskQueueId(size_t value) : value_(value) {}
26
27 operator int() const { return value_; }
28
29 private:
30 size_t value_ = kUnmerged;
31};
32
33static const TaskQueueId _kUnmerged = TaskQueueId(TaskQueueId::kUnmerged);
34
35// This is keyed by the |TaskQueueId| and contains all the queue
36// components that make up a single TaskQueue.
37class TaskQueueEntry {
38 public:
39 using TaskObservers = std::map<intptr_t, fml::closure>;
40 Wakeable* wakeable;
41 TaskObservers task_observers;
42 DelayedTaskQueue delayed_tasks;
43
44 // Note: Both of these can be _kUnmerged, which indicates that
45 // this queue has not been merged or subsumed. OR exactly one
46 // of these will be _kUnmerged, if owner_of is _kUnmerged, it means
47 // that the queue has been subsumed or else it owns another queue.
48 TaskQueueId owner_of;
49 TaskQueueId subsumed_by;
50
51 TaskQueueEntry();
52
53 private:
54 FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TaskQueueEntry);
55};
56
57enum class FlushType {
58 kSingle,
59 kAll,
60};
61
62// This class keeps track of all the tasks and observers that
63// need to be run on it's MessageLoopImpl. This also wakes up the
64// loop at the required times.
65class MessageLoopTaskQueues
66 : public fml::RefCountedThreadSafe<MessageLoopTaskQueues> {
67 public:
68 // Lifecycle.
69
70 static fml::RefPtr<MessageLoopTaskQueues> GetInstance();
71
72 TaskQueueId CreateTaskQueue();
73
74 void Dispose(TaskQueueId queue_id);
75
76 void DisposeTasks(TaskQueueId queue_id);
77
78 // Tasks methods.
79
80 void RegisterTask(TaskQueueId queue_id,
81 const fml::closure& task,
82 fml::TimePoint target_time);
83
84 bool HasPendingTasks(TaskQueueId queue_id) const;
85
86 void GetTasksToRunNow(TaskQueueId queue_id,
87 FlushType type,
88 std::vector<fml::closure>& invocations);
89
90 size_t GetNumPendingTasks(TaskQueueId queue_id) const;
91
92 // Observers methods.
93
94 void AddTaskObserver(TaskQueueId queue_id,
95 intptr_t key,
96 const fml::closure& callback);
97
98 void RemoveTaskObserver(TaskQueueId queue_id, intptr_t key);
99
100 std::vector<fml::closure> GetObserversToNotify(TaskQueueId queue_id) const;
101
102 // Misc.
103
104 void SetWakeable(TaskQueueId queue_id, fml::Wakeable* wakeable);
105
106 // Invariants for merge and un-merge
107 // 1. RegisterTask will always submit to the queue_id that is passed
108 // to it. It is not aware of whether a queue is merged or not. Same with
109 // task observers.
110 // 2. When we get the tasks to run now, we look at both the queue_ids
111 // for the owner, subsumed will spin.
112 // 3. Each task queue can only be merged and subsumed once.
113 //
114 // Methods currently aware of the merged state of the queues:
115 // HasPendingTasks, GetTasksToRunNow, GetNumPendingTasks
116
117 // This method returns false if either the owner or subsumed has already been
118 // merged with something else.
119 bool Merge(TaskQueueId owner, TaskQueueId subsumed);
120
121 // Will return false if the owner has not been merged before.
122 bool Unmerge(TaskQueueId owner);
123
124 // Returns true if owner owns the subsumed task queue.
125 bool Owns(TaskQueueId owner, TaskQueueId subsumed) const;
126
127 private:
128 class MergedQueuesRunner;
129
130 MessageLoopTaskQueues();
131
132 ~MessageLoopTaskQueues();
133
134 void WakeUpUnlocked(TaskQueueId queue_id, fml::TimePoint time) const;
135
136 bool HasPendingTasksUnlocked(TaskQueueId queue_id) const;
137
138 const DelayedTask& PeekNextTaskUnlocked(TaskQueueId owner,
139 TaskQueueId& top_queue_id) const;
140
141 fml::TimePoint GetNextWakeTimeUnlocked(TaskQueueId queue_id) const;
142
143 static std::mutex creation_mutex_;
144 static fml::RefPtr<MessageLoopTaskQueues> instance_;
145
146 mutable std::mutex queue_mutex_;
147 std::map<TaskQueueId, std::unique_ptr<TaskQueueEntry>> queue_entries_;
148
149 size_t task_queue_id_counter_;
150
151 std::atomic_int order_;
152
153 FML_FRIEND_MAKE_REF_COUNTED(MessageLoopTaskQueues);
154 FML_FRIEND_REF_COUNTED_THREAD_SAFE(MessageLoopTaskQueues);
155 FML_DISALLOW_COPY_ASSIGN_AND_MOVE(MessageLoopTaskQueues);
156};
157
158} // namespace fml
159
160#endif // FLUTTER_FML_MESSAGE_LOOP_TASK_QUEUES_H_
161