Categories
Uncategorized

Scala Actors

I’ve been wanting to play with scala for a while. So when I needed an irc bot to read RSS feeds recently, it seemed like a good excuse to play. Sure there are probably loads of existing bots out there that can do this. But I wanted to play. With tools like pircbot and rome, it should be little more than an integration exercise, right? In particular, it seemed like a great excuse to play with scala’s actors.

Actors are a really nice API for handling concurrency. Modelled after erlang’s concurrency, they remove most of the locking problems you’re likely to face in your own code by funnelling everything through a single mailbox (which is well implemented and thread-safe). You just pass messages.

So I started bender and ended up with the obvious solution: two actors, one for speaking irc and one for reading feeds. I can’t make the bot an actor, as that’s already a thread under the control of pircbot.

Original Bender architecture

This worked fine to start with, but I needed to kick off periodic downloads of all the feeds I’m supposed to be monitoring. So after looking at programming in scala, I came up with something like this.

class Feeder extends Actor {
  val periodSeconds = 60 * 60;
  private def periodicFetch() {
    val feeder = self
    actor {
      loop {
        Thread.sleep(periodSeconds * 1000)
        feeder ! "fetch"
      }
    }
  }
  periodicFetch()
  // …
}
bender2.png

This introduces a third actor, which does nothing except sleep and prod the feed reading actor into action every now & again. All very simple. Or so I thought.

The problem was that the fetch message never arrived at Feeder. I put lots of debugging in, but it just never showed up. There was one weirdness though. When I printed out the value of feeder inside periodicFetch(), it claimed to be an ActorProxy.

This is where my inexperience kicked in. I assumed that it was a proxy for the Feeder actor. Sadly not. Really, it’s a proxy for Thread.currentThread(). So the reason I was never seeing the fetch message turn up was that it was going to a completely different mailbox. Simple.

But why was I ending up with an ActorProxy? Well, it stems from my ignoring the rule: “don’t do real work in a constructor”. When I call periodicFetch() initially, the actors mechanism isn’t yet set up. So Actor.self returns the ActorProxy instead.

The fix is simple: either make the initialisation explicit or override start(). Hey presto, my feeds all start downloading now.

class Feeder extends Actor {
  val periodSeconds = 60 * 60;
  private def periodicFetch() {
    // … as before …
  }
  // …
  def act() {
    loop {
      react {
        // …
        case "init" => periodicFetch()
      }
    }
  }
}

This is all obvious in retrospect, but wasn’t particularly easy to figure out from first principles. I have to say that I found actors to be quite difficult to debug. That said, they have one huge advantage. They’re part of a library. So I can just extract the library, and start editing it to put debug println()s in.

The other thing that caused me grief so far was import scala.actors.Actor._. The examples in the book tend to use this, and it’s handy for that. But it introduces a lot of extra names into your space. In particular, exit() caught me out.

Anyway, despite all this, I still like actors very much. They’re so much simpler to use than any other concurrency mechanism I’ve come across.

2 replies on “Scala Actors”

I’ve just tried to override start() instead of using an explicit initialisation message. That failed and I’m not entirely sure why. I think it’s because the Feeder actor doesn’t get put into the ThreadLocal that Actor.self uses until Reaction.run() has started, which may be a little while later.

Any way, the explicit message for initialisation seems like a safer bet. It’ll just get put in the actors mailbox for when it does run.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s