The purpose of this document is to provide an overview of the Celluloid
project architecture. It is intended as a learning tool for
GSoC (Google Summer of Code) and for anyone else interested in peeking under the hood to figure out how Celluloid works.
This document will evolve since the people writing it are learning the project too. It will likely evolve in the following manner:
- Document all major classes by describing the purpose of each one.
- Document the dependencies between classes described in item 1.
- Describe the flow of messages between the classes documented in items 1 and 2.
- Generate a Big Picture overview of the overall Celluloid architecture.
- Create a flew graphics to depict class dependencies and message flows (perhaps as a directed acyclic graph).
The document is working on item 1.
logger
thread
timeout
set
Document the steps taken from the moment require "celluloid"
is executed by the runtime.
The file celluloid.rb
is read in by the runtime. This file requires some standard libraries like Thread
and Set
(see Required Libraries for full list) and initializes the Celluloid
module namespace. It then sets up a Celluloid singleton class which will contain utility functions that need to be accessed from anywhere. Think of these singleton methods as global methods. These specific methods should be considered private to the library and should not be directly called by user code.
From here it continues to extend
and include
other modules so that any Ruby object that executes include Celluloid
as part of its class definition automatically gains all of the Celluloid magic.
Next, it defines regular methods within the Celluloid namespace. These are also global methods but they are essentially the public API to the outside world. These methods (such as current_actor
and terminate
) can be called by user code.
Next we have a list of require
'd subfiles. This loads the remainder of the library in preparation to start running.
Next, the code sets up two global default settings for the task_class
and group_class
. I don't know what these are yet but by exposing them this way it's clearly intended for these items to be defined in such a way that user code can override them with use-case-specific code. The code here can also read names from the environment variables to set the defaults. This is likely intended for use by the spec/test system.
Lastly, the code registers some methods for shutdown to terminate all actors at_exit
and then initializes the system.
To boot the system, call require 'celluloid'
.
The final call to Actor::System.new.start
kicks off the top level supervisor. The supervisor container is an actor and it will keep track of all defined actors as they are added for supervision. A user may create an actor that is not supervised, so the top-level supervisor does not necessarily have a reference to every actor in the system.
The Registry is also created at this time and lives within the main container. Actors can be added to the registry even if they are not supervised. The two concepts are separate even though the code is somewhat comingled.
- Sets up class accessors used throughout/globally such as
logger
- When using
include Celluloid
on a class, it performs the following work duringinclude
:- Extends
ClassMethods
andInternals::Properties
- Includes
InstanceMethods
- Sets properties on the class object.
- Removes
trap_exit
andexclusive
if they exist on the singleton class so that Celluloid can redefine them for itself.
- Extends
- Defines class methods inside the
Celluloid
namespace such asactor?
andmailbox
andcores
. These are utility functions. They are defined on the Celluloid singleton. - Provides the entry method
boot
to start up the whole system and its oppositeshutdown
to terminate everything.
- Internals::Logger
- Internals::CallChain
- Actor::System
- Celluloid::Mailbox
- Thread (primarily for Thread.current to access fiber locals like
:celluloid_actor
) - Future
This class contains class-level methods which are added to every user class that contains include Celluloid
.
- Overrides
new
for the class object.
- Cell
- Actor
- Celluloid
This module contains instance-level methods which are added to every user class that contains include Celluloid
.
- Actor
- Celluloid
Is created and start
'ed as part of the entire boot sequence. This class provides essential services utilized by the rest of the library such as the Registry and the top-level Supervisor.
Creates the Registry
and initializes the Celluloid.group_class
. Upon start
it makes sure that it switches the Thread.current[:celluloid_actor_system]
to itself, then defines and deploys the Root
services.
- Supervision::Service::Root which is in gem
celluloid-supervision
- Celluloid::Notifications::Fanout
- Celluloid::IncidentReporter
- Celluloid::Supervision::Service::Public
- Celluloid::Actor::Manager
- Celluloid
- Internals::Registry
- Celluloid.group_class
Necessary for the core system to boot.
Really has only two classes/modules of note. One is Supervision::Container which is an actor that keeps track of all actors under supervision. It also handles restart. Two is Supervision::Configuration. This class manages the several different ways that an actor may be placed under supervision, arguments passed to it, and other data.
Be careful when reading the code. Each file in the gem relies on open Ruby classes. These files reopen previously defined classes and add more behavior, so to get a complete picture for what the Configuration and Container classes can do you must look through all of the files. While this logical separation of business logic to its own file is neat, it adds to the cognitive load when reading the code since the entire class defintion is spread over multiple files.
- Supervision::Container
Stores references to Actors on behalf of a Supervisor.
This is the only class under the Supervision
namespace that is an Actor. Every other class is a regular Ruby class.
- Supervision::Configuration