diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2020-01-19 21:02:22 +0100 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2020-01-19 21:13:37 +0100 |
| commit | 4d13f702c87bc195d8edbd19c5cd6567ecfd2af4 (patch) | |
| tree | 3262880c18204adce76e3d4a8091d1b24b2b4c9a /odcsim/odcsim-api/src/main | |
| parent | ce952cf3f27c154e06cfa56ca1ad7db9ba3eac7c (diff) | |
refactor: Introduce revised API design for 2.x
This change introduces the revised API design for version 2.0 of the
OpenDC simulator. This version drops built-in support for Java and
instead opts to build on Kotlin coroutines to simplify the API surface.
During development of and experimentation with the previous API for version
2.x, we found that the design based on Akka Typed was too limiting and caused
too much boilerplate for the models we needed to implement. Essential
patterns such as request-response were found to be hard to implement
with only a single mailbox. Moveover, limiting each actor's mailbox to a
single type hindered composition and often resulted in unchecked casts
or the type being changed to `Any`, eliminating the type-safety of the
API.
In this revised API design, a simulation is now represented as the interplay of
logical processes that communicate via multiple message passing channels.
We use Kotlin coroutines to describe the behavior of the processes.
The API has been design from the start to take into account
distributed/parallel simulations by disallowing messages from arbitrary
processes, which was possible in the previous design. Instead, the
'communication graph' is known during runtime as procsses must register
themselves before being able to send/receive messages to/from channels.
We are still figuring out process/channel identity and supervision. Currently,
all logical processes run on a single level, instead of being
hierachical. However, this might change in the future.
Diffstat (limited to 'odcsim/odcsim-api/src/main')
27 files changed, 254 insertions, 3124 deletions
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-api/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.kt b/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/ProcessRef.kt index 16b6f534..833458e4 100644 --- a/odcsim/odcsim-api/src/main/kotlin/com/atlarge/odcsim/coroutines/dsl/Timeout.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,21 +22,24 @@ * SOFTWARE. */ -package com.atlarge.odcsim.coroutines.dsl +package com.atlarge.odcsim -import com.atlarge.odcsim.Duration -import com.atlarge.odcsim.coroutines.suspendWithBehavior -import com.atlarge.odcsim.withTimeout -import kotlin.coroutines.resume +import java.io.Serializable /** - * Block execution for the specified duration. - * - * @param after The duration after which execution should continue. + * A reference to a logical process in simulation. */ -suspend fun timeout(after: Duration) = suspendWithBehavior<Any, Unit> { cont, next -> - withTimeout(after) { - cont.resume(Unit) - next() - } +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/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) - } - } - } -} |
