mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 05:28:31 +09:00
attempting to fix VM reboot bug(2)
This commit is contained in:
@@ -29,8 +29,10 @@ internal object UnsafeHelper {
|
||||
return UnsafePtr(ptr, size, caller)
|
||||
}
|
||||
|
||||
fun memcpy(src: UnsafePtr, fromIndex: Long, dest: UnsafePtr, toIndex: Long, copyLength: Long) =
|
||||
fun memcpy(src: UnsafePtr, fromIndex: Long, dest: UnsafePtr, toIndex: Long, copyLength: Long) {
|
||||
if (src.destroyed || dest.destroyed) return
|
||||
unsafe.copyMemory(src.ptr + fromIndex, dest.ptr + toIndex, copyLength)
|
||||
}
|
||||
fun memcpy(srcAddress: Long, destAddress: Long, copyLength: Long) =
|
||||
unsafe.copyMemory(srcAddress, destAddress, copyLength)
|
||||
fun memcpyRaw(srcObj: Any?, srcPos: Long, destObj: Any?, destPos: Long, len: Long) =
|
||||
@@ -84,81 +86,96 @@ internal class UnsafePtr(pointer: Long, allocSize: Long, private val caller: Any
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun checkNullPtr(index: Long) { // ignore what IDEA says and do inline this
|
||||
//// commenting out because of the suspected (or minor?) performance impact.
|
||||
//// You may break the glass and use this tool when some fucking incomprehensible bugs ("vittujen vitun bugit")
|
||||
//// appear (e.g. getting garbage values when it fucking shouldn't)
|
||||
|
||||
// if (destroyed) { throw DanglingPointerException("The pointer is already destroyed ($this)") }
|
||||
// if (index !in 0 until size) throw AddressOverflowException("Index: $index; alloc size: $size; pointer: ${this}\n${Thread.currentThread().stackTrace.joinToString("\n", limit=10) { " $it" }}")
|
||||
/**
|
||||
* Returns true when the operation should proceed; false when the pointer is destroyed
|
||||
* (so the caller short-circuits to a safe no-op / zero return).
|
||||
*
|
||||
* Why no exception: a JS worker thread that survives killVMenv (because it wasn't
|
||||
* tracked in vm.contexts, e.g. raw java.lang.Thread spawned by JS code) will keep
|
||||
* poking peripheral memory for one or more iterations after dispose(). Letting it
|
||||
* actually call unsafe.putByte on freed memory corrupts the malloc heap and crashes
|
||||
* the JVM with `free_list_checksum_botch`. Returning quietly turns the race into a
|
||||
* harmless no-op until the thread drains.
|
||||
*/
|
||||
private inline fun aliveAt(index: Long): Boolean {
|
||||
if (destroyed) return false
|
||||
if (index < 0 || index >= size) return false
|
||||
return true
|
||||
}
|
||||
|
||||
operator fun get(index: Long): Byte {
|
||||
checkNullPtr(index)
|
||||
if (!aliveAt(index)) return 0
|
||||
return UnsafeHelper.unsafe.getByte(ptr + index)
|
||||
}
|
||||
|
||||
operator fun set(index: Long, value: Byte) {
|
||||
checkNullPtr(index)
|
||||
if (!aliveAt(index)) return
|
||||
UnsafeHelper.unsafe.putByte(ptr + index, value)
|
||||
}
|
||||
|
||||
|
||||
fun getFloatFree(index: Long): Float {
|
||||
checkNullPtr(index)
|
||||
if (!aliveAt(index + 3)) return 0f
|
||||
return UnsafeHelper.unsafe.getFloat(ptr + index)
|
||||
}
|
||||
fun getFloat(unit: Long): Float {
|
||||
checkNullPtr(unit * 4L)
|
||||
return UnsafeHelper.unsafe.getFloat(ptr + (unit * 4L))
|
||||
val idx = unit * 4L
|
||||
if (!aliveAt(idx + 3)) return 0f
|
||||
return UnsafeHelper.unsafe.getFloat(ptr + idx)
|
||||
}
|
||||
|
||||
fun getIntFree(index: Long): Int {
|
||||
checkNullPtr(index)
|
||||
if (!aliveAt(index + 3)) return 0
|
||||
return UnsafeHelper.unsafe.getInt(ptr + index)
|
||||
}
|
||||
fun getInt(unit: Long): Int {
|
||||
checkNullPtr(unit * 4L)
|
||||
return UnsafeHelper.unsafe.getInt(ptr + (unit * 4L))
|
||||
val idx = unit * 4L
|
||||
if (!aliveAt(idx + 3)) return 0
|
||||
return UnsafeHelper.unsafe.getInt(ptr + idx)
|
||||
}
|
||||
|
||||
fun getShortFree(index: Long): Short {
|
||||
checkNullPtr(index)
|
||||
if (!aliveAt(index + 1)) return 0
|
||||
return UnsafeHelper.unsafe.getShort(ptr + index)
|
||||
}
|
||||
fun getShort(unit: Long): Short {
|
||||
checkNullPtr(unit * 2L)
|
||||
return UnsafeHelper.unsafe.getShort(ptr + (unit * 2L))
|
||||
val idx = unit * 2L
|
||||
if (!aliveAt(idx + 1)) return 0
|
||||
return UnsafeHelper.unsafe.getShort(ptr + idx)
|
||||
}
|
||||
|
||||
fun setFloatFree(index: Long, value: Float) {
|
||||
checkNullPtr(index)
|
||||
if (!aliveAt(index + 3)) return
|
||||
UnsafeHelper.unsafe.putFloat(ptr + index, value)
|
||||
}
|
||||
fun setFloat(unit: Long, value: Float) {
|
||||
checkNullPtr(unit * 4L)
|
||||
UnsafeHelper.unsafe.putFloat(ptr + (unit * 4L), value)
|
||||
val idx = unit * 4L
|
||||
if (!aliveAt(idx + 3)) return
|
||||
UnsafeHelper.unsafe.putFloat(ptr + idx, value)
|
||||
}
|
||||
|
||||
fun setIntFree(index: Long, value: Int) {
|
||||
checkNullPtr(index)
|
||||
if (!aliveAt(index + 3)) return
|
||||
UnsafeHelper.unsafe.putInt(ptr + index, value)
|
||||
}
|
||||
fun setInt(unit: Long, value: Int) {
|
||||
checkNullPtr(unit * 4L)
|
||||
UnsafeHelper.unsafe.putInt(ptr + (unit * 4L), value)
|
||||
val idx = unit * 4L
|
||||
if (!aliveAt(idx + 3)) return
|
||||
UnsafeHelper.unsafe.putInt(ptr + idx, value)
|
||||
}
|
||||
|
||||
fun setShortFree(index: Long, value: Short) {
|
||||
checkNullPtr(index)
|
||||
if (!aliveAt(index + 1)) return
|
||||
UnsafeHelper.unsafe.putShort(ptr + index, value)
|
||||
}
|
||||
fun setShortUnit(unit: Long, value: Short) {
|
||||
checkNullPtr(unit * 2L)
|
||||
UnsafeHelper.unsafe.putShort(ptr + (unit * 2L), value)
|
||||
val idx = unit * 2L
|
||||
if (!aliveAt(idx + 1)) return
|
||||
UnsafeHelper.unsafe.putShort(ptr + idx, value)
|
||||
}
|
||||
|
||||
fun fillWith(byte: Byte) {
|
||||
if (destroyed) return
|
||||
UnsafeHelper.unsafe.setMemory(ptr, size, byte)
|
||||
}
|
||||
|
||||
|
||||
@@ -333,6 +333,29 @@ class VM(
|
||||
try { it.join(500L) } catch (_: InterruptedException) { Thread.currentThread().interrupt() }
|
||||
}
|
||||
contexts.clear()
|
||||
|
||||
// Some JS code (e.g. TVDOS) spawns workers that aren't routed through Parallel.attachProgram,
|
||||
// so they never land in `contexts`. We can still find them by walking the JVM thread set and
|
||||
// matching the per-VM suffix that VMRunnerFactory uses for thread names ("…!<vmId>").
|
||||
val suffix = "!${id.text}"
|
||||
val all = arrayOfNulls<Thread>(Thread.activeCount() * 2)
|
||||
val n = Thread.enumerate(all)
|
||||
for (i in 0 until n) {
|
||||
val t = all[i] ?: continue
|
||||
if (t === Thread.currentThread()) continue
|
||||
val name = t.name
|
||||
if (name.endsWith(suffix) || name == "VmRunner:${id.text}") {
|
||||
t.interrupt()
|
||||
}
|
||||
}
|
||||
for (i in 0 until n) {
|
||||
val t = all[i] ?: continue
|
||||
if (t === Thread.currentThread()) continue
|
||||
val name = t.name
|
||||
if (name.endsWith(suffix) || name == "VmRunner:${id.text}") {
|
||||
try { t.join(500L) } catch (_: InterruptedException) { Thread.currentThread().interrupt() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,6 +45,7 @@ class CLCDDisplay(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRoot, vm,
|
||||
shader: ShaderProgram?,
|
||||
uiFBO: FrameBuffer?
|
||||
) {
|
||||
if (disposed) return
|
||||
batch.shader = null
|
||||
batch.inUse {
|
||||
batch.color = Color.WHITE
|
||||
|
||||
@@ -40,6 +40,7 @@ class CharacterLCDdisplay(assetsRoot: String, vm: VM) : GraphicsAdapter(assetsRo
|
||||
shader: ShaderProgram?,
|
||||
uiFBO: FrameBuffer?
|
||||
) {
|
||||
if (disposed) return
|
||||
batch.shader = null
|
||||
batch.inUse {
|
||||
batch.color = Color.WHITE
|
||||
|
||||
@@ -942,7 +942,11 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
||||
try { this.dispose() } catch (_: GdxRuntimeException) {} catch (_: IllegalArgumentException) {}
|
||||
}
|
||||
|
||||
@Volatile var disposed = false; private set
|
||||
|
||||
override fun dispose() {
|
||||
if (disposed) return
|
||||
disposed = true
|
||||
//testTex.dispose()
|
||||
// paletteShader.tryDispose()
|
||||
// textShader.tryDispose()
|
||||
@@ -986,6 +990,11 @@ open class GraphicsAdapter(private val assetsRoot: String, val vm: VM, val confi
|
||||
private val isRefSize = (WIDTH == 560 && HEIGHT == 448)
|
||||
|
||||
open fun render(delta: Float, uiBatch: SpriteBatch, xoff: Float, yoff: Float, flipY: Boolean = false, shader: ShaderProgram? = null, uiFBO: FrameBuffer? = null) {
|
||||
// Bail out if the adapter has already been torn down. Otherwise touching
|
||||
// any of the disposed Pixmaps / Textures / native buffers below would
|
||||
// raise a GdxRuntimeException or SIGSEGV.
|
||||
if (disposed) return
|
||||
|
||||
uiFBO?.end()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user