Added explicit nullification of all ActorRef references in Actor to make the Actor instance eligable for GC

This commit is contained in:
Jonas Bonér 2010-05-18 20:25:13 +02:00
parent 3d973fea9b
commit 05058af370
3 changed files with 96 additions and 13 deletions

View file

@ -1,6 +1,6 @@
This software is licensed under the Apache 2 license, quoted below.
Copyright 2009 Scalable Solutions AB [http://scalablesolutions.se]
Copyright 2009-2010 Scalable Solutions AB [http://scalablesolutions.se]
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of

View file

@ -73,10 +73,11 @@ object Actor extends Logging {
val TIMEOUT = config.getInt("akka.actor.timeout", 5000)
val SERIALIZE_MESSAGES = config.getBool("akka.actor.serialize-messages", false)
/** A Receive is the type that defines actor message behavior
* currently modeled as a PartialFunction[Any,Unit]
/**
* A Receive is a convenience type that defines actor message behavior currently modeled as
* a PartialFunction[Any, Unit].
*/
type Receive = PartialFunction[Any,Unit]
type Receive = PartialFunction[Any, Unit]
private[actor] val actorRefInCreation = new scala.util.DynamicVariable[Option[ActorRef]](None)
@ -89,6 +90,10 @@ object Actor extends Logging {
* actor ! message
* actor.stop
* </pre>
* You can create and start the actor in one statement like this:
* <pre>
* val actor = actorOf[MyActor].start
* </pre>
*/
def actorOf[T <: Actor: Manifest]: ActorRef = new LocalActorRef(manifest[T].erasure.asInstanceOf[Class[_ <: Actor]])
@ -105,6 +110,10 @@ object Actor extends Logging {
* actor ! message
* actor.stop
* </pre>
* You can create and start the actor in one statement like this:
* <pre>
* val actor = actorOf(new MyActor).start
* </pre>
*/
def actorOf(factory: => Actor): ActorRef = new LocalActorRef(() => factory)
@ -239,17 +248,57 @@ object Actor extends Logging {
* => SHUT DOWN (when 'exit' is invoked) - can't do anything
* </pre>
*
* <p/>
* The Actor's API is available in the 'self' member variable.
*
* <p/>
* Here you find functions like:
* - !, !!, !!! and forward
* - link, unlink, startLink, spawnLink etc
* - makeTransactional, makeRemote etc.
* - start, stop
* - etc.
*
* <p/>
* Here you also find fields like
* - dispatcher = ...
* - id = ...
* - lifeCycle = ...
* - faultHandler = ...
* - trapExit = ...
* - etc.
*
* <p/>
* This means that to use them you have to prefix them with 'self', like this: <tt>self ! Message</tt>
*
* However, for convenience you can import these functions and fields like below, which will allow you do
* drop the 'self' prefix:
* <pre>
* class MyActor extends Actor {
* import self._
* id = ...
* dispatcher = ...
* spawnLink[OtherActor]
* ...
* }
* </pre>
*
* <p/>
* The Actor trait also has a 'log' member field that can be used for logging within the Actor.
*
* @author <a href="http://jonasboner.com">Jonas Bon&#233;r</a>
*/
trait Actor extends Logging {
//Type alias because traits cannot have companion objects...
/**
* Type alias because traits cannot have companion objects.
*/
type Receive = Actor.Receive
/**
/*
* For internal use only, functions as the implicit sender references when invoking
* one of the message send functions (!, !!, !!! and forward).
* one of the message send functions (!, !! and !!!).
*/
implicit val optionSelf: Option[ActorRef] = {
implicit val optionSelf: Option[ActorRef] = {
val ref = Actor.actorRefInCreation.value
Actor.actorRefInCreation.value = None
if (ref.isEmpty) throw new ActorInitializationException(
@ -258,10 +307,15 @@ trait Actor extends Logging {
"\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(..))'")
"\n\t\t'val actor = Actor.actorOf(new MyActor(..))'" +
"\n\t\t'val actor = Actor.actor { case msg => .. } }'")
else ref
}
/*
* For internal use only, functions as the implicit sender references when invoking
* the forward function.
*/
implicit val someSelf: Some[ActorRef] = optionSelf.asInstanceOf[Some[ActorRef]]
/**
@ -274,13 +328,12 @@ trait Actor extends Logging {
*/
val self: ActorRef = optionSelf.get
self.id = getClass.getName
import self._
/**
* User overridable callback/setting.
* <p/>
* Partial function implementing the actor logic.
* To be implemented by subclassing actor.
* To be implemented by concrete actor class.
* <p/>
* Example code:
* <pre>

View file

@ -26,6 +26,7 @@ import java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.ConcurrentHashMap
import java.util.{Map => JMap}
import java.lang.reflect.Field
/**
* The ActorRef object can be used to deserialize ActorRef instances from of its binary representation
@ -593,7 +594,11 @@ sealed class LocalActorRef private[akka](
@volatile private var isInInitialization = false
@volatile private var runActorInitialization = false
// Needed to be able to null out the 'val self: ActorRef' member variables to make the Actor
// instance eligeble for garbage collection
private val actorSelfFields = findActorSelfField(actor.getClass)
if (runActorInitialization) initializeActorInstance
/**
@ -720,6 +725,7 @@ sealed class LocalActorRef private[akka](
remoteAddress.foreach(address => RemoteClient.unregister(
address.getHostName, address.getPort, uuid))
RemoteNode.unregister(this)
nullOutActorRefReferencesFor(actorInstance.get)
} else if (isBeingRestarted) throw new ActorKilledException("Actor [" + toString + "] is being restarted.")
}
@ -1082,6 +1088,7 @@ sealed class LocalActorRef private[akka](
Actor.log.debug("Restarting linked actors for actor [%s].", id)
Actor.log.debug("Invoking 'preRestart' for failed actor instance [%s].", id)
failedActor.preRestart(reason)
nullOutActorRefReferencesFor(failedActor)
val freshActor = newActor
freshActor.synchronized {
freshActor.init
@ -1137,7 +1144,30 @@ sealed class LocalActorRef private[akka](
protected[akka] def linkedActorsAsList: List[ActorRef] =
linkedActors.values.toArray.toList.asInstanceOf[List[ActorRef]]
private def nullOutActorRefReferencesFor(actor: Actor) = {
actorSelfFields._1.set(actor, null)
actorSelfFields._2.set(actor, null)
actorSelfFields._3.set(actor, null)
}
private def findActorSelfField(clazz: Class[_]): Tuple3[Field, Field, Field] = {
try {
val selfField = clazz.getDeclaredField("self")
val optionSelfField = clazz.getDeclaredField("optionSelf")
val someSelfField = clazz.getDeclaredField("someSelf")
selfField.setAccessible(true)
optionSelfField.setAccessible(true)
someSelfField.setAccessible(true)
(selfField, optionSelfField, someSelfField)
} catch {
case e: NoSuchFieldException =>
val parent = clazz.getSuperclass
if (parent != null) findActorSelfField(parent)
else throw new IllegalStateException(toString + " is not an Actor since it have not mixed in the 'Actor' trait")
}
}
private def initializeActorInstance = if (!isRunning) {
dispatcher.register(this)
dispatcher.start