Skip to content

Instantly share code, notes, and snippets.

@havocp
Created May 29, 2012 04:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save havocp/2822571 to your computer and use it in GitHub Desktop.
Save havocp/2822571 to your computer and use it in GitHub Desktop.
set the 'actor' field in ActorCell mid-actor-construction rather than post-construction
diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala
index f268b8f..2db8956 100644
--- a/akka-actor/src/main/scala/akka/actor/Actor.scala
+++ b/akka-actor/src/main/scala/akka/actor/Actor.scala
@@ -304,6 +304,10 @@ trait Actor {
*/
implicit final val self = context.self //MUST BE A VAL, TRUST ME
+ // this will call back to receive and aroundReceive so do it
+ // only after we set up `context` and `self`
+ context.setInstance(this)
+
/**
* The reference sender Actor of the last received message.
* Is defined if the message was sent from another Actor,
diff --git a/akka-actor/src/main/scala/akka/actor/ActorCell.scala b/akka-actor/src/main/scala/akka/actor/ActorCell.scala
index 52da820..3c17bd5 100644
--- a/akka-actor/src/main/scala/akka/actor/ActorCell.scala
+++ b/akka-actor/src/main/scala/akka/actor/ActorCell.scala
@@ -140,6 +140,11 @@ trait ActorContext extends ActorRefFactory {
*/
final protected def writeObject(o: ObjectOutputStream): Unit =
throw new NotSerializableException("ActorContext is not serializable!")
+
+ /**
+ * Internal to Akka, called on each construction of the actor instance.
+ */
+ private[actor] def setInstance(instance: Actor): Unit
}
/**
@@ -186,8 +191,6 @@ private[akka] object ActorCell {
final val emptyReceiveTimeoutData: (Long, Cancellable) = (-1, emptyCancellable)
- final val behaviorStackPlaceHolder: Stack[Actor.Receive] = Stack.empty.push(Actor.emptyBehavior)
-
sealed trait SuspendReason
case object UserRequest extends SuspendReason
case class Recreation(cause: Throwable) extends SuspendReason
@@ -492,23 +495,24 @@ private[akka] class ActorCell(
case _ ⇒ system.deadLetters
}
+ private[actor] def setInstance(instance: Actor): Unit = {
+ // this code runs at the start of Actor construction, should make
+ // the ActorCell valid so Actor construction can use its context
+ actor = instance
+ behaviorStack = behaviorStack.push(actor.aroundReceive(actor.receive))
+ }
+
//This method is in charge of setting up the contextStack and create a new instance of the Actor
- protected def newActor(): Actor = {
+ protected def createActor(): Unit = {
contextStack.set(contextStack.get.push(this))
try {
- import ActorCell.behaviorStackPlaceHolder
-
- behaviorStack = behaviorStackPlaceHolder
+ behaviorStack = Stack.empty
val instance = props.creator.apply()
+ require(instance eq actor)
if (instance eq null)
throw new ActorInitializationException(self, "Actor instance passed to actorOf can't be 'null'")
- behaviorStack = behaviorStack match {
- case `behaviorStackPlaceHolder` ⇒ Stack.empty.push(instance.receive)
- case newBehaviors ⇒ Stack.empty.push(instance.receive).pushAll(newBehaviors.reverse.drop(1))
- }
- behaviorStack = behaviorStack.map(instance.aroundReceive(_))
instance
} finally {
val stackAfter = contextStack.get
@@ -522,11 +526,10 @@ private[akka] class ActorCell(
def create(): Unit = if (isNormal) {
try {
- val created = newActor()
- actor = created
- created.preStart()
+ createActor()
+ actor.preStart()
checkReceiveTimeout
- if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(created), "started (" + created + ")"))
+ if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(actor), "started (" + actor + ")"))
} catch {
case NonFatal(i: InstantiationException) ⇒
throw new ActorInitializationException(self,
@@ -649,12 +652,7 @@ private[akka] class ActorCell(
def become(behavior: Actor.Receive, discardOld: Boolean = true): Unit = {
if (discardOld) unbecome()
- // actor is null during construction of the actor
- val wrappedBehavior = if (actor ne null)
- actor.aroundReceive(behavior)
- else
- behavior
- behaviorStack = behaviorStack.push(wrappedBehavior)
+ behaviorStack = behaviorStack.push(actor.aroundReceive(behavior))
}
/**
@@ -709,7 +707,7 @@ private[akka] class ActorCell(
if (system.settings.DebugLifecycle)
system.eventStream.publish(Debug(self.path.toString, clazz(actor), "stopped"))
} finally {
- behaviorStack = ActorCell.behaviorStackPlaceHolder
+ behaviorStack = Stack.empty
clearActorFields(a)
actor = null
}
@@ -719,12 +717,16 @@ private[akka] class ActorCell(
private def doRecreate(cause: Throwable, failedActor: Actor): Unit = try {
// after all killed children have terminated, recreate the rest, then go on to start the new instance
actor.supervisorStrategy.handleSupervisorRestarted(cause, self, children)
- val freshActor = newActor()
- actor = freshActor // this must happen before postRestart has a chance to fail
- if (freshActor eq failedActor) setActorFields(freshActor, this, self) // If the creator returns the same instance, we need to restore our nulled out fields.
+ createActor()
+ if (actor eq failedActor) {
+ // If the creator returns the same instance, we need to restore our nulled out fields
+ // and fill in the behavior stack.
+ setActorFields(actor, this, self)
+ setInstance(actor)
+ }
- freshActor.postRestart(cause)
- if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(freshActor), "restarted"))
+ actor.postRestart(cause)
+ if (system.settings.DebugLifecycle) system.eventStream.publish(Debug(self.path.toString, clazz(actor), "restarted"))
dispatcher.resume(this)
} catch {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment