| // 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. | |
| #ifndef RUNTIME_ENGINE_ENGINE_H_ | |
| #define RUNTIME_ENGINE_ENGINE_H_ | |
| #include <memory> | |
| #include <unordered_map> | |
| #include <vector> | |
| #include "include/dart_engine.h" | |
| #include "platform/synchronization.h" | |
| namespace dart { | |
| namespace engine { | |
| // Manages Dart VM initialization/shutdown, isolates, and all state that needs | |
| // to be disposed (e.g. buffers of loaded snapshots). | |
| // | |
| // Since Dart VM has a global state, there should be only one instance of this | |
| // class (accessible through Engine::instance()). | |
| class Engine { | |
| public: | |
| // Loads Kernel snapshot from given file path. | |
| // | |
| // Just reads all bytes into a single buffer, owned by this class. | |
| DartEngine_SnapshotData KernelFromFile(const char* path, char** error); | |
| // Loads AOT snapshot from given file path. | |
| // | |
| // Opens given AOT snapshot as a shared library and loads buffer symbols from | |
| // it. | |
| DartEngine_SnapshotData AotFromFile(const char* path, char** error); | |
| // Starts an isolate from a snapshot. | |
| // | |
| // Starting an isolate also routes message notify callback to NotifyMessage. | |
| Dart_Isolate StartIsolate(const DartEngine_SnapshotData snapshot, | |
| char** error); | |
| // Acquires a lock for an isolate, so that the thread can enter it. | |
| void LockIsolate(Dart_Isolate isolate); | |
| // Releases the lock obtained by LockIsolate. | |
| void UnlockIsolate(Dart_Isolate isolate); | |
| // Called by Dart VM via Engine::MessageNotifyCallback. | |
| // | |
| // Calls isolate-specific message scheduler (if any), default message | |
| // scheduler, or ignores the message. | |
| void NotifyMessage(Dart_Isolate isolate); | |
| // Calls Dart_HandleMessage, managing an isolate lock and Dart scope. | |
| void HandleMessage(Dart_Isolate isolate); | |
| // Drains the microtasks queue, requires an active isolate. | |
| // | |
| // If a microtask throws, the error is returned to the caller, | |
| // and the queue may still contain more entries. | |
| // The caller should continue to drain the queue after handling the error. | |
| Dart_Handle DrainMicrotasksQueue(); | |
| // Sets a callback to be called when Dart_HandleMessage returns an error. | |
| void SetHandleMessageErrorCallback( | |
| DartEngine_HandleMessageErrorCallback callback); | |
| // Sets a message scheduler for a given isolate. | |
| void SetDefaultMessageScheduler(DartEngine_MessageScheduler scheduler); | |
| // Sets default message scheduler for isolates without dedicated | |
| // message scheduler. | |
| void SetMessageScheduler(DartEngine_MessageScheduler scheduler, | |
| Dart_Isolate isolate); | |
| // Initializes embedder and partially initializes Dart VM. | |
| // | |
| // Full initialization happens only when the user starts the first isolate, as | |
| // in case of AOT snapshots we don't have vm snapshot data and vm snapshot | |
| // instuructions before the user makes the first call to StartIsolate. | |
| bool Initialize(char** error); | |
| // Shuts down the engine. | |
| // | |
| // Once shut down, it is not supposed to be re-initialized. | |
| void Shutdown(); | |
| static Engine* instance(); | |
| // Passed to Dart_SetMessageNotifyCallback. | |
| static void MessageNotifyCallback(Dart_Isolate isolate); | |
| // Passed to user-previded message scheduler callback. | |
| static void HandleMessageCallback(Dart_Isolate isolate); | |
| private: | |
| // Engine's internal data for isolate. | |
| struct IsolateData { | |
| DartEngine_MessageScheduler scheduler; | |
| Mutex mutex; | |
| Dart_PersistentHandle isolate_library; | |
| Dart_PersistentHandle drain_microtasks_function_name; | |
| }; | |
| // Set to false once shutdown starts. | |
| bool is_running_ = false; | |
| // Controls initalization and shutdown, and prevents deadlocks when delivering | |
| // messages during shutdown. | |
| Mutex engine_lifecycle_; | |
| // Guards access to engine state. | |
| Mutex engine_state_; | |
| // Whether Engine::Initialize is called. | |
| bool initialized_ = false; | |
| // Added because of AOT mode, because in AOT mode Dart VM cannot be fully | |
| // initialized until we get vm snapshot data, which we typically get when the | |
| // user starts the first isolate. | |
| bool first_isolate_started_ = false; | |
| // Snapshots, whose buffers are owned by the engine. | |
| // and are freed on Shutdown. | |
| std::vector<DartEngine_SnapshotData> owned_snapshots_; | |
| // Loaded dynamic libraries. Dynamic libraries are loaded | |
| // when reading AOT snapshots. | |
| std::vector<void*> loaded_libraries_; | |
| // All isolates, started via Engine::StartIsolate. | |
| std::vector<Dart_Isolate> isolates_; | |
| // Stores per-isolate engine state. | |
| std::unordered_map<Dart_Isolate, std::shared_ptr<IsolateData>> isolate_data_; | |
| // Default scheduler. | |
| DartEngine_MessageScheduler default_scheduler_; | |
| // Callback to notify about Dart_HandleMessage errors. | |
| DartEngine_HandleMessageErrorCallback handle_message_error_callback_; | |
| // Helper function to get an element from isolate_data_. | |
| std::shared_ptr<IsolateData> DataForIsolate(Dart_Isolate isolate); | |
| }; | |
| } // namespace engine | |
| } // namespace dart | |
| #endif // RUNTIME_ENGINE_ENGINE_H_ |