History

Merged in file_desc_impl_change (pull request #17)

File desc impl change

Aaron R Robinson committed 9 years ago

Commit cb38bc42 parents b1b22c2 558c6ea12 files · +600 −315Browse files

Unified · Split

12 files
  1. src/include/exceptions.h +9 −0
  2. src/include/utils.h +4 −4
  3. src/include/vm_memory.h +1 −1
  4. src/vm/disvm.gyp +1 −1
  5. src/vm/execution_table.cpp +77 −27
  6. src/vm/list.cpp +7 −5
  7. src/vm/sys/Sysmod.cpp +128 −170
  8. src/vm/sys/fd_manager.cpp +66 −47
  9. src/vm/sys/fd_types.cpp +217 −0
  10. src/vm/sys/fd_types.h +78 −0
  11. src/vm/sys/file_system.cpp +0 −46
  12. src/vm/sys/sys_utils.h +12 −14
src/include/exceptions.h+9−0
@@ -124,6 +124,15 @@ namespace disvm · 123 unmodified lines
124 const word_t invalid_value;124 const word_t invalid_value;
125 };125 };
126 126
127 // [TODO] Allow non-string exceptions to be marshalled.
128 class marshallable_user_exception final : public vm_user_exception
129 {
130 public:
131 explicit marshallable_user_exception(const char *message)
132 : vm_user_exception(message)
133 { }
134 };
135
127 class unhandled_user_exception final : public vm_user_exception136 class unhandled_user_exception final : public vm_user_exception
128 {137 {
129 public:138 public:
src/include/utils.h+4−4
@@ -76,12 +76,12 @@ namespace disvm · 75 unmodified lines
76 76
77// Adopted from 'winnt.h' in the Windows SDK77// Adopted from 'winnt.h' in the Windows SDK
78#define DEFINE_ENUM_FLAG_OPERATORS(TYPE) \78#define DEFINE_ENUM_FLAG_OPERATORS(TYPE) \
79inline TYPE operator | (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) | static_cast<std::underlying_type_t<TYPE>>(b)); } \79constexpr TYPE operator | (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) | static_cast<std::underlying_type_t<TYPE>>(b)); } \
80inline TYPE &operator |= (TYPE &a, TYPE b) { a = a | b; return a; } \80inline TYPE &operator |= (TYPE &a, TYPE b) { a = a | b; return a; } \
81inline TYPE operator & (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) & static_cast<std::underlying_type_t<TYPE>>(b)); } \81constexpr TYPE operator & (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) & static_cast<std::underlying_type_t<TYPE>>(b)); } \
82inline TYPE &operator &= (TYPE &a, TYPE b) { a = a & b; return a; } \82inline TYPE &operator &= (TYPE &a, TYPE b) { a = a & b; return a; } \
83inline TYPE operator ^ (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) ^ static_cast<std::underlying_type_t<TYPE>>(b)); } \83constexpr TYPE operator ^ (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) ^ static_cast<std::underlying_type_t<TYPE>>(b)); } \
84inline TYPE &operator ^= (TYPE &a, TYPE b) { a = a ^ b; return a; } \84inline TYPE &operator ^= (TYPE &a, TYPE b) { a = a ^ b; return a; } \
85inline TYPE operator ~ (TYPE a) { return static_cast<TYPE>(~static_cast<std::underlying_type_t<TYPE>>(a)); }85constexpr TYPE operator ~ (TYPE a) { return static_cast<TYPE>(~static_cast<std::underlying_type_t<TYPE>>(a)); }
86 86
87#endif // _DISVM_SRC_INCLUDE_UTILS_H_87#endif // _DISVM_SRC_INCLUDE_UTILS_H_
src/include/vm_memory.h+1−1
@@ -57,7 +57,7 @@ namespace disvm · 56 unmodified lines
57 template<typename CB>57 template<typename CB>
58 void enum_pointer_fields(const type_descriptor_t &type_desc, void *data, CB callback)58 void enum_pointer_fields(const type_descriptor_t &type_desc, void *data, CB callback)
59 {59 {
60 assert(data != nullptr && callback != nullptr);60 assert(data != nullptr);
61 61
62 auto memory = reinterpret_cast<word_t *>(data);62 auto memory = reinterpret_cast<word_t *>(data);
63 for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8)63 for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8)
src/vm/disvm.gyp+1−1
@@ -26,8 +26,8 @@ · 25 unmodified lines
26 'math/Mathmod.cpp',26 'math/Mathmod.cpp',
27 'sys/print.cpp',27 'sys/print.cpp',
28 'sys/Sysmod.cpp',28 'sys/Sysmod.cpp',
29 'sys/file_system.cpp',
30 'sys/fd_manager.cpp',29 'sys/fd_manager.cpp',
30 'sys/fd_types.cpp',
31 ],31 ],
32 'include_dirs': [32 'include_dirs': [
33 '../include'33 '../include'
src/vm/execution_table.cpp+77−27
@@ -164,7 +164,7 @@ namespace · 163 unmodified lines
164 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));164 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
165 165
166 auto str = at_val<vm_string_t>(r.src);166 auto str = at_val<vm_string_t>(r.src);
167 auto new_array = new vm_array_t(str);167 auto new_array = new vm_array_t{ str };
168 pt_ref(r.dest) = new_array->get_allocation();168 pt_ref(r.dest) = new_array->get_allocation();
169 }169 }
170 170
@@ -537,7 +537,7 @@ namespace · 366 unmodified lines
537 auto type_id = vt_ref<word_t>(r.mid);537 auto type_id = vt_ref<word_t>(r.mid);
538 const auto &types = r.module_ref->type_section;538 const auto &types = r.module_ref->type_section;
539 assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));539 assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));
540 auto type = types[type_id];540 const auto &type = types[type_id];
541 541
542 disvm::runtime::inc_ref_count_in_memory(*type, r.src);542 disvm::runtime::inc_ref_count_in_memory(*type, r.src);
543 destroy_memory(*type, r.dest);543 destroy_memory(*type, r.dest);
@@ -853,7 +853,7 @@ namespace · 309 unmodified lines
853 throw out_of_range_memory{};853 throw out_of_range_memory{};
854 }854 }
855 855
856 auto new_array = new vm_array_t(*arr, begin_index, length);856 auto new_array = new vm_array_t{ *arr, begin_index, length };
857 857
858 if (arr->alloc_type->map_in_bytes > 0)858 if (arr->alloc_type->map_in_bytes > 0)
859 vm.get_garbage_collector().track_allocation(new_array);859 vm.get_garbage_collector().track_allocation(new_array);
@@ -971,14 +971,15 @@ namespace · 111 unmodified lines
971 auto tail_maybe = at_val<vm_list_t>(r.dest);971 auto tail_maybe = at_val<vm_list_t>(r.dest);
972 972
973 // Create a new list973 // Create a new list
974 auto new_list = new vm_list_t{ type, tail_maybe };974 const auto type_size_in_bytes = type->size_in_bytes;
975 auto new_list = new vm_list_t{ std::move(type), tail_maybe };
975 if (tail_maybe != nullptr)976 if (tail_maybe != nullptr)
976 tail_maybe->release();977 tail_maybe->release();
977 978
978 auto destination = new_list->value();979 auto destination = new_list->value();
979 std::memmove(destination, r.src, type->size_in_bytes);980 std::memmove(destination, r.src, type_size_in_bytes);
980 981
981 if (type->map_in_bytes > 0)982 if (type_size_in_bytes > 0)
982 vm.get_garbage_collector().track_allocation(new_list);983 vm.get_garbage_collector().track_allocation(new_list);
983 984
984 // Return the list985 // Return the list
@@ -1329,9 +1330,10 @@ namespace · 344 unmodified lines
1329 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));1330 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
1330 1331
1331 auto type_desc = types[type_id];1332 auto type_desc = types[type_id];
1332 auto new_alloc = vm_alloc_t::allocate(type_desc);1333 const auto track_object = type_desc->map_in_bytes > 0;
1334 auto new_alloc = vm_alloc_t::allocate(std::move(type_desc));
1333 1335
1334 if (type_desc->map_in_bytes > 0)1336 if (track_object)
1335 vm.get_garbage_collector().track_allocation(new_alloc);1337 vm.get_garbage_collector().track_allocation(new_alloc);
1336 1338
1337 pt_ref(r.dest) = new_alloc->get_allocation();1339 pt_ref(r.dest) = new_alloc->get_allocation();
@@ -1363,9 +1365,10 @@ namespace · 25 unmodified lines
1363 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));1365 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
1364 1366
1365 auto type_desc = types[type_id];1367 auto type_desc = types[type_id];
1366 auto new_alloc = vm_alloc_t::allocate(type_desc);1368 const auto track_object = type_desc->map_in_bytes > 0;
1369 auto new_alloc = vm_alloc_t::allocate(std::move(type_desc));
1367 1370
1368 if (type_desc->map_in_bytes > 0)1371 if (track_object)
1369 vm.get_garbage_collector().track_allocation(new_alloc);1372 vm.get_garbage_collector().track_allocation(new_alloc);
1370 1373
1371 pt_ref(r.dest) = new_alloc->get_allocation();1374 pt_ref(r.dest) = new_alloc->get_allocation();
@@ -1385,9 +1388,10 @@ namespace · 13 unmodified lines
1385 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));1388 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
1386 1389
1387 const auto array_size = vt_ref<word_t>(r.src);1390 const auto array_size = vt_ref<word_t>(r.src);
1388 auto new_array = new vm_array_t(type_desc, array_size);1391 const auto track_object = type_desc->map_in_bytes > 0;
1392 auto new_array = new vm_array_t{ std::move(type_desc), array_size };
1389 1393
1390 if (type_desc->map_in_bytes > 0)1394 if (track_object)
1391 vm.get_garbage_collector().track_allocation(new_array);1395 vm.get_garbage_collector().track_allocation(new_array);
1392 1396
1393 pt_ref(r.dest) = new_array->get_allocation();1397 pt_ref(r.dest) = new_array->get_allocation();
@@ -1437,7 +1441,7 @@ namespace · 43 unmodified lines
1437 1441
1438 const auto frame_type_id = vt_ref<word_t>(r.src);1442 const auto frame_type_id = vt_ref<word_t>(r.src);
1439 assert(0 <= frame_type_id && frame_type_id < static_cast<word_t>(types.size()));1443 assert(0 <= frame_type_id && frame_type_id < static_cast<word_t>(types.size()));
1440 const auto frame_type = types[frame_type_id];1444 auto frame_type = types[frame_type_id];
1441 1445
1442 pt_ref(r.dest) = r.stack.alloc_frame(std::move(frame_type))->base();1446 pt_ref(r.dest) = r.stack.alloc_frame(std::move(frame_type))->base();
1443 }1447 }
@@ -1456,9 +1460,9 @@ namespace · 12 unmodified lines
1456 1460
1457 const auto frame_type_id = function_ref.frame_type;1461 const auto frame_type_id = function_ref.frame_type;
1458 assert(0 <= frame_type_id && frame_type_id < static_cast<word_t>(types.size()));1462 assert(0 <= frame_type_id && frame_type_id < static_cast<word_t>(types.size()));
1459 const auto frame_type = types[frame_type_id];1463 auto frame_type = types[frame_type_id];
1460 1464
1461 pt_ref(r.dest) = r.stack.alloc_frame(frame_type)->base();1465 pt_ref(r.dest) = r.stack.alloc_frame(std::move(frame_type))->base();
1462 }1466 }
1463 1467
1464 EXEC_DECL(ret)1468 EXEC_DECL(ret)
@@ -1503,6 +1507,9 @@ namespace · 38 unmodified lines
1503 disvm::debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "enter: function");1507 disvm::debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "enter: function");
1504 }1508 }
1505 1509
1510 // Forward declaration
1511 EXEC_DECL(raise);
1512
1506 EXEC_DECL(mcall)1513 EXEC_DECL(mcall)
1507 {1514 {
1508 auto target_module = at_val<vm_module_ref_t>(r.dest);1515 auto target_module = at_val<vm_module_ref_t>(r.dest);
@@ -1547,22 +1554,52 @@ namespace · 38 unmodified lines
1547 r.mp_base = r.module_ref->mp_base;1554 r.mp_base = r.module_ref->mp_base;
1548 r.next_pc = function_pc;1555 r.next_pc = function_pc;
1549 1556
1550 if (r.module_ref->is_builtin_module())1557 // Non-built-in modules ref count and return
1558 if (!r.module_ref->is_builtin_module())
1551 {1559 {
1552 const auto &inst = r.module_ref->code_section[function_pc];1560 r.mp_base->add_ref();
1553 assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)");1561 return;
1562 }
1563
1564 // Calling into a built-in module is a bit complicated since control leaves the VM and thus
1565 // the callee clean-up semantics of DisVM are difficult to enforced. The solution here is
1566 // to release VM control of the thread and clean-up the stack on behalf of the callee when
1567 // control returns to the VM.
1568 // Built-in modules are also permitted to throw marshallable exceptions which requires
1569 // raising the exception manually.
1554 1570
1555 r.current_thread_state = vm_thread_state_t::release;1571 const auto &inst = r.module_ref->code_section[function_pc];
1572 assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)");
1573
1574 vm_alloc_t *marshallable_err{};
1575 r.current_thread_state = vm_thread_state_t::release;
1576
1577 try
1578 {
1556 inst.native(r, vm);1579 inst.native(r, vm);
1557 ret(r, vm);
1558 1580
1559 // Only reset the thread if it is in the state set prior to native call.1581 // Handle callee clean-up semantics
1560 if (r.current_thread_state == vm_thread_state_t::release)1582 ret(r, vm);
1561 r.current_thread_state = vm_thread_state_t::running;
1562 }1583 }
1563 else1584 catch (const marshallable_user_exception &e)
1564 {1585 {
1565 r.mp_base->add_ref();1586 auto msg = e.what();
1587 marshallable_err = new vm_string_t{ std::strlen(msg), reinterpret_cast<const uint8_t*>(msg) };
1588 }
1589
1590 // Only reset the thread if it is in the state set prior to native call.
1591 if (r.current_thread_state == vm_thread_state_t::release)
1592 r.current_thread_state = vm_thread_state_t::running;
1593
1594 // Raise marshallable exception
1595 if (marshallable_err != nullptr)
1596 {
1597 assert(r.current_thread_state == vm_thread_state_t::running && "Throwing a marshallable error should not result in an altered VM thread state");
1598 auto err = marshallable_err->get_allocation();
1599 r.src = reinterpret_cast<pointer_t>(&err);
1600 raise(r, vm);
1601
1602 disvm::runtime::dec_ref_count_and_free(marshallable_err);
1566 }1603 }
1567 }1604 }
1568 1605
@@ -1706,10 +1743,23 @@ namespace · 137 unmodified lines
1706 1743
1707 if (target_frame != r.stack.peek_frame())1744 if (target_frame != r.stack.peek_frame())
1708 {1745 {
1709 while (target_frame != r.stack.pop_frame())1746 // Unwind stack to find target frame
1747 do
1710 {1748 {
1711 // Remove frames from the stack until the target frame is found.1749 auto curr_frame = r.stack.peek_frame();
1750
1751 // Update the module reference register during the stack unwind
1752 if (curr_frame->prev_module_ref() != nullptr)
1753 {
1754 dec_ref_count_and_free(r.module_ref);
1755 r.module_ref = curr_frame->prev_module_ref();
1756 curr_frame->prev_module_ref() = nullptr;
1757
1758 dec_ref_count_and_free(r.mp_base);
1759 r.mp_base = r.module_ref->mp_base;
1760 }
1712 }1761 }
1762 while (target_frame != r.stack.pop_frame());
1713 }1763 }
1714 1764
1715 // Re-initialize the current frame1765 // Re-initialize the current frame
src/vm/list.cpp+7−5
@@ -45,10 +45,6 @@ vm_list_t::vm_list_t(std::shared_ptr<const type_descriptor_t> td, vm_list_t *tai · 44 unmodified lines
45 45
46vm_list_t::~vm_list_t()46vm_list_t::~vm_list_t()
47{47{
48 // Remove tail
49 dec_ref_count_and_free(_tail);
50 debug::assign_debug_pointer(&_tail);
51
52 // Destroy and free the list element, if default element size was surpassed.48 // Destroy and free the list element, if default element size was surpassed.
53 if (_is_alloc)49 if (_is_alloc)
54 {50 {
@@ -61,7 +57,13 @@ vm_list_t::~vm_list_t() · 6 unmodified lines
61 }57 }
62 58
63 debug::assign_debug_pointer(&_mem.alloc);59 debug::assign_debug_pointer(&_mem.alloc);
64 disvm::debug::log_msg(component_trace_t::memory, log_level_t::debug, "destroy: vm list");60
61 // Remove tail
62 dec_ref_count_and_free(_tail);
63 debug::assign_debug_pointer(&_tail);
64
65 if (disvm::debug::is_component_tracing_enabled<component_trace_t::memory>())
66 disvm::debug::log_msg(component_trace_t::memory, log_level_t::debug, "destroy: vm list");
65}67}
66 68
67std::shared_ptr<const type_descriptor_t> vm_list_t::get_element_type() const69std::shared_ptr<const type_descriptor_t> vm_list_t::get_element_type() const
src/vm/sys/Sysmod.cpp+128−170
@@ -17,6 +17,7 @@ · 16 unmodified lines
17#include <vm_memory.h>17#include <vm_memory.h>
18#include "Sysmod.h"18#include "Sysmod.h"
19#include "sys_utils.h"19#include "sys_utils.h"
20#include "fd_types.h"
20 21
21using disvm::vm_t;22using disvm::vm_t;
22 23
@@ -34,8 +35,13 @@ using disvm::runtime::vm_string_t; · 11 unmodified lines
34using disvm::runtime::vm_syscall_exception;35using disvm::runtime::vm_syscall_exception;
35using disvm::runtime::vm_system_exception;36using disvm::runtime::vm_system_exception;
36using disvm::runtime::vm_user_exception;37using disvm::runtime::vm_user_exception;
38using disvm::runtime::marshallable_user_exception;
37using disvm::runtime::word_t;39using disvm::runtime::word_t;
38 40
41using disvm::runtime::sys::open_mode_t;
42using disvm::runtime::sys::seek_start_t;
43using disvm::runtime::sys::vm_fd_t;
44
39namespace45namespace
40{46{
41 disvm::runtime::builtin::vm_runtab_t Sysmodtab[] = {47 disvm::runtime::builtin::vm_runtab_t Sysmodtab[] = {
@@ -93,12 +99,14 @@ namespace · 51 unmodified lines
93 std::shared_ptr<const type_descriptor_t> T_Connection;99 std::shared_ptr<const type_descriptor_t> T_Connection;
94 std::shared_ptr<const type_descriptor_t> T_FileIO;100 std::shared_ptr<const type_descriptor_t> T_FileIO;
95 101
96 class vm_fd_t final : public Sys_FD102 class Sys_FD_Impl final : public Sys_FD
97 {103 {
98 public: // static104 public: // static
99 static void finalizer(vm_alloc_t *fileHandle)105 static void finalizer(vm_alloc_t *fileHandle)
100 {106 {
101 auto fd = fileHandle->get_allocation<vm_fd_t>();107 auto fd = fileHandle->get_allocation<Sys_FD_Impl>();
108
109 fd->~Sys_FD_Impl();
102 110
103 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())111 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
104 {112 {
@@ -108,121 +116,51 @@ namespace · 3 unmodified lines
108 "sys: fd: finalize: %d",116 "sys: fd: finalize: %d",
109 fd->fd);117 fd->fd);
110 }118 }
111
112 disvm::runtime::sys::drop_fd_record(fd->fd);
113 fd->~vm_fd_t();
114 }119 }
115 120
116 static std::tuple<word_t, vm_alloc_t *> create(std::unique_ptr<std::iostream> s, word_t fd_mode, vm_string_t *fd_path)121 static vm_alloc_t * create(vm_fd_t *f)
117 {122 {
118 assert(s != nullptr);
119 auto alloc = vm_alloc_t::allocate(T_FD);123 auto alloc = vm_alloc_t::allocate(T_FD);
120 const auto fd = disvm::runtime::sys::create_fd_record(alloc);124 auto fd_impl = ::new(alloc->get_allocation())Sys_FD_Impl{};
121 auto vm_fd = ::new(alloc->get_allocation())vm_fd_t{ fd, std::move(s), fd_mode, fd_path };
122
123 return std::make_tuple(vm_fd->fd, alloc);
124 }
125 125
126 private:126 fd_impl->fd = disvm::runtime::sys::create_fd_record(f);
127 vm_fd_t(const word_t fd, std::unique_ptr<std::iostream> s, const word_t fd_mode, vm_string_t *fd_path)127 fd_impl->impl = f;
128 : Sys_FD{ fd }
129 , _fd_mode{ fd_mode }
130 , _fd_path{ fd_path }
131 , _istream{ s.get() }
132 , _ostream{ s.get() }
133 {
134 _stream = std::move(s);
135 128
136 if (_fd_path != nullptr)129 return alloc;
137 _fd_path->add_ref();
138 }130 }
139 131
140 public:132 static vm_alloc_t * try_create(word_t fd)
141 vm_fd_t(const word_t fd, std::istream &is)
142 : Sys_FD{ fd }
143 , _fd_mode{}
144 , _fd_path{ nullptr }
145 , _istream{ &is }
146 , _ostream{ nullptr }
147 {133 {
148 }134 auto vm_fd = disvm::runtime::sys::fetch_fd_record(fd);
135 if (vm_fd == nullptr)
136 return nullptr;
149 137
150 vm_fd_t(const word_t fd, std::ostream &os)138 auto alloc = vm_alloc_t::allocate(T_FD);
151 : Sys_FD{ fd }139 auto fd_impl = ::new(alloc->get_allocation())Sys_FD_Impl{};
152 , _fd_mode{}
153 , _fd_path{ nullptr }
154 , _istream{ nullptr }
155 , _ostream{ &os }
156 {
157 }
158
159 ~vm_fd_t()
160 {
161 disvm::debug::assign_debug_pointer(&_istream);
162 disvm::debug::assign_debug_pointer(&_ostream);
163 if (_stream != nullptr)
164 _stream.reset();
165
166 // Honor the close flag
167 if ((_fd_mode & Sys_ORCLOSE) && _fd_path != nullptr)
168 std::remove(_fd_path->str());
169
170 dec_ref_count_and_free(_fd_path);
171 disvm::debug::assign_debug_pointer(&_fd_path);
172 }
173
174 word_t get_original_mode() const
175 {
176 return _fd_mode;
177 }
178
179 vm_string_t *get_original_path() const
180 {
181 return _fd_path;
182 }
183
184 word_t read(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer)
185 {
186 if (_istream == nullptr)
187 throw vm_system_exception{ "File descriptor not open for read operation" };
188 140
189 _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));141 fd_impl->fd = fd;
190 if (_istream->bad())142 fd_impl->impl = vm_fd;
191 throw vm_syscall_exception{ vm, "Read operation error" };
192 143
193 return static_cast<word_t>(_istream->gcount());144 return alloc;
194 }145 }
195 146
196 void write(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer)147 public:
148 ~Sys_FD_Impl()
197 {149 {
198 if (_ostream == nullptr)150 disvm::runtime::sys::drop_fd_record(fd);
199 throw vm_system_exception{ "File descriptor not open for write operation" };151 disvm::debug::assign_debug_pointer(&impl);
200
201 _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
202 if (_ostream->bad())
203 throw vm_syscall_exception{ vm, "Write operation error" };
204 }152 }
205 153
206 big_t seek(const std::ios::seekdir start, const big_t offset)154 vm_fd_t *impl;
207 {
208 if (_stream == nullptr)
209 throw vm_user_exception{ "Unable to seek on a fifo file descriptor (e.g. stdin, stdout, stderr)" };
210
211 auto new_offset = _stream->rdbuf()->pubseekoff(static_cast<std::streamoff>(offset), start, _fd_mode);
212 return static_cast<big_t>(new_offset);
213 }
214 155
215 private:156 private:
216 const word_t _fd_mode;157 Sys_FD_Impl() = default;
217 vm_string_t *_fd_path;
218 std::istream *_istream;
219 std::ostream *_ostream;
220 std::unique_ptr<std::ios> _stream;
221 };158 };
222 159
223 vm_fd_t *fd_stdin = nullptr;160 // File descriptors for std streams.
224 vm_fd_t *fd_stdout = nullptr;161 word_t fd_stdin = disvm::runtime::sys::vm_invalid_fd;
225 vm_fd_t *fd_stderr = nullptr;162 word_t fd_stdout = disvm::runtime::sys::vm_invalid_fd;
163 word_t fd_stderr = disvm::runtime::sys::vm_invalid_fd;
226 164
227 word_t printf_to_fd(165 word_t printf_to_fd(
228 disvm::vm_t &vm,166 disvm::vm_t &vm,
@@ -247,35 +185,41 @@ namespace · 18 unmodified lines
247 return written;185 return written;
248 }186 }
249 187
250 std::ios::openmode convert_to_openmode(const word_t mode)188 open_mode_t convert_to_openmode(const word_t mode)
251 {189 {
252 auto om = std::ios::openmode{};190 auto om = open_mode_t{};
253 191
254 // Lowest bit is for read/write.192 // Lowest bit is for read/write.
255 if (mode & Sys_OWRITE)193 if (mode & Sys_OWRITE)
256 om |= std::ios::out;194 om |= open_mode_t::write;
257 else195 else
258 om |= std::ios::in; // Sys_OREAD196 om |= open_mode_t::read; // Sys_OREAD
259 197
260 if (mode & Sys_ORDWR)198 if (mode & Sys_ORDWR)
261 om |= (std::ios::in | std::ios::out);199 om |= (open_mode_t::write | open_mode_t::read);
262 200
263 if (mode & Sys_OTRUNC)201 if (mode & Sys_OTRUNC)
264 om |= std::ios::trunc;202 om |= open_mode_t::truncate;
203
204 if (mode & Sys_OEXCL)
205 om |= open_mode_t::atomic;
206
207 if (mode & Sys_ORCLOSE)
208 om |= open_mode_t::delete_on_close;
265 209
266 return om;210 return om;
267 }211 }
268 212
269 std::ios::seekdir convert_to_seekdir(const word_t start)213 seek_start_t convert_to_seekdir(const word_t start)
270 {214 {
271 switch (start)215 switch (start)
272 {216 {
273 case Sys_SEEKSTART:217 case Sys_SEEKSTART:
274 return std::ios::beg;218 return seek_start_t::start;
275 case Sys_SEEKRELA:219 case Sys_SEEKRELA:
276 return std::ios::cur;220 return seek_start_t::relative;
277 case Sys_SEEKEND:221 case Sys_SEEKEND:
278 return std::ios::end;222 return seek_start_t::end;
279 default:223 default:
280 throw vm_user_exception{ "Invalid seek mode" };224 throw vm_user_exception{ "Invalid seek mode" };
281 }225 }
@@ -288,32 +232,27 @@ Sysmodinit(void) · 6 unmodified lines
288 disvm::runtime::builtin::register_module_exports(Sys_PATH, Sysmodlen, Sysmodtab);232 disvm::runtime::builtin::register_module_exports(Sys_PATH, Sysmodlen, Sysmodtab);
289 T_Qid = type_descriptor_t::create(sizeof(Sys_Qid), sizeof(Sys_Qid_map), Sys_Qid_map);233 T_Qid = type_descriptor_t::create(sizeof(Sys_Qid), sizeof(Sys_Qid_map), Sys_Qid_map);
290 T_Dir = type_descriptor_t::create(sizeof(Sys_Dir), sizeof(Sys_Dir_map), Sys_Dir_map);234 T_Dir = type_descriptor_t::create(sizeof(Sys_Dir), sizeof(Sys_Dir_map), Sys_Dir_map);
291 T_FD = type_descriptor_t::create(sizeof(vm_fd_t), sizeof(Sys_FD_map), Sys_FD_map, vm_fd_t::finalizer);235 T_FD = type_descriptor_t::create(sizeof(Sys_FD_Impl), sizeof(Sys_FD_map), Sys_FD_map, Sys_FD_Impl::finalizer);
292 T_Connection = type_descriptor_t::create(sizeof(Sys_Connection), sizeof(Sys_Connection_map), Sys_Connection_map);236 T_Connection = type_descriptor_t::create(sizeof(Sys_Connection), sizeof(Sys_Connection_map), Sys_Connection_map);
293 T_FileIO = type_descriptor_t::create(sizeof(Sys_FileIO), sizeof(Sys_FileIO_map), Sys_FileIO_map);237 T_FileIO = type_descriptor_t::create(sizeof(Sys_FileIO), sizeof(Sys_FileIO_map), Sys_FileIO_map);
294 238
295 // Initialize default file descriptors239 // Initialize default file descriptors
240 auto std_streams = disvm::runtime::sys::get_std_streams();
296 241
297 {242 // Initialize in this order to assign POSIX defined values for the 'standard' streams.
298 auto vm_stdin = vm_alloc_t::allocate(T_FD);243 // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html
299 const auto fd = disvm::runtime::sys::create_fd_record(vm_stdin);
300 fd_stdin = ::new(vm_stdin->get_allocation())vm_fd_t{ fd, std::cin };
301 disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stdin: %d", fd);
302 }
303 244
304 {245 fd_stdin = disvm::runtime::sys::create_fd_record(std_streams.input);
305 auto vm_stdout = vm_alloc_t::allocate(T_FD);246 assert(fd_stdin == 0);
306 const auto fd = disvm::runtime::sys::create_fd_record(vm_stdout);247 disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stdin: %d", fd_stdin);
307 fd_stdout = ::new(vm_stdout->get_allocation())vm_fd_t{ fd, std::cout };
308 disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stdout: %d", fd);
309 }
310 248
311 {249 fd_stdout = disvm::runtime::sys::create_fd_record(std_streams.output);
312 auto vm_stderr = vm_alloc_t::allocate(T_FD);250 assert(fd_stdout == 1);
313 const auto fd = disvm::runtime::sys::create_fd_record(vm_stderr);251 disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stdout: %d", fd_stdout);
314 fd_stderr = ::new(vm_stderr->get_allocation())vm_fd_t{ fd, std::cerr };252
315 disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stderr: %d", fd);253 fd_stderr = disvm::runtime::sys::create_fd_record(std_streams.error);
316 }254 assert(fd_stderr == 2);
255 disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stderr: %d", fd_stderr);
317}256}
318 257
319void258void
@@ -429,40 +368,34 @@ Sys_create(vm_registers_t &r, vm_t &vm) · 109 unmodified lines
429{368{
430 auto &fp = r.stack.peek_frame()->base<F_Sys_create>();369 auto &fp = r.stack.peek_frame()->base<F_Sys_create>();
431 370
371 dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
372 *fp.ret = nullptr;
373
432 auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);374 auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
433 const auto open_mode = convert_to_openmode(fp.mode);375 const auto open_mode = convert_to_openmode(fp.mode);
434 376
435 auto cfs_flag = disvm::runtime::sys::cfs_flags_t::none;377 // [TODO] Apply the requested permissions to the file - fp.perm
436 if (fp.mode & Sys_OEXCL)378 auto vm_fd = disvm::runtime::sys::create_from_file_path(path, open_mode);
437 cfs_flag = (disvm::runtime::sys::cfs_flags_t::atomic | disvm::runtime::sys::cfs_flags_t::ensure_create);
438 379
439 // [TODO] Apply the requested permissions to the file.380 // Return null if file is not open.
440 auto file_stream = disvm::runtime::sys::create_file_stream(path->str(), (open_mode | std::ios::binary), cfs_flag);381 if (vm_fd == nullptr)
441
442 // Return null if file stream is not open.
443 if (file_stream == nullptr)
444 {
445 *fp.ret = nullptr;
446 return;382 return;
447 }
448 383
449 vm_alloc_t *fd_alloc;384 auto fd_alloc = Sys_FD_Impl::create(vm_fd);
450 auto fd = word_t{};385 *fp.ret = fd_alloc->get_allocation();
451 std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path);
452 386
453 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())387 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
454 {388 {
389 auto fd_impl = fd_alloc->get_allocation<Sys_FD_Impl>();
455 disvm::debug::log_msg(390 disvm::debug::log_msg(
456 component_trace_t::builtin,391 component_trace_t::builtin,
457 log_level_t::debug,392 log_level_t::debug,
458 "sys: create: %d >>%s<< %#x %#x",393 "sys: create: %d >>%s<< %#x %#x",
459 fd,394 fd_impl->fd,
460 path->str(),395 path->str(),
461 fp.mode,396 fp.mode,
462 fp.perm);397 fp.perm);
463 }398 }
464
465 *fp.ret = fd_alloc->get_allocation();
466}399}
467 400
468void401void
@@ -479,11 +412,37 @@ Sys_dirread(vm_registers_t &r, vm_t &vm) · 10 unmodified lines
479 throw vm_system_exception{ "Function not implemented" };412 throw vm_system_exception{ "Function not implemented" };
480}413}
481 414
415namespace
416{
417 // [SPEC] In the Inferno OS, this error is represented by
418 // Ebadfd - 'fd out of range or not open' (emu/port/error.c).
419 auto invalid_fd_error = "Invalid FD value";
420}
421
482void422void
483Sys_dup(vm_registers_t &r, vm_t &vm)423Sys_dup(vm_registers_t &r, vm_t &vm)
484{424{
485 auto &fp = r.stack.peek_frame()->base<F_Sys_dup>();425 auto &fp = r.stack.peek_frame()->base<F_Sys_dup>();
486 throw vm_system_exception{ "Function not implemented" };426
427 const auto old_fd = fp.old;
428 auto fd_maybe = disvm::runtime::sys::fetch_fd_record(old_fd);
429 if (fd_maybe == nullptr)
430 throw marshallable_user_exception{ invalid_fd_error };
431
432 auto new_fd_maybe = fp.new_;
433 if (new_fd_maybe == disvm::runtime::sys::vm_invalid_fd)
434 {
435 new_fd_maybe = disvm::runtime::sys::create_fd_record(fd_maybe);
436 }
437 else if (!disvm::runtime::sys::try_update_fd_record(new_fd_maybe, fd_maybe))
438 {
439 throw marshallable_user_exception{ invalid_fd_error };
440 }
441
442 *fp.ret = new_fd_maybe;
443
444 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
445 disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: fd: dup %d %d", old_fd, new_fd_maybe);
487}446}
488 447
489void448void
@@ -521,12 +480,9 @@ Sys_fildes(vm_registers_t &r, vm_t &vm) · 31 unmodified lines
521 dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));480 dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
522 *fp.ret = nullptr;481 *fp.ret = nullptr;
523 482
524 auto fd = disvm::runtime::sys::fetch_fd_record(fp.fd);483 auto fd_alloc = Sys_FD_Impl::try_create(fp.fd);
525 if (fd != nullptr)484 if (fd_alloc != nullptr)
526 {485 *fp.ret = fd_alloc->get_allocation();
527 fd->add_ref();
528 *fp.ret = fd->get_allocation();
529 }
530}486}
531 487
532void488void
@@ -546,12 +502,12 @@ Sys_fprint(vm_registers_t &r, vm_t &vm) · 13 unmodified lines
546 auto &fp = r.stack.peek_frame()->base<F_Sys_fprint>();502 auto &fp = r.stack.peek_frame()->base<F_Sys_fprint>();
547 auto fd_alloc = vm_alloc_t::from_allocation(fp.fd);503 auto fd_alloc = vm_alloc_t::from_allocation(fp.fd);
548 assert(fd_alloc->alloc_type == T_FD);504 assert(fd_alloc->alloc_type == T_FD);
549 auto fd = fd_alloc->get_allocation<vm_fd_t>();505 auto fd = fd_alloc->get_allocation<Sys_FD_Impl>();
550 auto str = vm_alloc_t::from_allocation<vm_string_t>(fp.s);506 auto str = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
551 if (str == nullptr)507 if (str == nullptr)
552 throw dereference_nil{ "Print string to FD" };508 throw dereference_nil{ "Print string to FD" };
553 509
554 *fp.ret = printf_to_fd(vm, *fd, *str, &fp.vargs, fp_base);510 *fp.ret = printf_to_fd(vm, *fd->impl, *str, &fp.vargs, fp_base);
555}511}
556 512
557void513void
@@ -611,35 +567,33 @@ Sys_open(vm_registers_t &r, vm_t &vm) · 53 unmodified lines
611{567{
612 auto &fp = r.stack.peek_frame()->base<F_Sys_open>();568 auto &fp = r.stack.peek_frame()->base<F_Sys_open>();
613 569
570 dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
571 *fp.ret = nullptr;
572
614 auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);573 auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
615 const auto open_mode = convert_to_openmode(fp.mode);574 const auto open_mode = convert_to_openmode(fp.mode);
616 575
617 // [TODO] Handle opening devices - not just files (i.e. /dev/random, /dev/source, /proc/1234)576 // [TODO] Handle opening devices - not just files (i.e. /dev/random, /dev/source, /proc/1234)
618 auto file_stream = disvm::runtime::sys::create_file_stream(path->str(), (open_mode | std::ios::binary));577 auto vm_fd = disvm::runtime::sys::create_from_file_path(path, open_mode | open_mode_t::ensure_exists);
619 578
620 // Return null if file stream is not open.579 // Return null if file is not open.
621 if (file_stream == nullptr)580 if (vm_fd == nullptr)
622 {
623 *fp.ret = nullptr;
624 return;581 return;
625 }
626 582
627 vm_alloc_t *fd_alloc;583 auto fd_alloc = Sys_FD_Impl::create(vm_fd);
628 auto fd = word_t{};584 *fp.ret = fd_alloc->get_allocation();
629 std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path);
630 585
631 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())586 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
632 {587 {
588 auto fd_impl = fd_alloc->get_allocation<Sys_FD_Impl>();
633 disvm::debug::log_msg(589 disvm::debug::log_msg(
634 component_trace_t::builtin,590 component_trace_t::builtin,
635 log_level_t::debug,591 log_level_t::debug,
636 "sys: open: %d >>%s<< %#x",592 "sys: open: %d >>%s<< %#x",
637 fd,593 fd_impl->fd,
638 path->str(),594 path->str(),
639 fp.mode);595 fp.mode);
640 }596 }
641
642 *fp.ret = fd_alloc->get_allocation();
643}597}
644 598
645void599void
@@ -672,7 +626,11 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 26 unmodified lines
672 if (str == nullptr)626 if (str == nullptr)
673 throw dereference_nil{ "Print string" };627 throw dereference_nil{ "Print string" };
674 628
675 *fp.ret = printf_to_fd(vm, *fd_stdout, *str, &fp.vargs, fp_base);629 auto std_output = disvm::runtime::sys::fetch_fd_record(fd_stdout);
630 if (std_output == nullptr)
631 throw vm_system_exception{ "stdout not available" };
632
633 *fp.ret = printf_to_fd(vm, *std_output, *str, &fp.vargs, fp_base);
676}634}
677 635
678void636void
@@ -708,8 +666,8 @@ Sys_read(vm_registers_t &r, vm_t &vm) · 29 unmodified lines
708 666
709 assert(fd_alloc->alloc_type == T_FD);667 assert(fd_alloc->alloc_type == T_FD);
710 668
711 auto fd = fd_alloc->get_allocation<vm_fd_t>();669 auto fd = fd_alloc->get_allocation<Sys_FD_Impl>();
712 *fp.ret = fd->read(vm, n, buffer->at(0));670 *fp.ret = fd->impl->read(vm, n, buffer->at(0));
713}671}
714 672
715void673void
@@ -742,10 +700,10 @@ Sys_seek(vm_registers_t &r, vm_t &vm) · 26 unmodified lines
742 throw dereference_nil{ "Seek in file descriptor" };700 throw dereference_nil{ "Seek in file descriptor" };
743 701
744 assert(fd_alloc->alloc_type == T_FD);702 assert(fd_alloc->alloc_type == T_FD);
745 auto fd = fd_alloc->get_allocation<vm_fd_t>();703 auto fd = fd_alloc->get_allocation<Sys_FD_Impl>();
746 704
747 const auto start = convert_to_seekdir(fp.start);705 const auto start = convert_to_seekdir(fp.start);
748 *fp.ret = fd->seek(start, fp.off);706 *fp.ret = fd->impl->seek(vm, start, fp.off);
749}707}
750 708
751void709void
@@ -993,8 +951,8 @@ Sys_write(vm_registers_t &r, vm_t &vm) · 241 unmodified lines
993 951
994 assert(fd_alloc->alloc_type == T_FD);952 assert(fd_alloc->alloc_type == T_FD);
995 953
996 auto fd = fd_alloc->get_allocation<vm_fd_t>();954 auto fd = fd_alloc->get_allocation<Sys_FD_Impl>();
997 fd->write(vm, n, buffer->at(0));955 fd->impl->write(vm, n, buffer->at(0));
998 956
999 *fp.ret = n;957 *fp.ret = n;
1000}958}
src/vm/sys/fd_manager.cpp+66−47
@@ -6,7 +6,8 @@ · 5 unmodified lines
6 6
7#include <array>7#include <array>
8#include <mutex>8#include <mutex>
9#include <unordered_map>9#include <vector>
10#include <queue>
10#include <exceptions.h>11#include <exceptions.h>
11#include <vm_memory.h>12#include <vm_memory.h>
12#include "sys_utils.h"13#include "sys_utils.h"
@@ -14,72 +15,90 @@ · 1 unmodified line
14using disvm::runtime::vm_alloc_t;15using disvm::runtime::vm_alloc_t;
15using disvm::runtime::word_t;16using disvm::runtime::word_t;
16 17
18using disvm::runtime::sys::vm_fd_t;
19
17namespace20namespace
18{21{
19 using fd_map_t = std::unordered_map<word_t, vm_alloc_t *>;22 std::mutex fd_map_lock;
23 auto fd_map = std::vector<vm_fd_t *>{};
24 auto fd_available = std::priority_queue<word_t, std::vector<word_t>, std::greater<word_t>>{};
25}
26
27word_t disvm::runtime::sys::create_fd_record(vm_fd_t * vm_fd)
28{
29 assert(vm_fd != nullptr);
20 30
21 auto std_fd = std::array<vm_alloc_t * , 3>31 std::lock_guard<std::mutex> lock{ fd_map_lock };
32
33 word_t new_fd;
34 if (fd_available.empty())
35 {
36 new_fd = fd_map.size();
37 fd_map.push_back(vm_fd);
38 }
39 else
22 {40 {
23 nullptr, // stdin41 new_fd = fd_available.top();
24 nullptr, // stdout42 fd_available.pop();
25 nullptr, // stderr
26 };
27 43
28 auto next_fd = word_t{ 0 };44 assert(fd_map[new_fd] == nullptr && "Reused FD should be null");
45 fd_map[new_fd] = vm_fd;
46 }
29 47
30 std::mutex fd_map_lock;48 return new_fd;
31 auto fd_map = fd_map_t{};
32}49}
33 50
34word_t disvm::runtime::sys::create_fd_record(vm_alloc_t * fd_alloc)51bool disvm::runtime::sys::try_update_fd_record(word_t fd, vm_fd_t *vm_fd)
35{52{
36 assert(fd_alloc != nullptr);53 assert(fd != sys::vm_invalid_fd);
54 assert(vm_fd != nullptr);
37 55
38 {56 std::lock_guard<std::mutex> lock{ fd_map_lock };
39 std::lock_guard<std::mutex> lock{ fd_map_lock };57 if (fd < 0 || static_cast<word_t>(fd_map.size()) <= fd)
40 58 return false;
41 auto new_fd = next_fd;59
42 next_fd++;60 auto vm_fd_curr = fd_map[fd];
43 if (new_fd < static_cast<word_t>(std_fd.size()))61 auto current_ref = vm_fd_curr->release();
44 {62 if (current_ref == 0)
45 std_fd[new_fd] = fd_alloc;63 delete vm_fd_curr;
46 }64
47 else65 fd_map[fd] = vm_fd;
48 {66 return true;
49 assert(fd_map.cend() == fd_map.find(new_fd));
50 fd_map[new_fd] = fd_alloc;
51 }
52
53 return new_fd;
54 }
55}67}
56 68
57vm_alloc_t * disvm::runtime::sys::fetch_fd_record(word_t fd)69vm_fd_t * disvm::runtime::sys::fetch_fd_record(word_t fd)
58{70{
59 assert(fd != sys::vm_invalid_fd && "Invalid FD value");71 assert(fd != sys::vm_invalid_fd);
60 72
61 if (0 <= fd && fd < static_cast<word_t>(std_fd.size()))73 std::lock_guard<std::mutex> lock{ fd_map_lock };
62 return std_fd[fd];74 if (fd < 0 || static_cast<word_t>(fd_map.size()) <= fd)
75 return nullptr;
63 76
64 {77 auto vm_fd = fd_map[fd];
65 std::lock_guard<std::mutex> lock{ fd_map_lock };78 if (vm_fd != nullptr)
66 auto iter = fd_map.find(fd);79 vm_fd->add_ref();
67 if (iter == fd_map.cend())
68 return nullptr;
69 80
70 return iter->second;81 return vm_fd;
71 }
72}82}
73 83
74void disvm::runtime::sys::drop_fd_record(word_t fd)84void disvm::runtime::sys::drop_fd_record(word_t fd)
75{85{
76 assert(fd != sys::vm_invalid_fd && "Invalid FD value");86 assert(fd != sys::vm_invalid_fd);
77 assert(fd >= static_cast<word_t>(std_fd.size()) && "Request to drop std file?");87
88 std::lock_guard<std::mutex> lock{ fd_map_lock };
89 if (fd < 0 || static_cast<word_t>(fd_map.size()) <= fd)
90 return;
78 91
92 assert(fd_map[fd] != nullptr);
93
94 // Manual management of file descriptors, since there is an inherent life time
95 // race amoung the enclosing ADT instances that can have a reference.
96 auto vm_fd = fd_map[fd];
97 auto current_ref = vm_fd->release();
98 if (current_ref == 0)
79 {99 {
80 std::lock_guard<std::mutex> lock{ fd_map_lock };100 delete vm_fd;
81 auto iter = fd_map.find(fd);101 fd_map[fd] = nullptr;
82 if (iter != fd_map.cend())102 fd_available.push(fd);
83 fd_map.erase(iter);
84 }103 }
85}104}
src/vm/sys/fd_types.cpp+217−0
@@ -0,0 +1,217 @@
1//
2// Dis VM
3// File: fd_types.cpp
4// Author: arr
5//
6
7#include <cstdio>
8#include <algorithm>
9#include <streambuf>
10
11#include <debug.h>
12#include <exceptions.h>
13#include <vm_memory.h>
14#include "fd_types.h"
15
16using disvm::vm_t;
17
18using disvm::debug::component_trace_t;
19using disvm::debug::log_level_t;
20
21using disvm::runtime::big_t;
22using disvm::runtime::byte_t;
23using disvm::runtime::type_descriptor_t;
24using disvm::runtime::vm_string_t;
25using disvm::runtime::word_t;
26using disvm::runtime::vm_system_exception;
27using disvm::runtime::vm_syscall_exception;
28using disvm::runtime::vm_user_exception;
29
30using disvm::runtime::sys::open_mode_t;
31using disvm::runtime::sys::seek_start_t;
32using disvm::runtime::sys::std_streams;
33using disvm::runtime::sys::vm_fd_t;
34
35vm_fd_t::vm_fd_t(std::shared_ptr<const type_descriptor_t> td)
36 : vm_alloc_t(std::move(td))
37{ }
38
39namespace
40{
41 namespace hidden_type_desc
42 {
43 const type_descriptor_t vm_fd_t{ 0, 0, nullptr, type_descriptor_t::no_finalizer, "vm_fd_t" };
44
45 struct
46 {
47 void operator()(const type_descriptor_t *)
48 {
49 // Not allocated on heap
50 }
51 } deleter;
52 }
53}
54
55std::shared_ptr<const type_descriptor_t> vm_fd_t::type_desc()
56{
57 static std::shared_ptr<const type_descriptor_t> t = { &hidden_type_desc::vm_fd_t, hidden_type_desc::deleter };
58 return t;
59}
60
61namespace
62{
63 int seek_to_int(const seek_start_t m)
64 {
65 switch (m)
66 {
67 default:
68 assert(false && "Unknown seek start type");
69 case seek_start_t::start:
70 return SEEK_SET;
71 case seek_start_t::relative:
72 return SEEK_CUR;
73 case seek_start_t::end:
74 return SEEK_END;
75 }
76 }
77
78 class sys_fd_t final : public vm_fd_t
79 {
80 public:
81 sys_fd_t(std::FILE *sys_fd, vm_string_t *fd_path, const open_mode_t fd_mode)
82 : vm_fd_t(type_desc())
83 , _fd_mode{ fd_mode }
84 , _fd_path{ fd_path }
85 , _sys_fd{ sys_fd }
86 {
87 assert(sys_fd != nullptr);
88
89 if (_fd_path != nullptr)
90 _fd_path->add_ref();
91 }
92
93 ~sys_fd_t()
94 {
95 std::fclose(_sys_fd);
96 disvm::debug::assign_debug_pointer(&_sys_fd);
97
98 // Honor the close flag
99 if (disvm::util::has_flag(_fd_mode, open_mode_t::delete_on_close) && _fd_path != nullptr)
100 std::remove(_fd_path->str());
101
102 disvm::runtime::dec_ref_count_and_free(_fd_path);
103 disvm::debug::assign_debug_pointer(&_fd_path);
104 }
105
106 word_t read(vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) override
107 {
108 if (!disvm::util::has_flag(_fd_mode, open_mode_t::read))
109 throw vm_system_exception{ "File descriptor not open for read operation" };
110
111 auto read = std::fread(buffer, sizeof(byte_t), buffer_size_in_bytes, _sys_fd);
112 if (std::ferror(_sys_fd) != 0)
113 throw vm_syscall_exception{ vm, "Read operation error" };
114
115 return static_cast<word_t>(read);
116 }
117
118 void write(vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) override
119 {
120 if (!disvm::util::has_flag(_fd_mode, open_mode_t::write))
121 throw vm_system_exception{ "File descriptor not open for write operation" };
122
123 std::fwrite(buffer, sizeof(byte_t), buffer_size_in_bytes, _sys_fd);
124 if (std::ferror(_sys_fd) != 0)
125 throw vm_syscall_exception{ vm, "Write operation error" };
126 }
127
128 big_t seek(vm_t &vm, const seek_start_t seek_start, const big_t offset) override
129 {
130 if (disvm::util::has_flag(_fd_mode, open_mode_t::disable_seek))
131 throw vm_user_exception{ "Unable to seek on a fifo file descriptor (e.g. stdin, stdout, stderr)" };
132
133 auto result = std::fseek(_sys_fd, static_cast<long>(offset), seek_to_int(seek_start));
134 if (result != 0)
135 throw vm_syscall_exception{ vm, "Seek operation error" };
136
137 return static_cast<big_t>(std::ftell(_sys_fd));
138 }
139
140 private:
141 const open_mode_t _fd_mode;
142 vm_string_t *_fd_path;
143 std::FILE *_sys_fd;
144 };
145}
146
147namespace
148{
149 std::mutex _create_file_path_mutex;
150
151 const std::array<char *, 8> fopen_mode_strings =
152 {
153 // R W T
154 nullptr, // 0 0 0
155 "rb", // 1 0 0
156 "wb", // 0 1 0
157 "rwb", // 1 1 0
158 nullptr, // 0 0 1
159 "rb+", // 1 0 1
160 "wb+", // 0 1 1
161 "wb+", // 1 1 1
162 };
163
164 const auto fopen_modes = (open_mode_t::read | open_mode_t::write | open_mode_t::truncate);
165
166 constexpr const char *mode_for_fopen(const open_mode_t o)
167 {
168 return fopen_mode_strings[static_cast<std::size_t>(fopen_modes & o)];
169 }
170}
171
172vm_fd_t *disvm::runtime::sys::create_from_file_path(vm_string_t *path, open_mode_t mode)
173{
174 std::unique_lock<std::mutex> lock{ _create_file_path_mutex, std::defer_lock };
175
176 if ((mode & ~fopen_modes) != open_mode_t::none)
177 {
178 if (util::has_flag(mode, open_mode_t::atomic))
179 lock.lock();
180
181 if (util::has_flag(mode, (open_mode_t::ensure_create | open_mode_t::ensure_exists)))
182 {
183 assert(util::has_flag(mode, open_mode_t::ensure_create) != util::has_flag(mode, open_mode_t::ensure_exists) && "Create/Exists flags are mutually exclusive");
184 // Check if the file already exists/permission to read
185 auto tmp = std::fopen(path->str(), mode_for_fopen(open_mode_t::read));
186 const auto exists = (tmp != nullptr);
187
188 if (exists)
189 std::fclose(tmp);
190
191 if (exists && util::has_flag(mode, open_mode_t::ensure_create)
192 || !exists && util::has_flag(mode, open_mode_t::ensure_exists))
193 {
194 return{};
195 }
196 }
197 }
198
199 auto fopen_mode = mode_for_fopen(mode);
200 assert(fopen_mode != nullptr);
201
202 std::FILE *file = std::fopen(path->str(), fopen_mode);
203 if (file == nullptr)
204 return{}; // [TODO] Report system error?
205
206 return new sys_fd_t{ file, path, mode };
207}
208
209std_streams disvm::runtime::sys::get_std_streams()
210{
211 return
212 {
213 new sys_fd_t{ stdin, nullptr, (open_mode_t::read | open_mode_t::disable_seek) },
214 new sys_fd_t{ stdout, nullptr, (open_mode_t::write | open_mode_t::disable_seek) },
215 new sys_fd_t{ stderr, nullptr, (open_mode_t::write | open_mode_t::disable_seek) },
216 };
217}
src/vm/sys/fd_types.h+78−0
@@ -0,0 +1,78 @@
1//
2// Dis VM
3// File: fd_types.h
4// Author: arr
5//
6
7#ifndef _DISVM_SRC_VM_SYS_FD_TYPES_H_
8#define _DISVM_SRC_VM_SYS_FD_TYPES_H_
9
10#include <memory>
11
12#include <runtime.h>
13#include <utils.h>
14
15namespace disvm
16{
17 namespace runtime
18 {
19 namespace sys
20 {
21 enum class seek_start_t
22 {
23 start,
24 relative,
25 end,
26 };
27
28 enum class open_mode_t
29 {
30 none = 0,
31 read = (1 << 0),
32 write = (1 << 1),
33 truncate = (1 << 2),
34 atomic = (1 << 3),
35 ensure_create = (1 << 4), // Mutually exclusive with 'ensure_exists'
36 ensure_exists = (1 << 5), // Mutually exclusive with 'ensure_create'
37 delete_on_close = (1 << 6),
38 disable_seek = (1 << 7),
39 };
40
41 DEFINE_ENUM_FLAG_OPERATORS(open_mode_t);
42
43 // VM file descriptor
44 class vm_fd_t : public vm_alloc_t
45 {
46 public: // static
47 static std::shared_ptr<const type_descriptor_t> type_desc();
48
49 public:
50 // Read from the file descriptor into the supplied buffer
51 virtual word_t read(vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) = 0;
52
53 // Write to the file descriptor from the supplied buffer
54 virtual void write(vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) = 0;
55
56 // Seek to the supplied offset in the file descriptor
57 virtual big_t seek(vm_t &vm, const seek_start_t seek_start, const big_t offset) = 0;
58
59 protected:
60 vm_fd_t(std::shared_ptr<const type_descriptor_t> td);
61 };
62
63 // Create a file descriptor based on a file system path
64 vm_fd_t *create_from_file_path(vm_string_t *path, open_mode_t mode);
65
66 struct std_streams final
67 {
68 vm_fd_t *input;
69 vm_fd_t *output;
70 vm_fd_t *error;
71 };
72
73 std_streams get_std_streams();
74 }
75 }
76}
77
78#endif // _DISVM_SRC_VM_SYS_FD_TYPES_H_
src/vm/sys/file_system.cpp+0−46
@@ -1,46 +0,0 @@
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
13namespace
14{
15 std::mutex _create_file_stream_mutex;
16}
17
18std::unique_ptr<std::fstream> disvm::runtime::sys::create_file_stream(const char *path, std::ios::openmode mode, cfs_flags_t flags)
19{
20 auto file = std::unique_ptr<std::fstream>{};
21 if (flags == cfs_flags_t::none)
22 {
23 file = std::make_unique<std::fstream>(path, mode);
24 }
25 else
26 {
27 std::unique_lock<std::mutex> lock{ _create_file_stream_mutex, std::defer_lock };
28 if (util::has_flag(flags, cfs_flags_t::atomic))
29 lock.lock();
30
31 if (util::has_flag(flags, cfs_flags_t::ensure_create))
32 {
33 std::fstream file_exists{ path };
34 if (file_exists)
35 return{};
36 }
37
38 file = std::make_unique<std::fstream>(path, mode);
39 }
40
41 // Return null if file stream is not open.
42 if (!file->is_open())
43 return{};
44
45 return file;
46}
src/vm/sys/sys_utils.h+12−14
@@ -12,6 +12,7 @@ · 11 unmodified lines
12#include <memory>12#include <memory>
13#include <disvm.h>13#include <disvm.h>
14#include <utils.h>14#include <utils.h>
15#include "fd_types.h"
15 16
16namespace disvm17namespace disvm
17{18{
@@ -38,25 +39,22 @@ namespace disvm · 20 unmodified lines
38 pointer_t base,39 pointer_t base,
39 std::vector<char> &buffer);40 std::vector<char> &buffer);
40 41
41 enum class cfs_flags_t
42 {
43 none = 0,
44 atomic = 1 << 0,
45 ensure_create = 1 << 1,
46 };
47
48 DEFINE_ENUM_FLAG_OPERATORS(cfs_flags_t);
49
50 std::unique_ptr<std::fstream> create_file_stream(const char *path, std::ios::openmode mode, cfs_flags_t flags = sys::cfs_flags_t::none);
51
52 const word_t vm_invalid_fd = -1;42 const word_t vm_invalid_fd = -1;
53 43
54 // The first three FD records are optimized for lock-less access.44 // No reference count updates are performed on supplied vm_fd_t instance.
55 word_t create_fd_record(vm_alloc_t *fd_alloc);45 word_t create_fd_record(vm_fd_t *vm_fd);
46
47 // Returns 'false' if the supplied file descriptor is invalid, otherwise 'true'.
48 // No reference count updates are performed on supplied vm_fd_t instance.
49 bool try_update_fd_record(word_t fd, vm_fd_t *vm_fd);
56 50
57 // Returns null if the record does not exist.51 // Returns null if the record does not exist.
58 vm_alloc_t * fetch_fd_record(word_t fd);52 // If the record does exist, the reference count is incremented prior
53 // to the instance being returned.
54 vm_fd_t * fetch_fd_record(word_t fd);
59 55
56 // The associated vm_fd_t instance reference count is decremented
57 // and will be deleted once all references are released.
60 void drop_fd_record(word_t fd);58 void drop_fd_record(word_t fd);
61 }59 }
62 }60 }