• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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