// 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 #include #include #include #include #include "flutter/common/task_runners.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 "flutter/runtime/isolate_configuration.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); void SetIsDontNeedSafe(bool value); 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 { // NOLINTBEGIN(readability-identifier-naming) //-------------------------------------------------------------------------- /// 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 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, // NOLINTEND(readability-identifier-naming) }; //---------------------------------------------------------------------------- /// @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] dart_entrypoint_args Arguments passed as a List /// to Dart's entrypoint function. /// @param[in] isolate_configuration The isolate configuration used to /// configure the isolate before /// invoking the entrypoint. /// @param[in] root_isolate_create_callback A callback called after the root /// isolate is created, _without_ /// isolate scope. This gives the /// caller a chance to finish any /// setup before running the Dart /// program, and after any embedder /// callbacks in the settings object. /// @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. /// @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 CreateRunningRootIsolate( const Settings& settings, const fml::RefPtr& isolate_snapshot, std::unique_ptr platform_configuration, Flags flags, const fml::closure& root_isolate_create_callback, const fml::closure& isolate_create_callback, const fml::closure& isolate_shutdown_callback, std::optional dart_entrypoint, std::optional dart_entrypoint_library, const std::vector& dart_entrypoint_args, std::unique_ptr isolate_configuration, const UIDartState::Context& context, const DartIsolate* spawning_isolate = nullptr); // |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( const std::shared_ptr& 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> 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> 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 library_name, std::optional entrypoint, const std::vector& 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 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 GetMessageHandlingTaskRunner() const; //---------------------------------------------------------------------------- /// @brief Creates a new isolate in the same group as this isolate, which /// runs on the platform thread. This method can only be invoked /// on the root isolate. /// /// @param[in] entry_point The entrypoint to invoke once the isolate is /// spawned. Will be run on the platform thread. /// @param[out] error If spawning fails inside the Dart VM, this is /// set to the error string. The error should be /// reported to the user. Otherwise it is set to /// null. It's possible for spawning to fail, but /// this error still be null. In that case the /// failure should not be reported to the user. /// /// @return The newly created isolate, or null if spawning failed. /// Dart_Isolate CreatePlatformIsolate(Dart_Handle entry_point, char** error) override; bool LoadLoadingUnit( intptr_t loading_unit_id, std::unique_ptr snapshot_data, std::unique_ptr snapshot_instructions); void LoadLoadingUnitError(intptr_t loading_unit_id, const std::string& error_message, bool transient); DartIsolateGroupData& GetIsolateGroupData(); const DartIsolateGroupData& GetIsolateGroupData() const; /// Returns the "main" entrypoint of the library contained in the kernel /// data in `mapping`. static Dart_Handle LoadLibraryFromKernel( const std::shared_ptr& mapping); private: friend class IsolateConfiguration; class AutoFireClosure { public: explicit 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> shutdown_callbacks_; std::unordered_set> loading_unit_snapshots_; fml::RefPtr message_handling_task_runner_; const bool may_insecurely_connect_to_all_domains_; const bool is_platform_isolate_; const bool is_spawning_in_group_; std::string domain_network_policy_; std::shared_ptr platform_isolate_manager_; static std::weak_ptr CreateRootIsolate( const Settings& settings, fml::RefPtr isolate_snapshot, std::unique_ptr platform_configuration, const 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, bool is_spawning_in_group = false); DartIsolate(const Settings& settings, const UIDartState::Context& context, std::shared_ptr platform_isolate_manager); //---------------------------------------------------------------------------- /// @brief Initializes the given (current) isolate. /// /// @param[in] dart_isolate The current isolate that is to be initialized. /// /// @return Whether the initialization succeeded. Irrespective of whether /// the initialization suceeded, the current isolate will still be /// active. /// [[nodiscard]] bool Initialize(Dart_Isolate dart_isolate); void SetMessageHandlingTaskRunner(const fml::RefPtr& runner, bool post_directly_to_runner); bool LoadKernel(const std::shared_ptr& mapping, bool last_piece); [[nodiscard]] bool LoadLibraries(); bool UpdateThreadPoolNames() const; [[nodiscard]] bool MarkIsolateRunnable(); void OnShutdownCallback(); // |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* 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*, std::shared_ptr*, Dart_IsolateFlags*, char**)> IsolateMaker; static Dart_Isolate CreateDartIsolateGroup( std::unique_ptr> isolate_group_data, std::unique_ptr> isolate_data, Dart_IsolateFlags* flags, char** error, const IsolateMaker& make_isolate); static bool InitializeIsolate( const std::shared_ptr& embedder_isolate, Dart_Isolate isolate, char** error); // |Dart_IsolateShutdownCallback| static void DartIsolateShutdownCallback( std::shared_ptr* isolate_group_data, std::shared_ptr* isolate_data); // |Dart_IsolateCleanupCallback| static void DartIsolateCleanupCallback( std::shared_ptr* isolate_group_data, std::shared_ptr* isolate_data); // |Dart_IsolateGroupCleanupCallback| static void DartIsolateGroupCleanupCallback( std::shared_ptr* isolate_group_data); // |Dart_DeferredLoadHandler| static Dart_Handle OnDartLoadLibrary(intptr_t loading_unit_id); static void SpawnIsolateShutdownCallback( std::shared_ptr* isolate_group_data, std::shared_ptr* isolate_data); FML_DISALLOW_COPY_AND_ASSIGN(DartIsolate); }; } // namespace flutter #endif // FLUTTER_RUNTIME_DART_ISOLATE_H_