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
124124 const word_t invalid_value;
125125 };
126126
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+
127136 class unhandled_user_exception final : public vm_user_exception
128137 {
129138 public:
src/include/utils.h+4−4
@@ -76,12 +76,12 @@ namespace disvm · 75 unmodified lines
7676
7777 // Adopted from 'winnt.h' in the Windows SDK
7878 #define DEFINE_ENUM_FLAG_OPERATORS(TYPE) \
79−inline 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)); } \
79+constexpr 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)); } \
8080 inline TYPE &operator |= (TYPE &a, TYPE b) { a = a | b; return a; } \
81−inline 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)); } \
81+constexpr 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)); } \
8282 inline TYPE &operator &= (TYPE &a, TYPE b) { a = a & b; return a; } \
83−inline 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)); } \
83+constexpr 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)); } \
8484 inline TYPE &operator ^= (TYPE &a, TYPE b) { a = a ^ b; return a; } \
85−inline TYPE operator ~ (TYPE a) { return static_cast<TYPE>(~static_cast<std::underlying_type_t<TYPE>>(a)); }
85+constexpr TYPE operator ~ (TYPE a) { return static_cast<TYPE>(~static_cast<std::underlying_type_t<TYPE>>(a)); }
8686
8787 #endif // _DISVM_SRC_INCLUDE_UTILS_H_
src/include/vm_memory.h+1−1
@@ -57,7 +57,7 @@ namespace disvm · 56 unmodified lines
5757 template<typename CB>
5858 void enum_pointer_fields(const type_descriptor_t &type_desc, void *data, CB callback)
5959 {
60− assert(data != nullptr && callback != nullptr);
60+ assert(data != nullptr);
6161
6262 auto memory = reinterpret_cast<word_t *>(data);
6363 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
2626 'math/Mathmod.cpp',
2727 'sys/print.cpp',
2828 'sys/Sysmod.cpp',
29− 'sys/file_system.cpp',
3029 'sys/fd_manager.cpp',
30+ 'sys/fd_types.cpp',
3131 ],
3232 'include_dirs': [
3333 '../include'
src/vm/execution_table.cpp+77−27
@@ -164,7 +164,7 @@ namespace · 163 unmodified lines
164164 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
165165
166166 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 };
168168 pt_ref(r.dest) = new_array->get_allocation();
169169 }
170170
@@ -537,7 +537,7 @@ namespace · 366 unmodified lines
537537 auto type_id = vt_ref<word_t>(r.mid);
538538 const auto &types = r.module_ref->type_section;
539539 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];
541541
542542 disvm::runtime::inc_ref_count_in_memory(*type, r.src);
543543 destroy_memory(*type, r.dest);
@@ -853,7 +853,7 @@ namespace · 309 unmodified lines
853853 throw out_of_range_memory{};
854854 }
855855
856− auto new_array = new vm_array_t(*arr, begin_index, length);
856+ auto new_array = new vm_array_t{ *arr, begin_index, length };
857857
858858 if (arr->alloc_type->map_in_bytes > 0)
859859 vm.get_garbage_collector().track_allocation(new_array);
@@ -971,14 +971,15 @@ namespace · 111 unmodified lines
971971 auto tail_maybe = at_val<vm_list_t>(r.dest);
972972
973973 // 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 };
975976 if (tail_maybe != nullptr)
976977 tail_maybe->release();
977978
978979 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);
980981
981− if (type->map_in_bytes > 0)
982+ if (type_size_in_bytes > 0)
982983 vm.get_garbage_collector().track_allocation(new_list);
983984
984985 // Return the list
@@ -1329,9 +1330,10 @@ namespace · 344 unmodified lines
13291330 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
13301331
13311332 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));
13331335
1334− if (type_desc->map_in_bytes > 0)
1336+ if (track_object)
13351337 vm.get_garbage_collector().track_allocation(new_alloc);
13361338
13371339 pt_ref(r.dest) = new_alloc->get_allocation();
@@ -1363,9 +1365,10 @@ namespace · 25 unmodified lines
13631365 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
13641366
13651367 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));
13671370
1368− if (type_desc->map_in_bytes > 0)
1371+ if (track_object)
13691372 vm.get_garbage_collector().track_allocation(new_alloc);
13701373
13711374 pt_ref(r.dest) = new_alloc->get_allocation();
@@ -1385,9 +1388,10 @@ namespace · 13 unmodified lines
13851388 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
13861389
13871390 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 };
13891393
1390− if (type_desc->map_in_bytes > 0)
1394+ if (track_object)
13911395 vm.get_garbage_collector().track_allocation(new_array);
13921396
13931397 pt_ref(r.dest) = new_array->get_allocation();
@@ -1437,7 +1441,7 @@ namespace · 43 unmodified lines
14371441
14381442 const auto frame_type_id = vt_ref<word_t>(r.src);
14391443 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];
14411445
14421446 pt_ref(r.dest) = r.stack.alloc_frame(std::move(frame_type))->base();
14431447 }
@@ -1456,9 +1460,9 @@ namespace · 12 unmodified lines
14561460
14571461 const auto frame_type_id = function_ref.frame_type;
14581462 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];
14601464
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();
14621466 }
14631467
14641468 EXEC_DECL(ret)
@@ -1503,6 +1507,9 @@ namespace · 38 unmodified lines
15031507 disvm::debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "enter: function");
15041508 }
15051509
1510+ // Forward declaration
1511+ EXEC_DECL(raise);
1512+
15061513 EXEC_DECL(mcall)
15071514 {
15081515 auto target_module = at_val<vm_module_ref_t>(r.dest);
@@ -1547,22 +1554,52 @@ namespace · 38 unmodified lines
15471554 r.mp_base = r.module_ref->mp_base;
15481555 r.next_pc = function_pc;
15491556
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())
15511559 {
1552− const auto &inst = r.module_ref->code_section[function_pc];
1553− assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)");
1560+ r.mp_base->add_ref();
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.
15541570
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+ {
15561579 inst.native(r, vm);
1557− ret(r, vm);
15581580
1559− // Only reset the thread if it is in the state set prior to native call.
1560− if (r.current_thread_state == vm_thread_state_t::release)
1561− r.current_thread_state = vm_thread_state_t::running;
1581+ // Handle callee clean-up semantics
1582+ ret(r, vm);
15621583 }
1563− else
1584+ catch (const marshallable_user_exception &e)
15641585 {
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);
15661603 }
15671604 }
15681605
@@ -1706,10 +1743,23 @@ namespace · 137 unmodified lines
17061743
17071744 if (target_frame != r.stack.peek_frame())
17081745 {
1709− while (target_frame != r.stack.pop_frame())
1746+ // Unwind stack to find target frame
1747+ do
17101748 {
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+ }
17121761 }
1762+ while (target_frame != r.stack.pop_frame());
17131763 }
17141764
17151765 // 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
4545
4646 vm_list_t::~vm_list_t()
4747 {
48− // Remove tail
49− dec_ref_count_and_free(_tail);
50− debug::assign_debug_pointer(&_tail);
51−
5248 // Destroy and free the list element, if default element size was surpassed.
5349 if (_is_alloc)
5450 {
@@ -61,7 +57,13 @@ vm_list_t::~vm_list_t() · 6 unmodified lines
6157 }
6258
6359 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");
6567 }
6668
6769 std::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
1717 #include <vm_memory.h>
1818 #include "Sysmod.h"
1919 #include "sys_utils.h"
20+#include "fd_types.h"
2021
2122 using disvm::vm_t;
2223
@@ -34,8 +35,13 @@ using disvm::runtime::vm_string_t; · 11 unmodified lines
3435 using disvm::runtime::vm_syscall_exception;
3536 using disvm::runtime::vm_system_exception;
3637 using disvm::runtime::vm_user_exception;
38+using disvm::runtime::marshallable_user_exception;
3739 using disvm::runtime::word_t;
3840
41+using disvm::runtime::sys::open_mode_t;
42+using disvm::runtime::sys::seek_start_t;
43+using disvm::runtime::sys::vm_fd_t;
44+
3945 namespace
4046 {
4147 disvm::runtime::builtin::vm_runtab_t Sysmodtab[] = {
@@ -93,12 +99,14 @@ namespace · 51 unmodified lines
9399 std::shared_ptr<const type_descriptor_t> T_Connection;
94100 std::shared_ptr<const type_descriptor_t> T_FileIO;
95101
96− class vm_fd_t final : public Sys_FD
102+ class Sys_FD_Impl final : public Sys_FD
97103 {
98104 public: // static
99105 static void finalizer(vm_alloc_t *fileHandle)
100106 {
101− auto fd = fileHandle->get_allocation<vm_fd_t>();
107+ auto fd = fileHandle->get_allocation<Sys_FD_Impl>();
108+
109+ fd->~Sys_FD_Impl();
102110
103111 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
104112 {
@@ -108,121 +116,51 @@ namespace · 3 unmodified lines
108116 "sys: fd: finalize: %d",
109117 fd->fd);
110118 }
111−
112− disvm::runtime::sys::drop_fd_record(fd->fd);
113− fd->~vm_fd_t();
114119 }
115120
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)
117122 {
118− assert(s != nullptr);
119123 auto alloc = vm_alloc_t::allocate(T_FD);
120− const auto fd = disvm::runtime::sys::create_fd_record(alloc);
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− }
124+ auto fd_impl = ::new(alloc->get_allocation())Sys_FD_Impl{};
125125
126− private:
127− vm_fd_t(const word_t fd, std::unique_ptr<std::iostream> s, const word_t fd_mode, vm_string_t *fd_path)
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);
126+ fd_impl->fd = disvm::runtime::sys::create_fd_record(f);
127+ fd_impl->impl = f;
135128
136− if (_fd_path != nullptr)
137− _fd_path->add_ref();
129+ return alloc;
138130 }
139131
140− public:
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 }
132+ static vm_alloc_t * try_create(word_t fd)
147133 {
148− }
134+ auto vm_fd = disvm::runtime::sys::fetch_fd_record(fd);
135+ if (vm_fd == nullptr)
136+ return nullptr;
149137
150− vm_fd_t(const word_t fd, std::ostream &os)
151− : Sys_FD{ fd }
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" };
138+ auto alloc = vm_alloc_t::allocate(T_FD);
139+ auto fd_impl = ::new(alloc->get_allocation())Sys_FD_Impl{};
188140
189− _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
190− if (_istream->bad())
191− throw vm_syscall_exception{ vm, "Read operation error" };
141+ fd_impl->fd = fd;
142+ fd_impl->impl = vm_fd;
192143
193− return static_cast<word_t>(_istream->gcount());
144+ return alloc;
194145 }
195146
196− void write(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer)
147+ public:
148+ ~Sys_FD_Impl()
197149 {
198− if (_ostream == nullptr)
199− throw vm_system_exception{ "File descriptor not open for write operation" };
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" };
150+ disvm::runtime::sys::drop_fd_record(fd);
151+ disvm::debug::assign_debug_pointer(&impl);
204152 }
205153
206− big_t seek(const std::ios::seekdir start, const big_t offset)
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− }
154+ vm_fd_t *impl;
214155
215156 private:
216− const word_t _fd_mode;
217− vm_string_t *_fd_path;
218− std::istream *_istream;
219− std::ostream *_ostream;
220− std::unique_ptr<std::ios> _stream;
157+ Sys_FD_Impl() = default;
221158 };
222159
223− vm_fd_t *fd_stdin = nullptr;
224− vm_fd_t *fd_stdout = nullptr;
225− vm_fd_t *fd_stderr = nullptr;
160+ // File descriptors for std streams.
161+ word_t fd_stdin = disvm::runtime::sys::vm_invalid_fd;
162+ word_t fd_stdout = disvm::runtime::sys::vm_invalid_fd;
163+ word_t fd_stderr = disvm::runtime::sys::vm_invalid_fd;
226164
227165 word_t printf_to_fd(
228166 disvm::vm_t &vm,
@@ -247,35 +185,41 @@ namespace · 18 unmodified lines
247185 return written;
248186 }
249187
250− std::ios::openmode convert_to_openmode(const word_t mode)
188+ open_mode_t convert_to_openmode(const word_t mode)
251189 {
252− auto om = std::ios::openmode{};
190+ auto om = open_mode_t{};
253191
254192 // Lowest bit is for read/write.
255193 if (mode & Sys_OWRITE)
256− om |= std::ios::out;
194+ om |= open_mode_t::write;
257195 else
258− om |= std::ios::in; // Sys_OREAD
196+ om |= open_mode_t::read; // Sys_OREAD
259197
260198 if (mode & Sys_ORDWR)
261− om |= (std::ios::in | std::ios::out);
199+ om |= (open_mode_t::write | open_mode_t::read);
262200
263201 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;
265209
266210 return om;
267211 }
268212
269− std::ios::seekdir convert_to_seekdir(const word_t start)
213+ seek_start_t convert_to_seekdir(const word_t start)
270214 {
271215 switch (start)
272216 {
273217 case Sys_SEEKSTART:
274− return std::ios::beg;
218+ return seek_start_t::start;
275219 case Sys_SEEKRELA:
276− return std::ios::cur;
220+ return seek_start_t::relative;
277221 case Sys_SEEKEND:
278− return std::ios::end;
222+ return seek_start_t::end;
279223 default:
280224 throw vm_user_exception{ "Invalid seek mode" };
281225 }
@@ -288,32 +232,27 @@ Sysmodinit(void) · 6 unmodified lines
288232 disvm::runtime::builtin::register_module_exports(Sys_PATH, Sysmodlen, Sysmodtab);
289233 T_Qid = type_descriptor_t::create(sizeof(Sys_Qid), sizeof(Sys_Qid_map), Sys_Qid_map);
290234 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);
292236 T_Connection = type_descriptor_t::create(sizeof(Sys_Connection), sizeof(Sys_Connection_map), Sys_Connection_map);
293237 T_FileIO = type_descriptor_t::create(sizeof(Sys_FileIO), sizeof(Sys_FileIO_map), Sys_FileIO_map);
294238
295239 // Initialize default file descriptors
240+ auto std_streams = disvm::runtime::sys::get_std_streams();
296241
297− {
298− auto vm_stdin = vm_alloc_t::allocate(T_FD);
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− }
242+ // Initialize in this order to assign POSIX defined values for the 'standard' streams.
243+ // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html
303244
304− {
305− auto vm_stdout = vm_alloc_t::allocate(T_FD);
306− const auto fd = disvm::runtime::sys::create_fd_record(vm_stdout);
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− }
245+ fd_stdin = disvm::runtime::sys::create_fd_record(std_streams.input);
246+ assert(fd_stdin == 0);
247+ disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stdin: %d", fd_stdin);
310248
311− {
312− auto vm_stderr = vm_alloc_t::allocate(T_FD);
313− const auto fd = disvm::runtime::sys::create_fd_record(vm_stderr);
314− fd_stderr = ::new(vm_stderr->get_allocation())vm_fd_t{ fd, std::cerr };
315− disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stderr: %d", fd);
316− }
249+ fd_stdout = disvm::runtime::sys::create_fd_record(std_streams.output);
250+ assert(fd_stdout == 1);
251+ disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stdout: %d", fd_stdout);
252+
253+ fd_stderr = disvm::runtime::sys::create_fd_record(std_streams.error);
254+ assert(fd_stderr == 2);
255+ disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stderr: %d", fd_stderr);
317256 }
318257
319258 void
@@ -429,40 +368,34 @@ Sys_create(vm_registers_t &r, vm_t &vm) · 109 unmodified lines
429368 {
430369 auto &fp = r.stack.peek_frame()->base<F_Sys_create>();
431370
371+ dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
372+ *fp.ret = nullptr;
373+
432374 auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
433375 const auto open_mode = convert_to_openmode(fp.mode);
434376
435− auto cfs_flag = disvm::runtime::sys::cfs_flags_t::none;
436− if (fp.mode & Sys_OEXCL)
437− cfs_flag = (disvm::runtime::sys::cfs_flags_t::atomic | disvm::runtime::sys::cfs_flags_t::ensure_create);
377+ // [TODO] Apply the requested permissions to the file - fp.perm
378+ auto vm_fd = disvm::runtime::sys::create_from_file_path(path, open_mode);
438379
439− // [TODO] Apply the requested permissions to the file.
440− auto file_stream = disvm::runtime::sys::create_file_stream(path->str(), (open_mode | std::ios::binary), cfs_flag);
441−
442− // Return null if file stream is not open.
443− if (file_stream == nullptr)
444− {
445− *fp.ret = nullptr;
380+ // Return null if file is not open.
381+ if (vm_fd == nullptr)
446382 return;
447− }
448383
449− vm_alloc_t *fd_alloc;
450− auto fd = word_t{};
451− std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path);
384+ auto fd_alloc = Sys_FD_Impl::create(vm_fd);
385+ *fp.ret = fd_alloc->get_allocation();
452386
453387 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
454388 {
389+ auto fd_impl = fd_alloc->get_allocation<Sys_FD_Impl>();
455390 disvm::debug::log_msg(
456391 component_trace_t::builtin,
457392 log_level_t::debug,
458393 "sys: create: %d >>%s<< %#x %#x",
459− fd,
394+ fd_impl->fd,
460395 path->str(),
461396 fp.mode,
462397 fp.perm);
463398 }
464−
465− *fp.ret = fd_alloc->get_allocation();
466399 }
467400
468401 void
@@ -479,11 +412,37 @@ Sys_dirread(vm_registers_t &r, vm_t &vm) · 10 unmodified lines
479412 throw vm_system_exception{ "Function not implemented" };
480413 }
481414
415+namespace
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+
482422 void
483423 Sys_dup(vm_registers_t &r, vm_t &vm)
484424 {
485425 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);
487446 }
488447
489448 void
@@ -521,12 +480,9 @@ Sys_fildes(vm_registers_t &r, vm_t &vm) · 31 unmodified lines
521480 dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
522481 *fp.ret = nullptr;
523482
524− auto fd = disvm::runtime::sys::fetch_fd_record(fp.fd);
525− if (fd != nullptr)
526− {
527− fd->add_ref();
528− *fp.ret = fd->get_allocation();
529− }
483+ auto fd_alloc = Sys_FD_Impl::try_create(fp.fd);
484+ if (fd_alloc != nullptr)
485+ *fp.ret = fd_alloc->get_allocation();
530486 }
531487
532488 void
@@ -546,12 +502,12 @@ Sys_fprint(vm_registers_t &r, vm_t &vm) · 13 unmodified lines
546502 auto &fp = r.stack.peek_frame()->base<F_Sys_fprint>();
547503 auto fd_alloc = vm_alloc_t::from_allocation(fp.fd);
548504 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>();
550506 auto str = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
551507 if (str == nullptr)
552508 throw dereference_nil{ "Print string to FD" };
553509
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);
555511 }
556512
557513 void
@@ -611,35 +567,33 @@ Sys_open(vm_registers_t &r, vm_t &vm) · 53 unmodified lines
611567 {
612568 auto &fp = r.stack.peek_frame()->base<F_Sys_open>();
613569
570+ dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
571+ *fp.ret = nullptr;
572+
614573 auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
615574 const auto open_mode = convert_to_openmode(fp.mode);
616575
617576 // [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);
619578
620− // Return null if file stream is not open.
621− if (file_stream == nullptr)
622− {
623− *fp.ret = nullptr;
579+ // Return null if file is not open.
580+ if (vm_fd == nullptr)
624581 return;
625− }
626582
627− vm_alloc_t *fd_alloc;
628− auto fd = word_t{};
629− std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path);
583+ auto fd_alloc = Sys_FD_Impl::create(vm_fd);
584+ *fp.ret = fd_alloc->get_allocation();
630585
631586 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
632587 {
588+ auto fd_impl = fd_alloc->get_allocation<Sys_FD_Impl>();
633589 disvm::debug::log_msg(
634590 component_trace_t::builtin,
635591 log_level_t::debug,
636592 "sys: open: %d >>%s<< %#x",
637− fd,
593+ fd_impl->fd,
638594 path->str(),
639595 fp.mode);
640596 }
641−
642− *fp.ret = fd_alloc->get_allocation();
643597 }
644598
645599 void
@@ -672,7 +626,11 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 26 unmodified lines
672626 if (str == nullptr)
673627 throw dereference_nil{ "Print string" };
674628
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);
676634 }
677635
678636 void
@@ -708,8 +666,8 @@ Sys_read(vm_registers_t &r, vm_t &vm) · 29 unmodified lines
708666
709667 assert(fd_alloc->alloc_type == T_FD);
710668
711− auto fd = fd_alloc->get_allocation<vm_fd_t>();
712− *fp.ret = fd->read(vm, n, buffer->at(0));
669+ auto fd = fd_alloc->get_allocation<Sys_FD_Impl>();
670+ *fp.ret = fd->impl->read(vm, n, buffer->at(0));
713671 }
714672
715673 void
@@ -742,10 +700,10 @@ Sys_seek(vm_registers_t &r, vm_t &vm) · 26 unmodified lines
742700 throw dereference_nil{ "Seek in file descriptor" };
743701
744702 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>();
746704
747705 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);
749707 }
750708
751709 void
@@ -993,8 +951,8 @@ Sys_write(vm_registers_t &r, vm_t &vm) · 241 unmodified lines
993951
994952 assert(fd_alloc->alloc_type == T_FD);
995953
996− auto fd = fd_alloc->get_allocation<vm_fd_t>();
997− fd->write(vm, n, buffer->at(0));
954+ auto fd = fd_alloc->get_allocation<Sys_FD_Impl>();
955+ fd->impl->write(vm, n, buffer->at(0));
998956
999957 *fp.ret = n;
1000958 }
src/vm/sys/fd_manager.cpp+66−47
@@ -6,7 +6,8 @@ · 5 unmodified lines
66
77 #include <array>
88 #include <mutex>
9−#include <unordered_map>
9+#include <vector>
10+#include <queue>
1011 #include <exceptions.h>
1112 #include <vm_memory.h>
1213 #include "sys_utils.h"
@@ -14,72 +15,90 @@ · 1 unmodified line
1415 using disvm::runtime::vm_alloc_t;
1516 using disvm::runtime::word_t;
1617
18+using disvm::runtime::sys::vm_fd_t;
19+
1720 namespace
1821 {
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+
27+word_t disvm::runtime::sys::create_fd_record(vm_fd_t * vm_fd)
28+{
29+ assert(vm_fd != nullptr);
2030
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
2240 {
23− nullptr, // stdin
24− nullptr, // stdout
25− nullptr, // stderr
26− };
41+ new_fd = fd_available.top();
42+ fd_available.pop();
2743
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+ }
2947
30− std::mutex fd_map_lock;
31− auto fd_map = fd_map_t{};
48+ return new_fd;
3249 }
3350
34−word_t disvm::runtime::sys::create_fd_record(vm_alloc_t * fd_alloc)
51+bool disvm::runtime::sys::try_update_fd_record(word_t fd, vm_fd_t *vm_fd)
3552 {
36− assert(fd_alloc != nullptr);
53+ assert(fd != sys::vm_invalid_fd);
54+ assert(vm_fd != nullptr);
3755
38− {
39− std::lock_guard<std::mutex> lock{ fd_map_lock };
40−
41− auto new_fd = next_fd;
42− next_fd++;
43− if (new_fd < static_cast<word_t>(std_fd.size()))
44− {
45− std_fd[new_fd] = fd_alloc;
46− }
47− else
48− {
49− assert(fd_map.cend() == fd_map.find(new_fd));
50− fd_map[new_fd] = fd_alloc;
51− }
52−
53− return new_fd;
54− }
56+ std::lock_guard<std::mutex> lock{ fd_map_lock };
57+ if (fd < 0 || static_cast<word_t>(fd_map.size()) <= fd)
58+ return false;
59+
60+ auto vm_fd_curr = fd_map[fd];
61+ auto current_ref = vm_fd_curr->release();
62+ if (current_ref == 0)
63+ delete vm_fd_curr;
64+
65+ fd_map[fd] = vm_fd;
66+ return true;
5567 }
5668
57−vm_alloc_t * disvm::runtime::sys::fetch_fd_record(word_t fd)
69+vm_fd_t * disvm::runtime::sys::fetch_fd_record(word_t fd)
5870 {
59− assert(fd != sys::vm_invalid_fd && "Invalid FD value");
71+ assert(fd != sys::vm_invalid_fd);
6072
61− if (0 <= fd && fd < static_cast<word_t>(std_fd.size()))
62− return std_fd[fd];
73+ std::lock_guard<std::mutex> lock{ fd_map_lock };
74+ if (fd < 0 || static_cast<word_t>(fd_map.size()) <= fd)
75+ return nullptr;
6376
64− {
65− std::lock_guard<std::mutex> lock{ fd_map_lock };
66− auto iter = fd_map.find(fd);
67− if (iter == fd_map.cend())
68− return nullptr;
77+ auto vm_fd = fd_map[fd];
78+ if (vm_fd != nullptr)
79+ vm_fd->add_ref();
6980
70− return iter->second;
71− }
81+ return vm_fd;
7282 }
7383
7484 void disvm::runtime::sys::drop_fd_record(word_t fd)
7585 {
76− assert(fd != sys::vm_invalid_fd && "Invalid FD value");
77− assert(fd >= static_cast<word_t>(std_fd.size()) && "Request to drop std file?");
86+ assert(fd != sys::vm_invalid_fd);
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;
7891
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)
7999 {
80− std::lock_guard<std::mutex> lock{ fd_map_lock };
81− auto iter = fd_map.find(fd);
82− if (iter != fd_map.cend())
83− fd_map.erase(iter);
100+ delete vm_fd;
101+ fd_map[fd] = nullptr;
102+ fd_available.push(fd);
84103 }
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+
16+using disvm::vm_t;
17+
18+using disvm::debug::component_trace_t;
19+using disvm::debug::log_level_t;
20+
21+using disvm::runtime::big_t;
22+using disvm::runtime::byte_t;
23+using disvm::runtime::type_descriptor_t;
24+using disvm::runtime::vm_string_t;
25+using disvm::runtime::word_t;
26+using disvm::runtime::vm_system_exception;
27+using disvm::runtime::vm_syscall_exception;
28+using disvm::runtime::vm_user_exception;
29+
30+using disvm::runtime::sys::open_mode_t;
31+using disvm::runtime::sys::seek_start_t;
32+using disvm::runtime::sys::std_streams;
33+using disvm::runtime::sys::vm_fd_t;
34+
35+vm_fd_t::vm_fd_t(std::shared_ptr<const type_descriptor_t> td)
36+ : vm_alloc_t(std::move(td))
37+{ }
38+
39+namespace
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+
55+std::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+
61+namespace
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+
147+namespace
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+
172+vm_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+
209+std_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+
15+namespace 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−
13−namespace
14−{
15− std::mutex _create_file_stream_mutex;
16−}
17−
18−std::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
1212 #include <memory>
1313 #include <disvm.h>
1414 #include <utils.h>
15+#include "fd_types.h"
1516
1617 namespace disvm
1718 {
@@ -38,25 +39,22 @@ namespace disvm · 20 unmodified lines
3839 pointer_t base,
3940 std::vector<char> &buffer);
4041
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−
5242 const word_t vm_invalid_fd = -1;
5343
54− // The first three FD records are optimized for lock-less access.
55− word_t create_fd_record(vm_alloc_t *fd_alloc);
44+ // No reference count updates are performed on supplied vm_fd_t instance.
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);
5650
5751 // 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);
5955
56+ // The associated vm_fd_t instance reference count is decremented
57+ // and will be deleted once all references are released.
6058 void drop_fd_record(word_t fd);
6159 }
6260 }