diff --git a/akka-actor-migration/src/main/scala/akka/migration/AskableActorRef.scala b/akka-actor-migration/src/main/scala/akka/migration/AskableActorRef.scala new file mode 100644 index 0000000000..caf8921812 --- /dev/null +++ b/akka-actor-migration/src/main/scala/akka/migration/AskableActorRef.scala @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.migration + +import akka.actor.ActorRef +import akka.dispatch.Future +import akka.util.Timeout + +class AskableActorRef(val actorRef: ActorRef) { + + /** + * Sends a message asynchronously and returns a [[akka.dispatch.Future]] + * holding the eventual reply message; this means that the target actor + * needs to send the result to the `sender` reference provided. The Future + * will be completed with an [[akka.actor.AskTimeoutException]] after the + * given timeout has expired; this is independent from any timeout applied + * while awaiting a result for this future (i.e. in + * `Await.result(..., timeout)`). + * + * Warning: + * 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. + * + * Recommended usage: + * + * {{{ + * val f = worker.ask(request)(timeout) + * flow { + * EnrichedRequest(request, f()) + * } pipeTo nextActor + * }}} + * + * [see the [[akka.dispatch.Future]] companion object for a description of `flow`] + */ + def ask(message: Any)(implicit timeout: Timeout = null): Future[Any] = akka.pattern.ask(actorRef, message)(timeout) + + /** + * Sends a message asynchronously and returns a [[akka.dispatch.Future]] + * holding the eventual reply message; this means that the target actor + * needs to send the result to the `sender` reference provided. The Future + * will be completed with an [[akka.actor.AskTimeoutException]] after the + * given timeout has expired; this is independent from any timeout applied + * while awaiting a result for this future (i.e. in + * `Await.result(..., timeout)`). + * + * Warning: + * 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. + * + * Recommended usage: + * + * {{{ + * val f = worker ? request + * flow { + * EnrichedRequest(request, f()) + * } pipeTo nextActor + * }}} + * + * [see the [[akka.dispatch.Future]] companion object for a description of `flow`] + */ + def ?(message: Any)(implicit timeout: Timeout): Future[Any] = akka.pattern.ask(actorRef, message)(timeout) + + @deprecated("use ?(msg)(timeout), this method has dangerous ambiguity", "2.0-migration") + def ?(message: Any, timeout: Timeout)(i: Int = 0): Future[Any] = this.?(message)(timeout) +} \ No newline at end of file diff --git a/akka-actor-migration/src/main/scala/akka/migration/package.scala b/akka-actor-migration/src/main/scala/akka/migration/package.scala index 319fdd997e..4fd8d5eeea 100644 --- a/akka-actor-migration/src/main/scala/akka/migration/package.scala +++ b/akka-actor-migration/src/main/scala/akka/migration/package.scala @@ -31,4 +31,7 @@ package object migration { def stop(): Unit = GlobalActorSystem.stop(actorRef) } + implicit def ask(actorRef: ActorRef) = new akka.migration.AskableActorRef(actorRef) + def ask(actorRef: ActorRef, message: Any)(implicit timeout: Timeout = null): Future[Any] = akka.pattern.ask(actorRef, message)(timeout) + } \ No newline at end of file diff --git a/akka-actor/src/main/scala/akka/pattern/AskSupport.scala b/akka-actor/src/main/scala/akka/pattern/AskSupport.scala index ca07ea5052..57dec1debe 100644 --- a/akka-actor/src/main/scala/akka/pattern/AskSupport.scala +++ b/akka-actor/src/main/scala/akka/pattern/AskSupport.scala @@ -81,7 +81,7 @@ object AskSupport { * * [see the [[akka.dispatch.Future]] companion object for a description of `flow`] */ - def ?(message: Any)(implicit timeout: Timeout): Future[Any] = akka.pattern.ask(actorRef, message)(timeout) + def ?(message: Any)(implicit timeout: Timeout = null): Future[Any] = akka.pattern.ask(actorRef, message)(timeout) } /** diff --git a/akka-docs/project/migration-guide-1.3.x-2.0.x.rst b/akka-docs/project/migration-guide-1.3.x-2.0.x.rst index a3f874894d..33752b8ce7 100644 --- a/akka-docs/project/migration-guide-1.3.x-2.0.x.rst +++ b/akka-docs/project/migration-guide-1.3.x-2.0.x.rst @@ -205,6 +205,26 @@ Documentation: * :ref:`actors-scala` * :ref:`untyped-actors-java` +``ActorRef.?(msg, timeout)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This method has a dangerous overlap with ``ActorRef.?(msg)(implicit timeout)`` +due to the fact that Scala allows to pass a :class:`Tuple` in place of the +message without requiring extra parentheses:: + + actor ? (1, "hallo") // will send a tuple + actor ? (1, Timeout()) // will send 1 with an explicit timeout + +To remove this ambiguity, the latter variant is removed in version 2.0. If you +were using it before, it will now send tuples where that is not desired. In +order to correct all places in the code where this happens, simply import +``akka.migration.ask`` instead of ``akka.pattern.ask`` to obtain a variant +which will give deprecation warnings where the old method signature is used:: + + import akka.migration.ask + + actor ? (1, Timeout(2 seconds)) // will give deprecation warning + ActorPool ^^^^^^^^^