summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--opendc-core/build.gradle19
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Clock.kt42
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Context.kt86
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Interrupt.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/ImmutableTopology.kt)7
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Kernel.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/Context.kt)42
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Process.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/Kernel.kt)25
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Envelope.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Envelope.kt)18
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Readable.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Readable.kt)2
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Writable.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Writable.kt)9
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/EntityContext.kt45
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Channel.kt49
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Port.kt33
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/omega/OmegaSimulator.kt233
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/AdjacencyList.kt202
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Component.kt13
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Edge.kt13
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Entity.kt8
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/MutableTopology.kt22
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Node.kt77
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Topology.kt11
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/TopologyContext.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/clock/Clock.kt)25
-rw-r--r--opendc-omega/build.gradle87
-rw-r--r--opendc-omega/src/main/kotlin/nl/atlarge/opendc/kernel/omega/OmegaClock.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/Simulator.kt)18
-rw-r--r--opendc-omega/src/main/kotlin/nl/atlarge/opendc/kernel/omega/OmegaKernel.kt281
-rw-r--r--opendc-omega/src/main/kotlin/nl/atlarge/opendc/kernel/omega/Resume.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/ChannelContext.kt)13
-rw-r--r--opendc-omega/src/test/kotlin/nl/atlarge/opendc/SmokeTest.kt (renamed from opendc-core/src/test/kotlin/nl/atlarge/opendc/SmokeTest.kt)30
-rw-r--r--opendc-stdlib/build.gradle85
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Experiment.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Experiment.kt)0
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/ExperimentRunner.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/ExperimentRunner.kt)0
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Job.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Job.kt)0
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt)6
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Task.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Task.kt)0
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt)0
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt)2
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/container/Rack.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Rack.kt)2
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/container/Room.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Room.kt)2
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt)2
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt)2
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt)19
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt)2
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt)2
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt)2
-rw-r--r--opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt)2
-rw-r--r--settings.gradle2
44 files changed, 912 insertions, 628 deletions
diff --git a/opendc-core/build.gradle b/opendc-core/build.gradle
index bf71b63b..92cdb2c4 100644
--- a/opendc-core/build.gradle
+++ b/opendc-core/build.gradle
@@ -25,24 +25,24 @@
/* Build configuration */
buildscript {
ext.kotlin_version = '1.1.4-3'
+ ext.dokka_version = '0.9.15'
repositories {
mavenCentral()
+ jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-RC3'
}
}
-plugins {
- id 'java'
- id 'org.jetbrains.kotlin.jvm' version '1.1.4'
-}
-
-apply plugin: 'org.junit.platform.gradle.plugin'
+apply plugin: 'java'
apply plugin: 'kotlin'
+apply plugin: 'org.jetbrains.dokka'
+apply plugin: 'org.junit.platform.gradle.plugin'
compileKotlin {
kotlinOptions {
@@ -62,6 +62,11 @@ kotlin {
}
}
+dokka {
+ outputFormat = 'html'
+ outputDirectory = "$buildDir/javadoc"
+}
+
/* Project configuration */
group 'nl.atlarge.opendc'
version '1.0'
@@ -74,9 +79,9 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.18"
- compile "io.github.microutils:kotlin-logging:1.4.6"
testCompile "org.junit.jupiter:junit-jupiter-api:5.0.0-RC3"
testRuntime "org.junit.jupiter:junit-jupiter-engine:5.0.0-RC3"
+ testCompile "org.junit.platform:junit-platform-launcher:1.0.0-RC3"
testCompile "org.slf4j:slf4j-simple:1.7.25"
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Clock.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Clock.kt
new file mode 100644
index 00000000..0328bbe6
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Clock.kt
@@ -0,0 +1,42 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 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 nl.atlarge.opendc.kernel
+
+/**
+ * A tick represents a moment of time in which some work is done by an entity.
+ */
+typealias Tick = Long
+
+/**
+ * The clock of a simulation manages the simulation time of a simulation [Kernel].
+ *
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface Clock {
+ /**
+ * The tick the clock is currently at.
+ */
+ val tick: Tick
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Context.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Context.kt
new file mode 100644
index 00000000..600a9cee
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Context.kt
@@ -0,0 +1,86 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 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 nl.atlarge.opendc.kernel
+
+import nl.atlarge.opendc.kernel.messaging.Readable
+import nl.atlarge.opendc.kernel.messaging.Writable
+import nl.atlarge.opendc.topology.Entity
+import nl.atlarge.opendc.topology.Topology
+import nl.atlarge.opendc.topology.TopologyContext
+
+/**
+ * This interface provides a context for simulation [Process]es, which defines the environment in which the simulation
+ * is run and provides means of communicating with other entities in the environment and control its own behaviour.
+ *
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface Context<out E : Entity<*>> : Readable, Writable, TopologyContext {
+ /**
+ * The [Topology] over which the simulation is run.
+ */
+ val topology: Topology
+
+ /**
+ * The global [Clock] that keeps track of the simulation time.
+ */
+ val clock: Clock
+
+ /**
+ * The [Entity] in simulation by the [Process].
+ */
+ val entity: E
+
+ /**
+ * The observable state of an [Entity] in simulation, which is provided by the simulation context.
+ */
+ val <E : Entity<S>, S> E.state: S
+
+ /**
+ * Interrupt the [Process] of an [Entity] in simulation.
+ */
+ suspend fun Entity<*>.interrupt()
+
+ /**
+ * Suspend the [Process] of the [Entity] in simulation until the next tick has occurred in the simulation.
+ */
+ suspend fun tick(): Boolean
+
+ /**
+ * Suspend the [Process] of the [Entity] in simulation for <code>n</code> ticks before resuming execution.
+ *
+ * @param n The amount of ticks to suspend the process.
+ */
+ suspend fun wait(n: Int)
+
+ /**
+ * Update the observable state of the entity being simulated.
+ *
+ * Instead of directly mutating the entity, we create a new instance of the entity to prevent other objects
+ * referencing the old entity having their data changed.
+ *
+ * @param next The next state of the entity.
+ */
+ suspend fun <C : Context<E>, E : Entity<S>, S> C.update(next: S)
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/ImmutableTopology.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Interrupt.kt
index 90ba5dc5..de7c5c6c 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/ImmutableTopology.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Interrupt.kt
@@ -22,10 +22,11 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.topology
+package nl.atlarge.opendc.kernel
+
/**
- * A [Topology] whose elements and structural relationships will never change.
+ * An [Interrupt] message is sent to a [Process] in order to interrupt its suspended state.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface ImmutableTopology: Topology
+object Interrupt: Throwable("The process has been interrupted by another entity")
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/Context.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Kernel.kt
index 61ba8192..9678db41 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/Context.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Kernel.kt
@@ -22,47 +22,47 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.simulator
+package nl.atlarge.opendc.kernel
-import nl.atlarge.opendc.simulator.messaging.Readable
-import nl.atlarge.opendc.topology.Component
import nl.atlarge.opendc.topology.Entity
import nl.atlarge.opendc.topology.Topology
+import java.lang.Process
/**
- * The [Context] interface provides a context for a simulation kernel, which defines the environment in which the
- * simulation is run.
+ * A message-based discrete event simulator (DES). This interface allows running simulations over a [Topology].
+ * This discrete event simulator works by having entities in a [Topology] interchange messages between each other and
+ * updating their observable state accordingly.
+ *
+ * In order to run a simulation, a kernel needs to bootstrapped by an initial set of messages to be processed by
+ * entities in the topology of the simulation. Otherwise, the simulation will immediately exit.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Context<out T: Component<*>>: Readable {
+interface Kernel {
/**
* The [Topology] over which the simulation is run.
*/
val topology: Topology
/**
- * The [Component] that is simulated.
- */
- val component: T
-
- /**
- * The observable state of an [Entity] within the simulation is provided by context.
+ * Step through one cycle in the simulation. This method will process all events in a single tick, update the
+ * internal clock and then return the control to the user.
*/
- val <S> Entity<S>.state: S
+ fun step()
/**
- * Suspend the simulation kernel until the next tick occurs in the simulation.
+ * Run a simulation over the specified [Topology].
+ * This method will step through multiple cycles in the simulation until no more message exist in the queue.
*/
- suspend fun tick(): Boolean {
- wait(1)
- return true
- }
+ fun run()
/**
- * Suspend the simulation kernel for <code>n</code> ticks before resuming the execution.
+ * Schedule a message for processing by a [Process].
*
- * @param n The amount of ticks to suspend the simulation kernel.
+ * @param message The message to schedule.
+ * @param destination The destination of the message.
+ * @param sender The sender of the message.
+ * @param delay The amount of ticks to wait before processing the message.
*/
- suspend fun wait(n: Int)
+ fun schedule(message: Any?, destination: Entity<*>, sender: Entity<*>? = null, delay: Int = 0)
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/Kernel.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Process.kt
index 0e0a62a6..40fbefbf 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/Kernel.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Process.kt
@@ -22,25 +22,30 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.simulator
+package nl.atlarge.opendc.kernel
-import nl.atlarge.opendc.topology.Component
+import nl.atlarge.opendc.topology.Entity
/**
- * A simulation kernel that simulates a single [Component] instance in a cloud network.
+ * A [Process] defines the behaviour of an [Entity] within simulation.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Kernel<in C: Context<*>> {
+interface Process<in E : Entity<*>> {
/**
- * This method is invoked to start the simulation of the [Component] associated with this [Kernel].
+ * This method is invoked to start the simulation an [Entity] associated with this [Process].
*
- * <p>This method is assumed to be running during the experiment, but should hand back control to the simulator at
- * some point by calling [Context.tick] to wait for the next tick to occur, which allows to allows other entity
- * simulators to do work in the current tick of the simulation.
+ * This method is assumed to be running during a simulation, but should hand back control to the simulator at
+ * some point by suspending the process. This allows other processes to do work in the current tick of the
+ * simulation.
+ * Suspending the process can be achieved by calling suspending method in the context:
+ * - [Context.tick] - Wait for the next tick to occur
+ * - [Context.wait] - Wait for `n` amount of ticks before resuming execution.
+ * - [Context.receive] - Wait for a message to be received in the mailbox of the [Entity] before resuming
+ * execution.
*
- * <p>If this method exists early, before the simulation has finished, the entity is assumed to be shutdown and its
+ * If this method exits early, before the simulation has finished, the entity is assumed to be shutdown and its
* simulation will not run any further.
*/
- suspend fun C.simulate()
+ suspend fun Context<E>.run()
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Envelope.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Envelope.kt
index 2c09fe8a..61d1a0cf 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Envelope.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Envelope.kt
@@ -22,13 +22,15 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.simulator.messaging
+package nl.atlarge.opendc.kernel.messaging
-import nl.atlarge.opendc.topology.Node
+import nl.atlarge.opendc.kernel.Tick
+import nl.atlarge.opendc.topology.Entity
/**
* The envelope of a message that is received from a [Channel], also containing the metadata of the message.
*
+ * @param T The shape of the message inside the envelope.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
data class Envelope<out T>(
@@ -38,7 +40,17 @@ data class Envelope<out T>(
val message: T,
/**
+ * The tick at which the message should be delivered.
+ */
+ val tick: Tick,
+
+ /**
* The sender of the message.
*/
- val sender: Node<*>
+ val sender: Entity<*>?,
+
+ /**
+ * The destination of the message.
+ */
+ val destination: Entity<*>
)
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Readable.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Readable.kt
index 161bcbd8..422c5668 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Readable.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Readable.kt
@@ -22,7 +22,7 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.simulator.messaging
+package nl.atlarge.opendc.kernel.messaging
/**
* A [Readable] instance allows objects to pull messages from the instance.
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Writable.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Writable.kt
index c8b354b1..45c81e39 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Writable.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Writable.kt
@@ -22,12 +22,12 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.simulator.messaging
+package nl.atlarge.opendc.kernel.messaging
-import nl.atlarge.opendc.topology.Node
+import nl.atlarge.opendc.topology.Entity
/**
- * A [Writable] instance allows objects to send messages to it.
+ * A [Writable] instance allows entities to send messages.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
@@ -37,6 +37,7 @@ interface Writable {
*
* @param msg The message to send.
* @param sender The sender of the message.
+ * @param delay The number of ticks before the message should be received.
*/
- suspend fun send(msg: Any?, sender: Node<*>)
+ suspend fun Entity<*>.send(msg: Any?, sender: Entity<*>? = null, delay: Int = 0)
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/EntityContext.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/EntityContext.kt
deleted file mode 100644
index 3cbbea71..00000000
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/EntityContext.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2017 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 nl.atlarge.opendc.simulator
-
-import nl.atlarge.opendc.topology.Entity
-import nl.atlarge.opendc.topology.Node
-
-/**
- * The context provided to a simulation kernel for stateful entities in the topology.
- *
- * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
- */
-interface EntityContext<out T: Entity<*>>: Context<Node<T>> {
- /**
- * Update the state of the entity being simulated.
- *
- * <p>Instead of directly mutating the entity, we create a new instance of the entity to prevent other objects
- * referencing the old entity having their data changed.
- *
- * @param next The next state of the entity.
- */
- suspend fun <C: EntityContext<E>, E: Entity<S>, S> C.update(next: S)
-}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Channel.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Channel.kt
deleted file mode 100644
index 35407897..00000000
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Channel.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2017 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 nl.atlarge.opendc.simulator.messaging
-
-import nl.atlarge.opendc.topology.Edge
-import nl.atlarge.opendc.topology.Node
-
-/**
- * A unidirectional communication channel between two [Node] instances as seen from one of the entities.
- *
- * <p>A [Channel] is viewed as a directed edge that connects two entities in the topology of a cloud network.
- *
- * @param T The shape of the label of the edge of this channel.
- * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
- */
-interface Channel<out T> {
- /**
- * The directed edge between two nodes which represents this unidirectional communication channel.
- */
- val edge: Edge<T>
-
- /**
- * The channel the message originates from.
- */
- val Envelope<*>.channel: Channel<T>
- get() = this@Channel
-}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Port.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Port.kt
deleted file mode 100644
index a7d7caba..00000000
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/messaging/Port.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2017 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 nl.atlarge.opendc.simulator.messaging
-
-/**
- * A port connects multiple [Channel]s to an entity in the topology of a cloud network.
- *
- * @param C The shape of the channels that are connected to this port.
- * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
- */
-interface Port<out C: Channel<*>>: Iterable<C>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/omega/OmegaSimulator.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/omega/OmegaSimulator.kt
deleted file mode 100644
index dc3c0d0f..00000000
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/omega/OmegaSimulator.kt
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2017 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 nl.atlarge.opendc.simulator.omega
-
-import mu.KotlinLogging
-import nl.atlarge.opendc.simulator.*
-import nl.atlarge.opendc.simulator.clock.Clock
-import nl.atlarge.opendc.simulator.clock.Tick
-import nl.atlarge.opendc.simulator.messaging.Envelope
-import nl.atlarge.opendc.topology.*
-import java.util.*
-import kotlin.coroutines.experimental.*
-
-/**
- * The Omega simulator is the default [Simulator] implementation for the OpenDC core.
- *
- * <p>This simulator implementation is a single-threaded implementation running simulation kernels synchronously and
- * provides a single priority queue for all events (messages, ticks, etc) that occur in the components.
- *
- * <p>By default, [Kernel]s are resolved as part of the [Topology], meaning each [Component] in the topology also
- * implements its simulation logic by deriving from the [Kernel] interface.
- *
- * @param topology The topology to run the simulation over.
- * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
- */
-class OmegaSimulator(override val topology: Topology) : Simulator, Iterator<Unit> {
- /**
- * The logger instance to use for the simulator.
- */
- private val logger = KotlinLogging.logger {}
-
- /**
- * The registry of the simulation kernels used in the experiment.
- */
- private val registry: MutableMap<Component<*>, Context<*>?> = HashMap()
-
- /**
- * A mapping of the entities in the topology to their current state.
- */
- private val states: MutableMap<Entity<*>, Any?> = HashMap()
-
- /**
- * The clock of the simulator.
- */
- private val clock: OmegaClock = OmegaClock()
-
- /**
- * Initialize the simulator.
- */
- init {
- topology.forEach { node ->
- resolve(node)
- node.outgoingEdges.forEach { resolve(it) }
- }
-
- registry.values.forEach { context ->
- if (context == null)
- return@forEach
- @Suppress("UNCHECKED_CAST")
- val kernel = context.component.label as Kernel<Context<*>>
-
- // Start all kernel co-routines
- val block: suspend () -> Unit = { kernel.run { context.simulate() } }
- block.startCoroutine(KernelCoroutine())
- }
- }
-
- /**
- * Resolve the given [Component] to the [Kernel] of that component.
- *
- * @param component The component to resolve.
- * @return The [Kernel] that simulates that [Component].
- */
- fun <T : Component<*>> resolve(component: T): Context<T>? {
- @Suppress("UNCHECKED_CAST")
- return registry.computeIfAbsent(component, {
- if (component.label !is Kernel<*>)
- null
- else when (component) {
- is Node<*> -> OmegaEntityContext(component as Node<*>)
- is Edge<*> -> OmegaChannelContext(component as Edge<*>)
- else -> null
- }
- }) as Context<T>?
- }
-
- /**
- * Determine whether the simulator has a next non-empty cycle available.
- *
- * @return <code>true</code> if the simulator has a next non-empty cycle, <code>false</code> otherwise.
- */
- override fun hasNext(): Boolean = clock.queue.isNotEmpty()
-
- /**
- * Run the next cycle in the simulation.
- */
- override fun next() {
- clock.tick++
- while (true) {
- val (tick, block) = clock.queue.peek() ?: return
-
- if (tick > clock.tick) {
- // Tick has yet to occur
- // Jump in time to next event
- clock.tick = tick - 1
- break
- } else if (tick < clock.tick) {
- // Tick has already occurred
- logger.warn { "tick was not handled correctly" }
- }
- clock.queue.poll()
- block()
- }
- }
-
- /**
- * The co-routine which runs a simulation kernel.
- */
- private class KernelCoroutine : Continuation<Unit> {
- override val context: CoroutineContext = EmptyCoroutineContext
- override fun resume(value: Unit) {}
-
- override fun resumeWithException(exception: Throwable) {
- val currentThread = Thread.currentThread()
- currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
- }
- }
-
- /**
- * The [Clock] for this [OmegaSimulator] that keeps track of the simulation time in ticks.
- */
- private inner class OmegaClock : Clock {
- override var tick: Tick = 0
- internal val queue: PriorityQueue<Pair<Tick, () -> Unit>> = PriorityQueue(Comparator.comparingLong { it.first })
-
- override fun scheduleAt(tick: Tick, block: () -> Unit) {
- queue.add(Pair(tick, block))
- }
- }
-
- /**
- * This internal class provides the default implementation for the [Context] interface for this simulator.
- */
- private abstract inner class OmegaAbstractContext<out T : Component<*>> : Context<T> {
- /**
- * The [Topology] over which the simulation is run.
- */
- override val topology: Topology = this@OmegaSimulator.topology
-
- /**
- * Retrieves and removes a single message from this channel suspending the caller while the channel is empty.
- *
- * @param block The block to process the message with.
- * @return The processed message.
- */
- suspend override fun <T> receive(block: Envelope<*>.(Any?) -> T): T {
- TODO("not implemented")
- }
-
- /**
- * The observable state of an [Entity] within the simulation is provided by the context of the simulation.
- */
- @Suppress("UNCHECKED_CAST")
- override val <S> Entity<S>.state: S
- get() = states.computeIfAbsent(this, { initialState }) as S
-
- /**
- * Suspend the simulation kernel for <code>n</code> ticks before resuming the execution.
- *
- * @param n The amount of ticks to suspend the simulation kernel, with <code>n > 0</code>
- */
- suspend override fun wait(n: Int) {
- require(n > 0) { "The amount of ticks to suspend must be a non-zero positive number" }
- return suspendCoroutine { cont ->
- clock.scheduleAfter(n, { cont.resume(Unit) })
- }
- }
- }
-
- /**
- * An internal class to provide [Context] for an entity within the simulation.
- */
- private inner class OmegaEntityContext<out T : Entity<*>>(override val component: Node<T>) : OmegaAbstractContext<Node<T>>(), EntityContext<T> {
- /**
- * Update the state of the entity being simulated.
- *
- * <p>Instead of directly mutating the entity, we create a new instance of the entity to prevent other objects
- * referencing the old entity having their data changed.
- *
- * @param next The next state of the entity.
- */
- suspend override fun <C : EntityContext<E>, E : Entity<S>, S> C.update(next: S) {
- states.put(component.entity as Entity<*>, next)
- }
- }
-
- /**
- * An internal class to provide the [Context] for an edge kernel within the simulation.
- */
- private inner class OmegaChannelContext<out T>(override val component: Edge<T>) : OmegaAbstractContext<Edge<T>>(), ChannelContext<T> {
- /**
- * Send the given message downstream.
- *
- * @param msg The message to send.
- * @param sender The sender of the message.
- */
- suspend override fun send(msg: Any?, sender: Node<*>) {
- TODO("not implemented")
- }
- }
-}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/AdjacencyList.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/AdjacencyList.kt
index 28154695..e2824f5d 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/AdjacencyList.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/AdjacencyList.kt
@@ -24,14 +24,32 @@
package nl.atlarge.opendc.topology
+import nl.atlarge.opendc.topology.Edge as BaseEdge
import java.util.concurrent.atomic.AtomicInteger
/**
+ * This module provides a [Topology] implementation backed internally by an adjacency list.
+ *
+ * This implementation is best suited for sparse graphs, where an adjacency matrix would take up too much space with
+ * empty cells.
+ *
+ * *Note that this implementation is not synchronized.*
+ */
+object AdjacencyList {
+ /**
+ * Return a [TopologyBuilder] that constructs the topology represents as an adjacency list.
+ *
+ * @return A [TopologyBuilder] instance.
+ */
+ fun builder(): TopologyBuilder = AdjacencyListTopologyBuilder()
+}
+
+/**
* A builder for [Topology] instances, which is backed by an adjacency list.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-class AdjacencyListTopologyBuilder: TopologyBuilder {
+internal class AdjacencyListTopologyBuilder : TopologyBuilder {
/**
* Build a [Topology] instance from the current state of this builder.
*
@@ -43,41 +61,38 @@ class AdjacencyListTopologyBuilder: TopologyBuilder {
/**
* A [Topology] whose graph is represented as adjacency list.
*/
-internal class AdjacencyListTopology: MutableTopology {
- private val nextId: AtomicInteger = AtomicInteger(0)
- private val nodes: MutableList<Node<*>> = ArrayList()
-
+internal class AdjacencyListTopology : MutableTopology {
/**
- * Returns the size of the collection.
+ * The identifier for the next node in the graph.
*/
- override val size: Int = nodes.size
+ private var nextId: AtomicInteger = AtomicInteger(0)
/**
- * Checks if the specified element is contained in this collection.
+ * A mapping of nodes to their internal representation with the edges of the nodes.
*/
- override fun contains(element: Node<*>): Boolean = nodes.contains(element)
+ private var nodes: MutableMap<Entity<*>, Node> = HashMap()
+
+ // Topology
/**
- * Checks if all elements in the specified collection are contained in this collection.
+ * A unique identifier of this node within the topology.
*/
- override fun containsAll(elements: Collection<Node<*>>): Boolean = nodes.containsAll(elements)
+ override val Entity<*>.id: Int
+ get() = nodes[this]!!.id
/**
- * Returns `true` if the collection is empty (contains no elements), `false` otherwise.
+ * The set of ingoing edges of this node.
*/
- override fun isEmpty(): Boolean = nodes.isEmpty()
+ override val Entity<*>.ingoingEdges: MutableSet<BaseEdge<*>>
+ get() = nodes[this]!!.ingoingEdges
/**
- * Create a [Node] in this [Topology] for the given [Entity].
- *
- * @param entity The entity to create a node for.
- * @return The node created for the given entity.
+ * The set of outgoing edges of this node.
*/
- override fun <T : Entity<*>> node(entity: T): Node<T> {
- val node = AdjacencyListNode(nextId.getAndIncrement(), entity)
- nodes.add(node)
- return node
- }
+ override val Entity<*>.outgoingEdges: MutableSet<BaseEdge<*>>
+ get() = nodes[this]!!.outgoingEdges
+
+ // MutableTopology
/**
* Create a directed edge between two [Node]s in the topology.
@@ -88,33 +103,140 @@ internal class AdjacencyListTopology: MutableTopology {
* @param tag The tag of the edge that uniquely identifies the relationship the edge represents.
* @return The edge that has been created.
*/
- override fun <T> connect(from: Node<*>, to: Node<*>, label: T, tag: String?): Edge<T> {
- if (from !is AdjacencyListNode<*> || to !is AdjacencyListNode<*>)
- throw IllegalArgumentException()
- if (!from.validate(this) || !to.validate(this))
- throw IllegalArgumentException()
- val edge: Edge<T> = AdjacencyListEdge(label, tag, from, to)
+ override fun <T> connect(from: Entity<*>, to: Entity<*>, label: T, tag: String?): BaseEdge<T> {
+ if (!contains(from) || !contains(to))
+ throw IllegalArgumentException("One of the entities is not part of the topology")
+ val edge = Edge(label, tag, from, to)
from.outgoingEdges.add(edge)
to.ingoingEdges.add(edge)
return edge
}
+ // Cloneable
+
+ /**
+ * Create a copy of the graph.
+ *
+ * @return A new [Topology] instance with a copy of the graph.
+ */
+ override public fun clone(): Topology {
+ val copy = AdjacencyListTopology()
+ copy.nextId = AtomicInteger(nextId.get())
+ copy.nodes = HashMap(nodes)
+ return copy
+ }
+
+ // Set
+
+ /**
+ * Returns the size of the collection.
+ */
+ override val size: Int = nodes.size
+
+ /**
+ * Checks if the specified element is contained in this collection.
+ */
+ override fun contains(element: Entity<*>): Boolean = nodes.contains(element)
+
+ /**
+ * Checks if all elements in the specified collection are contained in this collection.
+ */
+ override fun containsAll(elements: Collection<Entity<*>>): Boolean = elements.all { nodes.containsKey(it) }
+
+ /**
+ * Returns `true` if the collection is empty (contains no elements), `false` otherwise.
+ */
+ override fun isEmpty(): Boolean = nodes.isEmpty()
+
+ // MutableSet
+
+ /**
+ * Add a node to the graph.
+ *
+ * @param element The element to add to this graph.
+ * @return `true` if the graph has changed, `false` otherwise.
+ */
+ override fun add(element: Entity<*>): Boolean = nodes.putIfAbsent(element, Node(nextId.getAndIncrement())) == null
+
+ /**
+ * Add all nodes in the specified collection to the graph.
+ *
+ * @param elements The nodes to add to this graph.
+ * @return `true` if the graph has changed, `false` otherwise.
+ */
+ override fun addAll(elements: Collection<Entity<*>>): Boolean = elements.any { add(it) }
+
+ /**
+ * Remove all nodes and their respective edges from the graph.
+ */
+ override fun clear() = nodes.clear()
+
+ /**
+ * Remove the given node and its edges from the graph.
+ *
+ * @param element The element to remove from the graph.
+ * @return `true` if the graph has changed, `false` otherwise.
+ */
+ override fun remove(element: Entity<*>): Boolean {
+ nodes[element]?.ingoingEdges?.forEach {
+ it.from.outgoingEdges.remove(it)
+ }
+ nodes[element]?.outgoingEdges?.forEach {
+ it.to.ingoingEdges.remove(it)
+ }
+ return nodes.keys.remove(element)
+ }
+
+
+ /**
+ * Remove all nodes in the given collection from the graph.
+ *
+ * @param elements The elements to remove from the graph.
+ * @return `true` if the graph has changed, `false` otherwise.
+ */
+ override fun removeAll(elements: Collection<Entity<*>>): Boolean = elements.any(this::remove)
+
/**
- * Returns an iterator over the elements of this object.
+ * Remove all nodes in the graph, except those in the specified collection.
+ *
+ * Take note that this method currently only guarantees a maximum runtime complexity of O(n^2).
+ *
+ * @param elements The elements to retain in the graph.
*/
- override fun iterator(): MutableIterator<Node<*>> = nodes.iterator()
+ override fun retainAll(elements: Collection<Entity<*>>): Boolean {
+ val iterator = nodes.keys.iterator()
+ var changed = false
+ while (iterator.hasNext()) {
+ val entity = iterator.next()
- internal inner class AdjacencyListNode<out T: Entity<*>>(override val id: Int, override val label: T): Node<T> {
- override var ingoingEdges: MutableSet<Edge<*>> = HashSet()
- override var outgoingEdges: MutableSet<Edge<*>> = HashSet()
- override fun toString(): String = label.toString()
- internal fun validate(instance: AdjacencyListTopology) = this@AdjacencyListTopology == instance
+ if (entity !in elements) {
+ iterator.remove()
+ changed = true
+ }
+ }
+ return changed
}
- internal class AdjacencyListEdge<out T>(override val label: T,
- override val tag: String?,
- override val from: Node<*>,
- override val to: Node<*>): Edge<T> {
- override fun toString(): String = label.toString()
+ /**
+ * Return a mutable iterator over the nodes of the graph.
+ *
+ * @return A [MutableIterator] over the nodes of the graph.
+ */
+ override fun iterator(): MutableIterator<Entity<*>> = nodes.keys.iterator()
+
+ /**
+ * The internal representation of a node within the graph.
+ */
+ internal data class Node(val id: Int) {
+ val ingoingEdges: MutableSet<BaseEdge<*>> = HashSet()
+ val outgoingEdges: MutableSet<BaseEdge<*>> = HashSet()
}
+
+ /**
+ * The internal representation of an edge within the graph.
+ */
+ internal class Edge<out T>(override val label: T,
+ override val tag: String?,
+ override val from: Entity<*>,
+ override val to: Entity<*>) : BaseEdge<T>
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Component.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Component.kt
index 79b35e86..3c383892 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Component.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Component.kt
@@ -25,16 +25,9 @@
package nl.atlarge.opendc.topology
/**
- * A component within a [Topology], which is either an [Node] or an [Edge] representing the relationship between
+ * A component within a [Topology], which is either a node or an [Edge] representing the relationship between
* entities within a logical topology of a cloud network.
- *
- * <p>A [Component]'s label provides access to user-specified data.
- *
+ **
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Component<out T> {
- /**
- * The label of this [Component].
- */
- val label: T
-}
+interface Component
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Edge.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Edge.kt
index 22ad57c1..3be14dec 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Edge.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Edge.kt
@@ -25,12 +25,17 @@
package nl.atlarge.opendc.topology
/**
- * An edge that represents a directed relationship between exactly two [Node]s in a logical topology of a cloud network.
+ * An edge that represents a directed relationship between exactly two nodes in a logical topology of a cloud network.
*
* @param T The relationship type the edge represents within a logical topology.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Edge<out T>: Component<T> {
+interface Edge<out T> : Component {
+ /**
+ * The label of this edge.
+ */
+ val label: T
+
/**
* A tag to uniquely identify the relationship this edge represents.
*/
@@ -42,7 +47,7 @@ interface Edge<out T>: Component<T> {
* This property is not guaranteed to have a runtime complexity of <code>O(1)</code>, but must be at least
* <code>O(n)</code>, with respect to the size of the topology.
*/
- val from: Node<*>
+ val from: Entity<*>
/**
* The destination of the edge.
@@ -50,5 +55,5 @@ interface Edge<out T>: Component<T> {
* This property is not guaranteed to have a runtime complexity of <code>O(1)</code>, but must be at least
* <code>O(n)</code>, with respect to the size of the topology.
*/
- val to: Node<*>
+ val to: Entity<*>
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Entity.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Entity.kt
index e6f37cf6..66a31d77 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Entity.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Entity.kt
@@ -25,10 +25,10 @@
package nl.atlarge.opendc.topology
/**
- * An entity within a cloud network.
+ * An entity within a cloud network, represented as a node within a topology.
*
- * <p>A [Entity] contains the immutable properties of this component given by the topology configuration at the start
- * of a simulation and remain unchanged during simulation.
+ * <p>A [Entity] contains immutable properties given by the topology configuration at the start of a simulation and
+ * remain unchanged during simulation.
*
* <p>In addition, other entities in a simulation have direct, immutable access to the observable state of this entity.
*
@@ -36,7 +36,7 @@ package nl.atlarge.opendc.topology
* a simulation.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Entity<out S> {
+interface Entity<out S> : Component {
/**
* The initial state of the entity.
*/
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/MutableTopology.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/MutableTopology.kt
index 0aa0d1b5..10a55e5b 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/MutableTopology.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/MutableTopology.kt
@@ -30,17 +30,9 @@ package nl.atlarge.opendc.topology
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface MutableTopology: Topology {
+interface MutableTopology : Topology, MutableSet<Entity<*>> {
/**
- * Create a [Node] in this [Topology] for the given [Entity].
- *
- * @param entity The entity to create a node for.
- * @return The node created for the given entity.
- */
- fun <T: Entity<*>> node(entity: T): Node<T>
-
- /**
- * Create a directed edge between two [Node]s in the topology.
+ * Create a directed, labeled edge between two nodes in the topology.
*
* @param from The source of the edge.
* @param to The destination of the edge.
@@ -48,23 +40,23 @@ interface MutableTopology: Topology {
* @param tag The tag of the edge that uniquely identifies the relationship the edge represents.
* @return The edge that has been created.
*/
- fun <T> connect(from: Node<*>, to: Node<*>, label: T, tag: String? = null): Edge<T>
+ fun <T> connect(from: Entity<*>, to: Entity<*>, label: T, tag: String? = null): Edge<T>
/**
- * Create a directed edge between two [Node]s in the topology.
+ * Create a directed, unlabeled edge between two nodes in the topology.
*
* @param from The source of the edge.
* @param to The destination of the edge.
* @param tag The tag of the edge that uniquely identifies the relationship the edge represents.
* @return The edge that has been created.
*/
- fun connect(from: Node<*>, to: Node<*>, tag: String? = null): Edge<Unit> = connect(from, to, Unit, tag)
+ fun connect(from: Entity<*>, to: Entity<*>, tag: String? = null): Edge<Unit> = connect(from, to, Unit, tag)
/**
- * Create a directed edge between two [Node]s in the topology.
+ * Create a directed, unlabeled edge between two nodes in the topology.
*
* @param dest The destination of the edge.
* @return The edge that has been created.
*/
- infix fun Node<*>.to(dest: Node<*>): Edge<Unit> = connect(this, dest)
+ infix fun Entity<*>.to(dest: Entity<*>): Edge<Unit> = connect(this, dest)
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Node.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Node.kt
deleted file mode 100644
index ee1cde9b..00000000
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Node.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2017 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 nl.atlarge.opendc.topology
-
-/**
- * A labeled node of graph representing an entity in a specific logical topology of a cloud network.
- *
- * <p>A [Node] is instantiated and managed by a [Topology] instance containing user-specified data in its label.
- *
- * @param T The entity type the node represents in a logical topology.
- * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
- */
-interface Node<out T: Entity<*>>: Component<T> {
- /**
- * A unique identifier of this node within the topology.
- */
- val id: Int
-
- /**
- * The set of ingoing edges of this node.
- */
- val ingoingEdges: Set<Edge<*>>
-
- /**
- * The set of outgoing edges of this node.
- */
- val outgoingEdges: Set<Edge<*>>
-
- /**
- * The [Entity] this node represents within a logical topology of a cloud network.
- */
- val entity: T
- get() = label
-}
-
-/**
- * Return the set of entities that are connected inwards to this node with the given tag.
- *
- * @param tag The tag of the edges to get.
- * @param T The shape of the label of these edges.
- * @return The entities of all edges whose destination is this node and have the given tag.
- */
-inline fun <reified T> Node<*>.ingoing(tag: String) =
- ingoingEdges.filter { it.tag == tag }.map { it.to.entity as T }.toSet()
-
-
-/**
- * Return the set of entities that are connected outwards to this node with the given tag.
- *
- * @param tag The tag of the edges to get.
- * @param T The shape of the label of these edges.
- * @return The entities of all edges whose source is this node and have the given tag.
- */
-inline fun <reified T> Node<*>.outgoing(tag: String) =
- outgoingEdges.filter { it.tag == tag }.map { it.to.entity as T }.toSet()
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Topology.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Topology.kt
index 5d88334c..5b697bfb 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Topology.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Topology.kt
@@ -28,8 +28,15 @@ package nl.atlarge.opendc.topology
* A graph data structure which represents the logical topology of a cloud network consisting of one or more
* datacenters.
*
- * <p>A topology is [Iterable] and allows implementation-dependent iteration of the nodes in the topology.
+ * A topology is [Iterable] and allows implementation-dependent iteration of the nodes in the topology.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Topology: Set<Node<*>>
+interface Topology : TopologyContext, Cloneable, Set<Entity<*>> {
+ /**
+ * Create a copy of the topology.
+ *
+ * @return A new [Topology] with a copy of the graph.
+ */
+ public override fun clone(): Topology
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/clock/Clock.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/TopologyContext.kt
index 07377e4a..22e7dd94 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/clock/Clock.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/TopologyContext.kt
@@ -22,32 +22,27 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.simulator.clock
+package nl.atlarge.opendc.topology
/**
- * A tick represents a moment of time in which some work is done by an entity.
- */
-typealias Tick = Long
-
-/**
- * The clock of a simulation manages the ticks that have elapsed and schedules the tick events.
+ * A [TopologyContext] represents the context for entities in a specific topology, providing access to the identifier
+ * and ingoing and outgoing edges of the [Entity] within a [Topology].
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Clock {
+interface TopologyContext {
/**
- * The tick the clock is currently at.
+ * A unique identifier of an [Entity] within the topology.
*/
- val tick: Tick
+ val Entity<*>.id: Int
/**
- *
- * @throws IllegalArgumentException
+ * The set of ingoing edges of an [Entity].
*/
- fun scheduleAt(tick: Tick, block: () -> Unit)
+ val Entity<*>.ingoingEdges: Set<Edge<*>>
/**
- * @throws IllegalArgumentException
+ * The set of outgoing edges of an [Entity].
*/
- fun scheduleAfter(n: Int, block: () -> Unit) = scheduleAt(tick + n, block)
+ val Entity<*>.outgoingEdges: Set<Edge<*>>
}
diff --git a/opendc-omega/build.gradle b/opendc-omega/build.gradle
new file mode 100644
index 00000000..89b5740d
--- /dev/null
+++ b/opendc-omega/build.gradle
@@ -0,0 +1,87 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 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.
+ */
+
+/* Build configuration */
+buildscript {
+ ext.kotlin_version = '1.1.4-3'
+ ext.dokka_version = '0.9.15'
+
+ repositories {
+ mavenCentral()
+ jcenter()
+ }
+
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
+ classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-RC3'
+ }
+}
+
+apply plugin: 'java'
+apply plugin: 'kotlin'
+apply plugin: 'org.jetbrains.dokka'
+apply plugin: 'org.junit.platform.gradle.plugin'
+
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+
+compileTestKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+
+kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+}
+
+dokka {
+ outputFormat = 'html'
+ outputDirectory = "$buildDir/javadoc"
+}
+
+/* Project configuration */
+group 'nl.atlarge.opendc'
+version '1.0'
+
+repositories {
+ jcenter()
+}
+
+dependencies {
+ compile project(':opendc-core')
+ compile "io.github.microutils:kotlin-logging:1.4.6"
+
+ testCompile "org.junit.jupiter:junit-jupiter-api:5.0.0-RC3"
+ testRuntime "org.junit.jupiter:junit-jupiter-engine:5.0.0-RC3"
+ testCompile "org.junit.platform:junit-platform-launcher:1.0.0-RC3"
+ testCompile "org.slf4j:slf4j-simple:1.7.25"
+ testCompile project(':opendc-stdlib')
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/Simulator.kt b/opendc-omega/src/main/kotlin/nl/atlarge/opendc/kernel/omega/OmegaClock.kt
index 72494793..c84f4dbf 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/Simulator.kt
+++ b/opendc-omega/src/main/kotlin/nl/atlarge/opendc/kernel/omega/OmegaClock.kt
@@ -22,23 +22,19 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.simulator
+package nl.atlarge.opendc.kernel.omega
-import nl.atlarge.opendc.topology.Topology
+import nl.atlarge.opendc.kernel.Clock
+import nl.atlarge.opendc.kernel.Tick
/**
- * A [Simulator] runs a simulation over the specified topology.
+ * A [Clock] implementation used by the Omega simulation kernel.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Simulator {
+class OmegaClock: Clock {
/**
- * The [Topology] over which the simulation runs.
+ * The simulation time expressed as the amount of ticks that passed.
*/
- val topology: Topology
-
- /**
- * Run the next cycle in the simulation.
- */
- fun next()
+ override var tick: Tick = 0
}
diff --git a/opendc-omega/src/main/kotlin/nl/atlarge/opendc/kernel/omega/OmegaKernel.kt b/opendc-omega/src/main/kotlin/nl/atlarge/opendc/kernel/omega/OmegaKernel.kt
new file mode 100644
index 00000000..631b6d45
--- /dev/null
+++ b/opendc-omega/src/main/kotlin/nl/atlarge/opendc/kernel/omega/OmegaKernel.kt
@@ -0,0 +1,281 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 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 nl.atlarge.opendc.kernel.omega
+
+import mu.KotlinLogging
+import nl.atlarge.opendc.kernel.*
+import nl.atlarge.opendc.kernel.messaging.Envelope
+import nl.atlarge.opendc.topology.Entity
+import nl.atlarge.opendc.topology.Topology
+import nl.atlarge.opendc.topology.TopologyContext
+import java.util.*
+import kotlin.coroutines.experimental.*
+
+/**
+ * The Omega simulation kernel is the reference simulation kernel implementation for the OpenDC Simulator core.
+ *
+ * This simulator implementation is a single-threaded implementation, running simulation kernels synchronously and
+ * provides a single priority queue for all events (messages, ticks, etc) that occur in the entities.
+ *
+ * By default, [Process]s are resolved as part of the [Topology], meaning each [Entity] in the topology should also
+ * implement its simulation behaviour by deriving from the [Process] interface.
+ *
+ * @param topology The topology to run the simulation over.
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+class OmegaKernel(override val topology: Topology) : Kernel {
+ /**
+ * The logger instance to use for the simulator.
+ */
+ private val logger = KotlinLogging.logger {}
+
+ /**
+ * The registry of the simulation kernels used in the experiment.
+ */
+ private val registry: MutableMap<Entity<*>, OmegaContext<*, *>> = HashMap()
+
+ /**
+ * The message queue.
+ */
+ private val queue: PriorityQueue<Envelope<*>> = PriorityQueue(Comparator.comparingLong(Envelope<*>::tick))
+
+ /**
+ * The clock of the simulator.
+ */
+ private val clock: OmegaClock = OmegaClock()
+
+ /**
+ * Initialise the simulator.
+ */
+ init {
+ topology.forEach { resolve(it) }
+ registry.values.forEach { context ->
+ @Suppress("UNCHECKED_CAST")
+ val process = context.entity as Process<Entity<*>>
+
+ // Start all process co-routines
+ val block: suspend () -> Unit = { process.run { context.run() } }
+ block.startCoroutine(context)
+ }
+ }
+
+ /**
+ * Step through one event in the simulation.
+ */
+ override fun step() {
+ while (true) {
+ val envelope = queue.peek() ?: return
+ val tick = envelope.tick
+
+ if (tick > clock.tick) {
+ // Tick has yet to occur
+ // Jump in time to next event
+ clock.tick = tick
+ break
+ } else if (tick < clock.tick) {
+ // Tick has already occurred
+ logger.warn { "message processed out of order" }
+ }
+ queue.poll()
+
+ val context = registry[envelope.destination] ?: continue
+
+ if (envelope.message !is Interrupt) {
+ context.continuation.resume(envelope)
+ } else {
+ context.continuation.resumeWithException(envelope.message as Interrupt)
+ }
+ }
+ }
+
+ /**
+ * Run a simulation over the specified [Topology].
+ * This method will step through multiple cycles in the simulation until no more message exist in the queue.
+ */
+ override fun run() {
+ while (queue.isNotEmpty()) {
+ step()
+ }
+ }
+
+ /**
+ * Schedule a message for processing by a [Process].
+ *
+ * @param message The message to schedule.
+ * @param destination The destination of the message.
+ * @param sender The sender of the message.
+ * @param delay The amount of ticks to wait before processing the message.
+ */
+ override fun schedule(message: Any?, destination: Entity<*>, sender: Entity<*>?, delay: Int) {
+ require(delay > 0) { "The amount of ticks to delay the message must be a positive number" }
+ queue.add(Envelope(message, clock.tick + delay, sender, destination))
+ }
+
+ /**
+ * Resolve the given [Context], given an [Entity] in a logical topology of a cloud network.
+ *
+ * @param entity The [Entity] to resolve the [Context] for.
+ * @return The [Context] for the given [Entity] or <code>null</code> if the component has no [Process] associated
+ * with it.
+ */
+ private fun <E : Entity<*>> resolve(entity: E): Context<E>? {
+ if (entity !is Process<*>)
+ return null
+
+ @Suppress("UNCHECKED_CAST")
+ return registry.computeIfAbsent(entity, {
+ OmegaContext(entity)
+ }) as Context<E>
+ }
+
+ /**
+ * This internal class provides the default implementation for the [Context] interface for this simulator.
+ */
+ private inner class OmegaContext<out E : Entity<S>, S>(override val entity: E) : Context<E>,
+ Continuation<Unit>, TopologyContext by topology {
+ /**
+ * The continuation to resume the execution of the process.
+ */
+ lateinit var continuation: Continuation<Envelope<*>>
+
+ /**
+ * The state of the entity.
+ */
+ var state: S = entity.initialState
+
+ /**
+ * The [Topology] over which the simulation is run.
+ */
+ override val topology: Topology = this@OmegaKernel.topology
+
+ /**
+ * The global [Clock] that keeps track of the simulation time.
+ */
+ override val clock: Clock = this@OmegaKernel.clock
+
+ /**
+ * The [CoroutineContext] for a [Process].
+ */
+ override val context: CoroutineContext = EmptyCoroutineContext
+
+ /**
+ * The observable state of an [Entity] within the simulation is provided by the context of the simulation.
+ */
+ @Suppress("UNCHECKED_CAST")
+ override val <T : Entity<S>, S> T.state: S
+ get() = (resolve(this) as OmegaContext<T, S>?)?.state ?: initialState
+
+ /**
+ * Retrieve and remove and single message from the mailbox of the [Entity] and suspend the [Process] until the
+ * message has been received.
+ *
+ * @return The envelope containing the message.
+ */
+ suspend fun receiveEnvelope(): Envelope<*> {
+ return suspendCoroutine { continuation = it }
+ }
+
+ /**
+ * Retrieves and removes a single message from this channel suspending the caller while the channel is empty.
+ *
+ * @param block The block to process the message with.
+ * @return The processed message.
+ */
+ suspend override fun <T> receive(block: Envelope<*>.(Any?) -> T): T {
+ val envelope = receiveEnvelope()
+ return block(envelope, envelope.message)
+ }
+
+ /**
+ * Send the given message downstream.
+ *
+ * @param msg The message to send.
+ * @param sender The sender of the message.
+ * @param delay The number of ticks before the message should be received.
+ */
+ suspend override fun Entity<*>.send(msg: Any?, sender: Entity<*>?, delay: Int) {
+ schedule(msg, this, sender, delay)
+ }
+
+ /**
+ * Send an interruption message to the given [Entity].
+ */
+ suspend override fun Entity<*>.interrupt() = send(Interrupt, this)
+
+ /**
+ * Suspend the simulation kernel until the next tick occurs in the simulation.
+ */
+ suspend override fun tick(): Boolean {
+ wait(1)
+ return true
+ }
+
+ /**
+ * Suspend the simulation kernel for <code>n</code> ticks before resuming the execution.
+ *
+ * @param n The amount of ticks to suspend the simulation kernel, with <code>n > 0</code>
+ */
+ suspend override fun wait(n: Int) {
+ require(n > 0) { "The amount of ticks to suspend must be a non-zero positive number" }
+ queue.add(Envelope(Resume, clock.tick + n, entity, entity))
+
+ while (true) {
+ if (receive() is Resume)
+ return
+ }
+ }
+
+ /**
+ * Update the state of the entity being simulated.
+ *
+ * <p>Instead of directly mutating the entity, we create a new instance of the entity to prevent other objects
+ * referencing the old entity having their data changed.
+ *
+ * @param next The next state of the entity.
+ */
+ suspend override fun <C : Context<E>, E : Entity<S>, S> C.update(next: S) {
+ @Suppress("UNCHECKED_CAST")
+ (this as OmegaContext<E, S>).state = next
+ }
+
+ // Completion continuation implementation
+ /**
+ * Resume the execution of this continuation with the given value.
+ *
+ * @param value The value to resume with.
+ */
+ override fun resume(value: Unit) {}
+
+ /**
+ * Resume the execution of this continuation with an exception.
+ *
+ * @param exception The exception to resume with.
+ */
+ override fun resumeWithException(exception: Throwable) {
+ val currentThread = Thread.currentThread()
+ currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
+ }
+ }
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/ChannelContext.kt b/opendc-omega/src/main/kotlin/nl/atlarge/opendc/kernel/omega/Resume.kt
index dee55730..d20115d0 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/simulator/ChannelContext.kt
+++ b/opendc-omega/src/main/kotlin/nl/atlarge/opendc/kernel/omega/Resume.kt
@@ -22,14 +22,17 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.simulator
+package nl.atlarge.opendc.kernel.omega
-import nl.atlarge.opendc.simulator.messaging.Writable
-import nl.atlarge.opendc.topology.Edge
+import nl.atlarge.opendc.kernel.Context
/**
- * The context provided to a simulation kernel for communication channels between entities.
+ * An internal message used by the Omega simulation kernel to indicate to a suspended [Process], that it should wake up
+ * and resume execution.
+ *
+ * This message is not guaranteed to work on other simulation kernels and [Context.interrupt] should be preferred to
+ * wake up a process from another entity.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface ChannelContext<out T>: Context<Edge<T>>, Writable
+object Resume
diff --git a/opendc-core/src/test/kotlin/nl/atlarge/opendc/SmokeTest.kt b/opendc-omega/src/test/kotlin/nl/atlarge/opendc/SmokeTest.kt
index 6d67c941..1408d03f 100644
--- a/opendc-core/src/test/kotlin/nl/atlarge/opendc/SmokeTest.kt
+++ b/opendc-omega/src/test/kotlin/nl/atlarge/opendc/SmokeTest.kt
@@ -24,36 +24,38 @@
package nl.atlarge.opendc
-import nl.atlarge.opendc.simulator.omega.OmegaSimulator
-import nl.atlarge.opendc.topology.AdjacencyListTopologyBuilder
+import nl.atlarge.opendc.kernel.omega.OmegaKernel
+import nl.atlarge.opendc.topology.AdjacencyList
import nl.atlarge.opendc.topology.container.Rack
import nl.atlarge.opendc.topology.machine.Cpu
import nl.atlarge.opendc.topology.machine.Machine
import org.junit.jupiter.api.Test
internal class SmokeTest {
- @Test
- fun smoke() {
- val builder = AdjacencyListTopologyBuilder()
+ @Test
+ fun smoke() {
+ val builder = AdjacencyList.builder()
val topology = builder.construct {
- val rack = node(Rack())
- val n = 100
+ val rack = Rack()
+ add(rack)
+ val n = 1000
// Create n machines in the rack
repeat(n) {
- val machine = node(Machine())
+ val machine = Machine()
+ add(machine)
connect(rack, machine, tag = "machine")
- val cpu1 = node(Cpu(10, 2, 2))
- val cpu2 = node(Cpu(5, 3, 2))
+ val cpu1 = Cpu(10, 2, 2)
+ val cpu2 = Cpu(5, 3, 2)
+ add(cpu1)
+ add(cpu2)
connect(machine, cpu1, tag = "cpu")
connect(machine, cpu2, tag = "cpu")
}
}
- val simulator = OmegaSimulator(topology)
- while (simulator.hasNext()) {
- simulator.next()
- }
+ val simulator = OmegaKernel(topology)
+ simulator.run()
}
}
diff --git a/opendc-stdlib/build.gradle b/opendc-stdlib/build.gradle
new file mode 100644
index 00000000..1013b82d
--- /dev/null
+++ b/opendc-stdlib/build.gradle
@@ -0,0 +1,85 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 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.
+ */
+
+/* Build configuration */
+buildscript {
+ ext.kotlin_version = '1.1.4-3'
+ ext.dokka_version = '0.9.15'
+
+ repositories {
+ mavenCentral()
+ jcenter()
+ }
+
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
+ classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-RC3'
+ }
+}
+
+apply plugin: 'java'
+apply plugin: 'kotlin'
+apply plugin: 'org.jetbrains.dokka'
+apply plugin: 'org.junit.platform.gradle.plugin'
+
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+
+compileTestKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+
+kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+}
+
+dokka {
+ outputFormat = 'html'
+ outputDirectory = "$buildDir/javadoc"
+}
+
+/* Project configuration */
+group 'nl.atlarge.opendc'
+version '1.0'
+
+repositories {
+ jcenter()
+}
+
+dependencies {
+ compile project(':opendc-core')
+
+ testCompile "org.junit.jupiter:junit-jupiter-api:5.0.0-RC3"
+ testRuntime "org.junit.jupiter:junit-jupiter-engine:5.0.0-RC3"
+ testCompile "org.junit.platform:junit-platform-launcher:1.0.0-RC3"
+ testCompile "org.slf4j:slf4j-simple:1.7.25"
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Experiment.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Experiment.kt
index a4f947c8..a4f947c8 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Experiment.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Experiment.kt
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/ExperimentRunner.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/ExperimentRunner.kt
index e53c6e08..e53c6e08 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/ExperimentRunner.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/ExperimentRunner.kt
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Job.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Job.kt
index 8c41735d..8c41735d 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Job.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Job.kt
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt
index 14a623a6..28aa84e7 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt
@@ -24,14 +24,14 @@
package nl.atlarge.opendc.experiment
-import nl.atlarge.opendc.topology.Node
+import nl.atlarge.opendc.topology.Entity
/**
- * A task scheduler that is coupled to an [Node] in the topology of the cloud network.
+ * A task scheduler that is coupled to an [Entity] in the topology of the cloud network.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Scheduler<in E: Node<*>> {
+interface Scheduler<in E : Entity<*>> {
/**
* Schedule the given jobs for the given entity.
*
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Task.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Task.kt
index ec2eb2fa..ec2eb2fa 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Task.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/Task.kt
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt
index bb87a167..bb87a167 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt
index b484f70e..b995732a 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt
@@ -31,6 +31,6 @@ import nl.atlarge.opendc.topology.Entity
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-class Datacenter: Entity<Unit> {
+class Datacenter : Entity<Unit> {
override val initialState = Unit
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Rack.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/container/Rack.kt
index f9595adc..27207d4c 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Rack.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/container/Rack.kt
@@ -32,6 +32,6 @@ import nl.atlarge.opendc.topology.Entity
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-class Rack: Entity<Unit> {
+class Rack : Entity<Unit> {
override val initialState = Unit
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Room.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/container/Room.kt
index 844d96a0..3b338899 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Room.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/container/Room.kt
@@ -31,4 +31,4 @@ import nl.atlarge.opendc.topology.Entity
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Room: Entity<Unit>
+interface Room : Entity<Unit>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt
index 1e2135e8..78e2eaaa 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt
@@ -33,6 +33,6 @@ data class Cpu(
override val speed: Int,
override val cores: Int,
override val energyConsumption: Int
-): ProcessingUnit {
+) : ProcessingUnit {
override val initialState = Unit
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt
index a294db38..09179c94 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt
@@ -33,7 +33,7 @@ class Gpu(
override val speed: Int,
override val cores: Int,
override val energyConsumption: Int
-): ProcessingUnit {
+) : ProcessingUnit {
override val initialState = Unit
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt
index b313b78b..dba0fe1b 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt
@@ -25,10 +25,9 @@
package nl.atlarge.opendc.topology.machine
import nl.atlarge.opendc.experiment.Task
-import nl.atlarge.opendc.simulator.EntityContext
-import nl.atlarge.opendc.simulator.Kernel
+import nl.atlarge.opendc.kernel.Context
+import nl.atlarge.opendc.kernel.Process
import nl.atlarge.opendc.topology.Entity
-import nl.atlarge.opendc.topology.outgoing
import java.util.*
/**
@@ -37,7 +36,7 @@ import java.util.*
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-class Machine: Entity<Machine.State>, Kernel<EntityContext<Machine>> {
+class Machine : Entity<Machine.State>, Process<Machine> {
/**
* The status of a machine.
*/
@@ -58,17 +57,17 @@ class Machine: Entity<Machine.State>, Kernel<EntityContext<Machine>> {
/**
* Run the simulation kernel for this entity.
*/
- override suspend fun EntityContext<Machine>.simulate() {
- update(state.copy(status = Machine.Status.IDLE))
+ override suspend fun Context<Machine>.run() {
+ update(state.copy(status = Status.IDLE))
- val cpus = component.outgoing<Cpu>("cpu")
+ val cpus = outgoingEdges.filter { it.tag == "cpu" }.map { it.to as Cpu }
val speed = cpus.fold(0, { acc, (speed, cores) -> acc + speed * cores })
val task: Task
val delay = Random().nextInt(1000) + 1
wait(delay)
- loop@while (true) {
+ loop@ while (true) {
val msg = receive()
when (msg) {
is Task -> {
@@ -79,10 +78,10 @@ class Machine: Entity<Machine.State>, Kernel<EntityContext<Machine>> {
}
}
- update(state.copy(status = Machine.Status.RUNNING))
+ update(state.copy(status = Status.RUNNING))
while (tick()) {
task.consume(speed.toLong())
}
- update(state.copy(status = Machine.Status.HALT))
+ update(state.copy(status = Status.HALT))
}
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt
index a235133f..31bfbcd6 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt
@@ -31,7 +31,7 @@ import nl.atlarge.opendc.topology.Entity
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface ProcessingUnit: Entity<Unit> {
+interface ProcessingUnit : Entity<Unit> {
/**
* The speed of this [ProcessingUnit] per core.
*/
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt
index 9c294125..d3a9eefe 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt
@@ -31,4 +31,4 @@ import nl.atlarge.opendc.topology.Entity
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface NetworkUnit: Entity<Unit>
+interface NetworkUnit : Entity<Unit>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt
index 2938e530..aac4ce03 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt
@@ -32,6 +32,6 @@ import nl.atlarge.opendc.topology.Entity
* @param output The power output of the power unit in Watt.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-class PowerUnit(val output: Double): Entity<Unit> {
+class PowerUnit(val output: Double) : Entity<Unit> {
override val initialState = Unit
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt
index 8e53e365..f719f152 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt
+++ b/opendc-stdlib/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt
@@ -31,4 +31,4 @@ import nl.atlarge.opendc.topology.Entity
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface StorageUnit: Entity<Unit>
+interface StorageUnit : Entity<Unit>
diff --git a/settings.gradle b/settings.gradle
index 2bee7ddc..b0370c43 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -24,3 +24,5 @@
rootProject.name = "opendc-simulator"
include 'opendc-core'
+include 'opendc-omega'
+include 'opendc-stdlib'