// 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 "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/window.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; //------------------------------------------------------------------------------ /// @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: //---------------------------------------------------------------------------- /// @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] isolate_snapshot The isolate snapshot. This is /// usually obtained from the /// DartVMData associated with the /// running Dart VM instance. /// @param[in] task_runners The task runners used by the /// isolate. Via UI bindings, the /// isolate will use the IO task /// runner to scheduled texture /// decompression jobs and post tasks /// back to the UI task runner. /// @param[in] window The weak pointer to the window /// associated with this root isolate. /// @param[in] io_manager The i/o manager. /// @param[in] unref_queue The Skia unref queue. /// @param[in] image_decoder The image decoder. /// @param[in] advisory_script_uri The advisory script uri. This is /// only used in instrumentation. /// @param[in] advisory_script_entrypoint The advisory script entrypoint. /// This is only used in /// instrumentation. Notably, this is /// NOT the main entrypoint of Dart /// code in the isolate. That is /// specified when making one of the /// `Run` calls. /// @param[in] flags The Dart isolate flags for this /// isolate instance. /// @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. /// /// @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 CreateRootIsolate( const Settings& settings, fml::RefPtr isolate_snapshot, TaskRunners task_runners, std::unique_ptr window, fml::WeakPtr snapshot_delegate, fml::WeakPtr io_manager, fml::RefPtr skia_unref_queue, fml::WeakPtr image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, Dart_IsolateFlags* flags, const fml::closure& isolate_create_callback, const fml::closure& isolate_shutdown_callback); // |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. /// FML_WARN_UNUSED_RESULT 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. /// FML_WARN_UNUSED_RESULT bool PrepareForRunningFromKernel(std::shared_ptr kernel, bool last_piece = true); //---------------------------------------------------------------------------- /// @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. /// FML_WARN_UNUSED_RESULT 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. /// FML_WARN_UNUSED_RESULT 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 root /// library. The isolate must already be in the `Phase::Ready` /// phase. /// /// @param[in] entrypoint The entrypoint in the root library. /// @param[in] args A list of string arguments to the entrypoint. /// @param[in] on_run A callback to run in isolate scope after the main /// entrypoint has been invoked. There is no isolate /// scope current on the thread once this method /// returns. /// /// @return If the isolate successfully transitioned to the running phase /// and the main entrypoint was invoked. /// FML_WARN_UNUSED_RESULT bool Run(const std::string& entrypoint, const std::vector& args, const fml::closure& on_run = nullptr); //---------------------------------------------------------------------------- /// @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. /// @param[in] on_run A callback to run in isolate scope after the /// main entrypoint has been invoked. There is no /// isolate scope current on the thread once this /// method returns. /// /// @return If the isolate successfully transitioned to the running phase /// and the main entrypoint was invoked. /// FML_WARN_UNUSED_RESULT bool RunFromLibrary(const std::string& library_name, const std::string& entrypoint, const std::vector& args, const fml::closure& on_run = nullptr); //---------------------------------------------------------------------------- /// @brief Transition the isolate to the `Phase::Shutdown` phase. The /// only thing left to do is to collect the isolate. /// /// @return If the isolate succesfully transitioned to the shutdown phase. /// FML_WARN_UNUSED_RESULT 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; // Root isolate of the VM application bool IsRootIsolate() const { return is_root_isolate_; } private: 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> kernel_buffers_; std::vector> shutdown_callbacks_; fml::RefPtr message_handling_task_runner_; const bool is_root_isolate_; DartIsolate(const Settings& settings, TaskRunners task_runners, fml::WeakPtr snapshot_delegate, fml::WeakPtr io_manager, fml::RefPtr unref_queue, fml::WeakPtr image_decoder, std::string advisory_script_uri, std::string advisory_script_entrypoint, bool is_root_isolate); FML_WARN_UNUSED_RESULT bool Initialize(Dart_Isolate isolate); void SetMessageHandlingTaskRunner(fml::RefPtr runner); bool LoadKernel(std::shared_ptr mapping, bool last_piece); FML_WARN_UNUSED_RESULT bool LoadLibraries(); bool UpdateThreadPoolNames() const; FML_WARN_UNUSED_RESULT bool MarkIsolateRunnable(); void OnShutdownCallback(); DartIsolateGroupData& GetIsolateGroupData(); // |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); static Dart_Isolate CreateDartIsolateGroup( std::unique_ptr> isolate_group_data, std::unique_ptr> isolate_data, Dart_IsolateFlags* flags, char** error); static bool InitializeIsolate(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); FML_DISALLOW_COPY_AND_ASSIGN(DartIsolate); }; } // namespace flutter #endif // FLUTTER_RUNTIME_DART_ISOLATE_H_