diff options
Diffstat (limited to 'odcsim')
40 files changed, 255 insertions, 4379 deletions
diff --git a/odcsim/odcsim-api/build.gradle.kts b/odcsim/odcsim-api/build.gradle.kts index b17cac39..51a80d2c 100644 --- a/odcsim/odcsim-api/build.gradle.kts +++ b/odcsim/odcsim-api/build.gradle.kts @@ -31,10 +31,9 @@ plugins { dependencies { implementation(kotlin("stdlib")) - api("org.slf4j:slf4j-api:${Library.SLF4J}") + api("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Library.KOTLINX_COROUTINES}") testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}") - testImplementation("com.nhaarman.mockitokotlin2:mockito-kotlin:2.0.0") } diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt deleted file mode 100644 index dc6ca7ec..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorContext.kt +++ /dev/null @@ -1,175 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim - -import org.slf4j.Logger - -/** - * Represents the context in which the execution of an actor's behavior takes place. - * - * @param T The shape of the messages the actor accepts. - */ -interface ActorContext<T : Any> { - /** - * The identity of the actor, bound to the lifecycle of this actor instance. - */ - val self: ActorRef<T> - - /** - * A view of the children of this actor. - */ - val children: List<ActorRef<*>> - - /** - * The point of time within the simulation. - */ - val time: Instant - - /** - * The [ActorSystem] the actor is part of. - */ - val system: ActorSystem<*> - - /** - * An actor specific logger instance. - */ - val log: Logger - - /** - * Obtain the child of this actor with the specified name. - * - * @param name The name of the child actor to obtain. - * @return The reference to the child actor or `null` if it does not exist. - */ - fun getChild(name: String): ActorRef<*>? - - /** - * Send the specified message to the actor referenced by this [ActorRef]. - * - * Please note that callees must guarantee that messages are sent strictly in increasing time. - * If so, this method guarantees that: - * - A message will never be received earlier than specified - * - A message might arrive later than specified if the two actors are not synchronized. - * - * @param ref The actor to send the message to. - * @param msg The message to send to the referenced actor. - * @param after The delay after which the message should be received by the actor. - */ - fun <U : Any> send(ref: ActorRef<U>, msg: U, after: Duration = 0.0) - - /** - * Spawn a child actor from the given [Behavior] and with the specified name. - * - * The name may not be empty or start with "$". Moreover, the name of an actor must be unique and this method - * will throw an [IllegalArgumentException] in case a child actor of the given name already exists. - * - * @param behavior The behavior of the child actor to spawn. - * @param name The name of the child actor to spawn. - * @return A reference to the child that has/will be spawned. - */ - fun <U : Any> spawn(behavior: Behavior<U>, name: String): ActorRef<U> - - /** - * Spawn an anonymous child actor from the given [Behavior]. - * - * @param behavior The behavior of the child actor to spawn. - * @return A reference to the child that has/will be spawned. - */ - fun <U : Any> spawnAnonymous(behavior: Behavior<U>): ActorRef<U> - - /** - * Force the specified child actor to terminate after it finishes processing its current message. - * Nothing will happen if the child is already stopped. - * - * Only direct children of an actor may be stopped through the actor context. Trying to stop other actors via this - * method will result in an [IllegalArgumentException]. Instead, stopping other actors has to be expressed as - * an explicit stop message that the actor accept. - * - * @param child The reference to the child actor to stop. - */ - fun stop(child: ActorRef<*>) - - /** - * Watch the specified [ActorRef] for termination of the referenced actor. On termination of the watched actor, - * a [Terminated] signal is sent to this actor. - * - * @param target The target actor to watch. - */ - fun watch(target: ActorRef<*>) - - /** - * Revoke the registration established by [watch]. - * - * In case there exists no registration for the specified [target], no action will be performed. - * - * @param target The target actor to unwatch. - */ - fun unwatch(target: ActorRef<*>) - - /** - * Synchronize the local virtual time of this target with the other referenced actor's local virtual time. - * - * By default, actors are not guaranteed to be synchronized, meaning that for some implementations, virtual time may - * drift between different actors. Synchronization between two actors ensures that virtual time remains consistent - * between at least the two actors. - * - * Be aware that this method may cause a jump in virtual time in order to get consistent with [target]. - * Furthermore, please note that synchronization might incur performance degradation and should only be used - * when necessary. - * - * @param target The reference to the target actor to synchronize with. - */ - fun sync(target: ActorRef<*>) - - /** - * Desynchronize virtual time between two actors if possible. - * - * Please note that this method only provides a hint to the [ActorSystem] that it may drop synchronization between - * the actors, but [ActorSystem] is not compelled to actually do so (i.e. in the case where synchronization is - * always guaranteed). - * - * Furthermore, if [target] is already desychronized, the method should return without error. [ActorContext.isSync] - * may be used to determine if an actor is synchronized. - * - * @param target The reference to the target actor to desynchronize with. - */ - fun unsync(target: ActorRef<*>) - - /** - * Determine whether this actor and [target] are synchronized in virtual time. - * - * @param target The target to check for synchronization. - * @return `true` if [target] is synchronized with this actor, `false` otherwise. - */ - fun isSync(target: ActorRef<*>): Boolean -} - -/** - * Unsafe helper method for widening the type accepted by this [ActorContext]. - */ -fun <U : Any, T : U> ActorContext<T>.unsafeCast(): ActorContext<U> { - @Suppress("UNCHECKED_CAST") - return this as ActorContext<U> -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt deleted file mode 100644 index a6c716a2..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorPath.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim - -import java.io.Serializable - -/** - * An actor path represents the unique path to a specific actor instance within an [ActorSystem]. - */ -sealed class ActorPath : Comparable<ActorPath>, Serializable { - /** - * The name of the actor that this path refers to. - */ - abstract val name: String - - /** - * The path for the parent actor. - */ - abstract val parent: ActorPath - - /** - * Walk up the tree to obtain and return the [ActorPath.Root]. - */ - abstract val root: Root - - /** - * Create a new child actor path. - */ - fun child(name: String): ActorPath = Child(this, name) - - /** - * Create a new child actor path. - */ - operator fun div(name: String): ActorPath = child(name) - - /** - * Recursively create a descendant’s path by appending all child names. - */ - fun descendant(children: Iterable<String>): ActorPath = children.fold(this) { parent, name -> - if (name.isNotBlank()) child(name) else parent - } - - /** - * Root of the hierarchy of [ActorPath]s. There is exactly root per [ActorSystem]. - */ - data class Root(override val name: String = "/") : ActorPath() { - init { - require(name.length == 1 || name.indexOf('/', 1) == -1) { - "/ may only exist at the beginning of the root actors name" - } - require(name.indexOf('#') == -1) { "# may not exist in a path component" } - } - - override val parent: ActorPath = this - - override val root: Root = this - - /** - * Compare the [specified][other] path with this root node for order. If both paths are roots, compare their - * name, otherwise the root is ordered higher. - * - * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater - * than the specified path. - */ - override fun compareTo(other: ActorPath): Int = if (other is Root) name.compareTo(other.name) else 1 - - /** - * Create a string representation of this root node which prints its own [name]. - * - * @return A string representation of this node. - */ - override fun toString(): String = name - } - - /** - * A child in the hierarchy of [ActorPath]s. - */ - data class Child(override val parent: ActorPath, override val name: String) : ActorPath() { - init { - require(name.indexOf('/') == -1) { "/ may not exist in a path component" } - require(name.indexOf('#') == -1) { "# may not exist in a path component" } - } - - override val root: Root by lazy { - when (parent) { - is Root -> parent - else -> parent.root - } - } - - /** - * Compare the [specified][other] path with this child node for order. - * - * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater - * than the specified path. - */ - override fun compareTo(other: ActorPath): Int { - tailrec fun rec(left: ActorPath, right: ActorPath): Int = when { - left == right -> 0 - left is Root -> left.compareTo(right) - right is Root -> -(right.compareTo(left)) - else -> { - val x = left.name.compareTo(right.name) - if (x == 0) - rec(left.parent, right.parent) - else - x - } - } - return rec(this, other) - } - - /** - * Create a string representation of this child node which prints the name of [parent] and its own [name]. - * - * @return A string representation of this node. - */ - override fun toString(): String = "$parent/$name" - } -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt deleted file mode 100644 index d65beebd..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorSystem.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim - -/** - * An actor system is a hierarchical grouping of actors that represents a discrete event simulation. - * - * An implementation of this interface should be provided by an engine. See for example *odcsim-engine-omega*, - * which is the reference implementation of the *odcsim* API. - * - * @param T The shape of the messages the root actor in the system can receive. - */ -interface ActorSystem<in T : Any> : ActorRef<T> { - /** - * The current point in simulation time. - */ - val time: Instant - - /** - * The name of this engine instance, used to distinguish between multiple engines running within the same JVM. - */ - val name: String - - /** - * Run the actors until the specified point in simulation time. - * - * @param until The point until which the simulation should run. - */ - fun run(until: Duration = Duration.POSITIVE_INFINITY) - - /** - * Send the specified message to the root actor of this [ActorSystem]. - * - * @param msg The message to send to the referenced actor. - * @param after The delay after which the message should be received by the actor. - */ - fun send(msg: T, after: Duration = 0.1) - - /** - * Terminates this actor system in an asynchronous fashion. - * - * This will stop the root actor and in turn will recursively stop all its child actors. - */ - fun terminate() - - /** - * Create an actor in the "/system" namespace. This actor will be shut down during `system.terminate()` only after - * all user actors have terminated. - * - * @param behavior The behavior of the system actor to spawn. - * @param name The name of the system actor to spawn. - */ - suspend fun <U : Any> spawnSystem(behavior: Behavior<U>, name: String): ActorRef<U> -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Behavior.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Behavior.kt index 9ad7f83f..1116c447 100644 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Behavior.kt +++ b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Behavior.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2018 atlarge-research + * Copyright (c) 2020 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 @@ -25,169 +25,6 @@ package com.atlarge.odcsim /** - * The representation of the behavior of an actor. - * - * Behavior can be formulated using factory methods on the companion object or by extending either [DeferredBehavior] or - * [ReceivingBehavior]. - * - * Users are advised not to close over [ActorContext] within [Behavior], as it will causes it to become immobile, - * meaning it cannot be moved to another context and executed there, and therefore it cannot be replicated or forked - * either. - * - * @param T The shape of the messages the behavior accepts. - */ -sealed class Behavior<T : Any> { - /** - * Narrow the type of this behavior. - * - * This is almost always a safe operation, but might cause [ClassCastException] in case a narrowed behavior sends - * messages of a different type to itself and is chained via [Behavior.orElse]. - */ - fun <U : T> narrow(): Behavior<U> = unsafeCast() - - /** - * Widen the type of this behavior by placing a funnel in front of it. - * - * @param transform The mapping from the widened type to the original type, returning `null` in-case the message - * should not be handled. - */ - fun <U : Any> widen(transform: (U) -> T?): Behavior<U> { - return wrap(this) { interpreter -> - receive<U> { ctx, msg -> - val res = transform(msg) - @Suppress("UNCHECKED_CAST") - if (res == null || interpreter.interpretMessage(ctx as ActorContext<T>, res)) - unhandled() - else - interpreter.behavior.unsafeCast() - }.unsafeCast() - }.unsafeCast() - } - - /** - * Compose this [Behavior] with a fallback [Behavior] which is used in case this [Behavior] does not handle the - * incoming message or signal. - * - * @param that The fallback behavior. - */ - fun orElse(that: Behavior<T>): Behavior<T> = - wrap(this) { left -> - wrap(that) { right -> - object : ReceivingBehavior<T>() { - override fun receive(ctx: ActorContext<T>, msg: T): Behavior<T> { - if (left.interpretMessage(ctx, msg)) { - return left.behavior - } else if (right.interpretMessage(ctx, msg)) { - return right.behavior - } - - return unhandled() - } - - override fun receiveSignal(ctx: ActorContext<T>, signal: Signal): Behavior<T> { - if (left.interpretSignal(ctx, signal)) { - return left.behavior - } else if (right.interpretSignal(ctx, signal)) { - return right.behavior - } - - return unhandled() - } - } - } - } - - /** - * Unsafe utility method for changing the type accepted by this [Behavior]. - * Be aware that changing the type might result in [ClassCastException], when sending a message to the resulting - * behavior. - */ - fun <U : Any> unsafeCast(): Behavior<U> { - @Suppress("UNCHECKED_CAST") - return this as Behavior<U> - } -} - -/** - * A [Behavior] that defers the construction of the actual [Behavior] until the actor is started in some [ActorContext]. - * If the actor is already started, it will immediately evaluate. - * - * @param T The shape of the messages the behavior accepts. - */ -abstract class DeferredBehavior<T : Any> : Behavior<T>() { - /** - * Create a [Behavior] instance in the [specified][ctx] [ActorContext]. - * - * @param ctx The [ActorContext] in which the behavior runs. - * @return The actor's next behavior. - */ - abstract operator fun invoke(ctx: ActorContext<T>): Behavior<T> -} - -/** - * A [Behavior] that concretely defines how an actor will react to the messages and signals it receives. - * The message may either be of the type that the actor declares and which is part of the [ActorRef] signature, - * or it may be a system [Signal] that expresses a lifecycle event of either this actor or one of its child actors. - * - * @param T The shape of the messages the behavior accepts. - */ -abstract class ReceivingBehavior<T : Any> : Behavior<T>() { - /** - * Process an incoming message of type [T] and return the actor's next behavior. - * - * The returned behavior can in addition to normal behaviors be one of the canned special objects: - * - returning [stopped] will terminate this Behavior - * - returning [same] designates to reuse the current Behavior - * - returning [unhandled] keeps the same Behavior and signals that the message was not yet handled - * - * @param ctx The [ActorContext] in which the actor is currently running. - * @param msg The message that was received. - */ - open fun receive(ctx: ActorContext<T>, msg: T): Behavior<T> = unhandled() - - /** - * Process an incoming [Signal] and return the actor's next behavior. - * - * The returned behavior can in addition to normal behaviors be one of the canned special objects: - * - returning [stopped] will terminate this Behavior - * - returning [same] designates to reuse the current Behavior - * - returning [unhandled] keeps the same Behavior and signals that the message was not yet handled - * - * @param ctx The [ActorContext] in which the actor is currently running. - * @param signal The [Signal] that was received. - */ - open fun receiveSignal(ctx: ActorContext<T>, signal: Signal): Behavior<T> = unhandled() -} - -/** - * A flag to indicate whether a [Behavior] instance is still alive. - */ -val <T : Any> Behavior<T>.isAlive get() = this !is StoppedBehavior - -/** - * A flag to indicate whether the last message/signal went unhandled. - */ -val <T : Any> Behavior<T>.isUnhandled get() = this is UnhandledBehavior - -// The special behaviors are kept in this file as to be able to seal the Behavior class to prevent users from extending -// it. -/** - * A special [Behavior] instance that signals that the actor has stopped. - */ -internal object StoppedBehavior : Behavior<Any>() { - override fun toString() = "Stopped" -} - -/** - * A special [Behavior] object to signal that the actor wants to reuse its previous behavior. - */ -internal object SameBehavior : Behavior<Nothing>() { - override fun toString() = "Same" -} - -/** - * A special [Behavior] object that indicates that the last message or signal was not handled. + * The behavior of a logical process defines how the process operates within its environments, represented as coroutine. */ -internal object UnhandledBehavior : Behavior<Nothing>() { - override fun toString() = "Unhandled" -} +public typealias Behavior = suspend (ctx: ProcessContext) -> Unit diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt deleted file mode 100644 index eac254ec..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Behaviors.kt +++ /dev/null @@ -1,223 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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. - */ -@file:JvmName("Behaviors") -package com.atlarge.odcsim - -import com.atlarge.odcsim.internal.BehaviorInterpreter -import com.atlarge.odcsim.internal.EmptyBehavior -import com.atlarge.odcsim.internal.IgnoreBehavior -import com.atlarge.odcsim.internal.TimerSchedulerImpl -import com.atlarge.odcsim.internal.sendSignal - -/** - * This [Behavior] is used to signal that this actor shall terminate voluntarily. If this actor has created child actors - * then these will be stopped as part of the shutdown procedure. - */ -fun <T : Any> stopped(): Behavior<T> = StoppedBehavior.unsafeCast() - -/** - * This [Behavior] is used to signal that this actor wants to reuse its previous behavior. - */ -fun <T : Any> same(): Behavior<T> = SameBehavior.unsafeCast() - -/** - * This [Behavior] is used to signal to the system that the last message or signal went unhandled. This will - * reuse the previous behavior. - */ -fun <T : Any> unhandled(): Behavior<T> = UnhandledBehavior.unsafeCast() - -/** - * A factory for [Behavior]. Creation of the behavior instance is deferred until the actor is started. - */ -fun <T : Any> setup(block: (ActorContext<T>) -> Behavior<T>): Behavior<T> { - return object : DeferredBehavior<T>() { - override fun invoke(ctx: ActorContext<T>): Behavior<T> = block(ctx) - } -} - -/** - * A [Behavior] that ignores any incoming message or signal and keeps the same behavior. - */ -fun <T : Any> ignore(): Behavior<T> = IgnoreBehavior.narrow() - -/** - * A [Behavior] that treats every incoming message or signal as unhandled. - */ -fun <T : Any> empty(): Behavior<T> = EmptyBehavior.narrow() - -/** - * Construct a [Behavior] that reacts to incoming messages, provides access to the [ActorContext] and returns the - * actor's next behavior. - */ -fun <T : Any> receive(handler: (ActorContext<T>, T) -> Behavior<T>): Behavior<T> { - return object : ReceivingBehavior<T>() { - override fun receive(ctx: ActorContext<T>, msg: T): Behavior<T> = handler(ctx, msg) - } -} - -/** - * Construct a [Behavior] that reacts to incoming messages of type [U], provides access to the [ActorContext] and - * returns the actor's next behavior. Other messages will be unhandled. - */ -inline fun <T : Any, reified U : T> receiveOf(crossinline handler: (ActorContext<T>, U) -> Behavior<T>): Behavior<T> { - return object : ReceivingBehavior<T>() { - override fun receive(ctx: ActorContext<T>, msg: T): Behavior<T> { - return if (msg is U) - handler(ctx, msg) - else - unhandled() - } - } -} - -/** - * Construct a [Behavior] that reacts to incoming messages and returns the actor's next behavior. - */ -fun <T : Any> receiveMessage(handler: (T) -> Behavior<T>): Behavior<T> { - return object : ReceivingBehavior<T>() { - override fun receive(ctx: ActorContext<T>, msg: T): Behavior<T> = handler(msg) - } -} - -/** - * Construct a [Behavior] that reacts to incoming signals, provides access to the [ActorContext] and returns the - * actor's next behavior. - */ -fun <T : Any> receiveSignal(handler: (ActorContext<T>, Signal) -> Behavior<T>): Behavior<T> { - return object : ReceivingBehavior<T>() { - override fun receiveSignal(ctx: ActorContext<T>, signal: Signal): Behavior<T> = handler(ctx, signal) - } -} - -/** - * Construct a [Behavior] that wraps another behavior instance and uses a [BehaviorInterpreter] to pass incoming - * messages and signals to the wrapper behavior. - */ -fun <T : Any> wrap(behavior: Behavior<T>, wrap: (BehaviorInterpreter<T>) -> Behavior<T>): Behavior<T> { - return setup { ctx -> wrap(BehaviorInterpreter(behavior, ctx)) } -} - -/** - * Obtain a [TimerScheduler] for building a [Behavior] instance. - */ -fun <T : Any> withTimers(handler: (TimerScheduler<T>) -> Behavior<T>): Behavior<T> { - return setup { ctx -> - val scheduler = TimerSchedulerImpl(ctx) - receiveSignal<T> { _, signal -> - if (signal is TimerSchedulerImpl.TimerSignal) { - val res = scheduler.interceptTimerSignal(signal) - if (res != null) { - ctx.send(ctx.self, res) - return@receiveSignal same() - } - } - unhandled() - }.join(handler(scheduler)) - } -} - -/** - * Construct a [Behavior] that waits for the specified duration before constructing the next behavior. - * - * @param after The delay before constructing the next behavior. - * @param handler The handler to construct the behavior with. - */ -fun <T : Any> withTimeout(after: Duration, handler: (ActorContext<T>) -> Behavior<T>): Behavior<T> = - setup { ctx -> - val target = Any() - ctx.sendSignal(ctx.self, Timeout(target), after) - receiveSignal { _, signal -> - if (signal is Timeout && signal.target == target) { - handler(ctx) - } else { - unhandled() - } - } - } - -/** - * Join together both [Behavior] with another [Behavior], essentially running them side-by-side, only directly - * propagating stopped behavior. - * - * @param that The behavior to join with. - */ -fun <T : Any> Behavior<T>.join(that: Behavior<T>): Behavior<T> = - wrap(this) { left -> - wrap(that) { right -> - object : ReceivingBehavior<T>() { - override fun receive(ctx: ActorContext<T>, msg: T): Behavior<T> { - if (left.interpretMessage(ctx, msg)) { - return left.propagate(this) // Propagate stopped behavior - } else if (right.interpretMessage(ctx, msg)) { - return right.propagate(this) - } - - return unhandled() - } - - override fun receiveSignal(ctx: ActorContext<T>, signal: Signal): Behavior<T> { - if (left.interpretSignal(ctx, signal)) { - return left.propagate(this) - } else if (right.interpretSignal(ctx, signal)) { - return right.propagate(this) - } - - return unhandled() - } - } - } - } - -/** - * Widen the type of messages the [Behavior] by marking all other messages as unhandled. - */ -inline fun <U : Any, reified T : U> Behavior<T>.widen(): Behavior<U> = widen { - if (it is T) - it - else - null -} - -/** - * Keep the specified [Behavior] alive if it returns the stopped behavior. - */ -fun <T : Any> Behavior<T>.keepAlive(): Behavior<T> = - wrap(this) { interpreter -> - object : ReceivingBehavior<T>() { - override fun receive(ctx: ActorContext<T>, msg: T): Behavior<T> { - if (interpreter.interpretMessage(ctx, msg)) { - return this - } - return empty() - } - - override fun receiveSignal(ctx: ActorContext<T>, signal: Signal): Behavior<T> { - if (interpreter.interpretSignal(ctx, signal)) { - return this - } - - return empty() - } - } - } diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Channel.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Channel.kt new file mode 100644 index 00000000..10b4908a --- /dev/null +++ b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Channel.kt @@ -0,0 +1,70 @@ +/* + * MIT License + * + * Copyright (c) 2019 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.odcsim + +import java.io.Serializable + +/** + * A unidirectional communication medium using message passing. Processes may send messages over a channel, and + * another process is able to receive messages sent over a channel it has a reference to (in form of a [ReceivePort]). + * + * Channels are represented by their send and receive endpoints at which processes may respectively send and receive + * messages from this channel. + * + * Channels and their respective send and receive references may be shared freely between logical processes in the same + * simulation. + */ +public interface Channel<T : Any> : Serializable { + /** + * The endpoint of the channel processes may use to send messages to. + */ + public val send: SendRef<T> + + /** + * The endpoint of the channel processes may receive messages from. + */ + public val receive: ReceiveRef<T> + + /** + * Obtain the send endpoint of the channel when unpacking the channel. See [send]. + */ + public operator fun component1(): SendRef<T> = send + + /** + * Obtain the receive endpoint of the channel when unpacking the channel. See [receive]. + */ + public operator fun component2(): ReceiveRef<T> = receive +} + +/** + * An opaque object representing a [Channel] endpoint through which logical processes can send messages over the + * channel. + */ +public interface SendRef<in T : Any> : Serializable + +/** + * An opaque object representing the receive endpoint of a [Channel]. + */ +public interface ReceiveRef<out T : Any> : Serializable diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Envelope.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Envelope.kt deleted file mode 100644 index 3b73d52d..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Envelope.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim - -import java.io.Serializable - -/** - * A timestamped wrapper for messages that will be delivered to an actor. - */ -interface Envelope<T : Any> : Comparable<Envelope<*>>, Serializable { - /** - * The time at which this message should be delivered. - */ - val time: Instant - - /** - * The message contained in this envelope, of type [T] - */ - val message: T - - /** - * Extract the delivery time from the envelope. - */ - operator fun component1(): Instant = time - - /** - * Extract the message from this envelope. - */ - operator fun component2(): T = message - - /** - * Compare this envelope to the [other] envelope, ordered increasingly in time. - */ - override fun compareTo(other: Envelope<*>): Int = time.compareTo(other.time) -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Port.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Port.kt new file mode 100644 index 00000000..30d4790c --- /dev/null +++ b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Port.kt @@ -0,0 +1,64 @@ +/* + * MIT License + * + * Copyright (c) 2019 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.odcsim + +/** + * A communication endpoint of a specific logical process through which messages pass. + * + * Ports are tied to a specific logical process and may not be shared with other processes. Doing so results in + * undefined behavior and might cause violation of time-consistency between processes. + */ +public interface Port { + /** + * Close this communication endpoint. This informs the process(es) at the other end of the port that the caller + * will not send or receive messages via this port. + * + * This is an idempotent operation – subsequent invocations of this function have no effect and return `false`. + */ + fun close(): Boolean +} + +/** + * A [Port] through which a logical process may receive messages from other [SendPort]s. + */ +public interface ReceivePort<out T : Any> : Port { + /** + * Receive a message send to this port or suspend the caller while no messages have been received at this port yet. + */ + public suspend fun receive(): T +} + +/** + * A [Port] through which logical processes may send messages to a [ReceivePort]. + */ +public interface SendPort<in T : Any> : Port { + /** + * Send a message via this port to the process(es) listening at the other end of the port. + * + * Messages are send asynchronously to the receivers and do not suspend the caller. This method guarantees + * exactly-once delivery while respecting time-consistency between owner of the send port and its receivers. + */ + public fun send(message: T) +} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ProcessContext.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ProcessContext.kt new file mode 100644 index 00000000..2a72e331 --- /dev/null +++ b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ProcessContext.kt @@ -0,0 +1,82 @@ +/* + * MIT License + * + * Copyright (c) 2018 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.odcsim + +import java.time.Clock +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.coroutineContext + +/** + * Represents the execution context of a logical process in simulation. + */ +public interface ProcessContext : CoroutineContext.Element { + /** + * Key for [ProcessContext] instance in the coroutine context. + */ + companion object Key : CoroutineContext.Key<ProcessContext> + + /** + * The reference to the logical process of this context. + */ + public val self: ProcessRef + + /** + * The clock tracking the simulation time. + */ + public val clock: Clock + + /** + * Spawn an anonymous logical process in the simulation universe with the specified [behavior]. + */ + public fun spawn(behavior: Behavior): ProcessRef + + /** + * Spawn a logical process in the simulation universe with the specified [behavior] and [name]. + */ + public fun spawn(behavior: Behavior, name: String): ProcessRef + + /** + * Open a new communication [Channel] for messages of type [T]. + */ + public fun <T : Any> open(): Channel<T> + + /** + * Create a [SendPort] for sending messages to the specified [send]. + */ + public suspend fun <T : Any> connect(send: SendRef<T>): SendPort<T> + + /** + * Create a [ReceivePort] for listening to the messages sent to the specified [receive] endpoint of a channel. + */ + public suspend fun <T : Any> listen(receive: ReceiveRef<T>): ReceivePort<T> +} + +/** + * The process context of the current coroutine. + */ +@Suppress("WRONG_MODIFIER_TARGET") +public suspend inline val processContext: ProcessContext + @Suppress("ILLEGAL_SUSPEND_PROPERTY_ACCESS") + get() = coroutineContext[ProcessContext.Key] ?: throw IllegalStateException("No process context active") diff --git a/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactory.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ProcessRef.kt index 84bf1efb..833458e4 100644 --- a/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactory.kt +++ b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ProcessRef.kt @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2018 atlarge-research + * Copyright (c) 2020 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 @@ -22,17 +22,24 @@ * SOFTWARE. */ -package com.atlarge.odcsim.engine.omega +package com.atlarge.odcsim -import com.atlarge.odcsim.ActorSystem -import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.Behavior -import java.util.ServiceLoader +import java.io.Serializable /** - * An [ActorSystemFactory] for the Omega engine, used by the [ServiceLoader] API to create [OmegaActorSystem] instances. + * A reference to a logical process in simulation. */ -class OmegaActorSystemFactory : ActorSystemFactory { - override operator fun <T : Any> invoke(root: Behavior<T>, name: String): ActorSystem<T> = - OmegaActorSystem(root, name) +public interface ProcessRef : Comparable<ProcessRef>, Serializable { + /** + * The name of the process. + */ + public val name: String + + /** + * Compare [other] process ref with this process reference for order. + * + * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater + * than the specified path. + */ + override fun compareTo(other: ProcessRef): Int = name.compareTo(other.name) } diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Signals.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Signals.kt deleted file mode 100644 index 9b707348..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Signals.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim - -/** - * System signals are notifications that are generated by the system and delivered to the actor behavior in a reliable - * fashion. - */ -interface Signal - -/** - * Lifecycle signal that is fired upon creation of the actor. This will be the first message that the actor receives. - */ -object PreStart : Signal - -/** - * Lifecycle signal that is fired after this actor and all its child actors (transitively) have terminated. - * The [Terminated] signal is only sent to registered watchers after this signal has been processed. - */ -object PostStop : Signal - -/** - * A lifecycle signal to indicate that an actor that was watched has terminated. - * - * @property ref The reference to the actor that has terminated. - * @property failure The failure that caused the termination, or `null` on graceful termination. - */ -data class Terminated(val ref: ActorRef<*>, val failure: Throwable? = null) : Signal - -/** - * A [Signal] to indicate an actor has timed out. - * - * This class contains a [target] property in order to allow nested behavior to function properly when multiple layers - * are waiting on this signal. - * - * @property target The target object that has timed out. - */ -data class Timeout(val target: Any) : Signal diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorRef.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngine.kt index 45fc756e..e35acbf0 100644 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorRef.kt +++ b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngine.kt @@ -24,27 +24,25 @@ package com.atlarge.odcsim -import java.io.Serializable - /** - * A reference to an entity in simulation that accepts messages of type [T]. + * An engine for managing logical processes represented as [Behavior] during simulation. + * + * An implementation of this interface should be provided by an engine. See for example *odcsim-engine-omega*, + * which is the reference implementation of the *odcsim* API. */ -interface ActorRef<in T : Any> : Comparable<ActorRef<*>>, Serializable { +public interface SimulationEngine { /** - * The path for this actor (from this actor up to the root actor). + * The name of this engine instance, used to distinguish between multiple engines running within the same JVM. */ - val path: ActorPath + public val name: String /** - * Compare this reference to another actor reference. + * Run the simulation. */ - override fun compareTo(other: ActorRef<*>): Int = path.compareTo(other.path) -} + public suspend fun run() -/** - * Unsafe helper method for widening the type accepted by this [ActorRef]. - */ -fun <U : Any, T : U> ActorRef<T>.unsafeCast(): ActorRef<U> { - @Suppress("UNCHECKED_CAST") - return this as ActorRef<U> + /** + * Terminates this engine in an asynchronous fashion. + */ + public suspend fun terminate() } diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorSystemFactory.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngineProvider.kt index f59bc966..93dda963 100644 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ActorSystemFactory.kt +++ b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/SimulationEngineProvider.kt @@ -25,14 +25,14 @@ package com.atlarge.odcsim /** - * A factory for [ActorSystem] instances that allows users to dynamically load engine implementations. + * A factory for [SimulationEngine] instances that allows users to dynamically load engine implementations. */ -interface ActorSystemFactory { +public interface SimulationEngineProvider { /** - * Create an [ActorSystem] with the given root [Behavior] and the given name. + * Create an [SimulationEngine] with the given root [Behavior] and the given name. * - * @param root The behavior of the root actor. + * @param root The behavior of the root process. * @param name The name of the engine instance. */ - operator fun <T : Any> invoke(root: Behavior<T>, name: String): ActorSystem<T> + public operator fun invoke(root: Behavior, name: String): SimulationEngine } diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt deleted file mode 100644 index 5d73d808..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/StashBuffer.kt +++ /dev/null @@ -1,88 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim - -import com.atlarge.odcsim.internal.StashBufferImpl - -/** - * A non thread safe mutable message buffer that can be used to buffer messages inside actors and then unstash them. - * - * @param T The shape of the messages in this buffer. - */ -interface StashBuffer<T : Any> { - /** - * The first element of the buffer. - * - * @throws NoSuchElementException if the buffer is empty. - */ - val head: T - - /** - * A flag to indicate whether the buffer is empty. - */ - val isEmpty: Boolean - - /** - * A flag to indicate whether the buffer is full. - */ - val isFull: Boolean - - /** - * The number of elements in the stash buffer. - */ - val size: Int - - /** - * Iterate over all elements of the buffer and apply a function to each element, without removing them. - * - * @param block The function to invoke for each element. - */ - fun forEach(block: (T) -> Unit) - - /** - * Add one element to the end of the message buffer. - * - * @param msg The message to stash. - * @throws IllegalStateException if the element cannot be added at this time due to capacity restrictions - */ - fun stash(msg: T) - - /** - * Process all stashed messages with the behavior and the returned [Behavior] from each processed message. - * - * @param ctx The actor context to process these messages in. - * @param behavior The behavior to process the messages with. - */ - fun unstashAll(ctx: ActorContext<T>, behavior: Behavior<T>): Behavior<T> - - companion object { - /** - * Construct a [StashBuffer] with the specified [capacity]. - * - * @param capacity The capacity of the buffer. - */ - operator fun <T : Any> invoke(capacity: Int): StashBuffer<T> = StashBufferImpl(capacity) - } -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Time.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Time.kt deleted file mode 100644 index f19f6fe2..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/Time.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim - -/** - * An instantaneous point on the time-line, used to record message time-stamps in a simulation. - */ -typealias Instant = Double - -/** - * A time interval which represents the amount of elapsed time between two messages. - */ -typealias Duration = Double - -/** - * Convert this [Int] into an [Instant]. - */ -fun Int.toInstant(): Instant = toDouble() - -/** - * Convert this [Int] into a [Duration]. - */ -fun Int.toDuration(): Duration = toDouble() - -/** - * Convert this [Long] into an [Instant]. - */ -fun Long.toInstant(): Instant = toDouble() - -/** - * Convert this [Long] into a [Duration]. - */ -fun Long.toDuration(): Duration = toDouble() - -/** - * Convert this [Float] into an [Instant]. - */ -fun Float.toInstant(): Instant = toDouble() - -/** - * Convert this [Float] into a [Duration]. - */ -fun Float.toDuration(): Duration = toDouble() - -/** - * Convert this [Double] into an [Instant]. - */ -fun Double.toInstant(): Instant = toDouble() - -/** - * Convert this [Double] into a [Duration]. - */ -fun Double.toDuration(): Duration = toDouble() diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt deleted file mode 100644 index c5c54b64..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/TimerScheduler.kt +++ /dev/null @@ -1,92 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim - -/** - * An interface to provide support for scheduled self messages in an actor. It is used with [withTimers]. - * Timers are bound to the lifecycle of the actor that owns it, and thus are cancelled automatically when it is - * restarted or stopped. - * - * Please be aware that [TimerScheduler] is not thread-safe and must only be used within the actor that owns it. - * - * @param T The shape of the messages the owning actor of this scheduling accepts. - */ -interface TimerScheduler<T : Any> { - /** - * Cancel a timer with the given key. - * - * @param key The key of the timer. - */ - fun cancel(key: Any) - - /** - * Cancel all timers. - */ - fun cancelAll() - - /** - * Check if a timer with a given [key] is active. - * - * @param key The key to check if it is active. - * @return `true` if a timer with the specified key is active, `false` otherwise. - */ - fun isTimerActive(key: Any): Boolean - - /** - * Start a periodic timer that will send [msg] to the `self` actor at a fixed [interval]. - * - * @param key The key of the timer. - * @param msg The message to send to the actor. - * @param interval The interval of simulation time after which it should be sent. - */ - fun startPeriodicTimer(key: Any, msg: T, interval: Duration) - - /** - * Start a timer that will send [msg] once to the `self` actor after the given [delay]. - * - * @param key The key of the timer. - * @param msg The message to send to the actor. - * @param delay The delay in simulation time after which it should be sent. - */ - fun startSingleTimer(key: Any, msg: T, delay: Duration) - - /** - * Run [block] periodically at a fixed [interval] - * - * @param key The key of the timer. - * @param interval The delay of simulation time after which the block should run. - * @param block The block to run. - */ - fun every(key: Any, interval: Duration, block: () -> Unit) - - /** - * Run [block] after the specified [delay]. - * - * @param key The key of the timer. - * @param delay The delay in simulation time after which the block should run. - * @param block The block to run. - */ - fun after(key: Any, delay: Duration, block: () -> Unit) -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt deleted file mode 100644 index eb26add1..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim.coroutines - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.DeferredBehavior -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.internal.SuspendingActorContextImpl -import com.atlarge.odcsim.internal.SuspendingBehaviorImpl -import kotlin.coroutines.Continuation -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn -import kotlin.coroutines.suspendCoroutine - -/** - * A [Behavior] that allows method calls to suspend execution via Kotlin coroutines. - * - * @param T The shape of the messages the actor accepts. - */ -abstract class SuspendingBehavior<T : Any> : DeferredBehavior<T>() { - /** - * Run the suspending logic of this behavior. - * - * @param ctx The [SuspendingActorContext] in which the behavior is executed. - * @return The next behavior for the actor. - */ - abstract suspend operator fun invoke(ctx: SuspendingActorContext<T>): Behavior<T> - - // Immediately transfer to implementation - override fun invoke(ctx: ActorContext<T>): Behavior<T> = SuspendingBehaviorImpl(ctx, this).start() -} - -/** - * An [ActorContext] that provides additional functionality for receiving messages and signals from - * the actor's mailbox. - * - * @param T The shape of the messages the actor accepts. - */ -interface SuspendingActorContext<T : Any> : ActorContext<T>, CoroutineContext.Element { - /** - * Suspend execution of the active coroutine to wait for a message of type [T] to be received in the actor's - * mailbox. During suspension, incoming signals will be marked unhandled. - * - * @return The message of type [T] that has been received. - */ - suspend fun receive(): T - - /** - * Suspend execution of the active coroutine to wait for a [Signal] to be received in the actor's mailbox. - * During suspension, incoming messages will be marked unhandled. - * - * @return The [Signal] that has been received. - */ - suspend fun receiveSignal(): Signal - - /** - * A key to provide access to the untyped [SuspendingActorContext] via [CoroutineContext] for suspending methods - * running inside a [SuspendingBehavior]. - */ - companion object Key : CoroutineContext.Key<SuspendingActorContext<*>> -} - -/** - * Obtains the current continuation instance inside suspend functions and suspends currently running coroutine. [block] - * should return a [Behavior] that will resume the continuation and return the next behavior which is supplied via the - * second argument of the block. - */ -suspend fun <T : Any, U> suspendWithBehavior(block: (Continuation<U>, () -> Behavior<T>) -> Behavior<T>): U = - suspendCoroutine { cont -> - @Suppress("UNCHECKED_CAST") - val ctx = cont.context[SuspendingActorContext] as? SuspendingActorContextImpl<T> - ?: throw UnsupportedOperationException("Coroutine does not run inside SuspendingBehavior") - ctx.become(block(cont) { ctx.behavior }) - } - -/** - * Obtain the current [SuspendingActorContext] instance for the active continuation. - */ -suspend fun <T : Any> actorContext(): SuspendingActorContext<T> = - suspendCoroutineUninterceptedOrReturn { cont -> - @Suppress("UNCHECKED_CAST") - cont.context[SuspendingActorContext] as? SuspendingActorContext<T> - ?: throw UnsupportedOperationException("Coroutine does not run inside SuspendingBehavior") - } - -/** - * Construct a [Behavior] that uses Kotlin coroutines functionality to handle incoming messages and signals. - */ -fun <T : Any> suspending(block: suspend (SuspendingActorContext<T>) -> Behavior<T>): Behavior<T> { - return object : SuspendingBehavior<T>() { - override suspend fun invoke(ctx: SuspendingActorContext<T>) = block(ctx) - } -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt deleted file mode 100644 index e995c0e3..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Receive.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim.coroutines.dsl - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.coroutines.SuspendingActorContext -import com.atlarge.odcsim.coroutines.suspendWithBehavior -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.unhandled -import kotlin.coroutines.resume - -/** - * Receive only messages of type [U] and mark all other messages as unhandled. - * - * @return The received message. - */ -suspend inline fun <T : Any, reified U : T> SuspendingActorContext<T>.receiveOf(): U = - suspendWithBehavior<T, U> { cont, next -> - receiveMessage { msg -> - if (msg is U) { - cont.resume(msg) - next() - } else { - unhandled() - } - } - } - -/** - * Send the specified message to the given reference and wait for a reply. - * - * @param ref The actor to send the message to. - * @param after The delay after which the message should be received by the actor. - * @param transform The block to transform `self` to a message. - */ -suspend inline fun <T : Any, U : Any, reified V : T> SuspendingActorContext<T>.ask( - ref: ActorRef<U>, - after: Duration = 0.0, - transform: (ActorRef<T>) -> U -): V { - send(ref, transform(self), after) - return receiveOf() -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt deleted file mode 100644 index 16b6f534..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim.coroutines.dsl - -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.coroutines.suspendWithBehavior -import com.atlarge.odcsim.withTimeout -import kotlin.coroutines.resume - -/** - * Block execution for the specified duration. - * - * @param after The duration after which execution should continue. - */ -suspend fun timeout(after: Duration) = suspendWithBehavior<Any, Unit> { cont, next -> - withTimeout(after) { - cont.resume(Unit) - next() - } -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt deleted file mode 100644 index f1aba25e..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/ActorContext.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.Signal - -/** - * Send the specified [Signal] to the given actor reference after the specified duration. - * - * @param ref The actor to send the signal to. - * @param signal The signal to send to the referenced actor. - * @param after The delay after which the signal should be received by the actor. - */ -fun ActorContext<*>.sendSignal(ref: ActorRef<*>, signal: Signal, after: Duration = 0.0) { - // Signals are currently processed as regular messages - @Suppress("UNCHECKED_CAST") - send(ref as ActorRef<Any>, signal, after) -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt deleted file mode 100644 index b07cabc0..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/Behavior.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.ReceivingBehavior -import com.atlarge.odcsim.Signal - -/** - * A [Behavior] object that ignores all messages sent to the actor. - */ -internal object IgnoreBehavior : ReceivingBehavior<Any>() { - override fun receive(ctx: ActorContext<Any>, msg: Any): Behavior<Any> = this - - override fun receiveSignal(ctx: ActorContext<Any>, signal: Signal): Behavior<Any> = this - - override fun toString() = "Ignore" -} - -/** - * A [Behavior] object that does not handle any message it receives. - */ -internal object EmptyBehavior : ReceivingBehavior<Any>() { - override fun toString() = "Empty" -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt deleted file mode 100644 index 194c2a62..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/BehaviorInterpreter.kt +++ /dev/null @@ -1,201 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.DeferredBehavior -import com.atlarge.odcsim.ReceivingBehavior -import com.atlarge.odcsim.SameBehavior -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.StoppedBehavior -import com.atlarge.odcsim.UnhandledBehavior -import com.atlarge.odcsim.isAlive -import com.atlarge.odcsim.isUnhandled - -/** - * Helper class that interprets messages/signals, canonicalizes special objects and manages the life-cycle of - * [Behavior] instances. - * - * @param initialBehavior The initial behavior to use. - */ -class BehaviorInterpreter<T : Any>(initialBehavior: Behavior<T>) { - /** - * The current [Behavior] instance. - */ - var behavior: Behavior<T> = initialBehavior - private set - - /** - * A flag to indicate the interpreter is still alive. - */ - val isAlive: Boolean get() = behavior.isAlive - - /** - * Construct a [BehaviorInterpreter] with the specified initial behavior and immediately start it in the specified - * context. - * - * @param initialBehavior The initial behavior of the actor. - * @param ctx The [ActorContext] to run the behavior in. - */ - constructor(initialBehavior: Behavior<T>, ctx: ActorContext<T>) : this(initialBehavior) { - start(ctx) - } - - /** - * Start the initial behavior. - * - * @param ctx The [ActorContext] to start the behavior in. - */ - fun start(ctx: ActorContext<T>) { - behavior = validateAsInitial(start(ctx, behavior)) - } - - /** - * Stop the current active behavior and move into the stopped state. - * - * @param ctx The [ActorContext] this takes place in. - */ - fun stop(ctx: ActorContext<T>) { - behavior = start(ctx, StoppedBehavior.narrow()) - } - - /** - * Replace the current behavior with the specified new behavior. - * - * @param ctx The [ActorContext] to run the behavior in. - * @param next The behavior to replace the current behavior with. - */ - fun become(ctx: ActorContext<T>, next: Behavior<T>) { - this.behavior = canonicalize(ctx, behavior, next) - } - - /** - * Propagate special states of the wrapper [Behavior] to the specified [Behavior]. This means - * that if the behavior of this interpreter is stopped or unhandled, this will be propagated. - * - * @param behavior The [Behavior] to map. - * @return Either the specified [Behavior] or the propagated special objects. - */ - fun propagate(behavior: Behavior<T>): Behavior<T> = - if (this.behavior.isUnhandled || !this.behavior.isAlive) - this.behavior - else - behavior - - /** - * Interpret the given message of type [T] using the current active behavior. - * - * @return `true` if the message was handled by the active behavior, `false` otherwise. - */ - fun interpretMessage(ctx: ActorContext<T>, msg: T): Boolean = interpret(ctx, msg, false) - - /** - * Interpret the given [Signal] using the current active behavior. - * - * @return `true` if the signal was handled by the active behavior, `false` otherwise. - */ - fun interpretSignal(ctx: ActorContext<T>, signal: Signal): Boolean = interpret(ctx, signal, true) - - /** - * Interpret the given message or signal using the current active behavior. - * - * @return `true` if the message or signal was handled by the active behavior, `false` otherwise. - */ - private fun interpret(ctx: ActorContext<T>, msg: Any, isSignal: Boolean): Boolean = - if (isAlive) { - val next = when (val current = behavior) { - is DeferredBehavior<T> -> - throw IllegalStateException("Deferred [$current] should not be passed to interpreter") - is ReceivingBehavior<T> -> - if (isSignal) - current.receiveSignal(ctx, msg as Signal) - else - @Suppress("UNCHECKED_CAST") - current.receive(ctx, msg as T) - is SameBehavior, is UnhandledBehavior -> - throw IllegalStateException("Cannot execute with [$current] as behavior") - is StoppedBehavior -> current - } - - val unhandled = next.isUnhandled - behavior = canonicalize(ctx, behavior, next) - !unhandled - } else { - false - } - - /** - * Validate whether the given [Behavior] can be used as initial behavior. Throw an [IllegalArgumentException] if - * the [Behavior] is not valid. - * - * @param behavior The behavior to validate. - */ - private fun validateAsInitial(behavior: Behavior<T>): Behavior<T> = - when (behavior) { - is SameBehavior, is UnhandledBehavior -> - throw IllegalArgumentException("Cannot use [$behavior] as initial behavior") - else -> behavior - } - - /** - * Helper methods to properly manage the special, canned behavior objects. It highly recommended to use the - * [BehaviorInterpreter] instead to properly manage the life-cycles of the behavior objects. - */ - companion object { - /** - * Start the initial behavior of an actor in the specified [ActorContext]. - * - * This will activate the initial behavior and canonicalize the resulting behavior. - * - * @param ctx The [ActorContext] to start the behavior in. - * @param behavior The initial behavior to start. - * @return The behavior that has been started. - */ - tailrec fun <T : Any> start(ctx: ActorContext<T>, behavior: Behavior<T>): Behavior<T> = - when (behavior) { - is DeferredBehavior<T> -> start(ctx, behavior(ctx)) - else -> behavior - } - - /** - * Given a possibly special behavior (same or unhandled) and a "current" behavior (which defines the meaning of - * encountering a `same` behavior) this method computes the next behavior, suitable for passing a message or - * signal. - * - * @param ctx The context in which the actor runs. - * @param current The actor's current behavior. - * @param next The actor's next behavior. - * @return The actor's canonicalized next behavior. - */ - tailrec fun <T : Any> canonicalize(ctx: ActorContext<T>, current: Behavior<T>, next: Behavior<T>): Behavior<T> = - when (next) { - is SameBehavior, current -> current - is UnhandledBehavior -> current - is DeferredBehavior<T> -> canonicalize(ctx, current, next(ctx)) - else -> next - } - } -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt deleted file mode 100644 index c3346bdf..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt +++ /dev/null @@ -1,182 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.ActorSystem -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.ReceivingBehavior -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.coroutines.SuspendingActorContext -import com.atlarge.odcsim.coroutines.SuspendingBehavior -import com.atlarge.odcsim.coroutines.suspendWithBehavior -import com.atlarge.odcsim.empty -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.receiveSignal -import kotlin.coroutines.Continuation -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.resume -import kotlin.coroutines.startCoroutine -import org.slf4j.Logger - -/** - * This interface exposes internal functionality provided by [SuspendingBehaviorImpl] on [SuspendingActorContext] to - * control the active behavior of the coroutine. - */ -interface SuspendingActorContextImpl<T : Any> : SuspendingActorContext<T> { - /** - * The current active behavior - */ - val behavior: Behavior<T> - - /** - * Replace the current active behavior with the specified new behavior. - * - * @param next The behavior to replace the current behavior with. - */ - fun become(next: Behavior<T>) -} - -/** - * Implementation of [SuspendingBehavior] class that maps the suspending method calls to the [Behavior] - * interface. - * This implementation uses the fact that each actor is thread-safe (as it processes its mailbox sequentially). - */ -internal class SuspendingBehaviorImpl<T : Any>( - private var actorContext: ActorContext<T>, - initialBehavior: SuspendingBehavior<T> -) : ReceivingBehavior<T>(), SuspendingActorContextImpl<T> { - - /** - * The next behavior to use. - */ - private var next: Behavior<T> = this - - /** - * The [BehaviorInterpreter] to wrap the suspending behavior. - */ - private val interpreter = BehaviorInterpreter(initialBehavior) - - override fun receive(ctx: ActorContext<T>, msg: T): Behavior<T> { - this.actorContext = ctx - return interpreter.also { it.interpretMessage(ctx, msg) }.propagate(next) - } - - override fun receiveSignal(ctx: ActorContext<T>, signal: Signal): Behavior<T> { - this.actorContext = ctx - return interpreter.also { it.interpretSignal(ctx, signal) }.propagate(next) - } - - override val self: ActorRef<T> get() = actorContext.self - - override val time: Instant get() = actorContext.time - - override val children: List<ActorRef<*>> - get() = actorContext.children - - override val system: ActorSystem<*> - get() = actorContext.system - - override val log: Logger - get() = actorContext.log - - override fun getChild(name: String): ActorRef<*>? = actorContext.getChild(name) - - override fun <U : Any> send(ref: ActorRef<U>, msg: U, after: Duration) = actorContext.send(ref, msg, after) - - override fun <U : Any> spawn(behavior: Behavior<U>, name: String) = actorContext.spawn(behavior, name) - - override fun <U : Any> spawnAnonymous(behavior: Behavior<U>) = actorContext.spawnAnonymous(behavior) - - override fun stop(child: ActorRef<*>) = actorContext.stop(child) - - override fun watch(target: ActorRef<*>) = actorContext.watch(target) - - override fun unwatch(target: ActorRef<*>) = actorContext.unwatch(target) - - override fun sync(target: ActorRef<*>) = actorContext.sync(target) - - override fun unsync(target: ActorRef<*>) = actorContext.unsync(target) - - override fun isSync(target: ActorRef<*>): Boolean = actorContext.isSync(target) - - override suspend fun receive(): T = suspendWithBehavior<T, T> { cont, next -> - receiveMessage { msg -> - cont.resume(msg) - next() - } - } - - override suspend fun receiveSignal(): Signal = suspendWithBehavior<T, Signal> { cont, next -> - receiveSignal { _, signal -> - cont.resume(signal) - next() - } - } - - override val behavior: Behavior<T> get() = interpreter.behavior - - override fun become(next: Behavior<T>) { - interpreter.become(actorContext, next) - } - - override val key: CoroutineContext.Key<*> = SuspendingActorContext.Key - - /** - * Start the suspending behavior. - */ - internal fun start(): Behavior<T> { - val behavior = interpreter.behavior as SuspendingBehavior<T> - val block = suspend { behavior(this) } - interpreter.become(actorContext, empty()) - block.startCoroutine(SuspendingBehaviorImplContinuation()) - return next - } - - /** - * Stop the suspending behavior. - */ - private fun stop() { - this.interpreter.stop(actorContext) - } - - /** - * The continuation of suspending behavior. - */ - private inner class SuspendingBehaviorImplContinuation : Continuation<Behavior<T>> { - override val context = this@SuspendingBehaviorImpl - - override fun resumeWith(result: Result<Behavior<T>>) { - if (result.isSuccess) { - next = result.getOrNull()!! - } else if (result.isFailure) { - throw result.exceptionOrNull()!! - } - } - } -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt deleted file mode 100644 index 24c3a9d5..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/StashBufferImpl.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.StashBuffer -import java.util.ArrayDeque - -/** - * Internal implementation of the [StashBuffer] interface. - */ -internal class StashBufferImpl<T : Any>(private val capacity: Int) : StashBuffer<T> { - /** - * The internal queue used to store the messages. - */ - private val queue = ArrayDeque<T>(capacity) - - override val head: T - get() = queue.first - - override val isEmpty: Boolean - get() = queue.isEmpty() - - override val isFull: Boolean - get() = size > capacity - - override val size: Int - get() = queue.size - - override fun forEach(block: (T) -> Unit) { - queue.toList().forEach(block) - } - - override fun stash(msg: T) { - queue.add(msg) - } - - override fun unstashAll(ctx: ActorContext<T>, behavior: Behavior<T>): Behavior<T> { - val messages = queue.toList() - queue.clear() - - val interpreter = BehaviorInterpreter<T>(behavior) - interpreter.start(ctx) - - for (message in messages) { - interpreter.interpretMessage(ctx, message) - } - - return interpreter.behavior - } -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt deleted file mode 100644 index 22bec507..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/TimerSchedulerImpl.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim.internal - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.TimerScheduler - -/** - * Implementation of [TimerScheduler] that uses the actor's [ActorContext] to provide timer functionality. - * - * @property ctx The actor context to use. - */ -internal class TimerSchedulerImpl<T : Any>(private val ctx: ActorContext<T>) : TimerScheduler<T> { - private val timers = mutableMapOf<Any, Timer<T>>() - - override fun cancel(key: Any) { - val timer = timers[key] ?: return - ctx.log.debug("Cancel timer [{}] with generation [{}]", timer.key, timer.generation) - timers -= timer.key - } - - override fun cancelAll() { - ctx.log.debug("Cancel all timers") - timers.clear() - } - - override fun isTimerActive(key: Any): Boolean = timers.containsKey(key) - - override fun startPeriodicTimer(key: Any, msg: T, interval: Duration) { - startTimer(key, msg, interval, true) - } - - override fun startSingleTimer(key: Any, msg: T, delay: Duration) { - startTimer(key, msg, delay, false) - } - - override fun every(key: Any, interval: Duration, block: () -> Unit) { - @Suppress("UNCHECKED_CAST") - startTimer(key, Block(block) as T, interval, true) - } - - override fun after(key: Any, delay: Duration, block: () -> Unit) { - @Suppress("UNCHECKED_CAST") - startTimer(key, Block(block) as T, delay, false) - } - - private fun startTimer(key: Any, msg: T, duration: Duration, repeat: Boolean) { - val timer = timers.getOrPut(key) { Timer(key) } - timer.duration = duration - timer.generation += 1 - timer.msg = msg - timer.repeat = repeat - ctx.sendSignal(ctx.self, TimerSignal(key, timer.generation), duration) - ctx.log.debug("Start timer [{}] with generation [{}]", key, timer.generation) - } - - fun interceptTimerSignal(signal: TimerSignal): T? { - val timer = timers[signal.key] - - if (timer == null) { - // Message was from canceled timer that was already enqueued - ctx.log.debug("Received timer [{}] that has been removed, discarding", signal.key) - return null - } else if (signal.generation != timer.generation) { - // Message was from an old timer that was enqueued before canceled - ctx.log.debug("Received timer [{}] from old generation [{}], expected generation [{}], discarding", - signal.key, signal.generation, timer.generation) - } - - if (!timer.repeat) { - timers -= timer.key - } else { - ctx.sendSignal(ctx.self, signal, timer.duration) - } - - val msg = timer.msg - - if (msg is Block) { - msg() - return null - } - - return msg - } - - data class Timer<T : Any>(val key: Any) { - var duration: Duration = 0.0 - var repeat: Boolean = false - var generation: Int = 0 - lateinit var msg: T - } - - data class TimerSignal(val key: Any, val generation: Int) : Signal - - data class Block(val block: () -> Unit) { - operator fun invoke() = block() - } -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt deleted file mode 100644 index bf50b5e8..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationAwareLoggerImpl.kt +++ /dev/null @@ -1,567 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim.internal.logging - -import com.atlarge.odcsim.ActorContext -import org.slf4j.Logger -import org.slf4j.Marker -import org.slf4j.helpers.MessageFormatter -import org.slf4j.spi.LocationAwareLogger - -/** - * An actor-specific [Logger] implementation that is aware of the calling location. - * - * @param ctx The owning [ActorContext] of this logger. - * @param delegate The [LocationAwareLogger] to delegate the messages to. - */ -internal class LocationAwareLoggerImpl( - ctx: ActorContext<*>, - private val delegate: LocationAwareLogger -) : LoggerImpl(ctx), Logger by delegate { - /** - * The fully qualified name of this class. - */ - private val fqcn = LocationAwareLoggerImpl::class.java.name - - override fun trace(format: String?, arg: Any?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun trace(format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun trace(format: String?, argArray: Array<Any?>) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, argArray, null) - } - } - - override fun trace(msg: String?, t: Throwable?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.TRACE_INT, msg, null, t) - } - } - - override fun trace(marker: Marker?, msg: String?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, msg, null, null) - } - } - - override fun trace(marker: Marker?, format: String?, arg: Any?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun trace(marker: Marker?, format: String?, argArray: Array<Any?>) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, formattedMessage, argArray, null) - } - } - - override fun trace(marker: Marker?, msg: String?, t: Throwable?) { - if (!delegate.isTraceEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.TRACE_INT, msg, null, t) - } - } - - override fun debug(msg: String?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null) - } - } - - override fun debug(format: String?, arg: Any?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun debug(format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun debug(format: String?, argArray: Array<Any?>) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val ft = MessageFormatter.arrayFormat(format, argArray) - delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, ft.argArray, ft.throwable) - } - } - - override fun debug(msg: String?, t: Throwable?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, t) - } - } - - override fun debug(marker: Marker?, msg: String?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, null) - } - } - - override fun debug(marker: Marker?, format: String?, arg: Any?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val ft = MessageFormatter.format(format, arg) - delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, ft.argArray, ft.throwable) - } - } - - override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun debug(marker: Marker?, format: String?, argArray: Array<Any?>) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - val ft = MessageFormatter.arrayFormat(format, argArray) - delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, ft.message, argArray, ft.throwable) - } - } - - override fun debug(marker: Marker?, msg: String?, t: Throwable?) { - if (!delegate.isDebugEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.DEBUG_INT, msg, null, t) - } - } - - override fun info(msg: String?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, null) - } - } - - override fun info(format: String?, arg: Any?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun info(format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun info(format: String?, argArray: Array<Any?>) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, argArray, null) - } - } - - override fun info(msg: String?, t: Throwable?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.INFO_INT, msg, null, t) - } - } - - override fun info(marker: Marker?, msg: String?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, msg, null, null) - } - } - - override fun info(marker: Marker?, format: String?, arg: Any?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun info(marker: Marker?, format: String?, argArray: Array<Any?>) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, formattedMessage, argArray, null) - } - } - - override fun info(marker: Marker?, msg: String?, t: Throwable?) { - if (!delegate.isInfoEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.INFO_INT, msg, null, t) - } - } - - override fun warn(msg: String?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, null) - } - } - - override fun warn(format: String?, arg: Any?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun warn(format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun warn(format: String?, argArray: Array<Any?>) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, argArray, null) - } - } - - override fun warn(msg: String?, t: Throwable?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.WARN_INT, msg, null, t) - } - } - - override fun warn(marker: Marker?, msg: String?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, msg, null, null) - } - } - - override fun warn(marker: Marker?, format: String?, arg: Any?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun warn(marker: Marker?, format: String?, argArray: Array<Any?>) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, formattedMessage, argArray, null) - } - } - - override fun warn(marker: Marker?, msg: String?, t: Throwable?) { - if (!delegate.isWarnEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.WARN_INT, msg, null, t) - } - } - - override fun error(msg: String?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null) - } - } - - override fun error(format: String?, arg: Any?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun error(format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun error(format: String?, argArray: Array<Any?>) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, argArray, null) - } - } - - override fun error(msg: String?, t: Throwable?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - delegate.log(null, fqcn, LocationAwareLogger.ERROR_INT, msg, null, t) - } - } - - override fun error(marker: Marker?, msg: String?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, msg, null, null) - } - } - - override fun error(marker: Marker?, format: String?, arg: Any?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg).message - delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg), null) - } - } - - override fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.format(format, arg1, arg2).message - delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, arrayOf(arg1, arg2), null) - } - } - - override fun error(marker: Marker?, format: String?, argArray: Array<Any?>) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - val formattedMessage = MessageFormatter.arrayFormat(format, argArray).message - delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, formattedMessage, argArray, null) - } - } - - override fun error(marker: Marker?, msg: String?, t: Throwable?) { - if (!delegate.isErrorEnabled) { - return - } - - withMdc { - delegate.log(marker, fqcn, LocationAwareLogger.ERROR_INT, msg, null, t) - } - } -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt deleted file mode 100644 index 999e30e6..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/logging/LocationIgnorantLoggerImpl.kt +++ /dev/null @@ -1,440 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim.internal.logging - -import com.atlarge.odcsim.ActorContext -import org.slf4j.Logger -import org.slf4j.Marker - -/** - * A [Logger] implementation that is not aware of the calling location. - * - * @param ctx The owning [ActorContext] of this logger. - * @param delegate The [Logger] to delegate the messages to. - */ -internal class LocationIgnorantLoggerImpl( - ctx: ActorContext<*>, - private val delegate: Logger -) : LoggerImpl(ctx), Logger by delegate { - override fun warn(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(marker, format, arg1, arg2) } - } - - override fun warn(format: String?, arg1: Any?, arg2: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(format, arg1, arg2) } - } - - override fun warn(msg: String?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(msg) } - } - - override fun warn(marker: Marker?, format: String?, arg: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(marker, format, arg) } - } - - override fun warn(marker: Marker?, format: String?, vararg arguments: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(marker, format, arguments) } - } - - override fun warn(format: String?, arg: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(format, arg) } - } - - override fun warn(marker: Marker?, msg: String?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(marker, msg) } - } - - override fun warn(msg: String?, t: Throwable?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(msg, t) } - } - - override fun warn(format: String?, vararg arguments: Any?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(format, *arguments) } - } - - override fun warn(marker: Marker?, msg: String?, t: Throwable?) { - if (!isWarnEnabled) { - return - } - - withMdc { delegate.warn(marker, msg, t) } - } - - override fun info(marker: Marker?, format: String?, vararg arguments: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(marker, format, *arguments) } - } - - override fun info(format: String?, arg: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(format, arg) } - } - - override fun info(marker: Marker?, msg: String?, t: Throwable?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(marker, msg, t) } - } - - override fun info(msg: String?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(msg) } - } - - override fun info(format: String?, vararg arguments: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(format, *arguments) } - } - - override fun info(format: String?, arg1: Any?, arg2: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(format, arg1, arg2) } - } - - override fun info(marker: Marker?, msg: String?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(marker, msg) } - } - - override fun info(marker: Marker?, format: String?, arg: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(marker, format, arg) } - } - - override fun info(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(marker, format, arg1, arg2) } - } - - override fun info(msg: String?, t: Throwable?) { - if (!isInfoEnabled) { - return - } - - withMdc { delegate.info(msg, t) } - } - - override fun error(msg: String?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(msg) } - } - - override fun error(marker: Marker?, msg: String?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(marker, msg) } - } - - override fun error(format: String?, vararg arguments: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(format, *arguments) } - } - - override fun error(format: String?, arg: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(format, arg) } - } - - override fun error(msg: String?, t: Throwable?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(msg, t) } - } - - override fun error(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(marker, format, arg1, arg2) } - } - - override fun error(marker: Marker?, format: String?, vararg arguments: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(marker, format, *arguments) } - } - - override fun error(marker: Marker?, msg: String?, t: Throwable?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(marker, msg, t) } - } - - override fun error(format: String?, arg1: Any?, arg2: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(format, arg1, arg2) } - } - - override fun error(marker: Marker?, format: String?, arg: Any?) { - if (!isErrorEnabled) { - return - } - - withMdc { delegate.error(marker, format, arg) } - } - - override fun debug(format: String?, vararg arguments: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(format, *arguments) } - } - - override fun debug(format: String?, arg1: Any?, arg2: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(format, arg1, arg2) } - } - - override fun debug(msg: String?, t: Throwable?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(msg, t) } - } - - override fun debug(format: String?, arg: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(format, arg) } - } - - override fun debug(marker: Marker?, msg: String?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(marker, msg) } - } - - override fun debug(msg: String?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(msg) } - } - - override fun debug(marker: Marker?, msg: String?, t: Throwable?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(marker, msg, t) } - } - - override fun debug(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(marker, format, arg1, arg2) } - } - - override fun debug(marker: Marker?, format: String?, arg: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(marker, format, arg) } - } - - override fun debug(marker: Marker?, format: String?, vararg arguments: Any?) { - if (!isDebugEnabled) { - return - } - - withMdc { delegate.debug(marker, format, *arguments) } - } - - override fun trace(format: String?, arg: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(format, arg) } - } - - override fun trace(marker: Marker?, msg: String?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(marker, msg) } - } - - override fun trace(msg: String?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(msg) } - } - - override fun trace(msg: String?, t: Throwable?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(msg, t) } - } - - override fun trace(format: String?, arg1: Any?, arg2: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(format, arg1, arg2) } - } - - override fun trace(marker: Marker?, format: String?, arg1: Any?, arg2: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(marker, format, arg1, arg2) } - } - - override fun trace(marker: Marker?, format: String?, arg: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(marker, format, arg) } - } - - override fun trace(marker: Marker?, format: String?, vararg argArray: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(marker, format, *argArray) } - } - - override fun trace(marker: Marker?, msg: String?, t: Throwable?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(marker, msg, t) } - } - - override fun trace(format: String?, vararg arguments: Any?) { - if (!isTraceEnabled) { - return - } - - withMdc { delegate.trace(format, *arguments) } - } -} diff --git a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt deleted file mode 100644 index f971f08d..00000000 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/internal/logging/LoggerImpl.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim.internal.logging - -import com.atlarge.odcsim.ActorContext -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.slf4j.MDC -import org.slf4j.spi.LocationAwareLogger - -/** - * An actor-specific [Logger] implementation. - * - * @param ctx The owning [ActorContext] of this logger. - */ -abstract class LoggerImpl internal constructor(protected val ctx: ActorContext<*>) : Logger { - /** - * Configure [MDC] with actor-specific information. - */ - protected inline fun withMdc(block: () -> Unit) { - MDC.put(MDC_ACTOR_SYSTEM, ctx.system.name) - MDC.put(MDC_ACTOR_TIME, String.format("%.2f", ctx.time)) - MDC.put(MDC_ACTOR_REF, ctx.self.path.toString()) - try { - block() - } finally { - MDC.remove(MDC_ACTOR_SYSTEM) - MDC.remove(MDC_ACTOR_TIME) - MDC.remove(MDC_ACTOR_REF) - } - } - - /** - * Mapped Diagnostic Context (MDC) attribute names. - */ - companion object { - val MDC_ACTOR_SYSTEM = "actor.system" - val MDC_ACTOR_TIME = "actor.time" - val MDC_ACTOR_REF = "actor.ref" - - /** - * Create a [Logger] for the specified [ActorContext]. - * - * @param ctx The actor context to create the logger for. - */ - operator fun invoke(ctx: ActorContext<*>): Logger { - val logger = LoggerFactory.getLogger(ctx.javaClass) - return if (logger is LocationAwareLogger) { - LocationAwareLoggerImpl(ctx, logger) - } else { - LocationIgnorantLoggerImpl(ctx, logger) - } - } - } -} diff --git a/odcsim/odcsim-api/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt b/odcsim/odcsim-api/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt deleted file mode 100644 index 023d3efd..00000000 --- a/odcsim/odcsim-api/src/test/kotlin/com/atlarge/odcsim/ActorPathTest.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim - -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -/** - * A test suite for the [ActorPath] class. - */ -@DisplayName("ActorPath") -class ActorPathTest { - /** - * Test whether an [ActorPath.Root] may only start with a slash. - */ - @Test - fun `root node may only start with a slash`() { - ActorPath.Root() // Assert slash at start - assertThrows<IllegalArgumentException> { ActorPath.Root("abc/") } - } - - /** - * Test whether an [ActorPath.Child] disallows names with a slash. - */ - @Test - fun `child node should not allow name with a slash`() { - assertThrows<IllegalArgumentException> { ActorPath.Child(ActorPath.Root(), "/") } - } - - /** - * Test whether a root node can have a custom name. - */ - @Test - fun `root node can have a custom name`() { - val name = "user" - assertEquals(name, ActorPath.Root(name).name) - } - - /** - * Test whether a child node can be created on a root. - */ - @Test - fun `child node can be created on a root`() { - val root = ActorPath.Root(name = "/user") - val child = root.child("child") - - assertEquals(root, child.parent) - assertEquals("child", child.name) - } - - /** - * Test whether a child node can be created on a child. - */ - @Test - fun `child node can be created on a child`() { - val root = ActorPath.Root(name = "/user").child("child") - val child = root.child("child") - - assertEquals(root, child.parent) - assertEquals("child", child.name) - } -} diff --git a/odcsim/odcsim-api/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt b/odcsim/odcsim-api/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt deleted file mode 100644 index 1eb4f3b9..00000000 --- a/odcsim/odcsim-api/src/test/kotlin/com/atlarge/odcsim/BehaviorTest.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim - -import com.atlarge.odcsim.internal.BehaviorInterpreter -import com.nhaarman.mockitokotlin2.mock -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -/** - * Test suite for [Behavior] and [BehaviorInterpreter]. - */ -@DisplayName("Behavior") -class BehaviorTest { - /** - * Test whether we cannot start an actor with the [unhandled] behavior. - */ - @Test - fun `should not start with unhandled behavior`() { - val ctx = mock<ActorContext<Unit>>() - val interpreter = BehaviorInterpreter(unhandled<Unit>()) - assertThrows<IllegalArgumentException> { interpreter.start(ctx) } - } - - /** - * Test whether we cannot start an actor with deferred unhandled behavior. - */ - @Test - fun `should not start with deferred unhandled behavior`() { - val ctx = mock<ActorContext<Unit>>() - val interpreter = BehaviorInterpreter(setup<Unit> { unhandled() }) - assertThrows<IllegalArgumentException> { interpreter.start(ctx) } - } - - /** - * Test whether deferred behavior that returns [same] fails. - */ - @Test - fun `should not allow setup to return same`() { - val ctx = mock<ActorContext<Unit>>() - val interpreter = BehaviorInterpreter(setup<Unit> { same() }) - assertThrows<IllegalArgumentException> { interpreter.start(ctx) } - } - - /** - * Test whether deferred behavior that returns [unhandled] fails. - */ - @Test - fun `should not allow setup to return unhandled`() { - val ctx = mock<ActorContext<Unit>>() - val interpreter = BehaviorInterpreter(setup<Unit> { unhandled() }) - assertThrows<IllegalArgumentException> { interpreter.start(ctx) } - } -} diff --git a/odcsim/odcsim-api/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt b/odcsim/odcsim-api/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt deleted file mode 100644 index b59c5ea7..00000000 --- a/odcsim/odcsim-api/src/test/kotlin/com/atlarge/odcsim/CoroutinesTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim - -import com.atlarge.odcsim.coroutines.SuspendingBehavior -import com.atlarge.odcsim.coroutines.suspending -import com.atlarge.odcsim.internal.BehaviorInterpreter -import com.atlarge.odcsim.internal.EmptyBehavior -import com.nhaarman.mockitokotlin2.mock -import kotlin.coroutines.suspendCoroutine -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test - -/** - * Test suite for [SuspendingBehavior] using Kotlin Coroutines. - */ -@DisplayName("Coroutines") -internal class CoroutinesTest { - - @Test - fun `should immediately return new behavior`() { - val ctx = mock<ActorContext<Nothing>>() - val behavior = suspending<Nothing> { empty() } - val interpreter = BehaviorInterpreter(behavior) - interpreter.start(ctx) - assertTrue(interpreter.behavior as Behavior<*> is EmptyBehavior) - } - - @Test - fun `should be able to invoke regular suspend methods`() { - val ctx = mock<ActorContext<Unit>>() - val behavior = suspending<Unit> { - suspendCoroutine<Unit> {} - stopped() - } - val interpreter = BehaviorInterpreter(behavior) - interpreter.start(ctx) - interpreter.interpretMessage(ctx, Unit) - } -} diff --git a/odcsim/odcsim-engine-omega/build.gradle.kts b/odcsim/odcsim-engine-omega/build.gradle.kts deleted file mode 100644 index fb08a981..00000000 --- a/odcsim/odcsim-engine-omega/build.gradle.kts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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. - */ - -description = "Single-threaded reference implementation for the odcsim API" - -/* Build configuration */ -plugins { - `kotlin-library-convention` -} - -/* Project configuration */ -repositories { - jcenter() -} - -dependencies { - api(project(":odcsim:odcsim-api")) - - implementation(kotlin("stdlib")) - implementation("org.jetbrains:annotations:17.0.0") - - testImplementation(project(":odcsim:odcsim-engine-tests")) - testImplementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Library.JUNIT_JUPITER}") - testImplementation("org.junit.platform:junit-platform-launcher:${Library.JUNIT_PLATFORM}") - testRuntimeOnly("org.slf4j:slf4j-simple:${Library.SLF4J}") -} diff --git a/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt b/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt deleted file mode 100644 index 0cb0ed8d..00000000 --- a/odcsim/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt +++ /dev/null @@ -1,360 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim.engine.omega - -import com.atlarge.odcsim.ActorContext -import com.atlarge.odcsim.ActorPath -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.ActorSystem -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.Envelope -import com.atlarge.odcsim.Instant -import com.atlarge.odcsim.PostStop -import com.atlarge.odcsim.PreStart -import com.atlarge.odcsim.Signal -import com.atlarge.odcsim.Terminated -import com.atlarge.odcsim.empty -import com.atlarge.odcsim.internal.BehaviorInterpreter -import com.atlarge.odcsim.internal.logging.LoggerImpl -import java.util.Collections -import java.util.PriorityQueue -import java.util.UUID -import java.util.WeakHashMap -import kotlin.math.max -import org.jetbrains.annotations.Async -import org.slf4j.Logger - -/** - * The reference implementation of the [ActorSystem] instance for the OpenDC simulation core. - * - * This engine implementation is a single-threaded implementation, running actors synchronously and - * provides a single priority queue for all events (messages, ticks, etc) that occur. - * - * @param guardianBehavior The behavior of the guardian (root) actor. - * @param name The name of the engine instance. - */ -class OmegaActorSystem<in T : Any>(guardianBehavior: Behavior<T>, override val name: String) : ActorSystem<T>, ActorRef<T> { - /** - * The state of the actor system. - */ - private var state: ActorSystemState = ActorSystemState.CREATED - - /** - * The event queue to process - */ - private val queue: PriorityQueue<EnvelopeImpl> = PriorityQueue( - Comparator - .comparingDouble(EnvelopeImpl::time) - .thenComparingLong(EnvelopeImpl::id) - ) - - /** - * The registry of actors in the system. - */ - private val registry: MutableMap<ActorPath, Actor<*>> = HashMap() - - /** - * The root actor path of the system. - */ - private val root: ActorPath = ActorPath.Root() - - /** - * The system actor path. - */ - private val system: ActorPath = root / "system" - - /** - * The current point in simulation time. - */ - override var time: Instant = .0 - - /** - * The path to the root actor. - */ - override val path: ActorPath = root / "user" - - init { - registry[system] = Actor(ActorRefImpl(this, system), empty<Nothing>()) - registry[path] = Actor(this, guardianBehavior) - schedule(path, PreStart, .0) - } - - override fun run(until: Duration) { - require(until >= .0) { "The given instant must be a non-negative number" } - - // Start the system/guardian actor on initial run - if (state == ActorSystemState.CREATED) { - state = ActorSystemState.STARTED - registry[system]!!.isolate { it.start() } - registry[path]!!.isolate { it.start() } - } else if (state == ActorSystemState.TERMINATED) { - throw IllegalStateException("The ActorSystem has been terminated.") - } - - while (time < until) { - // Check whether the system was interrupted - if (Thread.interrupted()) { - throw InterruptedException() - } - - val envelope = queue.peek() ?: break - val delivery = envelope.time.takeUnless { it > until } ?: break - - // A message should never be delivered out of order in this single-threaded implementation. Assert for - // sanity - assert(delivery >= time) { "Message delivered out of order [expected=$delivery, actual=$time]" } - - time = delivery - queue.poll() - - processEnvelope(envelope) - } - - // Jump forward in time as the caller expects the system to have run until the specified instant - // Taking the maximum value prevents the caller to jump backwards in time - time = max(time, until) - } - - override fun send(msg: T, after: Duration) = schedule(path, msg, after) - - override fun terminate() { - registry[path]?.stop(null) - registry[system]?.stop(null) - } - - override suspend fun <U : Any> spawnSystem(behavior: Behavior<U>, name: String): ActorRef<U> { - return registry[system]!!.spawn(behavior, name) - } - - override fun compareTo(other: ActorRef<*>): Int = path.compareTo(other.path) - - /** - * The identifier for the next message to be scheduled. - */ - private var nextId: Long = 0 - - /** - * Schedule a message to be processed by the engine. - * - * @param path The path to the destination of the message. - * @param message The message to schedule. - * @param delay The time to wait before processing the message. - */ - private fun schedule(@Async.Schedule path: ActorPath, message: Any, delay: Duration) { - require(delay >= .0) { "The given delay must be a non-negative number" } - scheduleEnvelope(EnvelopeImpl(nextId++, path, time + delay, message)) - } - - /** - * Schedule the specified envelope to be processed by the engine. - */ - private fun scheduleEnvelope(@Async.Schedule envelope: EnvelopeImpl) { - queue.add(envelope) - } - - /** - * Process the delivery of a message. - */ - private fun processEnvelope(@Async.Execute envelope: EnvelopeImpl) { - val actor = registry[envelope.destination] ?: return - - // Notice that messages for unknown/terminated actors are ignored for now - actor.isolate { it.interpretMessage(envelope.message) } - } - - /** - * An actor as represented in the Omega engine. - * - * @param self The [ActorRef] to this actor. - * @param initialBehavior The initial behavior of this actor. - */ - private inner class Actor<T : Any>(override val self: ActorRef<T>, initialBehavior: Behavior<T>) : ActorContext<T> { - val childActors: MutableMap<String, Actor<*>> = mutableMapOf() - val interpreter = BehaviorInterpreter(initialBehavior) - val watchers: MutableSet<ActorPath> = Collections.newSetFromMap(WeakHashMap<ActorPath, Boolean>()) - - override val time: Instant - get() = this@OmegaActorSystem.time - - override val children: List<ActorRef<*>> - get() = childActors.values.map { it.self } - - override val system: ActorSystem<*> - get() = this@OmegaActorSystem - - override val log: Logger by lazy(LazyThreadSafetyMode.NONE) { LoggerImpl(this) } - - override fun getChild(name: String): ActorRef<*>? = childActors[name]?.self - - override fun <U : Any> send(ref: ActorRef<U>, msg: U, after: Duration) = schedule(ref.path, msg, after) - - override fun <U : Any> spawn(behavior: Behavior<U>, name: String): ActorRef<U> { - require(name.isNotEmpty()) { "Actor name may not be empty" } - require(!name.startsWith("$")) { "Actor name may not start with $-sign" } - return internalSpawn(behavior, name) - } - - override fun <U : Any> spawnAnonymous(behavior: Behavior<U>): ActorRef<U> { - val name = "$" + UUID.randomUUID() - return internalSpawn(behavior, name) - } - - private fun <U : Any> internalSpawn(behavior: Behavior<U>, name: String): ActorRef<U> { - require(name !in childActors) { "Actor name $name not unique" } - val ref = ActorRefImpl<U>(this@OmegaActorSystem, self.path.child(name)) - val actor = Actor(ref, behavior) - registry[ref.path] = actor - childActors[name] = actor - schedule(ref.path, PreStart, .0) - actor.start() - return ref - } - - override fun stop(child: ActorRef<*>) { - when { - // Must be a direct child of this actor - child.path.parent == self.path -> { - val ref = childActors[child.path.name] ?: return - ref.stop(null) - } - self == child -> throw IllegalArgumentException( - "Only direct children of an actor may be stopped through the actor context, " + - "but you tried to stop [$self] by passing its ActorRef to the `stop` method. " + - "Stopping self has to be expressed as explicitly returning a Stop Behavior." - ) - else -> throw IllegalArgumentException( - "Only direct children of an actor may be stopped through the actor context, " + - "but [$child] is not a child of [$self]. Stopping other actors has to be expressed as " + - "an explicit stop message that the actor accepts." - ) - } - } - - override fun watch(target: ActorRef<*>) { - registry[target.path]?.watchers?.add(path) - } - - override fun unwatch(target: ActorRef<*>) { - registry[target.path]?.watchers?.remove(path) - } - - // Synchronization of actors in a single-threaded simulation is trivial: all actors are consistent in virtual - // time. - override fun sync(target: ActorRef<*>) {} - - override fun unsync(target: ActorRef<*>) {} - - override fun isSync(target: ActorRef<*>): Boolean = true - - /** - * Start this actor. - */ - fun start() { - interpreter.start(this) - } - - /** - * Stop this actor. - */ - fun stop(failure: Throwable?) { - interpreter.stop(this) - childActors.values.forEach { it.stop(failure) } - registry.remove(self.path) - interpreter.interpretSignal(this, PostStop) - val termination = Terminated(self, failure) - watchers.forEach { schedule(it, termination, 0.0) } - } - - /** - * Interpret the given message send to an actor. - */ - fun interpretMessage(msg: Any) { - if (msg is Signal) { - interpreter.interpretSignal(this, msg) - } else { - @Suppress("UNCHECKED_CAST") - interpreter.interpretMessage(this, msg as T) - } - - if (!interpreter.isAlive) { - stop(null) - } - } - - override fun equals(other: Any?): Boolean = - other is OmegaActorSystem<*>.Actor<*> && self.path == other.self.path - - override fun hashCode(): Int = self.path.hashCode() - } - - /** - * Isolate uncaught exceptions originating from actor interpreter invocations. - */ - private inline fun <T : Any, U> Actor<T>.isolate(block: (Actor<T>) -> U): U? { - return try { - block(this) - } catch (t: Throwable) { - // Forcefully stop the actor if it crashed - stop(t) - log.error("Unhandled exception in actor $path", t) - null - } - } - - /** - * Enumeration to track the state of the actor system. - */ - private enum class ActorSystemState { - CREATED, STARTED, TERMINATED - } - - /** - * Internal [ActorRef] implementation for this actor system. - */ - private data class ActorRefImpl<T : Any>( - private val owner: OmegaActorSystem<*>, - override val path: ActorPath - ) : ActorRef<T> { - override fun toString(): String = "Actor[$path]" - - override fun compareTo(other: ActorRef<*>): Int = path.compareTo(other.path) - } - - /** - * A wrapper around a message that has been scheduled for processing. - * - * @property id The identifier of the message to keep the priority queue stable. - * @property destination The destination of the message. - * @property time The point in time to deliver the message. - * @property message The message to wrap. - */ - private class EnvelopeImpl( - val id: Long, - val destination: ActorPath, - override val time: Instant, - override val message: Any - ) : Envelope<Any> -} diff --git a/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory b/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory deleted file mode 100644 index d0ca8859..00000000 --- a/odcsim/odcsim-engine-omega/src/main/resources/META-INF/services/com.atlarge.odcsim.ActorSystemFactory +++ /dev/null @@ -1 +0,0 @@ -com.atlarge.odcsim.engine.omega.OmegaActorSystemFactory diff --git a/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt b/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt deleted file mode 100644 index 4e195e6e..00000000 --- a/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemFactoryTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim.engine.omega - -import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.engine.tests.ActorSystemFactoryContract -import org.junit.jupiter.api.DisplayName - -/** - * The [ActorSystemFactory] test suite for the Omega engine implementation. - */ -@DisplayName("OmegaActorSystemFactory") -class OmegaActorSystemFactoryTest : ActorSystemFactoryContract() { - override fun createFactory(): ActorSystemFactory = OmegaActorSystemFactory() -} diff --git a/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt b/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt deleted file mode 100644 index dc310d47..00000000 --- a/odcsim/odcsim-engine-omega/src/test/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystemTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018 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.odcsim.engine.omega - -import com.atlarge.odcsim.ActorSystem -import com.atlarge.odcsim.engine.tests.ActorSystemContract -import org.junit.jupiter.api.DisplayName - -/** - * The [ActorSystem] test suite for the Omega engine implementation. - */ -@DisplayName("OmegaActorSystem") -class OmegaActorSystemTest : ActorSystemContract() { - override val factory = OmegaActorSystemFactory() -} diff --git a/odcsim/odcsim-engine-tests/build.gradle.kts b/odcsim/odcsim-engine-tests/build.gradle.kts deleted file mode 100644 index e68070cc..00000000 --- a/odcsim/odcsim-engine-tests/build.gradle.kts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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. - */ - -description = "Conformance suite for implementors of the odcsim API" - -/* Build configuration */ -plugins { - `kotlin-library-convention` -} - -/* Project configuration */ -repositories { - jcenter() -} - -dependencies { - api(project(":odcsim:odcsim-api")) - - implementation(kotlin("stdlib")) - implementation("org.junit.jupiter:junit-jupiter-api:${Library.JUNIT_JUPITER}") -} diff --git a/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt b/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt deleted file mode 100644 index 5e735e68..00000000 --- a/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemContract.kt +++ /dev/null @@ -1,391 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim.engine.tests - -import com.atlarge.odcsim.ActorRef -import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.Behavior -import com.atlarge.odcsim.Terminated -import com.atlarge.odcsim.coroutines.dsl.timeout -import com.atlarge.odcsim.coroutines.suspending -import com.atlarge.odcsim.empty -import com.atlarge.odcsim.ignore -import com.atlarge.odcsim.receiveMessage -import com.atlarge.odcsim.receiveSignal -import com.atlarge.odcsim.same -import com.atlarge.odcsim.setup -import com.atlarge.odcsim.stopped -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows - -/** - * A conformance test suite for implementors of the [ActorSystem] interface. - */ -abstract class ActorSystemContract { - /** - * An [ActorSystemFactory] provided by implementors to create the [ActorSystem] to be tested. - */ - abstract val factory: ActorSystemFactory - - /** - * Test whether the created [ActorSystem] has the correct name. - */ - @Test - fun `should have a name`() { - val name = "test" - val system = factory(empty<Unit>(), name) - - assertEquals(name, system.name) - system.terminate() - } - - /** - * Test whether creating an [ActorSystem] sets the initial time at 0. - */ - @Test - fun `should start at t=0`() { - val system = factory(empty<Unit>(), name = "test") - - assertEquals(.0, system.time, DELTA) - system.terminate() - } - - /** - * Test whether an [ActorSystem] does not accept invalid points in time. - */ - @Test - fun `should not accept negative instants for running`() { - val system = factory(empty<Unit>(), name = "test") - assertThrows<IllegalArgumentException> { system.run(-10.0) } - system.terminate() - } - - /** - * Test whether an [ActorSystem] will not jump backward in time when asking to run until a specified instant - * that has already occurred. - */ - @Test - fun `should not jump backward in time`() { - val until = 10.0 - val system = factory(empty<Unit>(), name = "test") - - system.run(until = until) - system.run(until = until - 0.5) - assertEquals(until, system.time, DELTA) - system.terminate() - } - - /** - * Test whether an [ActorSystem] will jump forward in time when asking to run until a specified instant. - */ - @Test - fun `should jump forward in time`() { - val until = 10.0 - val system = factory(empty<Unit>(), name = "test") - - system.run(until = until) - assertEquals(until, system.time, DELTA) - system.terminate() - } - - /** - * Test whether an [ActorSystem] will jump forward in time when asking to run until a specified instant. - */ - @Test - fun `should order messages at the instant by insertion time`() { - val behavior = receiveMessage<Int> { msg -> - assertEquals(1, msg) - receiveMessage { - assertEquals(2, it) - ignore() - } - } - val system = factory(behavior, name = "test") - system.send(1, after = 1.0) - system.send(2, after = 1.0) - system.run(until = 10.0) - system.terminate() - } - - /** - * Test whether an [ActorSystem] will not process messages in the queue after the deadline. - */ - @Test - fun `should not process messages after deadline`() { - var counter = 0 - val behavior = receiveMessage<Unit> { _ -> - counter++ - same() - } - val system = factory(behavior, name = "test") - system.send(Unit, after = 3.0) - system.send(Unit, after = 1.0) - system.run(until = 2.0) - assertEquals(1, counter) - system.terminate() - } - - /** - * Test whether an [ActorSystem] will not initialize the root actor if the system has not been run yet. - */ - @Test - fun `should not initialize root actor if not run`() { - val system = factory(setup<Unit> { TODO() }, name = "test") - system.terminate() - } - - @Nested - @DisplayName("ActorRef") - inner class ActorRefTest { - /** - * Test whether an [ActorSystem] disallows sending messages in the past. - */ - @Test - fun `should disallow messages in the past`() { - val system = factory(empty<Unit>(), name = "test") - assertThrows<IllegalArgumentException> { system.send(Unit, after = -1.0) } - system.terminate() - } - } - - @Nested - @DisplayName("Actor") - inner class Actor { - /** - * Test whether the pre-start time of the root actor is at 0. - */ - @Test - fun `should pre-start at t=0 if root`() { - val behavior = setup<Unit> { ctx -> - assertEquals(.0, ctx.time, DELTA) - ignore() - } - - val system = factory(behavior, "test") - system.run() - system.terminate() - } - - /** - * Test whether a child actor can be created from an actor. - */ - @Test - fun `should allow spawning of child actors`() { - var spawned = false - val behavior = setup<Unit> { spawned = true; empty() } - - val system = factory(setup<Unit> { ctx -> - val ref = ctx.spawn(behavior, "child") - assertEquals("child", ref.path.name) - ignore() - }, name = "test") - - system.run(until = 10.0) - assertTrue(spawned) - system.terminate() - } - - /** - * Test whether a child actor can be stopped from an actor. - */ - @Test - fun `should allow stopping of child actors`() { - val system = factory(setup<Unit> { ctx -> - val ref = ctx.spawn(receiveMessage<Unit> { throw UnsupportedOperationException() }, "child") - ctx.stop(ref) - assertEquals("child", ref.path.name) - ignore() - }, name = "test") - - system.run(until = 10.0) - system.terminate() - } - - /** - * Test whether only the parent of a child can terminate it. - */ - @Test - fun `should only be able to terminate child actors`() { - val system = factory(setup<Unit> { ctx1 -> - val child1 = ctx1.spawn(ignore<Unit>(), "child-1") - ctx1.spawn(setup<Unit> { ctx2 -> - ctx2.stop(child1) - ignore() - }, "child-2") - - ignore() - }, name = "test") - system.run() - system.terminate() - } - - /** - * Test whether stopping a child is idempotent. - */ - @Test - fun `should be able to stop a child twice`() { - val system = factory(setup<Unit> { ctx -> - val child = ctx.spawn(ignore<Unit>(), "child") - ctx.stop(child) - ctx.stop(child) - ignore() - }, name = "test") - system.run() - system.terminate() - } - - /** - * Test whether termination of a child also results in termination of its children. - */ - @Test - fun `should terminate children of child when terminating it`() { - val system = factory(setup<ActorRef<Unit>> { ctx -> - val root = ctx.self - val child = ctx.spawn(setup<Unit> { - val child = it.spawn(receiveMessage<Unit> { - throw IllegalStateException("DELIBERATE") - }, "child") - ctx.send(root, child) - ignore() - }, "child") - - receiveMessage { msg -> - ctx.stop(child) - ctx.send(msg, Unit) // This actor should be stopped now and not receive the message anymore - stopped() - } - }, name = "test") - - system.run() - system.terminate() - } - - /** - * Test whether [same] works correctly. - */ - @Test - fun `should keep same behavior on same`() { - var counter = 0 - - val behavior = setup<Unit> { ctx -> - counter++ - ctx.send(ctx.self, Unit) - receiveMessage { - counter++ - same() - } - } - - val system = factory(behavior, "test") - system.run() - assertEquals(2, counter) - system.terminate() - } - - /** - * Test whether the reference to the actor itself is valid. - */ - @Test - fun `should have reference to itself`() { - var flag = false - val behavior: Behavior<Unit> = setup { ctx -> - ctx.send(ctx.self, Unit) - receiveMessage { flag = true; same() } - } - - val system = factory(behavior, "test") - system.run() - assertTrue(flag) - system.terminate() - } - - /** - * Test whether we can start an actor with the [stopped] behavior. - */ - @Test - fun `should start with stopped behavior`() { - val system = factory(stopped<Unit>(), "test") - system.run() - system.terminate() - } - - /** - * Test whether an actor that is crashed cannot receive more messages. - */ - @Test - fun `should stop if it crashes`() { - var counter = 0 - val system = factory(receiveMessage<Unit> { - counter++ - throw IllegalArgumentException("STAGED") - }, "test") - - system.send(Unit) - system.send(Unit) - - system.run() - assertEquals(1, counter) - system.terminate() - } - - /** - * Test whether an actor can watch for termination. - */ - @Test - fun `should watch for termination`() { - var received = false - val system = factory(setup<Nothing> { ctx -> - val child = ctx.spawn(suspending<Nothing> { - timeout(50.0) - stopped() - }, "child") - ctx.watch(child) - - receiveSignal { _, signal -> - when (signal) { - is Terminated -> { - received = true - stopped() - } - else -> - same() - } - } - }, "test") - - system.run() - system.terminate() - assertTrue(received) - } - } - - companion object { - private const val DELTA: Double = 0.0001 - } -} diff --git a/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt b/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt deleted file mode 100644 index 565f4f4c..00000000 --- a/odcsim/odcsim-engine-tests/src/main/kotlin/com/atlarge/odcsim/engine/tests/ActorSystemFactoryContract.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 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.odcsim.engine.tests - -import com.atlarge.odcsim.ActorSystemFactory -import com.atlarge.odcsim.empty -import com.atlarge.odcsim.setup -import com.atlarge.odcsim.stopped -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Test - -/** - * A conformance test suite for implementors of the [ActorSystemFactory] interface. - */ -abstract class ActorSystemFactoryContract { - /** - * Create an [ActorSystemFactory] instance to test. - */ - abstract fun createFactory(): ActorSystemFactory - - /** - * Test whether the factory will create an [ActorSystem] with correct name. - */ - @Test - fun `should create a system with correct name`() { - val factory = createFactory() - val name = "test" - val system = factory(empty<Unit>(), name) - - assertEquals(name, system.name) - system.terminate() - } - - /** - * Test whether the factory will create an [ActorSystem] with valid root behavior. - */ - @Test - fun `should create a system with correct root behavior`() { - var flag = false - val factory = createFactory() - val system = factory(setup<Unit> { - flag = true - stopped() - }, "test") - - system.run(until = 10.0) - system.terminate() - assertTrue(flag) - } -} |
