| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef FLUTTER_RUNTIME_DART_ISOLATE_H_ | |
| #define FLUTTER_RUNTIME_DART_ISOLATE_H_ | |
| #include <memory> | |
| #include <optional> | |
| #include <set> | |
| #include <string> | |
| #include <unordered_set> | |
| #include "flutter/common/task_runners.h" | |
| #include "flutter/fml/compiler_specific.h" | |
| #include "flutter/fml/macros.h" | |
| #include "flutter/fml/mapping.h" | |
| #include "flutter/lib/ui/io_manager.h" | |
| #include "flutter/lib/ui/snapshot_delegate.h" | |
| #include "flutter/lib/ui/ui_dart_state.h" | |
| #include "flutter/lib/ui/window/platform_configuration.h" | |
| #include "flutter/runtime/dart_snapshot.h" | |
| #include "third_party/dart/runtime/include/dart_api.h" | |
| #include "third_party/tonic/dart_state.h" | |
| namespace flutter { | |
| class DartVM; | |
| class DartIsolateGroupData; | |
| class IsolateConfiguration; | |
| //------------------------------------------------------------------------------ | |
| /// @brief Represents an instance of a live isolate. An isolate is a | |
| /// separate Dart execution context. Different Dart isolates don't | |
| /// share memory and can be scheduled concurrently by the Dart VM on | |
| /// one of the Dart VM managed worker pool threads. | |
| /// | |
| /// The entire lifecycle of a Dart isolate is controlled by the Dart | |
| /// VM. Because of this, the engine never holds a strong pointer to | |
| /// the Dart VM for extended periods of time. This allows the VM (or | |
| /// the isolates themselves) to terminate Dart execution without | |
| /// consulting the engine. | |
| /// | |
| /// The isolate that the engine creates to act as the host for the | |
| /// Flutter application code with UI bindings is called the root | |
| /// isolate. | |
| /// | |
| /// The root isolate is special in the following ways: | |
| /// * The root isolate forms a new isolate group. Child isolates are | |
| /// added to their parents groups. When the root isolate dies, all | |
| /// isolates in its group are terminated. | |
| /// * Only root isolates get UI bindings. | |
| /// * Root isolates execute their code on engine managed threads. | |
| /// All other isolates run their Dart code on Dart VM managed | |
| /// thread pool workers that the engine has no control over. | |
| /// * Since the engine does not know the thread on which non-root | |
| /// isolates are run, the engine has no opportunity to get a | |
| /// reference to non-root isolates. Such isolates can only be | |
| /// terminated if they terminate themselves or their isolate group | |
| /// is torn down. | |
| /// | |
| class DartIsolate : public UIDartState { | |
| public: | |
| class Flags { | |
| public: | |
| Flags(); | |
| explicit Flags(const Dart_IsolateFlags* flags); | |
| ~Flags(); | |
| void SetNullSafetyEnabled(bool enabled); | |
| Dart_IsolateFlags Get() const; | |
| private: | |
| Dart_IsolateFlags flags_; | |
| }; | |
| //---------------------------------------------------------------------------- | |
| /// @brief The engine represents all dart isolates as being in one of the | |
| /// known phases. By invoking various methods on the Dart isolate, | |
| /// the engine transition the Dart isolate from one phase to the | |
| /// next. The Dart isolate will only move from one phase to the | |
| /// next in the order specified in the `DartIsolate::Phase` enum. | |
| /// That is, once the isolate has moved out of a particular phase, | |
| /// it can never transition back to that phase in the future. | |
| /// There is no error recovery mechanism and callers that find | |
| /// their isolates in an undesirable phase must discard the | |
| /// isolate and start over. | |
| /// | |
| enum class Phase { | |
| //-------------------------------------------------------------------------- | |
| /// The initial phase of all Dart isolates. This is an internal phase and | |
| /// callers can never get a reference to a Dart isolate in this phase. | |
| /// | |
| Unknown, | |
| //-------------------------------------------------------------------------- | |
| /// The Dart isolate has been created but none of the library tag or message | |
| /// handers have been set yet. The is an internal phase and callers can | |
| /// never get a reference to a Dart isolate in this phase. | |
| /// | |
| Uninitialized, | |
| //-------------------------------------------------------------------------- | |
| /// The Dart isolate has been been fully initialized but none of the | |
| /// libraries referenced by that isolate have been loaded yet. This is an | |
| /// internal phase and callers can never get a reference to a Dart isolate | |
| /// in this phase. | |
| /// | |
| Initialized, | |
| //-------------------------------------------------------------------------- | |
| /// The isolate has been fully initialized and is waiting for the caller to | |
| /// associate isolate snapshots with the same. The isolate will only be | |
| /// ready to execute Dart code once one of the `Prepare` calls are | |
| /// successfully made. | |
| /// | |
| LibrariesSetup, | |
| //-------------------------------------------------------------------------- | |
| /// The isolate is fully ready to start running Dart code. Callers can | |
| /// transition the isolate to the next state by calling the `Run` or | |
| /// `RunFromLibrary` methods. | |
| /// | |
| Ready, | |
| //-------------------------------------------------------------------------- | |
| /// The isolate is currently running Dart code. | |
| /// | |
| Running, | |
| //-------------------------------------------------------------------------- | |
| /// The isolate is no longer running Dart code and is in the middle of being | |
| /// collected. This is in internal phase and callers can never get a | |
| /// reference to a Dart isolate in this phase. | |
| /// | |
| Shutdown, | |
| }; | |
| //---------------------------------------------------------------------------- | |
| /// @brief Creates an instance of a root isolate and returns a weak | |
| /// pointer to the same. The isolate instance may only be used | |
| /// safely on the engine thread on which it was created. In the | |
| /// shell, this is the UI thread and task runner. Using the | |
| /// isolate on any other thread is user error. | |
| /// | |
| /// The isolate that the engine creates to act as the host for the | |
| /// Flutter application code with UI bindings is called the root | |
| /// isolate. | |
| /// | |
| /// The root isolate is special in the following ways: | |
| /// * The root isolate forms a new isolate group. Child isolates | |
| /// are added to their parents groups. When the root isolate | |
| /// dies, all isolates in its group are terminated. | |
| /// * Only root isolates get UI bindings. | |
| /// * Root isolates execute their code on engine managed threads. | |
| /// All other isolates run their Dart code on Dart VM managed | |
| /// thread pool workers that the engine has no control over. | |
| /// * Since the engine does not know the thread on which non-root | |
| /// isolates are run, the engine has no opportunity to get a | |
| /// reference to non-root isolates. Such isolates can only be | |
| /// terminated if they terminate themselves or their isolate | |
| /// group is torn down. | |
| /// | |
| /// @param[in] settings The settings used to create the | |
| /// isolate. | |
| /// @param[in] platform_configuration The platform configuration for | |
| /// handling communication with the | |
| /// framework. | |
| /// @param[in] flags The Dart isolate flags for this | |
| /// isolate instance. | |
| /// @param[in] dart_entrypoint The name of the dart entrypoint | |
| /// function to invoke. | |
| /// @param[in] dart_entrypoint_library The name of the dart library | |
| /// containing the entrypoint. | |
| /// @param[in] isolate_configuration The isolate configuration used to | |
| /// configure the isolate before | |
| /// invoking the entrypoint. | |
| /// @param[in] isolate_create_callback The isolate create callback. This | |
| /// will be called when the before the | |
| /// main Dart entrypoint is invoked in | |
| /// the root isolate. The isolate is | |
| /// already in the running state at | |
| /// this point and an isolate scope is | |
| /// current. | |
| /// @param[in] isolate_shutdown_callback The isolate shutdown callback. | |
| /// This will be called before the | |
| /// isolate is about to transition | |
| /// into the Shutdown phase. The | |
| /// isolate is still running at this | |
| /// point and an isolate scope is | |
| /// current. | |
| /// @param[in] context Engine-owned state which is | |
| /// accessed by the root dart isolate. | |
| /// @param[in] spawning_isolate The isolate that is spawning the | |
| /// new isolate. See also | |
| /// DartIsolate::SpawnIsolate. | |
| /// @return A weak pointer to the root Dart isolate. The caller must | |
| /// ensure that the isolate is not referenced for long periods of | |
| /// time as it prevents isolate collection when the isolate | |
| /// terminates itself. The caller may also only use the isolate on | |
| /// the thread on which the isolate was created. | |
| /// | |
| static std::weak_ptr<DartIsolate> CreateRunningRootIsolate( | |
| const Settings& settings, | |
| fml::RefPtr<const DartSnapshot> isolate_snapshot, | |
| std::unique_ptr<PlatformConfiguration> platform_configuration, | |
| Flags flags, | |
| const fml::closure& isolate_create_callback, | |
| const fml::closure& isolate_shutdown_callback, | |
| std::optional<std::string> dart_entrypoint, | |
| std::optional<std::string> dart_entrypoint_library, | |
| std::unique_ptr<IsolateConfiguration> isolate_configration, | |
| const UIDartState::Context& context, | |
| const DartIsolate* spawning_isolate = nullptr); | |
| //---------------------------------------------------------------------------- | |
| /// @brief Creates a running DartIsolate who shares as many resources as | |
| /// possible with the caller DartIsolate. This allows them to | |
| /// occupy less memory together and to be created faster. | |
| /// @details Shared components will be destroyed when the last live | |
| /// DartIsolate is destroyed. SpawnIsolate can only be used to | |
| /// create DartIsolates whose executable code is shared with the | |
| /// calling DartIsolate. | |
| /// @attention Only certain setups can take advantage of the most savings | |
| /// currently, AOT specifically. | |
| /// @return A weak pointer to a new running DartIsolate. The caller must | |
| /// ensure that the isolate is not referenced for long periods of | |
| /// time as it prevents isolate collection when the isolate | |
| /// terminates itself. The caller may also only use the isolate on | |
| /// the thread on which the isolate was created. | |
| std::weak_ptr<DartIsolate> SpawnIsolate( | |
| const Settings& settings, | |
| std::unique_ptr<PlatformConfiguration> platform_configuration, | |
| fml::WeakPtr<SnapshotDelegate> snapshot_delegate, | |
| std::string advisory_script_uri, | |
| std::string advisory_script_entrypoint, | |
| Flags flags, | |
| const fml::closure& isolate_create_callback, | |
| const fml::closure& isolate_shutdown_callback, | |
| std::optional<std::string> dart_entrypoint, | |
| std::optional<std::string> dart_entrypoint_library, | |
| std::unique_ptr<IsolateConfiguration> isolate_configration) const; | |
| // |UIDartState| | |
| ~DartIsolate() override; | |
| //---------------------------------------------------------------------------- | |
| /// @brief The current phase of the isolate. The engine represents all | |
| /// dart isolates as being in one of the known phases. By invoking | |
| /// various methods on the Dart isolate, the engine transitions | |
| /// the Dart isolate from one phase to the next. The Dart isolate | |
| /// will only move from one phase to the next in the order | |
| /// specified in the `DartIsolate::Phase` enum. That is, the once | |
| /// the isolate has moved out of a particular phase, it can never | |
| /// transition back to that phase in the future. There is no error | |
| /// recovery mechanism and callers that find their isolates in an | |
| /// undesirable phase must discard the isolate and start over. | |
| /// | |
| /// @return The current isolate phase. | |
| /// | |
| Phase GetPhase() const; | |
| //---------------------------------------------------------------------------- | |
| /// @brief Returns the ID for an isolate which is used to query the | |
| /// service protocol. | |
| /// | |
| /// @return The service identifier for this isolate. | |
| /// | |
| std::string GetServiceId(); | |
| //---------------------------------------------------------------------------- | |
| /// @brief Prepare the isolate for running for a precompiled code bundle. | |
| /// The Dart VM must be configured for running precompiled code. | |
| /// | |
| /// The isolate must already be in the `Phase::LibrariesSetup` | |
| /// phase. After a successful call to this method, the isolate | |
| /// will transition to the `Phase::Ready` phase. | |
| /// | |
| /// @return Whether the isolate was prepared and the described phase | |
| /// transition made. | |
| /// | |
| [[nodiscard]] bool PrepareForRunningFromPrecompiledCode(); | |
| //---------------------------------------------------------------------------- | |
| /// @brief Prepare the isolate for running for a a list of kernel files. | |
| /// | |
| /// The Dart VM must be configured for running from kernel | |
| /// snapshots. | |
| /// | |
| /// The isolate must already be in the `Phase::LibrariesSetup` | |
| /// phase. This call can be made multiple times. After a series of | |
| /// successful calls to this method, the caller can specify the | |
| /// last kernel file mapping by specifying `last_piece` to `true`. | |
| /// On success, the isolate will transition to the `Phase::Ready` | |
| /// phase. | |
| /// | |
| /// @param[in] kernel The kernel mapping. | |
| /// @param[in] last_piece Indicates if this is the last kernel mapping | |
| /// expected. After this point, the isolate will | |
| /// attempt a transition to the `Phase::Ready` phase. | |
| /// | |
| /// @return If the kernel mapping supplied was successfully used to | |
| /// prepare the isolate. | |
| /// | |
| [[nodiscard]] bool PrepareForRunningFromKernel( | |
| std::shared_ptr<const fml::Mapping> kernel, | |
| bool child_isolate, | |
| bool last_piece); | |
| //---------------------------------------------------------------------------- | |
| /// @brief Prepare the isolate for running for a a list of kernel files. | |
| /// | |
| /// The Dart VM must be configured for running from kernel | |
| /// snapshots. | |
| /// | |
| /// The isolate must already be in the `Phase::LibrariesSetup` | |
| /// phase. After a successful call to this method, the isolate | |
| /// will transition to the `Phase::Ready` phase. | |
| /// | |
| /// @param[in] kernels The kernels | |
| /// | |
| /// @return If the kernel mappings supplied were successfully used to | |
| /// prepare the isolate. | |
| /// | |
| [[nodiscard]] bool PrepareForRunningFromKernels( | |
| std::vector<std::shared_ptr<const fml::Mapping>> kernels); | |
| //---------------------------------------------------------------------------- | |
| /// @brief Prepare the isolate for running for a a list of kernel files. | |
| /// | |
| /// The Dart VM must be configured for running from kernel | |
| /// snapshots. | |
| /// | |
| /// The isolate must already be in the `Phase::LibrariesSetup` | |
| /// phase. After a successful call to this method, the isolate | |
| /// will transition to the `Phase::Ready` phase. | |
| /// | |
| /// @param[in] kernels The kernels | |
| /// | |
| /// @return If the kernel mappings supplied were successfully used to | |
| /// prepare the isolate. | |
| /// | |
| [[nodiscard]] bool PrepareForRunningFromKernels( | |
| std::vector<std::unique_ptr<const fml::Mapping>> kernels); | |
| //---------------------------------------------------------------------------- | |
| /// @brief Transition the root isolate to the `Phase::Running` phase and | |
| /// invoke the main entrypoint (the "main" method) in the | |
| /// specified library. The isolate must already be in the | |
| /// `Phase::Ready` phase. | |
| /// | |
| /// @param[in] library_name The name of the library in which to invoke the | |
| /// supplied entrypoint. | |
| /// @param[in] entrypoint The entrypoint in `library_name` | |
| /// @param[in] args A list of string arguments to the entrypoint. | |
| /// | |
| /// @return If the isolate successfully transitioned to the running phase | |
| /// and the main entrypoint was invoked. | |
| /// | |
| [[nodiscard]] bool RunFromLibrary(std::optional<std::string> library_name, | |
| std::optional<std::string> entrypoint, | |
| const std::vector<std::string>& args); | |
| //---------------------------------------------------------------------------- | |
| /// @brief Transition the isolate to the `Phase::Shutdown` phase. The | |
| /// only thing left to do is to collect the isolate. | |
| /// | |
| /// @return If the isolate successfully transitioned to the shutdown | |
| /// phase. | |
| /// | |
| [[nodiscard]] bool Shutdown(); | |
| //---------------------------------------------------------------------------- | |
| /// @brief Registers a callback that will be invoked in isolate scope | |
| /// just before the isolate transitions to the `Phase::Shutdown` | |
| /// phase. | |
| /// | |
| /// @param[in] closure The callback to invoke on isolate shutdown. | |
| /// | |
| void AddIsolateShutdownCallback(const fml::closure& closure); | |
| //---------------------------------------------------------------------------- | |
| /// @brief A weak pointer to the Dart isolate instance. This instance may | |
| /// only be used on the task runner that created the root isolate. | |
| /// | |
| /// @return The weak isolate pointer. | |
| /// | |
| std::weak_ptr<DartIsolate> GetWeakIsolatePtr(); | |
| //---------------------------------------------------------------------------- | |
| /// @brief The task runner on which the Dart code for the root isolate is | |
| /// running. For the root isolate, this is the UI task runner for | |
| /// the shell that owns the root isolate. | |
| /// | |
| /// @return The message handling task runner. | |
| /// | |
| fml::RefPtr<fml::TaskRunner> GetMessageHandlingTaskRunner() const; | |
| bool LoadLoadingUnit( | |
| intptr_t loading_unit_id, | |
| std::unique_ptr<const fml::Mapping> snapshot_data, | |
| std::unique_ptr<const fml::Mapping> snapshot_instructions); | |
| void LoadLoadingUnitError(intptr_t loading_unit_id, | |
| const std::string error_message, | |
| bool transient); | |
| private: | |
| friend class IsolateConfiguration; | |
| class AutoFireClosure { | |
| public: | |
| AutoFireClosure(const fml::closure& closure); | |
| ~AutoFireClosure(); | |
| private: | |
| fml::closure closure_; | |
| FML_DISALLOW_COPY_AND_ASSIGN(AutoFireClosure); | |
| }; | |
| friend class DartVM; | |
| Phase phase_ = Phase::Unknown; | |
| std::vector<std::shared_ptr<const fml::Mapping>> kernel_buffers_; | |
| std::vector<std::unique_ptr<AutoFireClosure>> shutdown_callbacks_; | |
| std::unordered_set<fml::RefPtr<DartSnapshot>> loading_unit_snapshots_; | |
| fml::RefPtr<fml::TaskRunner> message_handling_task_runner_; | |
| const bool may_insecurely_connect_to_all_domains_; | |
| std::string domain_network_policy_; | |
| static std::weak_ptr<DartIsolate> CreateRootIsolate( | |
| const Settings& settings, | |
| fml::RefPtr<const DartSnapshot> isolate_snapshot, | |
| std::unique_ptr<PlatformConfiguration> platform_configuration, | |
| Flags flags, | |
| const fml::closure& isolate_create_callback, | |
| const fml::closure& isolate_shutdown_callback, | |
| const UIDartState::Context& context, | |
| const DartIsolate* spawning_isolate = nullptr); | |
| DartIsolate(const Settings& settings, | |
| bool is_root_isolate, | |
| const UIDartState::Context& context); | |
| [[nodiscard]] bool Initialize(Dart_Isolate isolate); | |
| void SetMessageHandlingTaskRunner(fml::RefPtr<fml::TaskRunner> runner); | |
| bool LoadKernel(std::shared_ptr<const fml::Mapping> mapping, bool last_piece); | |
| [[nodiscard]] bool LoadLibraries(); | |
| bool UpdateThreadPoolNames() const; | |
| [[nodiscard]] bool MarkIsolateRunnable(); | |
| void OnShutdownCallback(); | |
| DartIsolateGroupData& GetIsolateGroupData(); | |
| const DartIsolateGroupData& GetIsolateGroupData() const; | |
| // |Dart_IsolateGroupCreateCallback| | |
| static Dart_Isolate DartIsolateGroupCreateCallback( | |
| const char* advisory_script_uri, | |
| const char* advisory_script_entrypoint, | |
| const char* package_root, | |
| const char* package_config, | |
| Dart_IsolateFlags* flags, | |
| std::shared_ptr<DartIsolate>* parent_isolate_group, | |
| char** error); | |
| // |Dart_IsolateInitializeCallback| | |
| static bool DartIsolateInitializeCallback(void** child_callback_data, | |
| char** error); | |
| static Dart_Isolate DartCreateAndStartServiceIsolate( | |
| const char* package_root, | |
| const char* package_config, | |
| Dart_IsolateFlags* flags, | |
| char** error); | |
| typedef std::function<Dart_Isolate(std::shared_ptr<DartIsolateGroupData>*, | |
| std::shared_ptr<DartIsolate>*, | |
| Dart_IsolateFlags*, | |
| char**)> | |
| IsolateMaker; | |
| static Dart_Isolate CreateDartIsolateGroup( | |
| std::unique_ptr<std::shared_ptr<DartIsolateGroupData>> isolate_group_data, | |
| std::unique_ptr<std::shared_ptr<DartIsolate>> isolate_data, | |
| Dart_IsolateFlags* flags, | |
| char** error, | |
| const IsolateMaker& make_isolate); | |
| static bool InitializeIsolate(std::shared_ptr<DartIsolate> embedder_isolate, | |
| Dart_Isolate isolate, | |
| char** error); | |
| // |Dart_IsolateShutdownCallback| | |
| static void DartIsolateShutdownCallback( | |
| std::shared_ptr<DartIsolateGroupData>* isolate_group_data, | |
| std::shared_ptr<DartIsolate>* isolate_data); | |
| // |Dart_IsolateCleanupCallback| | |
| static void DartIsolateCleanupCallback( | |
| std::shared_ptr<DartIsolateGroupData>* isolate_group_data, | |
| std::shared_ptr<DartIsolate>* isolate_data); | |
| // |Dart_IsolateGroupCleanupCallback| | |
| static void DartIsolateGroupCleanupCallback( | |
| std::shared_ptr<DartIsolateGroupData>* isolate_group_data); | |
| // |Dart_DeferredLoadHandler| | |
| static Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id); | |
| static void SpawnIsolateShutdownCallback( | |
| std::shared_ptr<DartIsolateGroupData>* isolate_group_data, | |
| std::shared_ptr<DartIsolate>* isolate_data); | |
| FML_DISALLOW_COPY_AND_ASSIGN(DartIsolate); | |
| }; | |
| } // namespace flutter | |
| #endif // FLUTTER_RUNTIME_DART_ISOLATE_H_ |