History

Basic implementation of Sys module functions - Sys.create, Sys.open, Sys.read, Sys.write - Sys.tokenize

Code analysis violations Bug in scheduler - race during with worker thread shutdown.

arr committed 10 years ago

Commit 992a33f1 parent 6d77c2d18 files · +695 −243Browse files

Unified · Split

18 files
  1. src/include/builtin_module.h +36 −0
  2. src/include/runtime.h +1 −1
  3. src/sh/main.cpp +8 −7
  4. src/vm/builtin_module.cpp +1 −1
  5. src/vm/disvm.gyp +2 −0
  6. src/vm/execution_table.cpp +6 −33
  7. src/vm/list.cpp +19 −13
  8. src/vm/module_reader.cpp +4 −4
  9. src/vm/scheduler.cpp +34 −42
  10. src/vm/scheduler.h +0 −1
  11. src/vm/string.cpp +9 −9
  12. src/vm/sys/Sysmod.cpp +375 −97
  13. src/vm/sys/fd_manager.cpp +84 −0
  14. src/vm/sys/file_system.cpp +48 −0
  15. src/vm/sys/print.cpp +12 −12
  16. src/vm/sys/sys_utils.h +53 −20
  17. src/vm/vm_exception_handler.cpp +2 −2
  18. src/vm/vm_memory.cpp +1 −1
src/include/builtin_module.h+36−0
@@ -60,6 +60,42 @@ namespace disvm · 59 unmodified lines
6060 static_assert(
6161 sizeof(vm_frame_base_alloc_t) == runtime_constants::default_register_count * runtime_constants::default_register_size_bytes,
6262 "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+ }
6399 }
64100 }
65101
src/include/runtime.h+1−1
@@ -249,7 +249,7 @@ namespace disvm · 248 unmodified lines
249249
250250 vm_list_t* get_tail() const;
251251
252− vm_list_t* drop_tail();
252+ void set_tail(vm_list_t *new_tail);
253253
254254 // Returns a pointer to the value
255255 pointer_t value() const;
src/sh/main.cpp+8−7
@@ -12,15 +12,16 @@ · 11 unmodified lines
1212
1313 int main(int argc, char* argv[])
1414 {
15− auto module = "C:\\Users\\linuxuser27\\source\\disvm\\basic_limbo_tests.dis";
15+ auto module = "basic_limbo_tests.dis";
1616 //auto module = "C:\\inferno\\dis\\limbo.dis";
17− //auto module = "C:\\Users\\linuxuser27\\source\\disvm\\empty.dis";
17+ //auto module = "empty.dis";
1818
1919 if (argc >= 2)
2020 module = argv[1];
2121
22− clock_t t = ::clock();
22+ clock_t t = std::clock();
2323
24+ //disvm::debug::set_component_tracing(disvm::debug::component_trace_t::builtin, true);
2425 //disvm::debug::set_component_tracing(disvm::debug::component_trace_t::memory, true);
2526 //disvm::debug::set_component_tracing(disvm::debug::component_trace_t::module, true);
2627 //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
3334 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); });
3435
3536 int c = 1;
36− //for (;;)
37+ for (;;)
3738 {
38− ::printf("####### VM test loop (%d) #######\n", c++);
39− disvm::vm_t v{ 1, 2048 };
39+ std::printf("####### VM test loop (%d) #######\n", c++);
40+ disvm::vm_t v{ 4, 2048 };
4041
41− for (auto i = 0; i < 1; ++i)
42+ for (auto i = 0; i < 10; ++i)
4243 v.exec(module);
4344
4445 v.spin_sleep_till_idle(std::chrono::milliseconds(10));
src/vm/builtin_module.cpp+1−1
@@ -19,7 +19,7 @@ using namespace disvm::runtime::builtin; · 18 unmodified lines
1919
2020 namespace
2121 {
22− std::atomic_bool builtin_modules_initialized;
22+ std::atomic_bool builtin_modules_initialized{ false };
2323
2424 std::mutex builtin_modules_lock;
2525 std::forward_list<std::shared_ptr<vm_module_t>> builtin_modules;
src/vm/disvm.gyp+2−0
@@ -23,6 +23,8 @@ · 22 unmodified lines
2323 'vm_exception_handler.cpp',
2424 'sys/print.cpp',
2525 'sys/Sysmod.cpp',
26+ 'sys/file_system.cpp',
27+ 'sys/fd_manager.cpp',
2628 ],
2729 'include_dirs': [
2830 '../include'
src/vm/execution_table.cpp+6−33
@@ -17,6 +17,7 @@ · 16 unmodified lines
1717 #include <utils.h>
1818 #include <exceptions.h>
1919 #include <debug.h>
20+#include <builtin_module.h>
2021 #include "execution_table.h"
2122
2223 using namespace disvm;
@@ -31,36 +32,6 @@ namespace · 8 unmodified lines
3132 using uword_t = uint32_t; // unsigned word_t
3233 using ubig_t = uint64_t; // unsigned big_t
3334
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−
6435 #define EXEC_DECL(inst_name) void inst_name(vm_registers_t &r, vm_t& vm)
6536
6637 EXEC_DECL(notimpl) { assert(false && "Instruction not implemented"); throw vm_system_exception{ "Instruction not implemented" }; }
@@ -805,7 +776,7 @@ namespace · 738 unmodified lines
805776 if (tail_maybe != nullptr)
806777 tail_maybe->release();
807778
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 };
809780
810781 auto value = vt_ref<PrimitiveType>(r.src);
811782 vt_ref<PrimitiveType>(new_list->value()) = value;
@@ -825,7 +796,8 @@ namespace · 13 unmodified lines
825796 tail_maybe->release();
826797
827798 // 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+
829801 auto alloc = at_val<vm_alloc_t>(r.src);
830802 if (alloc != nullptr)
831803 {
@@ -858,7 +830,8 @@ namespace · 26 unmodified lines
858830 tail_maybe->release();
859831
860832 // 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+
862835 auto destination = new_list->value();
863836 std::memmove(destination, r.src, type->size_in_bytes);
864837
src/vm/list.cpp+19−13
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 //
66
77 #include <runtime.h>
8+#include <exceptions.h>
89 #include <vm_memory.h>
910 #include <debug.h>
1011
@@ -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
2122 , _element_type{ td }
2223 , _is_alloc{ sizeof(_mem) < td->size_in_bytes }
2324 , _mem{}
24− , _tail{ tail }
25+ , _tail{ nullptr }
2526 {
2627 assert(_element_type != nullptr);
27− if (_tail != nullptr)
28− {
29− assert(tail->get_element_type() == _element_type);
30− _tail->add_ref();
31− }
28+ set_tail(tail);
3229
3330 // If the default list element storage is not enough, allocate more memory.
3431 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
4138 init_memory(*_element_type, &_mem.local);
4239 }
4340
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");
4543 }
4644
4745 vm_list_t::~vm_list_t()
4846 {
4947 // Remove tail
5048 dec_ref_count_and_free(_tail);
51− _tail = nullptr;
49+ debug::assign_debug_pointer(&_tail);
5250
5351 // Destroy and free the list element, if default element size was surpassed.
5452 if (_is_alloc)
@@ -88,12 +86,20 @@ vm_list_t *vm_list_t::get_tail() const · 33 unmodified lines
8886 return _tail;
8987 }
9088
91−vm_list_t *vm_list_t::drop_tail()
89+void vm_list_t::set_tail(vm_list_t *new_tail)
9290 {
93− auto tail = _tail;
94− _tail = nullptr;
95− tail->release();
96− return tail;
91+ if (new_tail != nullptr)
92+ {
93+ if (new_tail->get_element_type() != _element_type)
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);
97103 }
98104
99105 pointer_t vm_list_t::value() const
src/vm/module_reader.cpp+4−4
@@ -392,8 +392,8 @@ namespace · 391 unmodified lines
392392 auto base = reinterpret_cast<byte_t *>(modobj.original_mp->get_allocation());
393393 for (;;)
394394 {
395− auto bytesRead = reader.get_next_bytes(sizeof(code), &code);
396− if (bytesRead != sizeof(code)) throw module_reader_exception{ "Failed to read data item code" };
395+ auto bytes_read = reader.get_next_bytes(sizeof(code), &code);
396+ if (bytes_read != sizeof(code)) throw module_reader_exception{ "Failed to read data item code" };
397397 if (code == 0)
398398 break;
399399
@@ -444,8 +444,8 @@ namespace · 44 unmodified lines
444444 case datum_type_t::utf_string:
445445 {
446446 auto str_buffer = std::vector<uint8_t>(count);
447− auto bytesRead = reader.get_next_bytes(count, str_buffer.data());
448− if (bytesRead != count)
447+ auto str_bytes = reader.get_next_bytes(count, str_buffer.data());
448+ if (str_bytes != count)
449449 throw module_reader_exception{ "Failed to read string data" };
450450
451451 auto new_string = new vm_string_t(str_buffer.size(), str_buffer.data());
src/vm/scheduler.cpp+34−42
@@ -47,6 +47,7 @@ default_scheduler_t::default_scheduler_t(vm_t &vm, uint32_t system_thread_count, · 46 unmodified lines
4747 : _gc_complete{ true }
4848 , _gc_counter{ 1 }
4949 , _running_vm_thread_count{ 0 }
50+ , _terminating{ false }
5051 , _worker_thread_count{ system_thread_count }
5152 , _vm{ vm }
5253 , _vm_thread_quanta{ thread_quanta }
@@ -58,18 +59,14 @@ default_scheduler_t::~default_scheduler_t() · 5 unmodified lines
5859 // Drain the runnable queue and clear out all the threads
5960 {
6061 std::lock_guard<std::mutex> lock{ _vm_threads_lock };
62+ _terminating = true;
63+
6164 while (!_runnable_vm_threads.empty())
6265 _runnable_vm_threads.pop();
6366
6467 _all_vm_threads.clear();
6568 }
6669
67− // Set terminating flag
68− {
69− std::unique_lock<std::mutex> worker_event_lock{ _worker_event_lock };
70− _terminating = true;
71− }
72−
7370 // Notify all workers
7471 _worker_event.notify_all();
7572
@@ -193,49 +190,44 @@ std::shared_ptr<default_scheduler_t::thread_instance_t> default_scheduler_t::nex · 117 unmodified lines
193190 for (;;)
194191 {
195192 // 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)
196198 {
197− std::unique_lock<std::mutex> lock{ _vm_threads_lock };
198−
199− // Check if a GC should be performed.
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− }
199+ const auto running_thread_count_local = _running_vm_thread_count;
200+ const auto is_gc_thread = running_thread_count_local == 0;
201+ perform_gc(is_gc_thread, lock);
225202 }
226203
227− // If the queue is empty, wait for a notification
204+ if (!_runnable_vm_threads.empty())
228205 {
229− std::unique_lock<std::mutex> worker_event_lock{ _worker_event_lock };
230− if (_terminating)
231− return{};
206+ next_thread = _runnable_vm_threads.front();
207+ _runnable_vm_threads.pop();
208+ ++_running_vm_thread_count;
209+ ++_gc_counter;
232210
233− debug::log_msg(debug::component_trace_t::scheduler, debug::log_level_t::debug, "scheduler: worker: waiting\n");
234− _worker_event.wait(worker_event_lock);
211+ // This system thread now takes ownership of the vm thread
212+ next_thread->system_thread_ownership.lock();
213+ return next_thread;
214+ }
235215
236− if (_terminating)
237− return{};
216+ if (!_all_vm_threads.empty() && _blocked_vm_thread_ids.size() == _all_vm_threads.size())
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");
238220 }
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{};
239231 }
240232 }
241233
src/vm/scheduler.h+0−1
@@ -76,7 +76,6 @@ namespace disvm · 75 unmodified lines
7676 const uint32_t _worker_thread_count;
7777 std::vector<std::thread> _worker_pool;
7878
79− std::mutex _worker_event_lock;
8079 std::condition_variable _worker_event;
8180 std::atomic_bool _terminating;
8281
src/vm/string.cpp+9−9
@@ -22,7 +22,7 @@ std::shared_ptr<const type_descriptor_t> vm_string_t::type_desc() · 21 unmodified lines
2222 namespace
2323 {
2424 // Enumeration for character type compare cases.
25− enum class compare_case
25+ enum class compare_case_t
2626 {
2727 ascii_ascii,
2828 ascii_rune,
@@ -31,10 +31,10 @@ namespace · 2 unmodified lines
3131 };
3232
3333 // Scenario matrix for compare cases
34− const compare_case scenario_matrix[][2] =
34+ const compare_case_t scenario_matrix[][2] =
3535 {
36− { compare_case::ascii_ascii, compare_case::ascii_rune },
37− { compare_case::rune_ascii, compare_case::rune_rune },
36+ { compare_case_t::ascii_ascii, compare_case_t::ascii_rune },
37+ { compare_case_t::rune_ascii, compare_case_t::rune_rune },
3838 };
3939
4040 // Case indices
@@ -92,14 +92,14 @@ int vm_string_t::compare(const vm_string_t *s1, const vm_string_t *s2) · 51 unmodified lines
9292 {
9393 default:
9494 assert(false && "Unknown compare scenario");
95− case compare_case::ascii_ascii:
96− case compare_case::rune_rune:
95+ case compare_case_t::ascii_ascii:
96+ case compare_case_t::rune_rune:
9797 result = std::memcmp(str_mem_1, str_mem_2, min_length);
9898 break;
99− case compare_case::ascii_rune:
99+ case compare_case_t::ascii_rune:
100100 result = _compare(min_length, reinterpret_cast<char *>(str_mem_1), reinterpret_cast<rune_t *>(str_mem_2));
101101 break;
102− case compare_case::rune_ascii:
102+ case compare_case_t::rune_ascii:
103103 result = _compare(min_length, reinterpret_cast<rune_t *>(str_mem_1), reinterpret_cast<char *>(str_mem_2));
104104 break;
105105 }
@@ -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
281281 }
282282
283283 // 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);
285285 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);
286286 }
287287
src/vm/sys/Sysmod.cpp+375−97
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 //
66
77 #include <array>
8+#include <algorithm>
89 #include <chrono>
910 #include <thread>
1011 #include <iostream>
@@ -13,6 +14,7 @@ · 2 unmodified lines
1314 #include <exceptions.h>
1415 #include <debug.h>
1516 #include <utf8.h>
17+#include <vm_memory.h>
1618 #include "Sysmod.h"
1719 #include "sys_utils.h"
1820
@@ -69,99 +71,153 @@ namespace · 50 unmodified lines
6971 };
7072
7173 const word_t Sysmodlen = 43;
72−}
7374
74−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_FD;
77−std::shared_ptr<const type_descriptor_t> T_Connection;
78−std::shared_ptr<const type_descriptor_t> T_FileIO;
75+ std::shared_ptr<const type_descriptor_t> T_Qid;
76+ std::shared_ptr<const type_descriptor_t> T_Dir;
77+ std::shared_ptr<const type_descriptor_t> T_FD;
78+ std::shared_ptr<const type_descriptor_t> T_Connection;
79+ std::shared_ptr<const type_descriptor_t> T_FileIO;
7980
80−struct vm_fd_t final : public Sys_FD
81−{
82− static void finalizer(vm_alloc_t *fileHandle)
81+ class vm_fd_t final : public Sys_FD
8382 {
84− auto fd = fileHandle->get_allocation<vm_fd_t>();
85− fd->~vm_fd_t();
86− }
83+ public: // static
84+ static void finalizer(vm_alloc_t *fileHandle)
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+ }
87100
88− vm_fd_t(word_t fd, std::unique_ptr<std::iostream> s)
89− : Sys_FD{ fd }
90− , _istream{ s.get() }
91− , _ostream{ s.get() }
92− {
93− assert(s != nullptr);
94− _stream = std::move(s);
95− }
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)
102+ {
103+ assert(s != nullptr);
104+ auto alloc = vm_alloc_t::allocate(T_FD);
105+ const auto fd = sys::create_fd_record(alloc);
106+ auto vm_fd = ::new(alloc->get_allocation())vm_fd_t{ fd, std::move(s), fd_mode, fd_path };
96107
97− vm_fd_t(word_t fd, std::unique_ptr<std::istream> s)
98− : Sys_FD{ fd }
99− , _istream{ s.get() }
100− , _ostream{ nullptr }
101− {
102− assert(s != nullptr);
103− _stream = std::move(s);
104− }
108+ return std::make_tuple(vm_fd->fd, alloc);
109+ }
105110
106− vm_fd_t(word_t fd, std::unique_ptr<std::ostream> s)
107− : Sys_FD{ fd }
108− , _istream{ nullptr }
109− , _ostream{ s.get() }
110− {
111− assert(s != nullptr);
112− _stream = std::move(s);
113− }
111+ private:
112+ vm_fd_t(const word_t fd, std::unique_ptr<std::iostream> s, const word_t fd_mode, vm_string_t *fd_path)
113+ : Sys_FD{ fd }
114+ , _fd_mode{ fd_mode }
115+ , _fd_path{ fd_path }
116+ , _istream{ s.get() }
117+ , _ostream{ s.get() }
118+ {
119+ _stream = std::move(s);
114120
115− vm_fd_t(word_t fd, std::istream &s)
116− : Sys_FD{ fd }
117− , _istream{ &s }
118− , _ostream{ nullptr }
119− {
120− }
121+ if (_fd_path != nullptr)
122+ _fd_path->add_ref();
123+ }
121124
122− vm_fd_t(word_t fd, std::ostream &s)
123− : Sys_FD{ fd }
124− , _istream{ nullptr }
125− , _ostream{ &s }
126− {
127− }
125+ public:
126+ vm_fd_t(const word_t fd, std::istream &is)
127+ : Sys_FD{ fd }
128+ , _fd_mode{}
129+ , _fd_path{ nullptr }
130+ , _istream{ &is }
131+ , _ostream{ nullptr }
132+ {
133+ }
128134
129− ~vm_fd_t()
130− {
131− debug::assign_debug_pointer(&_istream);
132− debug::assign_debug_pointer(&_ostream);
135+ vm_fd_t(const word_t fd, std::ostream &os)
136+ : Sys_FD{ fd }
137+ , _fd_mode{}
138+ , _fd_path{ nullptr }
139+ , _istream{ nullptr }
140+ , _ostream{ &os }
141+ {
142+ }
133143
134− if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())
135− debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: file descriptor: %d", fd);
136− }
144+ ~vm_fd_t()
145+ {
146+ debug::assign_debug_pointer(&_istream);
147+ debug::assign_debug_pointer(&_ostream);
148+ if (_stream != nullptr)
149+ _stream.reset();
137150
138− std::istream& input() const
139− {
140− if (_istream == nullptr)
141− throw vm_user_exception{ "File descriptor not open for write" };
151+ // Honor the close flag
152+ if ((_fd_mode & Sys_ORCLOSE) && _fd_path != nullptr)
153+ std::remove(_fd_path->str());
142154
143− return *_istream;
144− }
155+ dec_ref_count_and_free(_fd_path);
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" };
145177
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)
147204 {
148− if (_ostream == nullptr)
149− throw vm_user_exception{ "File descriptor not open for read" };
205+ auto om = std::ios::openmode{};
150206
151− return *_ostream;
152− }
207+ // Lowest bit is for read/write.
208+ if (mode & Sys_OWRITE)
209+ om |= std::ios::out;
210+ else
211+ om |= std::ios::in; // Sys_OREAD
153212
154−private:
155− std::istream *_istream;
156− std::ostream *_ostream;
157− std::unique_ptr<std::ios> _stream;
158−};
213+ if (mode & Sys_ORDWR)
214+ om |= (std::ios::in | std::ios::out);
159215
160−namespace
161−{
162− vm_alloc_t *vm_stdin = nullptr;
163− vm_alloc_t *vm_stdout = nullptr;
164− vm_alloc_t *vm_stderr = nullptr;
216+ if (mode & Sys_OTRUNC)
217+ om |= std::ios::trunc;
218+
219+ return om;
220+ }
165221 }
166222
167223 void
@@ -176,14 +232,26 @@ Sysmodinit(void) · 8 unmodified lines
176232
177233 // Initialize default file descriptors
178234
179− vm_stdin = vm_alloc_t::allocate(T_FD);
180− ::new(vm_stdin->get_allocation()) vm_fd_t{ 0, std::cin };
235+ {
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+ }
181241
182− vm_stdout = vm_alloc_t::allocate(T_FD);
183− ::new(vm_stdout->get_allocation()) vm_fd_t{ 1, std::cout };
242+ {
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+ }
184248
185− vm_stderr = vm_alloc_t::allocate(T_FD);
186− ::new(vm_stderr->get_allocation()) vm_fd_t{ 2, std::cerr };
249+ {
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+ }
187255 }
188256
189257 void
@@ -260,19 +328,19 @@ Sys_char2byte(vm_registers_t &r, vm_t &vm) · 70 unmodified lines
260328
261329 const auto rune = static_cast<rune_t>(fp.c);
262330
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");
264332 uint8_t buffer_local[Sys_UTFmax];
265333 auto begin = buffer_local;
266334
267335 auto end = utf8::encode(rune, begin);
268336 const auto count = static_cast<word_t>(end - begin);
269337
270− // Initialize for failure.
271− *fp.ret = 0;
272−
273338 const auto final_index = n + count;
274339 if (final_index > buffer->get_length())
340+ {
341+ *fp.ret = 0;
275342 return;
343+ }
276344
277345 // Insert the bytes into the buffer
278346 for (auto i = word_t{ n }; i < final_index; ++i)
@@ -295,7 +363,41 @@ void · 16 unmodified lines
295363 Sys_create(vm_registers_t &r, vm_t &vm)
296364 {
297365 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();
299401 }
300402
301403 void
@@ -417,7 +519,36 @@ void · 115 unmodified lines
417519 Sys_open(vm_registers_t &r, vm_t &vm)
418520 {
419521 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();
421552 }
422553
423554 void
@@ -453,16 +584,16 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 29 unmodified lines
453584
454585 auto static_buffer = std::array<char, 1024>{};
455586 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());
457588 if (written >= 0)
458589 {
459− vm_stdout->get_allocation<vm_fd_t>()->output() << static_buffer.data();
590+ fd_stdout->write(written, static_buffer.data());
460591 }
461592 else
462593 {
463594 auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2);
464− written = printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer);
465− vm_stdout->get_allocation<vm_fd_t>()->output() << static_buffer.data();
595+ written = sys::printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer);
596+ fd_stdout->write(written, dynamic_buffer.data());
466597 }
467598
468599 *fp.ret = written;
@@ -479,7 +610,26 @@ void · 10 unmodified lines
479610 Sys_read(vm_registers_t &r, vm_t &vm)
480611 {
481612 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));
483633 }
484634
485635 void
@@ -511,9 +661,7 @@ Sys_sleep(vm_registers_t &r, vm_t &vm) · 25 unmodified lines
511661 const auto period_ms = fp.period;
512662
513663 // [TODO] Thread should yield to another according to the sleep() specification.
514− r.current_thread_state = vm_thread_state_t::release;
515664 std::this_thread::sleep_for(std::chrono::milliseconds(period_ms));
516− r.current_thread_state = vm_thread_state_t::ready;
517665
518666 *fp.ret = 0;
519667 }
@@ -539,11 +687,120 @@ Sys_stream(vm_registers_t &r, vm_t &vm) · 19 unmodified lines
539687 throw vm_system_exception{ "Instruction not implemented" };
540688 }
541689
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+
542720 void
543721 Sys_tokenize(vm_registers_t &r, vm_t &vm)
544722 {
545723 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;
547804 }
548805
549806 void
@@ -571,7 +828,28 @@ void · 21 unmodified lines
571828 Sys_write(vm_registers_t &r, vm_t &vm)
572829 {
573830 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;
575853 }
576854
577855 void
src/vm/sys/fd_manager.cpp+84−0
@@ -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+}
src/vm/sys/file_system.cpp+48−0
@@ -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+}
src/vm/sys/print.cpp+12−12
@@ -16,7 +16,7 @@ namespace · 15 unmodified lines
1616 {
1717 // Align the supplied pointer for the template type relative to base.
1818 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)
2020 {
2121 for (;;)
2222 {
@@ -31,7 +31,7 @@ namespace · 8 unmodified lines
3131
3232 // Shamelessly adopted and altered from Inferno implementation (xprint - libinterp/runt.c)
3333 // [TODO] Handle UTF-8
34−word_t printf_to_buffer(
34+word_t disvm::runtime::sys::printf_to_buffer(
3535 const disvm::runtime::vm_string_t &msg_fmt,
3636 disvm::runtime::byte_t *msg_args,
3737 disvm::runtime::pointer_t base,
@@ -121,17 +121,19 @@ word_t printf_to_buffer( · 83 unmodified lines
121121 break;
122122 case 's': // string
123123 {
124− auto s = vm_alloc_t::from_allocation<vm_string_t>(*reinterpret_cast<pointer_t *>(msg_args));
125− if (s != nullptr)
126− wb = std::snprintf(b_curr, (b_end - b_curr), "%s", s->str());
124+ auto p = *reinterpret_cast<pointer_t *>(msg_args);
125+ auto str = vm_alloc_t::from_allocation<vm_string_t>(p);
126+ if (str != nullptr)
127+ wb = std::snprintf(b_curr, (b_end - b_curr), "%s", str->str());
127128
128− msg_args += sizeof(pointer_t);
129+ msg_args += sizeof(p);
129130 break;
130131 }
131132 // [SPEC] Undocumented format verb for debugging VM objects.
132133 case 'H':
133134 {
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);
135137 auto rc = static_cast<std::size_t>(-1);
136138 const void *type_alloc = nullptr;
137139 if (alloc != nullptr)
@@ -140,8 +142,8 @@ word_t printf_to_buffer( · 2 unmodified lines
140142 type_alloc = alloc->alloc_type.get();
141143 }
142144
143− wb = std::snprintf(b_curr, (b_end - b_curr), "%" PRIdPTR ".%#08" PRIxPTR, rc, reinterpret_cast<std::size_t>(type_alloc));
144− msg_args += sizeof(pointer_t);
145+ wb = std::snprintf(b_curr, (b_end - b_curr), "%" PRIuPTR ".%#08" PRIxPTR, rc, reinterpret_cast<std::size_t>(type_alloc));
146+ msg_args += sizeof(p);
145147 break;
146148 }
147149 }
@@ -159,7 +161,7 @@ word_t printf_to_buffer( · 11 unmodified lines
159161 return (b_curr - buffer);
160162 }
161163
162−word_t printf_to_dynamic_buffer(
164+word_t disvm::runtime::sys::printf_to_dynamic_buffer(
163165 const disvm::runtime::vm_string_t &msg_fmt,
164166 disvm::runtime::byte_t *msg_args,
165167 disvm::runtime::pointer_t base,
@@ -167,9 +169,7 @@ word_t printf_to_dynamic_buffer( · 1 unmodified line
167169 {
168170 auto result = word_t{ 0 };
169171 while (-1 == (result = printf_to_buffer(msg_fmt, msg_args, base, buffer.size(), buffer.data())))
170− {
171172 buffer.resize(buffer.size() * 2);
172− }
173173
174174 return result;
175175 }
src/vm/sys/sys_utils.h+53−20
@@ -4,27 +4,60 @@ · 3 unmodified lines
44 // Author: arr
55 //
66
7−#ifndef _DISVM_SRC_VM_SYS_UTILS_H_
8−#define _DISVM_SRC_VM_SYS_UTILS_H_
7+#ifndef _DISVM_SRC_VM_SYS_SYS_UTILS_H_
8+#define _DISVM_SRC_VM_SYS_SYS_UTILS_H_
99
1010 #include <vector>
11+#include <iosfwd>
12+#include <memory>
1113 #include <disvm.h>
14+#include <utils.h>
1215
13−// Prints the format and arguments to the supplied buffer.
14−// Returns the number of bytes written.
15−disvm::runtime::word_t printf_to_buffer(
16− const disvm::runtime::vm_string_t &msg_fmt,
17− disvm::runtime::byte_t *msg_args,
18− disvm::runtime::pointer_t base,
19− const std::size_t buffer_size,
20− char *buffer);
21−
22−// Prints the format and arguments to the supplied dynamic buffer.
23−// Returns the number of bytes written.
24−disvm::runtime::word_t printf_to_dynamic_buffer(
25− const disvm::runtime::vm_string_t &msg_fmt,
26− disvm::runtime::byte_t *msg_args,
27− disvm::runtime::pointer_t base,
28− std::vector<char> &buffer);
29−
30−#endif // _DISVM_SRC_VM_SYS_UTILS_H_
16+namespace disvm
17+{
18+ namespace runtime
19+ {
20+ namespace sys
21+ {
22+ // Prints the format and arguments to the supplied buffer.
23+ // Returns the number of bytes written.
24+ word_t printf_to_buffer(
25+ const vm_string_t &msg_fmt,
26+ byte_t *msg_args,
27+ pointer_t base,
28+ const std::size_t buffer_size,
29+ char *buffer);
30+
31+ // Prints the format and arguments to the supplied dynamic buffer.
32+ // Returns the number of bytes written.
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_
src/vm/vm_exception_handler.cpp+2−2
@@ -43,12 +43,12 @@ std::tuple<const handler_t *, vm_frame_t *, vm_pc_t> disvm::runtime::find_except · 42 unmodified lines
4343 if (exception_id.compare_to(exception_name) == 0)
4444 {
4545 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);
4747 }
4848 else if (exception_name == nullptr && exception.pc != runtime_constants::invalid_program_counter)
4949 {
5050 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);
5252 }
5353 }
5454 }
src/vm/vm_memory.cpp+1−1
@@ -344,7 +344,7 @@ namespace · 343 unmodified lines
344344 }
345345 }
346346
347−std::atomic_bool intrinsic_type_desc::is_initialized;
347+std::atomic_bool intrinsic_type_desc::is_initialized{ false };
348348
349349 void intrinsic_type_desc::initialize()
350350 {