TEVD DOM changes committing to the physical disk

This commit is contained in:
minjaesong
2022-12-18 16:30:06 +09:00
parent 7ebd0a329b
commit 7a349d6192
7 changed files with 88 additions and 49 deletions

View File

@@ -25,6 +25,7 @@ class VM(
val worldInterface: WorldInterface, val worldInterface: WorldInterface,
val roms: Array<VMProgramRom>, // first ROM must contain the BIOS val roms: Array<VMProgramRom>, // first ROM must contain the BIOS
_peripheralSlots: Int = 8, _peripheralSlots: Int = 8,
val watchdogs: HashMap<String, VMWatchdog>
) { ) {
val peripheralSlots = _peripheralSlots.coerceIn(1,8) val peripheralSlots = _peripheralSlots.coerceIn(1,8)

View File

@@ -0,0 +1,56 @@
package net.torvald.tsvm
import com.badlogic.gdx.utils.Queue
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.VDUtil
import net.torvald.terrarum.modulecomputers.virtualcomputer.tvd.VirtualDisk
import java.io.File
/**
* Created by minjaesong on 2022-12-18.
*/
abstract class VMWatchdog {
/** Seconds between sleep */
abstract val interval: Float
protected var akku = 0f
open fun update(delta: Float) {
akku += delta
while (akku > interval) {
consumeMessages()
println("[${this.javaClass.simpleName}] boop!")
akku -= interval
}
}
protected abstract fun consumeMessages()
abstract fun addMessage(message: Array<Any?>)
}
object TevdSyncWatchdog : VMWatchdog() {
override val interval = 5f
private val messageQueue = ArrayList<Pair<File, VirtualDisk>>()
override fun consumeMessages() {
synchronized(this) {
messageQueue.forEach { (outfile, dom) ->
VDUtil.dumpToRealMachine(dom, outfile)
println("[${this.javaClass.simpleName}] dump ${outfile.path}")
}
messageQueue.clear()
}
}
override fun addMessage(message: Array<Any?>) {
val file = message[0] as File
val dom = message[1] as VirtualDisk
val hasDup = messageQueue.fold(false) { acc, pair -> acc or (pair.first.path == file.path) }
if (!hasDup) {
messageQueue.add(file to dom)
}
}
}

View File

@@ -37,42 +37,18 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
private val messageComposeBuffer = ByteArrayOutputStream(BLOCK_SIZE) // always use this and don't alter blockSendBuffer please private val messageComposeBuffer = ByteArrayOutputStream(BLOCK_SIZE) // always use this and don't alter blockSendBuffer please
private var blockSendBuffer = ByteArray(1) private var blockSendBuffer = ByteArray(1)
private var blockSendCount = 0 private var blockSendCount = 0
private val hasChanges = AtomicBoolean(false)
init { init {
statusCode.set(TestDiskDrive.STATE_CODE_STANDBY) statusCode.set(TestDiskDrive.STATE_CODE_STANDBY)
if (!tevdPath.exists()) { if (!tevdPath.exists()) {
throw FileNotFoundException("Disk file '${theTevdPath}' not found") throw FileNotFoundException("Disk file '${theTevdPath}' not found")
} }
Thread {
while (vm.isRunning) {
println("TevdCommitWatchdog ping")
if (hasChanges.compareAndExchangeAcquire(true, false)) {
printdbg("Disk has changes, committing... $theTevdPath")
commit()
}
else {
printdbg("Disk has no changes, skipping... $theTevdPath")
}
Thread.sleep(1000L * COMMIT_INTERVAL)
}
}.let {
it.start()
vm.contexts.add(it)
}
} }
companion object { fun notifyDiskCommit() {
/** How often the changes in DOM (disk object model) should be saved to the physical drive when there are changes. Seconds. */ vm.watchdogs["TEVD_SYNC"]?.addMessage(arrayOf(tevdPath, DOM))
const val COMMIT_INTERVAL = 5
}
fun commit() {
VDUtil.dumpToRealMachine(DOM, tevdPath)
} }
@@ -140,8 +116,8 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
writeMode = false writeMode = false
appendMode = false appendMode = false
printdbg("Raising HasChanges flag (end of write)") printdbg("Notifying disk commit (end of write)")
hasChanges.set(true) notifyDiskCommit()
} }
} }
else { else {
@@ -229,8 +205,8 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
} }
try { try {
file.delete() file.delete()
printdbg("Raising HasChanges flag (file deleted)") printdbg("Notifying disk commit (file deleted)")
hasChanges.set(true) notifyDiskCommit()
} }
catch (e: SecurityException) { catch (e: SecurityException) {
statusCode.set(TestDiskDrive.STATE_CODE_SYSTEM_SECURITY_ERROR) statusCode.set(TestDiskDrive.STATE_CODE_SYSTEM_SECURITY_ERROR)
@@ -365,8 +341,8 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
val status = file.mkdir() val status = file.mkdir()
statusCode.set(if (status) 0 else 1) statusCode.set(if (status) 0 else 1)
if (status) { if (status) {
printdbg("Raising HasChanges flag (mkdir)") printdbg("Notifying disk commit (mkdir)")
hasChanges.set(true) notifyDiskCommit()
} }
} }
catch (e: SecurityException) { catch (e: SecurityException) {
@@ -386,8 +362,8 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
val f1 = file.createNewFile() val f1 = file.createNewFile()
statusCode.set(if (f1) TestDiskDrive.STATE_CODE_STANDBY else TestDiskDrive.STATE_CODE_OPERATION_FAILED) statusCode.set(if (f1) TestDiskDrive.STATE_CODE_STANDBY else TestDiskDrive.STATE_CODE_OPERATION_FAILED)
if (f1) { if (f1) {
printdbg("Raising HasChanges flag (mkfile)") printdbg("Notifying disk commit (mkfile)")
hasChanges.set(true) notifyDiskCommit()
} }
return return
} }
@@ -411,8 +387,8 @@ class TevdDiskDrive(private val vm: VM, private val driveNum: Int, private val t
val f1 = file.setLastModified(vm.worldInterface.currentTimeInMills()) val f1 = file.setLastModified(vm.worldInterface.currentTimeInMills())
statusCode.set(if (f1) TestDiskDrive.STATE_CODE_STANDBY else TestDiskDrive.STATE_CODE_OPERATION_FAILED) statusCode.set(if (f1) TestDiskDrive.STATE_CODE_STANDBY else TestDiskDrive.STATE_CODE_OPERATION_FAILED)
if (f1) { if (f1) {
printdbg("Raising HasChanges flag (touch)") printdbg("Notifying disk commit (touch)")
hasChanges.set(true) notifyDiskCommit()
} }
return return
} }

View File

@@ -15,7 +15,7 @@ import net.torvald.tsvm.peripheral.GraphicsAdapter
class V2kRunTest : ApplicationAdapter() { class V2kRunTest : ApplicationAdapter() {
val vm = VM("./assets", 64.kB(), TheRealWorld(), arrayOf()) val vm = VM("./assets", 64.kB(), TheRealWorld(), arrayOf(), watchdogs = hashMapOf())
lateinit var gpu: GraphicsAdapter lateinit var gpu: GraphicsAdapter
lateinit var batch: SpriteBatch lateinit var batch: SpriteBatch

View File

@@ -8,6 +8,7 @@ import kotlin.collections.CollectionsKt;
import net.torvald.tsvm.peripheral.*; import net.torvald.tsvm.peripheral.*;
import java.io.File; import java.io.File;
import java.util.HashMap;
public class AppLoader { public class AppLoader {
@@ -29,6 +30,8 @@ public class AppLoader {
appConfig.setWindowedMode(WIDTH, HEIGHT); appConfig.setWindowedMode(WIDTH, HEIGHT);
HashMap<String, VMWatchdog> watchdogs = new HashMap<>();
watchdogs.put("TEVD_SYNC", TevdSyncWatchdog.INSTANCE);
String diskPath = "assets/disk0"; String diskPath = "assets/disk0";
@@ -36,11 +39,11 @@ public class AppLoader {
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, BasicRom.INSTANCE}); // VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, BasicRom.INSTANCE});
// VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{OEMBios.INSTANCE, BasicRom.INSTANCE}); // VM vm = new VM(64 << 10, new TheRealWorld(), new VMProgramRom[]{OEMBios.INSTANCE, BasicRom.INSTANCE});
// VM vm = new VM("./assets", 64 << 10, new TheRealWorld(), new VMProgramRom[]{TandemBios.INSTANCE, BasicRom.INSTANCE}, 2); // VM vm = new VM("./assets", 64 << 10, new TheRealWorld(), new VMProgramRom[]{TandemBios.INSTANCE, BasicRom.INSTANCE}, 2, watchdogs);
// VM vm = new VM(128 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, WPBios.INSTANCE}); // VM vm = new VM(128 << 10, new TheRealWorld(), new VMProgramRom[]{BasicBios.INSTANCE, WPBios.INSTANCE});
VM vm = new VM("./assets", 8192 << 10, new TheRealWorld(), new VMProgramRom[]{TsvmBios.INSTANCE}, 8); VM vm = new VM("./assets", 8192 << 10, new TheRealWorld(), new VMProgramRom[]{TsvmBios.INSTANCE}, 8, watchdogs);
// VM vm = new VM("./assets", 8192 << 10, new TheRealWorld(), new VMProgramRom[]{OpenBios.INSTANCE}, 8); // VM vm = new VM("./assets", 8192 << 10, new TheRealWorld(), new VMProgramRom[]{OpenBios.INSTANCE}, 8, watchdogs);
// VM pipvm = new VM("./assets", 4096, new TheRealWorld(), new VMProgramRom[]{PipBios.INSTANCE, PipROM.INSTANCE}, 8); // VM pipvm = new VM("./assets", 4096, new TheRealWorld(), new VMProgramRom[]{PipBios.INSTANCE, PipROM.INSTANCE}, 8, watchdogs);
vm.getIO().getBlockTransferPorts()[0].attachDevice(new TestDiskDrive(vm, 0, diskPath)); vm.getIO().getBlockTransferPorts()[0].attachDevice(new TestDiskDrive(vm, 0, diskPath));
vm.getIO().getBlockTransferPorts()[1].attachDevice(new HttpModem(vm, 1024, -1)); vm.getIO().getBlockTransferPorts()[1].attachDevice(new HttpModem(vm, 1024, -1));

View File

@@ -5,14 +5,10 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.utils.GdxRuntimeException
import com.badlogic.gdx.utils.JsonReader
import com.badlogic.gdx.utils.JsonValue import com.badlogic.gdx.utils.JsonValue
import com.badlogic.gdx.utils.JsonWriter import com.badlogic.gdx.utils.JsonWriter
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import net.torvald.terrarum.FlippingSpriteBatch import net.torvald.terrarum.FlippingSpriteBatch
import net.torvald.terrarum.imagefont.TinyAlphNum import net.torvald.terrarum.imagefont.TinyAlphNum
import net.torvald.terrarum.utils.JsonFetcher import net.torvald.terrarum.utils.JsonFetcher
@@ -64,6 +60,9 @@ class VMEmuExecutableWrapper(val windowWidth: Int, val windowHeight: Int, var pa
*/ */
class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: Int, var panelsY: Int, val diskPathRoot: String) : ApplicationAdapter() { class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX: Int, var panelsY: Int, val diskPathRoot: String) : ApplicationAdapter() {
val TEVD_SYNC = TevdSyncWatchdog
val watchdogs = hashMapOf<String, VMWatchdog>("TEVD_SYNC" to TEVD_SYNC)
private data class VMRunnerInfo(val vm: VM, val name: String) private data class VMRunnerInfo(val vm: VM, val name: String)
@@ -235,7 +234,7 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
super.render() super.render()
val dt = Gdx.graphics.rawDeltaTime val dt = Gdx.graphics.deltaTime
updateAkku += dt updateAkku += dt
var i = 0L var i = 0L
@@ -246,6 +245,8 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
} }
renderGame(dt) renderGame(dt)
watchdogs.forEach { (_, watchdog) -> watchdog.update(dt) }
} }
private fun reboot(vm: VM) { private fun reboot(vm: VM) {
@@ -487,7 +488,7 @@ class VMEmuExecutable(val windowWidth: Int, val windowHeight: Int, var panelsX:
val cardslots = json.getInt("cardslots") val cardslots = json.getInt("cardslots")
val roms = json.get("roms").iterator().map { VMProgramRom(it.asString()) }.toTypedArray() val roms = json.get("roms").iterator().map { VMProgramRom(it.asString()) }.toTypedArray()
val vm = VM(assetsDir, ramsize, TheRealWorld(), roms, cardslots) val vm = VM(assetsDir, ramsize, TheRealWorld(), roms, cardslots, watchdogs)
// install peripherals // install peripherals
listOf("com1", "com2", "com3", "com4").map { json.get(it) }.forEachIndexed { index, jsonValue -> listOf("com1", "com2", "com3", "com4").map { json.get(it) }.forEachIndexed { index, jsonValue ->

View File

@@ -144,7 +144,7 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
super.render() super.render()
val dt = Gdx.graphics.rawDeltaTime val dt = Gdx.graphics.deltaTime
updateAkku += dt updateAkku += dt
var i = 0L var i = 0L
@@ -155,6 +155,8 @@ class VMGUI(val loaderInfo: EmulInstance, val viewportWidth: Int, val viewportHe
} }
renderGame(dt) renderGame(dt)
vm.watchdogs.forEach { (_, watchdog) -> watchdog.update(dt) }
} }
private fun updateGame(delta: Float) { private fun updateGame(delta: Float) {