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 #define FML_USED_ON_EMBEDDER
6
7 #include "flutter/shell/platform/embedder/embedder_thread_host.h"
8
9 #include <algorithm>
10
11 #include "flutter/fml/message_loop.h"
12 #include "flutter/shell/platform/embedder/embedder_safe_access.h"
13
14 namespace flutter {
15
16 //------------------------------------------------------------------------------
17 /// @brief Attempts to create a task runner from an embedder task runner
18 /// description. The first boolean in the pair indicate whether the
19 /// embedder specified an invalid task runner description. In this
20 /// case, engine launch must be aborted. If the embedder did not
21 /// specify any task runner, an engine managed task runner and
22 /// thread must be selected instead.
23 ///
24 /// @param[in] description The description
25 ///
26 /// @return A pair that returns if the embedder has specified a task runner
27 /// (null otherwise) and whether to terminate further engine launch.
28 ///
29 static std::pair<bool, fml::RefPtr<EmbedderTaskRunner>>
CreateEmbedderTaskRunner(const FlutterTaskRunnerDescription * description)30 CreateEmbedderTaskRunner(const FlutterTaskRunnerDescription* description) {
31 if (description == nullptr) {
32 // This is not embedder error. The embedder API will just have to create a
33 // plain old task runner (and create a thread for it) instead of using a
34 // task runner provided to us by the embedder.
35 return {true, {}};
36 }
37
38 if (SAFE_ACCESS(description, runs_task_on_current_thread_callback, nullptr) ==
39 nullptr) {
40 FML_LOG(ERROR) << "FlutterTaskRunnerDescription.runs_task_on_current_"
41 "thread_callback was nullptr.";
42 return {false, {}};
43 }
44
45 if (SAFE_ACCESS(description, post_task_callback, nullptr) == nullptr) {
46 FML_LOG(ERROR)
47 << "FlutterTaskRunnerDescription.post_task_callback was nullptr.";
48 return {false, {}};
49 }
50
51 auto user_data = SAFE_ACCESS(description, user_data, nullptr);
52
53 // ABI safety checks have been completed.
54 auto post_task_callback_c = description->post_task_callback;
55 auto runs_task_on_current_thread_callback_c =
56 description->runs_task_on_current_thread_callback;
57
58 EmbedderTaskRunner::DispatchTable task_runner_dispatch_table = {
59 // .post_task_callback
60 [post_task_callback_c, user_data](EmbedderTaskRunner* task_runner,
61 uint64_t task_baton,
62 fml::TimePoint target_time) -> void {
63 FlutterTask task = {
64 // runner
65 reinterpret_cast<FlutterTaskRunner>(task_runner),
66 // task
67 task_baton,
68 };
69 post_task_callback_c(task, target_time.ToEpochDelta().ToNanoseconds(),
70 user_data);
71 },
72 // runs_task_on_current_thread_callback
73 [runs_task_on_current_thread_callback_c, user_data]() -> bool {
74 return runs_task_on_current_thread_callback_c(user_data);
75 }};
76
77 return {true, fml::MakeRefCounted<EmbedderTaskRunner>(
78 task_runner_dispatch_table,
79 SAFE_ACCESS(description, identifier, 0u))};
80 }
81
82 std::unique_ptr<EmbedderThreadHost>
CreateEmbedderOrEngineManagedThreadHost(const FlutterCustomTaskRunners * custom_task_runners)83 EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost(
84 const FlutterCustomTaskRunners* custom_task_runners) {
85 {
86 auto host = CreateEmbedderManagedThreadHost(custom_task_runners);
87 if (host && host->IsValid()) {
88 return host;
89 }
90 }
91
92 // specify a custom configuration. Don't fallback to the engine managed
93 // configuration if the embedder attempted to specify a configuration but
94 // messed up with an incorrect configuration.
95 if (custom_task_runners == nullptr) {
96 auto host = CreateEngineManagedThreadHost();
97 if (host && host->IsValid()) {
98 return host;
99 }
100 }
101
102 return nullptr;
103 }
104
GetCurrentThreadTaskRunner()105 static fml::RefPtr<fml::TaskRunner> GetCurrentThreadTaskRunner() {
106 fml::MessageLoop::EnsureInitializedForCurrentThread();
107 return fml::MessageLoop::GetCurrent().GetTaskRunner();
108 }
109
110 constexpr const char* kFlutterThreadName = "io.flutter";
111
112 // static
113 std::unique_ptr<EmbedderThreadHost>
CreateEmbedderManagedThreadHost(const FlutterCustomTaskRunners * custom_task_runners)114 EmbedderThreadHost::CreateEmbedderManagedThreadHost(
115 const FlutterCustomTaskRunners* custom_task_runners) {
116 if (custom_task_runners == nullptr) {
117 return nullptr;
118 }
119
120 // The UI and IO threads are always created by the engine and the embedder has
121 // no opportunity to specify task runners for the same.
122 //
123 // If/when more task runners are exposed, this mask will need to be updated.
124 uint64_t engine_thread_host_mask =
125 ThreadHost::Type::UI | ThreadHost::Type::IO;
126
127 auto platform_task_runner_pair = CreateEmbedderTaskRunner(
128 SAFE_ACCESS(custom_task_runners, platform_task_runner, nullptr));
129 auto render_task_runner_pair = CreateEmbedderTaskRunner(
130 SAFE_ACCESS(custom_task_runners, render_task_runner, nullptr));
131
132 if (!platform_task_runner_pair.first || !render_task_runner_pair.first) {
133 // User error while supplying a custom task runner. Return an invalid thread
134 // host. This will abort engine initialization. Don't fallback to defaults
135 // if the user wanted to specify a task runner but just messed up instead.
136 return nullptr;
137 }
138
139 // If the embedder has not supplied a GPU task runner, one needs to be
140 // created.
141 if (!render_task_runner_pair.second) {
142 engine_thread_host_mask |= ThreadHost::Type::GPU;
143 }
144
145 // If both the platform task runner and the GPU task runner are specified and
146 // have the same identifier, store only one.
147 if (platform_task_runner_pair.second && render_task_runner_pair.second) {
148 if (platform_task_runner_pair.second->GetEmbedderIdentifier() ==
149 render_task_runner_pair.second->GetEmbedderIdentifier()) {
150 render_task_runner_pair.second = platform_task_runner_pair.second;
151 }
152 }
153
154 // Create a thread host with just the threads that need to be managed by the
155 // engine. The embedder has provided the rest.
156 ThreadHost thread_host(kFlutterThreadName, engine_thread_host_mask);
157
158 // If the embedder has supplied a platform task runner, use that. If not, use
159 // the current thread task runner.
160 auto platform_task_runner = platform_task_runner_pair.second
161 ? static_cast<fml::RefPtr<fml::TaskRunner>>(
162 platform_task_runner_pair.second)
163 : GetCurrentThreadTaskRunner();
164
165 // If the embedder has supplied a GPU task runner, use that. If not, use the
166 // one from our thread host.
167 auto render_task_runner = render_task_runner_pair.second
168 ? static_cast<fml::RefPtr<fml::TaskRunner>>(
169 render_task_runner_pair.second)
170 : thread_host.gpu_thread->GetTaskRunner();
171
172 flutter::TaskRunners task_runners(
173 kFlutterThreadName,
174 platform_task_runner, // platform
175 render_task_runner, // gpu
176 thread_host.ui_thread->GetTaskRunner(), // ui (always engine managed)
177 thread_host.io_thread->GetTaskRunner() // io (always engine managed)
178 );
179
180 if (!task_runners.IsValid()) {
181 return nullptr;
182 }
183
184 std::set<fml::RefPtr<EmbedderTaskRunner>> embedder_task_runners;
185
186 if (platform_task_runner_pair.second) {
187 embedder_task_runners.insert(platform_task_runner_pair.second);
188 }
189
190 if (render_task_runner_pair.second) {
191 embedder_task_runners.insert(render_task_runner_pair.second);
192 }
193
194 auto embedder_host = std::make_unique<EmbedderThreadHost>(
195 std::move(thread_host), std::move(task_runners),
196 std::move(embedder_task_runners));
197
198 if (embedder_host->IsValid()) {
199 return embedder_host;
200 }
201
202 return nullptr;
203 }
204
205 // static
206 std::unique_ptr<EmbedderThreadHost>
CreateEngineManagedThreadHost()207 EmbedderThreadHost::CreateEngineManagedThreadHost() {
208 // Create a thread host with the current thread as the platform thread and all
209 // other threads managed.
210 ThreadHost thread_host(kFlutterThreadName, ThreadHost::Type::GPU |
211 ThreadHost::Type::IO |
212 ThreadHost::Type::UI);
213
214 // For embedder platforms that don't have native message loop interop, this
215 // will reference a task runner that points to a null message loop
216 // implementation.
217 auto platform_task_runner = GetCurrentThreadTaskRunner();
218
219 flutter::TaskRunners task_runners(
220 kFlutterThreadName,
221 platform_task_runner, // platform
222 thread_host.gpu_thread->GetTaskRunner(), // gpu
223 thread_host.ui_thread->GetTaskRunner(), // ui
224 thread_host.io_thread->GetTaskRunner() // io
225 );
226
227 if (!task_runners.IsValid()) {
228 return nullptr;
229 }
230
231 std::set<fml::RefPtr<EmbedderTaskRunner>> empty_embedder_task_runners;
232
233 auto embedder_host = std::make_unique<EmbedderThreadHost>(
234 std::move(thread_host), std::move(task_runners),
235 empty_embedder_task_runners);
236
237 if (embedder_host->IsValid()) {
238 return embedder_host;
239 }
240
241 return nullptr;
242 }
243
EmbedderThreadHost(ThreadHost host,flutter::TaskRunners runners,std::set<fml::RefPtr<EmbedderTaskRunner>> embedder_task_runners)244 EmbedderThreadHost::EmbedderThreadHost(
245 ThreadHost host,
246 flutter::TaskRunners runners,
247 std::set<fml::RefPtr<EmbedderTaskRunner>> embedder_task_runners)
248 : host_(std::move(host)), runners_(std::move(runners)) {
249 for (const auto& runner : embedder_task_runners) {
250 runners_map_[reinterpret_cast<int64_t>(runner.get())] = runner;
251 }
252 }
253
254 EmbedderThreadHost::~EmbedderThreadHost() = default;
255
IsValid() const256 bool EmbedderThreadHost::IsValid() const {
257 return runners_.IsValid();
258 }
259
GetTaskRunners() const260 const flutter::TaskRunners& EmbedderThreadHost::GetTaskRunners() const {
261 return runners_;
262 }
263
PostTask(int64_t runner,uint64_t task) const264 bool EmbedderThreadHost::PostTask(int64_t runner, uint64_t task) const {
265 auto found = runners_map_.find(runner);
266 if (found == runners_map_.end()) {
267 return false;
268 }
269 return found->second->PostTask(task);
270 }
271
272 } // namespace flutter
273