mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 05:28:31 +09:00
attempting to fix VM reboot bug
This commit is contained in:
@@ -325,7 +325,13 @@ class VM(
|
||||
}
|
||||
|
||||
fun killAllContexts() {
|
||||
contexts.forEach { it.interrupt() }
|
||||
// Snapshot first: interrupt() can race with the worker thread mutating `contexts`
|
||||
// (see Parallel.kill / attachProgram) and we want to wait on every one of them.
|
||||
val snapshot = contexts.toList()
|
||||
snapshot.forEach { it.interrupt() }
|
||||
snapshot.forEach {
|
||||
try { it.join(500L) } catch (_: InterruptedException) { Thread.currentThread().interrupt() }
|
||||
}
|
||||
contexts.clear()
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,16 @@ object VMSetupBroker {
|
||||
* @param coroutineJobs Hashmap on the host of VMs that holds the coroutine-job object for the currently running VM-instance. Key: Int(VM's identifier), value: [kotlin.coroutines.Job]
|
||||
*/
|
||||
fun initVMenv(vm: VM, profileJson: JsonValue, profileName: String, gpu: GraphicsAdapter, vmRunners: HashMap<VmId, VMRunner>, coroutineJobs: HashMap<VmId, Thread>, whatToDoOnVmException: (Throwable) -> Unit) {
|
||||
// Refuse to start a new runner while the previous one is still alive:
|
||||
// running both concurrently would race on the VM's memory / IO and lead
|
||||
// to mixed text input, garbled rendering, and SIGSEGV on disposed peripherals.
|
||||
coroutineJobs[vm.id]?.let { old ->
|
||||
if (old.isAlive) {
|
||||
System.err.println("[VMSetupBroker] previous runner for ${vm.id} is still alive; tearing it down before re-init")
|
||||
killVMenv(vm, vmRunners, coroutineJobs)
|
||||
}
|
||||
}
|
||||
|
||||
vm.init()
|
||||
|
||||
try {
|
||||
@@ -61,9 +71,38 @@ object VMSetupBroker {
|
||||
*/
|
||||
fun killVMenv(vm: VM, vmRunners: HashMap<VmId, VMRunner>, coroutineJobs: HashMap<VmId, Thread>) {
|
||||
|
||||
// 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.
|
||||
// context.close(true) cancels in-flight script evaluation.
|
||||
val runnerThread = coroutineJobs[vm.id]
|
||||
runnerThread?.interrupt()
|
||||
try { vmRunners[vm.id]?.close() } catch (_: Throwable) {}
|
||||
|
||||
// 3. Wait for the main runner thread to actually finish.
|
||||
if (runnerThread != null && runnerThread !== Thread.currentThread()) {
|
||||
try {
|
||||
runnerThread.join(2000L)
|
||||
if (runnerThread.isAlive) {
|
||||
// Last resort: re-interrupt and accept that disposal will
|
||||
// happen with the thread still alive. This is logged so
|
||||
// diagnostics surface a stuck VM rather than failing silently.
|
||||
System.err.println("[VMSetupBroker] runner ${vm.id} did not exit within 2s; proceeding anyway")
|
||||
runnerThread.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()
|
||||
@@ -71,8 +110,9 @@ object VMSetupBroker {
|
||||
catch (_: Throwable) {}
|
||||
}
|
||||
|
||||
coroutineJobs[vm.id]?.interrupt()
|
||||
vmRunners[vm.id]?.close()
|
||||
// 5. Drop runner / job handles so a subsequent initVMenv won't see stale entries.
|
||||
vmRunners.remove(vm.id)
|
||||
coroutineJobs.remove(vm.id)
|
||||
|
||||
vm.getPrintStream = { TODO() }
|
||||
vm.getErrorStream = { TODO() }
|
||||
|
||||
@@ -1362,8 +1362,12 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
||||
textCursorIsOn = !textCursorIsOn
|
||||
}
|
||||
|
||||
// force light cursor up while typing
|
||||
textCursorIsOn = textCursorIsOn || ((1..254).any { Gdx.input.isKeyPressed(it) })
|
||||
// force light cursor up while typing -- only honour global key state when
|
||||
// this VM is the focused viewport; otherwise hidden VMs would react to
|
||||
// keypresses meant for the focused one.
|
||||
if (Gdx.input.inputProcessor === vm.getIO()) {
|
||||
textCursorIsOn = textCursorIsOn || ((1..254).any { Gdx.input.isKeyPressed(it) })
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -285,24 +285,35 @@ class IOSpace(val vm: VM) : PeriBase("io"), InputProcessor {
|
||||
private var rtc = 0L
|
||||
|
||||
fun update(delta: Float) {
|
||||
// Only the VM whose IOSpace is wired up as the active InputProcessor (i.e. the
|
||||
// currently focused viewport) may observe global keyboard/mouse state. Otherwise
|
||||
// hidden VMs would all see the same keypresses as the focused one.
|
||||
val isFocused = Gdx.input.inputProcessor === this
|
||||
|
||||
if (rawInputFunctionLatched) {
|
||||
rawInputFunctionLatched = false
|
||||
|
||||
// store mouse info
|
||||
mouseX = (Gdx.input.x + guiPosX).toShort()
|
||||
mouseY = (Gdx.input.y + guiPosY).toShort()
|
||||
mouseDown = Gdx.input.isTouched
|
||||
|
||||
// strobe keys to fill the key read buffer
|
||||
var keysPushed = 0
|
||||
keyEventBuffers.fill(0)
|
||||
for (k in 1..254) {
|
||||
if (Gdx.input.isKeyPressed(k)) {
|
||||
keyEventBuffers[keysPushed] = k.toByte()
|
||||
keysPushed += 1
|
||||
}
|
||||
|
||||
if (keysPushed >= 8) break
|
||||
if (isFocused) {
|
||||
// store mouse info
|
||||
mouseX = (Gdx.input.x + guiPosX).toShort()
|
||||
mouseY = (Gdx.input.y + guiPosY).toShort()
|
||||
mouseDown = Gdx.input.isTouched
|
||||
|
||||
// strobe keys to fill the key read buffer
|
||||
var keysPushed = 0
|
||||
for (k in 1..254) {
|
||||
if (Gdx.input.isKeyPressed(k)) {
|
||||
keyEventBuffers[keysPushed] = k.toByte()
|
||||
keysPushed += 1
|
||||
}
|
||||
|
||||
if (keysPushed >= 8) break
|
||||
}
|
||||
}
|
||||
else {
|
||||
mouseDown = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,26 +327,33 @@ class IOSpace(val vm: VM) : PeriBase("io"), InputProcessor {
|
||||
rtc = vm.worldInterface.currentTimeInMills()
|
||||
}
|
||||
|
||||
// SIGTERM key combination: Ctrl+Shift+T+R
|
||||
vm.stopDown = (Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.T) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.R)) || Gdx.input.isKeyPressed(Input.Keys.PAUSE)
|
||||
if (vm.stopDown) println("[VM-${vm.id}] SIGTERM requested")
|
||||
if (isFocused) {
|
||||
// SIGTERM key combination: Ctrl+Shift+T+R
|
||||
vm.stopDown = (Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.T) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.R)) || Gdx.input.isKeyPressed(Input.Keys.PAUSE)
|
||||
if (vm.stopDown) println("[VM-${vm.id}] SIGTERM requested")
|
||||
|
||||
// RESET key combination: Ctrl+Shift+R+S
|
||||
vm.resetDown = Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.R) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.S)
|
||||
if (vm.resetDown) println("[VM-${vm.id}] RESET requested")
|
||||
// RESET key combination: Ctrl+Shift+R+S
|
||||
vm.resetDown = Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.R) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.S)
|
||||
if (vm.resetDown) println("[VM-${vm.id}] RESET requested")
|
||||
|
||||
// SYSRQ key combination: Ctrl+Shift+S+Q
|
||||
vm.sysrqDown = (Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.Q) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.S)) || Gdx.input.isKeyPressed(Input.Keys.PRINT_SCREEN)
|
||||
if (vm.sysrqDown) println("[VM-${vm.id}] SYSRQ requested")
|
||||
// SYSRQ key combination: Ctrl+Shift+S+Q
|
||||
vm.sysrqDown = (Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.Q) &&
|
||||
Gdx.input.isKeyPressed(Input.Keys.S)) || Gdx.input.isKeyPressed(Input.Keys.PRINT_SCREEN)
|
||||
if (vm.sysrqDown) println("[VM-${vm.id}] SYSRQ requested")
|
||||
}
|
||||
else {
|
||||
vm.stopDown = false
|
||||
vm.resetDown = false
|
||||
vm.sysrqDown = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun touchUp(p0: Int, p1: Int, p2: Int, p3: Int): Boolean {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.6 KiB |
Reference in New Issue
Block a user