From ec89945d499e6aa1e686f2d6b6a9e13a67e02bb0 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Sun, 3 Mar 2019 21:52:23 +0100 Subject: feat: Expose actor context in coroutine This change makes a `SuspendingBehavior` instance expose its `SuspendingActorContext` in the coroutine context, so suspending methods inside the coroutine can access the actor context. --- .../com/atlarge/odcsim/coroutines/Behavior.kt | 13 +++++++++--- .../com/atlarge/odcsim/internal/Coroutines.kt | 23 +++++----------------- 2 files changed, 15 insertions(+), 21 deletions(-) (limited to 'odcsim-core/src/main') diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt index 4951d619..20aab232 100644 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt +++ b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/coroutines/Behavior.kt @@ -28,9 +28,10 @@ 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.SuspendingBehaviorContext +import com.atlarge.odcsim.internal.SuspendingActorContextImpl import com.atlarge.odcsim.internal.SuspendingBehaviorImpl import kotlin.coroutines.Continuation +import kotlin.coroutines.CoroutineContext import kotlin.coroutines.suspendCoroutine /** @@ -65,7 +66,7 @@ fun Behavior.Companion.suspending(block: suspend (SuspendingActorConte * * @param T The shape of the messages the actor accepts. */ -interface SuspendingActorContext : ActorContext { +interface SuspendingActorContext : ActorContext, 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. @@ -81,6 +82,12 @@ interface SuspendingActorContext : ActorContext { * @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> } /** @@ -91,7 +98,7 @@ interface SuspendingActorContext : ActorContext { suspend fun suspendWithBehavior(block: (Continuation, () -> Behavior) -> Behavior): U = suspendCoroutine { cont -> @Suppress("UNCHECKED_CAST") - val ctx = cont.context[SuspendingBehaviorContext] as? SuspendingBehaviorContext + val ctx = cont.context[SuspendingActorContext] as? SuspendingActorContextImpl ?: throw UnsupportedOperationException("Coroutine does not run inside SuspendingBehavior") ctx.become(block(cont) { ctx.behavior }) } diff --git a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt index d8c3e8fc..7f2d39bc 100644 --- a/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt +++ b/odcsim-core/src/main/kotlin/com/atlarge/odcsim/internal/Coroutines.kt @@ -41,15 +41,10 @@ import kotlin.coroutines.resume import kotlin.coroutines.startCoroutine /** - * The interface that is exposed from the [CoroutineContext] and provides control over the [SuspendingBehavior] - * instance. + * This interface exposes internal functionality provided by [SuspendingBehaviorImpl] on [SuspendingActorContext] to + * control the active behavior of the coroutine. */ -interface SuspendingBehaviorContext : CoroutineContext.Element { - /** - * The [ActorContext] in which the actor currently runs. - */ - val context: SuspendingActorContext - +interface SuspendingActorContextImpl : SuspendingActorContext { /** * The current active behavior */ @@ -61,12 +56,6 @@ interface SuspendingBehaviorContext : CoroutineContext.Element { * @param next The behavior to replace the current behavior with. */ fun become(next: Behavior) - - /** - * This key provides users access to an untyped actor context in case the coroutine runs inside a - * [SuspendingBehavior]. - */ - companion object Key : CoroutineContext.Key> } /** @@ -75,7 +64,7 @@ interface SuspendingBehaviorContext : CoroutineContext.Element { * This implementation uses the fact that each actor is thread-safe (as it processes its mailbox sequentially). */ internal class SuspendingBehaviorImpl(actorContext: ActorContext, initialBehavior: SuspendingBehavior) : - ReceivingBehavior(), SuspendingActorContext, SuspendingBehaviorContext { + ReceivingBehavior(), SuspendingActorContextImpl { /** * The next behavior to use. */ @@ -128,15 +117,13 @@ internal class SuspendingBehaviorImpl(actorContext: ActorContext, in } } - override val context: SuspendingActorContext get() = this - override val behavior: Behavior get() = interpreter.behavior override fun become(next: Behavior) { interpreter.become(actorContext, next) } - override val key: CoroutineContext.Key<*> = SuspendingBehaviorContext.Key + override val key: CoroutineContext.Key<*> = SuspendingActorContext.Key /** * Start the suspending behavior. -- cgit v1.2.3