diff --git a/akka-actor/src/main/scala/Errtest.scala b/akka-actor/src/main/scala/Errtest.scala new file mode 100644 index 0000000000..c778e17ebe --- /dev/null +++ b/akka-actor/src/main/scala/Errtest.scala @@ -0,0 +1,49 @@ + +import akka.actor._ +import com.typesafe.config.ConfigFactory +import akka.event.LoggingReceive +import scala.annotation.tailrec + +object Errtest extends App { + + val config = ConfigFactory.parseString(""" + akka.loglevel = DEBUG + akka.actor.debug { + receive = on + lifecycle = on + } + """) + + val sys = ActorSystem("ErrSys", config) + val top = sys.actorOf(Props[Top], name = "top") + + for (n ← 1 to 100) { + top ! "run " + n + Thread.sleep(1000) + } +} + +class Top extends Actor { + var c: ActorRef = _ + def receive = LoggingReceive { + case x ⇒ + c = context.actorOf(Props[Child]); + c ! "ok" + } +} + +class Child extends Actor { + + //throw new Error("Simulated ERR") + blowUp(0) + + //not @tailrec + private final def blowUp(n: Long): Long = { + blowUp(n + 1) + 1 + } + + def receive = LoggingReceive { + case x ⇒ + //context.system.shutdown(); + } +} \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 94aef4bdef..386989f346 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -7,6 +7,7 @@ package akka.actor import akka.AkkaException import scala.reflect.BeanProperty import scala.util.control.NoStackTrace +import scala.collection.immutable.Stack import java.util.regex.Pattern /** @@ -112,6 +113,8 @@ object Actor { def isDefinedAt(x: Any) = false def apply(x: Any) = throw new UnsupportedOperationException("Empty behavior apply()") } + + private final val emptyBehaviourStack: Stack[Actor.Receive] = Stack.empty } /** @@ -172,7 +175,7 @@ trait Actor { type Receive = Actor.Receive /** - * Stores the context for this actor, including self, sender, and hotswap. + * Stores the context for this actor, including self, and sender. * It is implicit to support operations such as `forward`. * * [[akka.actor.ActorContext]] is the Scala API. `getContext` returns a @@ -282,7 +285,6 @@ trait Actor { // ========================================= private[akka] final def apply(msg: Any) = { - val behaviorStack = context.asInstanceOf[ActorCell].hotswap msg match { case msg if behaviorStack.nonEmpty && behaviorStack.head.isDefinedAt(msg) ⇒ behaviorStack.head.apply(msg) case msg if behaviorStack.isEmpty && processingBehavior.isDefinedAt(msg) ⇒ processingBehavior.apply(msg) @@ -291,5 +293,18 @@ trait Actor { } private[this] val processingBehavior = receive //ProcessingBehavior is the original behavior + + private[akka] def pushBehavior(behavior: Receive): Unit = { + behaviorStack = behaviorStack.push(behavior) + } + + private[akka] def popBehavior(): Unit = { + val stack = behaviorStack + if (stack.nonEmpty) behaviorStack = stack.pop + } + + private[akka] def clearBehaviorStack(): Unit = { behaviorStack = emptyBehaviourStack } + + private var behaviorStack: Stack[PartialFunction[Any, Unit]] = emptyBehaviourStack } diff --git a/akka-actor/src/main/scala/akka/actor/ActorCell.scala b/akka-actor/src/main/scala/akka/actor/ActorCell.scala index 5aaf4ae8d5..2f8eee04ef 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorCell.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorCell.scala @@ -174,8 +174,7 @@ private[akka] class ActorCell( val self: InternalActorRef, val props: Props, @volatile var parent: InternalActorRef, - /*no member*/ _receiveTimeout: Option[Duration], - var hotswap: Stack[PartialFunction[Any, Unit]]) extends UntypedActorContext { + /*no member*/ _receiveTimeout: Option[Duration]) extends UntypedActorContext { import ActorCell._ @@ -389,7 +388,6 @@ private[akka] class ActorCell( } } actor = freshActor // assign it here so if preStart fails, we can null out the sef-refs next call - hotswap = Props.noHotSwap // Reset the behavior freshActor.postRestart(cause) if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(freshActor), "restarted")) @@ -509,9 +507,9 @@ private[akka] class ActorCell( } } - def become(behavior: Actor.Receive, discardOld: Boolean = true) { + def become(behavior: Actor.Receive, discardOld: Boolean = true): Unit = { if (discardOld) unbecome() - hotswap = hotswap.push(behavior) + actor.pushBehavior(behavior) } /** @@ -527,10 +525,7 @@ private[akka] class ActorCell( become(newReceive, discardOld) } - def unbecome() { - val h = hotswap - if (h.nonEmpty) hotswap = h.pop - } + def unbecome(): Unit = actor.popBehavior() def autoReceiveMessage(msg: Envelope) { if (system.settings.DebugAutoReceive) @@ -547,9 +542,9 @@ private[akka] class ActorCell( } private def doTerminate() { + val a = actor try { try { - val a = actor if (a ne null) a.postStop() } finally { dispatcher.detach(this) @@ -563,7 +558,7 @@ private[akka] class ActorCell( } finally { currentMessage = null clearActorFields() - hotswap = Props.noHotSwap + if (a ne null) a.clearBehaviorStack() } } } diff --git a/akka-actor/src/main/scala/akka/actor/ActorRef.scala b/akka-actor/src/main/scala/akka/actor/ActorRef.scala index fa6c9962e7..1199a35152 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRef.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRef.scala @@ -224,8 +224,7 @@ private[akka] class LocalActorRef private[akka] ( _supervisor: InternalActorRef, val path: ActorPath, val systemService: Boolean = false, - _receiveTimeout: Option[Duration] = None, - _hotswap: Stack[PartialFunction[Any, Unit]] = Props.noHotSwap) + _receiveTimeout: Option[Duration] = None) extends InternalActorRef with LocalRef { /* @@ -238,7 +237,7 @@ private[akka] class LocalActorRef private[akka] ( * us to use purely factory methods for creating LocalActorRefs. */ @volatile - private var actorCell = newActorCell(_system, this, _props, _supervisor, _receiveTimeout, _hotswap) + private var actorCell = newActorCell(_system, this, _props, _supervisor, _receiveTimeout) actorCell.start() protected def newActorCell( @@ -246,9 +245,8 @@ private[akka] class LocalActorRef private[akka] ( ref: InternalActorRef, props: Props, supervisor: InternalActorRef, - receiveTimeout: Option[Duration], - hotswap: Stack[PartialFunction[Any, Unit]]): ActorCell = - new ActorCell(system, ref, props, supervisor, receiveTimeout, hotswap) + receiveTimeout: Option[Duration]): ActorCell = + new ActorCell(system, ref, props, supervisor, receiveTimeout) protected def actorContext: ActorContext = actorCell diff --git a/akka-actor/src/main/scala/akka/actor/Props.scala b/akka-actor/src/main/scala/akka/actor/Props.scala index cd9a62abe7..2362c4c255 100644 --- a/akka-actor/src/main/scala/akka/actor/Props.scala +++ b/akka-actor/src/main/scala/akka/actor/Props.scala @@ -22,7 +22,6 @@ object Props { final val defaultRoutedProps: RouterConfig = NoRouter - final val noHotSwap: Stack[Actor.Receive] = Stack.empty final val empty = new Props(() ⇒ new Actor { def receive = Actor.emptyBehavior }) /** diff --git a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala index 18618a8f0c..3cfbf0ce1b 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestActorRef.scala @@ -41,9 +41,8 @@ class TestActorRef[T <: Actor]( ref: InternalActorRef, props: Props, supervisor: InternalActorRef, - receiveTimeout: Option[Duration], - hotswap: Stack[PartialFunction[Any, Unit]]): ActorCell = - new ActorCell(system, ref, props, supervisor, receiveTimeout, hotswap) { + receiveTimeout: Option[Duration]): ActorCell = + new ActorCell(system, ref, props, supervisor, receiveTimeout) { override def autoReceiveMessage(msg: Envelope) { msg.message match { case InternalGetActor ⇒ sender ! actor