Made vm lifecycle events use an event bus and event classes.
Allows passing along program start so that firmware doesn't have to hardcode this. Also made unload a method instead of an event, as it needs to be called on individual objects anyway; previous implementation was a little awkward.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package li.cil.oc2.api.bus.device.data;
|
||||
|
||||
import li.cil.oc2.api.API;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMInitializationException;
|
||||
import li.cil.sedna.api.memory.MemoryMap;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
@@ -34,7 +35,7 @@ public interface Firmware extends IForgeRegistryEntry<Firmware> {
|
||||
* @param memory access to the memory map of the machine.
|
||||
* @param startAddress the memory address where execution will commence.
|
||||
*/
|
||||
void run(final MemoryMap memory, final long startAddress);
|
||||
boolean run(final MemoryMap memory, final long startAddress);
|
||||
|
||||
/**
|
||||
* The display name of this firmware. May be shown in the tooltip of item devices
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package li.cil.oc2.api.bus.device.vm;
|
||||
|
||||
import li.cil.oc2.api.bus.DeviceBus;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMLifecycleEventBus;
|
||||
import li.cil.sedna.api.device.InterruptController;
|
||||
import li.cil.sedna.api.device.MemoryMappedDevice;
|
||||
import li.cil.sedna.api.memory.MemoryMap;
|
||||
@@ -81,4 +82,15 @@ public interface VMContext {
|
||||
* @return the memory allocator.
|
||||
*/
|
||||
MemoryAllocator getMemoryAllocator();
|
||||
|
||||
/**
|
||||
* Allows registering to VM lifecycle events.
|
||||
* <p>
|
||||
* Registered subscribers will automatically be unsubscribed when the {@link VMDevice}
|
||||
* that registered them is unloaded, e.g. because it is removed from the {@link DeviceBus}
|
||||
* of the VM stopped.
|
||||
*
|
||||
* @return the event bus.
|
||||
*/
|
||||
VMLifecycleEventBus getEventBus();
|
||||
}
|
||||
|
||||
@@ -12,11 +12,9 @@ import li.cil.sedna.api.device.MemoryMappedDevice;
|
||||
* implemented through this interface will require explicit driver support
|
||||
* in the guest system.
|
||||
* <p>
|
||||
* To listen to lifecycle events of the VM and the device, implement the
|
||||
* {@link VMDeviceLifecycleListener} interface. This is particularly useful
|
||||
* for releasing unmanaged resources acquired in {@link #load(VMContext)}.
|
||||
* To listen to lifecycle events of the VM and the device, register to the event
|
||||
* bus provided via {@link VMContext#getEventBus()} in {@link #load(VMContext)}.
|
||||
*
|
||||
* @see VMDeviceLifecycleListener
|
||||
* @see li.cil.oc2.api.bus.device.provider.BlockDeviceProvider
|
||||
* @see li.cil.oc2.api.bus.device.provider.ItemDeviceProvider
|
||||
*/
|
||||
@@ -36,4 +34,14 @@ public interface VMDevice extends Device {
|
||||
* @return {@code true} if the device was loaded successfully; {@code false} otherwise.
|
||||
*/
|
||||
VMDeviceLoadResult load(VMContext context);
|
||||
|
||||
/**
|
||||
* Called when the device is removed from the context it was loaded with.
|
||||
* <p>
|
||||
* This can happen because the VM was stopped or the device was removed from
|
||||
* the device bus that connected it to the VM, for example.
|
||||
* <p>
|
||||
* Intended for releasing resources acquired in {@link #load(VMContext)}.
|
||||
*/
|
||||
void unload();
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
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>
|
||||
* Fired after all devices reported success from {@link VMDevice#load(VMContext)}.
|
||||
* <p>
|
||||
* Fired on initial boot-up as well as when the VM resumes after being restored
|
||||
* from a saved state as well as when continuing to run after being paused for
|
||||
* a save. It is intended for awaiting asynchronous load and store operations.
|
||||
*/
|
||||
RESUME_RUNNING,
|
||||
|
||||
/**
|
||||
* Fired when the VM resumed running.
|
||||
* <p>
|
||||
* Fired after {@link #RESUME_RUNNING} has been fired and handled by all devices.
|
||||
* <p>
|
||||
* Allows device initialization that relies on all other devices having fully loaded.
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
RESUMED_RUNNING,
|
||||
|
||||
/**
|
||||
* Fired when the device is disposed, either because the VM is disposed or the source
|
||||
* of the device is disconnected / removed from the current VM.
|
||||
* <p>
|
||||
* Intended for releasing resources acquired in {@link VMDevice#load(VMContext)}.
|
||||
*/
|
||||
UNLOAD,
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package li.cil.oc2.api.bus.device.vm;
|
||||
|
||||
/**
|
||||
* Specialization of {@link VMDevice} for devices that require additional
|
||||
* lifecycle events to function correctly.
|
||||
* <p>
|
||||
* A typical use case is to add custom synchronization points for asynchronous
|
||||
* operations, such as loading blobs, and for disposing unmanaged resources
|
||||
* acquired in {@link VMDevice#load(VMContext)}.
|
||||
*/
|
||||
public interface VMDeviceLifecycleListener extends VMDevice {
|
||||
/**
|
||||
* Called to notify the device of a lifecycle event.
|
||||
*
|
||||
* @param event the type of the event.
|
||||
*/
|
||||
void handleLifecycleEvent(VMDeviceLifecycleEventType event);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package li.cil.oc2.api.bus.device.vm.event;
|
||||
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public final class VMInitializationException extends Exception {
|
||||
private final ITextComponent message;
|
||||
|
||||
public VMInitializationException(final ITextComponent message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public VMInitializationException() {
|
||||
this.message = null;
|
||||
}
|
||||
|
||||
public Optional<ITextComponent> getErrorMessage() {
|
||||
return Optional.ofNullable(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package li.cil.oc2.api.bus.device.vm.event;
|
||||
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* Listeners of this event may throw a {@link VMInitializationException} in case
|
||||
* initialization fails. For some devices it may be too costly to perform a full
|
||||
* validity check in {@link VMDevice#load(VMContext)}. These devices may still cause
|
||||
* a startup to fail this way.
|
||||
* <p>
|
||||
* <em>This is invoked from the worker thread running the VM.</em>
|
||||
*/
|
||||
public final class VMInitializingEvent extends VMLifecycleEvent {
|
||||
private final long programStartAddress;
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
public VMInitializingEvent(final long programStartAddress) {
|
||||
this.programStartAddress = programStartAddress;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* The address where code execution will begin.
|
||||
* <p>
|
||||
* Some VM implementations may perform some early setup before jumping to this
|
||||
* memory address.
|
||||
*
|
||||
* @return the memory address where code execution begins.
|
||||
*/
|
||||
public long getProgramStartAddress() {
|
||||
return programStartAddress;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package li.cil.oc2.api.bus.device.vm.event;
|
||||
|
||||
public class VMLifecycleEvent {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package li.cil.oc2.api.bus.device.vm.event;
|
||||
|
||||
public interface VMLifecycleEventBus {
|
||||
void register(Object object);
|
||||
|
||||
void unregister(Object object);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package li.cil.oc2.api.bus.device.vm.event;
|
||||
|
||||
/**
|
||||
* 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 VMResumedRunningEvent} is fired. This is required
|
||||
* if such interactions may modify VM state, to prevent corrupting data being
|
||||
* serialized asynchronously.
|
||||
*/
|
||||
public final class VMPausingEvent extends VMLifecycleEvent {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package li.cil.oc2.api.bus.device.vm.event;
|
||||
|
||||
/**
|
||||
* Fired when the VM resumed running.
|
||||
* <p>
|
||||
* Fired after {@link VMResumingRunningEvent} has been fired and handled by all devices.
|
||||
* <p>
|
||||
* Allows device initialization that relies on all other devices having fully loaded.
|
||||
* <p>
|
||||
* Typically this is used in combination with {@link VMPausingEvent}, to re-enable external
|
||||
* interactions after VM state is guaranteed to be safe to modify again.
|
||||
*/
|
||||
public final class VMResumedRunningEvent extends VMLifecycleEvent {
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package li.cil.oc2.api.bus.device.vm.event;
|
||||
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
|
||||
/**
|
||||
* Fired when the VM resumes running.
|
||||
* <p>
|
||||
* Fired after all devices reported success from {@link VMDevice#load(VMContext)}.
|
||||
* <p>
|
||||
* Fired on initial boot-up as well as when the VM resumes after being restored
|
||||
* from a saved state as well as when continuing to run after being paused for
|
||||
* a save. It is intended for awaiting asynchronous load and store operations.
|
||||
*/
|
||||
public final class VMResumingRunningEvent extends VMLifecycleEvent {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.api.bus.device.vm.event;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -67,7 +67,8 @@ public final class Constants {
|
||||
public static final String COMPUTER_SCREEN_POWER_CAPTION = "gui.oc2.computer.power.capt";
|
||||
public static final String COMPUTER_SCREEN_POWER_DESCRIPTION = "gui.oc2.computer.power.desc";
|
||||
public static final String COMPUTER_BOOT_ERROR_UNKNOWN = "gui.oc2.computer.boot_error.unknown";
|
||||
public static final String COMPUTER_BOOT_ERROR_NO_MEMORY = "gui.oc2.computer.boot_error.no_memory";
|
||||
public static final String COMPUTER_BOOT_ERROR_MISSING_FIRMWARE = "gui.oc2.computer.boot_error.missing_firmware";
|
||||
public static final String COMPUTER_BOOT_ERROR_INSUFFICIENT_MEMORY = "gui.oc2.computer.boot_error.insufficient_memory";
|
||||
public static final String COMPUTER_BUS_STATE_INCOMPLETE = "gui.oc2.computer.bus_state.incomplete";
|
||||
public static final String COMPUTER_BUS_STATE_TOO_COMPLEX = "gui.oc2.computer.bus_state.too_complex";
|
||||
public static final String COMPUTER_BUS_STATE_MULTIPLE_CONTROLLERS = "gui.oc2.computer.bus_state.multiple_controllers";
|
||||
|
||||
@@ -7,21 +7,18 @@ import li.cil.sedna.memory.MemoryMaps;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraftforge.registries.ForgeRegistryEntry;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class BuildrootFirmware extends ForgeRegistryEntry<Firmware> implements Firmware {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
@Override
|
||||
public void run(final MemoryMap memory, final long startAddress) {
|
||||
public boolean run(final MemoryMap memory, final long startAddress) {
|
||||
try {
|
||||
MemoryMaps.store(memory, startAddress, Buildroot.getFirmware());
|
||||
MemoryMaps.store(memory, startAddress + 0x200000, Buildroot.getLinuxImage());
|
||||
return true;
|
||||
} catch (final IOException e) {
|
||||
LOGGER.error(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package li.cil.oc2.common.bus.device.item;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import li.cil.oc2.api.bus.device.ItemDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.*;
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMResumingRunningEvent;
|
||||
import li.cil.oc2.common.bus.device.util.IdentityProxy;
|
||||
import li.cil.oc2.common.bus.device.util.OptionalAddress;
|
||||
import li.cil.oc2.common.bus.device.util.OptionalInterrupt;
|
||||
@@ -18,7 +22,8 @@ import java.io.OutputStream;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class AbstractHardDriveVMDevice<T extends BlockDevice> extends IdentityProxy<ItemStack> implements VMDevice, VMDeviceLifecycleListener, ItemDevice {
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public abstract class AbstractHardDriveVMDevice<T extends BlockDevice> extends IdentityProxy<ItemStack> implements VMDevice, ItemDevice {
|
||||
private static final String DEVICE_TAG_NAME = "device";
|
||||
private static final String ADDRESS_TAG_NAME = "address";
|
||||
private static final String INTERRUPT_TAG_NAME = "interrupt";
|
||||
@@ -64,21 +69,33 @@ public abstract class AbstractHardDriveVMDevice<T extends BlockDevice> extends I
|
||||
return VMDeviceLoadResult.fail();
|
||||
}
|
||||
|
||||
context.getEventBus().register(this);
|
||||
|
||||
loadPersistedState();
|
||||
|
||||
return VMDeviceLoadResult.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) {
|
||||
switch (event) {
|
||||
case RESUME_RUNNING:
|
||||
awaitStorageOperation();
|
||||
break;
|
||||
case UNLOAD:
|
||||
unload();
|
||||
break;
|
||||
}
|
||||
public void unload() {
|
||||
// Since we cannot serialize the data in a regular serialize call due to the
|
||||
// actual data being unloaded at that point, but want to permanently persist
|
||||
// it (it's the contents of the block device) we need to serialize it in the
|
||||
// unload, too. Don't need to wait for the job, though.
|
||||
serializeData();
|
||||
|
||||
data = null;
|
||||
jobHandle = null;
|
||||
|
||||
device = null;
|
||||
deviceNbt = null;
|
||||
address.clear();
|
||||
interrupt.clear();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void handleResumingRunningEvent(final VMResumingRunningEvent event) {
|
||||
awaitStorageOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,22 +196,6 @@ public abstract class AbstractHardDriveVMDevice<T extends BlockDevice> extends I
|
||||
}
|
||||
}
|
||||
|
||||
private void unload() {
|
||||
// Since we cannot serialize the data in a regular serialize call due to the
|
||||
// actual data being unloaded at that point, but want to permanently persist
|
||||
// it (it's the contents of the block device) we need to serialize it in the
|
||||
// unload, too. Don't need to wait for the job, though.
|
||||
serializeData();
|
||||
|
||||
data = null;
|
||||
jobHandle = null;
|
||||
|
||||
device = null;
|
||||
deviceNbt = null;
|
||||
address.clear();
|
||||
interrupt.clear();
|
||||
}
|
||||
|
||||
private void serializeData() {
|
||||
if (data != null) {
|
||||
final Optional<InputStream> optional = getSerializationStream(data);
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package li.cil.oc2.common.bus.device.item;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import li.cil.oc2.api.bus.device.ItemDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.*;
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMInitializingEvent;
|
||||
import li.cil.oc2.common.bus.device.util.IdentityProxy;
|
||||
import li.cil.sedna.api.memory.MemoryAccessException;
|
||||
import li.cil.sedna.api.memory.MemoryMap;
|
||||
@@ -15,7 +19,8 @@ import org.apache.logging.log4j.Logger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
public final class ByteBufferFlashMemoryVMDevice extends IdentityProxy<ItemStack> implements VMDevice, VMDeviceLifecycleListener, ItemDevice {
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public final class ByteBufferFlashMemoryVMDevice extends IdentityProxy<ItemStack> implements VMDevice, ItemDevice {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
@@ -58,36 +63,42 @@ public final class ByteBufferFlashMemoryVMDevice extends IdentityProxy<ItemStack
|
||||
|
||||
memoryMap = context.getMemoryMap();
|
||||
|
||||
context.getEventBus().register(this);
|
||||
|
||||
return VMDeviceLoadResult.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) {
|
||||
switch (event) {
|
||||
case INITIALIZING:
|
||||
// TODO Have start address passed with event?
|
||||
copyDataToMemory(0x80000000L);
|
||||
break;
|
||||
case UNLOAD:
|
||||
unload();
|
||||
break;
|
||||
}
|
||||
public void unload() {
|
||||
memoryMap = null;
|
||||
data = null;
|
||||
device = null;
|
||||
deviceNbt = null;
|
||||
address = null;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void handleInitializingEvent(final VMInitializingEvent event) {
|
||||
copyDataToMemory(event.getProgramStartAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT serializeNBT() {
|
||||
final CompoundNBT nbt = new CompoundNBT();
|
||||
final CompoundNBT tag = new CompoundNBT();
|
||||
|
||||
if (device != null) {
|
||||
nbt.putByteArray(DATA_TAG_NAME, device.getData().array());
|
||||
tag.putByteArray(DATA_TAG_NAME, device.getData().array());
|
||||
}
|
||||
|
||||
return nbt;
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeNBT(final CompoundNBT tag) {
|
||||
|
||||
final byte[] data = tag.getByteArray(DATA_TAG_NAME);
|
||||
final ByteBuffer bufferData = device.getData();
|
||||
bufferData.clear();
|
||||
bufferData.put(data, 0, Math.min(bufferData.limit(), data.length));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
@@ -139,12 +150,4 @@ public final class ByteBufferFlashMemoryVMDevice extends IdentityProxy<ItemStack
|
||||
LOGGER.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void unload() {
|
||||
memoryMap = null;
|
||||
data = null;
|
||||
device = null;
|
||||
deviceNbt = null;
|
||||
address = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
package li.cil.oc2.common.bus.device.item;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import li.cil.oc2.api.bus.device.ItemDevice;
|
||||
import li.cil.oc2.api.bus.device.data.Firmware;
|
||||
import li.cil.oc2.api.bus.device.vm.*;
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMInitializationException;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMInitializingEvent;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.bus.device.util.IdentityProxy;
|
||||
import li.cil.sedna.api.memory.MemoryMap;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
|
||||
public final class FirmwareFlashMemoryVMDevice extends IdentityProxy<ItemStack> implements VMDevice, VMDeviceLifecycleListener, ItemDevice {
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public final class FirmwareFlashMemoryVMDevice extends IdentityProxy<ItemStack> implements VMDevice, ItemDevice {
|
||||
private final Firmware firmware;
|
||||
private MemoryMap memoryMap;
|
||||
|
||||
@@ -24,19 +32,20 @@ public final class FirmwareFlashMemoryVMDevice extends IdentityProxy<ItemStack>
|
||||
public VMDeviceLoadResult load(final VMContext context) {
|
||||
memoryMap = context.getMemoryMap();
|
||||
|
||||
context.getEventBus().register(this);
|
||||
|
||||
return VMDeviceLoadResult.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) {
|
||||
switch (event) {
|
||||
case INITIALIZING:
|
||||
// TODO Have start address passed with event?
|
||||
firmware.run(memoryMap, 0x80000000L);
|
||||
break;
|
||||
case UNLOAD:
|
||||
memoryMap = null;
|
||||
break;
|
||||
public void unload() {
|
||||
memoryMap = null;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void handleInitializingEvent(final VMInitializingEvent event) throws VMInitializationException {
|
||||
if (!firmware.run(memoryMap, event.getProgramStartAddress())) {
|
||||
throw new VMInitializationException(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_INSUFFICIENT_MEMORY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package li.cil.oc2.common.bus.device.item;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import li.cil.oc2.api.bus.device.ItemDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.*;
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMResumingRunningEvent;
|
||||
import li.cil.oc2.common.Config;
|
||||
import li.cil.oc2.common.bus.device.util.IdentityProxy;
|
||||
import li.cil.oc2.common.bus.device.util.OptionalAddress;
|
||||
@@ -18,7 +22,8 @@ import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class MemoryDevice extends IdentityProxy<ItemStack> implements VMDevice, VMDeviceLifecycleListener, ItemDevice {
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public final class MemoryDevice extends IdentityProxy<ItemStack> implements VMDevice, ItemDevice {
|
||||
private static final String BLOB_HANDLE_TAG_NAME = "blob";
|
||||
private static final String ADDRESS_TAG_NAME = "address";
|
||||
|
||||
@@ -54,19 +59,25 @@ public final class MemoryDevice extends IdentityProxy<ItemStack> implements VMDe
|
||||
|
||||
loadPersistedState();
|
||||
|
||||
context.getEventBus().register(this);
|
||||
|
||||
return VMDeviceLoadResult.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) {
|
||||
switch (event) {
|
||||
case RESUME_RUNNING:
|
||||
awaitStorageOperation();
|
||||
break;
|
||||
case UNLOAD:
|
||||
unload();
|
||||
break;
|
||||
}
|
||||
public void unload() {
|
||||
// Memory is volatile, so free up our persisted blob when device is unloaded.
|
||||
BlobStorage.freeHandle(blobHandle);
|
||||
blobHandle = null;
|
||||
jobHandle = null;
|
||||
|
||||
device = null;
|
||||
address.clear();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void handleResumingRunningEvent(final VMResumingRunningEvent event) {
|
||||
awaitStorageOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -120,14 +131,4 @@ public final class MemoryDevice extends IdentityProxy<ItemStack> implements VMDe
|
||||
jobHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void unload() {
|
||||
// Memory is volatile, so free up our persisted blob when device is unloaded.
|
||||
BlobStorage.freeHandle(blobHandle);
|
||||
blobHandle = null;
|
||||
jobHandle = null;
|
||||
|
||||
device = null;
|
||||
address.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package li.cil.oc2.common.bus.device.item;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import li.cil.oc2.api.bus.device.ItemDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.*;
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMResumingRunningEvent;
|
||||
import li.cil.oc2.api.capabilities.NetworkInterface;
|
||||
import li.cil.oc2.common.bus.device.util.IdentityProxy;
|
||||
import li.cil.oc2.common.bus.device.util.OptionalAddress;
|
||||
@@ -19,7 +23,8 @@ import net.minecraftforge.common.util.LazyOptional;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class NetworkInterfaceCardItemDevice extends IdentityProxy<ItemStack> implements VMDevice, VMDeviceLifecycleListener, ItemDevice, ICapabilityProvider {
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public final class NetworkInterfaceCardItemDevice extends IdentityProxy<ItemStack> implements VMDevice, ItemDevice, ICapabilityProvider {
|
||||
private static final String DEVICE_TAG_NAME = "device";
|
||||
private static final String ADDRESS_TAG_NAME = "address";
|
||||
private static final String INTERRUPT_TAG_NAME = "interrupt";
|
||||
@@ -69,19 +74,22 @@ public final class NetworkInterfaceCardItemDevice extends IdentityProxy<ItemStac
|
||||
NBTSerialization.deserialize(deviceNbt, device);
|
||||
}
|
||||
|
||||
context.getEventBus().register(this);
|
||||
|
||||
return VMDeviceLoadResult.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) {
|
||||
switch (event) {
|
||||
case RESUMED_RUNNING:
|
||||
isRunning = true;
|
||||
break;
|
||||
case UNLOAD:
|
||||
unload();
|
||||
break;
|
||||
}
|
||||
public void unload() {
|
||||
device = null;
|
||||
isRunning = false;
|
||||
address.clear();
|
||||
interrupt.clear();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void handleResumingRunningEvent(final VMResumingRunningEvent event) {
|
||||
isRunning = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,15 +127,6 @@ public final class NetworkInterfaceCardItemDevice extends IdentityProxy<ItemStac
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
private void unload() {
|
||||
device = null;
|
||||
isRunning = false;
|
||||
address.clear();
|
||||
interrupt.clear();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
private final class NetworkInterfaceImpl implements NetworkInterface {
|
||||
@Override
|
||||
public byte[] readEthernetFrame() {
|
||||
|
||||
@@ -2,7 +2,9 @@ package li.cil.oc2.common.vm;
|
||||
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue;
|
||||
import li.cil.ceres.api.Serialized;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLifecycleEventType;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMInitializingEvent;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMResumedRunningEvent;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMResumingRunningEvent;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@@ -73,13 +75,13 @@ public abstract class AbstractTerminalVirtualMachineRunner extends VirtualMachin
|
||||
protected void handleBeforeRun() {
|
||||
if (!firedInitializationEvent) {
|
||||
firedInitializationEvent = true;
|
||||
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.INITIALIZING);
|
||||
virtualMachine.vmAdapter.postLifecycleEvent(new VMInitializingEvent(0x80000000L));
|
||||
}
|
||||
|
||||
if (!firedResumeEvent) {
|
||||
firedResumeEvent = true;
|
||||
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.RESUME_RUNNING);
|
||||
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.RESUMED_RUNNING);
|
||||
virtualMachine.vmAdapter.postLifecycleEvent(new VMResumingRunningEvent());
|
||||
virtualMachine.vmAdapter.postLifecycleEvent(new VMResumedRunningEvent());
|
||||
}
|
||||
|
||||
int value;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLifecycleEventType;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMPausingEvent;
|
||||
import li.cil.oc2.common.Constants;
|
||||
import li.cil.oc2.common.bus.AbstractDeviceBusController;
|
||||
import li.cil.oc2.common.serialization.NBTSerialization;
|
||||
@@ -51,7 +51,6 @@ public abstract class AbstractVirtualMachineState<TBusController extends Abstrac
|
||||
public AbstractVirtualMachineState(final TBusController busController, final TVirtualMachine virtualMachine) {
|
||||
this.busController = busController;
|
||||
this.virtualMachine = virtualMachine;
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -191,6 +190,7 @@ public abstract class AbstractVirtualMachineState<TBusController extends Abstrac
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// May have a valid runner after load. In which case we just had to wait for
|
||||
// bus setup and devices to load. So we can keep using it.
|
||||
if (runner == null) {
|
||||
@@ -203,7 +203,7 @@ public abstract class AbstractVirtualMachineState<TBusController extends Abstrac
|
||||
// a program that only uses registers. But not supporting that esoteric
|
||||
// use-case loses out against avoiding people getting confused for having
|
||||
// forgotten to add some RAM modules.
|
||||
setBootError(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_NO_MEMORY));
|
||||
setBootError(new TranslationTextComponent(Constants.COMPUTER_BOOT_ERROR_INSUFFICIENT_MEMORY));
|
||||
setRunState(RunState.STOPPED);
|
||||
return;
|
||||
} catch (final MemoryAccessException e) {
|
||||
@@ -239,7 +239,7 @@ public abstract class AbstractVirtualMachineState<TBusController extends Abstrac
|
||||
|
||||
if (runner != null) {
|
||||
tag.put(RUNNER_TAG_NAME, NBTSerialization.serialize(runner));
|
||||
virtualMachine.vmAdapter.fireLifecycleEvent(VMDeviceLifecycleEventType.PAUSING);
|
||||
virtualMachine.vmAdapter.postLifecycleEvent(new VMPausingEvent());
|
||||
runner.scheduleResumeEvent(); // Allow synchronizing to async device saves.
|
||||
} else {
|
||||
NBTUtils.putEnum(tag, RUN_STATE_TAG_NAME, runState);
|
||||
|
||||
35
src/main/java/li/cil/oc2/common/vm/ManagedEventBus.java
Normal file
35
src/main/java/li/cil/oc2/common/vm/ManagedEventBus.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMLifecycleEventBus;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public final class ManagedEventBus implements VMLifecycleEventBus {
|
||||
private final EventBus eventBus;
|
||||
private final ArrayList<Object> subscribers = new ArrayList<>();
|
||||
|
||||
public ManagedEventBus(final EventBus eventBus) {
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
for (final Object subscriber : subscribers) {
|
||||
eventBus.unregister(subscriber);
|
||||
}
|
||||
subscribers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(final Object object) {
|
||||
eventBus.register(object);
|
||||
subscribers.add(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(final Object object) {
|
||||
eventBus.unregister(object);
|
||||
subscribers.remove(object);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import li.cil.oc2.api.bus.device.vm.InterruptAllocator;
|
||||
import li.cil.oc2.api.bus.device.vm.MemoryAllocator;
|
||||
import li.cil.oc2.api.bus.device.vm.MemoryRangeAllocator;
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMLifecycleEventBus;
|
||||
import li.cil.sedna.api.Board;
|
||||
import li.cil.sedna.api.device.InterruptController;
|
||||
import li.cil.sedna.api.device.MemoryMappedDevice;
|
||||
@@ -13,26 +15,29 @@ import java.util.BitSet;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.function.Function;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public final class ManagedVMContext implements VMContext {
|
||||
private final ManagedMemoryMap memoryMap;
|
||||
private final ManagedInterruptController interruptController;
|
||||
private final ManagedMemoryRangeAllocator memoryRangeAllocator;
|
||||
private final ManagedInterruptAllocator interruptAllocator;
|
||||
private final ManagedMemoryAllocator memoryAllocator;
|
||||
private final ManagedEventBus eventBus;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public ManagedVMContext(final Board board, final BitSet claimedInterrupts, final BitSet reservedInterrupts,
|
||||
final Function<MemoryMappedDevice, OptionalLong> defaultAddress) {
|
||||
final EventBus eventBus, final Function<MemoryMappedDevice, OptionalLong> defaultAddress) {
|
||||
this.memoryRangeAllocator = new ManagedMemoryRangeAllocator(board, defaultAddress);
|
||||
this.interruptAllocator = new ManagedInterruptAllocator(claimedInterrupts, reservedInterrupts, board.getInterruptCount());
|
||||
this.memoryMap = new ManagedMemoryMap(board.getMemoryMap());
|
||||
this.interruptController = new ManagedInterruptController(board.getInterruptController(), interruptAllocator);
|
||||
this.memoryAllocator = new ManagedMemoryAllocator();
|
||||
this.eventBus = new ManagedEventBus(eventBus);
|
||||
}
|
||||
|
||||
public ManagedVMContext(final Board board, final BitSet claimedInterrupts, final BitSet reservedInterrupts) {
|
||||
this(board, claimedInterrupts, reservedInterrupts, (memoryMappedDevice) -> OptionalLong.empty());
|
||||
public ManagedVMContext(final Board board, final BitSet claimedInterrupts, final BitSet reservedInterrupts, final EventBus eventBus) {
|
||||
this(board, claimedInterrupts, reservedInterrupts, eventBus, (memoryMappedDevice) -> OptionalLong.empty());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -48,6 +53,7 @@ public final class ManagedVMContext implements VMContext {
|
||||
interruptAllocator.invalidate();
|
||||
interruptController.invalidate();
|
||||
memoryAllocator.invalidate();
|
||||
eventBus.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,4 +80,9 @@ public final class ManagedVMContext implements VMContext {
|
||||
public MemoryAllocator getMemoryAllocator() {
|
||||
return memoryAllocator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMLifecycleEventBus getEventBus() {
|
||||
return eventBus;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import li.cil.ceres.api.Serialized;
|
||||
import li.cil.oc2.api.bus.device.Device;
|
||||
import li.cil.oc2.api.bus.device.vm.*;
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
|
||||
import li.cil.oc2.api.bus.device.vm.event.VMLifecycleEvent;
|
||||
import li.cil.sedna.api.Board;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public final class VirtualMachineDeviceBusAdapter {
|
||||
private final Board board;
|
||||
|
||||
private final EventBus eventBus = new EventBus();
|
||||
private final ManagedVMContext globalContext;
|
||||
private final BitSet claimedInterrupts = new BitSet();
|
||||
private final HashMap<VMDevice, ManagedVMContext> deviceContexts = new HashMap<>();
|
||||
private final ArrayList<VMDevice> incompleteLoads = new ArrayList<>();
|
||||
|
||||
private final HashSet<VMDeviceLifecycleListener> lifecycleEventListeners = new HashSet<>();
|
||||
private DefaultAddressProvider defaultAddressProvider = unused -> OptionalLong.empty();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -30,8 +35,7 @@ public final class VirtualMachineDeviceBusAdapter {
|
||||
|
||||
public VirtualMachineDeviceBusAdapter(final Board board) {
|
||||
this.board = board;
|
||||
this.globalContext = new ManagedVMContext(
|
||||
board, claimedInterrupts, reservedInterrupts);
|
||||
this.globalContext = new ManagedVMContext(board, claimedInterrupts, reservedInterrupts, eventBus);
|
||||
this.claimedInterrupts.set(0);
|
||||
}
|
||||
|
||||
@@ -50,7 +54,7 @@ public final class VirtualMachineDeviceBusAdapter {
|
||||
final VMDevice device = incompleteLoads.get(i);
|
||||
|
||||
final ManagedVMContext context = new ManagedVMContext(
|
||||
board, claimedInterrupts, reservedInterrupts,
|
||||
board, claimedInterrupts, reservedInterrupts, eventBus,
|
||||
(memoryMappedDevice) -> defaultAddressProvider.getDefaultAddress(device));
|
||||
|
||||
deviceContexts.put(device, context);
|
||||
@@ -75,7 +79,9 @@ public final class VirtualMachineDeviceBusAdapter {
|
||||
}
|
||||
|
||||
public void unload() {
|
||||
fireLifecycleEvent(VMDeviceLifecycleEventType.UNLOAD);
|
||||
for (final VMDevice device : deviceContexts.keySet()) {
|
||||
device.unload();
|
||||
}
|
||||
|
||||
suspend();
|
||||
}
|
||||
@@ -91,10 +97,8 @@ public final class VirtualMachineDeviceBusAdapter {
|
||||
incompleteLoads.addAll(deviceContexts.keySet());
|
||||
}
|
||||
|
||||
public void fireLifecycleEvent(final VMDeviceLifecycleEventType event) {
|
||||
for (final VMDeviceLifecycleListener tickListener : lifecycleEventListeners) {
|
||||
tickListener.handleLifecycleEvent(event);
|
||||
}
|
||||
public void postLifecycleEvent(final VMLifecycleEvent event) {
|
||||
eventBus.post(event);
|
||||
}
|
||||
|
||||
public void addDevices(final Collection<Device> devices) {
|
||||
@@ -108,10 +112,6 @@ public final class VirtualMachineDeviceBusAdapter {
|
||||
}
|
||||
|
||||
incompleteLoads.add(vmDevice);
|
||||
|
||||
if (vmDevice instanceof VMDeviceLifecycleListener) {
|
||||
lifecycleEventListeners.add((VMDeviceLifecycleListener) vmDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,18 +121,14 @@ public final class VirtualMachineDeviceBusAdapter {
|
||||
if (device instanceof VMDevice) {
|
||||
final VMDevice vmDevice = (VMDevice) device;
|
||||
|
||||
vmDevice.unload();
|
||||
|
||||
final ManagedVMContext context = deviceContexts.remove(vmDevice);
|
||||
if (context != null) {
|
||||
context.invalidate();
|
||||
}
|
||||
|
||||
incompleteLoads.remove(vmDevice);
|
||||
|
||||
if (vmDevice instanceof VMDeviceLifecycleListener) {
|
||||
final VMDeviceLifecycleListener eventListener = (VMDeviceLifecycleListener) vmDevice;
|
||||
lifecycleEventListeners.remove(eventListener);
|
||||
eventListener.handleLifecycleEvent(VMDeviceLifecycleEventType.UNLOAD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
"config.oc2.admin.fakePlayerUUID": "Fake Player UUID",
|
||||
|
||||
"gui.oc2.computer.boot_error.unknown": "Unknown Error",
|
||||
"gui.oc2.computer.boot_error.no_memory": "Insufficient Memory",
|
||||
"gui.oc2.computer.boot_error.missing_firmware": "Missing Flash Memory",
|
||||
"gui.oc2.computer.boot_error.insufficient_memory": "Insufficient Memory",
|
||||
"gui.oc2.computer.bus_state.incomplete": "Bus Incomplete",
|
||||
"gui.oc2.computer.bus_state.too_complex": "Bus Too Complex",
|
||||
"gui.oc2.computer.bus_state.multiple_controllers": "Multiple Bus Controllers",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package li.cil.oc2.common.bus;
|
||||
|
||||
import li.cil.oc2.api.bus.device.vm.*;
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDevice;
|
||||
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
|
||||
import li.cil.oc2.common.vm.VirtualMachineDeviceBusAdapter;
|
||||
import li.cil.sedna.api.Board;
|
||||
import li.cil.sedna.api.device.InterruptController;
|
||||
@@ -71,31 +73,31 @@ public final class VMDeviceTests {
|
||||
|
||||
@Test
|
||||
public void removedDevicesHaveUnloadCalled() {
|
||||
final VMDeviceLifecycleListener device = mock(VMDeviceLifecycleListener.class);
|
||||
final VMDevice device = mock(VMDevice.class);
|
||||
when(device.load(any())).thenReturn(VMDeviceLoadResult.success());
|
||||
|
||||
adapter.addDevices(Collections.singleton(device));
|
||||
assertTrue(adapter.load().wasSuccessful());
|
||||
|
||||
adapter.removeDevices(Collections.singleton(device));
|
||||
verify(device).handleLifecycleEvent(VMDeviceLifecycleEventType.UNLOAD);
|
||||
verify(device).unload();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void devicesHaveUnloadCalledOnGlobalUnload() {
|
||||
final VMDeviceLifecycleListener device = mock(VMDeviceLifecycleListener.class);
|
||||
final VMDevice device = mock(VMDevice.class);
|
||||
when(device.load(any())).thenReturn(VMDeviceLoadResult.success());
|
||||
|
||||
adapter.addDevices(Collections.singleton(device));
|
||||
assertTrue(adapter.load().wasSuccessful());
|
||||
|
||||
adapter.unload();
|
||||
verify(device).handleLifecycleEvent(VMDeviceLifecycleEventType.UNLOAD);
|
||||
verify(device).unload();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void devicesHaveLoadCalledAfterGlobalUnload() {
|
||||
final VMDeviceLifecycleListener device = mock(VMDeviceLifecycleListener.class);
|
||||
final VMDevice device = mock(VMDevice.class);
|
||||
when(device.load(any())).thenReturn(VMDeviceLoadResult.success());
|
||||
|
||||
adapter.addDevices(Collections.singleton(device));
|
||||
@@ -103,7 +105,7 @@ public final class VMDeviceTests {
|
||||
verify(device).load(any());
|
||||
|
||||
adapter.unload();
|
||||
verify(device).handleLifecycleEvent(VMDeviceLifecycleEventType.UNLOAD);
|
||||
verify(device).unload();
|
||||
|
||||
assertTrue(adapter.load().wasSuccessful());
|
||||
verify(device, times(2)).load(any());
|
||||
|
||||
Reference in New Issue
Block a user