| // 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: |
| // The kernel mapping may be nullptr if reusing the group's loaded kernel. |
| explicit 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_), |
| /*child_isolate=*/false, |
| /*last_piece=*/true); |
| } |
| |
| // |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: |
| explicit 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]), /*child_isolate=*/false, |
| 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(const std::vector<std::string>& kernel_pieces_paths, |
| const std::shared_ptr<AssetManager>& asset_manager, |
| const 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, |
| const std::shared_ptr<AssetManager>& asset_manager, |
| const fml::RefPtr<fml::TaskRunner>& io_worker, |
| IsolateLaunchType launch_type) { |
| // Running in AOT mode. |
| if (DartVM::IsRunningPrecompiledCode()) { |
| return CreateForAppSnapshot(); |
| } |
| |
| if (launch_type == IsolateLaunchType::kExistingGroup) { |
| return CreateForKernel(nullptr); |
| } |
| |
| 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(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 |