1. Actors - Concurrent Rubinius

NOTE this document is outdated in parts referencing the older implementation
of Rubinius that was based on the stackless execution model.

2. Background

Rubinius is shaping up to be quite a nice platform for networking and
distributed computing.  The virtual machine uses
[libev](http://software.schmorp.de/pkg/libev.html) at the foundation, which
provides a high performance event loop, timers, and high speed, asynchronous
IO using epoll and kqueue.  A lot of this functionality is utilized through
the Channel class, an elegant mechanism that comes from the
[π-calculus](http://en.wikipedia.org/wiki/Pi-calculus) that serves a number of
roles in Rubinius.

3. Channel

First, you can treat a Channel like a thread safe queue that lets you push an
item onto the tail from any thread, and then receive the item at the head from
any thread:

    c = Rubinius::Channel.new

    Thread.new do
      c.send "foo"
    end

    Thread.new do
      puts c.receive
    end

Unless you schedule a timeout (e.g. `Scheduler.send_in_seconds(...)`) the call
to `Channel#receive` will block until a new item is available.  If multiple
threads are blocked on the same channel then they will be unblocked one at a
time, in the order of arrival, as new items become available.  This
functionality means the Channel can also serve as a mutex:

    lock = Rubinius::Channel.new

    #...

    lock.receive
    begin
      run_shared_code
    ensure
      lock << nil
    end

or when sharing a set of resources a Channel can act as a counting semaphore.
Say a pool of threads are sharing a limited number of database connections:

    # Initialize the semaphore by writing a value per available resource
    sem = Rubinius::Channel.new
    NUM_CONNECTIONS.times { sem << nil }

    #...

    NUM_THREADS.times do
      Thread.new do
        sem.receive
        begin
          do_db_stuff
        ensure
          sem << nil
        end
      end
    end

So this is all pretty cool, but everyone knows that multi-threaded programming
is just error prone.  In simple situations where you don't need to share
resources between threads it's not so tricky, but for more complex occasions
threads can get really tough.  Beyond that, if you try to uses multiple
threaded libraries you can run into extremely difficult to debug problems.

4. Actors

One answer to this problem is the [actor
model](http://en.wikipedia.org/wiki/Actor_model).  In this model an actor is a
process that comes with a special mailbox.  Actors communicate by sending
messages to each other, and on reception of a message they can do local
processing, send more messages, or spawn additional actors.  Besides sending
messages actors should not access any shared resources, which greatly
simplifies the concurrency problems that can arise.  Additionally, it makes it
easy to run actors in parallel on multi-core machines, or across a network on
multiple boxes.  

4.1. Current Status

Rubinius has initial support for local actors now, and we are working on
building them up to be the foundation for a full fledged distributed computing
framework.  This is an outline of the currently envisioned design:

![actors.png](images/actors.png)

5. Glossary

*Actor*

A lightweight thread with a mailbox.

*VMManager*

An Actor that accepts inter-vm messages over a Channel, and then forwards them
on to whichever Actor they are destined for within its VM instance.

*NodeManager*

An Actor that accepts inter-process messages over a Socket, and then forwards
them on to whichever VM they are destined for within its process.

*VMActorProxy*

A proxy object that lets actors transparently send messages to other actors
that are located on another VM instance within the same process.  This proxy
is created as necessary whenever an Actor is received across a VM boundary,
whether through a mailbox message or by looking up a named service.

*NetActorProxy*

A proxy object that lets actors transparently send messages to other actors
that are located in another process, typically across a network.  This proxy
is created as necessary whenever an Actor is received across a process
boundary, whether through a mailbox message or by looking up a named service.

