Skip to content

Latest commit

 

History

History
120 lines (83 loc) · 6.45 KB

architecture.md

File metadata and controls

120 lines (83 loc) · 6.45 KB

Architecture

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:

  1. Document all major classes by describing the purpose of each one.
  2. Document the dependencies between classes described in item 1.
  3. Describe the flow of messages between the classes documented in items 1 and 2.
  4. Generate a Big Picture overview of the overall Celluloid architecture.
  5. Create a flew graphics to depict class dependencies and message flows (perhaps as a directed acyclic graph).

Document Status

The document is working on item 1.

Required Libraries

logger thread timeout set

Require Lifecycle

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'.

Supervision Lifecycle

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.

Classes / Modules

Celluloid Module

  • Sets up class accessors used throughout/globally such as logger
  • When using include Celluloid on a class, it performs the following work during include:
    • Extends ClassMethods and Internals::Properties
    • Includes InstanceMethods
    • Sets properties on the class object.
    • Removes trap_exit and exclusive if they exist on the singleton class so that Celluloid can redefine them for itself.
  • Defines class methods inside the Celluloid namespace such as actor? and mailbox and cores. These are utility functions. They are defined on the Celluloid singleton.
  • Provides the entry method boot to start up the whole system and its opposite shutdown to terminate everything.

Depends On Classes

  • Internals::Logger
  • Internals::CallChain
  • Actor::System
  • Celluloid::Mailbox
  • Thread (primarily for Thread.current to access fiber locals like :celluloid_actor)
  • Future

ClassMethods Module

This class contains class-level methods which are added to every user class that contains include Celluloid.

  • Overrides new for the class object.

Depends On Classes

  • Cell
  • Actor
  • Celluloid

InstanceMethods Module

This module contains instance-level methods which are added to every user class that contains include Celluloid.

Depends on Classes

  • Actor
  • Celluloid

Actor::System class

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.

Lifecycle

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.

Depends On Classes

  • 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

Gem - celluloid-supervision

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::Service::Root

Depends On Classes

  • Supervision::Container

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.

Depends On Classes

  • Supervision::Configuration