File desc impl change
Unified · Split
| @@ -124,6 +124,15 @@ namespace disvm · 123 unmodified lines | ||
| 124 | 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 | 136 | class unhandled_user_exception final : public vm_user_exception |
| 128 | 137 | { |
| 129 | 138 | public: |
| @@ -76,12 +76,12 @@ namespace disvm · 75 unmodified lines | ||
| 76 | 76 | |
| 77 | 77 | // Adopted from 'winnt.h' in the Windows SDK |
| 78 | 78 | #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)); } \ | |
| 80 | 80 | 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)); } \ | |
| 82 | 82 | 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)); } \ | |
| 84 | 84 | 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)); } | |
| 86 | 86 | |
| 87 | 87 | #endif // _DISVM_SRC_INCLUDE_UTILS_H_ |
| @@ -57,7 +57,7 @@ namespace disvm · 56 unmodified lines | ||
| 57 | 57 | template<typename CB> |
| 58 | 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 | 62 | auto memory = reinterpret_cast<word_t *>(data); |
| 63 | 63 | for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8) |
| @@ -26,8 +26,8 @@ · 25 unmodified lines | ||
| 26 | 26 | 'math/Mathmod.cpp', |
| 27 | 27 | 'sys/print.cpp', |
| 28 | 28 | 'sys/Sysmod.cpp', |
| 29 | − 'sys/file_system.cpp', | |
| 30 | 29 | 'sys/fd_manager.cpp', |
| 30 | + 'sys/fd_types.cpp', | |
| 31 | 31 | ], |
| 32 | 32 | 'include_dirs': [ |
| 33 | 33 | '../include' |
| @@ -164,7 +164,7 @@ namespace · 163 unmodified lines | ||
| 164 | 164 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 165 | 165 | |
| 166 | 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 | 168 | pt_ref(r.dest) = new_array->get_allocation(); |
| 169 | 169 | } |
| 170 | 170 | |
| @@ -537,7 +537,7 @@ namespace · 366 unmodified lines | ||
| 537 | 537 | auto type_id = vt_ref<word_t>(r.mid); |
| 538 | 538 | const auto &types = r.module_ref->type_section; |
| 539 | 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 | 542 | disvm::runtime::inc_ref_count_in_memory(*type, r.src); |
| 543 | 543 | destroy_memory(*type, r.dest); |
| @@ -853,7 +853,7 @@ namespace · 309 unmodified lines | ||
| 853 | 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 | 858 | if (arr->alloc_type->map_in_bytes > 0) |
| 859 | 859 | vm.get_garbage_collector().track_allocation(new_array); |
| @@ -971,14 +971,15 @@ namespace · 111 unmodified lines | ||
| 971 | 971 | auto tail_maybe = at_val<vm_list_t>(r.dest); |
| 972 | 972 | |
| 973 | 973 | // 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 | 976 | if (tail_maybe != nullptr) |
| 976 | 977 | tail_maybe->release(); |
| 977 | 978 | |
| 978 | 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 | 983 | vm.get_garbage_collector().track_allocation(new_list); |
| 983 | 984 | |
| 984 | 985 | // Return the list |
| @@ -1329,9 +1330,10 @@ namespace · 344 unmodified lines | ||
| 1329 | 1330 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 1330 | 1331 | |
| 1331 | 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 | 1337 | vm.get_garbage_collector().track_allocation(new_alloc); |
| 1336 | 1338 | |
| 1337 | 1339 | pt_ref(r.dest) = new_alloc->get_allocation(); |
| @@ -1363,9 +1365,10 @@ namespace · 25 unmodified lines | ||
| 1363 | 1365 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 1364 | 1366 | |
| 1365 | 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 | 1372 | vm.get_garbage_collector().track_allocation(new_alloc); |
| 1370 | 1373 | |
| 1371 | 1374 | pt_ref(r.dest) = new_alloc->get_allocation(); |
| @@ -1385,9 +1388,10 @@ namespace · 13 unmodified lines | ||
| 1385 | 1388 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 1386 | 1389 | |
| 1387 | 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 | 1395 | vm.get_garbage_collector().track_allocation(new_array); |
| 1392 | 1396 | |
| 1393 | 1397 | pt_ref(r.dest) = new_array->get_allocation(); |
| @@ -1437,7 +1441,7 @@ namespace · 43 unmodified lines | ||
| 1437 | 1441 | |
| 1438 | 1442 | const auto frame_type_id = vt_ref<word_t>(r.src); |
| 1439 | 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 | 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 | 1461 | const auto frame_type_id = function_ref.frame_type; |
| 1458 | 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 | 1468 | EXEC_DECL(ret) |
| @@ -1503,6 +1507,9 @@ namespace · 38 unmodified lines | ||
| 1503 | 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 | 1513 | EXEC_DECL(mcall) |
| 1507 | 1514 | { |
| 1508 | 1515 | auto target_module = at_val<vm_module_ref_t>(r.dest); |
| @@ -1547,22 +1554,52 @@ namespace · 38 unmodified lines | ||
| 1547 | 1554 | r.mp_base = r.module_ref->mp_base; |
| 1548 | 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]; | |
| 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. | |
| 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 | 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. | |
| 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); | |
| 1562 | 1583 | } |
| 1563 | − else | |
| 1584 | + 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 | 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 | 1765 | // Re-initialize the current frame |
| @@ -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 | |
| 46 | 46 | vm_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 | 48 | // Destroy and free the list element, if default element size was surpassed. |
| 53 | 49 | if (_is_alloc) |
| 54 | 50 | { |
| @@ -61,7 +57,13 @@ vm_list_t::~vm_list_t() · 6 unmodified lines | ||
| 61 | 57 | } |
| 62 | 58 | |
| 63 | 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 | |
| 67 | 69 | std::shared_ptr<const type_descriptor_t> vm_list_t::get_element_type() const |
| @@ -17,6 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <vm_memory.h> |
| 18 | 18 | #include "Sysmod.h" |
| 19 | 19 | #include "sys_utils.h" |
| 20 | +#include "fd_types.h" | |
| 20 | 21 | |
| 21 | 22 | using disvm::vm_t; |
| 22 | 23 | |
| @@ -34,8 +35,13 @@ using disvm::runtime::vm_string_t; · 11 unmodified lines | ||
| 34 | 35 | using disvm::runtime::vm_syscall_exception; |
| 35 | 36 | using disvm::runtime::vm_system_exception; |
| 36 | 37 | using disvm::runtime::vm_user_exception; |
| 38 | +using disvm::runtime::marshallable_user_exception; | |
| 37 | 39 | using disvm::runtime::word_t; |
| 38 | 40 | |
| 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 | + | |
| 39 | 45 | namespace |
| 40 | 46 | { |
| 41 | 47 | disvm::runtime::builtin::vm_runtab_t Sysmodtab[] = { |
| @@ -93,12 +99,14 @@ namespace · 51 unmodified lines | ||
| 93 | 99 | std::shared_ptr<const type_descriptor_t> T_Connection; |
| 94 | 100 | std::shared_ptr<const type_descriptor_t> T_FileIO; |
| 95 | 101 | |
| 96 | − class vm_fd_t final : public Sys_FD | |
| 102 | + class Sys_FD_Impl final : public Sys_FD | |
| 97 | 103 | { |
| 98 | 104 | public: // static |
| 99 | 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 | 111 | if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>()) |
| 104 | 112 | { |
| @@ -108,121 +116,51 @@ namespace · 3 unmodified lines | ||
| 108 | 116 | "sys: fd: finalize: %d", |
| 109 | 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 | 123 | 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{}; | |
| 125 | 125 | |
| 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; | |
| 135 | 128 | |
| 136 | − if (_fd_path != nullptr) | |
| 137 | − _fd_path->add_ref(); | |
| 129 | + return alloc; | |
| 138 | 130 | } |
| 139 | 131 | |
| 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) | |
| 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) | |
| 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{}; | |
| 188 | 140 | |
| 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; | |
| 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) | |
| 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); | |
| 204 | 152 | } |
| 205 | 153 | |
| 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; | |
| 214 | 155 | |
| 215 | 156 | 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; | |
| 221 | 158 | }; |
| 222 | 159 | |
| 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; | |
| 226 | 164 | |
| 227 | 165 | word_t printf_to_fd( |
| 228 | 166 | disvm::vm_t &vm, |
| @@ -247,35 +185,41 @@ namespace · 18 unmodified lines | ||
| 247 | 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 | 192 | // Lowest bit is for read/write. |
| 255 | 193 | if (mode & Sys_OWRITE) |
| 256 | − om |= std::ios::out; | |
| 194 | + om |= open_mode_t::write; | |
| 257 | 195 | else |
| 258 | − om |= std::ios::in; // Sys_OREAD | |
| 196 | + om |= open_mode_t::read; // Sys_OREAD | |
| 259 | 197 | |
| 260 | 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 | 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 | 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 | 215 | switch (start) |
| 272 | 216 | { |
| 273 | 217 | case Sys_SEEKSTART: |
| 274 | − return std::ios::beg; | |
| 218 | + return seek_start_t::start; | |
| 275 | 219 | case Sys_SEEKRELA: |
| 276 | − return std::ios::cur; | |
| 220 | + return seek_start_t::relative; | |
| 277 | 221 | case Sys_SEEKEND: |
| 278 | − return std::ios::end; | |
| 222 | + return seek_start_t::end; | |
| 279 | 223 | default: |
| 280 | 224 | throw vm_user_exception{ "Invalid seek mode" }; |
| 281 | 225 | } |
| @@ -288,32 +232,27 @@ Sysmodinit(void) · 6 unmodified lines | ||
| 288 | 232 | disvm::runtime::builtin::register_module_exports(Sys_PATH, Sysmodlen, Sysmodtab); |
| 289 | 233 | T_Qid = type_descriptor_t::create(sizeof(Sys_Qid), sizeof(Sys_Qid_map), Sys_Qid_map); |
| 290 | 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 | 236 | T_Connection = type_descriptor_t::create(sizeof(Sys_Connection), sizeof(Sys_Connection_map), Sys_Connection_map); |
| 293 | 237 | T_FileIO = type_descriptor_t::create(sizeof(Sys_FileIO), sizeof(Sys_FileIO_map), Sys_FileIO_map); |
| 294 | 238 | |
| 295 | 239 | // Initialize default file descriptors |
| 240 | + auto std_streams = disvm::runtime::sys::get_std_streams(); | |
| 296 | 241 | |
| 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 | |
| 303 | 244 | |
| 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); | |
| 310 | 248 | |
| 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); | |
| 317 | 256 | } |
| 318 | 257 | |
| 319 | 258 | void |
| @@ -429,40 +368,34 @@ Sys_create(vm_registers_t &r, vm_t &vm) · 109 unmodified lines | ||
| 429 | 368 | { |
| 430 | 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 | 374 | auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s); |
| 433 | 375 | const auto open_mode = convert_to_openmode(fp.mode); |
| 434 | 376 | |
| 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); | |
| 438 | 379 | |
| 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) | |
| 446 | 382 | return; |
| 447 | − } | |
| 448 | 383 | |
| 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(); | |
| 452 | 386 | |
| 453 | 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 | 390 | disvm::debug::log_msg( |
| 456 | 391 | component_trace_t::builtin, |
| 457 | 392 | log_level_t::debug, |
| 458 | 393 | "sys: create: %d >>%s<< %#x %#x", |
| 459 | − fd, | |
| 394 | + fd_impl->fd, | |
| 460 | 395 | path->str(), |
| 461 | 396 | fp.mode, |
| 462 | 397 | fp.perm); |
| 463 | 398 | } |
| 464 | − | |
| 465 | − *fp.ret = fd_alloc->get_allocation(); | |
| 466 | 399 | } |
| 467 | 400 | |
| 468 | 401 | void |
| @@ -479,11 +412,37 @@ Sys_dirread(vm_registers_t &r, vm_t &vm) · 10 unmodified lines | ||
| 479 | 412 | throw vm_system_exception{ "Function not implemented" }; |
| 480 | 413 | } |
| 481 | 414 | |
| 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 | + | |
| 482 | 422 | void |
| 483 | 423 | Sys_dup(vm_registers_t &r, vm_t &vm) |
| 484 | 424 | { |
| 485 | 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 | |
| 489 | 448 | void |
| @@ -521,12 +480,9 @@ Sys_fildes(vm_registers_t &r, vm_t &vm) · 31 unmodified lines | ||
| 521 | 480 | dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret)); |
| 522 | 481 | *fp.ret = nullptr; |
| 523 | 482 | |
| 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(); | |
| 530 | 486 | } |
| 531 | 487 | |
| 532 | 488 | void |
| @@ -546,12 +502,12 @@ Sys_fprint(vm_registers_t &r, vm_t &vm) · 13 unmodified lines | ||
| 546 | 502 | auto &fp = r.stack.peek_frame()->base<F_Sys_fprint>(); |
| 547 | 503 | auto fd_alloc = vm_alloc_t::from_allocation(fp.fd); |
| 548 | 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 | 506 | auto str = vm_alloc_t::from_allocation<vm_string_t>(fp.s); |
| 551 | 507 | if (str == nullptr) |
| 552 | 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 | |
| 557 | 513 | void |
| @@ -611,35 +567,33 @@ Sys_open(vm_registers_t &r, vm_t &vm) · 53 unmodified lines | ||
| 611 | 567 | { |
| 612 | 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 | 573 | auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s); |
| 615 | 574 | const auto open_mode = convert_to_openmode(fp.mode); |
| 616 | 575 | |
| 617 | 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. | |
| 621 | − if (file_stream == nullptr) | |
| 622 | − { | |
| 623 | − *fp.ret = nullptr; | |
| 579 | + // Return null if file is not open. | |
| 580 | + if (vm_fd == nullptr) | |
| 624 | 581 | return; |
| 625 | − } | |
| 626 | 582 | |
| 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(); | |
| 630 | 585 | |
| 631 | 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 | 589 | disvm::debug::log_msg( |
| 634 | 590 | component_trace_t::builtin, |
| 635 | 591 | log_level_t::debug, |
| 636 | 592 | "sys: open: %d >>%s<< %#x", |
| 637 | − fd, | |
| 593 | + fd_impl->fd, | |
| 638 | 594 | path->str(), |
| 639 | 595 | fp.mode); |
| 640 | 596 | } |
| 641 | − | |
| 642 | − *fp.ret = fd_alloc->get_allocation(); | |
| 643 | 597 | } |
| 644 | 598 | |
| 645 | 599 | void |
| @@ -672,7 +626,11 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 26 unmodified lines | ||
| 672 | 626 | if (str == nullptr) |
| 673 | 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 | |
| 678 | 636 | void |
| @@ -708,8 +666,8 @@ Sys_read(vm_registers_t &r, vm_t &vm) · 29 unmodified lines | ||
| 708 | 666 | |
| 709 | 667 | assert(fd_alloc->alloc_type == T_FD); |
| 710 | 668 | |
| 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)); | |
| 713 | 671 | } |
| 714 | 672 | |
| 715 | 673 | void |
| @@ -742,10 +700,10 @@ Sys_seek(vm_registers_t &r, vm_t &vm) · 26 unmodified lines | ||
| 742 | 700 | throw dereference_nil{ "Seek in file descriptor" }; |
| 743 | 701 | |
| 744 | 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 | 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 | |
| 751 | 709 | void |
| @@ -993,8 +951,8 @@ Sys_write(vm_registers_t &r, vm_t &vm) · 241 unmodified lines | ||
| 993 | 951 | |
| 994 | 952 | assert(fd_alloc->alloc_type == T_FD); |
| 995 | 953 | |
| 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)); | |
| 998 | 956 | |
| 999 | 957 | *fp.ret = n; |
| 1000 | 958 | } |
| @@ -6,7 +6,8 @@ · 5 unmodified lines | ||
| 6 | 6 | |
| 7 | 7 | #include <array> |
| 8 | 8 | #include <mutex> |
| 9 | −#include <unordered_map> | |
| 9 | +#include <vector> | |
| 10 | +#include <queue> | |
| 10 | 11 | #include <exceptions.h> |
| 11 | 12 | #include <vm_memory.h> |
| 12 | 13 | #include "sys_utils.h" |
| @@ -14,72 +15,90 @@ · 1 unmodified line | ||
| 14 | 15 | using disvm::runtime::vm_alloc_t; |
| 15 | 16 | using disvm::runtime::word_t; |
| 16 | 17 | |
| 18 | +using disvm::runtime::sys::vm_fd_t; | |
| 19 | + | |
| 17 | 20 | namespace |
| 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 | + | |
| 27 | +word_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, // stdin | |
| 24 | − nullptr, // stdout | |
| 25 | − nullptr, // stderr | |
| 26 | − }; | |
| 41 | + new_fd = fd_available.top(); | |
| 42 | + fd_available.pop(); | |
| 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; | |
| 31 | − auto fd_map = fd_map_t{}; | |
| 48 | + return new_fd; | |
| 32 | 49 | } |
| 33 | 50 | |
| 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) | |
| 35 | 52 | { |
| 36 | − assert(fd_alloc != nullptr); | |
| 53 | + assert(fd != sys::vm_invalid_fd); | |
| 54 | + assert(vm_fd != nullptr); | |
| 37 | 55 | |
| 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; | |
| 55 | 67 | } |
| 56 | 68 | |
| 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) | |
| 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())) | |
| 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; | |
| 63 | 76 | |
| 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(); | |
| 69 | 80 | |
| 70 | − return iter->second; | |
| 71 | − } | |
| 81 | + return vm_fd; | |
| 72 | 82 | } |
| 73 | 83 | |
| 74 | 84 | void disvm::runtime::sys::drop_fd_record(word_t fd) |
| 75 | 85 | { |
| 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; | |
| 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 }; | |
| 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); | |
| 84 | 103 | } |
| 85 | −} | |
| 104 | +} | |
| @@ -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 | +} | |
| @@ -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_ | |
| @@ -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 | −} | |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <memory> |
| 13 | 13 | #include <disvm.h> |
| 14 | 14 | #include <utils.h> |
| 15 | +#include "fd_types.h" | |
| 15 | 16 | |
| 16 | 17 | namespace disvm |
| 17 | 18 | { |
| @@ -38,25 +39,22 @@ namespace disvm · 20 unmodified lines | ||
| 38 | 39 | pointer_t base, |
| 39 | 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 | 42 | const word_t vm_invalid_fd = -1; |
| 53 | 43 | |
| 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); | |
| 56 | 50 | |
| 57 | 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 | 58 | void drop_fd_record(word_t fd); |
| 61 | 59 | } |
| 62 | 60 | } |