| 1 | // Copyright 2013 The Flutter Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "flutter/runtime/isolate_configuration.h" |
| 6 | |
| 7 | #include "flutter/fml/make_copyable.h" |
| 8 | #include "flutter/runtime/dart_vm.h" |
| 9 | |
| 10 | namespace flutter { |
| 11 | |
| 12 | IsolateConfiguration::IsolateConfiguration() = default; |
| 13 | |
| 14 | IsolateConfiguration::~IsolateConfiguration() = default; |
| 15 | |
| 16 | bool IsolateConfiguration::PrepareIsolate(DartIsolate& isolate) { |
| 17 | if (isolate.GetPhase() != DartIsolate::Phase::LibrariesSetup) { |
| 18 | FML_DLOG(ERROR) |
| 19 | << "Isolate was in incorrect phase to be prepared for running." ; |
| 20 | return false; |
| 21 | } |
| 22 | |
| 23 | return DoPrepareIsolate(isolate); |
| 24 | } |
| 25 | |
| 26 | class AppSnapshotIsolateConfiguration final : public IsolateConfiguration { |
| 27 | public: |
| 28 | AppSnapshotIsolateConfiguration() = default; |
| 29 | |
| 30 | // |IsolateConfiguration| |
| 31 | bool DoPrepareIsolate(DartIsolate& isolate) override { |
| 32 | return isolate.PrepareForRunningFromPrecompiledCode(); |
| 33 | } |
| 34 | |
| 35 | // |IsolateConfiguration| |
| 36 | bool IsNullSafetyEnabled(const DartSnapshot& snapshot) override { |
| 37 | return snapshot.IsNullSafetyEnabled(application_kernel_mapping: nullptr); |
| 38 | } |
| 39 | |
| 40 | private: |
| 41 | FML_DISALLOW_COPY_AND_ASSIGN(AppSnapshotIsolateConfiguration); |
| 42 | }; |
| 43 | |
| 44 | class KernelIsolateConfiguration : public IsolateConfiguration { |
| 45 | public: |
| 46 | explicit KernelIsolateConfiguration( |
| 47 | std::unique_ptr<const fml::Mapping> kernel) |
| 48 | : kernel_(std::move(kernel)) {} |
| 49 | |
| 50 | // |IsolateConfiguration| |
| 51 | bool DoPrepareIsolate(DartIsolate& isolate) override { |
| 52 | if (DartVM::IsRunningPrecompiledCode()) { |
| 53 | return false; |
| 54 | } |
| 55 | return isolate.PrepareForRunningFromKernel(kernel: std::move(kernel_), |
| 56 | /*child_isolate=*/false, |
| 57 | /*last_piece=*/true); |
| 58 | } |
| 59 | |
| 60 | // |IsolateConfiguration| |
| 61 | bool IsNullSafetyEnabled(const DartSnapshot& snapshot) override { |
| 62 | return snapshot.IsNullSafetyEnabled(application_kernel_mapping: kernel_.get()); |
| 63 | } |
| 64 | |
| 65 | private: |
| 66 | std::unique_ptr<const fml::Mapping> kernel_; |
| 67 | |
| 68 | FML_DISALLOW_COPY_AND_ASSIGN(KernelIsolateConfiguration); |
| 69 | }; |
| 70 | |
| 71 | class KernelListIsolateConfiguration final : public IsolateConfiguration { |
| 72 | public: |
| 73 | explicit KernelListIsolateConfiguration( |
| 74 | std::vector<std::future<std::unique_ptr<const fml::Mapping>>> |
| 75 | kernel_pieces) |
| 76 | : kernel_piece_futures_(std::move(kernel_pieces)) { |
| 77 | if (kernel_piece_futures_.empty()) { |
| 78 | FML_LOG(ERROR) << "Attempted to create kernel list configuration without " |
| 79 | "any kernel blobs." ; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | // |IsolateConfiguration| |
| 84 | bool DoPrepareIsolate(DartIsolate& isolate) override { |
| 85 | if (DartVM::IsRunningPrecompiledCode()) { |
| 86 | return false; |
| 87 | } |
| 88 | |
| 89 | ResolveKernelPiecesIfNecessary(); |
| 90 | |
| 91 | if (resolved_kernel_pieces_.empty()) { |
| 92 | FML_DLOG(ERROR) << "No kernel pieces provided to prepare this isolate." ; |
| 93 | return false; |
| 94 | } |
| 95 | |
| 96 | for (size_t i = 0; i < resolved_kernel_pieces_.size(); i++) { |
| 97 | if (!resolved_kernel_pieces_[i]) { |
| 98 | FML_DLOG(ERROR) << "This kernel list isolate configuration was already " |
| 99 | "used to prepare an isolate." ; |
| 100 | return false; |
| 101 | } |
| 102 | const bool last_piece = i + 1 == resolved_kernel_pieces_.size(); |
| 103 | if (!isolate.PrepareForRunningFromKernel( |
| 104 | kernel: std::move(resolved_kernel_pieces_[i]), /*child_isolate=*/false, |
| 105 | last_piece)) { |
| 106 | return false; |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | return true; |
| 111 | } |
| 112 | |
| 113 | // |IsolateConfiguration| |
| 114 | bool IsNullSafetyEnabled(const DartSnapshot& snapshot) override { |
| 115 | ResolveKernelPiecesIfNecessary(); |
| 116 | const auto kernel = resolved_kernel_pieces_.empty() |
| 117 | ? nullptr |
| 118 | : resolved_kernel_pieces_.front().get(); |
| 119 | return snapshot.IsNullSafetyEnabled(application_kernel_mapping: kernel); |
| 120 | } |
| 121 | |
| 122 | // This must be call as late as possible before accessing any of the kernel |
| 123 | // pieces. This will delay blocking on the futures for as long as possible. So |
| 124 | // far, only Fuchsia depends on this optimization and only on the non-AOT |
| 125 | // configs. |
| 126 | void ResolveKernelPiecesIfNecessary() { |
| 127 | if (resolved_kernel_pieces_.size() == kernel_piece_futures_.size()) { |
| 128 | return; |
| 129 | } |
| 130 | |
| 131 | resolved_kernel_pieces_.clear(); |
| 132 | for (auto& piece : kernel_piece_futures_) { |
| 133 | // The get() call will xfer the unique pointer out and leave an empty |
| 134 | // future in the original vector. |
| 135 | resolved_kernel_pieces_.emplace_back(args: piece.get()); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | private: |
| 140 | std::vector<std::future<std::unique_ptr<const fml::Mapping>>> |
| 141 | kernel_piece_futures_; |
| 142 | std::vector<std::unique_ptr<const fml::Mapping>> resolved_kernel_pieces_; |
| 143 | |
| 144 | FML_DISALLOW_COPY_AND_ASSIGN(KernelListIsolateConfiguration); |
| 145 | }; |
| 146 | |
| 147 | static std::vector<std::string> ParseKernelListPaths( |
| 148 | std::unique_ptr<fml::Mapping> kernel_list) { |
| 149 | FML_DCHECK(kernel_list); |
| 150 | |
| 151 | std::vector<std::string> kernel_pieces_paths; |
| 152 | |
| 153 | const char* kernel_list_str = |
| 154 | reinterpret_cast<const char*>(kernel_list->GetMapping()); |
| 155 | size_t kernel_list_size = kernel_list->GetSize(); |
| 156 | |
| 157 | size_t piece_path_start = 0; |
| 158 | while (piece_path_start < kernel_list_size) { |
| 159 | size_t piece_path_end = piece_path_start; |
| 160 | while ((piece_path_end < kernel_list_size) && |
| 161 | (kernel_list_str[piece_path_end] != '\n')) { |
| 162 | piece_path_end++; |
| 163 | } |
| 164 | std::string piece_path(&kernel_list_str[piece_path_start], |
| 165 | piece_path_end - piece_path_start); |
| 166 | kernel_pieces_paths.emplace_back(args: std::move(piece_path)); |
| 167 | |
| 168 | piece_path_start = piece_path_end + 1; |
| 169 | } |
| 170 | |
| 171 | return kernel_pieces_paths; |
| 172 | } |
| 173 | |
| 174 | static std::vector<std::future<std::unique_ptr<const fml::Mapping>>> |
| 175 | PrepareKernelMappings(const std::vector<std::string>& kernel_pieces_paths, |
| 176 | const std::shared_ptr<AssetManager>& asset_manager, |
| 177 | const fml::RefPtr<fml::TaskRunner>& io_worker) { |
| 178 | FML_DCHECK(asset_manager); |
| 179 | std::vector<std::future<std::unique_ptr<const fml::Mapping>>> fetch_futures; |
| 180 | |
| 181 | for (const auto& kernel_pieces_path : kernel_pieces_paths) { |
| 182 | std::promise<std::unique_ptr<const fml::Mapping>> fetch_promise; |
| 183 | fetch_futures.push_back(x: fetch_promise.get_future()); |
| 184 | auto fetch_task = |
| 185 | fml::MakeCopyable(lambda: [asset_manager, kernel_pieces_path, |
| 186 | fetch_promise = std::move(fetch_promise)]() mutable { |
| 187 | fetch_promise.set_value( |
| 188 | asset_manager->GetAsMapping(asset_name: kernel_pieces_path)); |
| 189 | }); |
| 190 | // Fulfill the promise on the worker if one is available or the current |
| 191 | // thread if one is not. |
| 192 | if (io_worker) { |
| 193 | io_worker->PostTask(task: fetch_task); |
| 194 | } else { |
| 195 | fetch_task(); |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | return fetch_futures; |
| 200 | } |
| 201 | |
| 202 | std::unique_ptr<IsolateConfiguration> IsolateConfiguration::InferFromSettings( |
| 203 | const Settings& settings, |
| 204 | const std::shared_ptr<AssetManager>& asset_manager, |
| 205 | const fml::RefPtr<fml::TaskRunner>& io_worker) { |
| 206 | // Running in AOT mode. |
| 207 | if (DartVM::IsRunningPrecompiledCode()) { |
| 208 | return CreateForAppSnapshot(); |
| 209 | } |
| 210 | |
| 211 | if (settings.application_kernels) { |
| 212 | return CreateForKernelList(kernel_pieces: settings.application_kernels()); |
| 213 | } |
| 214 | |
| 215 | if (settings.application_kernel_asset.empty() && |
| 216 | settings.application_kernel_list_asset.empty()) { |
| 217 | FML_DLOG(ERROR) << "application_kernel_asset or " |
| 218 | "application_kernel_list_asset must be set" ; |
| 219 | return nullptr; |
| 220 | } |
| 221 | |
| 222 | if (!asset_manager) { |
| 223 | FML_DLOG(ERROR) << "No asset manager specified when attempting to create " |
| 224 | "isolate configuration." ; |
| 225 | return nullptr; |
| 226 | } |
| 227 | |
| 228 | // Running from kernel snapshot. Requires asset manager. |
| 229 | { |
| 230 | std::unique_ptr<fml::Mapping> kernel = |
| 231 | asset_manager->GetAsMapping(asset_name: settings.application_kernel_asset); |
| 232 | if (kernel) { |
| 233 | return CreateForKernel(kernel: std::move(kernel)); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | // Running from kernel divided into several pieces (for sharing). Requires |
| 238 | // asset manager and io worker. |
| 239 | |
| 240 | if (!io_worker) { |
| 241 | FML_DLOG(ERROR) << "No IO worker specified to load kernel pieces." ; |
| 242 | return nullptr; |
| 243 | } |
| 244 | |
| 245 | { |
| 246 | std::unique_ptr<fml::Mapping> kernel_list = |
| 247 | asset_manager->GetAsMapping(asset_name: settings.application_kernel_list_asset); |
| 248 | if (!kernel_list) { |
| 249 | FML_LOG(ERROR) << "Failed to load: " |
| 250 | << settings.application_kernel_list_asset; |
| 251 | return nullptr; |
| 252 | } |
| 253 | auto kernel_pieces_paths = ParseKernelListPaths(kernel_list: std::move(kernel_list)); |
| 254 | auto kernel_mappings = |
| 255 | PrepareKernelMappings(kernel_pieces_paths, asset_manager, io_worker); |
| 256 | return CreateForKernelList(kernel_pieces: std::move(kernel_mappings)); |
| 257 | } |
| 258 | |
| 259 | return nullptr; |
| 260 | } |
| 261 | |
| 262 | std::unique_ptr<IsolateConfiguration> |
| 263 | IsolateConfiguration::CreateForAppSnapshot() { |
| 264 | return std::make_unique<AppSnapshotIsolateConfiguration>(); |
| 265 | } |
| 266 | |
| 267 | std::unique_ptr<IsolateConfiguration> IsolateConfiguration::CreateForKernel( |
| 268 | std::unique_ptr<const fml::Mapping> kernel) { |
| 269 | return std::make_unique<KernelIsolateConfiguration>(args: std::move(kernel)); |
| 270 | } |
| 271 | |
| 272 | std::unique_ptr<IsolateConfiguration> IsolateConfiguration::CreateForKernelList( |
| 273 | std::vector<std::unique_ptr<const fml::Mapping>> kernel_pieces) { |
| 274 | std::vector<std::future<std::unique_ptr<const fml::Mapping>>> pieces; |
| 275 | for (auto& piece : kernel_pieces) { |
| 276 | if (!piece) { |
| 277 | FML_DLOG(ERROR) << "Invalid kernel piece." ; |
| 278 | continue; |
| 279 | } |
| 280 | std::promise<std::unique_ptr<const fml::Mapping>> promise; |
| 281 | pieces.push_back(x: promise.get_future()); |
| 282 | promise.set_value(std::move(piece)); |
| 283 | } |
| 284 | return CreateForKernelList(kernel_pieces: std::move(pieces)); |
| 285 | } |
| 286 | |
| 287 | std::unique_ptr<IsolateConfiguration> IsolateConfiguration::CreateForKernelList( |
| 288 | std::vector<std::future<std::unique_ptr<const fml::Mapping>>> |
| 289 | kernel_pieces) { |
| 290 | return std::make_unique<KernelListIsolateConfiguration>( |
| 291 | args: std::move(kernel_pieces)); |
| 292 | } |
| 293 | |
| 294 | } // namespace flutter |
| 295 | |