A quick preview of the actor API

Since the actor / future API is starting to mature, I thought it’d be nice to show what concurrent programming in JavaScript actually looks like. To begin with, here’s the canonical actor ping-pong example; two actors collaboratively incrementing a counter without explicitly sharing any state:

// Shamelessly pulled from the Rubinius example
// at http://rubini.us/doc/en/systems/concurrency/
var ping = new actor(function(msg) {
  if (msg === 1000) {
    console.log(msg);
  } else {
    pong.send(msg++);
  }
});

var pong = new actor(function(msg) {
  if (msg === 1000) {
    console.log(msg);
  } else {
    ping.send(msg++);
  }
});

ping.send(1);

You can also safely hot-swap the message handler inside an actor. When you call upgrade, the supplied function gets pushed onto an internal stack and is used as the new handler. Calling downgrade pops it off and uses the previous function (or the original one if you’re at the bottom of the stack, in which case it’s idempotent).

var x = new actor(function(msg) {
  console.log('Foo!');
});

x.send('something'); // => 'Foo!'

x.upgrade(function(msg) {
  console.log('Bar!');
});

x.send('something'); // => 'Bar!'

x.downgrade();

x.send('something'); // => 'Foo!'

Because message processing is mutually exclusive (any given actor will only process one message in its mailbox at any one time) this is generally quite safe to perform on a live system- no restarts! I say generally because messages passed to actors must be immutable, and neither JavaScript nor Java can enforce that sufficiently strongly to stop the ignorant or the strong-willed from throwing caution to the wind. For now, you just have to be disciplined. But it’s a promising start, anyway.

Rhinode can talk to the internets!

I’ve held off talking about Rhinode up until now, primarily because going into detail about the actor system, STM and concurrent I/O in JavaScript is all a bit meaningless without a concrete example to flesh it out. Well that’s no longer an impediment, because Rhinode now has a structured and extensible (albeit incomplete) event system and a TCP library emulating the core functions from the net module in node.js. What does this mean in practice? For starters it means running this example from the node.js documentation in no longer throws a stack trace:

var net = require('net');
var server = net.createServer(function(c) { //'connection' listener
  console.log('server connected');
  c.on('end', function() {
    console.log('server disconnected');
  });
  c.write('hello\r\n');
  c.pipe(c);
});
server.listen(8124, function() { //'listening' listener
  console.log('server bound');
});

More profoundly, each ‘connection’ event gets executed inside an actor whose work can be distributed across Rhinode instances, all transparently to the developer. Put another way, I’ve hit one of my short-term goals for this little toy project: to provide an almost seamless way for developers to transplant their JS to the JVM, scaling to multiple cores in the process, without having to resort to the somewhat less refined tools currently available to node.js. Happy hacking. Lots more to come…

GitHub repo is here.