Make VM lifecycle events allow being used to sync up device state pre/post resume.

This commit is contained in:
Florian Nücke
2021-01-08 20:02:05 +01:00
parent c12b662253
commit ef895a4bf9
4 changed files with 39 additions and 15 deletions

View File

@@ -1,6 +1,29 @@
package li.cil.oc2.api.bus.device.vm;
public enum VMDeviceLifecycleEventType {
/**
* Fired exactly once, when the VM first starts running.
* <p>
* Fired after all devices reported success from {@link VMDevice#load(VMContext)}.
* <p>
* If a running VM is restored from a saved state, this event will not be fired. It is
* intended for initializing the VM state on boot, e.g. by loading initial executable
* code into memory.
* <p>
* <em>This is invoked from the worker thread running the VM.</em>
*/
INITIALIZING,
/**
* Fired when the VM is paused, typically before state is persisted.
* <p>
* Allows devices that offer interaction to external code-flow to suspend
* such interactions until {@link #RESUMED_RUNNING} is fired. This is required
* if such interactions may modify VM state, to prevent corrupting data being
* serialized asynchronously.
*/
PAUSING,
/**
* Fired when the VM resumes running.
* <p>
@@ -13,17 +36,16 @@ public enum VMDeviceLifecycleEventType {
RESUME_RUNNING,
/**
* Fired exactly once, when the VM first starts running.
* Fired when the VM resumed running.
* <p>
* Fired after all devices reported success from {@link VMDevice#load(VMContext)}.
* Fired after {@link #RESUME_RUNNING} has been fired and handled by all devices.
* <p>
* If a running VM is restored from a saved state, this event will not be fired. It is
* intended for initializing the VM state on boot, e.g. by loading initial executable
* code into memory.
* Allows device initialization that relies on all other devices having fully loaded.
* <p>
* <em>This is invoked from the worker thread running the VM.</em>
* Typically this is used in combination with {@link #PAUSING}, to re-enable external
* interactions after VM state is guaranteed to be safe to modify again.
*/
INITIALIZE,
RESUMED_RUNNING,
/**
* Fired when the device is disposed, either because the VM is disposed or the source

View File

@@ -64,7 +64,7 @@ public final class ByteBufferFlashMemoryVMDevice extends IdentityProxy<ItemStack
@Override
public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) {
switch (event) {
case INITIALIZE:
case INITIALIZING:
// TODO Have start address passed with event?
copyDataToMemory(0x80000000L);
break;

View File

@@ -30,7 +30,7 @@ public final class FirmwareFlashMemoryVMDevice extends IdentityProxy<ItemStack>
@Override
public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) {
switch (event) {
case INITIALIZE:
case INITIALIZING:
// TODO Have start address passed with event?
firmware.run(memoryMap, 0x80000000L);
break;

View File

@@ -407,18 +407,19 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
joinVirtualMachine();
tag.put(TERMINAL_TAG_NAME, NBTSerialization.serialize(terminal));
tag.put(BUS_ELEMENT_TAG_NAME, NBTSerialization.serialize(busElement));
tag.put(VIRTUAL_MACHINE_TAG_NAME, NBTSerialization.serialize(virtualMachine));
if (runner != null) {
tag.put(RUNNER_TAG_NAME, NBTSerialization.serialize(runner));
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.PAUSING);
runner.scheduleResumeEvent(); // Allow synchronizing to async device saves.
} else {
NBTUtils.putEnum(tag, RUN_STATE_TAG_NAME, runState);
}
tag.put(TERMINAL_TAG_NAME, NBTSerialization.serialize(terminal));
tag.put(BUS_ELEMENT_TAG_NAME, NBTSerialization.serialize(busElement));
tag.put(VIRTUAL_MACHINE_TAG_NAME, NBTSerialization.serialize(virtualMachine));
final CompoundNBT items = new CompoundNBT();
items.put(MEMORY_TAG_NAME, memoryItemHandler.serializeNBT());
items.put(HARD_DRIVE_TAG_NAME, hardDriveItemHandler.serializeNBT());
@@ -739,12 +740,13 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic
protected void handleBeforeRun() {
if (!firedInitializationEvent) {
firedInitializationEvent = true;
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.INITIALIZE);
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.INITIALIZING);
}
if (!firedResumeEvent) {
firedResumeEvent = true;
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.RESUME_RUNNING);
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.RESUMED_RUNNING);
}
int value;