2012-01-18 11:52:35 +01:00
/* *
2013-01-09 01:47:48 +01:00
* Copyright ( C ) 2009 - 2013 Typesafe Inc . < http : //www.typesafe.com>
2012-01-18 11:52:35 +01:00
*/
package akka.pattern
2012-06-15 13:04:10 +02:00
import language.implicitConversions
2012-01-18 11:52:35 +01:00
import java.util.concurrent.TimeoutException
2012-03-24 23:02:37 +01:00
import akka.actor._
2013-03-05 16:19:54 +01:00
import akka.dispatch.sysmsg._
2012-07-22 15:33:18 +02:00
import scala.annotation.tailrec
import scala.util.control.NonFatal
2012-07-17 17:21:08 +02:00
import scala.concurrent. { Future , Promise , ExecutionContext }
2012-07-22 15:33:18 +02:00
import akka.util. { Timeout , Unsafe }
2012-08-20 15:21:44 +02:00
import scala.util. { Success , Failure }
2012-01-18 11:52:35 +01:00
/* *
* This is what is used to complete a Future that is returned from an ask /? call ,
* when it times out .
*/
2012-03-06 14:56:34 +01:00
class AskTimeoutException ( message : String , cause : Throwable ) extends TimeoutException ( message ) {
2012-01-18 11:52:35 +01:00
def this ( message : String ) = this ( message , null : Throwable )
2012-03-06 14:56:34 +01:00
override def getCause ( ) : Throwable = cause
2012-01-18 11:52:35 +01:00
}
2012-01-23 18:23:34 +01:00
/* *
* This object contains implementation details of the “ ask ” pattern .
*/
2012-02-01 13:37:57 +01:00
trait AskSupport {
/* *
* Import this implicit conversion to gain `?` and `ask` methods on
* [ [ akka . actor . ActorRef ] ] , which will defer to the
* `ask(actorRef, message)(timeout)` method defined here .
*
* { { {
* import akka.pattern.ask
*
* val future = actor ? message // => ask(actor, message)
* val future = actor ask message // => ask(actor, message)
* val future = actor . ask ( message ) ( timeout ) // => ask(actor, message)(timeout)
* } } }
*
* All of the above use an implicit [ [ akka . actor . Timeout ] ] .
*/
implicit def ask ( actorRef : ActorRef ) : AskableActorRef = new AskableActorRef ( actorRef )
/* *
2012-07-04 15:25:30 +02:00
* Sends a message asynchronously and returns a [ [ scala . concurrent . Future ] ]
2012-02-01 13:37:57 +01:00
* holding the eventual reply message ; this means that the target actor
* needs to send the result to the `sender` reference provided . The Future
2012-05-18 15:04:08 +02:00
* will be completed with an [ [ akka . pattern . AskTimeoutException ] ] after the
2012-02-01 13:37:57 +01:00
* given timeout has expired ; this is independent from any timeout applied
* while awaiting a result for this future ( i . e . in
* `Await.result(..., timeout)` ) .
*
* < b > Warning : </ b >
* When using future callbacks , inside actors you need to carefully avoid closing over
* the containing actor ’ s object , i . e . do not call methods or access mutable state
* on the enclosing actor from within the callback . This would break the actor
* encapsulation and may introduce synchronization bugs and race conditions because
* the callback will be scheduled concurrently to the enclosing actor . Unfortunately
* there is not yet a way to detect these illegal accesses at compile time .
*
* < b > Recommended usage : </ b >
*
* { { {
* val f = ask ( worker , request ) ( timeout )
* flow {
* EnrichedRequest ( request , f ( ) )
* } pipeTo nextActor
* } } }
*
2012-07-25 13:26:33 +02:00
* See [ [ scala . concurrent . Future ] ] for a description of `flow`
2012-02-01 13:37:57 +01:00
*/
2012-12-17 14:22:17 +01:00
def ask ( actorRef : ActorRef , message : Any ) ( implicit timeout : Timeout ) : Future [ Any ] = actorRef ? message
}
/*
* Implementation class of the “ ask ” pattern enrichment of ActorRef
*/
final class AskableActorRef ( val actorRef : ActorRef ) extends AnyVal {
def ask ( message : Any ) ( implicit timeout : Timeout ) : Future [ Any ] = actorRef match {
2012-05-03 21:14:47 +02:00
case ref : InternalActorRef if ref . isTerminated ⇒
2012-09-19 23:55:53 +02:00
actorRef ! message
2012-07-23 13:52:48 +02:00
Future . failed [ Any ] ( new AskTimeoutException ( "Recipient[%s] had already been terminated." format actorRef ) )
2012-02-01 13:37:57 +01:00
case ref : InternalActorRef ⇒
2012-09-14 10:08:40 +02:00
if ( timeout . duration . length <= 0 ) Future . failed [ Any ] ( new IllegalArgumentException ( "Timeout length for an `ask` must be greater or equal to 1. Question not sent to [%s]" format actorRef ) )
2012-07-23 13:52:48 +02:00
else {
val provider = ref . provider
2012-03-23 21:35:52 +01:00
val a = PromiseActorRef ( provider , timeout )
2012-02-01 13:37:57 +01:00
actorRef . tell ( message , a )
2012-07-04 15:25:30 +02:00
a . result . future
2012-02-01 13:37:57 +01:00
}
2012-07-23 13:52:48 +02:00
case _ ⇒ Future . failed [ Any ] ( new IllegalArgumentException ( "Unsupported type of ActorRef for the recipient. Question not sent to [%s]" format actorRef ) )
2012-02-01 13:37:57 +01:00
}
2012-01-18 11:52:35 +01:00
2012-12-17 14:22:17 +01:00
def ? ( message : Any ) ( implicit timeout : Timeout ) : Future [ Any ] = ask ( message ) ( timeout )
2012-03-23 21:35:52 +01:00
}
/* *
* Akka private optimized representation of the temporary actor spawned to
* receive the reply to an "ask" operation .
2012-05-18 16:41:19 +02:00
*
* INTERNAL API
2012-03-23 21:35:52 +01:00
*/
2012-03-27 09:28:54 +02:00
private [ akka ] final class PromiseActorRef private ( val provider : ActorRefProvider , val result : Promise [ Any ] )
extends MinimalActorRef {
2012-03-23 21:35:52 +01:00
import PromiseActorRef._
2012-03-24 23:02:37 +01:00
import AbstractPromiseActorRef.stateOffset
2012-05-30 13:24:38 +02:00
import AbstractPromiseActorRef.watchedByOffset
2012-01-18 11:52:35 +01:00
/* *
2012-03-23 21:35:52 +01:00
* As an optimization for the common ( local ) case we only register this PromiseActorRef
* with the provider when the `path` member is actually queried , which happens during
2012-03-24 23:02:37 +01:00
* serialization ( but also during a simple call to `toString` , `equals` or `hashCode` ! ) .
2012-03-23 21:35:52 +01:00
*
* Defined states :
2012-03-24 23:02:37 +01:00
* null => started , path not yet created
* Registering => currently creating temp path and registering it
* path : ActorPath => path is available and was registered
* StoppedWithPath ( path ) => stopped , path available
* Stopped => stopped , path not yet created
2012-01-18 11:52:35 +01:00
*/
2012-03-23 21:35:52 +01:00
@volatile
2012-03-27 09:28:54 +02:00
private [ this ] var _stateDoNotCallMeDirectly : AnyRef = _
2012-01-18 11:52:35 +01:00
2012-05-30 13:24:38 +02:00
@volatile
private [ this ] var _watchedByDoNotCallMeDirectly : Set [ ActorRef ] = ActorCell . emptyActorRefSet
@inline
private [ this ] def watchedBy : Set [ ActorRef ] = Unsafe . instance . getObjectVolatile ( this , watchedByOffset ) . asInstanceOf [ Set [ ActorRef ] ]
@inline
private [ this ] def updateWatchedBy ( oldWatchedBy : Set [ ActorRef ] , newWatchedBy : Set [ ActorRef ] ) : Boolean =
Unsafe . instance . compareAndSwapObject ( this , watchedByOffset , oldWatchedBy , newWatchedBy )
@tailrec // Returns false if the Promise is already completed
private [ this ] final def addWatcher ( watcher : ActorRef ) : Boolean = watchedBy match {
case null ⇒ false
2012-06-02 14:49:28 +02:00
case other ⇒ updateWatchedBy ( other , other + watcher ) || addWatcher ( watcher )
2012-05-30 13:24:38 +02:00
}
@tailrec
private [ this ] final def remWatcher ( watcher : ActorRef ) : Unit = watchedBy match {
case null ⇒ ( )
case other ⇒ if ( ! updateWatchedBy ( other , other - watcher ) ) remWatcher ( watcher )
}
@tailrec
private [ this ] final def clearWatchers ( ) : Set [ ActorRef ] = watchedBy match {
case null ⇒ ActorCell . emptyActorRefSet
case other ⇒ if ( ! updateWatchedBy ( other , null ) ) clearWatchers ( ) else other
}
2012-03-23 21:35:52 +01:00
@inline
2012-05-30 13:24:38 +02:00
private [ this ] def state : AnyRef = Unsafe . instance . getObjectVolatile ( this , stateOffset )
2012-03-24 23:02:37 +01:00
@inline
2012-05-30 13:24:38 +02:00
private [ this ] def updateState ( oldState : AnyRef , newState : AnyRef ) : Boolean =
Unsafe . instance . compareAndSwapObject ( this , stateOffset , oldState , newState )
2012-03-24 23:02:37 +01:00
@inline
2012-05-30 13:24:38 +02:00
private [ this ] def setState ( newState : AnyRef ) : Unit = Unsafe . instance . putObjectVolatile ( this , stateOffset , newState )
2012-03-23 15:52:36 +01:00
2012-05-18 16:41:19 +02:00
override def getParent : InternalActorRef = provider . tempContainer
2012-03-27 09:28:54 +02:00
2012-03-23 21:35:52 +01:00
/* *
* Contract of this method :
2012-03-24 23:02:37 +01:00
* Must always return the same ActorPath , which must have
2012-03-23 21:35:52 +01:00
* been registered if we haven 't been stopped yet .
*/
@tailrec
def path : ActorPath = state match {
case null ⇒
2012-03-24 23:02:37 +01:00
if ( updateState ( null , Registering ) ) {
2012-03-23 21:35:52 +01:00
var p : ActorPath = null
try {
p = provider . tempPath ( )
2012-03-23 15:52:36 +01:00
provider . registerTempActor ( this , p )
p
2012-03-24 23:02:37 +01:00
} finally { setState ( p ) }
2012-03-23 21:35:52 +01:00
} else path
2012-03-24 23:02:37 +01:00
case p : ActorPath ⇒ p
case StoppedWithPath ( p ) ⇒ p
2012-03-23 21:35:52 +01:00
case Stopped ⇒
// even if we are already stopped we still need to produce a proper path
2012-03-24 23:02:37 +01:00
updateState ( Stopped , StoppedWithPath ( provider . tempPath ( ) ) )
2012-03-23 21:35:52 +01:00
path
case Registering ⇒ path // spin until registration is completed
}
2012-01-18 11:52:35 +01:00
2012-10-06 00:13:42 +02:00
override def ! ( message : Any ) ( implicit sender : ActorRef = Actor . noSender ) : Unit = state match {
2012-03-24 23:02:37 +01:00
case Stopped | _ : StoppedWithPath ⇒ provider . deadLetters ! message
2013-02-20 11:42:29 +01:00
case _ ⇒
if ( message == null ) throw new InvalidMessageException ( "Message is null" )
if ( ! ( result . tryComplete (
message match {
case Status . Success ( r ) ⇒ Success ( r )
case Status . Failure ( f ) ⇒ Failure ( f )
case other ⇒ Success ( other )
} ) ) ) provider . deadLetters ! message
2012-03-23 21:35:52 +01:00
}
2012-01-18 11:52:35 +01:00
2012-05-29 14:09:22 +02:00
override def sendSystemMessage ( message : SystemMessage ) : Unit = message match {
2012-06-01 14:49:12 +02:00
case _ : Terminate ⇒ stop ( )
case Watch ( watchee , watcher ) ⇒
if ( watchee == this && watcher != this ) {
2012-09-28 11:18:15 +02:00
if ( ! addWatcher ( watcher ) ) watcher ! Terminated ( watchee ) ( existenceConfirmed = true , addressTerminated = false )
2012-06-01 14:49:12 +02:00
} else System . err . println ( "BUG: illegal Watch(%s,%s) for %s" . format ( watchee , watcher , this ) )
case Unwatch ( watchee , watcher ) ⇒
if ( watchee == this && watcher != this ) remWatcher ( watcher )
else System . err . println ( "BUG: illegal Unwatch(%s,%s) for %s" . format ( watchee , watcher , this ) )
case _ ⇒
2012-03-23 21:35:52 +01:00
}
2012-01-18 11:52:35 +01:00
2012-05-18 16:41:19 +02:00
override def isTerminated : Boolean = state match {
2012-03-24 23:02:37 +01:00
case Stopped | _ : StoppedWithPath ⇒ true
case _ ⇒ false
}
2012-01-18 11:52:35 +01:00
2012-03-23 21:35:52 +01:00
@tailrec
2012-03-24 23:02:37 +01:00
override def stop ( ) : Unit = {
2012-05-30 13:24:38 +02:00
def ensureCompleted ( ) : Unit = {
2012-08-20 15:21:44 +02:00
result tryComplete Failure ( new ActorKilledException ( "Stopped" ) )
2012-05-30 13:24:38 +02:00
val watchers = clearWatchers ( )
if ( ! watchers . isEmpty ) {
2012-09-28 11:18:15 +02:00
val termination = Terminated ( this ) ( existenceConfirmed = true , addressTerminated = false )
2012-10-16 12:06:03 +02:00
watchers foreach { _ . tell ( termination , this ) }
2012-05-30 13:24:38 +02:00
}
}
2012-03-24 23:02:37 +01:00
state match {
2012-05-28 16:49:49 +02:00
case null ⇒ // if path was never queried nobody can possibly be watching us, so we don't have to publish termination either
if ( updateState ( null , Stopped ) ) ensureCompleted ( ) else stop ( )
2012-03-24 23:02:37 +01:00
case p : ActorPath ⇒
2012-05-30 15:37:29 +02:00
if ( updateState ( p , StoppedWithPath ( p ) ) ) { try ensureCompleted ( ) finally provider . unregisterTempActor ( p ) } else stop ( )
2012-05-28 16:49:49 +02:00
case Stopped | _ : StoppedWithPath ⇒ // already stopped
2012-03-24 23:02:37 +01:00
case Registering ⇒ stop ( ) // spin until registration is completed before stopping
}
2012-01-18 11:52:35 +01:00
}
2012-03-23 21:35:52 +01:00
}
2012-01-18 11:52:35 +01:00
2012-05-18 16:41:19 +02:00
/* *
* INTERNAL API
*/
2012-03-23 21:35:52 +01:00
private [ akka ] object PromiseActorRef {
2012-03-24 23:02:37 +01:00
private case object Registering
private case object Stopped
private case class StoppedWithPath ( path : ActorPath )
2012-03-23 21:35:52 +01:00
def apply ( provider : ActorRefProvider , timeout : Timeout ) : PromiseActorRef = {
2012-07-17 17:21:08 +02:00
implicit val ec = provider . dispatcher // TODO should we take an ExecutionContext in the method signature?
2012-07-04 15:25:30 +02:00
val result = Promise [ Any ] ( )
2012-03-27 09:28:54 +02:00
val a = new PromiseActorRef ( provider , result )
2012-08-20 15:21:44 +02:00
val f = provider . scheduler . scheduleOnce ( timeout . duration ) { result tryComplete Failure ( new AskTimeoutException ( "Timed out" ) ) }
2012-07-04 15:25:30 +02:00
result . future onComplete { _ ⇒ try a . stop ( ) finally f . cancel ( ) }
2012-01-18 11:52:35 +01:00
a
}
2013-01-09 01:47:48 +01:00
}