blob: 58ae916c48084167eb4a8764bc5390b46ac74b57 [file] [log] [blame]
// 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 "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<String>
/// 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<DartIsolate> CreateRunningRootIsolate(
const Settings& settings,
const fml::RefPtr<const DartSnapshot>& isolate_snapshot,
std::unique_ptr<PlatformConfiguration> 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<std::string> dart_entrypoint,
std::optional<std::string> dart_entrypoint_library,
const std::vector<std::string>& dart_entrypoint_args,
std::unique_ptr<IsolateConfiguration> 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<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);
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<const fml::Mapping>& 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<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_;
const bool is_spawning_in_group_;
static std::weak_ptr<DartIsolate> CreateRootIsolate(
const Settings& settings,
fml::RefPtr<const DartSnapshot> isolate_snapshot,
std::unique_ptr<PlatformConfiguration> 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);
//----------------------------------------------------------------------------
/// @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<fml::TaskRunner>& runner);
bool LoadKernel(const std::shared_ptr<const fml::Mapping>& 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<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(
const 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_