Showing the changes to src/vm/thread.cpp (1 of 11 files). Show all
Unified · Split
| @@ -54,6 +54,7 @@ vm_registers_t::vm_registers_t( · 53 unmodified lines | ||
| 54 | 54 | const vm_thread_t &thread, |
| 55 | 55 | vm_module_ref_t &entry) |
| 56 | 56 | : current_thread_state{ vm_thread_state_t::ready } |
| 57 | + , current_thread_quanta{ 0 } | |
| 57 | 58 | , module_ref{ &entry } |
| 58 | 59 | , mp_base{ entry.mp_base } |
| 59 | 60 | , thread{ thread } |
| @@ -163,64 +164,89 @@ vm_thread_t::~vm_thread_t() · 103 unmodified lines | ||
| 163 | 164 | debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "destroy: vm thread: %d %d\n", _thread_id, _parent_thread_id); |
| 164 | 165 | } |
| 165 | 166 | |
| 166 | −vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) | |
| 167 | +namespace | |
| 167 | 168 | { |
| 168 | − _registers.current_thread_state = vm_thread_state_t::running; | |
| 169 | − assert(_registers.pc != runtime_constants::invalid_program_counter); | |
| 170 | − | |
| 171 | − auto execution_duration = uint32_t{ 0 }; | |
| 172 | − | |
| 173 | − try | |
| 169 | + struct execute_with_tool_t | |
| 174 | 170 | { |
| 175 | − for (; execution_duration < quanta; ++execution_duration) | |
| 171 | + void begin_exec_loop(vm_registers_t ®isters, vm_t &) | |
| 176 | 172 | { |
| 177 | − if (_registers.stack.peek_frame() == nullptr) | |
| 178 | − { | |
| 179 | − if (debug::is_component_tracing_enabled<debug::component_trace_t::thread>()) | |
| 180 | − debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "exit: vm thread: stack exhausted: %d\n", _thread_id); | |
| 173 | + auto tool_dispatch = registers.tool_dispatch.load(); | |
| 174 | + assert(tool_dispatch != nullptr); | |
| 181 | 175 | |
| 182 | − _registers.current_thread_state = vm_thread_state_t::empty_stack; | |
| 176 | + tool_dispatch->block_if_thread_suspended(registers.thread.get_thread_id()); | |
| 177 | + } | |
| 178 | + }; | |
| 183 | 179 | |
| 184 | − auto tool_dispatch = _registers.tool_dispatch.load(); | |
| 185 | − if (tool_dispatch != nullptr) | |
| 186 | − tool_dispatch->on_thread_end(*this); | |
| 180 | + struct execute_normal_t | |
| 181 | + { | |
| 182 | + void begin_exec_loop(vm_registers_t &, vm_t &) { } | |
| 183 | + }; | |
| 187 | 184 | |
| 188 | − break; | |
| 189 | − } | |
| 185 | + template<typename EXEC_DETOUR> | |
| 186 | + void execute_impl(vm_registers_t ®isters, vm_t &vm) | |
| 187 | + { | |
| 188 | + EXEC_DETOUR detour{}; | |
| 189 | + auto &execution_duration = registers.current_thread_quanta; | |
| 190 | + for (; 0 != execution_duration; --execution_duration) | |
| 191 | + { | |
| 192 | + detour.begin_exec_loop(registers, vm); | |
| 193 | + assert(registers.stack.peek_frame() != nullptr && "Thread state should not be running with empty stack"); | |
| 190 | 194 | |
| 191 | − const auto &code_section = _registers.module_ref->code_section; | |
| 192 | − assert(static_cast<std::size_t>(_registers.pc) < code_section.size()); | |
| 195 | + const auto &code_section = registers.module_ref->code_section; | |
| 196 | + assert(static_cast<std::size_t>(registers.pc) < code_section.size()); | |
| 193 | 197 | |
| 194 | − assert(!_registers.module_ref->is_builtin_module() && "Interpreter thread is unable to execute native instructions"); | |
| 195 | − const auto &inst = code_section[_registers.pc].op; | |
| 198 | + assert(!registers.module_ref->is_builtin_module() && "Interpreter thread is unable to execute native instructions"); | |
| 199 | + const auto &inst = code_section[registers.pc].op; | |
| 196 | 200 | const auto opcode = static_cast<std::size_t>(inst.opcode); |
| 197 | 201 | if (static_cast<std::size_t>(opcode_t::last_opcode) < opcode) |
| 198 | 202 | throw vm_system_exception{ "Unknown op-code" }; |
| 199 | 203 | |
| 200 | 204 | // Instruction is valid, decode and update registers for operation |
| 201 | − decode_address(inst, _registers); | |
| 202 | − _registers.pc++; | |
| 203 | − vm_exec_table[opcode](_registers, vm); | |
| 205 | + decode_address(inst, registers); | |
| 206 | + registers.pc++; | |
| 207 | + vm_exec_table[opcode](registers, vm); | |
| 204 | 208 | |
| 205 | − if (_registers.current_thread_state != vm_thread_state_t::running) | |
| 209 | + if (registers.current_thread_state != vm_thread_state_t::running) | |
| 206 | 210 | break; |
| 207 | 211 | } |
| 208 | 212 | } |
| 213 | +} | |
| 214 | + | |
| 215 | +vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) | |
| 216 | +{ | |
| 217 | + _registers.current_thread_state = vm_thread_state_t::running; | |
| 218 | + _registers.current_thread_quanta = quanta; | |
| 219 | + assert(_registers.pc != runtime_constants::invalid_program_counter); | |
| 220 | + auto tool_dispatch = _registers.tool_dispatch.load(); | |
| 221 | + | |
| 222 | + try | |
| 223 | + { | |
| 224 | + if (tool_dispatch == nullptr) | |
| 225 | + execute_impl<execute_normal_t>(_registers, vm); | |
| 226 | + else | |
| 227 | + execute_impl<execute_with_tool_t>(_registers, vm); | |
| 228 | + } | |
| 209 | 229 | catch (const unhandled_user_exception &uue) |
| 210 | 230 | { |
| 211 | 231 | // [TODO] Store the exception |
| 212 | 232 | std::fprintf(stderr, "%s - '%s' in %s at instruction %d\n", uue.what(), uue.exception_id, uue.module_name, uue.program_counter); |
| 213 | 233 | _registers.current_thread_state = vm_thread_state_t::broken; |
| 234 | + if (tool_dispatch != nullptr) | |
| 235 | + tool_dispatch->on_thread_broken(*this); | |
| 214 | 236 | } |
| 215 | 237 | catch (const index_out_of_range_memory &ioor) |
| 216 | 238 | { |
| 217 | 239 | std::fprintf(stderr, "%s - %d [%d,%d]\n", ioor.what(), ioor.invalid_value, ioor.valid_min, ioor.valid_max); |
| 218 | 240 | _registers.current_thread_state = vm_thread_state_t::broken; |
| 241 | + if (tool_dispatch != nullptr) | |
| 242 | + tool_dispatch->on_thread_broken(*this); | |
| 219 | 243 | } |
| 220 | 244 | catch (const vm_user_exception &ue) |
| 221 | 245 | { |
| 222 | 246 | std::fprintf(stderr, "%s\n", ue.what()); |
| 223 | 247 | _registers.current_thread_state = vm_thread_state_t::broken; |
| 248 | + if (tool_dispatch != nullptr) | |
| 249 | + tool_dispatch->on_thread_broken(*this); | |
| 224 | 250 | } |
| 225 | 251 | |
| 226 | 252 | // Log the execution result |
| @@ -231,14 +257,22 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 4 unmodified lines | ||
| 231 | 257 | debug::log_level_t::debug, |
| 232 | 258 | "status: vm thread: execute: %d %d %d %d\n", |
| 233 | 259 | _thread_id, |
| 234 | − execution_duration, | |
| 260 | + (quanta - _registers.current_thread_quanta), | |
| 235 | 261 | quanta, |
| 236 | 262 | _registers.current_thread_state); |
| 237 | 263 | } |
| 238 | 264 | |
| 239 | − // If the thread is still in the running state transition it back to ready. | |
| 265 | + // If the thread is still in the running state transition it back to its entry state. | |
| 240 | 266 | if (_registers.current_thread_state == vm_thread_state_t::running) |
| 241 | − _registers.current_thread_state = vm_thread_state_t::ready; | |
| 267 | + { | |
| 268 | + _registers.current_thread_state = (tool_dispatch == nullptr) ? vm_thread_state_t::ready : vm_thread_state_t::debug; | |
| 269 | + } | |
| 270 | + else if (vm_thread_state_t::exiting == _registers.current_thread_state | |
| 271 | + || vm_thread_state_t::empty_stack == _registers.current_thread_state) | |
| 272 | + { | |
| 273 | + if (tool_dispatch != nullptr) | |
| 274 | + tool_dispatch->on_thread_end(*this); | |
| 275 | + } | |
| 242 | 276 | |
| 243 | 277 | return _registers.current_thread_state; |
| 244 | 278 | } |