I want to use Akka actors as a mailbox only, i.e. I want to create n threads, which each create 1 remote actor.
Each thread obtains a reference to all the remote actors of the other threads, such that they can send messages to eachother via their respective actors.
The actor is defined as follows:
case class Receive
case class GroupReceive(id: Symbol)
case class GroupMsg[T](id: Symbol, msg: T)
class FooParActor(val distributor: Distributor) extends Actor
with Stash {
import context._
val globalRank: Int = distributor.globalRank
def doReceive(realSender: ActorRef, ID: Symbol) {
unstashAll()
become({
case GroupMsg(ID, msg) =>
realSender ! msg
unbecome()
case GroupMsg(otherId, msg) =>
println(globalRank + ": stashing " + otherId)
unbecome()
case x => sys.error("bad msg: " + x)
}, discardOld = false)
}
def receive = {
case GroupReceive(id) =>
doReceive(sender, id)
case GroupMsg(id, x) =>
stash()
case x => sys.error("bad msg: " + x)
}
}
To read a message, the owner-thread sends GroupReceive('someSymbol) to his local actor, which in turn forwards a GroupMsg to the thread. The code from the thread's point of view to read a message looks like this:
def groupRcv[T](id:Symbol) = Await.result(aref ? GroupReceive(id), timeout.duration).asInstanceOf[T]
where aref is a reference to the local actor of this thread.
I sometimes experience deadlocks(timeouts of 5 seconds) with the above pattern, even with extremely simple usage and small messages. I narrowed the problem down to actors stalling after receiving the GroupReceive(id) message, but before entering the first case of doReceive(...): case GroupMsg(ID, msg) =>.
I made printout-traces to check that the actors actually have messages in the stash before they go to the doReceive call, and it seems that for some reason, they just don't handle them. Can the code I presented above go to a state where a GroupMsg() gets lost from a FooParActor's stash? Or is there any other way the actor can go to a deadlock after receiving a GroupReceive() message?