diff --git a/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMContext.java b/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMContext.java index ace2fec2..fe3c864a 100644 --- a/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMContext.java +++ b/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMContext.java @@ -93,13 +93,4 @@ public interface VMContext { * @return the event bus. */ VMLifecycleEventBus getEventBus(); - - /** - * Waits for the executor thread of the virtual machine to finish running. - *

- * Note that this may trigger a {@link li.cil.oc2.api.bus.device.vm.event.VMPausingEvent} - * if the virtual machine has not been paused before. Calling this on a paused virtual - * machine is a no-op. - */ - void joinWorkerThread(); } diff --git a/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMLifecycleEventBus.java b/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMLifecycleEventBus.java index 1baf042e..28db8c66 100644 --- a/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMLifecycleEventBus.java +++ b/src/main/java/li/cil/oc2/api/bus/device/vm/context/VMLifecycleEventBus.java @@ -4,9 +4,8 @@ package li.cil.oc2.api.bus.device.vm.context; * Allows registering for VM lifecycle events. * * @see li.cil.oc2.api.bus.device.vm.event.VMInitializingEvent - * @see li.cil.oc2.api.bus.device.vm.event.VMResumingRunningEvent + * @see li.cil.oc2.api.bus.device.vm.event.VMSynchronizeEvent * @see li.cil.oc2.api.bus.device.vm.event.VMResumedRunningEvent - * @see li.cil.oc2.api.bus.device.vm.event.VMPausingEvent */ public interface VMLifecycleEventBus { /** diff --git a/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMInitializingEvent.java b/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMInitializingEvent.java index e06c8185..dcc22637 100644 --- a/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMInitializingEvent.java +++ b/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMInitializingEvent.java @@ -8,8 +8,8 @@ import li.cil.oc2.api.bus.device.vm.context.VMContext; *

* Fired after all devices reported success from {@link VMDevice#mount(VMContext)}. *

- * 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 + * 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. *

* Listeners of this event may throw a {@link VMInitializationException} in case diff --git a/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMResumedRunningEvent.java b/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMResumedRunningEvent.java index d2d5549c..e1c9cf59 100644 --- a/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMResumedRunningEvent.java +++ b/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMResumedRunningEvent.java @@ -1,13 +1,16 @@ package li.cil.oc2.api.bus.device.vm.event; +import li.cil.oc2.api.bus.device.vm.VMDevice; +import li.cil.oc2.api.bus.device.vm.context.VMContext; + /** - * Fired when the VM resumed running. + * Fired when the VM resumed running, either when first starting up, when resuming after + * being loaded, or after a {@link VMSynchronizeEvent}. *

- * Fired after {@link VMResumingRunningEvent} has been fired and handled by all devices. + * May be used to ensure asynchronous work started in {@link VMDevice#mount(VMContext)} + * completes before regular execution of the virtual machine begins. *

- * Allows device initialization that relies on all other devices having fully loaded. - *

- * Typically, this is used in combination with {@link VMPausingEvent}, to re-enable external + * May also be used in combination with {@link VMSynchronizeEvent}, to re-enable external * interactions after VM state is guaranteed to be safe to modify again. */ public final class VMResumedRunningEvent { } diff --git a/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMResumingRunningEvent.java b/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMResumingRunningEvent.java deleted file mode 100644 index 46d136ab..00000000 --- a/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMResumingRunningEvent.java +++ /dev/null @@ -1,15 +0,0 @@ -package li.cil.oc2.api.bus.device.vm.event; - -import li.cil.oc2.api.bus.device.vm.VMDevice; -import li.cil.oc2.api.bus.device.vm.context.VMContext; - -/** - * Fired when the VM resumes running. - *

- * Fired after all devices reported success from {@link VMDevice#mount(VMContext)}. - *

- * 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 { } diff --git a/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMPausingEvent.java b/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMSynchronizeEvent.java similarity index 56% rename from src/main/java/li/cil/oc2/api/bus/device/vm/event/VMPausingEvent.java rename to src/main/java/li/cil/oc2/api/bus/device/vm/event/VMSynchronizeEvent.java index 476cf1ae..df2da01a 100644 --- a/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMPausingEvent.java +++ b/src/main/java/li/cil/oc2/api/bus/device/vm/event/VMSynchronizeEvent.java @@ -7,5 +7,10 @@ package li.cil.oc2.api.bus.device.vm.event; * such interactions until {@link VMResumedRunningEvent} is fired. This is required * if such interactions may modify VM state, to prevent corrupting data being * serialized asynchronously. + *

+ * Note that this is called right before the worker thread used to execute the + * virtual machine is joined to the main thread. As such, only devices that run + * their own threads modifying observable state will need to synchronize these + * threads here. */ -public final class VMPausingEvent { } +public final class VMSynchronizeEvent { } diff --git a/src/main/java/li/cil/oc2/common/blockentity/DiskDriveBlockEntity.java b/src/main/java/li/cil/oc2/common/blockentity/DiskDriveBlockEntity.java index 5c869151..a4943765 100644 --- a/src/main/java/li/cil/oc2/common/blockentity/DiskDriveBlockEntity.java +++ b/src/main/java/li/cil/oc2/common/blockentity/DiskDriveBlockEntity.java @@ -28,8 +28,6 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -37,10 +35,9 @@ import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.time.Duration; +import java.util.concurrent.CompletableFuture; public final class DiskDriveBlockEntity extends ModBlockEntity { - private static final Logger LOGGER = LogManager.getLogger(); - private static final String DATA_TAG_NAME = "data"; private static final ByteBufferBlockDevice EMPTY_BLOCK_DEVICE = ByteBufferBlockDevice.create(0, false); @@ -232,10 +229,18 @@ public final class DiskDriveBlockEntity extends ModBlockEntity { } public void updateBlockDevice(final CompoundTag tag) { + joinOpenJob(); + if (device == null) { return; } + try { + device.setBlock(EMPTY_BLOCK_DEVICE); + } catch (final IOException e) { + LOGGER.error(e); + } + if (blobHandle != null) { BlobStorage.close(blobHandle); blobHandle = null; @@ -243,46 +248,56 @@ public final class DiskDriveBlockEntity extends ModBlockEntity { importFromItemStack(tag); - try { - device.setBlock(createBlockDevice()); - } catch (final IOException e) { - LOGGER.error(e); - } + setOpenJob(createBlockDevice().thenAcceptAsync(blockDevice -> { + try { + device.setBlock(blockDevice); + } catch (final IOException e) { + throw new RuntimeException(e); + } + }, WORKERS)); } public void removeBlockDevice() { + joinOpenJob(); + if (device == null) { return; } - if (blobHandle != null) { - BlobStorage.close(blobHandle); - blobHandle = null; - } - try { device.setBlock(EMPTY_BLOCK_DEVICE); } catch (final IOException e) { LOGGER.error(e); } + + if (blobHandle != null) { + BlobStorage.close(blobHandle); + blobHandle = null; + } } @Override - protected BlockDevice createBlockDevice() throws IOException { + protected CompletableFuture createBlockDevice() { final ItemStack stack = itemHandler.getStackInSlotRaw(0); if (stack.isEmpty() || !(stack.getItem() instanceof final FloppyItem floppy)) { - return EMPTY_BLOCK_DEVICE; + return CompletableFuture.completedFuture(EMPTY_BLOCK_DEVICE); } final int capacity = Mth.clamp(floppy.getCapacity(stack), 0, Config.maxFloppySize); if (capacity <= 0) { - return EMPTY_BLOCK_DEVICE; + return CompletableFuture.completedFuture(EMPTY_BLOCK_DEVICE); } blobHandle = BlobStorage.validateHandle(blobHandle); - final FileChannel channel = BlobStorage.getOrOpen(blobHandle); - final MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, capacity); - return ByteBufferBlockDevice.wrap(buffer, false); + return CompletableFuture.supplyAsync(() -> { + try { + final FileChannel channel = BlobStorage.getOrOpen(blobHandle); + final MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, capacity); + return ByteBufferBlockDevice.wrap(buffer, false); + } catch (final IOException e) { + throw new RuntimeException(e); + } + }, WORKERS); } @Override diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/AbstractBlockDeviceVMDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/AbstractBlockDeviceVMDevice.java index 2f44b96c..10028467 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/item/AbstractBlockDeviceVMDevice.java +++ b/src/main/java/li/cil/oc2/common/bus/device/item/AbstractBlockDeviceVMDevice.java @@ -1,9 +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.VMDevice; import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult; import li.cil.oc2.api.bus.device.vm.context.VMContext; +import li.cil.oc2.api.bus.device.vm.event.VMResumedRunningEvent; import li.cil.oc2.common.Constants; import li.cil.oc2.common.bus.device.util.IdentityProxy; import li.cil.oc2.common.bus.device.util.OptionalAddress; @@ -23,18 +25,29 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; public abstract class AbstractBlockDeviceVMDevice extends IdentityProxy implements VMDevice, ItemDevice { - private static final Logger LOGGER = LogManager.getLogger(); + protected static final Logger LOGGER = LogManager.getLogger(); private static final String DEVICE_TAG_NAME = "device"; private static final String ADDRESS_TAG_NAME = "address"; private static final String INTERRUPT_TAG_NAME = "interrupt"; private static final String BLOB_HANDLE_TAG_NAME = "blob"; + protected static final ExecutorService WORKERS = Executors.newCachedThreadPool(r -> { + final Thread thread = new Thread(r, "Block Device Initializer"); + thread.setDaemon(false); + return thread; + }); + /////////////////////////////////////////////////////////////// protected VirtIOBlockDevice device; + private CompletableFuture openJob; /////////////////////////////////////////////////////////////// @@ -81,7 +94,7 @@ public abstract class AbstractBlockDeviceVMDevice job) { + joinOpenJob(); + openJob = job; } + protected void joinOpenJob() { + if (openJob != null) { + try { + openJob.join(); + } catch (final CompletionException e) { + LOGGER.error(e); + } finally { + openJob = null; + } + } + } + + protected abstract CompletableFuture createBlockDevice(); + protected void handleDataAccess() { } @@ -185,18 +206,40 @@ public abstract class AbstractBlockDeviceVMDevice { + try { + final ListenableBlockDevice listenableData = new ListenableBlockDevice(blockDevice); + listenableData.onAccess.add(this::handleDataAccess); + device.setBlock(listenableData); + } catch (final IOException e) { + throw new RuntimeException(e); + } + }, WORKERS)); return true; } + private void closeDevice() { + // Join the init job before releasing the device to avoid writes from thread to closed device. + // Since we use memory mapped memory, closing the device leads to it holding a dead pointer, + // meaning further access to it will hard-crash the JVM. + joinOpenJob(); + + if (device == null) { + return; + } + + try { + device.close(); + } catch (final IOException e) { + LOGGER.error(e); + } + + device = null; + } + /////////////////////////////////////////////////////////////// private static final class ListenableBlockDevice implements BlockDevice { diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDevice.java index f11331cc..9673943d 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDevice.java +++ b/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDevice.java @@ -12,6 +12,7 @@ import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.time.Duration; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; public class HardDriveVMDevice extends AbstractBlockDeviceVMDevice { @@ -32,11 +33,18 @@ public class HardDriveVMDevice extends AbstractBlockDeviceVMDevice createBlockDevice() { blobHandle = BlobStorage.validateHandle(blobHandle); - final FileChannel channel = BlobStorage.getOrOpen(blobHandle); - final MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, size); - return ByteBufferBlockDevice.wrap(buffer, readonly); + + return CompletableFuture.supplyAsync(() -> { + try { + final FileChannel channel = BlobStorage.getOrOpen(blobHandle); + final MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, size); + return ByteBufferBlockDevice.wrap(buffer, readonly); + } catch (final IOException e) { + throw new RuntimeException(e); + } + }, WORKERS); } @Override diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDeviceWithInitialData.java b/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDeviceWithInitialData.java index 34c8a8d9..e79a333a 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDeviceWithInitialData.java +++ b/src/main/java/li/cil/oc2/common/bus/device/item/HardDriveVMDeviceWithInitialData.java @@ -1,8 +1,6 @@ package li.cil.oc2.common.bus.device.item; -import com.google.common.eventbus.Subscribe; import com.google.common.io.ByteStreams; -import li.cil.oc2.api.bus.device.vm.event.VMResumedRunningEvent; import li.cil.oc2.common.util.Location; import li.cil.sedna.api.device.BlockDevice; import li.cil.sedna.device.block.ByteBufferBlockDevice; @@ -15,21 +13,12 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; import java.util.function.Supplier; public final class HardDriveVMDeviceWithInitialData extends HardDriveVMDevice { private static final Logger LOGGER = LogManager.getLogger(); - private static final ExecutorService WORKERS = Executors.newCachedThreadPool(r -> { - final Thread thread = new Thread(r, "Hard Drive Initializer"); - thread.setDaemon(false); - return thread; - }); private final BlockDevice base; - private Future copyJob; /////////////////////////////////////////////////////////////////// @@ -38,53 +27,23 @@ public final class HardDriveVMDeviceWithInitialData extends HardDriveVMDevice { this.base = base; } - @Subscribe - public void handleResumedRunningEvent(final VMResumedRunningEvent event) { - joinCopyJob(); - } - /////////////////////////////////////////////////////////////////// @Override - protected ByteBufferBlockDevice createBlockDevice() throws IOException { + protected CompletableFuture createBlockDevice() { final boolean isInitializing = blobHandle == null; - final ByteBufferBlockDevice device = super.createBlockDevice(); - if (isInitializing) { - copyJob = CompletableFuture.runAsync(() -> { + return super.createBlockDevice().thenApplyAsync(device -> { + if (isInitializing) { try { try (final InputStream input = base.getInputStream(0); final OutputStream output = device.getOutputStream(0)) { ByteStreams.copy(input, output); } } catch (final IOException e) { - LOGGER.error(e); + throw new RuntimeException(e); } - }, WORKERS); - } - return device; - } - - @Override - protected void closeBlockDevice() { - // Join the copy job before releasing the device to avoid writes from thread to closed device. - // Since we use memory mapped memory, closing the device leads to it holding a dead pointer, - // meaning further access to it will hard-crash the JVM. - joinCopyJob(); - - super.closeBlockDevice(); - } - - /////////////////////////////////////////////////////////////////// - - private void joinCopyJob() { - if (copyJob != null) { - try { - copyJob.get(); - } catch (final Throwable e) { - LOGGER.error(e); - } finally { - copyJob = null; } - } + return device; + }, WORKERS); } } diff --git a/src/main/java/li/cil/oc2/common/bus/device/item/MemoryDevice.java b/src/main/java/li/cil/oc2/common/bus/device/item/MemoryDevice.java index 9717f8ae..5b0dbbd0 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/item/MemoryDevice.java +++ b/src/main/java/li/cil/oc2/common/bus/device/item/MemoryDevice.java @@ -56,22 +56,12 @@ public final class MemoryDevice extends IdentityProxy implements VMDe return VMDeviceLoadResult.fail(); } - context.getEventBus().register(this); - return VMDeviceLoadResult.success(); } @Override public void unmount() { - if (device != null) { - try { - device.close(); - } catch (final Exception e) { - LOGGER.error(e); - } - - device = null; - } + closeDevice(); if (blobHandle != null) { BlobStorage.close(blobHandle); @@ -126,9 +116,24 @@ public final class MemoryDevice extends IdentityProxy implements VMDe final MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, size); device = new ByteBufferMemory(size, buffer); } catch (final IOException e) { + LOGGER.error(e); return false; } return true; } + + private void closeDevice() { + if (device == null) { + return; + } + + try { + device.close(); + } catch (final Exception e) { + LOGGER.error(e); + } + + device = null; + } } diff --git a/src/main/java/li/cil/oc2/common/serialization/BlobStorage.java b/src/main/java/li/cil/oc2/common/serialization/BlobStorage.java index 2deaaff9..2868e6e3 100644 --- a/src/main/java/li/cil/oc2/common/serialization/BlobStorage.java +++ b/src/main/java/li/cil/oc2/common/serialization/BlobStorage.java @@ -15,6 +15,7 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.concurrent.CompletableFuture; /** * This class facilitates storing binary chunks of data in an efficient, parallelized fashion. @@ -52,7 +53,7 @@ public final class BlobStorage { /** * Closes all currently open blobs. */ - public static void close() { + public static synchronized void close() { for (final FileChannel blob : BLOBS.values()) { try { blob.close(); @@ -98,7 +99,7 @@ public final class BlobStorage { * @return the file channel for the requested blob. * @throws IOException if opening the blob fails. */ - public static FileChannel getOrOpen(final UUID handle) throws IOException { + public static synchronized FileChannel getOrOpen(final UUID handle) throws IOException { FileChannel blob = BLOBS.get(handle); if (blob != null && blob.isOpen()) { return blob; @@ -115,7 +116,7 @@ public final class BlobStorage { * * @param handle the handle of the blob to close. */ - public static void close(final UUID handle) { + public static synchronized void close(final UUID handle) { try { final FileChannel blob = BLOBS.remove(handle); if (blob != null) { @@ -134,11 +135,13 @@ public final class BlobStorage { public static void delete(final UUID handle) { close(handle); - try { - final Path path = dataDirectory.resolve(handle.toString()); - Files.deleteIfExists(path); - } catch (final Throwable e) { - LOGGER.error(e); - } + final Path path = dataDirectory.resolve(handle.toString()); + CompletableFuture.runAsync(() -> { + try { + Files.deleteIfExists(path); + } catch (final Throwable e) { + LOGGER.error(e); + } + }); } } diff --git a/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java index 1fa210c8..584b3fc9 100644 --- a/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java +++ b/src/main/java/li/cil/oc2/common/vm/AbstractVirtualMachine.java @@ -70,7 +70,7 @@ public abstract class AbstractVirtualMachine implements VirtualMachine { busController.onDevicesRemoved.add(this::handleDevicesRemoved); state.board = new R5Board(); - state.context = new GlobalVMContext(state.board, this::joinWorkerThread); + state.context = new GlobalVMContext(state.board); state.builtinDevices = new BuiltinDevices(state.context); state.rpcAdapter = new RPCDeviceBusAdapter(state.builtinDevices.rpcSerialDevice); state.vmAdapter = new VMDeviceBusAdapter(state.context); @@ -171,13 +171,7 @@ public abstract class AbstractVirtualMachine implements VirtualMachine { private void joinWorkerThread() { if (runner != null) { - try { - runner.join(); - } catch (final Throwable e) { - LOGGER.error(e); - runner = null; - setRunState(VMRunState.STOPPED); - } + runner.join(); } } @@ -309,6 +303,8 @@ public abstract class AbstractVirtualMachine implements VirtualMachine { return; } + assert runner == null : "Runner active while still in load phase."; + final VMDeviceLoadResult loadResult = state.vmAdapter.mountDevices(); if (!loadResult.wasSuccessful()) { if (loadResult.getErrorMessage() != null) { @@ -410,10 +406,12 @@ public abstract class AbstractVirtualMachine implements VirtualMachine { } private void handleDevicesAdded(final CommonDeviceBusController.DevicesChangedEvent event) { + joinWorkerThread(); state.vmAdapter.addDevices(event.devices()); } private void handleDevicesRemoved(final CommonDeviceBusController.DevicesChangedEvent event) { + joinWorkerThread(); state.vmAdapter.removeDevices(event.devices()); } } diff --git a/src/main/java/li/cil/oc2/common/vm/VMDeviceBusAdapter.java b/src/main/java/li/cil/oc2/common/vm/VMDeviceBusAdapter.java index fdcfd43c..e0aa0ef4 100644 --- a/src/main/java/li/cil/oc2/common/vm/VMDeviceBusAdapter.java +++ b/src/main/java/li/cil/oc2/common/vm/VMDeviceBusAdapter.java @@ -33,8 +33,6 @@ public final class VMDeviceBusAdapter { } public VMDeviceLoadResult mountDevices() { - globalContext.joinWorkerThread(); - for (final VMDevice device : unmountedDevices) { final ManagedVMContext context = new ManagedVMContext(globalContext, globalContext, () -> baseAddressProvider.getBaseAddress(device)); @@ -64,8 +62,6 @@ public final class VMDeviceBusAdapter { } public void unmountDevices() { - globalContext.joinWorkerThread(); - mountedDevices.forEach((device, context) -> { device.unmount(); context.invalidate(); @@ -82,8 +78,6 @@ public final class VMDeviceBusAdapter { } public void addDevices(final Collection devices) { - globalContext.joinWorkerThread(); - for (final Device device : devices) { if (device instanceof final VMDevice vmDevice) { // Add to set of unmounted devices if we don't already track it. It's a set, so @@ -96,8 +90,6 @@ public final class VMDeviceBusAdapter { } public void removeDevices(final Collection devices) { - globalContext.joinWorkerThread(); - for (final Device device : devices) { if (device instanceof final VMDevice vmDevice) { final ManagedVMContext context = mountedDevices.remove(vmDevice); diff --git a/src/main/java/li/cil/oc2/common/vm/VMRunner.java b/src/main/java/li/cil/oc2/common/vm/VMRunner.java index 0c2e3bc3..4f09b043 100644 --- a/src/main/java/li/cil/oc2/common/vm/VMRunner.java +++ b/src/main/java/li/cil/oc2/common/vm/VMRunner.java @@ -1,7 +1,10 @@ package li.cil.oc2.common.vm; import li.cil.ceres.api.Serialized; -import li.cil.oc2.api.bus.device.vm.event.*; +import li.cil.oc2.api.bus.device.vm.event.VMInitializationException; +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.VMSynchronizeEvent; import li.cil.oc2.common.Constants; import li.cil.oc2.common.bus.RPCDeviceBusAdapter; import li.cil.oc2.common.vm.context.global.GlobalVMContext; @@ -71,21 +74,17 @@ public class VMRunner implements Runnable { } } - public void join() throws Throwable { - context.postEvent(new VMPausingEvent()); - try { - if (lastSchedule != null) { - try { - lastSchedule.get(); - } catch (final InterruptedException e) { - // We do not mind this. - } catch (final ExecutionException e) { - throw e.getCause(); - } + public void join() { + context.postEvent(new VMSynchronizeEvent()); + firedResumedRunningEvent = false; + if (lastSchedule != null) { + try { + lastSchedule.get(); + } catch (final InterruptedException e) { + // We do not mind this. + } catch (final ExecutionException e) { + throw new RuntimeException(e.getCause()); } - } finally { - context.postEvent(new VMResumingRunningEvent()); - firedResumedRunningEvent = false; } } diff --git a/src/main/java/li/cil/oc2/common/vm/context/global/GlobalVMContext.java b/src/main/java/li/cil/oc2/common/vm/context/global/GlobalVMContext.java index 73bee47b..aa58c0e7 100644 --- a/src/main/java/li/cil/oc2/common/vm/context/global/GlobalVMContext.java +++ b/src/main/java/li/cil/oc2/common/vm/context/global/GlobalVMContext.java @@ -19,7 +19,6 @@ public final class GlobalVMContext implements VMContext, VMContextManagerCollect private final GlobalInterruptController interruptController; private final GlobalMemoryAllocator memoryAllocator; private final GlobalEventBus eventBus; - private final Runnable joinWorkerThread; /////////////////////////////////////////////////////////////////// @@ -38,14 +37,13 @@ public final class GlobalVMContext implements VMContext, VMContextManagerCollect /////////////////////////////////////////////////////////////////// - public GlobalVMContext(final Board board, final Runnable joinWorkerThread) { + public GlobalVMContext(final Board board) { this.memoryMap = new GlobalMemoryMap(board.getMemoryMap()); this.memoryRangeAllocator = new GlobalMemoryRangeAllocator(board, reservedMemoryRanges); this.interruptAllocator = new GlobalInterruptAllocator(board.getInterruptCount(), reservedInterrupts); this.interruptController = new GlobalInterruptController(board.getInterruptController(), interruptAllocator); this.memoryAllocator = new GlobalMemoryAllocator(); this.eventBus = new GlobalEventBus(); - this.joinWorkerThread = joinWorkerThread; } /////////////////////////////////////////////////////////////////// @@ -98,11 +96,6 @@ public final class GlobalVMContext implements VMContext, VMContextManagerCollect return eventBus; } - @Override - public void joinWorkerThread() { - joinWorkerThread.run(); - } - @Override public InterruptManager getInterruptManager() { return interruptAllocator; diff --git a/src/main/java/li/cil/oc2/common/vm/context/managed/ManagedVMContext.java b/src/main/java/li/cil/oc2/common/vm/context/managed/ManagedVMContext.java index 27c1c926..174ac1d8 100644 --- a/src/main/java/li/cil/oc2/common/vm/context/managed/ManagedVMContext.java +++ b/src/main/java/li/cil/oc2/common/vm/context/managed/ManagedVMContext.java @@ -9,7 +9,6 @@ import java.util.OptionalLong; import java.util.function.Supplier; public final class ManagedVMContext implements VMContext { - private final VMContext parent; private final ManagedMemoryMap memoryMap; private final ManagedInterruptController interruptController; private final ManagedMemoryRangeAllocator memoryRangeAllocator; @@ -20,7 +19,6 @@ public final class ManagedVMContext implements VMContext { /////////////////////////////////////////////////////////////////// public ManagedVMContext(final VMContext parent, final VMContextManagerCollection managers, final Supplier baseAddressSupplier) { - this.parent = parent; this.memoryRangeAllocator = new ManagedMemoryRangeAllocator(parent.getMemoryRangeAllocator(), managers.getMemoryRangeManager(), baseAddressSupplier); this.interruptAllocator = new ManagedInterruptAllocator(parent.getInterruptAllocator(), managers.getInterruptManager()); this.memoryMap = new ManagedMemoryMap(parent.getMemoryMap()); @@ -76,9 +74,4 @@ public final class ManagedVMContext implements VMContext { public VMLifecycleEventBus getEventBus() { return eventBus; } - - @Override - public void joinWorkerThread() { - parent.joinWorkerThread(); - } } diff --git a/src/test/java/li/cil/oc2/common/vm/AbstractTestMethod.java b/src/test/java/li/cil/oc2/common/bus/AbstractTestMethod.java similarity index 97% rename from src/test/java/li/cil/oc2/common/vm/AbstractTestMethod.java rename to src/test/java/li/cil/oc2/common/bus/AbstractTestMethod.java index e8262f99..81a5795a 100644 --- a/src/test/java/li/cil/oc2/common/vm/AbstractTestMethod.java +++ b/src/test/java/li/cil/oc2/common/bus/AbstractTestMethod.java @@ -1,4 +1,4 @@ -package li.cil.oc2.common.vm; +package li.cil.oc2.common.bus; import li.cil.oc2.api.bus.device.rpc.AbstractRPCMethod; import li.cil.oc2.api.bus.device.rpc.RPCParameter; diff --git a/src/test/java/li/cil/oc2/common/vm/RPCAdapterTests.java b/src/test/java/li/cil/oc2/common/bus/RPCMethodTests.java similarity index 98% rename from src/test/java/li/cil/oc2/common/vm/RPCAdapterTests.java rename to src/test/java/li/cil/oc2/common/bus/RPCMethodTests.java index cb694648..d2133f24 100644 --- a/src/test/java/li/cil/oc2/common/vm/RPCAdapterTests.java +++ b/src/test/java/li/cil/oc2/common/bus/RPCMethodTests.java @@ -1,4 +1,4 @@ -package li.cil.oc2.common.vm; +package li.cil.oc2.common.bus; import com.google.gson.*; import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue; @@ -7,7 +7,6 @@ import li.cil.oc2.api.bus.device.object.Callback; import li.cil.oc2.api.bus.device.object.ObjectDevice; import li.cil.oc2.api.bus.device.object.Parameter; import li.cil.oc2.api.bus.device.rpc.*; -import li.cil.oc2.common.bus.RPCDeviceBusAdapter; import li.cil.sedna.api.device.serial.SerialDevice; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -25,7 +24,7 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -public class RPCAdapterTests { +public class RPCMethodTests { private static final UUID DEVICE_UUID = java.util.UUID.randomUUID(); private TestSerialDevice serialDevice; diff --git a/src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java b/src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java index 0d7c735e..cd9bd1e0 100644 --- a/src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java +++ b/src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java @@ -54,8 +54,7 @@ public final class VMDeviceTests { return null; }).when(board).removeDevice(any()); - context = new GlobalVMContext(board, () -> { - }); + context = new GlobalVMContext(board); adapter = new VMDeviceBusAdapter(context); } diff --git a/src/test/java/li/cil/oc2/common/vm/package-info.java b/src/test/java/li/cil/oc2/common/bus/package-info.java similarity index 84% rename from src/test/java/li/cil/oc2/common/vm/package-info.java rename to src/test/java/li/cil/oc2/common/bus/package-info.java index f415ab90..44244ffe 100644 --- a/src/test/java/li/cil/oc2/common/vm/package-info.java +++ b/src/test/java/li/cil/oc2/common/bus/package-info.java @@ -1,6 +1,6 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -package li.cil.oc2.common.vm; +package li.cil.oc2.common.bus; import net.minecraft.MethodsReturnNonnullByDefault;