8773b6618f
This is a reland of commit 3491f72880
Original change's description:
> Update libcxx and libcxxabi
>
> Add libc, required by libcxx.
>
> Based on https://github.com/flutter/flutter/pull/165621.
>
> Change-Id: I5b929dc81b2572e3b919a8d20e031f98196375e6
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/416220
> Auto-Submit: Ivan Inozemtsev <iinozemtsev@google.com>
> Reviewed-by: Ryan Macnak <rmacnak@google.com>
> Commit-Queue: Ivan Inozemtsev <iinozemtsev@google.com>
Change-Id: I9392f4cdc2ccb985085768c90bd293d353bf4c95
Cq-Include-Trybots: luci.dart.try:vm-aot-tsan-linux-release-x64-try,vm-tsan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-ubsan-linux-release-arm64-try,vm-asan-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/421300
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Ivan Inozemtsev <iinozemtsev@google.com>
378 lines
11 KiB
C++
378 lines
11 KiB
C++
// Copyright (c) 2025, 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.
|
|
|
|
#include "engine/engine.h"
|
|
#include <errno.h>
|
|
#include <memory>
|
|
#include "bin/dartutils.h"
|
|
#include "include/dart_api.h"
|
|
#include "include/dart_embedder_api.h"
|
|
#include "include/dart_engine.h"
|
|
#include "platform/lockers.h"
|
|
#include "platform/syslog.h"
|
|
#include "platform/utils.h"
|
|
|
|
namespace dart {
|
|
namespace engine {
|
|
|
|
constexpr char kRunPendingImmediateCallback[] = "_runPendingImmediateCallback";
|
|
|
|
using platform::MutexLocker;
|
|
|
|
Engine* Engine::instance() {
|
|
static Engine* instance = new Engine();
|
|
return instance;
|
|
}
|
|
|
|
DartEngine_SnapshotData Engine::KernelFromFile(const char* path, char** error) {
|
|
DartEngine_SnapshotData result;
|
|
result.kind = DartEngine_SnapshotKind_Kernel;
|
|
result.script_uri = Utils::SCreate("file://%s", path);
|
|
FILE* file = fopen(path, "rb");
|
|
if (file == nullptr) {
|
|
*error = Utils::SCreate("Error %d", errno);
|
|
return result;
|
|
}
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
intptr_t size = ftell(file);
|
|
rewind(file);
|
|
|
|
char* buffer = reinterpret_cast<char*>(malloc(sizeof(char) * size));
|
|
|
|
intptr_t bytes_read = fread(buffer, 1, size, file);
|
|
fclose(file);
|
|
if (bytes_read != size) {
|
|
free(buffer);
|
|
*error = Utils::SCreate("Error reading %s: read %zu bytes instead of %zu",
|
|
path, bytes_read, size);
|
|
return result;
|
|
}
|
|
|
|
result.kernel_buffer_size = size;
|
|
result.kernel_buffer = reinterpret_cast<uint8_t*>(buffer);
|
|
|
|
{
|
|
MutexLocker ml(&engine_state_);
|
|
owned_snapshots_.emplace_back(result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DartEngine_SnapshotData Engine::AotFromFile(const char* path, char** error) {
|
|
DartEngine_SnapshotData result;
|
|
result.kind = DartEngine_SnapshotKind_AOT;
|
|
result.script_uri = Utils::SCreate("file://%s", path);
|
|
|
|
void* library =
|
|
Utils::LoadDynamicLibrary(path, /*search_dll_load_dir=*/false, error);
|
|
|
|
result.vm_snapshot_data = reinterpret_cast<const uint8_t*>(
|
|
Utils::ResolveSymbolInDynamicLibrary(library, kVmSnapshotDataCSymbol));
|
|
result.vm_snapshot_instructions =
|
|
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
|
library, kVmSnapshotInstructionsCSymbol));
|
|
result.vm_isolate_data =
|
|
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
|
library, kIsolateSnapshotDataCSymbol));
|
|
result.vm_isolate_instructions =
|
|
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
|
library, kIsolateSnapshotInstructionsCSymbol));
|
|
|
|
if (*error != nullptr) {
|
|
return result;
|
|
}
|
|
|
|
{
|
|
MutexLocker ml(&engine_state_);
|
|
loaded_libraries_.emplace_back(library);
|
|
owned_snapshots_.emplace_back(result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
namespace {
|
|
Dart_InitializeParams CreateInitializeParams() {
|
|
Dart_InitializeParams params;
|
|
memset(¶ms, 0, sizeof(params));
|
|
params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
|
|
params.shutdown_isolate = nullptr;
|
|
params.create_group = nullptr;
|
|
return params;
|
|
}
|
|
} // namespace
|
|
|
|
bool Engine::Initialize(char** error) {
|
|
if (initialized_) {
|
|
return true;
|
|
}
|
|
MutexLocker ml(&engine_lifecycle_);
|
|
|
|
if (initialized_) {
|
|
return true;
|
|
}
|
|
if (!dart::embedder::InitOnce(error)) {
|
|
return false;
|
|
}
|
|
|
|
std::vector<const char*> flags{};
|
|
if (Dart_IsPrecompiledRuntime()) {
|
|
flags.push_back("--precompilation");
|
|
}
|
|
*error = Dart_SetVMFlags(flags.size(), flags.data());
|
|
|
|
if (*error != nullptr) {
|
|
return false;
|
|
}
|
|
|
|
initialized_ = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
Dart_Isolate Engine::StartIsolate(DartEngine_SnapshotData snapshot,
|
|
char** error) {
|
|
if (Dart_IsPrecompiledRuntime() &&
|
|
snapshot.kind != DartEngine_SnapshotKind_AOT) {
|
|
*error = Utils::StrDup("AOT Dart VM supports only AOT snapshots");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!Dart_IsPrecompiledRuntime() &&
|
|
snapshot.kind != DartEngine_SnapshotKind_Kernel) {
|
|
*error = Utils::StrDup("AOT Dart VM supports only AOT snapshots");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!first_isolate_started_) {
|
|
// This is a part of an initialization, so using engine_lifecycle_ mutex
|
|
// here.
|
|
MutexLocker ml(&engine_lifecycle_);
|
|
if (!first_isolate_started_) {
|
|
Dart_InitializeParams initialize_params = CreateInitializeParams();
|
|
|
|
if (Dart_IsPrecompiledRuntime()) {
|
|
initialize_params.vm_snapshot_data = snapshot.vm_snapshot_data;
|
|
initialize_params.vm_snapshot_instructions =
|
|
snapshot.vm_snapshot_instructions;
|
|
}
|
|
*error = Dart_Initialize(&initialize_params);
|
|
if (*error != nullptr) {
|
|
return nullptr;
|
|
}
|
|
first_isolate_started_ = true;
|
|
}
|
|
}
|
|
|
|
// Now we can start an isolate.
|
|
Dart_IsolateFlags isolate_flags;
|
|
Dart_IsolateFlagsInitialize(&isolate_flags);
|
|
|
|
Dart_Isolate isolate;
|
|
if (Dart_IsPrecompiledRuntime()) {
|
|
// Automatically sets the root library for the isolate.
|
|
isolate = Dart_CreateIsolateGroup(
|
|
snapshot.script_uri, strrchr(snapshot.script_uri, '/'),
|
|
snapshot.vm_isolate_data, snapshot.vm_isolate_instructions,
|
|
&isolate_flags, nullptr, nullptr, error);
|
|
} else {
|
|
isolate = Dart_CreateIsolateGroupFromKernel(
|
|
snapshot.script_uri, snapshot.script_uri, snapshot.kernel_buffer,
|
|
snapshot.kernel_buffer_size, &isolate_flags, nullptr, nullptr, error);
|
|
}
|
|
|
|
if (*error != nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
Dart_SetMessageNotifyCallback(Engine::MessageNotifyCallback);
|
|
|
|
Dart_EnterScope();
|
|
|
|
// In fact, this call initializes core libraries, (e.g. `print` doesn't work
|
|
// without it).
|
|
Dart_Handle core_libs_result =
|
|
bin::DartUtils::PrepareForScriptLoading(false, false);
|
|
if (Dart_IsError(core_libs_result)) {
|
|
*error = Utils::StrDup(Dart_GetError(core_libs_result));
|
|
Dart_ShutdownIsolate();
|
|
return nullptr;
|
|
}
|
|
|
|
if (!Dart_IsPrecompiledRuntime()) {
|
|
// In kernel mode, also call LoadScriptFromKernel to set the root library.
|
|
// Technically, the library is already loaded after
|
|
// Dart_CreateIsolateGroupFromKernel, the problem is we don't know its URI
|
|
// (it is not related to snapshot->uri and depends on where the kernel
|
|
// snapshot was built, e.g. file:///Users/user/samples/hello.dart)
|
|
Dart_Handle library = Dart_LoadScriptFromKernel(
|
|
snapshot.kernel_buffer, snapshot.kernel_buffer_size);
|
|
|
|
if (Dart_IsError(library)) {
|
|
*error = Utils::StrDup(Dart_GetError(library));
|
|
Dart_ShutdownIsolate();
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Dart_Handle isolate_library = Dart_LookupLibrary(
|
|
Dart_NewStringFromCString(bin::DartUtils::kIsolateLibURL));
|
|
if (Dart_IsError(isolate_library)) {
|
|
*error = Utils::StrDup(Dart_GetError(isolate_library));
|
|
Dart_ShutdownIsolate();
|
|
return nullptr;
|
|
}
|
|
|
|
std::shared_ptr<Engine::IsolateData> isolate_data = DataForIsolate(isolate);
|
|
isolate_data->isolate_library = Dart_NewPersistentHandle(isolate_library);
|
|
isolate_data->drain_microtasks_function_name = Dart_NewPersistentHandle(
|
|
Dart_NewStringFromCString(kRunPendingImmediateCallback));
|
|
isolate_data->scheduler.context = nullptr;
|
|
isolate_data->scheduler.schedule_callback = nullptr;
|
|
|
|
Dart_ExitScope();
|
|
Dart_ExitIsolate();
|
|
is_running_ = true;
|
|
isolates_.emplace_back(isolate);
|
|
return isolate;
|
|
}
|
|
|
|
void Engine::MessageNotifyCallback(Dart_Isolate isolate) {
|
|
Engine::instance()->NotifyMessage(isolate);
|
|
}
|
|
|
|
void Engine::Shutdown() {
|
|
MutexLocker shutdown_locker(&engine_lifecycle_);
|
|
|
|
is_running_ = false;
|
|
for (auto isolate : isolates_) {
|
|
std::shared_ptr<Engine::IsolateData> isolate_data = DataForIsolate(isolate);
|
|
LockIsolate(isolate);
|
|
Dart_EnterIsolate(isolate);
|
|
Dart_SetMessageNotifyCallback(nullptr);
|
|
Dart_DeletePersistentHandle(isolate_data->isolate_library);
|
|
Dart_DeletePersistentHandle(isolate_data->drain_microtasks_function_name);
|
|
Dart_ShutdownIsolate();
|
|
UnlockIsolate(isolate);
|
|
}
|
|
|
|
MutexLocker state_locker(&engine_state_);
|
|
for (auto& snapshot : owned_snapshots_) {
|
|
switch (snapshot.kind) {
|
|
case DartEngine_SnapshotKind_Kernel:
|
|
free(const_cast<uint8_t*>(snapshot.kernel_buffer));
|
|
break;
|
|
case DartEngine_SnapshotKind_AOT:
|
|
// No need to free AOT buffers loaded from dynamic library.
|
|
break;
|
|
default:
|
|
Syslog::PrintErr("Unsupported SnapshotKind: %d\n", snapshot.kind);
|
|
break;
|
|
}
|
|
free(const_cast<char*>(snapshot.script_uri));
|
|
}
|
|
}
|
|
|
|
void Engine::HandleMessage(Dart_Isolate isolate) {
|
|
LockIsolate(isolate);
|
|
Dart_EnterIsolate(isolate);
|
|
Dart_EnterScope();
|
|
|
|
Dart_Handle handle_result = Dart_HandleMessage();
|
|
|
|
if (Dart_IsError(handle_result)) {
|
|
if (handle_message_error_callback_ != nullptr) {
|
|
handle_message_error_callback_(handle_result, isolate);
|
|
} else {
|
|
Syslog::PrintErr("Error handling isolate message: %s",
|
|
Dart_GetError(handle_result));
|
|
}
|
|
}
|
|
|
|
Dart_ExitScope();
|
|
Dart_ExitIsolate();
|
|
UnlockIsolate(isolate);
|
|
}
|
|
|
|
Dart_Handle Engine::DrainMicrotasksQueue() {
|
|
std::shared_ptr<Engine::IsolateData> isolate_data =
|
|
DataForIsolate(Dart_CurrentIsolate());
|
|
return Dart_Invoke(isolate_data->isolate_library,
|
|
isolate_data->drain_microtasks_function_name, 0, nullptr);
|
|
}
|
|
|
|
std::shared_ptr<Engine::IsolateData> Engine::DataForIsolate(
|
|
Dart_Isolate isolate) {
|
|
MutexLocker ml(&engine_state_);
|
|
auto it = isolate_data_.find(isolate);
|
|
if (it == isolate_data_.end()) {
|
|
it = isolate_data_.emplace(isolate, std::make_shared<Engine::IsolateData>())
|
|
.first;
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
void Engine::LockIsolate(Dart_Isolate isolate) {
|
|
DataForIsolate(isolate)->mutex.Lock();
|
|
}
|
|
|
|
void Engine::UnlockIsolate(Dart_Isolate isolate) {
|
|
DataForIsolate(isolate)->mutex.Unlock();
|
|
}
|
|
|
|
void Engine::NotifyMessage(Dart_Isolate isolate) {
|
|
if (!engine_lifecycle_.TryLock() || !is_running_) {
|
|
// Shutdown is in progress or complete.
|
|
//
|
|
// Used to prevent a potential deadlock during shutdown.
|
|
//
|
|
// If Engine::MessageNotifyCallback is waiting for Engine::HandleMessage
|
|
// completion (which happens in samples/embedder/run_timer_async), and
|
|
// another thread calls Engine::Shutdown, the deadlock may occur:
|
|
//
|
|
// 1. MessageNotifyCallback thread owns PortMap::mutex_ (through
|
|
// PortMap::PostMessage) and wants to lock an isolate (via
|
|
// Engine::LockIsolate).
|
|
// 2. Shutdown thread owns an isolate lock and wants to lock PortMap::mutex_
|
|
// (inside Dart_ShutdownIsolate call).
|
|
//
|
|
// This mutex is used to prevent it:
|
|
// - Engine::Shutdown locks it.
|
|
// - Engine::NotifyMessage tries to lock it, and treats lock failure as
|
|
// shutdown in progress.
|
|
return;
|
|
}
|
|
|
|
DartEngine_MessageScheduler scheduler = DataForIsolate(isolate)->scheduler;
|
|
|
|
if (scheduler.schedule_callback == nullptr) {
|
|
scheduler = default_scheduler_;
|
|
}
|
|
if (scheduler.schedule_callback == nullptr) {
|
|
engine_lifecycle_.Unlock();
|
|
return;
|
|
}
|
|
scheduler.schedule_callback(isolate, scheduler.context);
|
|
engine_lifecycle_.Unlock();
|
|
}
|
|
|
|
void Engine::SetHandleMessageErrorCallback(
|
|
DartEngine_HandleMessageErrorCallback callback) {
|
|
handle_message_error_callback_ = callback;
|
|
}
|
|
|
|
void Engine::SetDefaultMessageScheduler(DartEngine_MessageScheduler scheduler) {
|
|
default_scheduler_ = scheduler;
|
|
}
|
|
|
|
void Engine::SetMessageScheduler(DartEngine_MessageScheduler scheduler,
|
|
Dart_Isolate isolate) {
|
|
DataForIsolate(isolate)->scheduler = scheduler;
|
|
}
|
|
|
|
} // namespace engine
|
|
} // namespace dart
|