From 2bc0e9834a81738e3af24886d8daf7af490bcc59 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Tue, 30 Oct 2018 23:53:58 +0100 Subject: refactor: Add support for deferred and special behavior This change adds support for deferred and special behavior in the Behavior API. Additionally, the new Behavior DSL is implemented using the new primitives to make the tests cleaner. --- .../odcsim/engine/omega/OmegaActorSystem.kt | 68 ++++++++++++++++++---- 1 file changed, 56 insertions(+), 12 deletions(-) (limited to 'odcsim-engine-omega/src') diff --git a/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt b/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt index 3eaddf51..3da82b3d 100644 --- a/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt +++ b/odcsim-engine-omega/src/main/kotlin/com/atlarge/odcsim/engine/omega/OmegaActorSystem.kt @@ -29,6 +29,7 @@ import com.atlarge.odcsim.ActorPath import com.atlarge.odcsim.ActorRef import com.atlarge.odcsim.ActorSystem import com.atlarge.odcsim.Behavior +import com.atlarge.odcsim.BehaviorInterpreter import com.atlarge.odcsim.Duration import com.atlarge.odcsim.Instant import com.atlarge.odcsim.PostStop @@ -57,6 +58,11 @@ class OmegaActorSystem(root: Behavior, override val name: String) */ override val path: ActorPath = ActorPath.Root(name = "/user") + /** + * A flag to indicate the system has started. + */ + private var isStarted: Boolean = false + /** * The event queue to process */ @@ -69,10 +75,21 @@ class OmegaActorSystem(root: Behavior, override val name: String) */ private val registry: MutableMap> = HashMap() + init { + registry[path] = Actor(this, root) + schedule(this, PreStart, .0) + } + override fun run(until: Duration) { require(until >= .0) { "The given instant must be a non-negative number" } - while (true) { + // Start the root actor on initial run + if (!isStarted) { + registry[path]!!.start() + isStarted = true + } + + while (time < until) { val envelope = queue.peek() ?: break val delivery = envelope.time.takeUnless { it > until } ?: break @@ -99,13 +116,15 @@ class OmegaActorSystem(root: Behavior, override val name: String) */ private var nextId: Long = 0 - init { - registry[path] = Actor(this, root) - schedule(this, PreStart, .0) - } - - private inner class Actor(override val self: ActorRef, var behavior: Behavior) : ActorContext { + /** + * An actor as represented in the Omega engine. + * + * @param self The [ActorRef] to this actor. + * @param initialBehavior The initial behavior of this actor. + */ + private inner class Actor(override val self: ActorRef, initialBehavior: Behavior) : ActorContext { val children: MutableSet> = mutableSetOf() + val interpreter = BehaviorInterpreter(initialBehavior) override val time: Instant get() = this@OmegaActorSystem.time @@ -117,6 +136,7 @@ class OmegaActorSystem(root: Behavior, override val name: String) registry[ref.path] = actor children += actor schedule(ref, PreStart, .0) + actor.start() } return ref } @@ -127,25 +147,49 @@ class OmegaActorSystem(root: Behavior, override val name: String) return false } val ref = registry[child.path] ?: return false - ref.terminate() + ref.stop() return true } + /** + * Start this actor. + */ + fun start() { + interpreter.start(this) + } + + /** + * Stop this actor. + */ + fun stop() { + interpreter.stop(this) + terminate() + interpreter.interpretSignal(this, PostStop) + } + /** * Terminate this actor and its children. */ fun terminate() { children.forEach { it.terminate() } registry.remove(self.path) - interpretMessage(PostStop) } /** - * Interpret the given message send to an actor. Make sure the message is of the correct type. + * Interpret the given message send to an actor. */ fun interpretMessage(msg: Any) { - @Suppress("UNCHECKED_CAST") - behavior = if (msg is Signal) behavior.receiveSignal(this, msg) else behavior.receive(this, msg as T) + if (msg is Signal) { + interpreter.interpretSignal(this, msg) + } else { + @Suppress("UNCHECKED_CAST") + interpreter.interpretMessage(this, msg as T) + } + + if (!interpreter.isAlive) { + terminate() + interpreter.interpretSignal(this, PostStop) + } } override fun equals(other: Any?): Boolean = -- cgit v1.2.3