197 lines · 5.7 KB · C++
RawDownload
//// Dis VM// File: vm.cpp// Author: arr// #include <cassert>#include <memory>#include <algorithm>#include <disvm.h>#include <debug.h>#include <runtime.h>#include <exceptions.h>#include <builtin_module.h>#include <vm_version.h>#include "scheduler.h"#include "garbage_collector.h" using namespace disvm;using namespace disvm::runtime; vm_t::loaded_module_t::loaded_module_t(std::unique_ptr<runtime::vm_string_t> origin, std::shared_ptr<const runtime::vm_module_t> module)    : origin{ std::move(origin) }    , module{ module }{    assert(this->origin != nullptr);} vm_t::loaded_module_t::loaded_module_t(loaded_module_t &&other)    : origin{ std::move(other.origin) }    , module{ std::move(other.module) }{} vm_t::loaded_module_t::~loaded_module_t(){    if (origin != nullptr)        origin->release();} vm_t::vm_t(    create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler,    create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc){    // Initialize built-in modules.    builtin::initialize_builtin_modules();     if (create_gc == nullptr)        _gc = std::make_unique<default_garbage_collector_t>(*this);    else        _gc = create_gc(*this);     if (create_scheduler == nullptr)    {        const auto system_thread_count = uint32_t{ 1 };         // The Inferno implementation defined the thread quanta as 2048 (include/interp.h)        const auto thread_quanta = uint32_t{ 2048 };         _scheduler = std::make_unique<default_scheduler_t>(*this, system_thread_count, thread_quanta);    }    else    {        _scheduler = create_scheduler(*this);    }} vm_t::vm_t(uint32_t system_thread_count, uint32_t thread_quanta){    // Initialize built-in modules.    builtin::initialize_builtin_modules();     _gc = std::make_unique<default_garbage_collector_t>(*this);    _scheduler = std::make_unique<default_scheduler_t>(*this, system_thread_count, thread_quanta);} vm_t::~vm_t(){    // Release the scheduler first since it could be using the gc instance.    _scheduler.reset();    _gc.reset();} vm_version_t vm_t::get_version() const{    return{ DISVM_VERSION_MAJOR, DISVM_VERSION_MINOR, DISVM_VERSION_PATCH, DISVM_VERSION_LABEL };} namespace{    const runtime::vm_thread_t& _exec(runtime::vm_scheduler_t &s, std::unique_ptr<runtime::vm_module_ref_t> &entry_module_ref)    {        auto thread = std::make_unique<vm_thread_t>(*entry_module_ref, vm_t::root_vm_thread_id);         // Now that the thread has been created and has inherited ownership of the pointer, relinquish the smart pointer's ownership.        entry_module_ref->release(); // decrement the vm alloc ref count        entry_module_ref.release(); // release the smart pointer         return s.schedule_thread(std::move(thread));    }} const runtime::vm_thread_t& vm_t::exec(const char *path){    assert(path != nullptr);    auto entry_module = load_module(path);     auto entry_module_ref = std::make_unique<vm_module_ref_t>(entry_module);    return _exec(*_scheduler, entry_module_ref);} const runtime::vm_thread_t& vm_t::exec(std::unique_ptr<runtime::vm_module_t> entry_module){    assert(entry_module != nullptr);     auto entry_module_ref = std::make_unique<vm_module_ref_t>(std::move(entry_module));    return _exec(*_scheduler, entry_module_ref);} const runtime::vm_thread_t& vm_t::fork(    const runtime::vm_thread_t &parent,    runtime::vm_module_ref_t &module_ref,    const runtime::vm_frame_t &initial_frame,    runtime::vm_pc_t initial_pc){    if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc)        throw vm_system_exception{ "Invalid entry program counter" };     auto thread = std::make_unique<vm_thread_t>(module_ref, initial_frame, initial_pc, parent.get_thread_id());    return _scheduler->schedule_thread(std::move(thread));} std::shared_ptr<const runtime::vm_module_t> vm_t::load_module(const char *path){    if (path == nullptr)        throw vm_user_exception{ "Invalid module path" };     std::lock_guard<std::mutex> lock{ _modules_lock };        // Check if the module is already loaded.    auto iter = std::begin(_modules);    while (iter != std::end(_modules))    {        if (0 == ::strcmp(iter->origin->str(), path))        {            auto module = iter->module.lock();             // If an entry exists but is null, the module was loaded before. Read the module again and return.            if (module == nullptr)            {                module = std::shared_ptr<vm_module_t>{ std::move(read_module(path)) };                iter->module = module;                 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<\n", path);            }             return module;        }         ++iter;    }     auto new_module = std::shared_ptr<vm_module_t>{};    if (path[0] == BUILTIN_MODULE_PREFIX_CHAR)    {        new_module = std::move(builtin::get_builtin_module(path));    }    else    {        // Read in the module        new_module = std::move(read_module(path));    }     auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path));    _modules.push_front(std::move(loaded_module_t{ std::move(path_local), new_module }));     debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "load: vm module: >>%s<<\n", path);     return new_module;} runtime::vm_scheduler_control_t &vm_t::get_scheduler_control() const{    return _scheduler->get_controller();} runtime::vm_garbage_collector_t &vm_t::get_garbage_collector() const{    return *_gc;} void vm_t::spin_sleep_till_idle(std::chrono::milliseconds sleep_interval) const{    do        std::this_thread::sleep_for(sleep_interval);    while (!_scheduler->is_idle());}