1
votes

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?

1

1 Answers

2
votes

You are using Await.result() but not sharing where you do that: if you call groupRcv on a thread on which your actors are supposed to be running then you can of course have starvation (i.e. the target actor does not have a thread available to run on, so it will never complete the request).

It seems that you are mixing thread-based concurrency with actors in an unhealthy fashion, but since you only hint at it and don’t show the code I can only give you the broad advice to not do that. When programming actors, forget about threads; those are managed by Akka. In particular do not misuse Akka’s threads (i.e. the Await.result would probably work on your own external thread pool, although there is nearly always a better alternative).

In the end, if you are using actors just to make “a thread with a mailbox” then Akka cannot help you and you will run into all the usual traditional concurrency pitfalls.