summaryrefslogtreecommitdiff
path: root/opendc-simulator
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2022-11-13 18:16:19 +0000
committerGitHub <noreply@github.com>2022-11-13 18:16:19 +0000
commit52eed48441693149993db79b63431b99e0973027 (patch)
treeba267db531bc3d81409ddfe9caeb6d3b5a65e8c8 /opendc-simulator
parent183cfa96910ebb74c668dea7ef98071966f8fcb9 (diff)
parent33d91ef30ad7bcb73365934fe536461210d1082a (diff)
merge: Increase minimum Java version to 17 (#115)
This pull request increases the minimum version of Java required by OpenDC to 17. This new version of Java introduces several new features compared to our old minimum version (11), which we attempt to apply in this conversion. ## Implementation Notes :hammer_and_pick: * Increase minimum Java version to Java 17 * Use RandomGenerator as randomness source * Add common dispatcher interface * Add compatibility with Kotlin coroutines * Use InstantSource as time source * Re-implement SimulationScheduler as Dispatcher * Replace use of CoroutineContext by Dispatcher ## External Dependencies :four_leaf_clover: * Java 17 ## Breaking API Changes :warning: * The use of `CoroutineContext` and `Clock` as parameters of classes has been replaced by the `Dispatcher` interface. * The use of `Clock` has been replaced by `InstantSource` which does not carry time zone info. * The use of `Random` and `SplittableRandom` as parameter type has been replaced by `RandomGenerator`
Diffstat (limited to 'opendc-simulator')
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt8
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java4
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java6
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt48
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt12
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt20
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt38
-rw-r--r--opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt16
-rw-r--r--opendc-simulator/opendc-simulator-core/build.gradle.kts1
-rw-r--r--opendc-simulator/opendc-simulator-core/src/main/java/org/opendc/simulator/SimulationDispatcher.java (renamed from opendc-simulator/opendc-simulator-core/src/main/java/org/opendc/simulator/SimulationScheduler.java)134
-rw-r--r--opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationBuilders.kt74
-rw-r--r--opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationController.kt34
-rw-r--r--opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineDispatcher.kt98
-rw-r--r--opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineScope.kt36
-rw-r--r--opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/SimulationDispatcherTest.kt (renamed from opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/SimulationSchedulerTest.kt)23
-rw-r--r--opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/kotlin/SimulationBuildersTest.kt98
-rw-r--r--opendc-simulator/opendc-simulator-flow/build.gradle.kts4
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt8
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowEngine.java28
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStage.java4
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InPort.java4
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutPort.java4
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt26
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt4
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt4
-rw-r--r--opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt18
-rw-r--r--opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt14
-rw-r--r--opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt4
-rw-r--r--opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt12
-rw-r--r--opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt14
-rw-r--r--opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt8
-rw-r--r--opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt4
32 files changed, 425 insertions, 385 deletions
diff --git a/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt b/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt
index ec032070..eea46b95 100644
--- a/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/jmh/kotlin/org/opendc/simulator/compute/SimMachineBenchmarks.kt
@@ -74,7 +74,7 @@ class SimMachineBenchmarks {
@Benchmark
fun benchmarkBareMetal() {
return runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
return@runSimulation machine.runWorkload(trace.createWorkload(0))
@@ -84,7 +84,7 @@ class SimMachineBenchmarks {
@Benchmark
fun benchmarkSpaceSharedHypervisor() {
return runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.forwardingMultiplexer(), SplittableRandom(1))
@@ -105,7 +105,7 @@ class SimMachineBenchmarks {
@Benchmark
fun benchmarkFairShareHypervisorSingle() {
return runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1))
@@ -126,7 +126,7 @@ class SimMachineBenchmarks {
@Benchmark
fun benchmarkFairShareHypervisorDouble() {
return runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
val hypervisor = SimHypervisor.create(FlowMultiplexerFactory.maxMinMultiplexer(), SplittableRandom(1))
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java
index 52d04052..05b40cf8 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java
+++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/SimPsuFactories.java
@@ -22,7 +22,7 @@
package org.opendc.simulator.compute;
-import java.time.Clock;
+import java.time.InstantSource;
import org.jetbrains.annotations.NotNull;
import org.opendc.simulator.compute.model.ProcessingUnit;
import org.opendc.simulator.compute.power.CpuPowerModel;
@@ -117,7 +117,7 @@ public class SimPsuFactories {
private final FlowStage stage;
private final OutPort out;
private final CpuPowerModel model;
- private final Clock clock;
+ private final InstantSource clock;
private double targetFreq;
private double totalUsage;
diff --git a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java
index 4ebcba71..a1623351 100644
--- a/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java
+++ b/opendc-simulator/opendc-simulator-compute/src/main/java/org/opendc/simulator/compute/kernel/SimHypervisor.java
@@ -22,7 +22,7 @@
package org.opendc.simulator.compute.kernel;
-import java.time.Clock;
+import java.time.InstantSource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -253,7 +253,7 @@ public final class SimHypervisor implements SimWorkload {
private final FlowMultiplexer multiplexer;
private final FlowStage stage;
private final List<ScalingGovernor> scalingGovernors;
- private final Clock clock;
+ private final InstantSource clock;
private final HvCounters counters;
private long lastCounterUpdate;
@@ -526,7 +526,7 @@ public final class SimHypervisor implements SimWorkload {
private final VmInterferenceMember interferenceMember;
private final FlowStage stage;
private final FlowMultiplexer multiplexer;
- private final Clock clock;
+ private final InstantSource clock;
private final List<VCpu> cpus;
private final SimAbstractMachine.Memory memory;
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt
index 2acf6ec7..58b01e06 100644
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/SimMachineTest.kt
@@ -72,7 +72,7 @@ class SimMachineTest {
@Test
fun testFlopsWorkload() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -83,7 +83,7 @@ class SimMachineTest {
machine.runWorkload(SimWorkloads.flops(2_000, /*utilization*/ 1.0))
// Two cores execute 1000 MFlOps per second (1000 ms)
- assertEquals(1000, clock.millis())
+ assertEquals(1000, timeSource.millis())
}
@Test
@@ -97,7 +97,7 @@ class SimMachineTest {
}
val trace = builder.build()
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
graph,
@@ -107,12 +107,12 @@ class SimMachineTest {
machine.runWorkload(trace.createWorkload(0))
// Two cores execute 1000 MFlOps per second (1000 ms)
- assertEquals(1000000000, clock.millis())
+ assertEquals(1000000000, timeSource.millis())
}
@Test
fun testDualSocketMachine() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val cpuNode = machineModel.cpus[0].node
@@ -128,12 +128,12 @@ class SimMachineTest {
machine.runWorkload(SimWorkloads.flops(2_000, /*utilization*/ 1.0))
// Two sockets with two cores execute 2000 MFlOps per second (500 ms)
- assertEquals(500, clock.millis())
+ assertEquals(500, timeSource.millis())
}
@Test
fun testPower() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
graph,
@@ -156,7 +156,7 @@ class SimMachineTest {
@Test
fun testCapacityClamp() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -184,7 +184,7 @@ class SimMachineTest {
@Test
fun testMemory() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -206,7 +206,7 @@ class SimMachineTest {
@Test
fun testMemoryUsage() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -225,12 +225,12 @@ class SimMachineTest {
override fun snapshot(): SimWorkload = TODO()
})
- assertEquals(1000, clock.millis())
+ assertEquals(1000, timeSource.millis())
}
@Test
fun testNetUsage() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -253,12 +253,12 @@ class SimMachineTest {
override fun snapshot(): SimWorkload = TODO()
})
- assertEquals(40, clock.millis())
+ assertEquals(40, timeSource.millis())
}
@Test
fun testDiskReadUsage() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -278,12 +278,12 @@ class SimMachineTest {
override fun snapshot(): SimWorkload = TODO()
})
- assertEquals(4000, clock.millis())
+ assertEquals(4000, timeSource.millis())
}
@Test
fun testDiskWriteUsage() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -303,12 +303,12 @@ class SimMachineTest {
override fun snapshot(): SimWorkload = TODO()
})
- assertEquals(4000, clock.millis())
+ assertEquals(4000, timeSource.millis())
}
@Test
fun testCancellation() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -325,12 +325,12 @@ class SimMachineTest {
// Ignore
}
- assertEquals(0, clock.millis())
+ assertEquals(0, timeSource.millis())
}
@Test
fun testConcurrentRuns() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -351,7 +351,7 @@ class SimMachineTest {
@Test
fun testCatchStartFailure() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -367,7 +367,7 @@ class SimMachineTest {
@Test
fun testCatchStopFailure() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -384,7 +384,7 @@ class SimMachineTest {
@Test
fun testCatchShutdownFailure() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -400,7 +400,7 @@ class SimMachineTest {
@Test
fun testCatchNestedFailure() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt
index 79669d40..99f47b2f 100644
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimFairShareHypervisorTest.kt
@@ -74,7 +74,7 @@ internal class SimFairShareHypervisorTest {
SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1)
).createWorkload(0)
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, model)
@@ -93,7 +93,7 @@ internal class SimFairShareHypervisorTest {
{ assertEquals(319781, hypervisor.counters.cpuActiveTime, "Active time does not match") },
{ assertEquals(880219, hypervisor.counters.cpuIdleTime, "Idle time does not match") },
{ assertEquals(28125, hypervisor.counters.cpuStealTime, "Steal time does not match") },
- { assertEquals(1200000, clock.millis()) { "Current time is correct" } }
+ { assertEquals(1200000, timeSource.millis()) { "Current time is correct" } }
)
}
@@ -118,7 +118,7 @@ internal class SimFairShareHypervisorTest {
SimTraceFragment(duration * 3000, duration * 1000, 73.0, 1)
).createWorkload(0)
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, model)
@@ -145,7 +145,7 @@ internal class SimFairShareHypervisorTest {
{ assertEquals(329250, hypervisor.counters.cpuActiveTime, "Active time does not match") },
{ assertEquals(870750, hypervisor.counters.cpuIdleTime, "Idle time does not match") },
{ assertEquals(318750, hypervisor.counters.cpuStealTime, "Steal time does not match") },
- { assertEquals(1200000, clock.millis()) }
+ { assertEquals(1200000, timeSource.millis()) }
)
}
@@ -157,7 +157,7 @@ internal class SimFairShareHypervisorTest {
/*memory*/ List(4) { MemoryUnit("Crucial", "MTA18ASF4G72AZ-3G2B1", 3200.0, 32_000) }
)
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, model)
@@ -184,7 +184,7 @@ internal class SimFairShareHypervisorTest {
.addGroup(setOf("a", "n"), 0.1, 0.8)
.build()
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, model)
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt
index d11b91ee..93b67aa3 100644
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/kernel/SimSpaceSharedHypervisorTest.kt
@@ -75,7 +75,7 @@ internal class SimSpaceSharedHypervisorTest {
SimTraceFragment(duration * 3000, duration * 1000, 183.0, 1)
).createWorkload(0)
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
@@ -89,7 +89,7 @@ internal class SimSpaceSharedHypervisorTest {
hypervisor.removeMachine(vm)
machine.cancel()
- assertEquals(5 * 60L * 4000, clock.millis()) { "Took enough time" }
+ assertEquals(5 * 60L * 4000, timeSource.millis()) { "Took enough time" }
}
/**
@@ -99,7 +99,7 @@ internal class SimSpaceSharedHypervisorTest {
fun testRuntimeWorkload() = runSimulation {
val duration = 5 * 60L * 1000
val workload = SimWorkloads.runtime(duration, 1.0)
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
@@ -113,7 +113,7 @@ internal class SimSpaceSharedHypervisorTest {
machine.cancel()
- assertEquals(duration, clock.millis()) { "Took enough time" }
+ assertEquals(duration, timeSource.millis()) { "Took enough time" }
}
/**
@@ -123,7 +123,7 @@ internal class SimSpaceSharedHypervisorTest {
fun testFlopsWorkload() = runSimulation {
val duration = 5 * 60L * 1000
val workload = SimWorkloads.flops((duration * 3.2).toLong(), 1.0)
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
@@ -135,7 +135,7 @@ internal class SimSpaceSharedHypervisorTest {
vm.runWorkload(workload)
machine.cancel()
- assertEquals(duration, clock.millis()) { "Took enough time" }
+ assertEquals(duration, timeSource.millis()) { "Took enough time" }
}
/**
@@ -144,7 +144,7 @@ internal class SimSpaceSharedHypervisorTest {
@Test
fun testTwoWorkloads() = runSimulation {
val duration = 5 * 60L * 1000
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
@@ -165,7 +165,7 @@ internal class SimSpaceSharedHypervisorTest {
machine.cancel()
- assertEquals(duration * 2, clock.millis()) { "Took enough time" }
+ assertEquals(duration * 2, timeSource.millis()) { "Took enough time" }
}
/**
@@ -173,7 +173,7 @@ internal class SimSpaceSharedHypervisorTest {
*/
@Test
fun testConcurrentWorkloadFails() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
@@ -200,7 +200,7 @@ internal class SimSpaceSharedHypervisorTest {
*/
@Test
fun testConcurrentWorkloadSucceeds() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt
index d0b0efaa..08bb6509 100644
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimChainWorkloadTest.kt
@@ -59,7 +59,7 @@ class SimChainWorkloadTest {
@Test
fun testMultipleWorkloads() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -75,12 +75,12 @@ class SimChainWorkloadTest {
machine.runWorkload(workload)
- assertEquals(2000, clock.millis())
+ assertEquals(2000, timeSource.millis())
}
@Test
fun testStartFailure() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -100,12 +100,12 @@ class SimChainWorkloadTest {
assertThrows<IllegalStateException> { machine.runWorkload(workload) }
- assertEquals(0, clock.millis())
+ assertEquals(0, timeSource.millis())
}
@Test
fun testStartFailureSecond() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -126,12 +126,12 @@ class SimChainWorkloadTest {
assertThrows<IllegalStateException> { machine.runWorkload(workload) }
- assertEquals(1000, clock.millis())
+ assertEquals(1000, timeSource.millis())
}
@Test
fun testStopFailure() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -150,12 +150,12 @@ class SimChainWorkloadTest {
assertThrows<IllegalStateException> { machine.runWorkload(workload) }
- assertEquals(1000, clock.millis())
+ assertEquals(1000, timeSource.millis())
}
@Test
fun testStopFailureSecond() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -175,12 +175,12 @@ class SimChainWorkloadTest {
assertThrows<IllegalStateException> { machine.runWorkload(workload) }
- assertEquals(2000, clock.millis())
+ assertEquals(2000, timeSource.millis())
}
@Test
fun testStartAndStopFailure() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -201,12 +201,12 @@ class SimChainWorkloadTest {
val exc = assertThrows<IllegalStateException> { machine.runWorkload(workload) }
assertEquals(2, exc.cause!!.suppressedExceptions.size)
- assertEquals(1000, clock.millis())
+ assertEquals(1000, timeSource.millis())
}
@Test
fun testShutdownAndStopFailure() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -227,12 +227,12 @@ class SimChainWorkloadTest {
val exc = assertThrows<IllegalStateException> { machine.runWorkload(workload) }
assertEquals(1, exc.cause!!.suppressedExceptions.size)
- assertEquals(1000, clock.millis())
+ assertEquals(1000, timeSource.millis())
}
@Test
fun testShutdownAndStartFailure() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -255,12 +255,12 @@ class SimChainWorkloadTest {
val exc = assertThrows<IllegalStateException> { machine.runWorkload(workload) }
assertEquals(1, exc.cause!!.suppressedExceptions.size)
- assertEquals(1000, clock.millis())
+ assertEquals(1000, timeSource.millis())
}
@Test
fun testSnapshot() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(graph, machineModel)
@@ -276,10 +276,10 @@ class SimChainWorkloadTest {
job.join()
- assertEquals(2000, clock.millis())
+ assertEquals(2000, timeSource.millis())
machine.runWorkload(snapshot)
- assertEquals(3500, clock.millis())
+ assertEquals(3500, timeSource.millis())
}
}
diff --git a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt
index e3b6e6c5..5c888fbc 100644
--- a/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt
+++ b/opendc-simulator/opendc-simulator-compute/src/test/kotlin/org/opendc/simulator/compute/workload/SimTraceWorkloadTest.kt
@@ -53,7 +53,7 @@ class SimTraceWorkloadTest {
@Test
fun testSmoke() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -71,12 +71,12 @@ class SimTraceWorkloadTest {
machine.runWorkload(workload)
- assertEquals(4000, clock.millis())
+ assertEquals(4000, timeSource.millis())
}
@Test
fun testOffset() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -94,12 +94,12 @@ class SimTraceWorkloadTest {
machine.runWorkload(workload)
- assertEquals(5000, clock.millis())
+ assertEquals(5000, timeSource.millis())
}
@Test
fun testSkipFragment() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -118,12 +118,12 @@ class SimTraceWorkloadTest {
delay(1000L)
machine.runWorkload(workload)
- assertEquals(4000, clock.millis())
+ assertEquals(4000, timeSource.millis())
}
@Test
fun testZeroCores() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val machine = SimBareMetalMachine.create(
@@ -141,6 +141,6 @@ class SimTraceWorkloadTest {
machine.runWorkload(workload)
- assertEquals(4000, clock.millis())
+ assertEquals(4000, timeSource.millis())
}
}
diff --git a/opendc-simulator/opendc-simulator-core/build.gradle.kts b/opendc-simulator/opendc-simulator-core/build.gradle.kts
index 0de96a8e..0ae95d42 100644
--- a/opendc-simulator/opendc-simulator-core/build.gradle.kts
+++ b/opendc-simulator/opendc-simulator-core/build.gradle.kts
@@ -28,5 +28,6 @@ plugins {
}
dependencies {
+ api(projects.opendc.opendcCommon)
api(libs.kotlinx.coroutines)
}
diff --git a/opendc-simulator/opendc-simulator-core/src/main/java/org/opendc/simulator/SimulationScheduler.java b/opendc-simulator/opendc-simulator-core/src/main/java/org/opendc/simulator/SimulationDispatcher.java
index 305bdf5e..8c74aacf 100644
--- a/opendc-simulator/opendc-simulator-core/src/main/java/org/opendc/simulator/SimulationScheduler.java
+++ b/opendc-simulator/opendc-simulator-core/src/main/java/org/opendc/simulator/SimulationDispatcher.java
@@ -22,17 +22,17 @@
package org.opendc.simulator;
-import java.time.Clock;
import java.time.Instant;
-import java.time.ZoneId;
-import java.util.concurrent.Executor;
+import java.time.InstantSource;
+import org.opendc.common.Dispatcher;
+import org.opendc.common.DispatcherHandle;
/**
- * A scheduler is used by simulations to manage execution of (future) tasks, providing a controllable (virtual) clock to
- * skip over delays.
+ * A {@link Dispatcher} used by simulations to manage execution of (future) tasks, providing a controllable (virtual)
+ * clock to skip over delays.
*
* <p>
- * The scheduler can be queried to advance the time (via {@link #advanceBy}), run all the scheduled tasks advancing the
+ * The dispatcher can be queried to advance the time (via {@link #advanceBy}), run all the scheduled tasks advancing the
* virtual time as needed (via {@link #advanceUntilIdle}), or run the tasks that are scheduled to run as soon as
* possible but have not yet been dispatched (via {@link #runCurrent}). These methods execute the pending tasks using
* a single thread.
@@ -40,7 +40,7 @@ import java.util.concurrent.Executor;
* <p>
* This class is not thread-safe and must not be used concurrently by multiple threads.
*/
-public final class SimulationScheduler implements Executor {
+public final class SimulationDispatcher implements Dispatcher {
/**
* The {@link TaskQueue} containing the pending tasks.
*/
@@ -57,36 +57,27 @@ public final class SimulationScheduler implements Executor {
private int count = 0;
/**
- * The {@link Clock} instance linked to this scheduler.
+ * The {@link InstantSource} instance linked to this scheduler.
*/
- private final SimulationClock clock = new SimulationClock(this, ZoneId.systemDefault());
+ private final SimulationClock timeSource = new SimulationClock(this);
/**
- * Construct a {@link SimulationScheduler} instance with the specified initial time.
+ * Construct a {@link SimulationDispatcher} instance with the specified initial time.
*
* @param initialTimeMs The initial virtual time of the scheduler in milliseconds since epoch.
*/
- public SimulationScheduler(long initialTimeMs) {
+ public SimulationDispatcher(long initialTimeMs) {
this.currentTime = initialTimeMs;
}
/**
- * Construct a {@link SimulationScheduler} instance with the initial time set to UNIX Epoch 0.
+ * Construct a {@link SimulationDispatcher} instance with the initial time set to UNIX Epoch 0.
*/
- public SimulationScheduler() {
+ public SimulationDispatcher() {
this(0);
}
/**
- * Return the virtual clock associated with this dispatcher.
- *
- * @return A {@link Clock} tracking the virtual time of the dispatcher.
- */
- public Clock getClock() {
- return clock;
- }
-
- /**
* Return the current virtual timestamp of the dispatcher (in milliseconds since epoch).
*
* @return A long value representing the virtual timestamp of the dispatcher in milliseconds since epoch.
@@ -96,37 +87,30 @@ public final class SimulationScheduler implements Executor {
}
/**
- * Schedule a <code>task</code> that executes after the specified <code>delayMs</code>.
+ * Return the virtual time source associated with this dispatcher.
*
- * @param delayMs The time from now until the execution of the task (in milliseconds).
- * @param task The task to execute after the delay.
- * @return The identifier of the task that can be used together with the timestamp of the task to cancel it.
+ * @return A {@link InstantSource} tracking the virtual time of the dispatcher.
*/
- public int schedule(long delayMs, Runnable task) {
- if (delayMs < 0) {
- throw new IllegalArgumentException(
- "Attempted scheduling an event earlier in time (delay " + delayMs + " ms)");
- }
+ @Override
+ public InstantSource getTimeSource() {
+ return timeSource;
+ }
+ @Override
+ public void schedule(long delayMs, Runnable command) {
+ internalSchedule(delayMs, command);
+ }
+
+ @Override
+ public DispatcherHandle scheduleCancellable(long delayMs, Runnable command) {
long target = currentTime + delayMs;
if (target < 0) {
target = Long.MAX_VALUE;
}
- int id = count++;
- queue.add(target, id, task);
- return id;
- }
-
- /**
- * Cancel a pending task.
- *
- * @param deadline The deadline of the task.
- * @param id The identifier of the task (returned by {@link #schedule(long, Runnable)}).
- * @return A boolean indicating whether a task was actually cancelled.
- */
- public boolean cancel(long deadline, int id) {
- return queue.remove(deadline, id);
+ long deadline = target;
+ int id = internalSchedule(delayMs, command);
+ return () -> internalCancel(deadline, id);
}
/**
@@ -198,50 +182,62 @@ public final class SimulationScheduler implements Executor {
}
/**
- * Schedule the specified command to run at this moment of virtual time.
+ * Schedule a <code>task</code> that executes after the specified <code>delayMs</code>.
*
- * @param command The command to execute.
+ * @param delayMs The time from now until the execution of the task (in milliseconds).
+ * @param task The task to execute after the delay.
+ * @return The identifier of the task that can be used together with the timestamp of the task to cancel it.
*/
- @Override
- public void execute(Runnable command) {
- schedule(0, command);
+ private int internalSchedule(long delayMs, Runnable task) {
+ if (delayMs < 0) {
+ throw new IllegalArgumentException(
+ "Attempted scheduling an event earlier in time (delay " + delayMs + " ms)");
+ }
+
+ long target = currentTime + delayMs;
+ if (target < 0) {
+ target = Long.MAX_VALUE;
+ }
+
+ int id = count++;
+ queue.add(target, id, task);
+ return id;
}
/**
- * A {@link Clock} implementation for a {@link SimulationScheduler}.
+ * Cancel a pending task.
+ *
+ * @param deadline The deadline of the task.
+ * @param id The identifier of the task (returned by {@link #internalSchedule(long, Runnable)}).
+ * @return A boolean indicating whether a task was actually cancelled.
*/
- private static class SimulationClock extends Clock {
- private final SimulationScheduler scheduler;
- private final ZoneId zone;
-
- SimulationClock(SimulationScheduler scheduler, ZoneId zone) {
- this.scheduler = scheduler;
- this.zone = zone;
- }
+ private boolean internalCancel(long deadline, int id) {
+ return queue.remove(deadline, id);
+ }
- @Override
- public ZoneId getZone() {
- return zone;
- }
+ /**
+ * A {@link InstantSource} implementation for a {@link SimulationDispatcher}.
+ */
+ private static class SimulationClock implements InstantSource {
+ private final SimulationDispatcher dispatcher;
- @Override
- public Clock withZone(ZoneId zoneId) {
- return new SimulationClock(scheduler, zone);
+ SimulationClock(SimulationDispatcher dispatcher) {
+ this.dispatcher = dispatcher;
}
@Override
public Instant instant() {
- return Instant.ofEpochMilli(scheduler.currentTime);
+ return Instant.ofEpochMilli(dispatcher.currentTime);
}
@Override
public long millis() {
- return scheduler.currentTime;
+ return dispatcher.currentTime;
}
@Override
public String toString() {
- return "SimulationClock[time=" + millis() + "ms]";
+ return "SimulationDispatcher.InstantSource[time=" + millis() + "ms]";
}
}
}
diff --git a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationBuilders.kt b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationBuilders.kt
index 882a0fc5..6e568137 100644
--- a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationBuilders.kt
+++ b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationBuilders.kt
@@ -22,11 +22,18 @@
package org.opendc.simulator.kotlin
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
+import kotlinx.coroutines.NonCancellable.children
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
-import org.opendc.simulator.SimulationScheduler
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.opendc.common.DispatcherProvider
+import org.opendc.common.asCoroutineDispatcher
+import org.opendc.simulator.SimulationDispatcher
+import java.time.InstantSource
import kotlin.coroutines.ContinuationInterceptor
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@@ -54,16 +61,16 @@ import kotlin.coroutines.EmptyCoroutineContext
* The simulation is run in a single thread, unless other [CoroutineDispatcher] are used for child coroutines.
* Because of this, child coroutines are not executed in parallel to [body].
* In order for the spawned-off asynchronous code to actually be executed, one must either [yield] or suspend the
- * body some other way, or use commands that control scheduling (see [SimulationScheduler]).
+ * body some other way, or use commands that control scheduling (see [SimulationDispatcher]).
*/
@OptIn(ExperimentalCoroutinesApi::class)
public fun runSimulation(
context: CoroutineContext = EmptyCoroutineContext,
- scheduler: SimulationScheduler = SimulationScheduler(),
+ scheduler: SimulationDispatcher = SimulationDispatcher(),
body: suspend SimulationCoroutineScope.() -> Unit
) {
- val (safeContext, dispatcher) = context.checkArguments(scheduler)
- val startingJobs = safeContext.activeJobs()
+ val (safeContext, job, dispatcher) = context.checkArguments(scheduler)
+ val startingJobs = job.activeJobs()
val scope = SimulationCoroutineScope(safeContext)
val deferred = scope.async {
body(scope)
@@ -72,7 +79,7 @@ public fun runSimulation(
deferred.getCompletionExceptionOrNull()?.let {
throw it
}
- val endingJobs = safeContext.activeJobs()
+ val endingJobs = job.activeJobs()
if ((endingJobs - startingJobs).isNotEmpty()) {
throw IllegalStateException("Test finished with active jobs: $endingJobs")
}
@@ -82,24 +89,51 @@ public fun runSimulation(
* Convenience method for calling [runSimulation] on an existing [SimulationCoroutineScope].
*/
public fun SimulationCoroutineScope.runSimulation(block: suspend SimulationCoroutineScope.() -> Unit): Unit =
- runSimulation(coroutineContext, scheduler, block)
+ runSimulation(coroutineContext, dispatcher, block)
+
+private fun CoroutineContext.checkArguments(scheduler: SimulationDispatcher): Triple<CoroutineContext, Job, SimulationController> {
+ val job = get(Job) ?: SupervisorJob()
+ val dispatcher = get(ContinuationInterceptor) ?: scheduler.asCoroutineDispatcher()
+ val simulationDispatcher = dispatcher.asSimulationDispatcher()
+ return Triple(this + dispatcher + job, job, simulationDispatcher.asController())
+}
+
+private fun Job.activeJobs(): Set<Job> {
+ return children.filter { it.isActive }.toSet()
+}
/**
- * Convenience method for calling [runSimulation] on an existing [SimulationCoroutineDispatcher].
+ * Convert a [ContinuationInterceptor] into a [SimulationDispatcher] if possible.
*/
-public fun SimulationCoroutineDispatcher.runSimulation(block: suspend SimulationCoroutineScope.() -> Unit): Unit =
- runSimulation(this, scheduler, block)
-
-private fun CoroutineContext.checkArguments(scheduler: SimulationScheduler): Pair<CoroutineContext, SimulationController> {
- val dispatcher = get(ContinuationInterceptor).run {
- this?.let { require(this is SimulationController) { "Dispatcher must implement SimulationController: $this" } }
- this ?: SimulationCoroutineDispatcher(scheduler)
- }
+internal fun ContinuationInterceptor.asSimulationDispatcher(): SimulationDispatcher {
+ val provider = this as? DispatcherProvider ?: throw IllegalArgumentException(
+ "DispatcherProvider such as SimulatorCoroutineDispatcher as the ContinuationInterceptor(Dispatcher) is required"
+ )
- val job = get(Job) ?: SupervisorJob()
- return Pair(this + dispatcher + job, dispatcher as SimulationController)
+ return provider.dispatcher as? SimulationDispatcher ?: throw IllegalArgumentException("Active dispatcher is not a SimulationDispatcher")
}
-private fun CoroutineContext.activeJobs(): Set<Job> {
- return checkNotNull(this[Job]).children.filter { it.isActive }.toSet()
+/**
+ * Helper method to convert a [SimulationDispatcher] into a [SimulationController].
+ */
+internal fun SimulationDispatcher.asController(): SimulationController {
+ return object : SimulationController {
+ override val dispatcher: SimulationDispatcher
+ get() = this@asController
+
+ override val timeSource: InstantSource
+ get() = this@asController.timeSource
+
+ override fun advanceUntilIdle() {
+ dispatcher.advanceUntilIdle()
+ }
+
+ override fun advanceBy(delayMs: Long) {
+ dispatcher.advanceBy(delayMs)
+ }
+
+ override fun runCurrent() {
+ dispatcher.runCurrent()
+ }
+ }
}
diff --git a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationController.kt b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationController.kt
index f96b2326..f7470ad9 100644
--- a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationController.kt
+++ b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationController.kt
@@ -23,30 +23,48 @@
package org.opendc.simulator.kotlin
import kotlinx.coroutines.CoroutineDispatcher
-import org.opendc.simulator.SimulationScheduler
-import java.time.Clock
+import org.opendc.simulator.SimulationDispatcher
+import java.time.InstantSource
/**
- * Control the virtual clock of a [CoroutineDispatcher].
+ * Interface to control the virtual clock of a [CoroutineDispatcher].
*/
public interface SimulationController {
/**
* The current virtual clock as it is known to this Dispatcher.
*/
- public val clock: Clock
+ public val timeSource: InstantSource
/**
- * The [SimulationScheduler] driving the simulation.
+ * The current virtual timestamp of the dispatcher (in milliseconds since epoch).
*/
- public val scheduler: SimulationScheduler
+ public val currentTime: Long
+ get() = timeSource.millis()
+
+ /**
+ * Return the [SimulationDispatcher] driving the simulation.
+ */
+ public val dispatcher: SimulationDispatcher
/**
* Immediately execute all pending tasks and advance the virtual clock-time to the last delay.
*
* If new tasks are scheduled due to advancing virtual time, they will be executed before `advanceUntilIdle`
* returns.
+ */
+ public fun advanceUntilIdle()
+
+ /**
+ * Move the virtual clock of this dispatcher forward by the specified amount, running the scheduled tasks in the
+ * meantime.
*
- * @return the amount of delay-time that this Dispatcher's clock has been forwarded in milliseconds.
+ * @param delayMs The amount of time to move the virtual clock forward (in milliseconds).
+ * @throws IllegalStateException if passed a negative <code>delay</code>.
+ */
+ public fun advanceBy(delayMs: Long)
+
+ /**
+ * Execute the tasks that are scheduled to execute at this moment of virtual time.
*/
- public fun advanceUntilIdle(): Long
+ public fun runCurrent()
}
diff --git a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineDispatcher.kt b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineDispatcher.kt
deleted file mode 100644
index cacbbbf7..00000000
--- a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineDispatcher.kt
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package org.opendc.simulator.kotlin
-
-import kotlinx.coroutines.CancellableContinuation
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.Delay
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.InternalCoroutinesApi
-import org.opendc.simulator.SimulationScheduler
-import java.lang.Runnable
-import java.time.Clock
-import kotlin.coroutines.CoroutineContext
-
-/**
- * A [CoroutineDispatcher] that performs both immediate execution of coroutines on the main thread and uses a virtual
- * clock for time management.
- *
- * @param scheduler The [SimulationScheduler] used to manage the execution of future tasks.
- */
-@OptIn(InternalCoroutinesApi::class)
-public class SimulationCoroutineDispatcher(
- override val scheduler: SimulationScheduler = SimulationScheduler()
-) : CoroutineDispatcher(), SimulationController, Delay {
- /**
- * The virtual clock of this dispatcher.
- */
- override val clock: Clock = scheduler.clock
-
- override fun dispatch(context: CoroutineContext, block: Runnable) {
- block.run()
- }
-
- override fun dispatchYield(context: CoroutineContext, block: Runnable) {
- scheduler.execute(block)
- }
-
- @OptIn(ExperimentalCoroutinesApi::class)
- override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) {
- scheduler.schedule(timeMillis, CancellableContinuationRunnable(continuation) { resumeUndispatched(Unit) })
- }
-
- override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle {
- return object : DisposableHandle {
- private val deadline = (scheduler.currentTime + timeMillis).let { if (it >= 0) it else Long.MAX_VALUE }
- private val id = scheduler.schedule(timeMillis, block)
-
- override fun dispose() {
- scheduler.cancel(deadline, id)
- }
- }
- }
-
- override fun toString(): String {
- return "SimulationCoroutineDispatcher[time=${scheduler.currentTime}ms]"
- }
-
- override fun advanceUntilIdle(): Long {
- val scheduler = scheduler
- val oldTime = scheduler.currentTime
-
- scheduler.advanceUntilIdle()
-
- return scheduler.currentTime - oldTime
- }
-
- /**
- * This class exists to allow cleanup code to avoid throwing for cancelled continuations scheduled
- * in the future.
- */
- private class CancellableContinuationRunnable<T>(
- @JvmField val continuation: CancellableContinuation<T>,
- private val block: CancellableContinuation<T>.() -> Unit
- ) : Runnable {
- override fun run() = continuation.block()
- }
-}
diff --git a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineScope.kt b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineScope.kt
index 6be8e67a..ca49fc53 100644
--- a/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineScope.kt
+++ b/opendc-simulator/opendc-simulator-core/src/main/kotlin/org/opendc/simulator/kotlin/SimulationCoroutineScope.kt
@@ -24,7 +24,8 @@ package org.opendc.simulator.kotlin
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
-import org.opendc.simulator.SimulationScheduler
+import org.opendc.common.asCoroutineDispatcher
+import org.opendc.simulator.SimulationDispatcher
import kotlin.coroutines.ContinuationInterceptor
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
@@ -34,33 +35,28 @@ import kotlin.coroutines.EmptyCoroutineContext
*/
public interface SimulationCoroutineScope : CoroutineScope, SimulationController
-private class SimulationCoroutineScopeImpl(
- override val coroutineContext: CoroutineContext
-) :
- SimulationCoroutineScope,
- SimulationController by coroutineContext.simulationController
-
/**
* A scope which provides detailed control over the execution of coroutines for simulations.
*
* If the provided context does not provide a [ContinuationInterceptor] (Dispatcher) or [CoroutineExceptionHandler], the
- * scope adds [SimulationCoroutineDispatcher] automatically.
+ * scope adds a dispatcher automatically.
*/
-@Suppress("FunctionName")
public fun SimulationCoroutineScope(
context: CoroutineContext = EmptyCoroutineContext,
- scheduler: SimulationScheduler = SimulationScheduler()
+ scheduler: SimulationDispatcher = SimulationDispatcher()
): SimulationCoroutineScope {
var safeContext = context
- if (context[ContinuationInterceptor] == null) safeContext += SimulationCoroutineDispatcher(scheduler)
- return SimulationCoroutineScopeImpl(safeContext)
-}
+ val simulationDispatcher: SimulationDispatcher
+ val interceptor = context[ContinuationInterceptor]
-private inline val CoroutineContext.simulationController: SimulationController
- get() {
- val handler = this[ContinuationInterceptor]
- return handler as? SimulationController ?: throw IllegalArgumentException(
- "SimulationCoroutineScope requires a SimulationController such as SimulatorCoroutineDispatcher as " +
- "the ContinuationInterceptor (Dispatcher)"
- )
+ if (interceptor != null) {
+ simulationDispatcher = interceptor.asSimulationDispatcher()
+ } else {
+ simulationDispatcher = scheduler
+ safeContext += scheduler.asCoroutineDispatcher()
}
+
+ return object : SimulationCoroutineScope, SimulationController by simulationDispatcher.asController() {
+ override val coroutineContext: CoroutineContext = safeContext
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/SimulationSchedulerTest.kt b/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/SimulationDispatcherTest.kt
index eca3b582..600102be 100644
--- a/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/SimulationSchedulerTest.kt
+++ b/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/SimulationDispatcherTest.kt
@@ -28,15 +28,15 @@ import org.junit.jupiter.api.assertThrows
import java.time.Instant
/**
- * Test suite for the [SimulationScheduler] class.
+ * Test suite for the [SimulationDispatcher] class.
*/
-class SimulationSchedulerTest {
+class SimulationDispatcherTest {
/**
- * Test the basic functionality of [SimulationScheduler.runCurrent].
+ * Test the basic functionality of [SimulationDispatcher.runCurrent].
*/
@Test
fun testRunCurrent() {
- val scheduler = SimulationScheduler()
+ val scheduler = SimulationDispatcher()
var count = 0
scheduler.schedule(1) { count += 1 }
@@ -58,11 +58,11 @@ class SimulationSchedulerTest {
}
/**
- * Test the clock of the [SimulationScheduler].
+ * Test the clock of the [SimulationDispatcher].
*/
@Test
fun testClock() {
- val scheduler = SimulationScheduler()
+ val scheduler = SimulationDispatcher()
var count = 0
scheduler.schedule(1) { count += 1 }
@@ -70,8 +70,8 @@ class SimulationSchedulerTest {
scheduler.advanceBy(2)
assertEquals(2, scheduler.currentTime)
- assertEquals(2, scheduler.clock.millis())
- assertEquals(Instant.ofEpochMilli(2), scheduler.clock.instant())
+ assertEquals(2, scheduler.timeSource.millis())
+ assertEquals(Instant.ofEpochMilli(2), scheduler.timeSource.instant())
}
/**
@@ -79,7 +79,7 @@ class SimulationSchedulerTest {
*/
@Test
fun testAdvanceByLargeDelays() {
- val scheduler = SimulationScheduler()
+ val scheduler = SimulationDispatcher()
var count = 0
scheduler.schedule(1) { count += 1 }
@@ -87,10 +87,11 @@ class SimulationSchedulerTest {
scheduler.advanceBy(10)
scheduler.schedule(Long.MAX_VALUE) { count += 1 }
+ scheduler.scheduleCancellable(Long.MAX_VALUE) { count += 1 }
scheduler.schedule(100_000_000) { count += 1 }
scheduler.advanceUntilIdle()
- assertEquals(3, count)
+ assertEquals(4, count)
}
/**
@@ -98,7 +99,7 @@ class SimulationSchedulerTest {
*/
@Test
fun testNegativeDelays() {
- val scheduler = SimulationScheduler()
+ val scheduler = SimulationDispatcher()
assertThrows<IllegalArgumentException> { scheduler.schedule(-100) { } }
assertThrows<IllegalArgumentException> { scheduler.advanceBy(-100) }
diff --git a/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/kotlin/SimulationBuildersTest.kt b/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/kotlin/SimulationBuildersTest.kt
new file mode 100644
index 00000000..26419a50
--- /dev/null
+++ b/opendc-simulator/opendc-simulator-core/src/test/kotlin/org/opendc/simulator/kotlin/SimulationBuildersTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2022 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.opendc.simulator.kotlin
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+
+/**
+ * Test suite for the Kotlin simulation builders.
+ */
+class SimulationBuildersTest {
+ @Test
+ fun testDelay() = runSimulation {
+ assertEquals(0, currentTime)
+ delay(100)
+ assertEquals(100, currentTime)
+ }
+
+ @Test
+ fun testController() = runSimulation {
+ var completed = false
+
+ launch {
+ delay(20)
+ completed = true
+ }
+
+ advanceBy(10)
+ assertFalse(completed)
+ advanceBy(11)
+ assertTrue(completed)
+
+ completed = false
+ launch { completed = true }
+ runCurrent()
+ assertTrue(completed)
+ }
+
+ @Test
+ fun testFailOnActiveJobs() {
+ assertThrows<IllegalStateException> {
+ runSimulation {
+ launch { suspendCancellableCoroutine {} }
+ }
+ }
+ }
+
+ @Test
+ fun testPropagateException() {
+ assertThrows<IllegalStateException> {
+ runSimulation {
+ throw IllegalStateException("Test")
+ }
+ }
+ }
+
+ @Test
+ fun testInvalidDispatcher() {
+ assertThrows<IllegalArgumentException> {
+ runSimulation(Dispatchers.Default) { }
+ }
+ }
+
+ @Test
+ fun testExistingJob() {
+ runSimulation(Job()) {
+ delay(10)
+ }
+ }
+}
diff --git a/opendc-simulator/opendc-simulator-flow/build.gradle.kts b/opendc-simulator/opendc-simulator-flow/build.gradle.kts
index 04d46607..4f04bdc1 100644
--- a/opendc-simulator/opendc-simulator-flow/build.gradle.kts
+++ b/opendc-simulator/opendc-simulator-flow/build.gradle.kts
@@ -28,8 +28,8 @@ plugins {
}
dependencies {
- api(libs.kotlinx.coroutines)
- implementation(libs.kotlin.logging)
+ api(projects.opendc.opendcCommon)
+ implementation(libs.slf4j.api)
testImplementation(projects.opendcSimulator.opendcSimulatorCore)
testImplementation(libs.slf4j.simple)
diff --git a/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt b/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt
index fb112082..59dd3bad 100644
--- a/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt
+++ b/opendc-simulator/opendc-simulator-flow/src/jmh/kotlin/org/opendc/simulator/flow2/FlowBenchmarks.kt
@@ -60,7 +60,7 @@ class FlowBenchmarks {
@Benchmark
fun benchmarkSink() {
return runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimpleFlowSink(graph, 4200.0f)
val source = TraceFlowSource(graph, trace)
@@ -71,7 +71,7 @@ class FlowBenchmarks {
@Benchmark
fun benchmarkForward() {
return runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimpleFlowSink(graph, 4200.0f)
val source = TraceFlowSource(graph, trace)
@@ -85,7 +85,7 @@ class FlowBenchmarks {
@Benchmark
fun benchmarkMuxMaxMinSingleSource() {
return runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val switch = MaxMinFlowMultiplexer(graph)
@@ -103,7 +103,7 @@ class FlowBenchmarks {
@Benchmark
fun benchmarkMuxMaxMinTripleSource() {
return runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val switch = MaxMinFlowMultiplexer(graph)
diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowEngine.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowEngine.java
index 0ebb0da9..c0f52505 100644
--- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowEngine.java
+++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowEngine.java
@@ -23,11 +23,11 @@
package org.opendc.simulator.flow2;
import java.time.Clock;
+import java.time.InstantSource;
import java.util.ArrayList;
import java.util.List;
-import kotlin.coroutines.ContinuationInterceptor;
import kotlin.coroutines.CoroutineContext;
-import kotlinx.coroutines.Delay;
+import org.opendc.common.Dispatcher;
/**
* A {@link FlowEngine} simulates a generic flow network.
@@ -56,29 +56,25 @@ public final class FlowEngine implements Runnable {
*/
private boolean active;
- private final CoroutineContext coroutineContext;
- private final Clock clock;
- private final Delay delay;
+ private final Dispatcher dispatcher;
+ private final InstantSource clock;
/**
- * Create a new {@link FlowEngine} instance using the specified {@link CoroutineContext} and {@link Clock}.
+ * Create a new {@link FlowEngine} instance using the specified {@link CoroutineContext} and {@link InstantSource}.
*/
- public static FlowEngine create(CoroutineContext coroutineContext, Clock clock) {
- return new FlowEngine(coroutineContext, clock);
+ public static FlowEngine create(Dispatcher dispatcher) {
+ return new FlowEngine(dispatcher);
}
- FlowEngine(CoroutineContext coroutineContext, Clock clock) {
- this.coroutineContext = coroutineContext;
- this.clock = clock;
-
- CoroutineContext.Key<? extends ContinuationInterceptor> key = ContinuationInterceptor.Key;
- this.delay = (Delay) coroutineContext.get(key);
+ FlowEngine(Dispatcher dispatcher) {
+ this.dispatcher = dispatcher;
+ this.clock = dispatcher.getTimeSource();
}
/**
* Obtain the (virtual) {@link Clock} driving the simulation.
*/
- public Clock getClock() {
+ public InstantSource getClock() {
return clock;
}
@@ -204,7 +200,7 @@ public final class FlowEngine implements Runnable {
// Only schedule a new scheduler invocation in case the target is earlier than all other pending
// scheduler invocations
if (scheduled.tryAdd(target)) {
- delay.invokeOnTimeout(target - now, this, coroutineContext);
+ dispatcher.schedule(target - now, this);
}
}
diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStage.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStage.java
index ed5579ea..25f87e04 100644
--- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStage.java
+++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/FlowStage.java
@@ -22,7 +22,7 @@
package org.opendc.simulator.flow2;
-import java.time.Clock;
+import java.time.InstantSource;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
@@ -67,7 +67,7 @@ public final class FlowStage {
*/
int timerIndex = -1;
- final Clock clock;
+ final InstantSource clock;
private final FlowStageLogic logic;
final FlowGraphInternal parentGraph;
private final FlowEngine engine;
diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InPort.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InPort.java
index fba12aaf..16fed4eb 100644
--- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InPort.java
+++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/InPort.java
@@ -22,7 +22,7 @@
package org.opendc.simulator.flow2;
-import java.time.Clock;
+import java.time.InstantSource;
import java.util.Objects;
/**
@@ -40,7 +40,7 @@ public final class InPort implements Inlet {
OutPort output;
private InHandler handler = InHandlers.noop();
- private final Clock clock;
+ private final InstantSource clock;
private final String name;
private final FlowStage stage;
diff --git a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutPort.java b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutPort.java
index 332296a0..1f7ed4ee 100644
--- a/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutPort.java
+++ b/opendc-simulator/opendc-simulator-flow/src/main/java/org/opendc/simulator/flow2/OutPort.java
@@ -22,7 +22,7 @@
package org.opendc.simulator.flow2;
-import java.time.Clock;
+import java.time.InstantSource;
import java.util.Objects;
/**
@@ -42,7 +42,7 @@ public final class OutPort implements Outlet {
private OutHandler handler = OutHandlers.noop();
private final String name;
private final FlowStage stage;
- private final Clock clock;
+ private final InstantSource clock;
OutPort(FlowStage stage, String name, int id) {
this.name = name;
diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt
index 839835ce..467bf334 100644
--- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt
+++ b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/FlowEngineTest.kt
@@ -38,7 +38,7 @@ import org.opendc.simulator.kotlin.runSimulation
class FlowEngineTest {
@Test
fun testSmoke() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val multiplexer = MaxMinFlowMultiplexer(graph)
@@ -55,7 +55,7 @@ class FlowEngineTest {
@Test
fun testConnectInvalidInlet() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val inlet = mockk<Inlet>()
@@ -65,7 +65,7 @@ class FlowEngineTest {
@Test
fun testConnectInvalidOutlet() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val outlet = mockk<Outlet>()
@@ -75,7 +75,7 @@ class FlowEngineTest {
@Test
fun testConnectInletBelongsToDifferentGraph() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graphA = engine.newGraph()
val graphB = engine.newGraph()
@@ -87,7 +87,7 @@ class FlowEngineTest {
@Test
fun testConnectOutletBelongsToDifferentGraph() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graphA = engine.newGraph()
val graphB = engine.newGraph()
@@ -99,7 +99,7 @@ class FlowEngineTest {
@Test
fun testConnectInletAlreadyConnected() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimpleFlowSink(graph, 2.0f)
@@ -112,7 +112,7 @@ class FlowEngineTest {
@Test
fun testConnectOutletAlreadyConnected() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sinkA = SimpleFlowSink(graph, 2.0f)
@@ -125,7 +125,7 @@ class FlowEngineTest {
@Test
fun testDisconnectInletInvalid() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val inlet = mockk<Inlet>()
@@ -134,7 +134,7 @@ class FlowEngineTest {
@Test
fun testDisconnectOutletInvalid() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val outlet = mockk<Outlet>()
@@ -143,7 +143,7 @@ class FlowEngineTest {
@Test
fun testDisconnectInletInvalidGraph() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graphA = engine.newGraph()
val graphB = engine.newGraph()
@@ -154,7 +154,7 @@ class FlowEngineTest {
@Test
fun testDisconnectOutletInvalidGraph() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graphA = engine.newGraph()
val graphB = engine.newGraph()
@@ -165,7 +165,7 @@ class FlowEngineTest {
@Test
fun testInletEquality() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sinkA = SimpleFlowSink(graph, 2.0f)
@@ -181,7 +181,7 @@ class FlowEngineTest {
@Test
fun testOutletEquality() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sourceA = SimpleFlowSource(graph, 2000.0f, 0.8f)
diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt
index a2ed2195..fef49786 100644
--- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt
+++ b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/ForwardingFlowMultiplexerTest.kt
@@ -39,7 +39,7 @@ class ForwardingFlowMultiplexerTest {
*/
@Test
fun testTrace() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val switch = ForwardingFlowMultiplexer(graph)
@@ -60,7 +60,7 @@ class ForwardingFlowMultiplexerTest {
advanceUntilIdle()
assertAll(
- { assertEquals(4000, clock.millis()) { "Took enough time" } }
+ { assertEquals(4000, timeSource.millis()) { "Took enough time" } }
)
}
}
diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt
index ba339ee3..ebae2d4e 100644
--- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt
+++ b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/mux/MaxMinFlowMultiplexerTest.kt
@@ -35,7 +35,7 @@ import org.opendc.simulator.kotlin.runSimulation
class MaxMinFlowMultiplexerTest {
@Test
fun testSmoke() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val switch = MaxMinFlowMultiplexer(graph)
@@ -49,6 +49,6 @@ class MaxMinFlowMultiplexerTest {
advanceUntilIdle()
- assertEquals(500, clock.millis())
+ assertEquals(500, timeSource.millis())
}
}
diff --git a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt
index a75efba3..ea516c63 100644
--- a/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt
+++ b/opendc-simulator/opendc-simulator-flow/src/test/kotlin/org/opendc/simulator/flow2/sink/FlowSinkTest.kt
@@ -37,7 +37,7 @@ import java.util.concurrent.ThreadLocalRandom
class FlowSinkTest {
@Test
fun testSmoke() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimpleFlowSink(graph, 1.0f)
@@ -46,12 +46,12 @@ class FlowSinkTest {
graph.connect(source.output, sink.input)
advanceUntilIdle()
- assertEquals(2000, clock.millis())
+ assertEquals(2000, timeSource.millis())
}
@Test
fun testAdjustCapacity() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimpleFlowSink(graph, 1.0f)
@@ -64,12 +64,12 @@ class FlowSinkTest {
advanceUntilIdle()
- assertEquals(3000, clock.millis())
+ assertEquals(3000, timeSource.millis())
}
@Test
fun testUtilization() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimpleFlowSink(graph, 1.0f)
@@ -78,12 +78,12 @@ class FlowSinkTest {
graph.connect(source.output, sink.input)
advanceUntilIdle()
- assertEquals(4000, clock.millis())
+ assertEquals(4000, timeSource.millis())
}
@Test
fun testFragments() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimpleFlowSink(graph, 1.0f)
@@ -100,7 +100,7 @@ class FlowSinkTest {
graph.connect(source.output, sink.input)
advanceUntilIdle()
- assertEquals(4000, clock.millis())
+ assertEquals(4000, timeSource.millis())
}
@Test
@@ -114,7 +114,7 @@ class FlowSinkTest {
)
return runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimpleFlowSink(graph, 4200.0f)
val source = TraceFlowSource(graph, trace)
diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt
index 8b4ebb89..181d9a20 100644
--- a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt
+++ b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSinkTest.kt
@@ -43,7 +43,7 @@ import org.opendc.simulator.kotlin.runSimulation
class SimNetworkSinkTest {
@Test
fun testInitialState() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimNetworkSink(graph, /*capacity*/ 100.0f)
@@ -56,7 +56,7 @@ class SimNetworkSinkTest {
@Test
fun testDisconnectIdempotent() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimNetworkSink(graph, /*capacity*/ 100.0f)
@@ -66,7 +66,7 @@ class SimNetworkSinkTest {
@Test
fun testConnectCircular() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimNetworkSink(graph, /*capacity*/ 100.0f)
@@ -77,7 +77,7 @@ class SimNetworkSinkTest {
@Test
fun testConnectAlreadyConnectedTarget() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimNetworkSink(graph, /*capacity*/ 100.0f)
val source = mockk<SimNetworkPort>(relaxUnitFun = true)
@@ -90,7 +90,7 @@ class SimNetworkSinkTest {
@Test
fun testConnectAlreadyConnected() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimNetworkSink(graph, /*capacity*/ 100.0f)
val source1 = TestSource(graph)
@@ -107,7 +107,7 @@ class SimNetworkSinkTest {
@Test
fun testConnect() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimNetworkSink(graph, /*capacity*/ 100.0f)
val source = TestSource(graph)
@@ -127,7 +127,7 @@ class SimNetworkSinkTest {
@Test
fun testDisconnect() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimNetworkSink(graph, /*capacity*/ 100.0f)
val source = TestSource(graph)
diff --git a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt
index 1507c4a1..4a489478 100644
--- a/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt
+++ b/opendc-simulator/opendc-simulator-network/src/test/kotlin/org/opendc/simulator/network/SimNetworkSwitchVirtualTest.kt
@@ -38,7 +38,7 @@ import org.opendc.simulator.kotlin.runSimulation
class SimNetworkSwitchVirtualTest {
@Test
fun testConnect() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimNetworkSink(graph, /*capacity*/ 100.0f)
val source = TestSource(graph)
@@ -60,7 +60,7 @@ class SimNetworkSwitchVirtualTest {
@Test
fun testConnectClosedPort() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val sink = SimNetworkSink(graph, /*capacity*/ 100.0f)
val switch = SimNetworkSwitchVirtual(graph)
diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt
index 6adb0548..f596ca4e 100644
--- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt
+++ b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPduTest.kt
@@ -35,7 +35,7 @@ import org.opendc.simulator.kotlin.runSimulation
internal class SimPduTest {
@Test
fun testZeroOutlets() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 100.0f)
val pdu = SimPdu(graph)
@@ -48,7 +48,7 @@ internal class SimPduTest {
@Test
fun testSingleOutlet() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 100.0f)
val pdu = SimPdu(graph)
@@ -62,7 +62,7 @@ internal class SimPduTest {
@Test
fun testDoubleOutlet() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 200.0f)
val pdu = SimPdu(graph)
@@ -78,7 +78,7 @@ internal class SimPduTest {
@Test
fun testDisconnect() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 300.0f)
val pdu = SimPdu(graph)
@@ -95,7 +95,7 @@ internal class SimPduTest {
@Test
fun testLoss() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 500.0f)
// https://download.schneider-electric.com/files?p_Doc_Ref=SPD_NRAN-66CK3D_EN
@@ -110,7 +110,7 @@ internal class SimPduTest {
@Test
fun testOutletClose() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 100.0f)
val pdu = SimPdu(graph)
diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt
index 03b8182c..03c942b4 100644
--- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt
+++ b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimPowerSourceTest.kt
@@ -42,7 +42,7 @@ import org.opendc.simulator.kotlin.runSimulation
internal class SimPowerSourceTest {
@Test
fun testInitialState() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 100.0f)
@@ -57,7 +57,7 @@ internal class SimPowerSourceTest {
@Test
fun testDisconnectIdempotent() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 100.0f)
@@ -67,7 +67,7 @@ internal class SimPowerSourceTest {
@Test
fun testConnect() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 100.0f)
val inlet = TestInlet(graph)
@@ -87,7 +87,7 @@ internal class SimPowerSourceTest {
@Test
fun testDisconnect() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 100.0f)
val inlet = TestInlet(graph)
@@ -102,7 +102,7 @@ internal class SimPowerSourceTest {
@Test
fun testDisconnectAssertion() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 100.0f)
@@ -120,7 +120,7 @@ internal class SimPowerSourceTest {
@Test
fun testOutletAlreadyConnected() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 100.0f)
val inlet = TestInlet(graph)
@@ -135,7 +135,7 @@ internal class SimPowerSourceTest {
@Test
fun testInletAlreadyConnected() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 100.0f)
val inlet = mockk<SimPowerInlet>(relaxUnitFun = true)
diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt
index 0dd7bb05..89fede63 100644
--- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt
+++ b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/SimUpsTest.kt
@@ -35,7 +35,7 @@ import org.opendc.simulator.kotlin.runSimulation
internal class SimUpsTest {
@Test
fun testSingleInlet() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 200.0f)
val ups = SimUps(graph)
@@ -49,7 +49,7 @@ internal class SimUpsTest {
@Test
fun testDoubleInlet() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source1 = SimPowerSource(graph, /*capacity*/ 200.0f)
val source2 = SimPowerSource(graph, /*capacity*/ 200.0f)
@@ -69,7 +69,7 @@ internal class SimUpsTest {
@Test
fun testLoss() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source = SimPowerSource(graph, /*capacity*/ 500.0f)
// https://download.schneider-electric.com/files?p_Doc_Ref=SPD_NRAN-66CK3D_EN
@@ -84,7 +84,7 @@ internal class SimUpsTest {
@Test
fun testDisconnect() = runSimulation {
- val engine = FlowEngine.create(coroutineContext, clock)
+ val engine = FlowEngine.create(dispatcher)
val graph = engine.newGraph()
val source1 = SimPowerSource(graph, /*capacity*/ 200.0f)
val source2 = SimPowerSource(graph, /*capacity*/ 200.0f)
diff --git a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt
index 7ba12ed9..d5f509e7 100644
--- a/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt
+++ b/opendc-simulator/opendc-simulator-power/src/test/kotlin/org/opendc/simulator/power/TestInlet.kt
@@ -22,7 +22,6 @@
package org.opendc.simulator.power
-import io.mockk.spyk
import org.opendc.simulator.flow2.FlowGraph
import org.opendc.simulator.flow2.FlowStage
import org.opendc.simulator.flow2.FlowStageLogic
@@ -32,8 +31,7 @@ import org.opendc.simulator.flow2.Outlet
* A test inlet.
*/
class TestInlet(graph: FlowGraph) : SimPowerInlet(), FlowStageLogic {
- val logic = spyk(this)
- private val stage = graph.newStage(logic)
+ private val stage = graph.newStage(this)
val flowOutlet = stage.getOutlet("out")
init {