From f0c370cf873a5de1bcc378e14fa31fb39751f10c Mon Sep 17 00:00:00 2001 From: Roland Date: Tue, 21 Aug 2012 13:43:57 +0200 Subject: [PATCH] add NullMessage after Supervise to prevent race, see #2418 - parent just checked for system messages and finds none - Supervise is enqueued to parent - Failed is enqueued to the parent - Failed is processed before Supervise and thus dropped due to non-matching UID remedy is to enqueue NullMessage after enqueuing Supervise but before really starting the actor, so that if NullMessage overtakes Supervise the loop in processMailbox will then pick up Supervise next --- akka-actor/src/main/scala/akka/actor/ActorCell.scala | 4 ++-- .../src/main/scala/akka/actor/ActorRefProvider.scala | 1 + akka-actor/src/main/scala/akka/actor/cell/Dispatch.scala | 2 ++ .../src/main/scala/akka/dispatch/AbstractDispatcher.scala | 7 +++++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/ActorCell.scala b/akka-actor/src/main/scala/akka/actor/ActorCell.scala index 90500cfc88..a07e60ea45 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorCell.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorCell.scala @@ -5,16 +5,15 @@ package akka.actor import java.io.{ ObjectOutputStream, NotSerializableException } - import scala.annotation.tailrec import scala.collection.immutable.TreeSet import scala.concurrent.util.Duration import scala.util.control.NonFatal - import akka.actor.cell.ChildrenContainer import akka.dispatch.{ Watch, Unwatch, Terminate, SystemMessage, Suspend, Supervise, Resume, Recreate, NoMessage, MessageDispatcher, Envelope, Create, ChildTerminated } import akka.event.Logging.{ LogEvent, Debug, Error } import akka.japi.Procedure +import akka.dispatch.NullMessage /** * The actor context - the view of the actor cell from the actor. @@ -365,6 +364,7 @@ private[akka] class ActorCell( publish(Debug(self.path.toString, clazz(actor), "received AutoReceiveMessage " + msg)) msg.message match { + case NullMessage ⇒ // do nothing case Failed(cause, uid) ⇒ handleFailure(sender, cause, uid) case t @ Terminated(actor) ⇒ getChildByRef(actor) match { diff --git a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala index 5c64e5554b..e71782869d 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala @@ -360,6 +360,7 @@ class LocalActorRefProvider( override def !(message: Any)(implicit sender: ActorRef = null): Unit = stopped.ifOff(message match { case Failed(ex, _) if sender ne null ⇒ causeOfTermination = Some(ex); sender.asInstanceOf[InternalActorRef].stop() + case NullMessage ⇒ // do nothing case _ ⇒ log.error(this + " received unexpected message [" + message + "]") }) diff --git a/akka-actor/src/main/scala/akka/actor/cell/Dispatch.scala b/akka-actor/src/main/scala/akka/actor/cell/Dispatch.scala index 794c051eed..5702ee91cb 100644 --- a/akka-actor/src/main/scala/akka/actor/cell/Dispatch.scala +++ b/akka-actor/src/main/scala/akka/actor/cell/Dispatch.scala @@ -10,6 +10,7 @@ import akka.dispatch.{ Terminate, SystemMessage, Suspend, Resume, Recreate, Mess import akka.event.Logging.Error import akka.util.Unsafe import scala.util.control.NonFatal +import akka.dispatch.NullMessage private[akka] trait Dispatch { this: ActorCell ⇒ @@ -56,6 +57,7 @@ private[akka] trait Dispatch { this: ActorCell ⇒ if (sendSupervise) { // ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅ parent.sendSystemMessage(akka.dispatch.Supervise(self, uid)) + parent.tell(NullMessage) } // This call is expected to start off the actor by scheduling its mailbox. diff --git a/akka-actor/src/main/scala/akka/dispatch/AbstractDispatcher.scala b/akka-actor/src/main/scala/akka/dispatch/AbstractDispatcher.scala index 3792748b02..45aac0279f 100644 --- a/akka-actor/src/main/scala/akka/dispatch/AbstractDispatcher.scala +++ b/akka-actor/src/main/scala/akka/dispatch/AbstractDispatcher.scala @@ -37,6 +37,13 @@ object Envelope { } } +/** + * This message is sent directly after the Supervise system message in order + * to form a barrier wrt. the first real message sent by the child, so that e.g. + * Failed() cannot overtake Supervise(). Processing this does nothing. + */ +case object NullMessage extends AutoReceivedMessage + /** * INTERNAL API */