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

source code of flutter_engine/flutter/runtime/dart_isolate.h