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