History

Create configuration struct for VM Add syscall failure exception - Create VM syscall failure message functions Respect the 'r' format verb Create module resolver interface Implement default module resolver interface Implement Sys.fildes() function

arr committed 9 years ago

Commit 232a6591 parent eb06d3d17 files · +416 −124Browse files

Showing the changes to src/vm/vm.cpp (1 of 17 files). Show all

Unified · Split

src/vm/vm.cpp+146−26
@@ -7,6 +7,7 @@ · 6 unmodified lines
7#include <cassert>7#include <cassert>
8#include <memory>8#include <memory>
9#include <algorithm>9#include <algorithm>
10#include <sstream>
10#include <disvm.h>11#include <disvm.h>
11#include <debug.h>12#include <debug.h>
12#include <runtime.h>13#include <runtime.h>
@@ -16,41 +17,107 @@ · 3 unmodified lines
16#include "scheduler.h"17#include "scheduler.h"
17#include "garbage_collector.h"18#include "garbage_collector.h"
18#include "tool_dispatch.h"19#include "tool_dispatch.h"
20#include "module_resolver.h"
19 21
20using namespace disvm;22using namespace disvm;
21using namespace disvm::runtime;23using namespace disvm::runtime;
22 24
25namespace
26{
27 // Ideally this would be custom assembly based on the CPU,
28 // but since this is just for the setting of the last error
29 // message there is already a large cost associated with the
30 // fact an error occurred so this is okay.
31 struct inefficient_spin_lock_t final
32 {
33 inefficient_spin_lock_t(std::atomic_flag &is_lock)
34 : _lf{ is_lock }
35 {
36 while (!_lf.test_and_set())
37 ;
38 }
39
40 ~inefficient_spin_lock_t()
41 {
42 _lf.clear();
43 }
44
45 private:
46 std::atomic_flag &_lf;
47 };
48}
49
50void disvm::runtime::push_syscall_error_message(disvm::vm_t &vm, const char *msg) noexcept
51{
52 inefficient_spin_lock_t sl{ vm._last_syscall_error_message_lock };
53
54 assert(msg != nullptr);
55 auto bytes_to_copy = std::min(std::strlen(msg), vm._last_syscall_error_message.size() - 1);
56 std::memcpy(vm._last_syscall_error_message.data(), msg, bytes_to_copy);
57
58 // Ensure there is always a null
59 vm._last_syscall_error_message[bytes_to_copy] = '\0';
60}
61
62std::string disvm::runtime::pop_syscall_error_message(disvm::vm_t &vm)
63{
64 inefficient_spin_lock_t sl{ vm._last_syscall_error_message_lock };
65
66 std::string msg{ vm._last_syscall_error_message.data() };
67 vm._last_syscall_error_message[0] = '\0';
68 return msg;
69}
70
23const uint32_t vm_t::root_vm_thread_id = 0;71const uint32_t vm_t::root_vm_thread_id = 0;
24const uint32_t vm_t::default_system_thread_count = 1;
25 72
26// The Inferno implementation defined the thread quanta as 2048 (include/interp.h)73// The Inferno implementation defined the thread quanta as 2048 (include/interp.h)
27const uint32_t vm_t::default_thread_quanta = 2048;74const uint32_t default_thread_quanta = 2048;
28 75const uint32_t default_system_thread_count = 1;
29vm_t::vm_t(76
30 create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler,77vm_config_t::vm_config_t()
31 create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc)78 : create_gc{ nullptr }
79 , create_scheduler{ nullptr }
80 , sys_thread_pool_size{ default_system_thread_count }
81 , thread_quanta{ default_thread_quanta }
82{ }
83
84vm_config_t::vm_config_t(vm_config_t &&other)
85 : additional_resolvers{ std::move(other.additional_resolvers) }
86 , create_gc{ other.create_gc }
87 , create_scheduler{ other.create_scheduler }
88 , probing_paths{ std::move(other.probing_paths) }
89 , sys_thread_pool_size{ other.sys_thread_pool_size }
90 , thread_quanta{ other.thread_quanta }
91{ }
92
93vm_t::vm_t()
94 : _last_syscall_error_message_lock{ ATOMIC_FLAG_INIT }
32{95{
33 // Initialize built-in modules.96 // Initialize built-in modules.
34 builtin::initialize_builtin_modules();97 builtin::initialize_builtin_modules();
35 98
36 if (create_gc == nullptr)99 _gc = std::make_unique<default_garbage_collector_t>(*this);
37 _gc = std::make_unique<default_garbage_collector_t>(*this);100 _scheduler = std::make_unique<default_scheduler_t>(*this, default_system_thread_count, default_thread_quanta);
38 else101 _module_resolvers.push_back(std::make_unique<default_resolver_t>(*this));
39 _gc = create_gc(*this);
40
41 if (create_scheduler == nullptr)
42 _scheduler = std::make_unique<default_scheduler_t>(*this, default_system_thread_count, default_thread_quanta);
43 else
44 _scheduler = create_scheduler(*this);
45}102}
46 103
47vm_t::vm_t(uint32_t system_thread_count, uint32_t thread_quanta)104vm_t::vm_t(vm_config_t config)
48{105{
49 // Initialize built-in modules.106 // Initialize built-in modules.
50 builtin::initialize_builtin_modules();107 builtin::initialize_builtin_modules();
51 108
52 _gc = std::make_unique<default_garbage_collector_t>(*this);109 if (config.create_gc == nullptr)
53 _scheduler = std::make_unique<default_scheduler_t>(*this, system_thread_count, thread_quanta);110 _gc = std::make_unique<default_garbage_collector_t>(*this);
111 else
112 _gc = config.create_gc(*this);
113
114 if (config.create_scheduler == nullptr)
115 _scheduler = std::make_unique<default_scheduler_t>(*this, config.sys_thread_pool_size, config.thread_quanta);
116 else
117 _scheduler = config.create_scheduler(*this);
118
119 _module_resolvers = std::move(config.additional_resolvers);
120 _module_resolvers.push_back(std::make_unique<default_resolver_t>(*this, std::move(config.probing_paths)));
54}121}
55 122
56vm_t::~vm_t()123vm_t::~vm_t()
@@ -74,7 +141,7 @@ vm_version_t vm_t::get_version() const · 17 unmodified lines
74 141
75namespace142namespace
76{143{
77 std::unique_ptr<runtime::vm_thread_t> _create_thread_safe(std::unique_ptr<runtime::vm_module_ref_t> &entry_module_ref)144 std::unique_ptr<runtime::vm_thread_t> _create_thread_safe(std::unique_ptr<runtime::vm_module_ref_t> entry_module_ref)
78 {145 {
79 auto thread = std::make_unique<vm_thread_t>(*entry_module_ref, disvm::vm_t::root_vm_thread_id);146 auto thread = std::make_unique<vm_thread_t>(*entry_module_ref, disvm::vm_t::root_vm_thread_id);
80 147
@@ -92,7 +159,7 @@ const runtime::vm_thread_t& vm_t::exec(const char *path) · 11 unmodified lines
92 auto entry_module = load_module(path);159 auto entry_module = load_module(path);
93 160
94 auto entry_module_ref = std::make_unique<vm_module_ref_t>(entry_module);161 auto entry_module_ref = std::make_unique<vm_module_ref_t>(entry_module);
95 auto thread = _create_thread_safe(entry_module_ref);162 auto thread = _create_thread_safe(std::move(entry_module_ref));
96 return schedule_thread(std::move(thread));163 return schedule_thread(std::move(thread));
97}164}
98 165
@@ -101,7 +168,7 @@ const runtime::vm_thread_t& vm_t::exec(std::unique_ptr<runtime::vm_module_t> ent · 2 unmodified lines
101 assert(entry_module != nullptr);168 assert(entry_module != nullptr);
102 169
103 auto entry_module_ref = std::make_unique<vm_module_ref_t>(std::move(entry_module));170 auto entry_module_ref = std::make_unique<vm_module_ref_t>(std::move(entry_module));
104 auto thread = _create_thread_safe(entry_module_ref);171 auto thread = _create_thread_safe(std::move(entry_module_ref));
105 return schedule_thread(std::move(thread));172 return schedule_thread(std::move(thread));
106}173}
107 174
@@ -112,12 +179,64 @@ const runtime::vm_thread_t& vm_t::fork( · 4 unmodified lines
112 runtime::vm_pc_t initial_pc)179 runtime::vm_pc_t initial_pc)
113{180{
114 if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc)181 if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc)
115 throw vm_system_exception{ "Invalid entry program counter" };182 throw vm_user_exception{ "Invalid entry program counter" };
116 183
117 auto thread = std::make_unique<vm_thread_t>(module_ref, parent_tid, initial_frame, initial_pc);184 auto thread = std::make_unique<vm_thread_t>(module_ref, parent_tid, initial_frame, initial_pc);
118 return schedule_thread(std::move(thread));185 return schedule_thread(std::move(thread));
119}186}
120 187
188namespace
189{
190 std::unique_ptr<vm_module_t> resolve_module_from_path(const char *path, const std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> &resolvers)
191 {
192 assert(!resolvers.empty());
193 std::unique_ptr<vm_module_t> resolved_module;
194
195 // Check if the path is for the Inferno OS.
196 const char *inferno_root_path = "/dis/";
197 if (std::strncmp(path, inferno_root_path, sizeof(inferno_root_path)) == 0)
198 {
199 if (debug::is_component_tracing_enabled<debug::component_trace_t::module>())
200 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "load: vm module: Inferno OS path detected - >>%s<<", path);
201
202 // Plus 1 for the next character. We know this will either be
203 // null or valid character since we searched for a string
204 // containing the character above.
205 const char *module_name = std::strrchr(path, '/') + 1;
206
207 if (*module_name != '\0')
208 {
209 for (auto &r : resolvers)
210 {
211 // Since we are using a modified path, throwing an exception
212 // isn't fair. We will catch all exceptions here and rely on
213 // non-modified path resolution to trigger the actual failure.
214 try
215 {
216 if (r->try_resolve_module(module_name, resolved_module))
217 return resolved_module;
218 }
219 catch (...)
220 {
221 // No-op
222 }
223 }
224 }
225 }
226
227 // Attempt to resolve the path
228 for (auto &r : resolvers)
229 {
230 if (r->try_resolve_module(path, resolved_module))
231 return resolved_module;
232 }
233
234 std::stringstream ss;
235 ss << "Failed to resolve path to module: " << path;
236 throw vm_module_exception{ ss.str().c_str() };
237 }
238}
239
121std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path)240std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path)
122{241{
123 if (path == nullptr)242 if (path == nullptr)
@@ -139,12 +258,13 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 15 unmodified lines
139 // If an entry exists but is null, the module was loaded before. Read the module again and return.258 // If an entry exists but is null, the module was loaded before. Read the module again and return.
140 if (module == nullptr)259 if (module == nullptr)
141 {260 {
142 module = std::shared_ptr<vm_module_t>{ std::move(read_module(path)) };261 module = std::shared_ptr<vm_module_t>{ std::move(resolve_module_from_path(path, _module_resolvers)) };
143 module->vm_id = iter->vm_id;262 module->vm_id = iter->vm_id;
144 263
145 iter->module = module;264 iter->module = module;
146 265
147 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<", path);266 if (debug::is_component_tracing_enabled<debug::component_trace_t::module>())
267 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<", path);
148 }268 }
149 269
150 return module;270 return module;
@@ -159,8 +279,8 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 8 unmodified lines
159 }279 }
160 else280 else
161 {281 {
162 // Read in the module282 new_module = resolve_module_from_path(path, _module_resolvers);
163 new_module = std::move(read_module(path));283 assert(new_module != nullptr);
164 }284 }
165 285
166 auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path));286 auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path));