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
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
src/include/runtime.h+1−1
@@ -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 value254 // Returns a pointer to the value
255 pointer_t value() const;255 pointer_t value() const;
src/sh/main.cpp+8−7
@@ -12,15 +12,16 @@ · 11 unmodified lines
12 12
13int main(int argc, char* argv[])13int 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));
src/vm/builtin_module.cpp+1−1
@@ -19,7 +19,7 @@ using namespace disvm::runtime::builtin; · 18 unmodified lines
19 19
20namespace20namespace
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;
src/vm/disvm.gyp+2−0
@@ -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'
src/vm/execution_table.cpp+6−33
@@ -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
22using namespace disvm;23using namespace disvm;
@@ -31,36 +32,6 @@ namespace · 8 unmodified lines
31 using uword_t = uint32_t; // unsigned word_t32 using uword_t = uint32_t; // unsigned word_t
32 using ubig_t = uint64_t; // unsigned big_t33 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 list798 // 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 list832 // 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
src/vm/list.cpp+19−13
@@ -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
47vm_list_t::~vm_list_t()45vm_list_t::~vm_list_t()
48{46{
49 // Remove tail47 // 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
91vm_list_t *vm_list_t::drop_tail()89void 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
99pointer_t vm_list_t::value() const105pointer_t vm_list_t::value() const
src/vm/module_reader.cpp+4−4
@@ -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());
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
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 threads59 // 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 workers70 // 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 queue192 // 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 notification204 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
src/vm/scheduler.h+0−1
@@ -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
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
22namespace22namespace
23{23{
24 // Enumeration for character type compare cases.24 // Enumeration for character type compare cases.
25 enum class compare_case25 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 cases33 // 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 indices40 // 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
src/vm/sys/Sysmod.cpp+375−97
@@ -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
74std::shared_ptr<const type_descriptor_t> T_Qid;75 std::shared_ptr<const type_descriptor_t> T_Qid;
75std::shared_ptr<const type_descriptor_t> T_Dir;76 std::shared_ptr<const type_descriptor_t> T_Dir;
76std::shared_ptr<const type_descriptor_t> T_FD;77 std::shared_ptr<const type_descriptor_t> T_FD;
77std::shared_ptr<const type_descriptor_t> T_Connection;78 std::shared_ptr<const type_descriptor_t> T_Connection;
78std::shared_ptr<const type_descriptor_t> T_FileIO;79 std::shared_ptr<const type_descriptor_t> T_FileIO;
79 80
80struct vm_fd_t final : public Sys_FD81 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() const151 // 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() const178 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
154private: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
160namespace216 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
167void223void
@@ -176,14 +232,26 @@ Sysmodinit(void) · 8 unmodified lines
176 232
177 // Initialize default file descriptors233 // 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
189void257void
@@ -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 buffer345 // 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
295Sys_create(vm_registers_t &r, vm_t &vm)363Sys_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
301void403void
@@ -417,7 +519,36 @@ void · 115 unmodified lines
417Sys_open(vm_registers_t &r, vm_t &vm)519Sys_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
423void554void
@@ -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 else592 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
479Sys_read(vm_registers_t &r, vm_t &vm)610Sys_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
485void635void
@@ -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
690namespace
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
542void720void
543Sys_tokenize(vm_registers_t &r, vm_t &vm)721Sys_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
549void806void
@@ -571,7 +828,28 @@ void · 21 unmodified lines
571Sys_write(vm_registers_t &r, vm_t &vm)828Sys_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
577void855void
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
14using namespace disvm::runtime;
15
16namespace
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
33word_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
56vm_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
73void 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
13using namespace disvm::runtime;
14
15namespace
16{
17 std::mutex _create_file_stream_mutex;
18}
19
20std::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
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-833// [TODO] Handle UTF-8
34word_t printf_to_buffer(34word_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': // string122 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
162word_t printf_to_dynamic_buffer(164word_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}
src/vm/sys/sys_utils.h+53−20
@@ -4,27 +4,60 @@ · 3 unmodified lines
4// Author: arr4// 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.16namespace disvm
14// Returns the number of bytes written.17{
15disvm::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,
24disvm::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_
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
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 }
src/vm/vm_memory.cpp+1−1
@@ -344,7 +344,7 @@ namespace · 343 unmodified lines
344 }344 }
345}345}
346 346
347std::atomic_bool intrinsic_type_desc::is_initialized;347std::atomic_bool intrinsic_type_desc::is_initialized{ false };
348 348
349void intrinsic_type_desc::initialize()349void intrinsic_type_desc::initialize()
350{350{