From 211da364be3fea334106a54ba1bf50308676cfd1 Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Wed, 23 Feb 2022 16:55:52 +0000 Subject: [PATCH] [vm] Experimental ability to spawn isolate from kernel blob in memory TEST=runtime/tests/vm/dart/spawn_uri_from_kernel_blob_test.dart Change-Id: Ieb327f0350d5d8ea1d344c64aa3dd217125da5fe Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/232682 Reviewed-by: Ryan Macnak Reviewed-by: Siva Annamalai Commit-Queue: Alexander Markov --- runtime/bin/dfe.cc | 100 ++++++++++++++++-- runtime/bin/dfe.h | 71 +++++++++++-- runtime/bin/loader.cc | 4 +- runtime/bin/main.cc | 27 +++-- runtime/include/dart_api.h | 40 +++++++ runtime/lib/isolate.cc | 56 ++++++++++ runtime/platform/hashmap.h | 2 +- .../spawn_uri_from_kernel_blob_script.dart | 11 ++ .../dart/spawn_uri_from_kernel_blob_test.dart | 63 +++++++++++ runtime/tests/vm/vm.status | 1 + runtime/vm/bootstrap_natives.h | 2 + runtime/vm/dart.cc | 2 + runtime/vm/isolate.cc | 6 ++ runtime/vm/isolate.h | 16 +++ sdk/lib/_internal/vm/lib/isolate_patch.dart | 25 +++++ 15 files changed, 401 insertions(+), 25 deletions(-) create mode 100644 runtime/tests/vm/dart/spawn_uri_from_kernel_blob_script.dart create mode 100644 runtime/tests/vm/dart/spawn_uri_from_kernel_blob_test.dart diff --git a/runtime/bin/dfe.cc b/runtime/bin/dfe.cc index b29c8c1e919..e2743515492 100644 --- a/runtime/bin/dfe.cc +++ b/runtime/bin/dfe.cc @@ -9,6 +9,7 @@ #include "bin/error_exit.h" #include "bin/exe_utils.h" #include "bin/file.h" +#include "bin/lockers.h" #include "bin/platform.h" #include "bin/utils.h" #include "include/dart_tools_api.h" @@ -65,8 +66,9 @@ DFE::DFE() use_incremental_compiler_(false), frontend_filename_(nullptr), application_kernel_buffer_(nullptr), - application_kernel_buffer_size_(0) { -} + application_kernel_buffer_size_(0), + kernel_blobs_(&SimpleHashMap::SameStringValue, 4), + kernel_blobs_lock_() {} DFE::~DFE() { if (frontend_filename_ != nullptr) { @@ -77,6 +79,9 @@ DFE::~DFE() { free(application_kernel_buffer_); application_kernel_buffer_ = nullptr; application_kernel_buffer_size_ = 0; + + kernel_blobs_.Clear( + [](void* value) { delete reinterpret_cast(value); }); } void DFE::Init() { @@ -247,14 +252,19 @@ void DFE::CompileAndReadScript(const char* script_uri, void DFE::ReadScript(const char* script_uri, uint8_t** kernel_buffer, intptr_t* kernel_buffer_size, - bool decode_uri) const { + bool decode_uri, + std::shared_ptr* kernel_blob_ptr) { int64_t start = Dart_TimelineGetMicros(); if (!TryReadKernelFile(script_uri, kernel_buffer, kernel_buffer_size, - decode_uri)) { + decode_uri, kernel_blob_ptr)) { return; } if (!Dart_IsKernel(*kernel_buffer, *kernel_buffer_size)) { - free(*kernel_buffer); + if (kernel_blob_ptr != nullptr && *kernel_blob_ptr) { + *kernel_blob_ptr = nullptr; + } else { + free(*kernel_buffer); + } *kernel_buffer = nullptr; *kernel_buffer_size = -1; } @@ -438,10 +448,21 @@ static bool TryReadKernelListBuffer(const char* script_uri, bool DFE::TryReadKernelFile(const char* script_uri, uint8_t** kernel_ir, intptr_t* kernel_ir_size, - bool decode_uri) { + bool decode_uri, + std::shared_ptr* kernel_blob_ptr) { *kernel_ir = nullptr; *kernel_ir_size = -1; + if (decode_uri && kernel_blob_ptr != nullptr) { + *kernel_blob_ptr = TryFindKernelBlob(script_uri, kernel_ir_size); + if (*kernel_blob_ptr) { + *kernel_ir = kernel_blob_ptr->get(); + ASSERT(DartUtils::SniffForMagicNumber(*kernel_ir, *kernel_ir_size) == + DartUtils::kKernelMagicNumber); + return true; + } + } + uint8_t* buffer; if (!TryReadFile(script_uri, &buffer, kernel_ir_size, decode_uri)) { return false; @@ -456,5 +477,72 @@ bool DFE::TryReadKernelFile(const char* script_uri, return TryReadSimpleKernelBuffer(buffer, kernel_ir, kernel_ir_size); } +const char* DFE::RegisterKernelBlob(const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size) { + ASSERT(DartUtils::SniffForMagicNumber(kernel_buffer, kernel_buffer_size) == + DartUtils::kKernelMagicNumber); + uint8_t* buffer_copy = reinterpret_cast(malloc(kernel_buffer_size)); + if (buffer_copy == nullptr) { + return nullptr; + } + memmove(buffer_copy, kernel_buffer, kernel_buffer_size); + + MutexLocker ml(&kernel_blobs_lock_); + ++kernel_blob_counter_; + char* uri = + Utils::SCreate("dart-kernel-blob://blob%" Pd, kernel_blob_counter_); + KernelBlob* blob = new KernelBlob(uri, buffer_copy, kernel_buffer_size); + + const uint32_t hash = SimpleHashMap::StringHash(uri); + SimpleHashMap::Entry* entry = + kernel_blobs_.Lookup(uri, hash, /*insert=*/true); + ASSERT(entry != nullptr); + ASSERT(entry->value == nullptr); + entry->value = blob; + + return uri; +} + +std::shared_ptr DFE::TryFindKernelBlob(const char* uri, + intptr_t* kernel_length) { + *kernel_length = -1; + + MutexLocker ml(&kernel_blobs_lock_); + if (kernel_blob_counter_ == 0) { + return nullptr; + } + + // This const_cast is safe as this 'key' is only used to find entry, not add. + void* key = const_cast(uri); + const uint32_t hash = SimpleHashMap::StringHash(uri); + SimpleHashMap::Entry* entry = + kernel_blobs_.Lookup(key, hash, /*insert=*/false); + if (entry == nullptr) { + return nullptr; + } + + KernelBlob* blob = reinterpret_cast(entry->value); + *kernel_length = blob->size(); + return blob->buffer(); +} + +void DFE::UnregisterKernelBlob(const char* uri) { + MutexLocker ml(&kernel_blobs_lock_); + + // This const_cast is safe as this 'key' is only used to find entry, not add. + void* key = const_cast(uri); + const uint32_t hash = SimpleHashMap::StringHash(uri); + SimpleHashMap::Entry* entry = + kernel_blobs_.Lookup(key, hash, /*insert=*/false); + if (entry == nullptr) { + return; + } + + KernelBlob* blob = reinterpret_cast(entry->value); + entry->value = nullptr; + kernel_blobs_.Remove(key, hash); + delete blob; +} + } // namespace bin } // namespace dart diff --git a/runtime/bin/dfe.h b/runtime/bin/dfe.h index eb18df6caba..21700db6127 100644 --- a/runtime/bin/dfe.h +++ b/runtime/bin/dfe.h @@ -7,10 +7,12 @@ #include +#include "bin/thread.h" #include "include/dart_api.h" #include "include/dart_native_api.h" #include "platform/assert.h" #include "platform/globals.h" +#include "platform/hashmap.h" #include "platform/utils.h" namespace dart { @@ -102,23 +104,34 @@ class DFE { // Reads the script kernel file if specified 'script_uri' is a kernel file. // Returns an in memory kernel representation of the specified script is a - // valid kernel file, false otherwise. + // valid kernel file, sets 'kernel_buffer' to nullptr otherwise. + // + // If 'kernel_blob_ptr' is not nullptr, then this function can also + // read kernel blobs. In such case it sets 'kernel_blob_ptr' + // to a shared pointer which owns the kernel buffer. + // Othwerise, the caller is responsible for free()ing 'kernel_buffer'. void ReadScript(const char* script_uri, uint8_t** kernel_buffer, intptr_t* kernel_buffer_size, - bool decode_uri = true) const; + bool decode_uri = true, + std::shared_ptr* kernel_blob_ptr = nullptr); bool KernelServiceDillAvailable() const; - // Tries to read [script_uri] as a Kernel IR file. - // Returns `true` if successful and sets [kernel_file] and [kernel_length] + // Tries to read 'script_uri' as a Kernel IR file. + // Returns `true` if successful and sets 'kernel_buffer' and 'kernel_length' // to be the kernel IR contents. - // The caller is responsible for free()ing [kernel_file] if `true` - // was returned. - static bool TryReadKernelFile(const char* script_uri, - uint8_t** kernel_buffer, - intptr_t* kernel_buffer_size, - bool decode_uri = true); + // + // If 'kernel_blob_ptr' is not nullptr, then this function can also + // read kernel blobs. In such case it sets 'kernel_blob_ptr' + // to a shared pointer which owns the kernel buffer. + // Othwerise, the caller is responsible for free()ing 'kernel_buffer' + // if `true` was returned. + bool TryReadKernelFile(const char* script_uri, + uint8_t** kernel_buffer, + intptr_t* kernel_buffer_size, + bool decode_uri = true, + std::shared_ptr* kernel_blob_ptr = nullptr); // We distinguish between "intent to use Dart frontend" vs "can actually // use Dart frontend". The method UseDartFrontend tells us about the @@ -131,6 +144,22 @@ class DFE { void LoadKernelService(const uint8_t** kernel_service_buffer, intptr_t* kernel_service_buffer_size); + // Registers given kernel blob and returns blob URI which + // can be used in TryReadKernelFile later to load the given kernel. + // Data from [kernel_buffer] is copied, it doesn't need to stay alive. + // Returns NULL if failed to allocate memory. + const char* RegisterKernelBlob(const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size); + + // Looks for kernel blob using the given [uri]. + // Returns non-null pointer to the kernel blob if successful and + // sets [kernel_length]. + std::shared_ptr TryFindKernelBlob(const char* uri, + intptr_t* kernel_length); + + // Unregisters kernel blob with given URI. + void UnregisterKernelBlob(const char* uri); + private: bool use_dfe_; bool use_incremental_compiler_; @@ -142,11 +171,33 @@ class DFE { uint8_t* application_kernel_buffer_; intptr_t application_kernel_buffer_size_; + // Registry of kernel blobs. Maps URI (char *) to KernelBlob. + SimpleHashMap kernel_blobs_; + intptr_t kernel_blob_counter_ = 0; + Mutex kernel_blobs_lock_; + void InitKernelServiceAndPlatformDills(); DISALLOW_COPY_AND_ASSIGN(DFE); }; +class KernelBlob { + public: + // Takes ownership over [uri] and [buffer]. + KernelBlob(char* uri, uint8_t* buffer, intptr_t size) + : uri_(uri, std::free), buffer_(buffer, std::free), size_(size) {} + + std::shared_ptr buffer() { return buffer_; } + intptr_t size() const { return size_; } + + private: + Utils::CStringUniquePtr uri_; + std::shared_ptr buffer_; + const intptr_t size_; + + DISALLOW_COPY_AND_ASSIGN(KernelBlob); +}; + class PathSanitizer { public: explicit PathSanitizer(const char* path); diff --git a/runtime/bin/loader.cc b/runtime/bin/loader.cc index 045bf33621b..709fc54032c 100644 --- a/runtime/bin/loader.cc +++ b/runtime/bin/loader.cc @@ -83,8 +83,8 @@ Dart_Handle Loader::LibraryTagHandler(Dart_LibraryTag tag, if (tag == Dart_kKernelTag) { uint8_t* kernel_buffer = NULL; intptr_t kernel_buffer_size = 0; - if (!DFE::TryReadKernelFile(url_string, &kernel_buffer, - &kernel_buffer_size)) { + if (!dfe.TryReadKernelFile(url_string, &kernel_buffer, + &kernel_buffer_size)) { return DartUtils::NewError("'%s' is not a kernel file", url_string); } result = Dart_NewExternalTypedData(Dart_TypedData_kUint8, kernel_buffer, diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc index 3461f25700e..52beba40e85 100644 --- a/runtime/bin/main.cc +++ b/runtime/bin/main.cc @@ -689,7 +689,7 @@ static Dart_Isolate CreateIsolateGroupAndSetupHelper( int64_t start = Dart_TimelineGetMicros(); ASSERT(script_uri != NULL); uint8_t* kernel_buffer = NULL; - std::shared_ptr parent_kernel_buffer; + std::shared_ptr kernel_buffer_ptr; intptr_t kernel_buffer_size = 0; AppSnapshot* app_snapshot = NULL; @@ -750,13 +750,14 @@ static Dart_Isolate CreateIsolateGroupAndSetupHelper( if (flags->copy_parent_code && callback_data != nullptr) { auto parent_isolate_group_data = reinterpret_cast(callback_data)->isolate_group_data(); - parent_kernel_buffer = parent_isolate_group_data->kernel_buffer(); - kernel_buffer = parent_kernel_buffer.get(); + kernel_buffer_ptr = parent_isolate_group_data->kernel_buffer(); + kernel_buffer = kernel_buffer_ptr.get(); kernel_buffer_size = parent_isolate_group_data->kernel_buffer_size(); } if (kernel_buffer == NULL && !isolate_run_app_snapshot) { - dfe.ReadScript(script_uri, &kernel_buffer, &kernel_buffer_size); + dfe.ReadScript(script_uri, &kernel_buffer, &kernel_buffer_size, + /*decode_uri=*/true, &kernel_buffer_ptr); } PathSanitizer script_uri_sanitizer(script_uri); PathSanitizer packages_config_sanitizer(packages_config); @@ -770,9 +771,9 @@ static Dart_Isolate CreateIsolateGroupAndSetupHelper( auto isolate_group_data = new IsolateGroupData( script_uri, packages_config, app_snapshot, isolate_run_app_snapshot); if (kernel_buffer != NULL) { - if (parent_kernel_buffer) { + if (kernel_buffer_ptr) { isolate_group_data->SetKernelBufferAlreadyOwned( - std::move(parent_kernel_buffer), kernel_buffer_size); + std::move(kernel_buffer_ptr), kernel_buffer_size); } else { isolate_group_data->SetKernelBufferNewlyOwned(kernel_buffer, kernel_buffer_size); @@ -888,6 +889,16 @@ static Dart_Isolate CreateIsolateGroupAndSetup(const char* script_uri, error, &exit_code); } +#if !defined(DART_PRECOMPILED_RUNTIME) +static const char* RegisterKernelBlob(const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size) { + return dfe.RegisterKernelBlob(kernel_buffer, kernel_buffer_size); +} +static void UnregisterKernelBlob(const char* kernel_blob_uri) { + dfe.UnregisterKernelBlob(kernel_blob_uri); +} +#endif // !defined(DART_PRECOMPILED_RUNTIME) + static void OnIsolateShutdown(void* isolate_group_data, void* isolate_data) { Dart_EnterScope(); Dart_Handle sticky_error = Dart_GetStickyError(); @@ -1332,6 +1343,10 @@ void main(int argc, char** argv) { #if !defined(DART_PRECOMPILED_RUNTIME) init_params.start_kernel_isolate = dfe.UseDartFrontend() && dfe.CanUseDartFrontend(); + if (init_params.start_kernel_isolate) { + init_params.register_kernel_blob = RegisterKernelBlob; + init_params.unregister_kernel_blob = UnregisterKernelBlob; + } #else init_params.start_kernel_isolate = false; #endif diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index 360de52663d..b884b36a689 100644 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -919,6 +919,36 @@ typedef void (*Dart_PostTaskCallback)(void* post_task_data, */ DART_EXPORT void Dart_RunTask(Dart_Task task); +/** + * Optional callback provided by the embedder that is used by the VM to + * implement registration of kernel blobs for the subsequent Isolate.spawnUri + * If no callback is provided, the registration of kernel blobs will throw + * an error. + * + * \param kernel_buffer A buffer which contains a kernel program. Callback + * should copy the contents of `kernel_buffer` as + * it may be freed immediately after registration. + * \param kernel_buffer_size The size of `kernel_buffer`. + * + * \return A C string representing URI which can be later used + * to spawn a new isolate. This C String should be scope allocated + * or owned by the embedder. + * Returns NULL if embedder runs out of memory. + */ +typedef const char* (*Dart_RegisterKernelBlobCallback)( + const uint8_t* kernel_buffer, + intptr_t kernel_buffer_size); + +/** + * Optional callback provided by the embedder that is used by the VM to + * unregister kernel blobs. + * If no callback is provided, the unregistration of kernel blobs will throw + * an error. + * + * \param kernel_blob_uri URI of the kernel blob to unregister. + */ +typedef void (*Dart_UnregisterKernelBlobCallback)(const char* kernel_blob_uri); + /** * Describes how to initialize the VM. Used with Dart_Initialize. */ @@ -1003,6 +1033,16 @@ typedef struct { Dart_PostTaskCallback post_task; void* post_task_data; + + /** + * Kernel blob registration callback function. See Dart_RegisterKernelBlobCallback. + */ + Dart_RegisterKernelBlobCallback register_kernel_blob; + + /** + * Kernel blob unregistration callback function. See Dart_UnregisterKernelBlobCallback. + */ + Dart_UnregisterKernelBlobCallback unregister_kernel_blob; } Dart_InitializeParams; /** diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc index 370b9d72a41..f5735790cd2 100644 --- a/runtime/lib/isolate.cc +++ b/runtime/lib/isolate.cc @@ -1071,6 +1071,62 @@ DEFINE_NATIVE_ENTRY(Isolate_getCurrentRootUriStr, 0, 0) { return root_lib.url(); } +DEFINE_NATIVE_ENTRY(Isolate_registerKernelBlob, 0, 1) { + GET_NON_NULL_NATIVE_ARGUMENT(TypedData, kernel_blob, + arguments->NativeArgAt(0)); + auto register_kernel_blob_callback = Isolate::RegisterKernelBlobCallback(); + if (register_kernel_blob_callback == nullptr) { + const auto& error = + String::Handle(zone, String::New("Registration of kernel blobs is not " + "supported by this Dart embedder.\n")); + Exceptions::ThrowArgumentError(error); + UNREACHABLE(); + } + bool is_kernel = false; + { + NoSafepointScope no_safepoint; + is_kernel = + Dart_IsKernel(reinterpret_cast(kernel_blob.DataAddr(0)), + kernel_blob.LengthInBytes()); + } + if (!is_kernel) { + const auto& error = String::Handle( + zone, String::New("kernelBlob doesn\'t contain a valid kernel.\n")); + Exceptions::ThrowArgumentError(error); + UNREACHABLE(); + } + const char* uri = nullptr; + { + NoSafepointScope no_safepoint; + uri = register_kernel_blob_callback( + reinterpret_cast(kernel_blob.DataAddr(0)), + kernel_blob.LengthInBytes()); + } + if (uri == nullptr) { + const Instance& exception = Instance::Handle( + thread->isolate_group()->object_store()->out_of_memory()); + Exceptions::Throw(thread, exception); + UNREACHABLE(); + } + return String::New(uri); +} + +DEFINE_NATIVE_ENTRY(Isolate_unregisterKernelBlob, 0, 1) { + GET_NON_NULL_NATIVE_ARGUMENT(String, kernel_blob_uri, + arguments->NativeArgAt(0)); + auto unregister_kernel_blob_callback = + Isolate::UnregisterKernelBlobCallback(); + if (unregister_kernel_blob_callback == nullptr) { + const auto& error = + String::Handle(zone, String::New("Registration of kernel blobs is not " + "supported by this Dart embedder.\n")); + Exceptions::ThrowArgumentError(error); + UNREACHABLE(); + } + unregister_kernel_blob_callback(kernel_blob_uri.ToCString()); + return Object::null(); +} + DEFINE_NATIVE_ENTRY(Isolate_sendOOB, 0, 2) { GET_NON_NULL_NATIVE_ARGUMENT(SendPort, port, arguments->NativeArgAt(0)); GET_NON_NULL_NATIVE_ARGUMENT(Array, msg, arguments->NativeArgAt(1)); diff --git a/runtime/platform/hashmap.h b/runtime/platform/hashmap.h index e3e9c5a1ddb..f71600f057c 100644 --- a/runtime/platform/hashmap.h +++ b/runtime/platform/hashmap.h @@ -23,7 +23,7 @@ class SimpleHashMap { static bool SamePointerValue(void* key1, void* key2) { return key1 == key2; } - static uint32_t StringHash(char* key) { + static uint32_t StringHash(const char* key) { uint32_t hash_ = 0; if (key == NULL) return hash_; int len = strlen(key); diff --git a/runtime/tests/vm/dart/spawn_uri_from_kernel_blob_script.dart b/runtime/tests/vm/dart/spawn_uri_from_kernel_blob_script.dart new file mode 100644 index 00000000000..43a200d869e --- /dev/null +++ b/runtime/tests/vm/dart/spawn_uri_from_kernel_blob_script.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "dart:convert"; +import "dart:isolate"; + +main(List args, SendPort replyPort) { + final String encoded = base64.encode(args[0].codeUnits); + replyPort.send(String.fromCharCodes(base64.decode(encoded))); +} diff --git a/runtime/tests/vm/dart/spawn_uri_from_kernel_blob_test.dart b/runtime/tests/vm/dart/spawn_uri_from_kernel_blob_test.dart new file mode 100644 index 00000000000..c9c2ae62f16 --- /dev/null +++ b/runtime/tests/vm/dart/spawn_uri_from_kernel_blob_test.dart @@ -0,0 +1,63 @@ +// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// OtherResources=spawn_uri_from_kernel_blob_script.dart + +// Test for Isolate.createUriForKernelBlob and subsequent Isolate.spawnUri. + +import 'dart:io' show Platform; +import 'dart:isolate' show Isolate, ReceivePort; +import 'dart:typed_data' show Uint8List; + +import "package:expect/expect.dart"; +import 'package:front_end/src/api_unstable/vm.dart' + show CompilerOptions, DiagnosticMessage, kernelForProgram, NnbdMode; +import 'package:kernel/kernel.dart'; +import 'package:kernel/target/targets.dart'; +import 'package:vm/target/vm.dart' show VmTarget; + +import 'snapshot_test_helper.dart'; + +main() async { + final sourceUri = + Platform.script.resolve('spawn_uri_from_kernel_blob_script.dart'); + final options = new CompilerOptions() + ..target = VmTarget(TargetFlags()) + ..additionalDills = [Uri.file(platformDill)] + ..environmentDefines = {} + ..nnbdMode = hasSoundNullSafety ? NnbdMode.Strong : NnbdMode.Weak + ..onDiagnostic = (DiagnosticMessage message) { + Expect.fail( + "Compilation error: ${message.plainTextFormatted.join('\n')}"); + }; + final Component component = + (await kernelForProgram(sourceUri, options))!.component!; + final kernelBlob = writeComponentToBytes(component) as Uint8List; + + final kernelBlobUri = + (Isolate.current as dynamic).createUriForKernelBlob(kernelBlob); + + print('URI: $kernelBlobUri'); + + for (int i = 0; i < 2; ++i) { + final receivePort = ReceivePort(); + receivePort.listen((message) { + Expect.equals(message, 'Hello'); + print('ok'); + receivePort.close(); + }); + + await Isolate.spawnUri(kernelBlobUri, ['Hello'], receivePort.sendPort); + } + + (Isolate.current as dynamic).unregisterKernelBlobUri(kernelBlobUri); + + try { + await Isolate.spawnUri(kernelBlobUri, ['Hello'], null); + Expect.fail( + "Isolate.spawnUri didn't complete with error after unregisterKernelBlobUri"); + } catch (e) { + print('Got exception: $e'); + } +} diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status index 8b0b2822827..7b19ec99ec2 100644 --- a/runtime/tests/vm/vm.status +++ b/runtime/tests/vm/vm.status @@ -98,6 +98,7 @@ cc/Mixin_PrivateSuperResolution: Skip cc/Mixin_PrivateSuperResolutionCrossLibraryShouldFail: Skip dart/b162922506_test: SkipByDesign # Only run in JIT dart/entrypoints/jit/*: SkipByDesign # These tests should only run on JIT. +dart/spawn_uri_from_kernel_blob_test: SkipByDesign # Only run in JIT. dart_2/b162922506_test: SkipByDesign # Only run in JIT dart_2/entrypoints/jit/*: SkipByDesign # These tests should only run on JIT. dart_2/isolates/reload_*: SkipByDesign # These tests only run on normal JIT. diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index 0c7b4b73b37..df65a0dbcbb 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -304,6 +304,8 @@ namespace dart { V(Isolate_getCurrentRootUriStr, 0) \ V(Isolate_getDebugName, 1) \ V(Isolate_getPortAndCapabilitiesOfCurrentIsolate, 0) \ + V(Isolate_registerKernelBlob, 1) \ + V(Isolate_unregisterKernelBlob, 1) \ V(Isolate_sendOOB, 2) \ V(Isolate_spawnFunction, 10) \ V(Isolate_spawnUri, 12) \ diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc index ad53435bc0d..b2fd4b316e8 100644 --- a/runtime/vm/dart.cc +++ b/runtime/vm/dart.cc @@ -484,6 +484,8 @@ char* Dart::DartInit(const Dart_InitializeParams* params) { Isolate::SetShutdownCallback(params->shutdown_isolate); Isolate::SetCleanupCallback(params->cleanup_isolate); Isolate::SetGroupCleanupCallback(params->cleanup_group); + Isolate::SetRegisterKernelBlobCallback(params->register_kernel_blob); + Isolate::SetUnregisterKernelBlobCallback(params->unregister_kernel_blob); #ifndef PRODUCT const bool support_service = true; diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc index a48a6228cfa..46145f42bf6 100644 --- a/runtime/vm/isolate.cc +++ b/runtime/vm/isolate.cc @@ -1785,6 +1785,8 @@ void Isolate::InitVM() { shutdown_callback_ = nullptr; cleanup_callback_ = nullptr; cleanup_group_callback_ = nullptr; + register_kernel_blob_callback_ = nullptr; + unregister_kernel_blob_callback_ = nullptr; if (isolate_creation_monitor_ == nullptr) { isolate_creation_monitor_ = new Monitor(); } @@ -2707,6 +2709,10 @@ Dart_IsolateGroupCreateCallback Isolate::create_group_callback_ = nullptr; Dart_IsolateShutdownCallback Isolate::shutdown_callback_ = nullptr; Dart_IsolateCleanupCallback Isolate::cleanup_callback_ = nullptr; Dart_IsolateGroupCleanupCallback Isolate::cleanup_group_callback_ = nullptr; +Dart_RegisterKernelBlobCallback Isolate::register_kernel_blob_callback_ = + nullptr; +Dart_UnregisterKernelBlobCallback Isolate::unregister_kernel_blob_callback_ = + nullptr; Random* IsolateGroup::isolate_group_random_ = nullptr; Monitor* Isolate::isolate_creation_monitor_ = nullptr; diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h index 39e320c0b14..7665ac1c2a7 100644 --- a/runtime/vm/isolate.h +++ b/runtime/vm/isolate.h @@ -1230,6 +1230,20 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry { static Dart_IsolateGroupCleanupCallback GroupCleanupCallback() { return cleanup_group_callback_; } + static void SetRegisterKernelBlobCallback( + Dart_RegisterKernelBlobCallback cb) { + register_kernel_blob_callback_ = cb; + } + static Dart_RegisterKernelBlobCallback RegisterKernelBlobCallback() { + return register_kernel_blob_callback_; + } + static void SetUnregisterKernelBlobCallback( + Dart_UnregisterKernelBlobCallback cb) { + unregister_kernel_blob_callback_ = cb; + } + static Dart_UnregisterKernelBlobCallback UnregisterKernelBlobCallback() { + return unregister_kernel_blob_callback_; + } #if !defined(PRODUCT) ObjectIdRing* object_id_ring() const { return object_id_ring_; } @@ -1680,6 +1694,8 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry { static Dart_IsolateShutdownCallback shutdown_callback_; static Dart_IsolateCleanupCallback cleanup_callback_; static Dart_IsolateGroupCleanupCallback cleanup_group_callback_; + static Dart_RegisterKernelBlobCallback register_kernel_blob_callback_; + static Dart_UnregisterKernelBlobCallback unregister_kernel_blob_callback_; #if !defined(PRODUCT) static void WakePauseEventHandler(Dart_Isolate isolate); diff --git a/sdk/lib/_internal/vm/lib/isolate_patch.dart b/sdk/lib/_internal/vm/lib/isolate_patch.dart index 6e3aabfa1d3..34df6461017 100644 --- a/sdk/lib/_internal/vm/lib/isolate_patch.dart +++ b/sdk/lib/_internal/vm/lib/isolate_patch.dart @@ -652,6 +652,31 @@ class Isolate { static Never exit([SendPort? finalMessagePort, Object? message]) { _exit(finalMessagePort, message); } + + /** + * Creates an Uri representing the script which was compiled into kernel + * binary in [kernelBlob]. + * The resulting Uri can be used for the subsequent spawnUri calls. + * Such spawnUri will start an isolate which would run the given + * compiled script in [kernelBlob]. + */ + /*static*/ Uri createUriForKernelBlob(Uint8List kernelBlob) { + return Uri.parse(_registerKernelBlob(kernelBlob)); + } + + /** + * Unregisters kernel blob previously registered with + * [createUriForKernelBlob] and frees underlying resources. + */ + /*static*/ void unregisterKernelBlobUri(Uri kernelBlobUri) { + _unregisterKernelBlob(kernelBlobUri.toString()); + } + + @pragma("vm:external-name", "Isolate_registerKernelBlob") + external static String _registerKernelBlob(Uint8List kernelBlob); + + @pragma("vm:external-name", "Isolate_unregisterKernelBlob") + external static void _unregisterKernelBlob(String kernelBlobUri); } @patch