Permalink
Browse files

Merge pull request #644 from akka/wip-2418-Supervise-vs-Failed-∂π

add NullMessage after Supervise to prevent race, see #2418
  • Loading branch information...
rkuhn committed Aug 21, 2012
2 parents c38f473 + 2089fcf commit 6389e1097bf2e219c221ec4030ff00b6daaa0bf7
@@ -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.
@@ -371,7 +370,7 @@ private[akka] class ActorCell(
checkReceiveTimeout // Reschedule receive timeout
}
- def autoReceiveMessage(msg: Envelope): Unit = {
+ def autoReceiveMessage(msg: Envelope): Unit = if (msg.message != NullMessage) {
if (system.settings.DebugAutoReceive)
publish(Debug(self.path.toString, clazz(actor), "received AutoReceiveMessage " + msg))
@@ -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 + "]")
})
@@ -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) // read ScalaDoc of NullMessage to see why
}
// This call is expected to start off the actor by scheduling its mailbox.
@@ -37,6 +37,21 @@ 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.
+ *
+ * Detailed explanation:
+ *
+ * The race happens because Supervise and Failed may be queued between the
+ * parent's check for system messages and dequeue(). Thus, if the parent
+ * processes the NullMessage first (by way of that tiny race window), it is
+ * guaranteed to then find the Supervise system message in its mailbox prior
+ * to turning its attention to the next real message.
+ */
+case object NullMessage extends AutoReceivedMessage
+
/**
* INTERNAL API
*/

0 comments on commit 6389e10

Please sign in to comment.