mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Embedders that have access to the Dart native API (only Fuchsia now) may perform library setup in the isolate create callback. The engine used to depend on the fact the root isolate entrypoint is invoked in the next iteration of message loop (via the `_startIsolate` trampoline in `isolate_patch.dart`) to ensure that library setup occur before the main entrypoint was invoked. However, due to differences in the way in which message loops are setup in Fuchsia, this entrypoint was run before the callback could be executed. Dart code on Fuchsia also has the ability to access the underlying event loops directly. This patch moves the invocation of the create callback to before user dart code has a chance to run. This difference in behavior on Fuchsia became an issue when the isolate initialization was reworked in https://github.com/flutter/engine/pull/21820 for null-safety. Another issue was discovered in that the callback was being invoked twice, I fixed that too and added a test. Fixes https://github.com/flutter/flutter/issues/68732
501 lines
23 KiB
C++
501 lines
23 KiB
C++
// 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 "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/hint_freed_delegate.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] 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. This callback is made for
|
|
/// all isolate launches (including
|
|
/// the children of the root isolate).
|
|
/// @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. This callback is made
|
|
/// for all isolate shutdowns
|
|
/// (including the children of the
|
|
/// root 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,
|
|
fml::RefPtr<const DartSnapshot> isolate_snapshot,
|
|
TaskRunners task_runners,
|
|
std::unique_ptr<PlatformConfiguration> platform_configuration,
|
|
fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
|
|
fml::WeakPtr<HintFreedDelegate> hint_freed_delegate,
|
|
fml::WeakPtr<IOManager> io_manager,
|
|
fml::RefPtr<SkiaUnrefQueue> skia_unref_queue,
|
|
fml::WeakPtr<ImageDecoder> image_decoder,
|
|
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);
|
|
|
|
// |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 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.
|
|
///
|
|
[[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 succesfully 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;
|
|
|
|
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_;
|
|
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,
|
|
TaskRunners task_runners,
|
|
std::unique_ptr<PlatformConfiguration> platform_configuration,
|
|
fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
|
|
fml::WeakPtr<HintFreedDelegate> hint_freed_delegate,
|
|
fml::WeakPtr<IOManager> io_manager,
|
|
fml::RefPtr<SkiaUnrefQueue> skia_unref_queue,
|
|
fml::WeakPtr<ImageDecoder> image_decoder,
|
|
std::string advisory_script_uri,
|
|
std::string advisory_script_entrypoint,
|
|
Flags flags,
|
|
const fml::closure& isolate_create_callback,
|
|
const fml::closure& isolate_shutdown_callback);
|
|
|
|
DartIsolate(const Settings& settings,
|
|
TaskRunners task_runners,
|
|
fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
|
|
fml::WeakPtr<HintFreedDelegate> hint_freed_delegate,
|
|
fml::WeakPtr<IOManager> io_manager,
|
|
fml::RefPtr<SkiaUnrefQueue> unref_queue,
|
|
fml::WeakPtr<ImageDecoder> image_decoder,
|
|
std::string advisory_script_uri,
|
|
std::string advisory_script_entrypoint,
|
|
bool is_root_isolate);
|
|
|
|
[[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();
|
|
|
|
// |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);
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
FML_DISALLOW_COPY_AND_ASSIGN(DartIsolate);
|
|
};
|
|
|
|
} // namespace flutter
|
|
|
|
#endif // FLUTTER_RUNTIME_DART_ISOLATE_H_
|