Code analysis violations Bug in scheduler - race during with worker thread shutdown.
Unified · Split
| @@ -60,6 +60,42 @@ namespace disvm · 59 unmodified lines | |||
| 60 | static_assert( | 60 | static_assert( |
| 61 | sizeof(vm_frame_base_alloc_t) == runtime_constants::default_register_count * runtime_constants::default_register_size_bytes, | 61 | sizeof(vm_frame_base_alloc_t) == runtime_constants::default_register_count * runtime_constants::default_register_size_bytes, |
| 62 | "Default register allocation should match base allocation type"); | 62 | "Default register allocation should match base allocation type"); |
| 63 | |||
| 64 | // | ||
| 65 | // Helpers for accessing VM types | ||
| 66 | // | ||
| 67 | |||
| 68 | // Get value-type reference | ||
| 69 | template<typename ValueType> | ||
| 70 | ValueType& vt_ref(pointer_t vt) | ||
| 71 | { | ||
| 72 | assert(vt != nullptr); | ||
| 73 | return *reinterpret_cast<ValueType *>(vt); | ||
| 74 | } | ||
| 75 | |||
| 76 | // Get allocation type value | ||
| 77 | template<typename AllocType> | ||
| 78 | typename std::enable_if<std::is_base_of<vm_alloc_t, AllocType>::value, AllocType *>::type | ||
| 79 | at_val(pointer_t at) | ||
| 80 | { | ||
| 81 | assert(at != nullptr); | ||
| 82 | return vm_alloc_t::from_allocation<AllocType>(*reinterpret_cast<pointer_t *>(at)); | ||
| 83 | } | ||
| 84 | |||
| 85 | // Get allocation type value - base alloc type | ||
| 86 | template<> | ||
| 87 | inline vm_alloc_t *at_val<vm_alloc_t>(pointer_t at) | ||
| 88 | { | ||
| 89 | assert(at != nullptr); | ||
| 90 | return vm_alloc_t::from_allocation(*reinterpret_cast<pointer_t *>(at)); | ||
| 91 | } | ||
| 92 | |||
| 93 | // Get pointer type reference | ||
| 94 | inline pointer_t &pt_ref(pointer_t pt) | ||
| 95 | { | ||
| 96 | assert(pt != nullptr); | ||
| 97 | return *reinterpret_cast<pointer_t *>(pt); | ||
| 98 | } | ||
| 63 | } | 99 | } |
| 64 | } | 100 | } |
| 65 | 101 | ||
| @@ -249,7 +249,7 @@ namespace disvm · 248 unmodified lines | |||
| 249 | 249 | ||
| 250 | vm_list_t* get_tail() const; | 250 | vm_list_t* get_tail() const; |
| 251 | 251 | ||
| 252 | vm_list_t* drop_tail(); | 252 | void set_tail(vm_list_t *new_tail); |
| 253 | 253 | ||
| 254 | // Returns a pointer to the value | 254 | // Returns a pointer to the value |
| 255 | pointer_t value() const; | 255 | pointer_t value() const; |
| @@ -12,15 +12,16 @@ · 11 unmodified lines | |||
| 12 | 12 | ||
| 13 | int main(int argc, char* argv[]) | 13 | int main(int argc, char* argv[]) |
| 14 | { | 14 | { |
| 15 | auto module = "C:\\Users\\linuxuser27\\source\\disvm\\basic_limbo_tests.dis"; | 15 | auto module = "basic_limbo_tests.dis"; |
| 16 | //auto module = "C:\\inferno\\dis\\limbo.dis"; | 16 | //auto module = "C:\\inferno\\dis\\limbo.dis"; |
| 17 | //auto module = "C:\\Users\\linuxuser27\\source\\disvm\\empty.dis"; | 17 | //auto module = "empty.dis"; |
| 18 | 18 | ||
| 19 | if (argc >= 2) | 19 | if (argc >= 2) |
| 20 | module = argv[1]; | 20 | module = argv[1]; |
| 21 | 21 | ||
| 22 | clock_t t = ::clock(); | 22 | clock_t t = std::clock(); |
| 23 | 23 | ||
| 24 | //disvm::debug::set_component_tracing(disvm::debug::component_trace_t::builtin, true); | ||
| 24 | //disvm::debug::set_component_tracing(disvm::debug::component_trace_t::memory, true); | 25 | //disvm::debug::set_component_tracing(disvm::debug::component_trace_t::memory, true); |
| 25 | //disvm::debug::set_component_tracing(disvm::debug::component_trace_t::module, true); | 26 | //disvm::debug::set_component_tracing(disvm::debug::component_trace_t::module, true); |
| 26 | //disvm::debug::set_component_tracing(disvm::debug::component_trace_t::exception, true); | 27 | //disvm::debug::set_component_tracing(disvm::debug::component_trace_t::exception, true); |
| @@ -33,12 +34,12 @@ int main(int argc, char* argv[]) · 6 unmodified lines | |||
| 33 | disvm::debug::set_logging_callback([](const disvm::debug::component_trace_t, const disvm::debug::log_level_t l, const char *msg, std::va_list ls) { std::vprintf(msg, ls); }); | 34 | disvm::debug::set_logging_callback([](const disvm::debug::component_trace_t, const disvm::debug::log_level_t l, const char *msg, std::va_list ls) { std::vprintf(msg, ls); }); |
| 34 | 35 | ||
| 35 | int c = 1; | 36 | int c = 1; |
| 36 | //for (;;) | 37 | for (;;) |
| 37 | { | 38 | { |
| 38 | ::printf("####### VM test loop (%d) #######\n", c++); | 39 | std::printf("####### VM test loop (%d) #######\n", c++); |
| 39 | disvm::vm_t v{ 1, 2048 }; | 40 | disvm::vm_t v{ 4, 2048 }; |
| 40 | 41 | ||
| 41 | for (auto i = 0; i < 1; ++i) | 42 | for (auto i = 0; i < 10; ++i) |
| 42 | v.exec(module); | 43 | v.exec(module); |
| 43 | 44 | ||
| 44 | v.spin_sleep_till_idle(std::chrono::milliseconds(10)); | 45 | v.spin_sleep_till_idle(std::chrono::milliseconds(10)); |
| @@ -19,7 +19,7 @@ using namespace disvm::runtime::builtin; · 18 unmodified lines | |||
| 19 | 19 | ||
| 20 | namespace | 20 | namespace |
| 21 | { | 21 | { |
| 22 | std::atomic_bool builtin_modules_initialized; | 22 | std::atomic_bool builtin_modules_initialized{ false }; |
| 23 | 23 | ||
| 24 | std::mutex builtin_modules_lock; | 24 | std::mutex builtin_modules_lock; |
| 25 | std::forward_list<std::shared_ptr<vm_module_t>> builtin_modules; | 25 | std::forward_list<std::shared_ptr<vm_module_t>> builtin_modules; |
| @@ -23,6 +23,8 @@ · 22 unmodified lines | |||
| 23 | 'vm_exception_handler.cpp', | 23 | 'vm_exception_handler.cpp', |
| 24 | 'sys/print.cpp', | 24 | 'sys/print.cpp', |
| 25 | 'sys/Sysmod.cpp', | 25 | 'sys/Sysmod.cpp', |
| 26 | 'sys/file_system.cpp', | ||
| 27 | 'sys/fd_manager.cpp', | ||
| 26 | ], | 28 | ], |
| 27 | 'include_dirs': [ | 29 | 'include_dirs': [ |
| 28 | '../include' | 30 | '../include' |
| @@ -17,6 +17,7 @@ · 16 unmodified lines | |||
| 17 | #include <utils.h> | 17 | #include <utils.h> |
| 18 | #include <exceptions.h> | 18 | #include <exceptions.h> |
| 19 | #include <debug.h> | 19 | #include <debug.h> |
| 20 | #include <builtin_module.h> | ||
| 20 | #include "execution_table.h" | 21 | #include "execution_table.h" |
| 21 | 22 | ||
| 22 | using namespace disvm; | 23 | using namespace disvm; |
| @@ -31,36 +32,6 @@ namespace · 8 unmodified lines | |||
| 31 | using uword_t = uint32_t; // unsigned word_t | 32 | using uword_t = uint32_t; // unsigned word_t |
| 32 | using ubig_t = uint64_t; // unsigned big_t | 33 | using ubig_t = uint64_t; // unsigned big_t |
| 33 | 34 | ||
| 34 | template<typename ValueType> | ||
| 35 | ValueType& vt_ref(pointer_t vt) | ||
| 36 | { | ||
| 37 | assert(vt != nullptr); | ||
| 38 | return *reinterpret_cast<ValueType *>(vt); | ||
| 39 | } | ||
| 40 | |||
| 41 | template<typename AllocType> | ||
| 42 | typename std::enable_if<std::is_base_of<vm_alloc_t, AllocType>::value, AllocType *>::type | ||
| 43 | at_val(pointer_t at) | ||
| 44 | { | ||
| 45 | assert(at != nullptr); | ||
| 46 | |||
| 47 | return vm_alloc_t::from_allocation<AllocType>(*reinterpret_cast<pointer_t *>(at)); | ||
| 48 | } | ||
| 49 | |||
| 50 | template<> | ||
| 51 | vm_alloc_t *at_val<vm_alloc_t>(pointer_t at) | ||
| 52 | { | ||
| 53 | assert(at != nullptr); | ||
| 54 | |||
| 55 | return vm_alloc_t::from_allocation(*reinterpret_cast<pointer_t *>(at)); | ||
| 56 | } | ||
| 57 | |||
| 58 | pointer_t &pt_ref(pointer_t pt) | ||
| 59 | { | ||
| 60 | assert(pt != nullptr); | ||
| 61 | return *reinterpret_cast<pointer_t *>(pt); | ||
| 62 | } | ||
| 63 | |||
| 64 | #define EXEC_DECL(inst_name) void inst_name(vm_registers_t &r, vm_t& vm) | 35 | #define EXEC_DECL(inst_name) void inst_name(vm_registers_t &r, vm_t& vm) |
| 65 | 36 | ||
| 66 | EXEC_DECL(notimpl) { assert(false && "Instruction not implemented"); throw vm_system_exception{ "Instruction not implemented" }; } | 37 | EXEC_DECL(notimpl) { assert(false && "Instruction not implemented"); throw vm_system_exception{ "Instruction not implemented" }; } |
| @@ -805,7 +776,7 @@ namespace · 738 unmodified lines | |||
| 805 | if (tail_maybe != nullptr) | 776 | if (tail_maybe != nullptr) |
| 806 | tail_maybe->release(); | 777 | tail_maybe->release(); |
| 807 | 778 | ||
| 808 | auto new_list = new vm_list_t(intrinsic_type_desc::type<PrimitiveType>(), tail_maybe); | 779 | auto new_list = new vm_list_t{ intrinsic_type_desc::type<PrimitiveType>(), tail_maybe }; |
| 809 | 780 | ||
| 810 | auto value = vt_ref<PrimitiveType>(r.src); | 781 | auto value = vt_ref<PrimitiveType>(r.src); |
| 811 | vt_ref<PrimitiveType>(new_list->value()) = value; | 782 | vt_ref<PrimitiveType>(new_list->value()) = value; |
| @@ -825,7 +796,8 @@ namespace · 13 unmodified lines | |||
| 825 | tail_maybe->release(); | 796 | tail_maybe->release(); |
| 826 | 797 | ||
| 827 | // Create a new list | 798 | // Create a new list |
| 828 | auto new_list = new vm_list_t(intrinsic_type_desc::type<pointer_t>(), tail_maybe); | 799 | auto new_list = new vm_list_t{ intrinsic_type_desc::type<pointer_t>(), tail_maybe }; |
| 800 | |||
| 829 | auto alloc = at_val<vm_alloc_t>(r.src); | 801 | auto alloc = at_val<vm_alloc_t>(r.src); |
| 830 | if (alloc != nullptr) | 802 | if (alloc != nullptr) |
| 831 | { | 803 | { |
| @@ -858,7 +830,8 @@ namespace · 26 unmodified lines | |||
| 858 | tail_maybe->release(); | 830 | tail_maybe->release(); |
| 859 | 831 | ||
| 860 | // Create a new list | 832 | // Create a new list |
| 861 | auto new_list = new vm_list_t(type, tail_maybe); | 833 | auto new_list = new vm_list_t{ type, tail_maybe }; |
| 834 | |||
| 862 | auto destination = new_list->value(); | 835 | auto destination = new_list->value(); |
| 863 | std::memmove(destination, r.src, type->size_in_bytes); | 836 | std::memmove(destination, r.src, type->size_in_bytes); |
| 864 | 837 | ||
| @@ -5,6 +5,7 @@ · 4 unmodified lines | |||
| 5 | // | 5 | // |
| 6 | 6 | ||
| 7 | #include <runtime.h> | 7 | #include <runtime.h> |
| 8 | #include <exceptions.h> | ||
| 8 | #include <vm_memory.h> | 9 | #include <vm_memory.h> |
| 9 | #include <debug.h> | 10 | #include <debug.h> |
| 10 | 11 | ||
| @@ -21,14 +22,10 @@ vm_list_t::vm_list_t(std::shared_ptr<const type_descriptor_t> td, vm_list_t *tai · 10 unmodified lines | |||
| 21 | , _element_type{ td } | 22 | , _element_type{ td } |
| 22 | , _is_alloc{ sizeof(_mem) < td->size_in_bytes } | 23 | , _is_alloc{ sizeof(_mem) < td->size_in_bytes } |
| 23 | , _mem{} | 24 | , _mem{} |
| 24 | , _tail{ tail } | 25 | , _tail{ nullptr } |
| 25 | { | 26 | { |
| 26 | assert(_element_type != nullptr); | 27 | assert(_element_type != nullptr); |
| 27 | if (_tail != nullptr) | 28 | set_tail(tail); |
| 28 | { | ||
| 29 | assert(tail->get_element_type() == _element_type); | ||
| 30 | _tail->add_ref(); | ||
| 31 | } | ||
| 32 | 29 | ||
| 33 | // If the default list element storage is not enough, allocate more memory. | 30 | // If the default list element storage is not enough, allocate more memory. |
| 34 | if (_is_alloc) | 31 | if (_is_alloc) |
| @@ -41,14 +38,15 @@ vm_list_t::vm_list_t(std::shared_ptr<const type_descriptor_t> td, vm_list_t *tai · 6 unmodified lines | |||
| 41 | init_memory(*_element_type, &_mem.local); | 38 | init_memory(*_element_type, &_mem.local); |
| 42 | } | 39 | } |
| 43 | 40 | ||
| 44 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm list: new\n"); | 41 | if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>()) |
| 42 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm list\n"); | ||
| 45 | } | 43 | } |
| 46 | 44 | ||
| 47 | vm_list_t::~vm_list_t() | 45 | vm_list_t::~vm_list_t() |
| 48 | { | 46 | { |
| 49 | // Remove tail | 47 | // Remove tail |
| 50 | dec_ref_count_and_free(_tail); | 48 | dec_ref_count_and_free(_tail); |
| 51 | _tail = nullptr; | 49 | debug::assign_debug_pointer(&_tail); |
| 52 | 50 | ||
| 53 | // Destroy and free the list element, if default element size was surpassed. | 51 | // Destroy and free the list element, if default element size was surpassed. |
| 54 | if (_is_alloc) | 52 | if (_is_alloc) |
| @@ -88,12 +86,20 @@ vm_list_t *vm_list_t::get_tail() const · 33 unmodified lines | |||
| 88 | return _tail; | 86 | return _tail; |
| 89 | } | 87 | } |
| 90 | 88 | ||
| 91 | vm_list_t *vm_list_t::drop_tail() | 89 | void vm_list_t::set_tail(vm_list_t *new_tail) |
| 92 | { | 90 | { |
| 93 | auto tail = _tail; | 91 | if (new_tail != nullptr) |
| 94 | _tail = nullptr; | 92 | { |
| 95 | tail->release(); | 93 | if (new_tail->get_element_type() != _element_type) |
| 96 | return tail; | 94 | throw type_violation{}; |
| 95 | |||
| 96 | new_tail->add_ref(); | ||
| 97 | } | ||
| 98 | |||
| 99 | auto prev_tail = _tail; | ||
| 100 | _tail = new_tail; | ||
| 101 | |||
| 102 | dec_ref_count_and_free(prev_tail); | ||
| 97 | } | 103 | } |
| 98 | 104 | ||
| 99 | pointer_t vm_list_t::value() const | 105 | pointer_t vm_list_t::value() const |
| @@ -392,8 +392,8 @@ namespace · 391 unmodified lines | |||
| 392 | auto base = reinterpret_cast<byte_t *>(modobj.original_mp->get_allocation()); | 392 | auto base = reinterpret_cast<byte_t *>(modobj.original_mp->get_allocation()); |
| 393 | for (;;) | 393 | for (;;) |
| 394 | { | 394 | { |
| 395 | auto bytesRead = reader.get_next_bytes(sizeof(code), &code); | 395 | auto bytes_read = reader.get_next_bytes(sizeof(code), &code); |
| 396 | if (bytesRead != sizeof(code)) throw module_reader_exception{ "Failed to read data item code" }; | 396 | if (bytes_read != sizeof(code)) throw module_reader_exception{ "Failed to read data item code" }; |
| 397 | if (code == 0) | 397 | if (code == 0) |
| 398 | break; | 398 | break; |
| 399 | 399 | ||
| @@ -444,8 +444,8 @@ namespace · 44 unmodified lines | |||
| 444 | case datum_type_t::utf_string: | 444 | case datum_type_t::utf_string: |
| 445 | { | 445 | { |
| 446 | auto str_buffer = std::vector<uint8_t>(count); | 446 | auto str_buffer = std::vector<uint8_t>(count); |
| 447 | auto bytesRead = reader.get_next_bytes(count, str_buffer.data()); | 447 | auto str_bytes = reader.get_next_bytes(count, str_buffer.data()); |
| 448 | if (bytesRead != count) | 448 | if (str_bytes != count) |
| 449 | throw module_reader_exception{ "Failed to read string data" }; | 449 | throw module_reader_exception{ "Failed to read string data" }; |
| 450 | 450 | ||
| 451 | auto new_string = new vm_string_t(str_buffer.size(), str_buffer.data()); | 451 | auto new_string = new vm_string_t(str_buffer.size(), str_buffer.data()); |
| @@ -47,6 +47,7 @@ default_scheduler_t::default_scheduler_t(vm_t &vm, uint32_t system_thread_count, · 46 unmodified lines | |||
| 47 | : _gc_complete{ true } | 47 | : _gc_complete{ true } |
| 48 | , _gc_counter{ 1 } | 48 | , _gc_counter{ 1 } |
| 49 | , _running_vm_thread_count{ 0 } | 49 | , _running_vm_thread_count{ 0 } |
| 50 | , _terminating{ false } | ||
| 50 | , _worker_thread_count{ system_thread_count } | 51 | , _worker_thread_count{ system_thread_count } |
| 51 | , _vm{ vm } | 52 | , _vm{ vm } |
| 52 | , _vm_thread_quanta{ thread_quanta } | 53 | , _vm_thread_quanta{ thread_quanta } |
| @@ -58,18 +59,14 @@ default_scheduler_t::~default_scheduler_t() · 5 unmodified lines | |||
| 58 | // Drain the runnable queue and clear out all the threads | 59 | // Drain the runnable queue and clear out all the threads |
| 59 | { | 60 | { |
| 60 | std::lock_guard<std::mutex> lock{ _vm_threads_lock }; | 61 | std::lock_guard<std::mutex> lock{ _vm_threads_lock }; |
| 62 | _terminating = true; | ||
| 63 | |||
| 61 | while (!_runnable_vm_threads.empty()) | 64 | while (!_runnable_vm_threads.empty()) |
| 62 | _runnable_vm_threads.pop(); | 65 | _runnable_vm_threads.pop(); |
| 63 | 66 | ||
| 64 | _all_vm_threads.clear(); | 67 | _all_vm_threads.clear(); |
| 65 | } | 68 | } |
| 66 | 69 | ||
| 67 | // Set terminating flag | ||
| 68 | { | ||
| 69 | std::unique_lock<std::mutex> worker_event_lock{ _worker_event_lock }; | ||
| 70 | _terminating = true; | ||
| 71 | } | ||
| 72 | |||
| 73 | // Notify all workers | 70 | // Notify all workers |
| 74 | _worker_event.notify_all(); | 71 | _worker_event.notify_all(); |
| 75 | 72 | ||
| @@ -193,49 +190,44 @@ std::shared_ptr<default_scheduler_t::thread_instance_t> default_scheduler_t::nex · 117 unmodified lines | |||
| 193 | for (;;) | 190 | for (;;) |
| 194 | { | 191 | { |
| 195 | // Check for a vm thread in the queue | 192 | // Check for a vm thread in the queue |
| 193 | std::unique_lock<std::mutex> lock{ _vm_threads_lock }; | ||
| 194 | |||
| 195 | // Check if a GC should be performed. | ||
| 196 | // Matching part of the logic in Inferno (emu/port/dis.c). | ||
| 197 | if ((_gc_counter & 0xff) == 0) | ||
| 196 | { | 198 | { |
| 197 | std::unique_lock<std::mutex> lock{ _vm_threads_lock }; | 199 | const auto running_thread_count_local = _running_vm_thread_count; |
| 198 | 200 | const auto is_gc_thread = running_thread_count_local == 0; | |
| 199 | // Check if a GC should be performed. | 201 | perform_gc(is_gc_thread, lock); |
| 200 | // Matching part of the logic in Inferno (emu/port/dis.c). | ||
| 201 | if ((_gc_counter & 0xff) == 0) | ||
| 202 | { | ||
| 203 | const auto running_thread_count_local = _running_vm_thread_count; | ||
| 204 | const auto is_gc_thread = running_thread_count_local == 0; | ||
| 205 | perform_gc(is_gc_thread, lock); | ||
| 206 | } | ||
| 207 | |||
| 208 | if (!_runnable_vm_threads.empty()) | ||
| 209 | { | ||
| 210 | next_thread = _runnable_vm_threads.front(); | ||
| 211 | _runnable_vm_threads.pop(); | ||
| 212 | ++_running_vm_thread_count; | ||
| 213 | ++_gc_counter; | ||
| 214 | |||
| 215 | // This system thread now takes ownership of the vm thread | ||
| 216 | next_thread->system_thread_ownership.lock(); | ||
| 217 | return next_thread; | ||
| 218 | } | ||
| 219 | |||
| 220 | if (!_all_vm_threads.empty() && _blocked_vm_thread_ids.size() == _all_vm_threads.size()) | ||
| 221 | { | ||
| 222 | debug::log_msg(debug::component_trace_t::scheduler, debug::log_level_t::warning, "scheduler: deadlock detected\n"); | ||
| 223 | assert(false && "VM thread deadlock detected"); | ||
| 224 | } | ||
| 225 | } | 202 | } |
| 226 | 203 | ||
| 227 | // If the queue is empty, wait for a notification | 204 | if (!_runnable_vm_threads.empty()) |
| 228 | { | 205 | { |
| 229 | std::unique_lock<std::mutex> worker_event_lock{ _worker_event_lock }; | 206 | next_thread = _runnable_vm_threads.front(); |
| 230 | if (_terminating) | 207 | _runnable_vm_threads.pop(); |
| 231 | return{}; | 208 | ++_running_vm_thread_count; |
| 209 | ++_gc_counter; | ||
| 232 | 210 | ||
| 233 | debug::log_msg(debug::component_trace_t::scheduler, debug::log_level_t::debug, "scheduler: worker: waiting\n"); | 211 | // This system thread now takes ownership of the vm thread |
| 234 | _worker_event.wait(worker_event_lock); | 212 | next_thread->system_thread_ownership.lock(); |
| 213 | return next_thread; | ||
| 214 | } | ||
| 235 | 215 | ||
| 236 | if (_terminating) | 216 | if (!_all_vm_threads.empty() && _blocked_vm_thread_ids.size() == _all_vm_threads.size()) |
| 237 | return{}; | 217 | { |
| 218 | debug::log_msg(debug::component_trace_t::scheduler, debug::log_level_t::warning, "scheduler: deadlock detected\n"); | ||
| 219 | assert(false && "VM thread deadlock detected"); | ||
| 238 | } | 220 | } |
| 221 | |||
| 222 | // If the queue is empty, wait for a notification | ||
| 223 | if (_terminating) | ||
| 224 | return{}; | ||
| 225 | |||
| 226 | debug::log_msg(debug::component_trace_t::scheduler, debug::log_level_t::debug, "scheduler: worker: waiting\n"); | ||
| 227 | _worker_event.wait(lock); | ||
| 228 | |||
| 229 | if (_terminating) | ||
| 230 | return{}; | ||
| 239 | } | 231 | } |
| 240 | } | 232 | } |
| 241 | 233 | ||
| @@ -76,7 +76,6 @@ namespace disvm · 75 unmodified lines | |||
| 76 | const uint32_t _worker_thread_count; | 76 | const uint32_t _worker_thread_count; |
| 77 | std::vector<std::thread> _worker_pool; | 77 | std::vector<std::thread> _worker_pool; |
| 78 | 78 | ||
| 79 | std::mutex _worker_event_lock; | ||
| 80 | std::condition_variable _worker_event; | 79 | std::condition_variable _worker_event; |
| 81 | std::atomic_bool _terminating; | 80 | std::atomic_bool _terminating; |
| 82 | 81 | ||
| @@ -22,7 +22,7 @@ std::shared_ptr<const type_descriptor_t> vm_string_t::type_desc() · 21 unmodified lines | |||
| 22 | namespace | 22 | namespace |
| 23 | { | 23 | { |
| 24 | // Enumeration for character type compare cases. | 24 | // Enumeration for character type compare cases. |
| 25 | enum class compare_case | 25 | enum class compare_case_t |
| 26 | { | 26 | { |
| 27 | ascii_ascii, | 27 | ascii_ascii, |
| 28 | ascii_rune, | 28 | ascii_rune, |
| @@ -31,10 +31,10 @@ namespace · 2 unmodified lines | |||
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | // Scenario matrix for compare cases | 33 | // Scenario matrix for compare cases |
| 34 | const compare_case scenario_matrix[][2] = | 34 | const compare_case_t scenario_matrix[][2] = |
| 35 | { | 35 | { |
| 36 | { compare_case::ascii_ascii, compare_case::ascii_rune }, | 36 | { compare_case_t::ascii_ascii, compare_case_t::ascii_rune }, |
| 37 | { compare_case::rune_ascii, compare_case::rune_rune }, | 37 | { compare_case_t::rune_ascii, compare_case_t::rune_rune }, |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | // Case indices | 40 | // Case indices |
| @@ -92,14 +92,14 @@ int vm_string_t::compare(const vm_string_t *s1, const vm_string_t *s2) · 51 unmodified lines | |||
| 92 | { | 92 | { |
| 93 | default: | 93 | default: |
| 94 | assert(false && "Unknown compare scenario"); | 94 | assert(false && "Unknown compare scenario"); |
| 95 | case compare_case::ascii_ascii: | 95 | case compare_case_t::ascii_ascii: |
| 96 | case compare_case::rune_rune: | 96 | case compare_case_t::rune_rune: |
| 97 | result = std::memcmp(str_mem_1, str_mem_2, min_length); | 97 | result = std::memcmp(str_mem_1, str_mem_2, min_length); |
| 98 | break; | 98 | break; |
| 99 | case compare_case::ascii_rune: | 99 | case compare_case_t::ascii_rune: |
| 100 | result = _compare(min_length, reinterpret_cast<char *>(str_mem_1), reinterpret_cast<rune_t *>(str_mem_2)); | 100 | result = _compare(min_length, reinterpret_cast<char *>(str_mem_1), reinterpret_cast<rune_t *>(str_mem_2)); |
| 101 | break; | 101 | break; |
| 102 | case compare_case::rune_ascii: | 102 | case compare_case_t::rune_ascii: |
| 103 | result = _compare(min_length, reinterpret_cast<rune_t *>(str_mem_1), reinterpret_cast<char *>(str_mem_2)); | 103 | result = _compare(min_length, reinterpret_cast<rune_t *>(str_mem_1), reinterpret_cast<char *>(str_mem_2)); |
| 104 | break; | 104 | break; |
| 105 | } | 105 | } |
| @@ -281,7 +281,7 @@ vm_string_t::vm_string_t(const vm_string_t &other, word_t begin_index, word_t en · 175 unmodified lines | |||
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | // Copy over string content starting at the correct index (char vs. rune_t) | 283 | // Copy over string content starting at the correct index (char vs. rune_t) |
| 284 | ::memcpy(dest, (src + (begin_index * _character_size)), length_in_bytes); | 284 | std::memcpy(dest, (src + (begin_index * _character_size)), length_in_bytes); |
| 285 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "copy: vm string: %#" PRIxPTR " %d %d %#" PRIxPTR "\n", &other, begin_index, end_index, this); | 285 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "copy: vm string: %#" PRIxPTR " %d %d %#" PRIxPTR "\n", &other, begin_index, end_index, this); |
| 286 | } | 286 | } |
| 287 | 287 | ||
| @@ -5,6 +5,7 @@ · 4 unmodified lines | |||
| 5 | // | 5 | // |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <algorithm> | ||
| 8 | #include <chrono> | 9 | #include <chrono> |
| 9 | #include <thread> | 10 | #include <thread> |
| 10 | #include <iostream> | 11 | #include <iostream> |
| @@ -13,6 +14,7 @@ · 2 unmodified lines | |||
| 13 | #include <exceptions.h> | 14 | #include <exceptions.h> |
| 14 | #include <debug.h> | 15 | #include <debug.h> |
| 15 | #include <utf8.h> | 16 | #include <utf8.h> |
| 17 | #include <vm_memory.h> | ||
| 16 | #include "Sysmod.h" | 18 | #include "Sysmod.h" |
| 17 | #include "sys_utils.h" | 19 | #include "sys_utils.h" |
| 18 | 20 | ||
| @@ -69,99 +71,153 @@ namespace · 50 unmodified lines | |||
| 69 | }; | 71 | }; |
| 70 | 72 | ||
| 71 | const word_t Sysmodlen = 43; | 73 | const word_t Sysmodlen = 43; |
| 72 | } | ||
| 73 | 74 | ||
| 74 | std::shared_ptr<const type_descriptor_t> T_Qid; | 75 | std::shared_ptr<const type_descriptor_t> T_Qid; |
| 75 | std::shared_ptr<const type_descriptor_t> T_Dir; | 76 | std::shared_ptr<const type_descriptor_t> T_Dir; |
| 76 | std::shared_ptr<const type_descriptor_t> T_FD; | 77 | std::shared_ptr<const type_descriptor_t> T_FD; |
| 77 | std::shared_ptr<const type_descriptor_t> T_Connection; | 78 | std::shared_ptr<const type_descriptor_t> T_Connection; |
| 78 | std::shared_ptr<const type_descriptor_t> T_FileIO; | 79 | std::shared_ptr<const type_descriptor_t> T_FileIO; |
| 79 | 80 | ||
| 80 | struct vm_fd_t final : public Sys_FD | 81 | class vm_fd_t final : public Sys_FD |
| 81 | { | ||
| 82 | static void finalizer(vm_alloc_t *fileHandle) | ||
| 83 | { | 82 | { |
| 84 | auto fd = fileHandle->get_allocation<vm_fd_t>(); | 83 | public: // static |
| 85 | fd->~vm_fd_t(); | 84 | static void finalizer(vm_alloc_t *fileHandle) |
| 86 | } | 85 | { |
| 86 | auto fd = fileHandle->get_allocation<vm_fd_t>(); | ||
| 87 | |||
| 88 | if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>()) | ||
| 89 | { | ||
| 90 | debug::log_msg( | ||
| 91 | debug::component_trace_t::builtin, | ||
| 92 | debug::log_level_t::debug, | ||
| 93 | "sys: fd: finalize: %d\n", | ||
| 94 | fd->fd); | ||
| 95 | } | ||
| 96 | |||
| 97 | sys::drop_fd_record(fd->fd); | ||
| 98 | fd->~vm_fd_t(); | ||
| 99 | } | ||
| 87 | 100 | ||
| 88 | vm_fd_t(word_t fd, std::unique_ptr<std::iostream> s) | 101 | static std::tuple<word_t, vm_alloc_t *> create(std::unique_ptr<std::iostream> s, word_t fd_mode, vm_string_t *fd_path) |
| 89 | : Sys_FD{ fd } | 102 | { |
| 90 | , _istream{ s.get() } | 103 | assert(s != nullptr); |
| 91 | , _ostream{ s.get() } | 104 | auto alloc = vm_alloc_t::allocate(T_FD); |
| 92 | { | 105 | const auto fd = sys::create_fd_record(alloc); |
| 93 | assert(s != nullptr); | 106 | auto vm_fd = ::new(alloc->get_allocation())vm_fd_t{ fd, std::move(s), fd_mode, fd_path }; |
| 94 | _stream = std::move(s); | ||
| 95 | } | ||
| 96 | 107 | ||
| 97 | vm_fd_t(word_t fd, std::unique_ptr<std::istream> s) | 108 | return std::make_tuple(vm_fd->fd, alloc); |
| 98 | : Sys_FD{ fd } | 109 | } |
| 99 | , _istream{ s.get() } | ||
| 100 | , _ostream{ nullptr } | ||
| 101 | { | ||
| 102 | assert(s != nullptr); | ||
| 103 | _stream = std::move(s); | ||
| 104 | } | ||
| 105 | 110 | ||
| 106 | vm_fd_t(word_t fd, std::unique_ptr<std::ostream> s) | 111 | private: |
| 107 | : Sys_FD{ fd } | 112 | vm_fd_t(const word_t fd, std::unique_ptr<std::iostream> s, const word_t fd_mode, vm_string_t *fd_path) |
| 108 | , _istream{ nullptr } | 113 | : Sys_FD{ fd } |
| 109 | , _ostream{ s.get() } | 114 | , _fd_mode{ fd_mode } |
| 110 | { | 115 | , _fd_path{ fd_path } |
| 111 | assert(s != nullptr); | 116 | , _istream{ s.get() } |
| 112 | _stream = std::move(s); | 117 | , _ostream{ s.get() } |
| 113 | } | 118 | { |
| 119 | _stream = std::move(s); | ||
| 114 | 120 | ||
| 115 | vm_fd_t(word_t fd, std::istream &s) | 121 | if (_fd_path != nullptr) |
| 116 | : Sys_FD{ fd } | 122 | _fd_path->add_ref(); |
| 117 | , _istream{ &s } | 123 | } |
| 118 | , _ostream{ nullptr } | ||
| 119 | { | ||
| 120 | } | ||
| 121 | 124 | ||
| 122 | vm_fd_t(word_t fd, std::ostream &s) | 125 | public: |
| 123 | : Sys_FD{ fd } | 126 | vm_fd_t(const word_t fd, std::istream &is) |
| 124 | , _istream{ nullptr } | 127 | : Sys_FD{ fd } |
| 125 | , _ostream{ &s } | 128 | , _fd_mode{} |
| 126 | { | 129 | , _fd_path{ nullptr } |
| 127 | } | 130 | , _istream{ &is } |
| 131 | , _ostream{ nullptr } | ||
| 132 | { | ||
| 133 | } | ||
| 128 | 134 | ||
| 129 | ~vm_fd_t() | 135 | vm_fd_t(const word_t fd, std::ostream &os) |
| 130 | { | 136 | : Sys_FD{ fd } |
| 131 | debug::assign_debug_pointer(&_istream); | 137 | , _fd_mode{} |
| 132 | debug::assign_debug_pointer(&_ostream); | 138 | , _fd_path{ nullptr } |
| 139 | , _istream{ nullptr } | ||
| 140 | , _ostream{ &os } | ||
| 141 | { | ||
| 142 | } | ||
| 133 | 143 | ||
| 134 | if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>()) | 144 | ~vm_fd_t() |
| 135 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: file descriptor: %d", fd); | 145 | { |
| 136 | } | 146 | debug::assign_debug_pointer(&_istream); |
| 147 | debug::assign_debug_pointer(&_ostream); | ||
| 148 | if (_stream != nullptr) | ||
| 149 | _stream.reset(); | ||
| 137 | 150 | ||
| 138 | std::istream& input() const | 151 | // Honor the close flag |
| 139 | { | 152 | if ((_fd_mode & Sys_ORCLOSE) && _fd_path != nullptr) |
| 140 | if (_istream == nullptr) | 153 | std::remove(_fd_path->str()); |
| 141 | throw vm_user_exception{ "File descriptor not open for write" }; | ||
| 142 | 154 | ||
| 143 | return *_istream; | 155 | dec_ref_count_and_free(_fd_path); |
| 144 | } | 156 | debug::assign_debug_pointer(&_fd_path); |
| 157 | } | ||
| 158 | |||
| 159 | word_t get_original_mode() const | ||
| 160 | { | ||
| 161 | return _fd_mode; | ||
| 162 | } | ||
| 163 | |||
| 164 | vm_string_t *get_original_path() const | ||
| 165 | { | ||
| 166 | return _fd_path; | ||
| 167 | } | ||
| 168 | |||
| 169 | word_t read(const word_t buffer_size_in_bytes, void *buffer) | ||
| 170 | { | ||
| 171 | if (_istream == nullptr) | ||
| 172 | throw vm_system_exception{ "File descriptor not open for read operation" }; | ||
| 173 | |||
| 174 | _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes)); | ||
| 175 | if (_istream->bad()) | ||
| 176 | throw vm_user_exception{ "Read operation error" }; | ||
| 145 | 177 | ||
| 146 | std::ostream& output() const | 178 | return static_cast<word_t>(_istream->gcount()); |
| 179 | } | ||
| 180 | |||
| 181 | void write(const word_t buffer_size_in_bytes, void *buffer) | ||
| 182 | { | ||
| 183 | if (_ostream == nullptr) | ||
| 184 | throw vm_system_exception{ "File descriptor not open for write operation" }; | ||
| 185 | |||
| 186 | _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes)); | ||
| 187 | if (_ostream->bad()) | ||
| 188 | throw vm_user_exception{ "Write operation error" }; | ||
| 189 | } | ||
| 190 | |||
| 191 | private: | ||
| 192 | const word_t _fd_mode; | ||
| 193 | vm_string_t *_fd_path; | ||
| 194 | std::istream *_istream; | ||
| 195 | std::ostream *_ostream; | ||
| 196 | std::unique_ptr<std::ios> _stream; | ||
| 197 | }; | ||
| 198 | |||
| 199 | vm_fd_t *fd_stdin = nullptr; | ||
| 200 | vm_fd_t *fd_stdout = nullptr; | ||
| 201 | vm_fd_t *fd_stderr = nullptr; | ||
| 202 | |||
| 203 | std::ios::openmode convert_to_openmode(const word_t mode) | ||
| 147 | { | 204 | { |
| 148 | if (_ostream == nullptr) | 205 | auto om = std::ios::openmode{}; |
| 149 | throw vm_user_exception{ "File descriptor not open for read" }; | ||
| 150 | 206 | ||
| 151 | return *_ostream; | 207 | // Lowest bit is for read/write. |
| 152 | } | 208 | if (mode & Sys_OWRITE) |
| 209 | om |= std::ios::out; | ||
| 210 | else | ||
| 211 | om |= std::ios::in; // Sys_OREAD | ||
| 153 | 212 | ||
| 154 | private: | 213 | if (mode & Sys_ORDWR) |
| 155 | std::istream *_istream; | 214 | om |= (std::ios::in | std::ios::out); |
| 156 | std::ostream *_ostream; | ||
| 157 | std::unique_ptr<std::ios> _stream; | ||
| 158 | }; | ||
| 159 | 215 | ||
| 160 | namespace | 216 | if (mode & Sys_OTRUNC) |
| 161 | { | 217 | om |= std::ios::trunc; |
| 162 | vm_alloc_t *vm_stdin = nullptr; | 218 | |
| 163 | vm_alloc_t *vm_stdout = nullptr; | 219 | return om; |
| 164 | vm_alloc_t *vm_stderr = nullptr; | 220 | } |
| 165 | } | 221 | } |
| 166 | 222 | ||
| 167 | void | 223 | void |
| @@ -176,14 +232,26 @@ Sysmodinit(void) · 8 unmodified lines | |||
| 176 | 232 | ||
| 177 | // Initialize default file descriptors | 233 | // Initialize default file descriptors |
| 178 | 234 | ||
| 179 | vm_stdin = vm_alloc_t::allocate(T_FD); | 235 | { |
| 180 | ::new(vm_stdin->get_allocation()) vm_fd_t{ 0, std::cin }; | 236 | auto vm_stdin = vm_alloc_t::allocate(T_FD); |
| 237 | const auto fd = sys::create_fd_record(vm_stdin); | ||
| 238 | fd_stdin = ::new(vm_stdin->get_allocation())vm_fd_t{ fd, std::cin }; | ||
| 239 | debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stdin: %d\n", fd); | ||
| 240 | } | ||
| 181 | 241 | ||
| 182 | vm_stdout = vm_alloc_t::allocate(T_FD); | 242 | { |
| 183 | ::new(vm_stdout->get_allocation()) vm_fd_t{ 1, std::cout }; | 243 | auto vm_stdout = vm_alloc_t::allocate(T_FD); |
| 244 | const auto fd = sys::create_fd_record(vm_stdout); | ||
| 245 | fd_stdout = ::new(vm_stdout->get_allocation())vm_fd_t{ fd, std::cout }; | ||
| 246 | debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stdout: %d\n", fd); | ||
| 247 | } | ||
| 184 | 248 | ||
| 185 | vm_stderr = vm_alloc_t::allocate(T_FD); | 249 | { |
| 186 | ::new(vm_stderr->get_allocation()) vm_fd_t{ 2, std::cerr }; | 250 | auto vm_stderr = vm_alloc_t::allocate(T_FD); |
| 251 | const auto fd = sys::create_fd_record(vm_stderr); | ||
| 252 | fd_stderr = ::new(vm_stderr->get_allocation())vm_fd_t{ fd, std::cerr }; | ||
| 253 | debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stderr: %d\n", fd); | ||
| 254 | } | ||
| 187 | } | 255 | } |
| 188 | 256 | ||
| 189 | void | 257 | void |
| @@ -260,19 +328,19 @@ Sys_char2byte(vm_registers_t &r, vm_t &vm) · 70 unmodified lines | |||
| 260 | 328 | ||
| 261 | const auto rune = static_cast<rune_t>(fp.c); | 329 | const auto rune = static_cast<rune_t>(fp.c); |
| 262 | 330 | ||
| 263 | static_assert(Sys_UTFmax == sizeof(rune_t), "UTF max should be same size as rune"); | 331 | static_assert(Sys_UTFmax == sizeof(rune), "UTF max should be same size as rune"); |
| 264 | uint8_t buffer_local[Sys_UTFmax]; | 332 | uint8_t buffer_local[Sys_UTFmax]; |
| 265 | auto begin = buffer_local; | 333 | auto begin = buffer_local; |
| 266 | 334 | ||
| 267 | auto end = utf8::encode(rune, begin); | 335 | auto end = utf8::encode(rune, begin); |
| 268 | const auto count = static_cast<word_t>(end - begin); | 336 | const auto count = static_cast<word_t>(end - begin); |
| 269 | 337 | ||
| 270 | // Initialize for failure. | ||
| 271 | *fp.ret = 0; | ||
| 272 | |||
| 273 | const auto final_index = n + count; | 338 | const auto final_index = n + count; |
| 274 | if (final_index > buffer->get_length()) | 339 | if (final_index > buffer->get_length()) |
| 340 | { | ||
| 341 | *fp.ret = 0; | ||
| 275 | return; | 342 | return; |
| 343 | } | ||
| 276 | 344 | ||
| 277 | // Insert the bytes into the buffer | 345 | // Insert the bytes into the buffer |
| 278 | for (auto i = word_t{ n }; i < final_index; ++i) | 346 | for (auto i = word_t{ n }; i < final_index; ++i) |
| @@ -295,7 +363,41 @@ void · 16 unmodified lines | |||
| 295 | Sys_create(vm_registers_t &r, vm_t &vm) | 363 | Sys_create(vm_registers_t &r, vm_t &vm) |
| 296 | { | 364 | { |
| 297 | auto &fp = r.stack.peek_frame()->base<F_Sys_create>(); | 365 | auto &fp = r.stack.peek_frame()->base<F_Sys_create>(); |
| 298 | throw vm_system_exception{ "Instruction not implemented" }; | 366 | |
| 367 | auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s); | ||
| 368 | const auto open_mode = convert_to_openmode(fp.mode); | ||
| 369 | |||
| 370 | auto cfs_flag = sys::cfs_flags_t::none; | ||
| 371 | if (fp.mode & Sys_OEXCL) | ||
| 372 | cfs_flag = (sys::cfs_flags_t::atomic | sys::cfs_flags_t::ensure_create); | ||
| 373 | |||
| 374 | // [TODO] Apply the requested permissions to the file. | ||
| 375 | auto file_stream = sys::create_file_stream(path->str(), (open_mode | std::ios::binary), cfs_flag); | ||
| 376 | |||
| 377 | // Return null if file stream is not open. | ||
| 378 | if (file_stream == nullptr) | ||
| 379 | { | ||
| 380 | *fp.ret = nullptr; | ||
| 381 | return; | ||
| 382 | } | ||
| 383 | |||
| 384 | vm_alloc_t *fd_alloc; | ||
| 385 | auto fd = word_t{}; | ||
| 386 | std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path); | ||
| 387 | |||
| 388 | if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>()) | ||
| 389 | { | ||
| 390 | debug::log_msg( | ||
| 391 | debug::component_trace_t::builtin, | ||
| 392 | debug::log_level_t::debug, | ||
| 393 | "sys: create: %d >>%s<< %#x %#x\n", | ||
| 394 | fd, | ||
| 395 | path->str(), | ||
| 396 | fp.mode, | ||
| 397 | fp.perm); | ||
| 398 | } | ||
| 399 | |||
| 400 | *fp.ret = fd_alloc->get_allocation(); | ||
| 299 | } | 401 | } |
| 300 | 402 | ||
| 301 | void | 403 | void |
| @@ -417,7 +519,36 @@ void · 115 unmodified lines | |||
| 417 | Sys_open(vm_registers_t &r, vm_t &vm) | 519 | Sys_open(vm_registers_t &r, vm_t &vm) |
| 418 | { | 520 | { |
| 419 | auto &fp = r.stack.peek_frame()->base<F_Sys_open>(); | 521 | auto &fp = r.stack.peek_frame()->base<F_Sys_open>(); |
| 420 | throw vm_system_exception{ "Instruction not implemented" }; | 522 | |
| 523 | auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s); | ||
| 524 | const auto open_mode = convert_to_openmode(fp.mode); | ||
| 525 | |||
| 526 | // [TODO] Handle opening devices - not just files (i.e. /dev/random, /dev/source, /proc/1234) | ||
| 527 | auto file_stream = sys::create_file_stream(path->str(), (open_mode | std::ios::binary)); | ||
| 528 | |||
| 529 | // Return null if file stream is not open. | ||
| 530 | if (file_stream == nullptr) | ||
| 531 | { | ||
| 532 | *fp.ret = nullptr; | ||
| 533 | return; | ||
| 534 | } | ||
| 535 | |||
| 536 | vm_alloc_t *fd_alloc; | ||
| 537 | auto fd = word_t{}; | ||
| 538 | std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path); | ||
| 539 | |||
| 540 | if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>()) | ||
| 541 | { | ||
| 542 | debug::log_msg( | ||
| 543 | debug::component_trace_t::builtin, | ||
| 544 | debug::log_level_t::debug, | ||
| 545 | "sys: open: %d >>%s<< %#x\n", | ||
| 546 | fd, | ||
| 547 | path->str(), | ||
| 548 | fp.mode); | ||
| 549 | } | ||
| 550 | |||
| 551 | *fp.ret = fd_alloc->get_allocation(); | ||
| 421 | } | 552 | } |
| 422 | 553 | ||
| 423 | void | 554 | void |
| @@ -453,16 +584,16 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 29 unmodified lines | |||
| 453 | 584 | ||
| 454 | auto static_buffer = std::array<char, 1024>{}; | 585 | auto static_buffer = std::array<char, 1024>{}; |
| 455 | auto msg_args = &fp.vargs; | 586 | auto msg_args = &fp.vargs; |
| 456 | auto written = printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data()); | 587 | auto written = sys::printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data()); |
| 457 | if (written >= 0) | 588 | if (written >= 0) |
| 458 | { | 589 | { |
| 459 | vm_stdout->get_allocation<vm_fd_t>()->output() << static_buffer.data(); | 590 | fd_stdout->write(written, static_buffer.data()); |
| 460 | } | 591 | } |
| 461 | else | 592 | else |
| 462 | { | 593 | { |
| 463 | auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2); | 594 | auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2); |
| 464 | written = printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer); | 595 | written = sys::printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer); |
| 465 | vm_stdout->get_allocation<vm_fd_t>()->output() << static_buffer.data(); | 596 | fd_stdout->write(written, dynamic_buffer.data()); |
| 466 | } | 597 | } |
| 467 | 598 | ||
| 468 | *fp.ret = written; | 599 | *fp.ret = written; |
| @@ -479,7 +610,26 @@ void · 10 unmodified lines | |||
| 479 | Sys_read(vm_registers_t &r, vm_t &vm) | 610 | Sys_read(vm_registers_t &r, vm_t &vm) |
| 480 | { | 611 | { |
| 481 | auto &fp = r.stack.peek_frame()->base<F_Sys_read>(); | 612 | auto &fp = r.stack.peek_frame()->base<F_Sys_read>(); |
| 482 | throw vm_system_exception{ "Instruction not implemented" }; | 613 | |
| 614 | const auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.buf); | ||
| 615 | assert(buffer->get_element_type()->size_in_bytes == intrinsic_type_desc::type<byte_t>()->size_in_bytes); | ||
| 616 | |||
| 617 | auto n = fp.n; | ||
| 618 | if (buffer == nullptr || n <= 0) | ||
| 619 | { | ||
| 620 | *fp.ret = 0; | ||
| 621 | return; | ||
| 622 | } | ||
| 623 | |||
| 624 | // [SPEC] Supplying a size greater than the buffer length is | ||
| 625 | // equivalent to indicating the entire buffer should be filled. | ||
| 626 | n = std::min(n, buffer->get_length()); | ||
| 627 | |||
| 628 | auto fd_alloc = vm_alloc_t::from_allocation(fp.fd); | ||
| 629 | assert(fd_alloc->alloc_type == T_FD); | ||
| 630 | |||
| 631 | auto fd = fd_alloc->get_allocation<vm_fd_t>(); | ||
| 632 | *fp.ret = fd->read(n, buffer->at(0)); | ||
| 483 | } | 633 | } |
| 484 | 634 | ||
| 485 | void | 635 | void |
| @@ -511,9 +661,7 @@ Sys_sleep(vm_registers_t &r, vm_t &vm) · 25 unmodified lines | |||
| 511 | const auto period_ms = fp.period; | 661 | const auto period_ms = fp.period; |
| 512 | 662 | ||
| 513 | // [TODO] Thread should yield to another according to the sleep() specification. | 663 | // [TODO] Thread should yield to another according to the sleep() specification. |
| 514 | r.current_thread_state = vm_thread_state_t::release; | ||
| 515 | std::this_thread::sleep_for(std::chrono::milliseconds(period_ms)); | 664 | std::this_thread::sleep_for(std::chrono::milliseconds(period_ms)); |
| 516 | r.current_thread_state = vm_thread_state_t::ready; | ||
| 517 | 665 | ||
| 518 | *fp.ret = 0; | 666 | *fp.ret = 0; |
| 519 | } | 667 | } |
| @@ -539,11 +687,120 @@ Sys_stream(vm_registers_t &r, vm_t &vm) · 19 unmodified lines | |||
| 539 | throw vm_system_exception{ "Instruction not implemented" }; | 687 | throw vm_system_exception{ "Instruction not implemented" }; |
| 540 | } | 688 | } |
| 541 | 689 | ||
| 690 | namespace | ||
| 691 | { | ||
| 692 | bool is_delim(const rune_t c, const vm_string_t &delim) | ||
| 693 | { | ||
| 694 | const auto max = delim.get_length(); | ||
| 695 | assert(max > 0); | ||
| 696 | |||
| 697 | // Optimization for 3 or less delimiters | ||
| 698 | switch (max) | ||
| 699 | { | ||
| 700 | default: | ||
| 701 | break; | ||
| 702 | case 1: | ||
| 703 | return (delim.get_rune(0) == c); | ||
| 704 | case 2: | ||
| 705 | return (delim.get_rune(0) == c) || (delim.get_rune(1) == c); | ||
| 706 | case 3: | ||
| 707 | return (delim.get_rune(0) == c) || (delim.get_rune(1) == c) || (delim.get_rune(2) == c); | ||
| 708 | } | ||
| 709 | |||
| 710 | for (auto i = word_t{ 0 }; i < max; ++i) | ||
| 711 | { | ||
| 712 | if (delim.get_rune(i) == c) | ||
| 713 | return true; | ||
| 714 | } | ||
| 715 | |||
| 716 | return false; | ||
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 542 | void | 720 | void |
| 543 | Sys_tokenize(vm_registers_t &r, vm_t &vm) | 721 | Sys_tokenize(vm_registers_t &r, vm_t &vm) |
| 544 | { | 722 | { |
| 545 | auto &fp = r.stack.peek_frame()->base<F_Sys_tokenize>(); | 723 | auto &fp = r.stack.peek_frame()->base<F_Sys_tokenize>(); |
| 546 | throw vm_system_exception{ "Instruction not implemented" }; | 724 | |
| 725 | // Initialize for failure. | ||
| 726 | fp.ret->t0 = 0; | ||
| 727 | dec_ref_count_and_free(vm_alloc_t::from_allocation(fp.ret->t1)); | ||
| 728 | fp.ret->t1 = nullptr; | ||
| 729 | |||
| 730 | auto s = vm_alloc_t::from_allocation<vm_string_t>(fp.s); | ||
| 731 | if (s == nullptr || s->get_length() == 0) | ||
| 732 | return; | ||
| 733 | |||
| 734 | auto curr_node = new vm_list_t{ intrinsic_type_desc::type<pointer_t>() }; | ||
| 735 | |||
| 736 | // Set the head of the list | ||
| 737 | fp.ret->t1 = curr_node->get_allocation(); | ||
| 738 | |||
| 739 | auto delim = vm_alloc_t::from_allocation<vm_string_t>(fp.delim); | ||
| 740 | if (delim == nullptr || delim->get_length() == 0) | ||
| 741 | { | ||
| 742 | // Add supplied string to list | ||
| 743 | pt_ref(curr_node->value()) = s->get_allocation(); | ||
| 744 | s->add_ref(); | ||
| 745 | |||
| 746 | fp.ret->t0 = 1; | ||
| 747 | return; | ||
| 748 | } | ||
| 749 | |||
| 750 | // Tokenize the string | ||
| 751 | vm_list_t *prev_node = nullptr; | ||
| 752 | auto n = word_t{ 0 }; | ||
| 753 | auto begin = word_t{ 0 }; | ||
| 754 | auto end = word_t{ 0 }; | ||
| 755 | const auto len = s->get_length(); | ||
| 756 | while (begin < len) | ||
| 757 | { | ||
| 758 | // Consume delimiters | ||
| 759 | while (begin < len) | ||
| 760 | { | ||
| 761 | const auto rune = s->get_rune(begin); | ||
| 762 | if (!is_delim(rune, *delim)) | ||
| 763 | break; | ||
| 764 | ++begin; | ||
| 765 | } | ||
| 766 | |||
| 767 | end = begin; | ||
| 768 | |||
| 769 | // Consume non-delimiters | ||
| 770 | while (end < len) | ||
| 771 | { | ||
| 772 | const auto rune = s->get_rune(end); | ||
| 773 | if (is_delim(rune, *delim)) | ||
| 774 | break; | ||
| 775 | ++end; | ||
| 776 | } | ||
| 777 | |||
| 778 | // No progress - It is and isn't a delimiter (i.e. no more characters). | ||
| 779 | if (begin == end) | ||
| 780 | break; | ||
| 781 | |||
| 782 | // Current node is initially non-null | ||
| 783 | if (curr_node == nullptr) | ||
| 784 | curr_node = new vm_list_t{ intrinsic_type_desc::type<pointer_t>() }; | ||
| 785 | |||
| 786 | const auto token = new vm_string_t{ *s, begin, end }; | ||
| 787 | pt_ref(curr_node->value()) = token->get_allocation(); | ||
| 788 | |||
| 789 | // Previous node is initially null | ||
| 790 | if (prev_node != nullptr) | ||
| 791 | { | ||
| 792 | prev_node->set_tail(curr_node); | ||
| 793 | curr_node->release(); | ||
| 794 | } | ||
| 795 | |||
| 796 | prev_node = curr_node; | ||
| 797 | curr_node = nullptr; | ||
| 798 | |||
| 799 | begin = end; | ||
| 800 | ++n; | ||
| 801 | } | ||
| 802 | |||
| 803 | fp.ret->t0 = n; | ||
| 547 | } | 804 | } |
| 548 | 805 | ||
| 549 | void | 806 | void |
| @@ -571,7 +828,28 @@ void · 21 unmodified lines | |||
| 571 | Sys_write(vm_registers_t &r, vm_t &vm) | 828 | Sys_write(vm_registers_t &r, vm_t &vm) |
| 572 | { | 829 | { |
| 573 | auto &fp = r.stack.peek_frame()->base<F_Sys_write>(); | 830 | auto &fp = r.stack.peek_frame()->base<F_Sys_write>(); |
| 574 | throw vm_system_exception{ "Instruction not implemented" }; | 831 | |
| 832 | const auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.buf); | ||
| 833 | assert(buffer->get_element_type()->size_in_bytes == intrinsic_type_desc::type<byte_t>()->size_in_bytes); | ||
| 834 | |||
| 835 | auto n = fp.n; | ||
| 836 | if (buffer == nullptr || n <= 0) | ||
| 837 | { | ||
| 838 | *fp.ret = 0; | ||
| 839 | return; | ||
| 840 | } | ||
| 841 | |||
| 842 | // [SPEC] Supplying a size greater than the buffer length is | ||
| 843 | // equivalent to indicating the entire buffer should be written. | ||
| 844 | n = std::min(n, buffer->get_length()); | ||
| 845 | |||
| 846 | auto fd_alloc = vm_alloc_t::from_allocation(fp.fd); | ||
| 847 | assert(fd_alloc->alloc_type == T_FD); | ||
| 848 | |||
| 849 | auto fd = fd_alloc->get_allocation<vm_fd_t>(); | ||
| 850 | fd->write(n, buffer->at(0)); | ||
| 851 | |||
| 852 | *fp.ret = n; | ||
| 575 | } | 853 | } |
| 576 | 854 | ||
| 577 | void | 855 | void |
| @@ -0,0 +1,84 @@ | |||
| 1 | // | ||
| 2 | // Dis VM | ||
| 3 | // File: fd_manager.cpp | ||
| 4 | // Author: arr | ||
| 5 | // | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <mutex> | ||
| 9 | #include <unordered_map> | ||
| 10 | #include <exceptions.h> | ||
| 11 | #include <vm_memory.h> | ||
| 12 | #include "sys_utils.h" | ||
| 13 | |||
| 14 | using namespace disvm::runtime; | ||
| 15 | |||
| 16 | namespace | ||
| 17 | { | ||
| 18 | using fd_map_t = std::unordered_map<word_t, vm_alloc_t *>; | ||
| 19 | |||
| 20 | auto std_fd = std::array<vm_alloc_t * , 3> | ||
| 21 | { | ||
| 22 | nullptr, // stdin | ||
| 23 | nullptr, // stdout | ||
| 24 | nullptr, // stderr | ||
| 25 | }; | ||
| 26 | |||
| 27 | auto next_fd = word_t{ 0 }; | ||
| 28 | |||
| 29 | std::mutex fd_map_lock; | ||
| 30 | auto fd_map = fd_map_t{}; | ||
| 31 | } | ||
| 32 | |||
| 33 | word_t disvm::runtime::sys::create_fd_record(vm_alloc_t * fd_alloc) | ||
| 34 | { | ||
| 35 | assert(fd_alloc != nullptr); | ||
| 36 | |||
| 37 | { | ||
| 38 | std::lock_guard<std::mutex> lock{ fd_map_lock }; | ||
| 39 | |||
| 40 | auto new_fd = next_fd; | ||
| 41 | next_fd++; | ||
| 42 | if (new_fd < static_cast<word_t>(std_fd.size())) | ||
| 43 | { | ||
| 44 | std_fd[new_fd] = fd_alloc; | ||
| 45 | } | ||
| 46 | else | ||
| 47 | { | ||
| 48 | assert(fd_map.cend() == fd_map.find(new_fd)); | ||
| 49 | fd_map[new_fd] = fd_alloc; | ||
| 50 | } | ||
| 51 | |||
| 52 | return new_fd; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | vm_alloc_t * disvm::runtime::sys::fetch_fd_record(word_t fd) | ||
| 57 | { | ||
| 58 | assert(fd != sys::vm_invalid_fd && "Invalid FD value"); | ||
| 59 | |||
| 60 | if (0 <= fd && fd < static_cast<word_t>(std_fd.size())) | ||
| 61 | return std_fd[fd]; | ||
| 62 | |||
| 63 | { | ||
| 64 | std::lock_guard<std::mutex> lock{ fd_map_lock }; | ||
| 65 | auto iter = fd_map.find(fd); | ||
| 66 | if (iter == fd_map.cend()) | ||
| 67 | throw vm_user_exception{ "Unknown file descriptor" }; | ||
| 68 | |||
| 69 | return iter->second; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | void disvm::runtime::sys::drop_fd_record(word_t fd) | ||
| 74 | { | ||
| 75 | assert(fd != sys::vm_invalid_fd && "Invalid FD value"); | ||
| 76 | assert(fd >= static_cast<word_t>(std_fd.size()) && "Request to drop std file?"); | ||
| 77 | |||
| 78 | { | ||
| 79 | std::lock_guard<std::mutex> lock{ fd_map_lock }; | ||
| 80 | auto iter = fd_map.find(fd); | ||
| 81 | if (iter != fd_map.cend()) | ||
| 82 | fd_map.erase(iter); | ||
| 83 | } | ||
| 84 | } | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | // | ||
| 2 | // Dis VM | ||
| 3 | // File: file_system.cpp | ||
| 4 | // Author: arr | ||
| 5 | // | ||
| 6 | |||
| 7 | #include <iostream> | ||
| 8 | #include <fstream> | ||
| 9 | #include <mutex> | ||
| 10 | #include <utils.h> | ||
| 11 | #include "sys_utils.h" | ||
| 12 | |||
| 13 | using namespace disvm::runtime; | ||
| 14 | |||
| 15 | namespace | ||
| 16 | { | ||
| 17 | std::mutex _create_file_stream_mutex; | ||
| 18 | } | ||
| 19 | |||
| 20 | std::unique_ptr<std::fstream> disvm::runtime::sys::create_file_stream(const char *path, std::ios::openmode mode, cfs_flags_t flags) | ||
| 21 | { | ||
| 22 | auto file = std::unique_ptr<std::fstream>{}; | ||
| 23 | if (flags == cfs_flags_t::none) | ||
| 24 | { | ||
| 25 | file = std::make_unique<std::fstream>(path, mode); | ||
| 26 | } | ||
| 27 | else | ||
| 28 | { | ||
| 29 | std::unique_lock<std::mutex> lock{ _create_file_stream_mutex, std::defer_lock }; | ||
| 30 | if (util::has_flag(flags, cfs_flags_t::atomic)) | ||
| 31 | lock.lock(); | ||
| 32 | |||
| 33 | if (util::has_flag(flags, cfs_flags_t::ensure_create)) | ||
| 34 | { | ||
| 35 | std::fstream file_exists{ path }; | ||
| 36 | if (file_exists) | ||
| 37 | return{}; | ||
| 38 | } | ||
| 39 | |||
| 40 | file = std::make_unique<std::fstream>(path, mode); | ||
| 41 | } | ||
| 42 | |||
| 43 | // Return null if file stream is not open. | ||
| 44 | if (!file->is_open()) | ||
| 45 | return{}; | ||
| 46 | |||
| 47 | return file; | ||
| 48 | } | ||
| @@ -16,7 +16,7 @@ namespace · 15 unmodified lines | |||
| 16 | { | 16 | { |
| 17 | // Align the supplied pointer for the template type relative to base. | 17 | // Align the supplied pointer for the template type relative to base. |
| 18 | template<typename T> | 18 | template<typename T> |
| 19 | inline byte_t *align_for(byte_t *p, pointer_t base) | 19 | byte_t *align_for(byte_t *p, pointer_t base) |
| 20 | { | 20 | { |
| 21 | for (;;) | 21 | for (;;) |
| 22 | { | 22 | { |
| @@ -31,7 +31,7 @@ namespace · 8 unmodified lines | |||
| 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 |
| 34 | word_t printf_to_buffer( | 34 | word_t disvm::runtime::sys::printf_to_buffer( |
| 35 | const disvm::runtime::vm_string_t &msg_fmt, | 35 | const disvm::runtime::vm_string_t &msg_fmt, |
| 36 | disvm::runtime::byte_t *msg_args, | 36 | disvm::runtime::byte_t *msg_args, |
| 37 | disvm::runtime::pointer_t base, | 37 | disvm::runtime::pointer_t base, |
| @@ -121,17 +121,19 @@ word_t printf_to_buffer( · 83 unmodified lines | |||
| 121 | break; | 121 | break; |
| 122 | case 's': // string | 122 | case 's': // string |
| 123 | { | 123 | { |
| 124 | auto s = vm_alloc_t::from_allocation<vm_string_t>(*reinterpret_cast<pointer_t *>(msg_args)); | 124 | auto p = *reinterpret_cast<pointer_t *>(msg_args); |
| 125 | if (s != nullptr) | 125 | auto str = vm_alloc_t::from_allocation<vm_string_t>(p); |
| 126 | wb = std::snprintf(b_curr, (b_end - b_curr), "%s", s->str()); | 126 | if (str != nullptr) |
| 127 | wb = std::snprintf(b_curr, (b_end - b_curr), "%s", str->str()); | ||
| 127 | 128 | ||
| 128 | msg_args += sizeof(pointer_t); | 129 | msg_args += sizeof(p); |
| 129 | break; | 130 | break; |
| 130 | } | 131 | } |
| 131 | // [SPEC] Undocumented format verb for debugging VM objects. | 132 | // [SPEC] Undocumented format verb for debugging VM objects. |
| 132 | case 'H': | 133 | case 'H': |
| 133 | { | 134 | { |
| 134 | auto alloc = vm_alloc_t::from_allocation(*reinterpret_cast<pointer_t *>(msg_args)); | 135 | auto p = *reinterpret_cast<pointer_t *>(msg_args); |
| 136 | auto alloc = vm_alloc_t::from_allocation(p); | ||
| 135 | auto rc = static_cast<std::size_t>(-1); | 137 | auto rc = static_cast<std::size_t>(-1); |
| 136 | const void *type_alloc = nullptr; | 138 | const void *type_alloc = nullptr; |
| 137 | if (alloc != nullptr) | 139 | if (alloc != nullptr) |
| @@ -140,8 +142,8 @@ word_t printf_to_buffer( · 2 unmodified lines | |||
| 140 | type_alloc = alloc->alloc_type.get(); | 142 | type_alloc = alloc->alloc_type.get(); |
| 141 | } | 143 | } |
| 142 | 144 | ||
| 143 | wb = std::snprintf(b_curr, (b_end - b_curr), "%" PRIdPTR ".%#08" PRIxPTR, rc, reinterpret_cast<std::size_t>(type_alloc)); | 145 | wb = std::snprintf(b_curr, (b_end - b_curr), "%" PRIuPTR ".%#08" PRIxPTR, rc, reinterpret_cast<std::size_t>(type_alloc)); |
| 144 | msg_args += sizeof(pointer_t); | 146 | msg_args += sizeof(p); |
| 145 | break; | 147 | break; |
| 146 | } | 148 | } |
| 147 | } | 149 | } |
| @@ -159,7 +161,7 @@ word_t printf_to_buffer( · 11 unmodified lines | |||
| 159 | return (b_curr - buffer); | 161 | return (b_curr - buffer); |
| 160 | } | 162 | } |
| 161 | 163 | ||
| 162 | word_t printf_to_dynamic_buffer( | 164 | word_t disvm::runtime::sys::printf_to_dynamic_buffer( |
| 163 | const disvm::runtime::vm_string_t &msg_fmt, | 165 | const disvm::runtime::vm_string_t &msg_fmt, |
| 164 | disvm::runtime::byte_t *msg_args, | 166 | disvm::runtime::byte_t *msg_args, |
| 165 | disvm::runtime::pointer_t base, | 167 | disvm::runtime::pointer_t base, |
| @@ -167,9 +169,7 @@ word_t printf_to_dynamic_buffer( · 1 unmodified line | |||
| 167 | { | 169 | { |
| 168 | auto result = word_t{ 0 }; | 170 | auto result = word_t{ 0 }; |
| 169 | while (-1 == (result = printf_to_buffer(msg_fmt, msg_args, base, buffer.size(), buffer.data()))) | 171 | while (-1 == (result = printf_to_buffer(msg_fmt, msg_args, base, buffer.size(), buffer.data()))) |
| 170 | { | ||
| 171 | buffer.resize(buffer.size() * 2); | 172 | buffer.resize(buffer.size() * 2); |
| 172 | } | ||
| 173 | 173 | ||
| 174 | return result; | 174 | return result; |
| 175 | } | 175 | } |
| @@ -4,27 +4,60 @@ · 3 unmodified lines | |||
| 4 | // Author: arr | 4 | // Author: arr |
| 5 | // | 5 | // |
| 6 | 6 | ||
| 7 | #ifndef _DISVM_SRC_VM_SYS_UTILS_H_ | 7 | #ifndef _DISVM_SRC_VM_SYS_SYS_UTILS_H_ |
| 8 | #define _DISVM_SRC_VM_SYS_UTILS_H_ | 8 | #define _DISVM_SRC_VM_SYS_SYS_UTILS_H_ |
| 9 | 9 | ||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | #include <iosfwd> | ||
| 12 | #include <memory> | ||
| 11 | #include <disvm.h> | 13 | #include <disvm.h> |
| 14 | #include <utils.h> | ||
| 12 | 15 | ||
| 13 | // Prints the format and arguments to the supplied buffer. | 16 | namespace disvm |
| 14 | // Returns the number of bytes written. | 17 | { |
| 15 | disvm::runtime::word_t printf_to_buffer( | 18 | namespace runtime |
| 16 | const disvm::runtime::vm_string_t &msg_fmt, | 19 | { |
| 17 | disvm::runtime::byte_t *msg_args, | 20 | namespace sys |
| 18 | disvm::runtime::pointer_t base, | 21 | { |
| 19 | const std::size_t buffer_size, | 22 | // Prints the format and arguments to the supplied buffer. |
| 20 | char *buffer); | 23 | // Returns the number of bytes written. |
| 21 | 24 | word_t printf_to_buffer( | |
| 22 | // Prints the format and arguments to the supplied dynamic buffer. | 25 | const vm_string_t &msg_fmt, |
| 23 | // Returns the number of bytes written. | 26 | byte_t *msg_args, |
| 24 | disvm::runtime::word_t printf_to_dynamic_buffer( | 27 | pointer_t base, |
| 25 | const disvm::runtime::vm_string_t &msg_fmt, | 28 | const std::size_t buffer_size, |
| 26 | disvm::runtime::byte_t *msg_args, | 29 | char *buffer); |
| 27 | disvm::runtime::pointer_t base, | 30 | |
| 28 | std::vector<char> &buffer); | 31 | // Prints the format and arguments to the supplied dynamic buffer. |
| 29 | 32 | // Returns the number of bytes written. | |
| 30 | #endif // _DISVM_SRC_VM_SYS_UTILS_H_ | 33 | word_t printf_to_dynamic_buffer( |
| 34 | const vm_string_t &msg_fmt, | ||
| 35 | byte_t *msg_args, | ||
| 36 | pointer_t base, | ||
| 37 | std::vector<char> &buffer); | ||
| 38 | |||
| 39 | enum class cfs_flags_t | ||
| 40 | { | ||
| 41 | none, | ||
| 42 | atomic, | ||
| 43 | ensure_create, | ||
| 44 | }; | ||
| 45 | |||
| 46 | DEFINE_ENUM_FLAG_OPERATORS(cfs_flags_t); | ||
| 47 | |||
| 48 | std::unique_ptr<std::fstream> create_file_stream(const char *path, std::ios::openmode mode, cfs_flags_t flags = sys::cfs_flags_t::none); | ||
| 49 | |||
| 50 | const word_t vm_invalid_fd = -1; | ||
| 51 | |||
| 52 | // The first three FD records are optimized for lock-less access. | ||
| 53 | word_t create_fd_record(vm_alloc_t *fd_alloc); | ||
| 54 | |||
| 55 | vm_alloc_t * fetch_fd_record(word_t fd); | ||
| 56 | |||
| 57 | void drop_fd_record(word_t fd); | ||
| 58 | |||
| 59 | } | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | #endif // _DISVM_SRC_VM_SYS_SYS_UTILS_H_ | ||
| @@ -43,12 +43,12 @@ std::tuple<const handler_t *, vm_frame_t *, vm_pc_t> disvm::runtime::find_except · 42 unmodified lines | |||
| 43 | if (exception_id.compare_to(exception_name) == 0) | 43 | if (exception_id.compare_to(exception_name) == 0) |
| 44 | { | 44 | { |
| 45 | debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "end: handler: match: %d\n", unwind_depth); | 45 | debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "end: handler: match: %d\n", unwind_depth); |
| 46 | return std::tuple<const handler_t *, vm_frame_t *, std::size_t>{ &handler, current_frame, exception.pc }; | 46 | return std::make_tuple(&handler, current_frame, exception.pc); |
| 47 | } | 47 | } |
| 48 | else if (exception_name == nullptr && exception.pc != runtime_constants::invalid_program_counter) | 48 | else if (exception_name == nullptr && exception.pc != runtime_constants::invalid_program_counter) |
| 49 | { | 49 | { |
| 50 | debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "end: handler: fallback: %d\n", unwind_depth); | 50 | debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "end: handler: fallback: %d\n", unwind_depth); |
| 51 | return std::tuple<const handler_t *, vm_frame_t *, std::size_t>{ &handler, current_frame, exception.pc }; | 51 | return std::make_tuple(&handler, current_frame, exception.pc); |
| 52 | } | 52 | } |
| 53 | } | 53 | } |
| 54 | } | 54 | } |
| @@ -344,7 +344,7 @@ namespace · 343 unmodified lines | |||
| 344 | } | 344 | } |
| 345 | } | 345 | } |
| 346 | 346 | ||
| 347 | std::atomic_bool intrinsic_type_desc::is_initialized; | 347 | std::atomic_bool intrinsic_type_desc::is_initialized{ false }; |
| 348 | 348 | ||
| 349 | void intrinsic_type_desc::initialize() | 349 | void intrinsic_type_desc::initialize() |
| 350 | { | 350 | { |