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_RUNTIME_DART_ISOLATE_H_ |
6 | #define FLUTTER_RUNTIME_DART_ISOLATE_H_ |
7 | |
8 | #include <memory> |
9 | #include <set> |
10 | #include <string> |
11 | |
12 | #include "flutter/common/task_runners.h" |
13 | #include "flutter/fml/compiler_specific.h" |
14 | #include "flutter/fml/macros.h" |
15 | #include "flutter/fml/mapping.h" |
16 | #include "flutter/lib/ui/io_manager.h" |
17 | #include "flutter/lib/ui/snapshot_delegate.h" |
18 | #include "flutter/lib/ui/ui_dart_state.h" |
19 | #include "flutter/lib/ui/window/platform_configuration.h" |
20 | #include "flutter/runtime/dart_snapshot.h" |
21 | #include "third_party/dart/runtime/include/dart_api.h" |
22 | #include "third_party/tonic/dart_state.h" |
23 | |
24 | namespace flutter { |
25 | |
26 | class DartVM; |
27 | class DartIsolateGroupData; |
28 | |
29 | //------------------------------------------------------------------------------ |
30 | /// @brief Represents an instance of a live isolate. An isolate is a |
31 | /// separate Dart execution context. Different Dart isolates don't |
32 | /// share memory and can be scheduled concurrently by the Dart VM on |
33 | /// one of the Dart VM managed worker pool threads. |
34 | /// |
35 | /// The entire lifecycle of a Dart isolate is controlled by the Dart |
36 | /// VM. Because of this, the engine never holds a strong pointer to |
37 | /// the Dart VM for extended periods of time. This allows the VM (or |
38 | /// the isolates themselves) to terminate Dart execution without |
39 | /// consulting the engine. |
40 | /// |
41 | /// The isolate that the engine creates to act as the host for the |
42 | /// Flutter application code with UI bindings is called the root |
43 | /// isolate. |
44 | /// |
45 | /// The root isolate is special in the following ways: |
46 | /// * The root isolate forms a new isolate group. Child isolates are |
47 | /// added to their parents groups. When the root isolate dies, all |
48 | /// isolates in its group are terminated. |
49 | /// * Only root isolates get UI bindings. |
50 | /// * Root isolates execute their code on engine managed threads. |
51 | /// All other isolates run their Dart code on Dart VM managed |
52 | /// thread pool workers that the engine has no control over. |
53 | /// * Since the engine does not know the thread on which non-root |
54 | /// isolates are run, the engine has no opportunity to get a |
55 | /// reference to non-root isolates. Such isolates can only be |
56 | /// terminated if they terminate themselves or their isolate group |
57 | /// is torn down. |
58 | /// |
59 | class DartIsolate : public UIDartState { |
60 | public: |
61 | //---------------------------------------------------------------------------- |
62 | /// @brief The engine represents all dart isolates as being in one of the |
63 | /// known phases. By invoking various methods on the Dart isolate, |
64 | /// the engine transition the Dart isolate from one phase to the |
65 | /// next. The Dart isolate will only move from one phase to the |
66 | /// next in the order specified in the `DartIsolate::Phase` enum. |
67 | /// That is, once the isolate has moved out of a particular phase, |
68 | /// it can never transition back to that phase in the future. |
69 | /// There is no error recovery mechanism and callers that find |
70 | /// their isolates in an undesirable phase must discard the |
71 | /// isolate and start over. |
72 | /// |
73 | enum class Phase { |
74 | //-------------------------------------------------------------------------- |
75 | /// The initial phase of all Dart isolates. This is an internal phase and |
76 | /// callers can never get a reference to a Dart isolate in this phase. |
77 | /// |
78 | Unknown, |
79 | //-------------------------------------------------------------------------- |
80 | /// The Dart isolate has been created but none of the library tag or message |
81 | /// handers have been set yet. The is an internal phase and callers can |
82 | /// never get a reference to a Dart isolate in this phase. |
83 | /// |
84 | Uninitialized, |
85 | //-------------------------------------------------------------------------- |
86 | /// The Dart isolate has been been fully initialized but none of the |
87 | /// libraries referenced by that isolate have been loaded yet. This is an |
88 | /// internal phase and callers can never get a reference to a Dart isolate |
89 | /// in this phase. |
90 | /// |
91 | Initialized, |
92 | //-------------------------------------------------------------------------- |
93 | /// The isolate has been fully initialized and is waiting for the caller to |
94 | /// associate isolate snapshots with the same. The isolate will only be |
95 | /// ready to execute Dart code once one of the `Prepare` calls are |
96 | /// successfully made. |
97 | /// |
98 | LibrariesSetup, |
99 | //-------------------------------------------------------------------------- |
100 | /// The isolate is fully ready to start running Dart code. Callers can |
101 | /// transition the isolate to the next state by calling the `Run` or |
102 | /// `RunFromLibrary` methods. |
103 | /// |
104 | Ready, |
105 | //-------------------------------------------------------------------------- |
106 | /// The isolate is currently running Dart code. |
107 | /// |
108 | Running, |
109 | //-------------------------------------------------------------------------- |
110 | /// The isolate is no longer running Dart code and is in the middle of being |
111 | /// collected. This is in internal phase and callers can never get a |
112 | /// reference to a Dart isolate in this phase. |
113 | /// |
114 | Shutdown, |
115 | }; |
116 | |
117 | //---------------------------------------------------------------------------- |
118 | /// @brief Creates an instance of a root isolate and returns a weak |
119 | /// pointer to the same. The isolate instance may only be used |
120 | /// safely on the engine thread on which it was created. In the |
121 | /// shell, this is the UI thread and task runner. Using the |
122 | /// isolate on any other thread is user error. |
123 | /// |
124 | /// The isolate that the engine creates to act as the host for the |
125 | /// Flutter application code with UI bindings is called the root |
126 | /// isolate. |
127 | /// |
128 | /// The root isolate is special in the following ways: |
129 | /// * The root isolate forms a new isolate group. Child isolates |
130 | /// are added to their parents groups. When the root isolate |
131 | /// dies, all isolates in its group are terminated. |
132 | /// * Only root isolates get UI bindings. |
133 | /// * Root isolates execute their code on engine managed threads. |
134 | /// All other isolates run their Dart code on Dart VM managed |
135 | /// thread pool workers that the engine has no control over. |
136 | /// * Since the engine does not know the thread on which non-root |
137 | /// isolates are run, the engine has no opportunity to get a |
138 | /// reference to non-root isolates. Such isolates can only be |
139 | /// terminated if they terminate themselves or their isolate |
140 | /// group is torn down. |
141 | /// |
142 | /// @param[in] settings The settings used to create the |
143 | /// isolate. |
144 | /// @param[in] isolate_snapshot The isolate snapshot. This is |
145 | /// usually obtained from the |
146 | /// DartVMData associated with the |
147 | /// running Dart VM instance. |
148 | /// @param[in] task_runners The task runners used by the |
149 | /// isolate. Via UI bindings, the |
150 | /// isolate will use the IO task |
151 | /// runner to scheduled texture |
152 | /// decompression jobs and post tasks |
153 | /// back to the UI task runner. |
154 | /// @param[in] window The weak pointer to the window |
155 | /// associated with this root isolate. |
156 | /// @param[in] io_manager The i/o manager. |
157 | /// @param[in] unref_queue The Skia unref queue. |
158 | /// @param[in] image_decoder The image decoder. |
159 | /// @param[in] advisory_script_uri The advisory script uri. This is |
160 | /// only used in instrumentation. |
161 | /// @param[in] advisory_script_entrypoint The advisory script entrypoint. |
162 | /// This is only used in |
163 | /// instrumentation. Notably, this is |
164 | /// NOT the main entrypoint of Dart |
165 | /// code in the isolate. That is |
166 | /// specified when making one of the |
167 | /// `Run` calls. |
168 | /// @param[in] flags The Dart isolate flags for this |
169 | /// isolate instance. |
170 | /// @param[in] isolate_create_callback The isolate create callback. This |
171 | /// will be called when the before the |
172 | /// main Dart entrypoint is invoked in |
173 | /// the root isolate. The isolate is |
174 | /// already in the running state at |
175 | /// this point and an isolate scope is |
176 | /// current. |
177 | /// @param[in] isolate_shutdown_callback The isolate shutdown callback. |
178 | /// This will be called before the |
179 | /// isolate is about to transition |
180 | /// into the Shutdown phase. The |
181 | /// isolate is still running at this |
182 | /// point and an isolate scope is |
183 | /// current. |
184 | /// |
185 | /// @return A weak pointer to the root Dart isolate. The caller must |
186 | /// ensure that the isolate is not referenced for long periods of |
187 | /// time as it prevents isolate collection when the isolate |
188 | /// terminates itself. The caller may also only use the isolate on |
189 | /// the thread on which the isolate was created. |
190 | /// |
191 | static std::weak_ptr<DartIsolate> CreateRootIsolate( |
192 | const Settings& settings, |
193 | fml::RefPtr<const DartSnapshot> isolate_snapshot, |
194 | TaskRunners task_runners, |
195 | std::unique_ptr<PlatformConfiguration> platform_configuration, |
196 | fml::WeakPtr<SnapshotDelegate> snapshot_delegate, |
197 | fml::WeakPtr<IOManager> io_manager, |
198 | fml::RefPtr<SkiaUnrefQueue> skia_unref_queue, |
199 | fml::WeakPtr<ImageDecoder> image_decoder, |
200 | std::string advisory_script_uri, |
201 | std::string advisory_script_entrypoint, |
202 | Dart_IsolateFlags* flags, |
203 | const fml::closure& isolate_create_callback, |
204 | const fml::closure& isolate_shutdown_callback); |
205 | |
206 | // |UIDartState| |
207 | ~DartIsolate() override; |
208 | |
209 | //---------------------------------------------------------------------------- |
210 | /// @brief The current phase of the isolate. The engine represents all |
211 | /// dart isolates as being in one of the known phases. By invoking |
212 | /// various methods on the Dart isolate, the engine transitions |
213 | /// the Dart isolate from one phase to the next. The Dart isolate |
214 | /// will only move from one phase to the next in the order |
215 | /// specified in the `DartIsolate::Phase` enum. That is, the once |
216 | /// the isolate has moved out of a particular phase, it can never |
217 | /// transition back to that phase in the future. There is no error |
218 | /// recovery mechanism and callers that find their isolates in an |
219 | /// undesirable phase must discard the isolate and start over. |
220 | /// |
221 | /// @return The current isolate phase. |
222 | /// |
223 | Phase GetPhase() const; |
224 | |
225 | //---------------------------------------------------------------------------- |
226 | /// @brief Returns the ID for an isolate which is used to query the |
227 | /// service protocol. |
228 | /// |
229 | /// @return The service identifier for this isolate. |
230 | /// |
231 | std::string GetServiceId(); |
232 | |
233 | //---------------------------------------------------------------------------- |
234 | /// @brief Prepare the isolate for running for a precompiled code bundle. |
235 | /// The Dart VM must be configured for running precompiled code. |
236 | /// |
237 | /// The isolate must already be in the `Phase::LibrariesSetup` |
238 | /// phase. After a successful call to this method, the isolate |
239 | /// will transition to the `Phase::Ready` phase. |
240 | /// |
241 | /// @return Whether the isolate was prepared and the described phase |
242 | /// transition made. |
243 | /// |
244 | [[nodiscard]] bool PrepareForRunningFromPrecompiledCode(); |
245 | |
246 | //---------------------------------------------------------------------------- |
247 | /// @brief Prepare the isolate for running for a a list of kernel files. |
248 | /// |
249 | /// The Dart VM must be configured for running from kernel |
250 | /// snapshots. |
251 | /// |
252 | /// The isolate must already be in the `Phase::LibrariesSetup` |
253 | /// phase. This call can be made multiple times. After a series of |
254 | /// successful calls to this method, the caller can specify the |
255 | /// last kernel file mapping by specifying `last_piece` to `true`. |
256 | /// On success, the isolate will transition to the `Phase::Ready` |
257 | /// phase. |
258 | /// |
259 | /// @param[in] kernel The kernel mapping. |
260 | /// @param[in] last_piece Indicates if this is the last kernel mapping |
261 | /// expected. After this point, the isolate will |
262 | /// attempt a transition to the `Phase::Ready` phase. |
263 | /// |
264 | /// @return If the kernel mapping supplied was successfully used to |
265 | /// prepare the isolate. |
266 | /// |
267 | [[nodiscard]] bool PrepareForRunningFromKernel( |
268 | std::shared_ptr<const fml::Mapping> kernel, |
269 | bool last_piece = true); |
270 | |
271 | //---------------------------------------------------------------------------- |
272 | /// @brief Prepare the isolate for running for a a list of kernel files. |
273 | /// |
274 | /// The Dart VM must be configured for running from kernel |
275 | /// snapshots. |
276 | /// |
277 | /// The isolate must already be in the `Phase::LibrariesSetup` |
278 | /// phase. After a successful call to this method, the isolate |
279 | /// will transition to the `Phase::Ready` phase. |
280 | /// |
281 | /// @param[in] kernels The kernels |
282 | /// |
283 | /// @return If the kernel mappings supplied were successfully used to |
284 | /// prepare the isolate. |
285 | /// |
286 | [[nodiscard]] bool PrepareForRunningFromKernels( |
287 | std::vector<std::shared_ptr<const fml::Mapping>> kernels); |
288 | |
289 | //---------------------------------------------------------------------------- |
290 | /// @brief Prepare the isolate for running for a a list of kernel files. |
291 | /// |
292 | /// The Dart VM must be configured for running from kernel |
293 | /// snapshots. |
294 | /// |
295 | /// The isolate must already be in the `Phase::LibrariesSetup` |
296 | /// phase. After a successful call to this method, the isolate |
297 | /// will transition to the `Phase::Ready` phase. |
298 | /// |
299 | /// @param[in] kernels The kernels |
300 | /// |
301 | /// @return If the kernel mappings supplied were successfully used to |
302 | /// prepare the isolate. |
303 | /// |
304 | [[nodiscard]] bool PrepareForRunningFromKernels( |
305 | std::vector<std::unique_ptr<const fml::Mapping>> kernels); |
306 | |
307 | //---------------------------------------------------------------------------- |
308 | /// @brief Transition the root isolate to the `Phase::Running` phase and |
309 | /// invoke the main entrypoint (the "main" method) in the root |
310 | /// library. The isolate must already be in the `Phase::Ready` |
311 | /// phase. |
312 | /// |
313 | /// @param[in] entrypoint The entrypoint in the root library. |
314 | /// @param[in] args A list of string arguments to the entrypoint. |
315 | /// @param[in] on_run A callback to run in isolate scope after the main |
316 | /// entrypoint has been invoked. There is no isolate |
317 | /// scope current on the thread once this method |
318 | /// returns. |
319 | /// |
320 | /// @return If the isolate successfully transitioned to the running phase |
321 | /// and the main entrypoint was invoked. |
322 | /// |
323 | [[nodiscard]] bool Run(const std::string& entrypoint, |
324 | const std::vector<std::string>& args, |
325 | const fml::closure& on_run = nullptr); |
326 | |
327 | //---------------------------------------------------------------------------- |
328 | /// @brief Transition the root isolate to the `Phase::Running` phase and |
329 | /// invoke the main entrypoint (the "main" method) in the |
330 | /// specified library. The isolate must already be in the |
331 | /// `Phase::Ready` phase. |
332 | /// |
333 | /// @param[in] library_name The name of the library in which to invoke the |
334 | /// supplied entrypoint. |
335 | /// @param[in] entrypoint The entrypoint in `library_name` |
336 | /// @param[in] args A list of string arguments to the entrypoint. |
337 | /// @param[in] on_run A callback to run in isolate scope after the |
338 | /// main entrypoint has been invoked. There is no |
339 | /// isolate scope current on the thread once this |
340 | /// method returns. |
341 | /// |
342 | /// @return If the isolate successfully transitioned to the running phase |
343 | /// and the main entrypoint was invoked. |
344 | /// |
345 | [[nodiscard]] bool RunFromLibrary(const std::string& library_name, |
346 | const std::string& entrypoint, |
347 | const std::vector<std::string>& args, |
348 | const fml::closure& on_run = nullptr); |
349 | |
350 | //---------------------------------------------------------------------------- |
351 | /// @brief Transition the isolate to the `Phase::Shutdown` phase. The |
352 | /// only thing left to do is to collect the isolate. |
353 | /// |
354 | /// @return If the isolate succesfully transitioned to the shutdown phase. |
355 | /// |
356 | [[nodiscard]] bool Shutdown(); |
357 | |
358 | //---------------------------------------------------------------------------- |
359 | /// @brief Registers a callback that will be invoked in isolate scope |
360 | /// just before the isolate transitions to the `Phase::Shutdown` |
361 | /// phase. |
362 | /// |
363 | /// @param[in] closure The callback to invoke on isolate shutdown. |
364 | /// |
365 | void AddIsolateShutdownCallback(const fml::closure& closure); |
366 | |
367 | //---------------------------------------------------------------------------- |
368 | /// @brief A weak pointer to the Dart isolate instance. This instance may |
369 | /// only be used on the task runner that created the root isolate. |
370 | /// |
371 | /// @return The weak isolate pointer. |
372 | /// |
373 | std::weak_ptr<DartIsolate> GetWeakIsolatePtr(); |
374 | |
375 | //---------------------------------------------------------------------------- |
376 | /// @brief The task runner on which the Dart code for the root isolate is |
377 | /// running. For the root isolate, this is the UI task runner for |
378 | /// the shell that owns the root isolate. |
379 | /// |
380 | /// @return The message handling task runner. |
381 | /// |
382 | fml::RefPtr<fml::TaskRunner> GetMessageHandlingTaskRunner() const; |
383 | |
384 | private: |
385 | class AutoFireClosure { |
386 | public: |
387 | AutoFireClosure(const fml::closure& closure); |
388 | |
389 | ~AutoFireClosure(); |
390 | |
391 | private: |
392 | fml::closure closure_; |
393 | FML_DISALLOW_COPY_AND_ASSIGN(AutoFireClosure); |
394 | }; |
395 | friend class DartVM; |
396 | |
397 | Phase phase_ = Phase::Unknown; |
398 | std::vector<std::shared_ptr<const fml::Mapping>> kernel_buffers_; |
399 | std::vector<std::unique_ptr<AutoFireClosure>> shutdown_callbacks_; |
400 | fml::RefPtr<fml::TaskRunner> message_handling_task_runner_; |
401 | const bool may_insecurely_connect_to_all_domains_; |
402 | std::string domain_network_policy_; |
403 | |
404 | DartIsolate(const Settings& settings, |
405 | TaskRunners task_runners, |
406 | fml::WeakPtr<SnapshotDelegate> snapshot_delegate, |
407 | fml::WeakPtr<IOManager> io_manager, |
408 | fml::RefPtr<SkiaUnrefQueue> unref_queue, |
409 | fml::WeakPtr<ImageDecoder> image_decoder, |
410 | std::string advisory_script_uri, |
411 | std::string advisory_script_entrypoint, |
412 | bool is_root_isolate); |
413 | [[nodiscard]] bool Initialize(Dart_Isolate isolate); |
414 | |
415 | void SetMessageHandlingTaskRunner(fml::RefPtr<fml::TaskRunner> runner); |
416 | |
417 | bool LoadKernel(std::shared_ptr<const fml::Mapping> mapping, bool last_piece); |
418 | |
419 | [[nodiscard]] bool LoadLibraries(); |
420 | |
421 | bool UpdateThreadPoolNames() const; |
422 | |
423 | [[nodiscard]] bool MarkIsolateRunnable(); |
424 | |
425 | void OnShutdownCallback(); |
426 | |
427 | DartIsolateGroupData& GetIsolateGroupData(); |
428 | |
429 | // |Dart_IsolateGroupCreateCallback| |
430 | static Dart_Isolate DartIsolateGroupCreateCallback( |
431 | const char* advisory_script_uri, |
432 | const char* advisory_script_entrypoint, |
433 | const char* package_root, |
434 | const char* package_config, |
435 | Dart_IsolateFlags* flags, |
436 | std::shared_ptr<DartIsolate>* parent_isolate_group, |
437 | char** error); |
438 | |
439 | // |Dart_IsolateInitializeCallback| |
440 | static bool DartIsolateInitializeCallback(void** child_callback_data, |
441 | char** error); |
442 | |
443 | static Dart_Isolate DartCreateAndStartServiceIsolate( |
444 | const char* package_root, |
445 | const char* package_config, |
446 | Dart_IsolateFlags* flags, |
447 | char** error); |
448 | |
449 | static Dart_Isolate CreateDartIsolateGroup( |
450 | std::unique_ptr<std::shared_ptr<DartIsolateGroupData>> isolate_group_data, |
451 | std::unique_ptr<std::shared_ptr<DartIsolate>> isolate_data, |
452 | Dart_IsolateFlags* flags, |
453 | char** error); |
454 | |
455 | static bool InitializeIsolate(std::shared_ptr<DartIsolate> embedder_isolate, |
456 | Dart_Isolate isolate, |
457 | char** error); |
458 | |
459 | // |Dart_IsolateShutdownCallback| |
460 | static void DartIsolateShutdownCallback( |
461 | std::shared_ptr<DartIsolateGroupData>* isolate_group_data, |
462 | std::shared_ptr<DartIsolate>* isolate_data); |
463 | |
464 | // |Dart_IsolateCleanupCallback| |
465 | static void DartIsolateCleanupCallback( |
466 | std::shared_ptr<DartIsolateGroupData>* isolate_group_data, |
467 | std::shared_ptr<DartIsolate>* isolate_data); |
468 | |
469 | // |Dart_IsolateGroupCleanupCallback| |
470 | static void DartIsolateGroupCleanupCallback( |
471 | std::shared_ptr<DartIsolateGroupData>* isolate_group_data); |
472 | |
473 | FML_DISALLOW_COPY_AND_ASSIGN(DartIsolate); |
474 | }; |
475 | |
476 | } // namespace flutter |
477 | |
478 | #endif // FLUTTER_RUNTIME_DART_ISOLATE_H_ |
479 | |