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
24namespace flutter {
25
26class DartVM;
27class 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///
59class 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