Reputation: 4231
using Akka 2.0 trying to send a message to self (using the ask pattern)
import akka.pattern.ask
import scala.concurrent.{Await, Future}
import akka.actor.{Props, Actor, ActorSystem}
import scala.concurrent.duration._
import akka.util.Timeout
object askTest extends App{
implicit val timeout = Timeout(5 seconds)
val system = ActorSystem("AskTestSystem")
val myActor = system.actorOf(Props(new TestActor), name = "myActor")
val future: Future[Foo] = ask(myActor, Foo("test")).mapTo[Foo]
val result = Await.result(future, timeout.duration)
println(result)
}
case class Foo(name:String){
override def toString = "Foo "+name
}
class TestActor extends Actor {
def receive = {
case Foo(a) => self ! Foo("buzz "+a)
case any => println(any+" that was unexpected")
}
}
however it crashes with Timeout exception with the following trace :
Exception in thread "main" java.util.concurrent.TimeoutException: Futures timed out after [5 seconds]
at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:96)
at scala.concurrent.impl.Promise$DefaultPromise.result(Promise.scala:100)
at scala.concurrent.Await$$anonfun$result$1.apply(package.scala:107)
at scala.concurrent.BlockContext$DefaultBlockContext$.blockOn(BlockContext.scala:53)
at scala.concurrent.Await$.result(package.scala:107)
at akkaTest.askTest$delayedInit$body.apply(askTest.scala:33)
at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
at scala.App$$anonfun$main$1.apply(App.scala:71)
at scala.App$$anonfun$main$1.apply(App.scala:71)
at scala.collection.immutable.List.foreach(List.scala:318)
at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)
at scala.App$class.main(App.scala:71)
at akkaTest.askTest$.main(askTest.scala:13)
at akkaTest.askTest.main(askTest.scala)
Upvotes: 1
Views: 11146
Reputation: 77
An Example of inifinite loop by sending a message to self-
class Swapper extends Actor {
override def preStart(): Unit = {
super.preStart()
self ! Swap
}
override def receive: Receive = {
case Swap =>
println("Hi")
self ! Swap
}
}
case object Swap
Upvotes: 0
Reputation: 1682
My apology, I know this is really an old question but it recently surfaced in my use cases. Just wanted to share my 2 cents.
I wanted to handle a message by routing it to self with updated parameters. And I'm expecting results by using ask pattern. To get this working, I had to use a second proxy actor, which main purpose is to relay to message back to the main actor.
(augmented)
Main Actor --> Proxy Actor --> Main Actor
ask ask
This has allowed me to ask for the results, at the costs of some extra overheads.
Upvotes: 0
Reputation: 170735
You can send messages to self
, but you can't wait for a reply. Because you won't get the new message until you finish handling the current message, and if you use ask
, you won't finish handling the current message until you get a reply to the new message! So you are creating a deadlock.
But in the case of code in question, you are not asking self
for a reply at all. The actor created by ask
is waiting for the reply from myActor
, and isn't getting it (because the code of TestActor
doesn't send any reply, and sends a message to self
instead).
You can see that self ! message
works by changing TestActor
a bit:
case class Bar(name: String)
class TestActor extends Actor {
def receive = {
case Foo(a) => self ! Bar(a)
case Bar(a) => println("Got a message to self: " + a)
case any => println(any+" that was unexpected")
}
}
Upvotes: 6
Reputation: 127781
Your code does what you told it to do: the actor indefinitely sends a message to itself. Because it sends the message to no one except self, your ask
pattern just can't work, because ask
is waiting for a message to be sent to the sender, not self.
self
is a reference to the current actor, you use it when you want the actor to send message to itself. sender
is a reference to an actor which has sent the message which is currently being processed. You use it if you want to "answer" to the sender.
ask
pattern creates an implicit actor which becomes sender
reference in the target actor - this is very natural. So you need to change self ! Foo("buzz " + a)
to sender ! Foo("buzz " + a)
, and your code will work.
Upvotes: 7