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
This commit is contained in:
Roland 2012-08-21 13:43:57 +02:00
parent f7a39d50f4
commit f0c370cf87
4 changed files with 12 additions and 2 deletions

View file

@ -5,16 +5,15 @@
package akka.actor package akka.actor
import java.io.{ ObjectOutputStream, NotSerializableException } import java.io.{ ObjectOutputStream, NotSerializableException }
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.collection.immutable.TreeSet import scala.collection.immutable.TreeSet
import scala.concurrent.util.Duration import scala.concurrent.util.Duration
import scala.util.control.NonFatal import scala.util.control.NonFatal
import akka.actor.cell.ChildrenContainer import akka.actor.cell.ChildrenContainer
import akka.dispatch.{ Watch, Unwatch, Terminate, SystemMessage, Suspend, Supervise, Resume, Recreate, NoMessage, MessageDispatcher, Envelope, Create, ChildTerminated } 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.event.Logging.{ LogEvent, Debug, Error }
import akka.japi.Procedure import akka.japi.Procedure
import akka.dispatch.NullMessage
/** /**
* The actor context - the view of the actor cell from the actor. * 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)) publish(Debug(self.path.toString, clazz(actor), "received AutoReceiveMessage " + msg))
msg.message match { msg.message match {
case NullMessage // do nothing
case Failed(cause, uid) handleFailure(sender, cause, uid) case Failed(cause, uid) handleFailure(sender, cause, uid)
case t @ Terminated(actor) case t @ Terminated(actor)
getChildByRef(actor) match { getChildByRef(actor) match {

View file

@ -360,6 +360,7 @@ class LocalActorRefProvider(
override def !(message: Any)(implicit sender: ActorRef = null): Unit = stopped.ifOff(message match { 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 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 + "]") case _ log.error(this + " received unexpected message [" + message + "]")
}) })

View file

@ -10,6 +10,7 @@ import akka.dispatch.{ Terminate, SystemMessage, Suspend, Resume, Recreate, Mess
import akka.event.Logging.Error import akka.event.Logging.Error
import akka.util.Unsafe import akka.util.Unsafe
import scala.util.control.NonFatal import scala.util.control.NonFatal
import akka.dispatch.NullMessage
private[akka] trait Dispatch { this: ActorCell private[akka] trait Dispatch { this: ActorCell
@ -56,6 +57,7 @@ private[akka] trait Dispatch { this: ActorCell ⇒
if (sendSupervise) { if (sendSupervise) {
// ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅ // ➡➡➡ NEVER SEND THE SAME SYSTEM MESSAGE OBJECT TO TWO ACTORS ⬅⬅⬅
parent.sendSystemMessage(akka.dispatch.Supervise(self, uid)) parent.sendSystemMessage(akka.dispatch.Supervise(self, uid))
parent.tell(NullMessage)
} }
// This call is expected to start off the actor by scheduling its mailbox. // This call is expected to start off the actor by scheduling its mailbox.

View file

@ -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 * INTERNAL API
*/ */