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
77 #include <cassert>
88 #include <memory>
99 #include <algorithm>
10+#include <sstream>
1011 #include <disvm.h>
1112 #include <debug.h>
1213 #include <runtime.h>
@@ -16,41 +17,107 @@ · 3 unmodified lines
1617 #include "scheduler.h"
1718 #include "garbage_collector.h"
1819 #include "tool_dispatch.h"
20+#include "module_resolver.h"
1921
2022 using namespace disvm;
2123 using namespace disvm::runtime;
2224
25+namespace
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+
50+void 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+
62+std::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+
2371 const uint32_t vm_t::root_vm_thread_id = 0;
24−const uint32_t vm_t::default_system_thread_count = 1;
2572
2673 // The Inferno implementation defined the thread quanta as 2048 (include/interp.h)
27−const uint32_t vm_t::default_thread_quanta = 2048;
28−
29−vm_t::vm_t(
30− create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler,
31− create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc)
74+const uint32_t default_thread_quanta = 2048;
75+const uint32_t default_system_thread_count = 1;
76+
77+vm_config_t::vm_config_t()
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+
84+vm_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+
93+vm_t::vm_t()
94+ : _last_syscall_error_message_lock{ ATOMIC_FLAG_INIT }
3295 {
3396 // Initialize built-in modules.
3497 builtin::initialize_builtin_modules();
3598
36− if (create_gc == nullptr)
37− _gc = std::make_unique<default_garbage_collector_t>(*this);
38− else
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);
99+ _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);
101+ _module_resolvers.push_back(std::make_unique<default_resolver_t>(*this));
45102 }
46103
47−vm_t::vm_t(uint32_t system_thread_count, uint32_t thread_quanta)
104+vm_t::vm_t(vm_config_t config)
48105 {
49106 // Initialize built-in modules.
50107 builtin::initialize_builtin_modules();
51108
52− _gc = std::make_unique<default_garbage_collector_t>(*this);
53− _scheduler = std::make_unique<default_scheduler_t>(*this, system_thread_count, thread_quanta);
109+ if (config.create_gc == nullptr)
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)));
54121 }
55122
56123 vm_t::~vm_t()
@@ -74,7 +141,7 @@ vm_version_t vm_t::get_version() const · 17 unmodified lines
74141
75142 namespace
76143 {
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)
78145 {
79146 auto thread = std::make_unique<vm_thread_t>(*entry_module_ref, disvm::vm_t::root_vm_thread_id);
80147
@@ -92,7 +159,7 @@ const runtime::vm_thread_t& vm_t::exec(const char *path) · 11 unmodified lines
92159 auto entry_module = load_module(path);
93160
94161 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));
96163 return schedule_thread(std::move(thread));
97164 }
98165
@@ -101,7 +168,7 @@ const runtime::vm_thread_t& vm_t::exec(std::unique_ptr<runtime::vm_module_t> ent · 2 unmodified lines
101168 assert(entry_module != nullptr);
102169
103170 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));
105172 return schedule_thread(std::move(thread));
106173 }
107174
@@ -112,12 +179,64 @@ const runtime::vm_thread_t& vm_t::fork( · 4 unmodified lines
112179 runtime::vm_pc_t initial_pc)
113180 {
114181 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" };
116183
117184 auto thread = std::make_unique<vm_thread_t>(module_ref, parent_tid, initial_frame, initial_pc);
118185 return schedule_thread(std::move(thread));
119186 }
120187
188+namespace
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+
121240 std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path)
122241 {
123242 if (path == nullptr)
@@ -139,12 +258,13 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 15 unmodified lines
139258 // If an entry exists but is null, the module was loaded before. Read the module again and return.
140259 if (module == nullptr)
141260 {
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)) };
143262 module->vm_id = iter->vm_id;
144263
145264 iter->module = module;
146265
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);
148268 }
149269
150270 return module;
@@ -159,8 +279,8 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 8 unmodified lines
159279 }
160280 else
161281 {
162− // Read in the module
163− new_module = std::move(read_module(path));
282+ new_module = resolve_module_from_path(path, _module_resolvers);
283+ assert(new_module != nullptr);
164284 }
165285
166286 auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path));