From 124c374a5fa1abedcfa29698ef0cddc9bc7fc7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Mickevi=C4=8Dius?= Date: Fri, 14 Apr 2017 16:31:21 +0300 Subject: [PATCH] #22687 Undefer before terminate --- .../src/main/scala/akka/typed/Behavior.scala | 29 ++++++------------- .../typed/internal/SupervisionMechanics.scala | 6 ++-- .../scala/akka/typed/patterns/Restarter.scala | 8 ++--- 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/akka-typed/src/main/scala/akka/typed/Behavior.scala b/akka-typed/src/main/scala/akka/typed/Behavior.scala index eac5ea2edc..51e0e42a8b 100644 --- a/akka-typed/src/main/scala/akka/typed/Behavior.scala +++ b/akka-typed/src/main/scala/akka/typed/Behavior.scala @@ -155,13 +155,9 @@ object Behavior { } @tailrec - def undefer[T](deferredBehavior: DeferredBehavior[T], ctx: ActorContext[T]): Behavior[T] = { - val behavior = deferredBehavior(ctx) - - behavior match { - case innerDeferred: DeferredBehavior[T] @unchecked ⇒ undefer(innerDeferred, ctx) - case _ ⇒ behavior - } + def undefer[T](behavior: Behavior[T], ctx: ActorContext[T]): Behavior[T] = behavior match { + case innerDeferred: DeferredBehavior[T] @unchecked ⇒ undefer(innerDeferred(ctx), ctx) + case _ ⇒ behavior } /** @@ -169,7 +165,7 @@ object Behavior { * notably the behavior can neither be `Same` nor `Unhandled`. Starting * out with a `Stopped` behavior is allowed, though. */ - def validateAsInitial[T](behavior: Behavior[T]): Behavior[T] = + def validateAsInitial[T](behavior: BehaSo I get updates every day or so. Bleeding edge, but also works whenever I need it, which is quite rare as well. vior[T]): Behavior[T] = behavior match { case SameBehavior | UnhandledBehavior ⇒ throw new IllegalArgumentException(s"cannot use $behavior as initial behavior") @@ -179,12 +175,8 @@ object Behavior { /** * Validate the given behavior as initial, initialize it if it is deferred. */ - def preStart[T](behavior: Behavior[T], ctx: ActorContext[T]): Behavior[T] = { - validateAsInitial(behavior) match { - case d: DeferredBehavior[T] @unchecked ⇒ validateAsInitial(undefer(d, ctx)) - case x ⇒ x - } - } + def preStart[T](behavior: Behavior[T], ctx: ActorContext[T]): Behavior[T] = + validateAsInitial(undefer(behavior, ctx)) /** * Returns true if the given behavior is not stopped. @@ -210,8 +202,8 @@ object Behavior { private def interpret[T](behavior: Behavior[T], ctx: ActorContext[T], msg: Any): Behavior[T] = behavior match { - case SameBehavior | UnhandledBehavior ⇒ throw new IllegalArgumentException(s"cannot execute with $behavior as behavior") - case _: DeferredBehavior[_] ⇒ throw new IllegalArgumentException(s"deferred should not be passed to interpreter") + case SameBehavior | UnhandledBehavior ⇒ throw new IllegalArgumentException(s"cannot execute with [$behavior] as behavior") + case d: DeferredBehavior[_] ⇒ throw new IllegalArgumentException(s"deferred [$d] should not be passed to interpreter") case IgnoreBehavior ⇒ SameBehavior.asInstanceOf[Behavior[T]] case StoppedBehavior ⇒ StoppedBehavior.asInstanceOf[Behavior[T]] case EmptyBehavior ⇒ UnhandledBehavior.asInstanceOf[Behavior[T]] @@ -220,10 +212,7 @@ object Behavior { case signal: Signal ⇒ ext.management(ctx, signal) case msg ⇒ ext.message(ctx, msg.asInstanceOf[T]) } - possiblyDeferredResult match { - case d: DeferredBehavior[T] @unchecked ⇒ undefer(d, ctx) - case notDeferred ⇒ notDeferred - } + undefer(possiblyDeferredResult, ctx) } } diff --git a/akka-typed/src/main/scala/akka/typed/internal/SupervisionMechanics.scala b/akka-typed/src/main/scala/akka/typed/internal/SupervisionMechanics.scala index cd00406647..b5856cfba7 100644 --- a/akka-typed/src/main/scala/akka/typed/internal/SupervisionMechanics.scala +++ b/akka-typed/src/main/scala/akka/typed/internal/SupervisionMechanics.scala @@ -4,9 +4,7 @@ package akka.typed package internal -import scala.annotation.{ switch, tailrec } import scala.util.control.NonFatal -import scala.util.control.Exception.Catcher import akka.event.Logging import akka.typed.Behavior.DeferredBehavior @@ -89,8 +87,10 @@ private[typed] trait SupervisionMechanics[T] { val a = behavior /* * The following order is crucial for things to work properly. Only change this if you're very confident and lucky. + * + * Do not undefer a DeferredBehavior as that may cause creation side-effects, which we do not want on termination. */ - try if (a ne null) Behavior.canonicalize(Behavior.interpretSignal(a, ctx, PostStop), a, ctx) + try if ((a ne null) && !a.isInstanceOf[DeferredBehavior[_]]) Behavior.canonicalize(Behavior.interpretSignal(a, ctx, PostStop), a, ctx) catch { case NonFatal(ex) ⇒ publish(Logging.Error(ex, self.path.toString, clazz(a), "failure during PostStop")) } finally try tellWatchersWeDied() finally try parent.sendSystem(DeathWatchNotification(self, failed)) diff --git a/akka-typed/src/main/scala/akka/typed/patterns/Restarter.scala b/akka-typed/src/main/scala/akka/typed/patterns/Restarter.scala index 5ba8872ac0..6256b65fa1 100644 --- a/akka-typed/src/main/scala/akka/typed/patterns/Restarter.scala +++ b/akka-typed/src/main/scala/akka/typed/patterns/Restarter.scala @@ -32,15 +32,13 @@ final case class Restarter[T, Thr <: Throwable: ClassTag](initialBehavior: Behav else if (b eq behavior) Same else { b match { - case d: DeferredBehavior[_] ⇒ canonical(Behavior.undefer(d.asInstanceOf[DeferredBehavior[T]], ctx), ctx) + case d: DeferredBehavior[_] ⇒ canonical(Behavior.undefer(d, ctx), ctx) case b ⇒ Restarter[T, Thr](initialBehavior, resume)(b) } } - private def preStart(b: Behavior[T], ctx: ActorContext[T]): Behavior[T] = b match { - case d: DeferredBehavior[_] ⇒ Behavior.undefer(d.asInstanceOf[DeferredBehavior[T]], ctx) - case b ⇒ b - } + private def preStart(b: Behavior[T], ctx: ActorContext[T]): Behavior[T] = + Behavior.undefer(b, ctx) override def management(ctx: ActorContext[T], signal: Signal): Behavior[T] = { val startedBehavior = preStart(behavior, ctx)