mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Snapshots compiled with sound null-safety enabled require changes to the way in which isolates are launched. Specifically, the `Dart_IsolateFlags::null_safety` field needs to be known upfront. The value of this field can only be determined once the kernel snapshot is available. This poses a problem in the engine because the engine used to launch the isolate at shell initialization and only need the kernel mappings later at isolate launch (when transitioning the root isolate to the `DartIsolate::Phase::Running` phase). This patch delays launch of the isolate on the UI task runner till a kernel mapping is available. The side effects of this delay (callers no longer having access to the non-running isolate handle) have been addressed in this patch. The DartIsolate API has also been amended to hide the method that could return a non-running isolate to the caller. Instead, it has been replaced with a method that requires a valid isolate configuration that returns a running root isolate. The isolate will be launched by asking the isolate configuration for its null-safety characteristics. A side effect of enabling null-safety is that Dart APIs that work with legacy types will now terminate the process if used with an isolate that has sound null-safety enabled. These APIs may no longer be used in the engine. This primarily affects the Dart Convertors in Tonic that convert certain C++ objects into the Dart counterparts. All known Dart Converters have been updated to convert C++ objects to non-nullable Dart types inferred using type traits of the corresponding C++ object. The few spots in the engine that used the old Dart APIs directly have been manually updated. To ensure that no usage of the legacy APIs remain in the engine (as these would cause runtime process terminations), the legacy APIs were prefixed with the `DART_LEGACY_API` macro and the macro defined to `[[deprecated]]` in all engine translation units. While the engine now primarily works with non-nullable Dart types, callers can still use `Dart_TypeToNonNullableType` to acquire nullable types for use directly or with Tonic. One use case that is not addressed with the Tonic Dart Convertors is the creation of non-nullable lists of nullable types. This hasn’t come up so far in the engine. A minor related change is reworking tonic to define a single library target. This allows the various tonic subsystems to depend on one another. Primarily, this is used to make the Dart convertors use the logging utilities. This now allows errors to be more descriptive as the presence of error handles is caught (and logged) earlier. Fixes https://github.com/flutter/flutter/issues/59879
291 lines
9.4 KiB
C++
291 lines
9.4 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.
|
|
|
|
#include "flutter/runtime/isolate_configuration.h"
|
|
|
|
#include "flutter/fml/make_copyable.h"
|
|
#include "flutter/runtime/dart_vm.h"
|
|
|
|
namespace flutter {
|
|
|
|
IsolateConfiguration::IsolateConfiguration() = default;
|
|
|
|
IsolateConfiguration::~IsolateConfiguration() = default;
|
|
|
|
bool IsolateConfiguration::PrepareIsolate(DartIsolate& isolate) {
|
|
if (isolate.GetPhase() != DartIsolate::Phase::LibrariesSetup) {
|
|
FML_DLOG(ERROR)
|
|
<< "Isolate was in incorrect phase to be prepared for running.";
|
|
return false;
|
|
}
|
|
|
|
return DoPrepareIsolate(isolate);
|
|
}
|
|
|
|
class AppSnapshotIsolateConfiguration final : public IsolateConfiguration {
|
|
public:
|
|
AppSnapshotIsolateConfiguration() = default;
|
|
|
|
// |IsolateConfiguration|
|
|
bool DoPrepareIsolate(DartIsolate& isolate) override {
|
|
return isolate.PrepareForRunningFromPrecompiledCode();
|
|
}
|
|
|
|
// |IsolateConfiguration|
|
|
bool IsNullSafetyEnabled(const DartSnapshot& snapshot) override {
|
|
return snapshot.IsNullSafetyEnabled(nullptr);
|
|
}
|
|
|
|
private:
|
|
FML_DISALLOW_COPY_AND_ASSIGN(AppSnapshotIsolateConfiguration);
|
|
};
|
|
|
|
class KernelIsolateConfiguration : public IsolateConfiguration {
|
|
public:
|
|
KernelIsolateConfiguration(std::unique_ptr<const fml::Mapping> kernel)
|
|
: kernel_(std::move(kernel)) {}
|
|
|
|
// |IsolateConfiguration|
|
|
bool DoPrepareIsolate(DartIsolate& isolate) override {
|
|
if (DartVM::IsRunningPrecompiledCode()) {
|
|
return false;
|
|
}
|
|
return isolate.PrepareForRunningFromKernel(std::move(kernel_));
|
|
}
|
|
|
|
// |IsolateConfiguration|
|
|
bool IsNullSafetyEnabled(const DartSnapshot& snapshot) override {
|
|
return snapshot.IsNullSafetyEnabled(kernel_.get());
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<const fml::Mapping> kernel_;
|
|
|
|
FML_DISALLOW_COPY_AND_ASSIGN(KernelIsolateConfiguration);
|
|
};
|
|
|
|
class KernelListIsolateConfiguration final : public IsolateConfiguration {
|
|
public:
|
|
KernelListIsolateConfiguration(
|
|
std::vector<std::future<std::unique_ptr<const fml::Mapping>>>
|
|
kernel_pieces)
|
|
: kernel_piece_futures_(std::move(kernel_pieces)) {
|
|
if (kernel_piece_futures_.empty()) {
|
|
FML_LOG(ERROR) << "Attempted to create kernel list configuration without "
|
|
"any kernel blobs.";
|
|
}
|
|
}
|
|
|
|
// |IsolateConfiguration|
|
|
bool DoPrepareIsolate(DartIsolate& isolate) override {
|
|
if (DartVM::IsRunningPrecompiledCode()) {
|
|
return false;
|
|
}
|
|
|
|
ResolveKernelPiecesIfNecessary();
|
|
|
|
if (resolved_kernel_pieces_.empty()) {
|
|
FML_DLOG(ERROR) << "No kernel pieces provided to prepare this isolate.";
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < resolved_kernel_pieces_.size(); i++) {
|
|
if (!resolved_kernel_pieces_[i]) {
|
|
FML_DLOG(ERROR) << "This kernel list isolate configuration was already "
|
|
"used to prepare an isolate.";
|
|
return false;
|
|
}
|
|
const bool last_piece = i + 1 == resolved_kernel_pieces_.size();
|
|
if (!isolate.PrepareForRunningFromKernel(
|
|
std::move(resolved_kernel_pieces_[i]), last_piece)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// |IsolateConfiguration|
|
|
bool IsNullSafetyEnabled(const DartSnapshot& snapshot) override {
|
|
ResolveKernelPiecesIfNecessary();
|
|
const auto kernel = resolved_kernel_pieces_.empty()
|
|
? nullptr
|
|
: resolved_kernel_pieces_.front().get();
|
|
return snapshot.IsNullSafetyEnabled(kernel);
|
|
}
|
|
|
|
// This must be call as late as possible before accessing any of the kernel
|
|
// pieces. This will delay blocking on the futures for as long as possible. So
|
|
// far, only Fuchsia depends on this optimization and only on the non-AOT
|
|
// configs.
|
|
void ResolveKernelPiecesIfNecessary() {
|
|
if (resolved_kernel_pieces_.size() == kernel_piece_futures_.size()) {
|
|
return;
|
|
}
|
|
|
|
resolved_kernel_pieces_.clear();
|
|
for (auto& piece : kernel_piece_futures_) {
|
|
// The get() call will xfer the unique pointer out and leave an empty
|
|
// future in the original vector.
|
|
resolved_kernel_pieces_.emplace_back(piece.get());
|
|
}
|
|
}
|
|
|
|
private:
|
|
std::vector<std::future<std::unique_ptr<const fml::Mapping>>>
|
|
kernel_piece_futures_;
|
|
std::vector<std::unique_ptr<const fml::Mapping>> resolved_kernel_pieces_;
|
|
|
|
FML_DISALLOW_COPY_AND_ASSIGN(KernelListIsolateConfiguration);
|
|
};
|
|
|
|
static std::vector<std::string> ParseKernelListPaths(
|
|
std::unique_ptr<fml::Mapping> kernel_list) {
|
|
FML_DCHECK(kernel_list);
|
|
|
|
std::vector<std::string> kernel_pieces_paths;
|
|
|
|
const char* kernel_list_str =
|
|
reinterpret_cast<const char*>(kernel_list->GetMapping());
|
|
size_t kernel_list_size = kernel_list->GetSize();
|
|
|
|
size_t piece_path_start = 0;
|
|
while (piece_path_start < kernel_list_size) {
|
|
size_t piece_path_end = piece_path_start;
|
|
while ((piece_path_end < kernel_list_size) &&
|
|
(kernel_list_str[piece_path_end] != '\n')) {
|
|
piece_path_end++;
|
|
}
|
|
std::string piece_path(&kernel_list_str[piece_path_start],
|
|
piece_path_end - piece_path_start);
|
|
kernel_pieces_paths.emplace_back(std::move(piece_path));
|
|
|
|
piece_path_start = piece_path_end + 1;
|
|
}
|
|
|
|
return kernel_pieces_paths;
|
|
}
|
|
|
|
static std::vector<std::future<std::unique_ptr<const fml::Mapping>>>
|
|
PrepareKernelMappings(std::vector<std::string> kernel_pieces_paths,
|
|
std::shared_ptr<AssetManager> asset_manager,
|
|
fml::RefPtr<fml::TaskRunner> io_worker) {
|
|
FML_DCHECK(asset_manager);
|
|
std::vector<std::future<std::unique_ptr<const fml::Mapping>>> fetch_futures;
|
|
|
|
for (const auto& kernel_pieces_path : kernel_pieces_paths) {
|
|
std::promise<std::unique_ptr<const fml::Mapping>> fetch_promise;
|
|
fetch_futures.push_back(fetch_promise.get_future());
|
|
auto fetch_task =
|
|
fml::MakeCopyable([asset_manager, kernel_pieces_path,
|
|
fetch_promise = std::move(fetch_promise)]() mutable {
|
|
fetch_promise.set_value(
|
|
asset_manager->GetAsMapping(kernel_pieces_path));
|
|
});
|
|
// Fulfill the promise on the worker if one is available or the current
|
|
// thread if one is not.
|
|
if (io_worker) {
|
|
io_worker->PostTask(fetch_task);
|
|
} else {
|
|
fetch_task();
|
|
}
|
|
}
|
|
|
|
return fetch_futures;
|
|
}
|
|
|
|
std::unique_ptr<IsolateConfiguration> IsolateConfiguration::InferFromSettings(
|
|
const Settings& settings,
|
|
std::shared_ptr<AssetManager> asset_manager,
|
|
fml::RefPtr<fml::TaskRunner> io_worker) {
|
|
// Running in AOT mode.
|
|
if (DartVM::IsRunningPrecompiledCode()) {
|
|
return CreateForAppSnapshot();
|
|
}
|
|
|
|
if (settings.application_kernels) {
|
|
return CreateForKernelList(settings.application_kernels());
|
|
}
|
|
|
|
if (settings.application_kernel_asset.empty() &&
|
|
settings.application_kernel_list_asset.empty()) {
|
|
FML_DLOG(ERROR) << "application_kernel_asset or "
|
|
"application_kernel_list_asset must be set";
|
|
return nullptr;
|
|
}
|
|
|
|
if (!asset_manager) {
|
|
FML_DLOG(ERROR) << "No asset manager specified when attempting to create "
|
|
"isolate configuration.";
|
|
return nullptr;
|
|
}
|
|
|
|
// Running from kernel snapshot. Requires asset manager.
|
|
{
|
|
std::unique_ptr<fml::Mapping> kernel =
|
|
asset_manager->GetAsMapping(settings.application_kernel_asset);
|
|
if (kernel) {
|
|
return CreateForKernel(std::move(kernel));
|
|
}
|
|
}
|
|
|
|
// Running from kernel divided into several pieces (for sharing). Requires
|
|
// asset manager and io worker.
|
|
|
|
if (!io_worker) {
|
|
FML_DLOG(ERROR) << "No IO worker specified to load kernel pieces.";
|
|
return nullptr;
|
|
}
|
|
|
|
{
|
|
std::unique_ptr<fml::Mapping> kernel_list =
|
|
asset_manager->GetAsMapping(settings.application_kernel_list_asset);
|
|
if (!kernel_list) {
|
|
FML_LOG(ERROR) << "Failed to load: "
|
|
<< settings.application_kernel_list_asset;
|
|
return nullptr;
|
|
}
|
|
auto kernel_pieces_paths = ParseKernelListPaths(std::move(kernel_list));
|
|
auto kernel_mappings = PrepareKernelMappings(std::move(kernel_pieces_paths),
|
|
asset_manager, io_worker);
|
|
return CreateForKernelList(std::move(kernel_mappings));
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<IsolateConfiguration>
|
|
IsolateConfiguration::CreateForAppSnapshot() {
|
|
return std::make_unique<AppSnapshotIsolateConfiguration>();
|
|
}
|
|
|
|
std::unique_ptr<IsolateConfiguration> IsolateConfiguration::CreateForKernel(
|
|
std::unique_ptr<const fml::Mapping> kernel) {
|
|
return std::make_unique<KernelIsolateConfiguration>(std::move(kernel));
|
|
}
|
|
|
|
std::unique_ptr<IsolateConfiguration> IsolateConfiguration::CreateForKernelList(
|
|
std::vector<std::unique_ptr<const fml::Mapping>> kernel_pieces) {
|
|
std::vector<std::future<std::unique_ptr<const fml::Mapping>>> pieces;
|
|
for (auto& piece : kernel_pieces) {
|
|
if (!piece) {
|
|
FML_DLOG(ERROR) << "Invalid kernel piece.";
|
|
continue;
|
|
}
|
|
std::promise<std::unique_ptr<const fml::Mapping>> promise;
|
|
pieces.push_back(promise.get_future());
|
|
promise.set_value(std::move(piece));
|
|
}
|
|
return CreateForKernelList(std::move(pieces));
|
|
}
|
|
|
|
std::unique_ptr<IsolateConfiguration> IsolateConfiguration::CreateForKernelList(
|
|
std::vector<std::future<std::unique_ptr<const fml::Mapping>>>
|
|
kernel_pieces) {
|
|
return std::make_unique<KernelListIsolateConfiguration>(
|
|
std::move(kernel_pieces));
|
|
}
|
|
|
|
} // namespace flutter
|