Thursday, July 31, 2008

Actor Exceptions

While working with the ActorPublisher trait I described in my previous post, I was annoyed by the fact that, whenever there was an error in my code that caused an uncaught exception, the Actor would terminate, so that the functionality it provided was no longer available. The application continued running, and some things would work, but not the functionality provided by that Actor.

I wanted to make my application more robust against this kind of problem by catching the exception, delivering an error message, and continuing. For a normal method, this would have been straightforward, but the execution path in an Actor using react (rather than receive) is not quite normal. The canonical act() method in an Actor using react looks like this:
def act() {
    loop {
        react {
            case a => ...
            case b => ...
        }
    }
}
I initially thought I could just catch the exceptions around the react statement, handle them, and continue the loop, like this:
//This does not work
def act() {
    loop {
        try {
            react {
                case a => ...
                case b => ...
            }
        } catch {
            case ex:Exception => ...
        }
    }
}
However, this does not work.

I mentioned in my previous blog posting how the react method takes an argument of type PartialFunction[Any,Unit]. It has a couple of other non-intuitive behaviors, which are a result of the event-based mechanism used in Scala to allow concurrently using huge numbers of Actors.
  • When it has finished executing its body, it throws a SuspendActorException rather than simply returning.
  • Rather than directly executing the supplied PartialFunction, it passes it to a scheduler, which may execute that function on a separate thread.
If only the first of the above two behaviors were true, it would be possible to make a special check for SuspendActorException in the catch block around the react statement and rethrow that exception. But because of that second behavior, executing the PartialFunction by using a scheduler, exceptions thrown from within the PartialFunction never make it to the enclosing block of the react statement.

In order to catch an exception thrown by the PartialFunction, the code that catches the exception must therefore be something within the body of the react statement. In other words, I needed to wrap my PartialFunction in another PartialFunction that catches and handles exceptions. I called this PartialFunction that catches exceptions PFCatch.

PFCatch is a PartialFunction that takes as an argument another PartialFunction, referred to here as the target function. PartialFunction defines two methods that relate to the computation defined by that PartialFunction: apply and isDefinedAt. The PFCatch class proxies these two methods to the target function. The call to the apply method of the target function is wrapped in a try/catch block, which is where I catch and handle the exceptions thrown by the target function. I did not bother to wrap the call to the isDefinedAt method of the target function because that method was not throwing exceptions, so was not giving me any problems.

The PFCatch class thus looks something like this:
class PFCatch[T](f:PartialFunction[T,Unit])
        extends PartialFunction[T,Unit] {

    def apply(x:T) = {
        try {
            f(x)
        } catch {
            case ex:Exception => handleException(ex)
        }
    }

    def isDefinedAt(x:T) = f.isDefinedAt(x)

    def handleException(ex:Exception):Unit = ...
}
For convenience, I also created a PFCatch companion object:
object PFCatch {
    def apply[T](f:PartialFunction[T,Unit]) =  new PFCatch(f)
}
To use PFCatch, I first modified my application to define an explicit PartialFunction as an argument to the react method (as discussed in more detail in my previous post), then I wrapped that method call in PFCatch. This calls the apply method in object PFCatch, which creates an instance of the PFCatch class using the given PartialFunction as a target function. That call looks like this:
def act() {
    loop {
        react (PFCatch(handleMessage))
    }
    private val handleMessage:PartialFunction[Any,Unit] = {
        case a => ...
        case b => ...
    }
}
Or, when combining this with the refactoring technique used in the previous post, the react line might look like this:
        react (PFCatch(handleSubscribe orElse handleOther))

My actual implementation of PFCatch takes a couple of extra arguments, which I use in order to better handle the exception. The complete listing of that version can be found as PFCatch.scala in the net.jimmc.util package of my Mimprint application, which is distributed under the GPL.

3 comments:

Julian Morrison said...

What you're looking for are the "link" methods. Linked actors get each other's exceptions, and die as a unit. If a linked actor sets "trapExit = true" in its act() method, it doesn't die but rather receives a message with the Exit case class. So then it can restart the whole shebang. Erlang design uses this a lot, and Scala copies it.

Jim McBeath said...

Julian: Thanks for pointing out what the "link" methods do, since that functionality is very poorly documented. Using link does sound like a better approach for a more sophisticated system, since it would probably be simpler and you could have one actor that handles restarting a bunch of other actors.

Richard said...
This comment has been removed by a blog administrator.