mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 13:38:30 +09:00
attempting to fix VM reboot bug
This commit is contained in:
@@ -90,6 +90,11 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
var vmRunners = HashMap<VmId, VMRunner>() // <VM's identifier, VMRunner>
|
||||
var coroutineJobs = HashMap<VmId, Thread>() // <VM's identifier, Job>
|
||||
|
||||
// Per-VM rising-edge latch for the RESET key combo (Ctrl+Shift+R+S). The reboot
|
||||
// only fires when the user releases the keys, otherwise we'd restart-spam every
|
||||
// frame while the combo is held.
|
||||
private val rebootLatched = HashMap<VmId, Boolean>()
|
||||
|
||||
internal val whatToDoOnVmExceptionQueue = ArrayList<() -> Unit>()
|
||||
|
||||
companion object {
|
||||
@@ -288,15 +293,20 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
}
|
||||
|
||||
private fun reboot(profileName: String) {
|
||||
val vm = currentlyLoadedProfiles[profileName]!!
|
||||
val vm = currentlyLoadedProfiles[profileName] ?: return
|
||||
|
||||
/*vmRunners[vm.id]!!.close()
|
||||
coroutineJobs[vm.id]!!.interrupt()
|
||||
// Tear down the old session (joins the runner thread, then disposes
|
||||
// peripherals) before spinning up a new one. Without the join, the old
|
||||
// JS thread races the new one on shared VM memory / IO state.
|
||||
killVMenv(vm)
|
||||
initVMenv(vm, profileName)
|
||||
|
||||
vm.init()
|
||||
initVMenv(vm, profileName)*/
|
||||
|
||||
// hypervisor will take over by monitoring MMIO addr 48
|
||||
// The old IOSpace was kept (peripheralTable[0] survives init/kill), so
|
||||
// the InputProcessor reference is still valid; just make sure the
|
||||
// currently focused viewport is still wired to it.
|
||||
if (currentVMselection != null && vms[currentVMselection!!]?.vm?.id == vm.id) {
|
||||
Gdx.input.inputProcessor = vm.getIO()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateGame(delta: Float) {
|
||||
@@ -312,8 +322,27 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
|
||||
}
|
||||
|
||||
vms.forEachIndexed { index, it ->
|
||||
if (it?.vm?.resetDown == true && index == currentVMselection) { reboot(it.profileName) }
|
||||
if (it?.vm?.isRunning == true) it?.vm?.update(delta)
|
||||
if (it == null) return@forEachIndexed
|
||||
val vmId = it.vm.id
|
||||
|
||||
// Trigger reboot on the *release* edge of the RESET key combo, and
|
||||
// only for the focused viewport (resetDown for a hidden VM is
|
||||
// already kept false by IOSpace.update; this guard is belt-and-braces).
|
||||
if (index == currentVMselection) {
|
||||
if (it.vm.resetDown) {
|
||||
rebootLatched[vmId] = true
|
||||
}
|
||||
else if (rebootLatched[vmId] == true) {
|
||||
rebootLatched[vmId] = false
|
||||
reboot(it.profileName)
|
||||
return@forEachIndexed // VM was just rebuilt; skip the update tick
|
||||
}
|
||||
}
|
||||
else {
|
||||
rebootLatched[vmId] = false
|
||||
}
|
||||
|
||||
if (it.vm.isRunning) it.vm.update(delta)
|
||||
}
|
||||
|
||||
updateMenu()
|
||||
|
||||
@@ -172,8 +172,34 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
|
||||
private fun killVMenv() {
|
||||
if (vmKilled.compareAndSet(0, System.currentTimeMillis())) {
|
||||
System.err.println("VMGUI is killing VM environment...")
|
||||
|
||||
// Order is critical: stop ALL execution first, then dispose peripherals.
|
||||
// If we disposed peripherals while the runner thread is still alive, the
|
||||
// thread would touch destroyed UnsafePtrs and SIGSEGV.
|
||||
|
||||
// 1. Stop parallel/child contexts. park() interrupts and joins them.
|
||||
vm.park()
|
||||
vm.poke(-90L, -128)
|
||||
|
||||
// 2. Interrupt the main runner thread and cancel the GraalVM context.
|
||||
if (::coroutineJob.isInitialized) coroutineJob.interrupt()
|
||||
try { if (::vmRunner.isInitialized) vmRunner.close() } catch (_: Throwable) {}
|
||||
|
||||
// 3. Wait for the main runner thread to actually finish.
|
||||
if (::coroutineJob.isInitialized && coroutineJob !== Thread.currentThread()) {
|
||||
try {
|
||||
coroutineJob.join(2000L)
|
||||
if (coroutineJob.isAlive) {
|
||||
System.err.println("[VMGUI] runner ${vm.id} did not exit within 2s; proceeding anyway")
|
||||
coroutineJob.interrupt()
|
||||
}
|
||||
}
|
||||
catch (_: InterruptedException) {
|
||||
Thread.currentThread().interrupt()
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Now it's safe to release native resources held by peripherals.
|
||||
for (i in 1 until vm.peripheralTable.size) {
|
||||
try {
|
||||
vm.peripheralTable[i].peripheral?.dispose()
|
||||
@@ -181,8 +207,7 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
|
||||
catch (_: Throwable) {
|
||||
}
|
||||
}
|
||||
coroutineJob.interrupt()
|
||||
vmRunner.close()
|
||||
|
||||
vm.getPrintStream = { TODO() }
|
||||
vm.getErrorStream = { TODO() }
|
||||
vm.getInputStream = { TODO() }
|
||||
@@ -195,12 +220,12 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
|
||||
private var rebootRequested = false
|
||||
|
||||
private fun reboot() {
|
||||
/*vmRunner.close()
|
||||
coroutineJob.interrupt()
|
||||
|
||||
init()*/
|
||||
|
||||
// hypervisor will take over by monitoring MMIO addr 48
|
||||
// Tear down the old session (joins the runner thread, then disposes
|
||||
// peripherals) before re-initialising. Without the join, the old JS
|
||||
// thread races the new one on shared VM memory / IO state and can
|
||||
// SIGSEGV on disposed peripherals.
|
||||
killVMenv()
|
||||
init()
|
||||
}
|
||||
|
||||
private var updateAkku = 0.0
|
||||
|
||||
Reference in New Issue
Block a user