Add empty actor context (just wrapping self). See #1202

This commit is contained in:
Peter Vlugter 2011-09-15 18:00:19 +02:00
parent b96f3d9260
commit 27bb7b4d52
5 changed files with 68 additions and 53 deletions

View file

@ -129,45 +129,45 @@ class ActorRefSpec extends WordSpec with MustMatchers {
})
}
def refStackMustBeEmpty = ActorInstance.refStack.get.headOption must be === None
def contextStackMustBeEmpty = ActorInstance.contextStack.get.headOption must be === None
refStackMustBeEmpty
contextStackMustBeEmpty
intercept[akka.actor.ActorInitializationException] {
actorOf(new FailingOuterActor(actorOf(new InnerActor)))
}
refStackMustBeEmpty
contextStackMustBeEmpty
intercept[akka.actor.ActorInitializationException] {
actorOf(new OuterActor(actorOf(new FailingInnerActor)))
}
refStackMustBeEmpty
contextStackMustBeEmpty
intercept[akka.actor.ActorInitializationException] {
actorOf(new FailingInheritingOuterActor(actorOf(new InnerActor)))
}
refStackMustBeEmpty
contextStackMustBeEmpty
intercept[akka.actor.ActorInitializationException] {
actorOf(new FailingOuterActor(actorOf(new FailingInheritingInnerActor)))
}
refStackMustBeEmpty
contextStackMustBeEmpty
intercept[akka.actor.ActorInitializationException] {
actorOf(new FailingInheritingOuterActor(actorOf(new FailingInheritingInnerActor)))
}
refStackMustBeEmpty
contextStackMustBeEmpty
intercept[akka.actor.ActorInitializationException] {
actorOf(new FailingInheritingOuterActor(actorOf(new FailingInnerActor)))
}
refStackMustBeEmpty
contextStackMustBeEmpty
intercept[akka.actor.ActorInitializationException] {
actorOf(new OuterActor(actorOf(new InnerActor {
@ -175,31 +175,31 @@ class ActorRefSpec extends WordSpec with MustMatchers {
})))
}
refStackMustBeEmpty
contextStackMustBeEmpty
intercept[akka.actor.ActorInitializationException] {
actorOf(new FailingOuterActor(actorOf(new FailingInheritingInnerActor)))
}
refStackMustBeEmpty
contextStackMustBeEmpty
intercept[akka.actor.ActorInitializationException] {
actorOf(new OuterActor(actorOf(new FailingInheritingInnerActor)))
}
refStackMustBeEmpty
contextStackMustBeEmpty
intercept[akka.actor.ActorInitializationException] {
actorOf(new OuterActor(actorOf({ new InnerActor; new InnerActor })))
}
refStackMustBeEmpty
contextStackMustBeEmpty
(intercept[java.lang.IllegalStateException] {
actorOf(new OuterActor(actorOf({ throw new IllegalStateException("Ur state be b0rked"); new InnerActor })))
}).getMessage must be === "Ur state be b0rked"
refStackMustBeEmpty
contextStackMustBeEmpty
}
"be serializable using Java Serialization on local node" in {

View file

@ -454,32 +454,35 @@ trait Actor {
*/
type Receive = Actor.Receive
/**
* Stores the context for this actor, including self, sender, and hotswap.
*/
@transient
private[akka] val actorContext: ActorContext = {
val contextStack = ActorInstance.contextStack.get
def noContextError = {
throw new ActorInitializationException(
"\n\tYou cannot create an instance of " + getClass.getName + " explicitly using the constructor (new)." +
"\n\tYou have to use one of the factory methods to create a new actor. Either use:" +
"\n\t\t'val actor = Actor.actorOf[MyActor]', or" +
"\n\t\t'val actor = Actor.actorOf(new MyActor(..))'")
}
if (contextStack.isEmpty) noContextError
val context = contextStack.head
if (context eq null) noContextError
ActorInstance.contextStack.set(contextStack.push(null))
context
}
/**
* Some[ActorRef] representation of the 'self' ActorRef reference.
* <p/>
* Mainly for internal use, functions as the implicit sender references when invoking
* the 'forward' function.
*/
@transient
val someSelf: Some[ScalaActorRef with SelfActorRef] = {
val refStack = ActorInstance.refStack.get
if (refStack.isEmpty) throw new ActorInitializationException(
"\n\tYou can not create an instance of an " + getClass.getName + " explicitly using 'new MyActor'." +
"\n\tYou have to use one of the factory methods in the 'Actor' object to create a new actor." +
"\n\tEither use:" +
"\n\t\t'val actor = Actor.actorOf[MyActor]', or" +
"\n\t\t'val actor = Actor.actorOf(new MyActor(..))'")
val ref = refStack.head
if (ref eq null)
throw new ActorInitializationException("Trying to create an instance of " + getClass.getName + " outside of a wrapping 'actorOf'")
else {
// Push a null marker so any subsequent calls to new Actor doesn't reuse this actor ref
ActorInstance.refStack.set(refStack.push(null))
Some(ref)
}
}
def someSelf: Some[ScalaActorRef with SelfActorRef] = Some(actorContext.self)
/*
* Option[ActorRef] representation of the 'self' ActorRef reference.
@ -515,7 +518,7 @@ trait Actor {
* </pre>
*/
@transient
implicit val self = someSelf.get
implicit def self = someSelf.get
/**
* User overridable callback/setting.

View file

@ -0,0 +1,13 @@
/**
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.actor
/**
* Everything that gets injected into the actor.
* Just a wrapper on self for now.
*/
private[akka] class ActorContext(val self: LocalActorRef) {
}

View file

@ -23,8 +23,8 @@ private[akka] object ActorInstance {
object Shutdown extends Status
}
val refStack = new ThreadLocal[Stack[ScalaActorRef with SelfActorRef]] {
override def initialValue = Stack[ScalaActorRef with SelfActorRef]()
val contextStack = new ThreadLocal[Stack[ActorContext]] {
override def initialValue = Stack[ActorContext]()
}
}
@ -93,8 +93,8 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) {
}
def newActor: Actor = {
val stackBefore = refStack.get
refStack.set(stackBefore.push(self))
val stackBefore = contextStack.get
contextStack.set(stackBefore.push(new ActorContext(self)))
try {
if (status == Status.BeingRestarted) {
val a = actor.get()
@ -111,9 +111,9 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) {
props.creator()
}
} finally {
val stackAfter = refStack.get
val stackAfter = contextStack.get
if (stackAfter.nonEmpty)
refStack.set(if (stackAfter.head eq null) stackAfter.pop.pop else stackAfter.pop) //pop null marker plus self
contextStack.set(if (stackAfter.head eq null) stackAfter.pop.pop else stackAfter.pop) // pop null marker plus our context
}
} match {
case null throw new ActorInitializationException("Actor instance passed to actorOf can't be 'null'")
@ -138,7 +138,7 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) {
stopSupervisedActors()
} finally {
self.currentMessage = null
setActorSelf(null)
clearActorContext()
}
}
}
@ -283,7 +283,7 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) {
val message = if (self.currentMessage ne null) Some(self.currentMessage.message) else None
failedActor.preRestart(reason, message)
val freshActor = newActor
setActorSelf(null) // only null out the references if we could instantiate the new actor
clearActorContext()
actor.set(freshActor) // assign it here so if preStart fails, we can null out the sef-refs next call
freshActor.postRestart(reason)
if (Actor.debugLifecycle) EventHandler.debug(freshActor, "restarted")
@ -404,16 +404,15 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) {
}
}
def setActorSelf(value: ActorRef): Unit = {
def clearActorContext(): Unit = setActorContext(null)
def setActorContext(newContext: ActorContext): Unit = {
@tailrec
def lookupAndSetSelfFields(clazz: Class[_], actor: Actor, value: ActorRef): Boolean = {
def lookupAndSetSelfFields(clazz: Class[_], actor: Actor, newContext: ActorContext): Boolean = {
val success = try {
val selfField = clazz.getDeclaredField("self")
val someSelfField = clazz.getDeclaredField("someSelf")
selfField.setAccessible(true)
someSelfField.setAccessible(true)
selfField.set(actor, value)
someSelfField.set(actor, if (value ne null) Some(value) else null)
val contextField = clazz.getDeclaredField("actorContext")
contextField.setAccessible(true)
contextField.set(actor, newContext)
true
} catch {
case e: NoSuchFieldException false
@ -424,10 +423,10 @@ private[akka] class ActorInstance(props: Props, self: LocalActorRef) {
val parent = clazz.getSuperclass
if (parent eq null)
throw new IllegalActorStateException(toString + " is not an Actor since it have not mixed in the 'Actor' trait")
lookupAndSetSelfFields(parent, actor, value)
lookupAndSetSelfFields(parent, actor, newContext)
}
}
lookupAndSetSelfFields(actor.get.getClass, actor.get, value)
lookupAndSetSelfFields(actor.get.getClass, actor.get, newContext)
}
}

View file

@ -341,7 +341,7 @@ class LocalActorRef private[akka] (
hotswap = __hotswap
receiveTimeout = __receiveTimeout
actorInstance.setActorSelf(this) // TODO: why is this needed?
actorInstance.setActorContext(new ActorContext(this)) // this is needed for deserialization - why?
}
private[this] val actorInstance = new ActorInstance(props, this)