Celluloid 0.2.0: a concurrent object framework for Ruby

Celluloid is a concurrent object framework for Ruby inspired by Erlang
and the Actor Model:

Celluloid wraps objects in threads, allowing them to run concurrently,
while still letting you talk to them using standard Ruby method call
conventions, in addition to providing asynchronous calls which run in
the background. This release brings with it two powerful new features
which extend Celluloid’s asynchronous capabilities even further:
method futures and signaling.

Method Futures:

All Celluloid objects now respond to the #future method. This method
takes the same parameters as #send (e.g. obj.future :method_name,
arg1, arg2, arg3) and immediately returns a Celluloid::Future object
while the requested method call executes in the background. When you
are ready to obtain the value returned from the method, you invoke
Celluloid::Future#value, which will automatically block until the
method call completes, or will return immediately if the method call
has already completed. This provides an easy way to initiate a method
call, do something else, and obtain the result at a later point in
time. An example use case is scatter/gather operations where you wish
to spread a long computation (or an I/O bound operation) across
multiple threads, then collect all the results later. Here’s some
pseudocode demonstrating the concept:

objects = (1…10).map { |n| MyCelluloidObject.new(n) }
futures = objects.map { |obj| obj.future
:do_something_crazy_with_my_number }
results = futures.map { |future| future.value }

This creates 10 concurrent Celluloid objects each running in their own
thread, then initiates the #do_something_crazy_with_my_number. What
sort of crazy things could this method do? It could fetch the nth page
of Google results for a particular search term. It could try to brute
force an MD5 hash with n leading zeroes. The #future method will kick
off this long-running computation in an asynchronous, non-blocking
manner, allowing the caller to scatter the given method call across 10
different objects, then gather the results by calling future.value on
each of the Celluloid::Future objects returned from the original
obj.future calls.

Signaling:

Every Celluloid object now responds to the #signal and #wait methods.
Each of these methods takes a name as a first parameter. Calling #wait
:my_signal_name suspends execution of the current method, allowing it
to process other incoming method calls (Celluloid only processes one
method at a time and maintains a queue of incoming calls). Another
method (or object) can unblock the #waiting method by calling #signal
:my_signal_name. #signal optionally accepts a value as a second
parameter (e.g. #signal :my_signal_name, 42) which is returned from
the corresponding #wait call. Several methods can be #waiting at the
same time. This allows objects processing a synchronous call to wait
until some outside event occurs before returning a result, similar to
ConditionVariables in threaded programming. See the Celluloid README
for examples.

Celluloid now provides asynchronous alternatives to all parts of the
method dispatch cycle:

  • You can “fire and forget”, invoking methods asynchronously and
    ignoring the values they return
  • You can asynchronously invoke a method and obtain its return value
    later via a future
  • Method calls in-progress can voluntarily suspend themselves waiting
    for an asynchronous signal before returning a value

Enjoy!