Unified · Split
| @@ -195,14 +195,12 @@ struct exec_options · 194 unmodified lines | |||
| 195 | , enabled_debugger{ false } | 195 | , enabled_debugger{ false } |
| 196 | , quiet_start{ false } | 196 | , quiet_start{ false } |
| 197 | , print_help{ false } | 197 | , print_help{ false } |
| 198 | , sys_thread_count{ vm_t::default_system_thread_count } | 198 | , vm_config{} |
| 199 | , thread_quanta{ vm_t::default_thread_quanta } | ||
| 200 | { } | 199 | { } |
| 201 | 200 | ||
| 202 | std::vector<char *> vm_args; | 201 | std::vector<char *> vm_args; |
| 203 | 202 | ||
| 204 | uint32_t sys_thread_count; | 203 | vm_config_t vm_config; |
| 205 | uint32_t thread_quanta; | ||
| 206 | 204 | ||
| 207 | bool print_help; | 205 | bool print_help; |
| 208 | bool quiet_start; | 206 | bool quiet_start; |
| @@ -220,7 +218,7 @@ void print_banner(const exec_options &options) · 11 unmodified lines | |||
| 220 | std::cout | 218 | std::cout |
| 221 | << "\n----------------\n" | 219 | << "\n----------------\n" |
| 222 | << "Debugger enabled: " << std::boolalpha << options.enabled_debugger << "\n" | 220 | << "Debugger enabled: " << std::boolalpha << options.enabled_debugger << "\n" |
| 223 | << "System thread usage: " << options.sys_thread_count << "\n"; | 221 | << "System thread usage: " << options.vm_config.sys_thread_pool_size << "\n"; |
| 224 | 222 | ||
| 225 | if (options.enabled_debugger) | 223 | if (options.enabled_debugger) |
| 226 | std::cout << "\n" << options.debugger << "\n"; | 224 | std::cout << "\n" << options.debugger << "\n"; |
| @@ -274,14 +272,6 @@ void log_callback(const debug::component_trace_t origin, const debug::log_level_ · 47 unmodified lines | |||
| 274 | std::cout << ss.str(); | 272 | std::cout << ss.str(); |
| 275 | } | 273 | } |
| 276 | 274 | ||
| 277 | void enable_logging(debug::component_trace_t c) | ||
| 278 | { | ||
| 279 | debug::set_component_tracing(c, true); | ||
| 280 | |||
| 281 | // Constantly setting the callback | ||
| 282 | debug::set_logging_callback(log_callback); | ||
| 283 | } | ||
| 284 | |||
| 285 | void process_arg(char* arg, std::function<char *()> next, exec_options &options) | 275 | void process_arg(char* arg, std::function<char *()> next, exec_options &options) |
| 286 | { | 276 | { |
| 287 | assert(arg != nullptr && next != nullptr); | 277 | assert(arg != nullptr && next != nullptr); |
| @@ -326,28 +316,30 @@ void process_arg(char* arg, std::function<char *()> next, exec_options &options) · 38 unmodified lines | |||
| 326 | if (sys_threads <= 0 || 4 < sys_threads) | 316 | if (sys_threads <= 0 || 4 < sys_threads) |
| 327 | throw arg_exception_t{ "Invalid system thread value", sys_threads_str }; | 317 | throw arg_exception_t{ "Invalid system thread value", sys_threads_str }; |
| 328 | 318 | ||
| 329 | options.sys_thread_count = sys_threads; | 319 | options.vm_config.sys_thread_pool_size = sys_threads; |
| 330 | } | 320 | } |
| 331 | break; | 321 | break; |
| 332 | 322 | ||
| 333 | case 'l': | 323 | case 'l': |
| 324 | // Set the callback | ||
| 325 | debug::set_logging_callback(log_callback); | ||
| 334 | for (auto i = std::size_t{ 2 }; i < arg_len; ++i) | 326 | for (auto i = std::size_t{ 2 }; i < arg_len; ++i) |
| 335 | { | 327 | { |
| 336 | switch (arg[i]) | 328 | switch (arg[i]) |
| 337 | { | 329 | { |
| 338 | case 's': enable_logging(debug::component_trace_t::scheduler); | 330 | case 's': debug::set_component_tracing(debug::component_trace_t::scheduler, true); |
| 339 | break; | 331 | break; |
| 340 | case 'S': enable_logging(debug::component_trace_t::stack); | 332 | case 'S': debug::set_component_tracing(debug::component_trace_t::stack, true); |
| 341 | break; | 333 | break; |
| 342 | case 't': enable_logging(debug::component_trace_t::thread); | 334 | case 't': debug::set_component_tracing(debug::component_trace_t::thread, true); |
| 343 | break; | 335 | break; |
| 344 | case 'T': enable_logging(debug::component_trace_t::tool); | 336 | case 'T': debug::set_component_tracing(debug::component_trace_t::tool, true); |
| 345 | break; | 337 | break; |
| 346 | case 'e': enable_logging(debug::component_trace_t::exception); | 338 | case 'e': debug::set_component_tracing(debug::component_trace_t::exception, true); |
| 347 | break; | 339 | break; |
| 348 | case 'g': enable_logging(debug::component_trace_t::garbage_collector); | 340 | case 'g': debug::set_component_tracing(debug::component_trace_t::garbage_collector, true); |
| 349 | break; | 341 | break; |
| 350 | case 'm': enable_logging(debug::component_trace_t::memory); | 342 | case 'm': debug::set_component_tracing(debug::component_trace_t::memory, true); |
| 351 | break; | 343 | break; |
| 352 | } | 344 | } |
| 353 | } | 345 | } |
| @@ -402,7 +394,7 @@ int main(int argc, char* argv[]) · 48 unmodified lines | |||
| 402 | return EXIT_SUCCESS; | 394 | return EXIT_SUCCESS; |
| 403 | } | 395 | } |
| 404 | 396 | ||
| 405 | vm_t vm{ options.sys_thread_count, options.thread_quanta }; | 397 | vm_t vm{ std::move(options.vm_config) }; |
| 406 | 398 | ||
| 407 | if (options.enabled_debugger) | 399 | if (options.enabled_debugger) |
| 408 | vm.load_tool(std::make_shared<debugger>(options.debugger)); | 400 | vm.load_tool(std::make_shared<debugger>(options.debugger)); |
| @@ -7,18 +7,17 @@ · 6 unmodified lines | |||
| 7 | #ifndef _DISVM_SRC_INCLUDE_DISVM_H_ | 7 | #ifndef _DISVM_SRC_INCLUDE_DISVM_H_ |
| 8 | #define _DISVM_SRC_INCLUDE_DISVM_H_ | 8 | #define _DISVM_SRC_INCLUDE_DISVM_H_ |
| 9 | 9 | ||
| 10 | #include "runtime.h" | 10 | #include <array> |
| 11 | #include <iosfwd> | 11 | #include <iosfwd> |
| 12 | #include <memory> | 12 | #include <memory> |
| 13 | #include <functional> | 13 | #include <functional> |
| 14 | #include <mutex> | 14 | #include <mutex> |
| 15 | #include <forward_list> | 15 | #include <forward_list> |
| 16 | #include "runtime.h" | ||
| 17 | #include "exceptions.h" | ||
| 16 | 18 | ||
| 17 | namespace disvm | 19 | namespace disvm |
| 18 | { | 20 | { |
| 19 | // Read in the module at the supplied path | ||
| 20 | std::unique_ptr<runtime::vm_module_t> read_module(const char *path); | ||
| 21 | |||
| 22 | // Read in a module from the supplied stream | 21 | // Read in a module from the supplied stream |
| 23 | std::unique_ptr<runtime::vm_module_t> read_module(std::istream &data); | 22 | std::unique_ptr<runtime::vm_module_t> read_module(std::istream &data); |
| 24 | 23 | ||
| @@ -51,20 +50,37 @@ namespace disvm · 26 unmodified lines | |||
| 51 | // Callback for enumerating loaded modules - return 'false' to stop enumeration, otherwise 'true' | 50 | // Callback for enumerating loaded modules - return 'false' to stop enumeration, otherwise 'true' |
| 52 | using loaded_vm_module_callback_t = std::function<bool(const loaded_vm_module_t&)>; | 51 | using loaded_vm_module_callback_t = std::function<bool(const loaded_vm_module_t&)>; |
| 53 | 52 | ||
| 53 | // Configuration for the created VM | ||
| 54 | class vm_config_t final | ||
| 55 | { | ||
| 56 | public: | ||
| 57 | vm_config_t(); | ||
| 58 | vm_config_t(vm_config_t &&); | ||
| 59 | |||
| 60 | // Settings for the default scheduler. Only used if create_scheduler is not set. | ||
| 61 | // Both of these values are initialized to valid default values on construction. | ||
| 62 | uint32_t sys_thread_pool_size; | ||
| 63 | uint32_t thread_quanta; | ||
| 64 | |||
| 65 | create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler; | ||
| 66 | create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc; | ||
| 67 | |||
| 68 | // Prefix paths used by the default module resolver if the path supplied by | ||
| 69 | // the user isn't found. Note these will be concatenated with the supplied | ||
| 70 | // path, not other manipulation will be performed. | ||
| 71 | std::vector<std::string> probing_paths; | ||
| 72 | std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> additional_resolvers; | ||
| 73 | }; | ||
| 74 | |||
| 54 | // Virtual machine | 75 | // Virtual machine |
| 55 | class vm_t final | 76 | class vm_t final |
| 56 | { | 77 | { |
| 57 | public: // static | 78 | public: // static |
| 58 | static const uint32_t root_vm_thread_id; | 79 | static const uint32_t root_vm_thread_id; |
| 59 | static const uint32_t default_system_thread_count; | ||
| 60 | static const uint32_t default_thread_quanta; | ||
| 61 | 80 | ||
| 62 | public: | 81 | public: |
| 63 | explicit vm_t( | 82 | explicit vm_t(); |
| 64 | create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler = nullptr, | 83 | explicit vm_t(vm_config_t config); |
| 65 | create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc = nullptr); | ||
| 66 | |||
| 67 | explicit vm_t(uint32_t thread_pool_size, uint32_t thread_quanta); | ||
| 68 | 84 | ||
| 69 | ~vm_t(); | 85 | ~vm_t(); |
| 70 | 86 | ||
| @@ -92,7 +108,7 @@ namespace disvm · 21 unmodified lines | |||
| 92 | // Enumerate all loaded modules - this can include references to modules that | 108 | // Enumerate all loaded modules - this can include references to modules that |
| 93 | // have been unloaded due to no longer being used. If the module instance is null, the | 109 | // have been unloaded due to no longer being used. If the module instance is null, the |
| 94 | // module is no longer loaded. | 110 | // module is no longer loaded. |
| 95 | // Note this will only enumerate modules that have been loaded by a call to vm_t::load_module. | 111 | // Note this will only enumerate modules that have been loaded via a path. |
| 96 | void enum_loaded_modules(loaded_vm_module_callback_t callback) const; | 112 | void enum_loaded_modules(loaded_vm_module_callback_t callback) const; |
| 97 | 113 | ||
| 98 | // Access the scheduler control for this VM. | 114 | // Access the scheduler control for this VM. |
| @@ -118,10 +134,17 @@ namespace disvm · 19 unmodified lines | |||
| 118 | mutable std::mutex _modules_lock; | 134 | mutable std::mutex _modules_lock; |
| 119 | loaded_modules_t _modules; | 135 | loaded_modules_t _modules; |
| 120 | 136 | ||
| 137 | std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> _module_resolvers; | ||
| 121 | std::unique_ptr<runtime::vm_scheduler_t> _scheduler; | 138 | std::unique_ptr<runtime::vm_scheduler_t> _scheduler; |
| 122 | std::unique_ptr<runtime::vm_garbage_collector_t> _gc; | 139 | std::unique_ptr<runtime::vm_garbage_collector_t> _gc; |
| 123 | std::unique_ptr<runtime::vm_tool_dispatch_t> _tool_dispatch; | 140 | std::unique_ptr<runtime::vm_tool_dispatch_t> _tool_dispatch; |
| 124 | std::mutex _tool_dispatch_lock; | 141 | std::mutex _tool_dispatch_lock; |
| 142 | |||
| 143 | std::atomic_flag _last_syscall_error_message_lock; | ||
| 144 | std::array<char, 128> _last_syscall_error_message; | ||
| 145 | |||
| 146 | friend void disvm::runtime::push_syscall_error_message(disvm::vm_t &, const char *) noexcept; | ||
| 147 | friend std::string disvm::runtime::pop_syscall_error_message(disvm::vm_t &); | ||
| 125 | }; | 148 | }; |
| 126 | } | 149 | } |
| 127 | 150 | ||
| @@ -16,6 +16,10 @@ namespace disvm · 15 unmodified lines | |||
| 16 | { | 16 | { |
| 17 | namespace runtime | 17 | namespace runtime |
| 18 | { | 18 | { |
| 19 | // Functions for managing syscall exception messages | ||
| 20 | void push_syscall_error_message(disvm::vm_t &, const char *) noexcept; | ||
| 21 | std::string pop_syscall_error_message(disvm::vm_t &); | ||
| 22 | |||
| 19 | class vm_user_exception : public std::runtime_error | 23 | class vm_user_exception : public std::runtime_error |
| 20 | { | 24 | { |
| 21 | public: | 25 | public: |
| @@ -32,6 +36,16 @@ namespace disvm · 10 unmodified lines | |||
| 32 | { } | 36 | { } |
| 33 | }; | 37 | }; |
| 34 | 38 | ||
| 39 | class vm_syscall_exception : public vm_system_exception | ||
| 40 | { | ||
| 41 | public: | ||
| 42 | explicit vm_syscall_exception(disvm::vm_t &vm, const char *message) | ||
| 43 | : vm_system_exception(message) | ||
| 44 | { | ||
| 45 | push_syscall_error_message(vm, message); | ||
| 46 | } | ||
| 47 | }; | ||
| 48 | |||
| 35 | class vm_module_exception : public vm_system_exception | 49 | class vm_module_exception : public vm_system_exception |
| 36 | { | 50 | { |
| 37 | public: | 51 | public: |
| @@ -43,10 +57,6 @@ namespace disvm · 5 unmodified lines | |||
| 43 | class module_reader_exception final : public vm_module_exception | 57 | class module_reader_exception final : public vm_module_exception |
| 44 | { | 58 | { |
| 45 | public: | 59 | public: |
| 46 | explicit module_reader_exception() | ||
| 47 | : vm_module_exception("Invalid module") | ||
| 48 | { } | ||
| 49 | |||
| 50 | explicit module_reader_exception(const char *message) | 60 | explicit module_reader_exception(const char *message) |
| 51 | : vm_module_exception(message) | 61 | : vm_module_exception(message) |
| 52 | { } | 62 | { } |
| @@ -880,6 +880,19 @@ namespace disvm · 879 unmodified lines | |||
| 880 | // Create a garbage collector that does nothing. | 880 | // Create a garbage collector that does nothing. |
| 881 | std::unique_ptr<vm_garbage_collector_t> create_no_op_gc(vm_t &); | 881 | std::unique_ptr<vm_garbage_collector_t> create_no_op_gc(vm_t &); |
| 882 | 882 | ||
| 883 | // VM module path resolver interface | ||
| 884 | class vm_module_resolver_t | ||
| 885 | { | ||
| 886 | public: | ||
| 887 | virtual ~vm_module_resolver_t() = 0; | ||
| 888 | |||
| 889 | // Try to resolve the supplied path to a module. | ||
| 890 | // Implementers of this interface should never explicitly throw | ||
| 891 | // an exception and instead return 'false'. Exception thrown | ||
| 892 | // indirectly by calling supplied DisVM functions are valid. | ||
| 893 | virtual bool try_resolve_module(const char *path, std::unique_ptr<vm_module_t> &new_module) = 0; | ||
| 894 | }; | ||
| 895 | |||
| 883 | // Forward declaration | 896 | // Forward declaration |
| 884 | class vm_tool_controller_t; | 897 | class vm_tool_controller_t; |
| 885 | 898 | ||
| @@ -11,6 +11,7 @@ · 10 unmodified lines | |||
| 11 | #include <type_traits> | 11 | #include <type_traits> |
| 12 | #include <string> | 12 | #include <string> |
| 13 | #include <cstring> | 13 | #include <cstring> |
| 14 | #include <cassert> | ||
| 14 | #include <vector> | 15 | #include <vector> |
| 15 | 16 | ||
| 16 | namespace disvm | 17 | namespace disvm |
| @@ -36,34 +37,39 @@ namespace disvm · 19 unmodified lines | |||
| 36 | } | 37 | } |
| 37 | 38 | ||
| 38 | // split the string using the delimiters contained in the supplied string | 39 | // split the string using the delimiters contained in the supplied string |
| 39 | inline std::vector<std::string> split(const std::string &str, const char *delims) | 40 | inline std::vector<std::string> split(const char *str, const char *delims) |
| 40 | { | 41 | { |
| 41 | auto split_string = std::vector<std::string>{}; | 42 | assert(str != nullptr); |
| 42 | 43 | ||
| 43 | auto curr = str.begin(); | 44 | std::vector<std::string> parts; |
| 44 | auto end = str.cend(); | 45 | auto begin = str; |
| 45 | while (curr != end) | 46 | while (*str != '\0') |
| 46 | { | 47 | { |
| 47 | auto curr_begin = curr; | 48 | auto curr = str++; |
| 48 | 49 | // Check for any delimiter | |
| 49 | while (curr != end && (nullptr == std::strchr(delims, *curr))) | 50 | if (nullptr != std::strchr(delims, *curr)) |
| 50 | ++curr; | 51 | { |
| 52 | if (begin != curr) | ||
| 53 | parts.push_back({ begin, curr }); | ||
| 51 | 54 | ||
| 52 | if (curr_begin != curr) | 55 | begin = str; |
| 53 | split_string.push_back(std::move(std::string{ curr_begin, curr })); | 56 | } |
| 57 | } | ||
| 54 | 58 | ||
| 55 | if (curr == end) | 59 | if (begin != str) |
| 56 | break; | 60 | parts.push_back({ begin, str }); |
| 57 | 61 | ||
| 58 | ++curr; | 62 | return parts; |
| 59 | } | 63 | } |
| 60 | 64 | ||
| 61 | return split_string; | 65 | inline std::vector<std::string> split(const std::string &str, const char *delims) |
| 66 | { | ||
| 67 | return split(str.c_str(), delims); | ||
| 62 | } | 68 | } |
| 63 | 69 | ||
| 64 | inline std::vector<std::string> split(const std::string &str) | 70 | inline std::vector<std::string> split(const std::string &str) |
| 65 | { | 71 | { |
| 66 | return split(str, " "); | 72 | return split(str.c_str(), " "); |
| 67 | } | 73 | } |
| 68 | } | 74 | } |
| 69 | } | 75 | } |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | |||
| 13 | 'list.cpp', | 13 | 'list.cpp', |
| 14 | 'module_reader.cpp', | 14 | 'module_reader.cpp', |
| 15 | 'module_ref.cpp', | 15 | 'module_ref.cpp', |
| 16 | 'module_resolver.cpp', | ||
| 16 | 'scheduler.cpp', | 17 | 'scheduler.cpp', |
| 17 | 'stack.cpp', | 18 | 'stack.cpp', |
| 18 | 'string.cpp', | 19 | 'string.cpp', |
| @@ -1773,7 +1773,6 @@ namespace · 1772 unmodified lines | |||
| 1773 | catch (const vm_module_exception &e) | 1773 | catch (const vm_module_exception &e) |
| 1774 | { | 1774 | { |
| 1775 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "exception: load: >>%s<<", e.what()); | 1775 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "exception: load: >>%s<<", e.what()); |
| 1776 | assert(false && "Failure reading module"); | ||
| 1777 | return; | 1776 | return; |
| 1778 | } | 1777 | } |
| 1779 | } | 1778 | } |
| @@ -8,7 +8,7 @@ · 7 unmodified lines | |||
| 8 | #include <limits> | 8 | #include <limits> |
| 9 | #include <memory> | 9 | #include <memory> |
| 10 | #include <iostream> | 10 | #include <iostream> |
| 11 | #include <fstream> | 11 | #include <sstream> |
| 12 | #include <array> | 12 | #include <array> |
| 13 | #include <tuple> | 13 | #include <tuple> |
| 14 | #include <disvm.h> | 14 | #include <disvm.h> |
| @@ -739,20 +739,6 @@ vm_module_t::~vm_module_t() · 724 unmodified lines | |||
| 739 | original_mp->release(); | 739 | original_mp->release(); |
| 740 | } | 740 | } |
| 741 | 741 | ||
| 742 | std::unique_ptr<vm_module_t> disvm::read_module(const char *path) | ||
| 743 | { | ||
| 744 | assert(path != nullptr); | ||
| 745 | |||
| 746 | auto module_file = std::ifstream{ path, std::ifstream::binary }; | ||
| 747 | if (!module_file.is_open()) | ||
| 748 | { | ||
| 749 | assert(false && "Failed to open module"); | ||
| 750 | throw vm_user_exception{ "Failed to open module" }; | ||
| 751 | } | ||
| 752 | |||
| 753 | return read_module(module_file); | ||
| 754 | } | ||
| 755 | |||
| 756 | std::unique_ptr<runtime::vm_module_t> disvm::read_module(std::istream &data) | 742 | std::unique_ptr<runtime::vm_module_t> disvm::read_module(std::istream &data) |
| 757 | { | 743 | { |
| 758 | util::buffered_reader_t reader{ data }; | 744 | util::buffered_reader_t reader{ data }; |
| @@ -0,0 +1,72 @@ | |||
| 1 | // | ||
| 2 | // Dis VM | ||
| 3 | // File: module_resolver.cpp | ||
| 4 | // Author: arr | ||
| 5 | // | ||
| 6 | |||
| 7 | #include <cassert> | ||
| 8 | #include <fstream> | ||
| 9 | #include <sstream> | ||
| 10 | #include <disvm.h> | ||
| 11 | #include <debug.h> | ||
| 12 | #include <exceptions.h> | ||
| 13 | #include "module_resolver.h" | ||
| 14 | |||
| 15 | using namespace disvm::runtime; | ||
| 16 | |||
| 17 | // Empty destructor for vm module resolver 'interface' | ||
| 18 | vm_module_resolver_t::~vm_module_resolver_t() | ||
| 19 | { } | ||
| 20 | |||
| 21 | default_resolver_t::default_resolver_t(disvm::vm_t &vm) | ||
| 22 | : _vm{ vm } | ||
| 23 | { } | ||
| 24 | |||
| 25 | default_resolver_t::default_resolver_t(disvm::vm_t &vm, std::vector<std::string> probing_paths) | ||
| 26 | : _probing_paths{ std::move(probing_paths) } | ||
| 27 | , _vm{ vm } | ||
| 28 | { } | ||
| 29 | |||
| 30 | default_resolver_t::~default_resolver_t() | ||
| 31 | { } | ||
| 32 | |||
| 33 | bool default_resolver_t::try_resolve_module(const char *path, std::unique_ptr<vm_module_t> &new_module) | ||
| 34 | { | ||
| 35 | assert(path != nullptr); | ||
| 36 | |||
| 37 | auto log = debug::is_component_tracing_enabled<debug::component_trace_t::module>(); | ||
| 38 | if (log) | ||
| 39 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "resolve: try module path: >>%s<<", path); | ||
| 40 | |||
| 41 | auto module_file = std::ifstream{ path, std::ifstream::binary }; | ||
| 42 | if (!module_file.is_open()) | ||
| 43 | { | ||
| 44 | // The raw path isn't valid, try using probing paths | ||
| 45 | for (auto p : _probing_paths) | ||
| 46 | { | ||
| 47 | p.append(path); | ||
| 48 | if (log) | ||
| 49 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "resolve: try module path: >>%s<<", p.c_str()); | ||
| 50 | |||
| 51 | module_file = std::ifstream{ p, std::ifstream::binary }; | ||
| 52 | if (module_file.is_open()) | ||
| 53 | { | ||
| 54 | if (log) | ||
| 55 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "resolve: successful modified module path: >>%s<< >>%s<<", path, p.c_str()); | ||
| 56 | |||
| 57 | break; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | if (!module_file.is_open()) | ||
| 62 | { | ||
| 63 | push_syscall_error_message(_vm, "Unable to resolve path"); | ||
| 64 | return false; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | new_module = disvm::read_module(module_file); | ||
| 69 | assert(new_module != nullptr); | ||
| 70 | |||
| 71 | return true; | ||
| 72 | } | ||
| @@ -0,0 +1,37 @@ | |||
| 1 | // | ||
| 2 | // Dis VM | ||
| 3 | // File: module_resolver.h | ||
| 4 | // Author: arr | ||
| 5 | // | ||
| 6 | |||
| 7 | #ifndef _DISVM_SRC_VM_MODULE_RESOLVER_H_ | ||
| 8 | #define _DISVM_SRC_VM_MODULE_RESOLVER_H_ | ||
| 9 | |||
| 10 | #include <cstdint> | ||
| 11 | #include <string> | ||
| 12 | #include <vector> | ||
| 13 | #include <runtime.h> | ||
| 14 | |||
| 15 | namespace disvm | ||
| 16 | { | ||
| 17 | namespace runtime | ||
| 18 | { | ||
| 19 | // Default module resolver | ||
| 20 | class default_resolver_t final : public vm_module_resolver_t | ||
| 21 | { | ||
| 22 | public: | ||
| 23 | default_resolver_t(disvm::vm_t &vm); | ||
| 24 | default_resolver_t(disvm::vm_t &vm, std::vector<std::string> probing_paths); | ||
| 25 | ~default_resolver_t(); | ||
| 26 | |||
| 27 | public: // vm_module_resolver_t | ||
| 28 | bool try_resolve_module(const char *path, std::unique_ptr<vm_module_t> &new_module); | ||
| 29 | |||
| 30 | private: | ||
| 31 | const std::vector<std::string> _probing_paths; | ||
| 32 | disvm::vm_t &_vm; | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | #endif // _DISVM_SRC_VM_MODULE_RESOLVER_H_ | ||
| @@ -431,8 +431,6 @@ bool default_scheduler_t::enqueue_thread_unsafe(std::shared_ptr<thread_instance_ · 430 unmodified lines | |||
| 431 | throw vm_term_request{ err_msg }; | 431 | throw vm_term_request{ err_msg }; |
| 432 | } | 432 | } |
| 433 | 433 | ||
| 434 | case vm_thread_state_t::release: | ||
| 435 | case vm_thread_state_t::running: | ||
| 436 | default: | 434 | default: |
| 437 | assert(false && "Unexpected thread state"); | 435 | assert(false && "Unexpected thread state"); |
| 438 | throw vm_system_exception{ "Unexpected thread state" }; | 436 | throw vm_system_exception{ "Unexpected thread state" }; |
| @@ -166,26 +166,26 @@ namespace · 165 unmodified lines | |||
| 166 | return _fd_path; | 166 | return _fd_path; |
| 167 | } | 167 | } |
| 168 | 168 | ||
| 169 | word_t read(const word_t buffer_size_in_bytes, void *buffer) | 169 | word_t read(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) |
| 170 | { | 170 | { |
| 171 | if (_istream == nullptr) | 171 | if (_istream == nullptr) |
| 172 | throw vm_system_exception{ "File descriptor not open for read operation" }; | 172 | throw vm_system_exception{ "File descriptor not open for read operation" }; |
| 173 | 173 | ||
| 174 | _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes)); | 174 | _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes)); |
| 175 | if (_istream->bad()) | 175 | if (_istream->bad()) |
| 176 | throw vm_user_exception{ "Read operation error" }; | 176 | throw vm_syscall_exception{ vm, "Read operation error" }; |
| 177 | 177 | ||
| 178 | return static_cast<word_t>(_istream->gcount()); | 178 | return static_cast<word_t>(_istream->gcount()); |
| 179 | } | 179 | } |
| 180 | 180 | ||
| 181 | void write(const word_t buffer_size_in_bytes, void *buffer) | 181 | void write(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) |
| 182 | { | 182 | { |
| 183 | if (_ostream == nullptr) | 183 | if (_ostream == nullptr) |
| 184 | throw vm_system_exception{ "File descriptor not open for write operation" }; | 184 | throw vm_system_exception{ "File descriptor not open for write operation" }; |
| 185 | 185 | ||
| 186 | _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes)); | 186 | _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes)); |
| 187 | if (_ostream->bad()) | 187 | if (_ostream->bad()) |
| 188 | throw vm_user_exception{ "Write operation error" }; | 188 | throw vm_syscall_exception{ vm, "Write operation error" }; |
| 189 | } | 189 | } |
| 190 | 190 | ||
| 191 | big_t seek(const std::ios::seekdir start, const big_t offset) | 191 | big_t seek(const std::ios::seekdir start, const big_t offset) |
| @@ -470,7 +470,16 @@ void · 278 unmodified lines | |||
| 470 | Sys_fildes(vm_registers_t &r, vm_t &vm) | 470 | Sys_fildes(vm_registers_t &r, vm_t &vm) |
| 471 | { | 471 | { |
| 472 | auto &fp = r.stack.peek_frame()->base<F_Sys_fildes>(); | 472 | auto &fp = r.stack.peek_frame()->base<F_Sys_fildes>(); |
| 473 | throw vm_system_exception{ "Instruction not implemented" }; | 473 | |
| 474 | dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret)); | ||
| 475 | *fp.ret = nullptr; | ||
| 476 | |||
| 477 | auto fd = sys::fetch_fd_record(fp.fd); | ||
| 478 | if (fd != nullptr) | ||
| 479 | { | ||
| 480 | fd->add_ref(); | ||
| 481 | *fp.ret = fd->get_allocation(); | ||
| 482 | } | ||
| 474 | } | 483 | } |
| 475 | 484 | ||
| 476 | void | 485 | void |
| @@ -608,16 +617,16 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 131 unmodified lines | |||
| 608 | 617 | ||
| 609 | auto static_buffer = std::array<char, 1024>{}; | 618 | auto static_buffer = std::array<char, 1024>{}; |
| 610 | auto msg_args = &fp.vargs; | 619 | auto msg_args = &fp.vargs; |
| 611 | auto written = sys::printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data()); | 620 | auto written = sys::printf_to_buffer(vm, *str, msg_args, fp_base, static_buffer.size(), static_buffer.data()); |
| 612 | if (written >= 0) | 621 | if (written >= 0) |
| 613 | { | 622 | { |
| 614 | fd_stdout->write(written, static_buffer.data()); | 623 | fd_stdout->write(vm, written, static_buffer.data()); |
| 615 | } | 624 | } |
| 616 | else | 625 | else |
| 617 | { | 626 | { |
| 618 | auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2); | 627 | auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2); |
| 619 | written = sys::printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer); | 628 | written = sys::printf_to_dynamic_buffer(vm, *str, msg_args, fp_base, dynamic_buffer); |
| 620 | fd_stdout->write(written, dynamic_buffer.data()); | 629 | fd_stdout->write(vm, written, dynamic_buffer.data()); |
| 621 | } | 630 | } |
| 622 | 631 | ||
| 623 | *fp.ret = written; | 632 | *fp.ret = written; |
| @@ -657,7 +666,7 @@ Sys_read(vm_registers_t &r, vm_t &vm) · 33 unmodified lines | |||
| 657 | assert(fd_alloc->alloc_type == T_FD); | 666 | assert(fd_alloc->alloc_type == T_FD); |
| 658 | 667 | ||
| 659 | auto fd = fd_alloc->get_allocation<vm_fd_t>(); | 668 | auto fd = fd_alloc->get_allocation<vm_fd_t>(); |
| 660 | *fp.ret = fd->read(n, buffer->at(0)); | 669 | *fp.ret = fd->read(vm, n, buffer->at(0)); |
| 661 | } | 670 | } |
| 662 | 671 | ||
| 663 | void | 672 | void |
| @@ -729,19 +738,28 @@ namespace · 65 unmodified lines | |||
| 729 | bool is_delim(const runtime::rune_t c, const vm_string_t &delim) | 738 | bool is_delim(const runtime::rune_t c, const vm_string_t &delim) |
| 730 | { | 739 | { |
| 731 | const auto max = delim.get_length(); | 740 | const auto max = delim.get_length(); |
| 732 | assert(max > 0); | ||
| 733 | 741 | ||
| 734 | // Optimization for 3 or less delimiters | 742 | // Optimization for 3 or less delimiters |
| 735 | switch (max) | 743 | switch (max) |
| 736 | { | 744 | { |
| 737 | default: | 745 | default: |
| 738 | break; | 746 | break; |
| 739 | case 1: | ||
| 740 | return (delim.get_rune(0) == c); | ||
| 741 | case 2: | ||
| 742 | return (delim.get_rune(0) == c) || (delim.get_rune(1) == c); | ||
| 743 | case 3: | 747 | case 3: |
| 744 | return (delim.get_rune(0) == c) || (delim.get_rune(1) == c) || (delim.get_rune(2) == c); | 748 | if (delim.get_rune(2) != c) |
| 749 | { | ||
| 750 | case 2: | ||
| 751 | if (delim.get_rune(1) != c) | ||
| 752 | { | ||
| 753 | case 1: | ||
| 754 | if (delim.get_rune(0) != c) | ||
| 755 | { | ||
| 756 | case 0: | ||
| 757 | return false; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | } | ||
| 761 | |||
| 762 | return true; | ||
| 745 | } | 763 | } |
| 746 | 764 | ||
| 747 | for (auto i = word_t{ 0 }; i < max; ++i) | 765 | for (auto i = word_t{ 0 }; i < max; ++i) |
| @@ -900,7 +918,7 @@ Sys_write(vm_registers_t &r, vm_t &vm) · 152 unmodified lines | |||
| 900 | assert(fd_alloc->alloc_type == T_FD); | 918 | assert(fd_alloc->alloc_type == T_FD); |
| 901 | 919 | ||
| 902 | auto fd = fd_alloc->get_allocation<vm_fd_t>(); | 920 | auto fd = fd_alloc->get_allocation<vm_fd_t>(); |
| 903 | fd->write(n, buffer->at(0)); | 921 | fd->write(vm, n, buffer->at(0)); |
| 904 | 922 | ||
| 905 | *fp.ret = n; | 923 | *fp.ret = n; |
| 906 | } | 924 | } |
| @@ -64,7 +64,7 @@ vm_alloc_t * disvm::runtime::sys::fetch_fd_record(word_t fd) · 63 unmodified lines | |||
| 64 | std::lock_guard<std::mutex> lock{ fd_map_lock }; | 64 | std::lock_guard<std::mutex> lock{ fd_map_lock }; |
| 65 | auto iter = fd_map.find(fd); | 65 | auto iter = fd_map.find(fd); |
| 66 | if (iter == fd_map.cend()) | 66 | if (iter == fd_map.cend()) |
| 67 | throw vm_user_exception{ "Unknown file descriptor" }; | 67 | return nullptr; |
| 68 | 68 | ||
| 69 | return iter->second; | 69 | return iter->second; |
| 70 | } | 70 | } |
| @@ -30,8 +30,9 @@ namespace · 29 unmodified lines | |||
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | // Shamelessly adopted and altered from Inferno implementation (xprint - libinterp/runt.c) | 32 | // Shamelessly adopted and altered from Inferno implementation (xprint - libinterp/runt.c) |
| 33 | // [TODO] Handle UTF-8 | 33 | // [TODO] Handle UTF-8, binary format 'B' |
| 34 | word_t disvm::runtime::sys::printf_to_buffer( | 34 | word_t disvm::runtime::sys::printf_to_buffer( |
| 35 | disvm::vm_t &vm, | ||
| 35 | const disvm::runtime::vm_string_t &msg_fmt, | 36 | const disvm::runtime::vm_string_t &msg_fmt, |
| 36 | disvm::runtime::byte_t *msg_args, | 37 | disvm::runtime::byte_t *msg_args, |
| 37 | disvm::runtime::pointer_t base, | 38 | disvm::runtime::pointer_t base, |
| @@ -101,8 +102,8 @@ word_t disvm::runtime::sys::printf_to_buffer( · 63 unmodified lines | |||
| 101 | } | 102 | } |
| 102 | case 'o': // octal | 103 | case 'o': // octal |
| 103 | case 'd': // decimal | 104 | case 'd': // decimal |
| 104 | case 'x': // hexidecimal | 105 | case 'x': // hexadecimal |
| 105 | case 'X': // Upper-case hexidecimal | 106 | case 'X': // Upper-case hexadecimal |
| 106 | format.push_back(static_cast<char>(c)); | 107 | format.push_back(static_cast<char>(c)); |
| 107 | format.push_back('\0'); | 108 | format.push_back('\0'); |
| 108 | if (big_flag) | 109 | if (big_flag) |
| @@ -130,6 +131,12 @@ word_t disvm::runtime::sys::printf_to_buffer( · 21 unmodified lines | |||
| 130 | msg_args += sizeof(p); | 131 | msg_args += sizeof(p); |
| 131 | break; | 132 | break; |
| 132 | } | 133 | } |
| 134 | case 'r': // Last system error message | ||
| 135 | { | ||
| 136 | auto err_msg = pop_syscall_error_message(vm); | ||
| 137 | wb = std::snprintf(b_curr, (b_end - b_curr), "%s", err_msg.c_str()); | ||
| 138 | break; | ||
| 139 | } | ||
| 133 | // [SPEC] Undocumented format verb for debugging VM objects. | 140 | // [SPEC] Undocumented format verb for debugging VM objects. |
| 134 | case 'H': | 141 | case 'H': |
| 135 | { | 142 | { |
| @@ -163,13 +170,14 @@ word_t disvm::runtime::sys::printf_to_buffer( · 27 unmodified lines | |||
| 163 | } | 170 | } |
| 164 | 171 | ||
| 165 | word_t disvm::runtime::sys::printf_to_dynamic_buffer( | 172 | word_t disvm::runtime::sys::printf_to_dynamic_buffer( |
| 173 | disvm::vm_t &vm, | ||
| 166 | const disvm::runtime::vm_string_t &msg_fmt, | 174 | const disvm::runtime::vm_string_t &msg_fmt, |
| 167 | disvm::runtime::byte_t *msg_args, | 175 | disvm::runtime::byte_t *msg_args, |
| 168 | disvm::runtime::pointer_t base, | 176 | disvm::runtime::pointer_t base, |
| 169 | std::vector<char> &buffer) | 177 | std::vector<char> &buffer) |
| 170 | { | 178 | { |
| 171 | auto result = word_t{ 0 }; | 179 | auto result = word_t{ 0 }; |
| 172 | while (-1 == (result = printf_to_buffer(msg_fmt, msg_args, base, buffer.size(), buffer.data()))) | 180 | while (-1 == (result = printf_to_buffer(vm, msg_fmt, msg_args, base, buffer.size(), buffer.data()))) |
| 173 | buffer.resize(buffer.size() * 2); | 181 | buffer.resize(buffer.size() * 2); |
| 174 | 182 | ||
| 175 | return result; | 183 | return result; |
| @@ -22,6 +22,7 @@ namespace disvm · 21 unmodified lines | |||
| 22 | // Prints the format and arguments to the supplied buffer. | 22 | // Prints the format and arguments to the supplied buffer. |
| 23 | // Returns the number of bytes written. | 23 | // Returns the number of bytes written. |
| 24 | word_t printf_to_buffer( | 24 | word_t printf_to_buffer( |
| 25 | disvm::vm_t &vm, | ||
| 25 | const vm_string_t &msg_fmt, | 26 | const vm_string_t &msg_fmt, |
| 26 | byte_t *msg_args, | 27 | byte_t *msg_args, |
| 27 | pointer_t base, | 28 | pointer_t base, |
| @@ -31,6 +32,7 @@ namespace disvm · 3 unmodified lines | |||
| 31 | // Prints the format and arguments to the supplied dynamic buffer. | 32 | // Prints the format and arguments to the supplied dynamic buffer. |
| 32 | // Returns the number of bytes written. | 33 | // Returns the number of bytes written. |
| 33 | word_t printf_to_dynamic_buffer( | 34 | word_t printf_to_dynamic_buffer( |
| 35 | disvm::vm_t &vm, | ||
| 34 | const vm_string_t &msg_fmt, | 36 | const vm_string_t &msg_fmt, |
| 35 | byte_t *msg_args, | 37 | byte_t *msg_args, |
| 36 | pointer_t base, | 38 | pointer_t base, |
| @@ -52,10 +54,10 @@ namespace disvm · 15 unmodified lines | |||
| 52 | // The first three FD records are optimized for lock-less access. | 54 | // The first three FD records are optimized for lock-less access. |
| 53 | word_t create_fd_record(vm_alloc_t *fd_alloc); | 55 | word_t create_fd_record(vm_alloc_t *fd_alloc); |
| 54 | 56 | ||
| 57 | // Returns null if the record does not exist. | ||
| 55 | vm_alloc_t * fetch_fd_record(word_t fd); | 58 | vm_alloc_t * fetch_fd_record(word_t fd); |
| 56 | 59 | ||
| 57 | void drop_fd_record(word_t fd); | 60 | void drop_fd_record(word_t fd); |
| 58 | |||
| 59 | } | 61 | } |
| 60 | } | 62 | } |
| 61 | } | 63 | } |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | |||
| 7 | #include <cassert> | 7 | #include <cassert> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <algorithm> | 9 | #include <algorithm> |
| 10 | #include <sstream> | ||
| 10 | #include <disvm.h> | 11 | #include <disvm.h> |
| 11 | #include <debug.h> | 12 | #include <debug.h> |
| 12 | #include <runtime.h> | 13 | #include <runtime.h> |
| @@ -16,41 +17,107 @@ · 3 unmodified lines | |||
| 16 | #include "scheduler.h" | 17 | #include "scheduler.h" |
| 17 | #include "garbage_collector.h" | 18 | #include "garbage_collector.h" |
| 18 | #include "tool_dispatch.h" | 19 | #include "tool_dispatch.h" |
| 20 | #include "module_resolver.h" | ||
| 19 | 21 | ||
| 20 | using namespace disvm; | 22 | using namespace disvm; |
| 21 | using namespace disvm::runtime; | 23 | using namespace disvm::runtime; |
| 22 | 24 | ||
| 25 | namespace | ||
| 26 | { | ||
| 27 | // Ideally this would be custom assembly based on the CPU, | ||
| 28 | // but since this is just for the setting of the last error | ||
| 29 | // message there is already a large cost associated with the | ||
| 30 | // fact an error occurred so this is okay. | ||
| 31 | struct inefficient_spin_lock_t final | ||
| 32 | { | ||
| 33 | inefficient_spin_lock_t(std::atomic_flag &is_lock) | ||
| 34 | : _lf{ is_lock } | ||
| 35 | { | ||
| 36 | while (!_lf.test_and_set()) | ||
| 37 | ; | ||
| 38 | } | ||
| 39 | |||
| 40 | ~inefficient_spin_lock_t() | ||
| 41 | { | ||
| 42 | _lf.clear(); | ||
| 43 | } | ||
| 44 | |||
| 45 | private: | ||
| 46 | std::atomic_flag &_lf; | ||
| 47 | }; | ||
| 48 | } | ||
| 49 | |||
| 50 | void disvm::runtime::push_syscall_error_message(disvm::vm_t &vm, const char *msg) noexcept | ||
| 51 | { | ||
| 52 | inefficient_spin_lock_t sl{ vm._last_syscall_error_message_lock }; | ||
| 53 | |||
| 54 | assert(msg != nullptr); | ||
| 55 | auto bytes_to_copy = std::min(std::strlen(msg), vm._last_syscall_error_message.size() - 1); | ||
| 56 | std::memcpy(vm._last_syscall_error_message.data(), msg, bytes_to_copy); | ||
| 57 | |||
| 58 | // Ensure there is always a null | ||
| 59 | vm._last_syscall_error_message[bytes_to_copy] = '\0'; | ||
| 60 | } | ||
| 61 | |||
| 62 | std::string disvm::runtime::pop_syscall_error_message(disvm::vm_t &vm) | ||
| 63 | { | ||
| 64 | inefficient_spin_lock_t sl{ vm._last_syscall_error_message_lock }; | ||
| 65 | |||
| 66 | std::string msg{ vm._last_syscall_error_message.data() }; | ||
| 67 | vm._last_syscall_error_message[0] = '\0'; | ||
| 68 | return msg; | ||
| 69 | } | ||
| 70 | |||
| 23 | const uint32_t vm_t::root_vm_thread_id = 0; | 71 | const uint32_t vm_t::root_vm_thread_id = 0; |
| 24 | const uint32_t vm_t::default_system_thread_count = 1; | ||
| 25 | 72 | ||
| 26 | // The Inferno implementation defined the thread quanta as 2048 (include/interp.h) | 73 | // The Inferno implementation defined the thread quanta as 2048 (include/interp.h) |
| 27 | const uint32_t vm_t::default_thread_quanta = 2048; | 74 | const uint32_t default_thread_quanta = 2048; |
| 28 | 75 | const uint32_t default_system_thread_count = 1; | |
| 29 | vm_t::vm_t( | 76 | |
| 30 | create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler, | 77 | vm_config_t::vm_config_t() |
| 31 | create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc) | 78 | : create_gc{ nullptr } |
| 79 | , create_scheduler{ nullptr } | ||
| 80 | , sys_thread_pool_size{ default_system_thread_count } | ||
| 81 | , thread_quanta{ default_thread_quanta } | ||
| 82 | { } | ||
| 83 | |||
| 84 | vm_config_t::vm_config_t(vm_config_t &&other) | ||
| 85 | : additional_resolvers{ std::move(other.additional_resolvers) } | ||
| 86 | , create_gc{ other.create_gc } | ||
| 87 | , create_scheduler{ other.create_scheduler } | ||
| 88 | , probing_paths{ std::move(other.probing_paths) } | ||
| 89 | , sys_thread_pool_size{ other.sys_thread_pool_size } | ||
| 90 | , thread_quanta{ other.thread_quanta } | ||
| 91 | { } | ||
| 92 | |||
| 93 | vm_t::vm_t() | ||
| 94 | : _last_syscall_error_message_lock{ ATOMIC_FLAG_INIT } | ||
| 32 | { | 95 | { |
| 33 | // Initialize built-in modules. | 96 | // Initialize built-in modules. |
| 34 | builtin::initialize_builtin_modules(); | 97 | builtin::initialize_builtin_modules(); |
| 35 | 98 | ||
| 36 | if (create_gc == nullptr) | 99 | _gc = std::make_unique<default_garbage_collector_t>(*this); |
| 37 | _gc = std::make_unique<default_garbage_collector_t>(*this); | 100 | _scheduler = std::make_unique<default_scheduler_t>(*this, default_system_thread_count, default_thread_quanta); |
| 38 | else | 101 | _module_resolvers.push_back(std::make_unique<default_resolver_t>(*this)); |
| 39 | _gc = create_gc(*this); | ||
| 40 | |||
| 41 | if (create_scheduler == nullptr) | ||
| 42 | _scheduler = std::make_unique<default_scheduler_t>(*this, default_system_thread_count, default_thread_quanta); | ||
| 43 | else | ||
| 44 | _scheduler = create_scheduler(*this); | ||
| 45 | } | 102 | } |
| 46 | 103 | ||
| 47 | vm_t::vm_t(uint32_t system_thread_count, uint32_t thread_quanta) | 104 | vm_t::vm_t(vm_config_t config) |
| 48 | { | 105 | { |
| 49 | // Initialize built-in modules. | 106 | // Initialize built-in modules. |
| 50 | builtin::initialize_builtin_modules(); | 107 | builtin::initialize_builtin_modules(); |
| 51 | 108 | ||
| 52 | _gc = std::make_unique<default_garbage_collector_t>(*this); | 109 | if (config.create_gc == nullptr) |
| 53 | _scheduler = std::make_unique<default_scheduler_t>(*this, system_thread_count, thread_quanta); | 110 | _gc = std::make_unique<default_garbage_collector_t>(*this); |
| 111 | else | ||
| 112 | _gc = config.create_gc(*this); | ||
| 113 | |||
| 114 | if (config.create_scheduler == nullptr) | ||
| 115 | _scheduler = std::make_unique<default_scheduler_t>(*this, config.sys_thread_pool_size, config.thread_quanta); | ||
| 116 | else | ||
| 117 | _scheduler = config.create_scheduler(*this); | ||
| 118 | |||
| 119 | _module_resolvers = std::move(config.additional_resolvers); | ||
| 120 | _module_resolvers.push_back(std::make_unique<default_resolver_t>(*this, std::move(config.probing_paths))); | ||
| 54 | } | 121 | } |
| 55 | 122 | ||
| 56 | vm_t::~vm_t() | 123 | vm_t::~vm_t() |
| @@ -74,7 +141,7 @@ vm_version_t vm_t::get_version() const · 17 unmodified lines | |||
| 74 | 141 | ||
| 75 | namespace | 142 | namespace |
| 76 | { | 143 | { |
| 77 | std::unique_ptr<runtime::vm_thread_t> _create_thread_safe(std::unique_ptr<runtime::vm_module_ref_t> &entry_module_ref) | 144 | std::unique_ptr<runtime::vm_thread_t> _create_thread_safe(std::unique_ptr<runtime::vm_module_ref_t> entry_module_ref) |
| 78 | { | 145 | { |
| 79 | auto thread = std::make_unique<vm_thread_t>(*entry_module_ref, disvm::vm_t::root_vm_thread_id); | 146 | auto thread = std::make_unique<vm_thread_t>(*entry_module_ref, disvm::vm_t::root_vm_thread_id); |
| 80 | 147 | ||
| @@ -92,7 +159,7 @@ const runtime::vm_thread_t& vm_t::exec(const char *path) · 11 unmodified lines | |||
| 92 | auto entry_module = load_module(path); | 159 | auto entry_module = load_module(path); |
| 93 | 160 | ||
| 94 | auto entry_module_ref = std::make_unique<vm_module_ref_t>(entry_module); | 161 | auto entry_module_ref = std::make_unique<vm_module_ref_t>(entry_module); |
| 95 | auto thread = _create_thread_safe(entry_module_ref); | 162 | auto thread = _create_thread_safe(std::move(entry_module_ref)); |
| 96 | return schedule_thread(std::move(thread)); | 163 | return schedule_thread(std::move(thread)); |
| 97 | } | 164 | } |
| 98 | 165 | ||
| @@ -101,7 +168,7 @@ const runtime::vm_thread_t& vm_t::exec(std::unique_ptr<runtime::vm_module_t> ent · 2 unmodified lines | |||
| 101 | assert(entry_module != nullptr); | 168 | assert(entry_module != nullptr); |
| 102 | 169 | ||
| 103 | auto entry_module_ref = std::make_unique<vm_module_ref_t>(std::move(entry_module)); | 170 | auto entry_module_ref = std::make_unique<vm_module_ref_t>(std::move(entry_module)); |
| 104 | auto thread = _create_thread_safe(entry_module_ref); | 171 | auto thread = _create_thread_safe(std::move(entry_module_ref)); |
| 105 | return schedule_thread(std::move(thread)); | 172 | return schedule_thread(std::move(thread)); |
| 106 | } | 173 | } |
| 107 | 174 | ||
| @@ -112,12 +179,64 @@ const runtime::vm_thread_t& vm_t::fork( · 4 unmodified lines | |||
| 112 | runtime::vm_pc_t initial_pc) | 179 | runtime::vm_pc_t initial_pc) |
| 113 | { | 180 | { |
| 114 | if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc) | 181 | if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc) |
| 115 | throw vm_system_exception{ "Invalid entry program counter" }; | 182 | throw vm_user_exception{ "Invalid entry program counter" }; |
| 116 | 183 | ||
| 117 | auto thread = std::make_unique<vm_thread_t>(module_ref, parent_tid, initial_frame, initial_pc); | 184 | auto thread = std::make_unique<vm_thread_t>(module_ref, parent_tid, initial_frame, initial_pc); |
| 118 | return schedule_thread(std::move(thread)); | 185 | return schedule_thread(std::move(thread)); |
| 119 | } | 186 | } |
| 120 | 187 | ||
| 188 | namespace | ||
| 189 | { | ||
| 190 | std::unique_ptr<vm_module_t> resolve_module_from_path(const char *path, const std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> &resolvers) | ||
| 191 | { | ||
| 192 | assert(!resolvers.empty()); | ||
| 193 | std::unique_ptr<vm_module_t> resolved_module; | ||
| 194 | |||
| 195 | // Check if the path is for the Inferno OS. | ||
| 196 | const char *inferno_root_path = "/dis/"; | ||
| 197 | if (std::strncmp(path, inferno_root_path, sizeof(inferno_root_path)) == 0) | ||
| 198 | { | ||
| 199 | if (debug::is_component_tracing_enabled<debug::component_trace_t::module>()) | ||
| 200 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "load: vm module: Inferno OS path detected - >>%s<<", path); | ||
| 201 | |||
| 202 | // Plus 1 for the next character. We know this will either be | ||
| 203 | // null or valid character since we searched for a string | ||
| 204 | // containing the character above. | ||
| 205 | const char *module_name = std::strrchr(path, '/') + 1; | ||
| 206 | |||
| 207 | if (*module_name != '\0') | ||
| 208 | { | ||
| 209 | for (auto &r : resolvers) | ||
| 210 | { | ||
| 211 | // Since we are using a modified path, throwing an exception | ||
| 212 | // isn't fair. We will catch all exceptions here and rely on | ||
| 213 | // non-modified path resolution to trigger the actual failure. | ||
| 214 | try | ||
| 215 | { | ||
| 216 | if (r->try_resolve_module(module_name, resolved_module)) | ||
| 217 | return resolved_module; | ||
| 218 | } | ||
| 219 | catch (...) | ||
| 220 | { | ||
| 221 | // No-op | ||
| 222 | } | ||
| 223 | } | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | // Attempt to resolve the path | ||
| 228 | for (auto &r : resolvers) | ||
| 229 | { | ||
| 230 | if (r->try_resolve_module(path, resolved_module)) | ||
| 231 | return resolved_module; | ||
| 232 | } | ||
| 233 | |||
| 234 | std::stringstream ss; | ||
| 235 | ss << "Failed to resolve path to module: " << path; | ||
| 236 | throw vm_module_exception{ ss.str().c_str() }; | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 121 | std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) | 240 | std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) |
| 122 | { | 241 | { |
| 123 | if (path == nullptr) | 242 | if (path == nullptr) |
| @@ -139,12 +258,13 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 15 unmodified lines | |||
| 139 | // If an entry exists but is null, the module was loaded before. Read the module again and return. | 258 | // If an entry exists but is null, the module was loaded before. Read the module again and return. |
| 140 | if (module == nullptr) | 259 | if (module == nullptr) |
| 141 | { | 260 | { |
| 142 | module = std::shared_ptr<vm_module_t>{ std::move(read_module(path)) }; | 261 | module = std::shared_ptr<vm_module_t>{ std::move(resolve_module_from_path(path, _module_resolvers)) }; |
| 143 | module->vm_id = iter->vm_id; | 262 | module->vm_id = iter->vm_id; |
| 144 | 263 | ||
| 145 | iter->module = module; | 264 | iter->module = module; |
| 146 | 265 | ||
| 147 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<", path); | 266 | if (debug::is_component_tracing_enabled<debug::component_trace_t::module>()) |
| 267 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<", path); | ||
| 148 | } | 268 | } |
| 149 | 269 | ||
| 150 | return module; | 270 | return module; |
| @@ -159,8 +279,8 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 8 unmodified lines | |||
| 159 | } | 279 | } |
| 160 | else | 280 | else |
| 161 | { | 281 | { |
| 162 | // Read in the module | 282 | new_module = resolve_module_from_path(path, _module_resolvers); |
| 163 | new_module = std::move(read_module(path)); | 283 | assert(new_module != nullptr); |
| 164 | } | 284 | } |
| 165 | 285 | ||
| 166 | auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path)); | 286 | auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path)); |
| @@ -283,7 +283,12 @@ vm_alloc_t::vm_alloc_t(std::shared_ptr<const type_descriptor_t> td) · 282 unmodified lines | |||
| 283 | 283 | ||
| 284 | vm_alloc_t::~vm_alloc_t() | 284 | vm_alloc_t::~vm_alloc_t() |
| 285 | { | 285 | { |
| 286 | assert(_ref_count == 0); | 286 | #ifndef NDEBUG |
| 287 | if (_ref_count != 0) | ||
| 288 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::warning, | ||
| 289 | "vm alloc being destroy with non-zero reference count. " | ||
| 290 | "This could be okay if this is happening due to frame unwinding from an exception"); | ||
| 291 | #endif | ||
| 287 | 292 | ||
| 288 | auto finalizer = alloc_type->finalizer; | 293 | auto finalizer = alloc_type->finalizer; |
| 289 | if (finalizer != type_descriptor_t::no_finalizer) | 294 | if (finalizer != type_descriptor_t::no_finalizer) |
| @@ -291,7 +296,9 @@ vm_alloc_t::~vm_alloc_t() · 1 unmodified line | |||
| 291 | 296 | ||
| 292 | // Free pointer types | 297 | // Free pointer types |
| 293 | destroy_memory(*alloc_type, get_allocation()); | 298 | destroy_memory(*alloc_type, get_allocation()); |
| 294 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm alloc"); | 299 | |
| 300 | if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>()) | ||
| 301 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm alloc"); | ||
| 295 | } | 302 | } |
| 296 | 303 | ||
| 297 | std::size_t vm_alloc_t::add_ref() | 304 | std::size_t vm_alloc_t::add_ref() |