From b1c4d1f94e35445bdba5a56b614d0ec28a332624 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Thu, 11 Jan 2018 16:27:05 +0100 Subject: refactor(#18): Redesign core simulation API This change contains the redesign of the core simulation API and provides a cleaner interface for developing simulation models for the users. --- .../com/atlarge/opendc/simulator/Bootstrap.kt | 64 +++++++ .../kotlin/com/atlarge/opendc/simulator/Context.kt | 184 +++++++++++++++++++++ .../kotlin/com/atlarge/opendc/simulator/Entity.kt | 44 +++++ .../kotlin/com/atlarge/opendc/simulator/Process.kt | 27 +++ .../kotlin/com/atlarge/opendc/simulator/Time.kt | 35 ++++ .../com/atlarge/opendc/simulator/kernel/Kernel.kt | 79 +++++++++ .../opendc/simulator/kernel/KernelFactory.kt | 42 +++++ .../opendc/simulator/platform/Experiment.kt | 52 ++++++ 8 files changed, 527 insertions(+) create mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt create mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt create mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Entity.kt create mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt create mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Time.kt create mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt create mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt create mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt (limited to 'opendc-core/src/main/kotlin/com') diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt new file mode 100644 index 00000000..199e1701 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt @@ -0,0 +1,64 @@ +package com.atlarge.opendc.simulator + +/** + * A bootstrapping interface for a conceptual model that is a logical representation of some system of entities, + * relationships and processes, as a basis for simulations. + * + * @param M The shape of the model that is bootstrapped. + * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) + */ +interface Bootstrap { + /** + * Bootstrap a model `M` for a kernel in the given context. + * + * @param context The context to bootstrap to model in. + * @return The initialised model for the simulation. + */ + fun bootstrap(context: Context): M + + /** + * A context for the bootstrap of some model type `M` that allows the model to register the entities of the model to + * the simulation kernel. + * + * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) + */ + interface Context { + /** + * Register the given entity to the simulation kernel. + * + * @param entity The entity to register. + * @return `true` if the entity had not yet been registered, `false` otherwise. + */ + fun register(entity: Entity<*, M>): Boolean + + /** + * Deregister the given entity from the simulation kernel. + * + * @param entity The entity to deregister. + * @return `true` if the entity had not yet been unregistered, `false` otherwise. + */ + fun deregister(entity: Entity<*, M>): Boolean + + /** + * Schedule a message for processing by a [Context]. + * + * @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 time to wait before processing the message. + */ + fun schedule(message: Any, destination: Entity<*, *>, sender: Entity<*, *>? = null, delay: Duration = 0) + } + + companion object { + /** + * Create a [Bootstrap] procedure using the given block to produce a bootstrap for a model of type `M`. + * + * @param block The block to produce the bootstrap. + * @return The bootstrap procedure that has been built. + */ + fun create(block: (Context) -> M): Bootstrap = object : Bootstrap { + override fun bootstrap(context: Context) = block(context) + } + } +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt new file mode 100644 index 00000000..c4e906dd --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt @@ -0,0 +1,184 @@ +/* + * 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 com.atlarge.opendc.simulator + +import java.util.Queue + +/** + * This interface provides a context for simulation of [Entity] instances, by defining 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 { + /** + * The model of simulation in which the entity exists. + */ + val model: M + + /** + * The current point in simulation time. + */ + val time: Instant + + /** + * The duration between the current point in simulation time and the last point in simulation time where the + * [Entity] has done some work. This means the `run()` co-routine has been resumed. + */ + val delta: Duration + + /** + * The observable state of the entity bound to this scope. + */ + var state: S + + /** + * The observable state of an [Entity] in simulation, which is provided by the simulation context. + */ + val , S> E.state: S + + /** + * Interrupt an [Entity] process in simulation. + * + * If an [Entity] process has been suspended, the suspending call will throw an [Interrupt] object as a result of + * this call. + * Make sure the [Entity] process actually has error handling in place, so it won't take down the whole [Entity] + * process as result of the interrupt. + */ + suspend fun Entity<*, *>.interrupt() + + /** + * Suspend the [Context] of the [Entity] in simulation for the given duration of simulation time before resuming + * execution and drop all messages that are received during this period. + * + * A call to this method will not make the [Context] sleep for the actual duration of time, but instead suspend + * the process until the no more messages at an earlier point in time have to be processed. + * + * @param duration The duration of simulation time to hold before resuming execution. + */ + suspend fun hold(duration: Duration) + + /** + * Suspend the [Context] of the [Entity] in simulation for the given duration of simulation time before resuming + * execution and push all messages that are received during this period to the given queue. + * + * A call to this method will not make the [Context] sleep for the actual duration of time, but instead suspend + * the process until the no more messages at an earlier point in time have to be processed. + * + * @param duration The duration of simulation time to wait before resuming execution. + * @param queue The mutable queue to push the messages to. + */ + suspend fun hold(duration: Duration, queue: Queue) + + /** + * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is + * empty. The execution is resumed after the head of the mailbox is removed after which the message [Envelope] is + * transformed through `transform` to return the transformed result. + * + * @param transform The block to transform the message with in an envelope context, providing access to the sender + * of the message. + * @return The transformed message. + */ + suspend fun receive(transform: suspend Envelope<*>.(Any) -> T): T + + /** + * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is + * empty. The execution is either resumed after the head of the mailbox is removed after which the message + * [Envelope] is transformed through `transform` to return the transformed result or the timeout has been reached. + * + * @param timeout The duration to wait before resuming execution. + * @param transform The block to transform the message with in an envelope context, providing access to the sender + * of the message. + * @return The processed message or `null` if the timeout was reached. + */ + suspend fun receive(timeout: Duration, transform: suspend Envelope<*>.(Any) -> T): T? + + /** + * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is + * empty. The execution is resumed after the head of the mailbox is removed and returned. + * + * @return The received message. + */ + suspend fun receive(): Any = receive { it } + + /** + * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is + * empty. The execution is either resumed after the head of the mailbox is removed and returned or when the timeout + * has been reached. + * + * @return The received message or `null` if the timeout was reached. + */ + suspend fun receive(timeout: Duration): Any? = receive(timeout) { it } + + /** + * Send the given message to the specified entity, without providing any guarantees about the actual delivery of + * the message. + * + * @param msg The message to send. + * @param delay The amount of time to wait before the message should be received by the entity. + */ + suspend fun Entity<*, *>.send(msg: Any, delay: Duration = 0) + + /** + * Send the given message to the specified entity, without providing any guarantees about the actual delivery of + * the message. + * + * @param msg The message to send. + * @param sender The sender of the message. + * @param delay The amount of time to wait before the message should be received by the entity. + */ + suspend fun Entity<*, *>.send(msg: Any, sender: Entity<*, *>, delay: Duration = 0) +} + +/** + * The message envelope that is sent to an [Entity], 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) + */ +interface Envelope { + /** + * The message in this envelope. + */ + val message: T + + /** + * The sender of the message. + */ + val sender: Entity<*, *>? + + /** + * The destination of the message. + */ + val destination: Entity<*, *> +} + +/** + * An [Interrupt] message is sent to a [Entity] process in order to interrupt its suspended state. + * + * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) + */ +object Interrupt: Throwable("The entity process has been interrupted by another entity") diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Entity.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Entity.kt new file mode 100644 index 00000000..800f2f1d --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Entity.kt @@ -0,0 +1,44 @@ +/* + * 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 com.atlarge.opendc.simulator + +/** + * An entity in some model `M`. + * + *

A [Entity] directly contains its immutable properties that remain unchanged during simulation. + * + *

In addition, other entities in simulation have direct, immutable access to the observable state of this entity. + * + * @param S The shape of the observable state of this entity, which is directly accessible by other components within + * a simulation. + * @param M The shape of the model in which the entity exists. + * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) + */ +interface Entity { + /** + * The initial state of the entity. + */ + val initialState: S +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt new file mode 100644 index 00000000..30280657 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt @@ -0,0 +1,27 @@ +package com.atlarge.opendc.simulator + +/** + * A process is dynamic entity within a simulation, that interacts with the model environment by the interchange of + * messages. + * + * @param S The shape of the observable state of the process. + * @param M The shape of the model in which the process exists. + * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) + */ +interface Process: Entity { + /** + * This method is invoked to start the simulation a process. + * + * 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 at the current point in time of the + * simulation. + * Suspending the process can be achieved by calling suspending method in the context: + * - [Context.hold] - Hold for `n` units of time before resuming execution. + * - [Context.receive] - Wait for a message to be received in the mailbox of the [Entity] before resuming + * execution. + * + * 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 Context.run() +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Time.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Time.kt new file mode 100644 index 00000000..64e3cb80 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Time.kt @@ -0,0 +1,35 @@ +/* + * 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 com.atlarge.opendc.simulator + +/** + * An instantaneous point on the time-line, used to record event time-stamps in a simulation. + */ +typealias Instant = Long + +/** + * A time interval which represents the amount of elapsed time between two events. + */ +typealias Duration = Long diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt new file mode 100644 index 00000000..3eee0f67 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt @@ -0,0 +1,79 @@ +/* + * 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 com.atlarge.opendc.simulator.kernel + +import com.atlarge.opendc.simulator.Bootstrap +import com.atlarge.opendc.simulator.Instant +import com.atlarge.opendc.simulator.Entity + +/** + * A message based discrete event simulation kernel. + * + * The kernel is created by bootstrapping some model `M` (see [Bootstrap]) to simulate and controls the simulation by + * allowing the user to step over cycles in the simulation and inspect the internal state using [Entity.state]. + * + * A kernel should provide additionally a [KernelFactory] to create new kernel instances given a certain model + * [Bootstrap]. + * + * @param M The shape of the model over which the simulation runs. + * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) + */ +interface Kernel { + /** + * The model in which the simulation runs. + */ + val model: M + + /** + * The simulation time. + */ + var time: Instant + + /** + * The observable state of an [Entity] in simulation, which is provided by the simulation context. + */ + val , S> E.state: S + + /** + * 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. + */ + fun step() + + /** + * Run a simulation over the specified model. + * This method will step through multiple cycles in the simulation until no more message exist in the queue. + */ + fun run() + + /** + * Run a simulation over the specified model, stepping through cycles until the specified clock tick has + * occurred. The control is then handed back to the user. + * + * @param until The point in simulation time at which the simulation should be paused and the control is handed + * back to the user. + */ + fun run(until: Instant) +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt new file mode 100644 index 00000000..93667eb8 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.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 com.atlarge.opendc.simulator.kernel + +import com.atlarge.opendc.simulator.Bootstrap + +/** + * A factory for bootstrapping simulation [Kernel] instances. + * + * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) + */ +interface KernelFactory { + /** + * Create a simulation over the given model facilitated by this simulation kernel. + * + * @param bootstrap The bootstrap procedure to bootstrap the simulation with. + * @return A [Kernel] instance to control the simulation. + */ + fun create(bootstrap: Bootstrap): Kernel +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt new file mode 100644 index 00000000..88b606fd --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt @@ -0,0 +1,52 @@ +/* + * 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 com.atlarge.opendc.simulator.platform + +import com.atlarge.opendc.simulator.kernel.KernelFactory +import com.atlarge.opendc.simulator.Duration + +/** + * A blueprint for a reproducible simulation in a pre-defined setting. + * + * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) + */ +interface Experiment { + /** + * Run the experiment on the specified kernel implementation. + * + * @param factory The factory to create the simulation kernel with. + * @return The result of the experiment. + */ + fun run(factory: KernelFactory): T + + /** + * Run the experiment on the specified kernel implementation. + * + * @param factory The factory to create the simulation kernel with. + * @param timeout The maximum duration of the experiment before returning to the caller. + * @return The result of the experiment or `null`. + */ + fun run(factory: KernelFactory, timeout: Duration): T? +} -- cgit v1.2.3 From e97d9bf3f2cccf19a21631e26d55d60c9f4d7c7a Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 12 Feb 2018 14:26:44 +0100 Subject: refactor(#18): Align formatting with official Kotlin Style Guide This change aligns the code formatting of the project with the official Kotlin Style Guide. They can be found at http://kotlinlang.org/docs/reference/coding-conventions.html. --- .../com/atlarge/opendc/simulator/Bootstrap.kt | 98 ++++---- .../kotlin/com/atlarge/opendc/simulator/Context.kt | 268 ++++++++++----------- .../kotlin/com/atlarge/opendc/simulator/Entity.kt | 8 +- .../kotlin/com/atlarge/opendc/simulator/Process.kt | 32 +-- .../com/atlarge/opendc/simulator/kernel/Kernel.kt | 62 ++--- .../opendc/simulator/kernel/KernelFactory.kt | 14 +- .../opendc/simulator/platform/Experiment.kt | 32 +-- 7 files changed, 257 insertions(+), 257 deletions(-) (limited to 'opendc-core/src/main/kotlin/com') diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt index 199e1701..10a89704 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt @@ -8,57 +8,57 @@ package com.atlarge.opendc.simulator * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ interface Bootstrap { - /** - * Bootstrap a model `M` for a kernel in the given context. - * - * @param context The context to bootstrap to model in. - * @return The initialised model for the simulation. - */ - fun bootstrap(context: Context): M + /** + * Bootstrap a model `M` for a kernel in the given context. + * + * @param context The context to bootstrap to model in. + * @return The initialised model for the simulation. + */ + fun bootstrap(context: Context): M - /** - * A context for the bootstrap of some model type `M` that allows the model to register the entities of the model to - * the simulation kernel. - * - * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) - */ - interface Context { - /** - * Register the given entity to the simulation kernel. - * - * @param entity The entity to register. - * @return `true` if the entity had not yet been registered, `false` otherwise. - */ - fun register(entity: Entity<*, M>): Boolean + /** + * A context for the bootstrap of some model type `M` that allows the model to register the entities of the model to + * the simulation kernel. + * + * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) + */ + interface Context { + /** + * Register the given entity to the simulation kernel. + * + * @param entity The entity to register. + * @return `true` if the entity had not yet been registered, `false` otherwise. + */ + fun register(entity: Entity<*, M>): Boolean - /** - * Deregister the given entity from the simulation kernel. - * - * @param entity The entity to deregister. - * @return `true` if the entity had not yet been unregistered, `false` otherwise. - */ - fun deregister(entity: Entity<*, M>): Boolean + /** + * Deregister the given entity from the simulation kernel. + * + * @param entity The entity to deregister. + * @return `true` if the entity had not yet been unregistered, `false` otherwise. + */ + fun deregister(entity: Entity<*, M>): Boolean - /** - * Schedule a message for processing by a [Context]. - * - * @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 time to wait before processing the message. - */ - fun schedule(message: Any, destination: Entity<*, *>, sender: Entity<*, *>? = null, delay: Duration = 0) - } + /** + * Schedule a message for processing by a [Context]. + * + * @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 time to wait before processing the message. + */ + fun schedule(message: Any, destination: Entity<*, *>, sender: Entity<*, *>? = null, delay: Duration = 0) + } - companion object { - /** - * Create a [Bootstrap] procedure using the given block to produce a bootstrap for a model of type `M`. - * - * @param block The block to produce the bootstrap. - * @return The bootstrap procedure that has been built. - */ - fun create(block: (Context) -> M): Bootstrap = object : Bootstrap { - override fun bootstrap(context: Context) = block(context) - } - } + companion object { + /** + * Create a [Bootstrap] procedure using the given block to produce a bootstrap for a model of type `M`. + * + * @param block The block to produce the bootstrap. + * @return The bootstrap procedure that has been built. + */ + fun create(block: (Context) -> M): Bootstrap = object : Bootstrap { + override fun bootstrap(context: Context) = block(context) + } + } } diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt index c4e906dd..c89bdb59 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt @@ -24,7 +24,7 @@ package com.atlarge.opendc.simulator -import java.util.Queue +import java.util.* /** * This interface provides a context for simulation of [Entity] instances, by defining the environment in which the @@ -34,123 +34,123 @@ import java.util.Queue * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ interface Context { - /** - * The model of simulation in which the entity exists. - */ - val model: M - - /** - * The current point in simulation time. - */ - val time: Instant - - /** - * The duration between the current point in simulation time and the last point in simulation time where the - * [Entity] has done some work. This means the `run()` co-routine has been resumed. - */ - val delta: Duration - - /** - * The observable state of the entity bound to this scope. - */ - var state: S - - /** - * The observable state of an [Entity] in simulation, which is provided by the simulation context. - */ - val , S> E.state: S - - /** - * Interrupt an [Entity] process in simulation. - * - * If an [Entity] process has been suspended, the suspending call will throw an [Interrupt] object as a result of - * this call. - * Make sure the [Entity] process actually has error handling in place, so it won't take down the whole [Entity] - * process as result of the interrupt. - */ - suspend fun Entity<*, *>.interrupt() - - /** - * Suspend the [Context] of the [Entity] in simulation for the given duration of simulation time before resuming - * execution and drop all messages that are received during this period. - * - * A call to this method will not make the [Context] sleep for the actual duration of time, but instead suspend - * the process until the no more messages at an earlier point in time have to be processed. - * - * @param duration The duration of simulation time to hold before resuming execution. - */ - suspend fun hold(duration: Duration) - - /** - * Suspend the [Context] of the [Entity] in simulation for the given duration of simulation time before resuming - * execution and push all messages that are received during this period to the given queue. - * - * A call to this method will not make the [Context] sleep for the actual duration of time, but instead suspend - * the process until the no more messages at an earlier point in time have to be processed. - * - * @param duration The duration of simulation time to wait before resuming execution. - * @param queue The mutable queue to push the messages to. - */ - suspend fun hold(duration: Duration, queue: Queue) - - /** - * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is - * empty. The execution is resumed after the head of the mailbox is removed after which the message [Envelope] is - * transformed through `transform` to return the transformed result. - * - * @param transform The block to transform the message with in an envelope context, providing access to the sender - * of the message. - * @return The transformed message. - */ - suspend fun receive(transform: suspend Envelope<*>.(Any) -> T): T - - /** - * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is - * empty. The execution is either resumed after the head of the mailbox is removed after which the message - * [Envelope] is transformed through `transform` to return the transformed result or the timeout has been reached. - * - * @param timeout The duration to wait before resuming execution. - * @param transform The block to transform the message with in an envelope context, providing access to the sender - * of the message. - * @return The processed message or `null` if the timeout was reached. - */ - suspend fun receive(timeout: Duration, transform: suspend Envelope<*>.(Any) -> T): T? - - /** - * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is - * empty. The execution is resumed after the head of the mailbox is removed and returned. - * - * @return The received message. - */ - suspend fun receive(): Any = receive { it } - - /** - * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is - * empty. The execution is either resumed after the head of the mailbox is removed and returned or when the timeout - * has been reached. - * - * @return The received message or `null` if the timeout was reached. - */ - suspend fun receive(timeout: Duration): Any? = receive(timeout) { it } - - /** - * Send the given message to the specified entity, without providing any guarantees about the actual delivery of - * the message. - * - * @param msg The message to send. - * @param delay The amount of time to wait before the message should be received by the entity. - */ - suspend fun Entity<*, *>.send(msg: Any, delay: Duration = 0) - - /** - * Send the given message to the specified entity, without providing any guarantees about the actual delivery of - * the message. - * - * @param msg The message to send. - * @param sender The sender of the message. - * @param delay The amount of time to wait before the message should be received by the entity. - */ - suspend fun Entity<*, *>.send(msg: Any, sender: Entity<*, *>, delay: Duration = 0) + /** + * The model of simulation in which the entity exists. + */ + val model: M + + /** + * The current point in simulation time. + */ + val time: Instant + + /** + * The duration between the current point in simulation time and the last point in simulation time where the + * [Entity] has done some work. This means the `run()` co-routine has been resumed. + */ + val delta: Duration + + /** + * The observable state of the entity bound to this scope. + */ + var state: S + + /** + * The observable state of an [Entity] in simulation, which is provided by the simulation context. + */ + val , S> E.state: S + + /** + * Interrupt an [Entity] process in simulation. + * + * If an [Entity] process has been suspended, the suspending call will throw an [Interrupt] object as a result of + * this call. + * Make sure the [Entity] process actually has error handling in place, so it won't take down the whole [Entity] + * process as result of the interrupt. + */ + suspend fun Entity<*, *>.interrupt() + + /** + * Suspend the [Context] of the [Entity] in simulation for the given duration of simulation time before resuming + * execution and drop all messages that are received during this period. + * + * A call to this method will not make the [Context] sleep for the actual duration of time, but instead suspend + * the process until the no more messages at an earlier point in time have to be processed. + * + * @param duration The duration of simulation time to hold before resuming execution. + */ + suspend fun hold(duration: Duration) + + /** + * Suspend the [Context] of the [Entity] in simulation for the given duration of simulation time before resuming + * execution and push all messages that are received during this period to the given queue. + * + * A call to this method will not make the [Context] sleep for the actual duration of time, but instead suspend + * the process until the no more messages at an earlier point in time have to be processed. + * + * @param duration The duration of simulation time to wait before resuming execution. + * @param queue The mutable queue to push the messages to. + */ + suspend fun hold(duration: Duration, queue: Queue) + + /** + * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is + * empty. The execution is resumed after the head of the mailbox is removed after which the message [Envelope] is + * transformed through `transform` to return the transformed result. + * + * @param transform The block to transform the message with in an envelope context, providing access to the sender + * of the message. + * @return The transformed message. + */ + suspend fun receive(transform: suspend Envelope<*>.(Any) -> T): T + + /** + * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is + * empty. The execution is either resumed after the head of the mailbox is removed after which the message + * [Envelope] is transformed through `transform` to return the transformed result or the timeout has been reached. + * + * @param timeout The duration to wait before resuming execution. + * @param transform The block to transform the message with in an envelope context, providing access to the sender + * of the message. + * @return The processed message or `null` if the timeout was reached. + */ + suspend fun receive(timeout: Duration, transform: suspend Envelope<*>.(Any) -> T): T? + + /** + * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is + * empty. The execution is resumed after the head of the mailbox is removed and returned. + * + * @return The received message. + */ + suspend fun receive(): Any = receive { it } + + /** + * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is + * empty. The execution is either resumed after the head of the mailbox is removed and returned or when the timeout + * has been reached. + * + * @return The received message or `null` if the timeout was reached. + */ + suspend fun receive(timeout: Duration): Any? = receive(timeout) { it } + + /** + * Send the given message to the specified entity, without providing any guarantees about the actual delivery of + * the message. + * + * @param msg The message to send. + * @param delay The amount of time to wait before the message should be received by the entity. + */ + suspend fun Entity<*, *>.send(msg: Any, delay: Duration = 0) + + /** + * Send the given message to the specified entity, without providing any guarantees about the actual delivery of + * the message. + * + * @param msg The message to send. + * @param sender The sender of the message. + * @param delay The amount of time to wait before the message should be received by the entity. + */ + suspend fun Entity<*, *>.send(msg: Any, sender: Entity<*, *>, delay: Duration = 0) } /** @@ -159,21 +159,21 @@ interface Context { * @param T The shape of the message inside the envelope. * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ -interface Envelope { - /** - * The message in this envelope. - */ - val message: T - - /** - * The sender of the message. - */ - val sender: Entity<*, *>? - - /** - * The destination of the message. - */ - val destination: Entity<*, *> +interface Envelope { + /** + * The message in this envelope. + */ + val message: T + + /** + * The sender of the message. + */ + val sender: Entity<*, *>? + + /** + * The destination of the message. + */ + val destination: Entity<*, *> } /** @@ -181,4 +181,4 @@ interface Envelope { * * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ -object Interrupt: Throwable("The entity process has been interrupted by another entity") +object Interrupt : Throwable("The entity process has been interrupted by another entity") diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Entity.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Entity.kt index 800f2f1d..56704c5d 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Entity.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Entity.kt @@ -37,8 +37,8 @@ package com.atlarge.opendc.simulator * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ interface Entity { - /** - * The initial state of the entity. - */ - val initialState: S + /** + * The initial state of the entity. + */ + val initialState: S } diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt index 30280657..e8b4d988 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt @@ -8,20 +8,20 @@ package com.atlarge.opendc.simulator * @param M The shape of the model in which the process exists. * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ -interface Process: Entity { - /** - * This method is invoked to start the simulation a process. - * - * 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 at the current point in time of the - * simulation. - * Suspending the process can be achieved by calling suspending method in the context: - * - [Context.hold] - Hold for `n` units of time before resuming execution. - * - [Context.receive] - Wait for a message to be received in the mailbox of the [Entity] before resuming - * execution. - * - * 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 Context.run() +interface Process : Entity { + /** + * This method is invoked to start the simulation a process. + * + * 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 at the current point in time of the + * simulation. + * Suspending the process can be achieved by calling suspending method in the context: + * - [Context.hold] - Hold for `n` units of time before resuming execution. + * - [Context.receive] - Wait for a message to be received in the mailbox of the [Entity] before resuming + * execution. + * + * 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 Context.run() } diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt index 3eee0f67..29b3bdee 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt @@ -25,8 +25,8 @@ package com.atlarge.opendc.simulator.kernel import com.atlarge.opendc.simulator.Bootstrap -import com.atlarge.opendc.simulator.Instant import com.atlarge.opendc.simulator.Entity +import com.atlarge.opendc.simulator.Instant /** * A message based discrete event simulation kernel. @@ -41,39 +41,39 @@ import com.atlarge.opendc.simulator.Entity * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ interface Kernel { - /** - * The model in which the simulation runs. - */ - val model: M + /** + * The model in which the simulation runs. + */ + val model: M - /** - * The simulation time. - */ - var time: Instant + /** + * The simulation time. + */ + var time: Instant - /** - * The observable state of an [Entity] in simulation, which is provided by the simulation context. - */ - val , S> E.state: S + /** + * The observable state of an [Entity] in simulation, which is provided by the simulation context. + */ + val , S> E.state: S - /** - * 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. - */ - fun step() + /** + * 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. + */ + fun step() - /** - * Run a simulation over the specified model. - * This method will step through multiple cycles in the simulation until no more message exist in the queue. - */ - fun run() + /** + * Run a simulation over the specified model. + * This method will step through multiple cycles in the simulation until no more message exist in the queue. + */ + fun run() - /** - * Run a simulation over the specified model, stepping through cycles until the specified clock tick has - * occurred. The control is then handed back to the user. - * - * @param until The point in simulation time at which the simulation should be paused and the control is handed - * back to the user. - */ - fun run(until: Instant) + /** + * Run a simulation over the specified model, stepping through cycles until the specified clock tick has + * occurred. The control is then handed back to the user. + * + * @param until The point in simulation time at which the simulation should be paused and the control is handed + * back to the user. + */ + fun run(until: Instant) } diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt index 93667eb8..30abb7ca 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt @@ -32,11 +32,11 @@ import com.atlarge.opendc.simulator.Bootstrap * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ interface KernelFactory { - /** - * Create a simulation over the given model facilitated by this simulation kernel. - * - * @param bootstrap The bootstrap procedure to bootstrap the simulation with. - * @return A [Kernel] instance to control the simulation. - */ - fun create(bootstrap: Bootstrap): Kernel + /** + * Create a simulation over the given model facilitated by this simulation kernel. + * + * @param bootstrap The bootstrap procedure to bootstrap the simulation with. + * @return A [Kernel] instance to control the simulation. + */ + fun create(bootstrap: Bootstrap): Kernel } diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt index 88b606fd..92d56be1 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt @@ -24,8 +24,8 @@ package com.atlarge.opendc.simulator.platform -import com.atlarge.opendc.simulator.kernel.KernelFactory import com.atlarge.opendc.simulator.Duration +import com.atlarge.opendc.simulator.kernel.KernelFactory /** * A blueprint for a reproducible simulation in a pre-defined setting. @@ -33,20 +33,20 @@ import com.atlarge.opendc.simulator.Duration * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ interface Experiment { - /** - * Run the experiment on the specified kernel implementation. - * - * @param factory The factory to create the simulation kernel with. - * @return The result of the experiment. - */ - fun run(factory: KernelFactory): T + /** + * Run the experiment on the specified kernel implementation. + * + * @param factory The factory to create the simulation kernel with. + * @return The result of the experiment. + */ + fun run(factory: KernelFactory): T - /** - * Run the experiment on the specified kernel implementation. - * - * @param factory The factory to create the simulation kernel with. - * @param timeout The maximum duration of the experiment before returning to the caller. - * @return The result of the experiment or `null`. - */ - fun run(factory: KernelFactory, timeout: Duration): T? + /** + * Run the experiment on the specified kernel implementation. + * + * @param factory The factory to create the simulation kernel with. + * @param timeout The maximum duration of the experiment before returning to the caller. + * @return The result of the experiment or `null`. + */ + fun run(factory: KernelFactory, timeout: Duration): T? } -- cgit v1.2.3 From 157d30beb52c75831e29a1a22c199b95d6d30b42 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 14 Feb 2018 12:32:57 +0100 Subject: refactor(#18): Create distinction between kernel and simulation This change creates a distinction between a kernel and a simulation. A single simulation is represented by a `Simulation` object which provides control over the simulation, while the `Kernel` interface allows users to create a new simulation using that kernel as backend. --- .../com/atlarge/opendc/simulator/Bootstrap.kt | 20 +++--- .../com/atlarge/opendc/simulator/kernel/Kernel.kt | 50 ++------------- .../opendc/simulator/kernel/KernelFactory.kt | 42 ------------ .../atlarge/opendc/simulator/kernel/Simulation.kt | 74 ++++++++++++++++++++++ .../opendc/simulator/platform/Experiment.kt | 6 +- 5 files changed, 94 insertions(+), 98 deletions(-) delete mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt create mode 100644 opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Simulation.kt (limited to 'opendc-core/src/main/kotlin/com') diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt index 10a89704..5f41c727 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Bootstrap.kt @@ -9,15 +9,15 @@ package com.atlarge.opendc.simulator */ interface Bootstrap { /** - * Bootstrap a model `M` for a kernel in the given context. + * Apply the apply procedure for model `M` for a simulation in the given context. * - * @param context The context to bootstrap to model in. - * @return The initialised model for the simulation. + * @param context The context to apply to model in. + * @return The initialised, resulting model for the simulation. */ - fun bootstrap(context: Context): M + fun apply(context: Context): M /** - * A context for the bootstrap of some model type `M` that allows the model to register the entities of the model to + * A context for the apply of some model type `M` that allows the model to register the entities of the model to * the simulation kernel. * * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) @@ -40,7 +40,7 @@ interface Bootstrap { fun deregister(entity: Entity<*, M>): Boolean /** - * Schedule a message for processing by a [Context]. + * Schedule a message to be received by the given [Entity]. * * @param message The message to schedule. * @param destination The destination of the message. @@ -52,13 +52,13 @@ interface Bootstrap { companion object { /** - * Create a [Bootstrap] procedure using the given block to produce a bootstrap for a model of type `M`. + * Create a [Bootstrap] procedure using the given block to produce a apply for a model of type `M`. * - * @param block The block to produce the bootstrap. - * @return The bootstrap procedure that has been built. + * @param block The block to produce the apply. + * @return The apply procedure that has been built. */ fun create(block: (Context) -> M): Bootstrap = object : Bootstrap { - override fun bootstrap(context: Context) = block(context) + override fun apply(context: Context) = block(context) } } } diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt index 29b3bdee..d4995283 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Kernel.kt @@ -25,55 +25,19 @@ package com.atlarge.opendc.simulator.kernel import com.atlarge.opendc.simulator.Bootstrap -import com.atlarge.opendc.simulator.Entity -import com.atlarge.opendc.simulator.Instant /** - * A message based discrete event simulation kernel. + * A message-based discrete event simulator (DES). This interface is a factory for creating [Simulation]s using the + * provided [Bootstrap] for the model. * - * The kernel is created by bootstrapping some model `M` (see [Bootstrap]) to simulate and controls the simulation by - * allowing the user to step over cycles in the simulation and inspect the internal state using [Entity.state]. - * - * A kernel should provide additionally a [KernelFactory] to create new kernel instances given a certain model - * [Bootstrap]. - * - * @param M The shape of the model over which the simulation runs. * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ -interface Kernel { - /** - * The model in which the simulation runs. - */ - val model: M - - /** - * The simulation time. - */ - var time: Instant - - /** - * The observable state of an [Entity] in simulation, which is provided by the simulation context. - */ - val , S> E.state: S - - /** - * 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. - */ - fun step() - - /** - * Run a simulation over the specified model. - * This method will step through multiple cycles in the simulation until no more message exist in the queue. - */ - fun run() - +interface Kernel { /** - * Run a simulation over the specified model, stepping through cycles until the specified clock tick has - * occurred. The control is then handed back to the user. + * Create a simulation over the given model facilitated by this simulation kernel. * - * @param until The point in simulation time at which the simulation should be paused and the control is handed - * back to the user. + * @param bootstrap The apply procedure to apply the simulation with. + * @return A [Simulation] instance representing the simulation. */ - fun run(until: Instant) + fun create(bootstrap: Bootstrap): Simulation } diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt deleted file mode 100644 index 30abb7ca..00000000 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/KernelFactory.kt +++ /dev/null @@ -1,42 +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 com.atlarge.opendc.simulator.kernel - -import com.atlarge.opendc.simulator.Bootstrap - -/** - * A factory for bootstrapping simulation [Kernel] instances. - * - * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) - */ -interface KernelFactory { - /** - * Create a simulation over the given model facilitated by this simulation kernel. - * - * @param bootstrap The bootstrap procedure to bootstrap the simulation with. - * @return A [Kernel] instance to control the simulation. - */ - fun create(bootstrap: Bootstrap): Kernel -} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Simulation.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Simulation.kt new file mode 100644 index 00000000..bb2ef818 --- /dev/null +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/kernel/Simulation.kt @@ -0,0 +1,74 @@ +/* + * 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 com.atlarge.opendc.simulator.kernel + +import com.atlarge.opendc.simulator.Entity +import com.atlarge.opendc.simulator.Instant + +/** + * A message based discrete event simulation over some model `M`. This interface provides direct control over the + * simulation, allowing the user to step over cycles of the simulation and inspecting the state of the simulation via + * [Entity.state]. + * + * @param M The shape of the model over which the simulation runs. + * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) + */ +interface Simulation { + /** + * The model in which the simulation runs. + */ + val model: M + + /** + * The simulation time. + */ + var time: Instant + + /** + * The observable state of an [Entity] in simulation, which is provided by the simulation context. + */ + val , S> E.state: S + + /** + * 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. + */ + fun step() + + /** + * Run a simulation over the specified model. + * This method will step through multiple cycles in the simulation until no more message exist in the queue. + */ + fun run() + + /** + * Run a simulation over the specified model, stepping through cycles until the specified clock tick has + * occurred. The control is then handed back to the user. + * + * @param until The point in simulation time at which the simulation should be paused and the control is handed + * back to the user. + */ + fun run(until: Instant) +} diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt index 92d56be1..eb959ded 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/platform/Experiment.kt @@ -25,7 +25,7 @@ package com.atlarge.opendc.simulator.platform import com.atlarge.opendc.simulator.Duration -import com.atlarge.opendc.simulator.kernel.KernelFactory +import com.atlarge.opendc.simulator.kernel.Kernel /** * A blueprint for a reproducible simulation in a pre-defined setting. @@ -39,7 +39,7 @@ interface Experiment { * @param factory The factory to create the simulation kernel with. * @return The result of the experiment. */ - fun run(factory: KernelFactory): T + fun run(factory: Kernel): T /** * Run the experiment on the specified kernel implementation. @@ -48,5 +48,5 @@ interface Experiment { * @param timeout The maximum duration of the experiment before returning to the caller. * @return The result of the experiment or `null`. */ - fun run(factory: KernelFactory, timeout: Duration): T? + fun run(factory: Kernel, timeout: Duration): T? } -- cgit v1.2.3 From b84a995a05fecb9ef90c9184959f285f324e7411 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Fri, 16 Feb 2018 14:39:53 +0100 Subject: refactor(#18): Provide access to latest sender This change adds a `sender` property to the `Context` interface to provide processes access to the sender of the latest received message. Please note that methods like `hold()` and `interrupt()` may change the value of this property. --- .../src/main/kotlin/com/atlarge/opendc/simulator/Context.kt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'opendc-core/src/main/kotlin/com') diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt index c89bdb59..13170256 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt @@ -50,6 +50,14 @@ interface Context { */ val delta: Duration + /** + * The sender of the last received message or `null` in case the process has not received any messages yet. + * + * Note that this property is only guaranteed to be correct when accessing after a single suspending call. Methods + * like `hold()` and `interrupt()` may change the value of this property. + */ + val sender: Entity<*, *>? + /** * The observable state of the entity bound to this scope. */ -- cgit v1.2.3 From f8a4095d1824df095ea91253f914bc0512646684 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Fri, 16 Feb 2018 15:25:19 +0100 Subject: refactor(#18): Provide access to process context in nested calls This change provides a method in the standard library to access the process context in nested suspending function calls. --- .../kotlin/com/atlarge/opendc/simulator/Context.kt | 30 +++++++++++++++++++--- .../kotlin/com/atlarge/opendc/simulator/Process.kt | 2 +- 2 files changed, 27 insertions(+), 5 deletions(-) (limited to 'opendc-core/src/main/kotlin/com') diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt index 13170256..24f87eff 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt @@ -25,6 +25,7 @@ package com.atlarge.opendc.simulator import java.util.* +import kotlin.coroutines.experimental.CoroutineContext /** * This interface provides a context for simulation of [Entity] instances, by defining the environment in which the @@ -33,7 +34,7 @@ import java.util.* * * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ -interface Context { +interface Context : CoroutineContext.Element { /** * The model of simulation in which the entity exists. */ @@ -50,6 +51,11 @@ interface Context { */ val delta: Duration + /** + * The [Entity] associated with this context. + */ + val self: Entity + /** * The sender of the last received message or `null` in case the process has not received any messages yet. * @@ -68,6 +74,14 @@ interface Context { */ val , S> E.state: S + /** + * Interrupt an [Entity] process in simulation. + * + * @see [Entity.interrupt(Interrupt)] + * @param reason The reason for interrupting the entity. + */ + suspend fun Entity<*, *>.interrupt(reason: String) = interrupt(Interrupt(reason)) + /** * Interrupt an [Entity] process in simulation. * @@ -75,8 +89,10 @@ interface Context { * this call. * Make sure the [Entity] process actually has error handling in place, so it won't take down the whole [Entity] * process as result of the interrupt. + * + * @param interrupt The interrupt to throw at the entity. */ - suspend fun Entity<*, *>.interrupt() + suspend fun Entity<*, *>.interrupt(interrupt: Interrupt) /** * Suspend the [Context] of the [Entity] in simulation for the given duration of simulation time before resuming @@ -159,6 +175,11 @@ interface Context { * @param delay The amount of time to wait before the message should be received by the entity. */ suspend fun Entity<*, *>.send(msg: Any, sender: Entity<*, *>, delay: Duration = 0) + + /** + * This key provides users access to an untyped process context in case the coroutine runs inside a simulation. + */ + companion object Key : CoroutineContext.Key> } /** @@ -185,8 +206,9 @@ interface Envelope { } /** - * An [Interrupt] message is sent to a [Entity] process in order to interrupt its suspended state. + * An [Interrupt] message is sent to an [Entity] process in order to interrupt its suspended state. * + * @param reason The reason for the interruption of the process. * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ -object Interrupt : Throwable("The entity process has been interrupted by another entity") +open class Interrupt(reason: String) : Throwable(reason) diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt index e8b4d988..f2b8a52b 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Process.kt @@ -8,7 +8,7 @@ package com.atlarge.opendc.simulator * @param M The shape of the model in which the process exists. * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl) */ -interface Process : Entity { +interface Process : Entity { /** * This method is invoked to start the simulation a process. * -- cgit v1.2.3 From 59247a4f7a2dc948b3a63ff185c64922eb4334ea Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 19 Feb 2018 15:09:19 +0100 Subject: refactor(#18): Refactor unused transformation receive methods This change removes the unused transformation receive methods from the `Context` class as this functionality can now be easily implemented in the standard library using the newly introduced `sender` property. --- .../kotlin/com/atlarge/opendc/simulator/Context.kt | 50 +--------------------- 1 file changed, 2 insertions(+), 48 deletions(-) (limited to 'opendc-core/src/main/kotlin/com') diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt index 24f87eff..b1d635fd 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt @@ -117,36 +117,13 @@ interface Context : CoroutineContext.Element { */ suspend fun hold(duration: Duration, queue: Queue) - /** - * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is - * empty. The execution is resumed after the head of the mailbox is removed after which the message [Envelope] is - * transformed through `transform` to return the transformed result. - * - * @param transform The block to transform the message with in an envelope context, providing access to the sender - * of the message. - * @return The transformed message. - */ - suspend fun receive(transform: suspend Envelope<*>.(Any) -> T): T - - /** - * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is - * empty. The execution is either resumed after the head of the mailbox is removed after which the message - * [Envelope] is transformed through `transform` to return the transformed result or the timeout has been reached. - * - * @param timeout The duration to wait before resuming execution. - * @param transform The block to transform the message with in an envelope context, providing access to the sender - * of the message. - * @return The processed message or `null` if the timeout was reached. - */ - suspend fun receive(timeout: Duration, transform: suspend Envelope<*>.(Any) -> T): T? - /** * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is * empty. The execution is resumed after the head of the mailbox is removed and returned. * * @return The received message. */ - suspend fun receive(): Any = receive { it } + suspend fun receive(): Any /** * Retrieve and remove a single message from the instance's mailbox, suspending the function if the mailbox is @@ -155,7 +132,7 @@ interface Context : CoroutineContext.Element { * * @return The received message or `null` if the timeout was reached. */ - suspend fun receive(timeout: Duration): Any? = receive(timeout) { it } + suspend fun receive(timeout: Duration): Any? /** * Send the given message to the specified entity, without providing any guarantees about the actual delivery of @@ -182,29 +159,6 @@ interface Context : CoroutineContext.Element { companion object Key : CoroutineContext.Key> } -/** - * The message envelope that is sent to an [Entity], 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) - */ -interface Envelope { - /** - * The message in this envelope. - */ - val message: T - - /** - * The sender of the message. - */ - val sender: Entity<*, *>? - - /** - * The destination of the message. - */ - val destination: Entity<*, *> -} - /** * An [Interrupt] message is sent to an [Entity] process in order to interrupt its suspended state. * -- cgit v1.2.3 From 86dc826db4cd91b5a6875d9ecdd64c9238d7b95c Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 19 Feb 2018 15:12:16 +0100 Subject: refactor(#18): Simplify Context interface This change simplifies the `Context` interface to reduce the amount of methods required to implement by implementors. --- .../kotlin/com/atlarge/opendc/simulator/Context.kt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'opendc-core/src/main/kotlin/com') diff --git a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt index b1d635fd..23d10e8f 100644 --- a/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt +++ b/opendc-core/src/main/kotlin/com/atlarge/opendc/simulator/Context.kt @@ -74,14 +74,6 @@ interface Context : CoroutineContext.Element { */ val , S> E.state: S - /** - * Interrupt an [Entity] process in simulation. - * - * @see [Entity.interrupt(Interrupt)] - * @param reason The reason for interrupting the entity. - */ - suspend fun Entity<*, *>.interrupt(reason: String) = interrupt(Interrupt(reason)) - /** * Interrupt an [Entity] process in simulation. * @@ -94,6 +86,14 @@ interface Context : CoroutineContext.Element { */ suspend fun Entity<*, *>.interrupt(interrupt: Interrupt) + /** + * Interrupt an [Entity] process in simulation. + * + * @see [Entity.interrupt(Interrupt)] + * @param reason The reason for interrupting the entity. + */ + suspend fun Entity<*, *>.interrupt(reason: String) = interrupt(Interrupt(reason)) + /** * Suspend the [Context] of the [Entity] in simulation for the given duration of simulation time before resuming * execution and drop all messages that are received during this period. @@ -139,19 +139,19 @@ interface Context : CoroutineContext.Element { * the message. * * @param msg The message to send. + * @param sender The sender of the message. * @param delay The amount of time to wait before the message should be received by the entity. */ - suspend fun Entity<*, *>.send(msg: Any, delay: Duration = 0) + suspend fun Entity<*, *>.send(msg: Any, sender: Entity<*, *>, delay: Duration = 0) /** * Send the given message to the specified entity, without providing any guarantees about the actual delivery of * the message. * * @param msg The message to send. - * @param sender The sender of the message. * @param delay The amount of time to wait before the message should be received by the entity. */ - suspend fun Entity<*, *>.send(msg: Any, sender: Entity<*, *>, delay: Duration = 0) + suspend fun Entity<*, *>.send(msg: Any, delay: Duration = 0) = send(msg, self, delay) /** * This key provides users access to an untyped process context in case the coroutine runs inside a simulation. -- cgit v1.2.3