Evented IO for Celluloid actors
Pull request Compare This branch is 403 commits behind celluloid:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



Build Status Dependency Status

You don't have to choose between threaded and evented IO! Celluloid::IO provides a simple and easy way to wait for IO events inside of a Celluloid actor. Underneath an event loop waits for activity on several IO objects at once, allowing a single Celluloid::IO actor running in a single thread to service multiple network connections simultaneously.

It's a somewhat similar idea to Ruby event frameworks like EventMachine and Cool.io, but Celluloid actors automatically wrap up all IO in Fibers, resulting in a synchronous API that's duck type compatible with the existing TCPServer and TCPSocket APIs.

Celluloid::IO uses the nio4r gem to monitor IO objects, which provides access to the high-performance epoll and kqueue system calls across a wide range of Ruby VMs, including MRI/YARV, JRuby, and Rubinius. For more information, please see the nio4r github page.

Unlike systems such as EventMachine and Node.js, which provide a singleton "god loop" for the entire process, you can make as many Celluloid::IO actors as you wish (system resources providing), which each run in their own independent thread.

Using many actors allows your program to scale across multiple CPU cores on Ruby implementations which don't have a GIL, such as JRuby and Rubinius.

Like Celluloid::IO? Join the Google Group

Supported Platforms

Celluloid::IO works on Ruby 1.9.2+, JRuby 1.6 (in 1.9 mode), and Rubinius 2.0.

To use JRuby in 1.9 mode, you'll need to pass the "--1.9" command line option to the JRuby executable, or set the "JRUBY_OPTS=--1.9" environment variable.


To use Celluloid::IO, define a normal Ruby class that includes Celluloid::IO. The following is an example of an echo server:

require 'celluloid/io'

class EchoServer
  include Celluloid::IO

  def initialize(host, port)
    puts "*** Starting echo server on #{host}:#{port}"

    # Since we included Celluloid::IO, we're actually making a
    # Celluloid::IO::TCPServer here
    @server = TCPServer.new(host, port)

  def finalize
    @server.close if @server

  def run
    loop { handle_connection! @server.accept }

  def handle_connection(socket)
    _, port, host = socket.peeraddr
    puts "*** Received connection from #{host}:#{port}"
    loop { socket.write socket.readpartial(4096) }
  rescue EOFError
    puts "*** #{host}:#{port} disconnected"

The very first thing including Celluloid::IO does is also include the Celluloid module, which promotes objects of this class to concurrent Celluloid actors each running in their own thread. Before trying to use Celluloid::IO you may want to familiarize yourself with Celluloid in general. Celluloid actors can each be thought of as being event loops. Celluloid::IO actors are heavier but have capabilities similar to other event loop-driven frameworks.

While this looks like a normal Ruby TCP server, there aren't any threads, so you might expect this server can only handle one connection at a time. However, this is all you need to do to build servers that handle as many connections as you want, and it happens all within a single thread.

The magic in this server which allows it to handle multiple connections comes in three forms:

  • Replacement classes: Celluloid::IO includes replacements for the core TCPServer and TCPSocket classes which automatically use an evented mode inside of Celluloid::IO actors. They're named Celluloid::IO::TCPServer and Celluloid::IO::TCPSocket, so they're automatically available inside your class when you include Celluloid::IO.

  • Asynchronous method calls: You may have noticed that while the methods of EchoServer are named run and handle_connection, they're invoked as run! and handle_connection!. This queues these methods to be executed after the current method is complete. You can queue up as many methods as you want, allowing asynchronous operation similar to the "call later" or "next tick" feature of Twisted, EventMachine, and Node. This echo server first kicks off a background task for accepting connections on the server socket, then kicks off a background task for each connection.

  • Reactor + Fibers: Celluloid::IO is a combination of Actor and Reactor concepts. The blocking mechanism used by the mailboxes of Celluloid::IO actors is a full-fledged reactor. When the current task needs to make a blocking I/O call, it first makes a non-blocking attempt, and if the socket isn't ready the current task is suspended until the reactor detects the operation is ready and resumes the suspended task.

The result is an API for doing evented I/O that looks identical to doing synchronous I/O. Adapting existing synchronous libraries to using evented I/O is as simple as having them use one of Celluloid::IO's provided replacement classes instead of the core Ruby TCPSocket and TCPServer classes.


Celluloid::IO::TCPServer is mostly complete, although only #accept is tested at the moment.

Celluloid::IO::TCPSocket is extremely shaky, untested, proof-of-concept code at this point but appears to mostly work. It's partially tested by the existing TCPServer#accept test. Real tests coming soon!

No UDP or UNIXSocket support yet. Also coming soon!

Filesystem monitoring is also likely on the horizon.

Contributing to Celluloid::IO

  • Fork this repository on github
  • Make your changes and send me a pull request
  • If I like them I'll merge them
  • If I've accepted a patch, feel free to ask for a commit bit!


Copyright (c) 2011 Tony Arcieri. Distributed under the MIT License. See LICENSE.txt for further details.

Contains code originally from the RubySpec project also under the MIT License Copyright (c) 2008 Engine Yard, Inc. All rights reserved.