celluloid 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +14 -0
 - data/.document +5 -0
 - data/.rspec +3 -0
 - data/Gemfile +4 -0
 - data/Gemfile.lock +34 -0
 - data/LICENSE.txt +20 -0
 - data/README.md +286 -0
 - data/Rakefile +51 -0
 - data/VERSION +1 -0
 - data/celluloid.gemspec +87 -0
 - data/lib/celluloid.rb +19 -0
 - data/lib/celluloid/actor.rb +179 -0
 - data/lib/celluloid/actor_proxy.rb +81 -0
 - data/lib/celluloid/calls.rb +56 -0
 - data/lib/celluloid/core_ext.rb +6 -0
 - data/lib/celluloid/events.rb +14 -0
 - data/lib/celluloid/future.rb +28 -0
 - data/lib/celluloid/linking.rb +75 -0
 - data/lib/celluloid/mailbox.rb +97 -0
 - data/lib/celluloid/registry.rb +33 -0
 - data/lib/celluloid/responses.rb +16 -0
 - data/lib/celluloid/supervisor.rb +40 -0
 - data/lib/celluloid/waker.rb +41 -0
 - data/spec/actor_spec.rb +141 -0
 - data/spec/future_spec.rb +20 -0
 - data/spec/mailbox_spec.rb +50 -0
 - data/spec/registry_spec.rb +22 -0
 - data/spec/spec_helper.rb +9 -0
 - data/spec/supervisor_spec.rb +94 -0
 - data/spec/waker_spec.rb +34 -0
 - metadata +138 -0
 
    
        data/lib/celluloid.rb
    ADDED
    
    | 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Celluloid
         
     | 
| 
      
 2 
     | 
    
         
            +
              VERSION = File.read File.expand_path('../../VERSION', __FILE__)
         
     | 
| 
      
 3 
     | 
    
         
            +
              
         
     | 
| 
      
 4 
     | 
    
         
            +
              def self.version; VERSION; end
         
     | 
| 
      
 5 
     | 
    
         
            +
            end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            require 'celluloid/actor'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'celluloid/actor_proxy'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'celluloid/calls'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'celluloid/core_ext'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require 'celluloid/events'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require 'celluloid/linking'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require 'celluloid/mailbox'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'celluloid/registry'
         
     | 
| 
      
 15 
     | 
    
         
            +
            require 'celluloid/responses'
         
     | 
| 
      
 16 
     | 
    
         
            +
            require 'celluloid/supervisor'
         
     | 
| 
      
 17 
     | 
    
         
            +
            require 'celluloid/waker'
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            require 'celluloid/future'
         
     | 
| 
         @@ -0,0 +1,179 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Celluloid
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Raised when trying to do Actor-like things outside Actor-space
         
     | 
| 
      
 3 
     | 
    
         
            +
              class NotActorError < StandardError; end
         
     | 
| 
      
 4 
     | 
    
         
            +
              
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Raised when we're asked to do something to a dead actor
         
     | 
| 
      
 6 
     | 
    
         
            +
              class DeadActorError < StandardError; end
         
     | 
| 
      
 7 
     | 
    
         
            +
              
         
     | 
| 
      
 8 
     | 
    
         
            +
              # Raised when the caller makes an error that shouldn't crash this actor
         
     | 
| 
      
 9 
     | 
    
         
            +
              class AbortError < StandardError
         
     | 
| 
      
 10 
     | 
    
         
            +
                attr_reader :cause
         
     | 
| 
      
 11 
     | 
    
         
            +
                
         
     | 
| 
      
 12 
     | 
    
         
            +
                def initialize(cause)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @cause = cause
         
     | 
| 
      
 14 
     | 
    
         
            +
                  super "caused by #{cause.inspect}: #{cause.to_s}"
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
              
         
     | 
| 
      
 18 
     | 
    
         
            +
              # Actors are Celluloid's concurrency primitive. They're implemented as
         
     | 
| 
      
 19 
     | 
    
         
            +
              # normal Ruby objects wrapped in threads which communicate with asynchronous
         
     | 
| 
      
 20 
     | 
    
         
            +
              # messages. The implementation is inspired by Erlang's gen_server
         
     | 
| 
      
 21 
     | 
    
         
            +
              module Actor
         
     | 
| 
      
 22 
     | 
    
         
            +
                attr_reader :mailbox
         
     | 
| 
      
 23 
     | 
    
         
            +
                
         
     | 
| 
      
 24 
     | 
    
         
            +
                # Methods added to classes which include Celluloid::Actor
         
     | 
| 
      
 25 
     | 
    
         
            +
                module ClassMethods
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # Retrieve the exit handler method for this class
         
     | 
| 
      
 27 
     | 
    
         
            +
                  attr_reader :exit_handler
         
     | 
| 
      
 28 
     | 
    
         
            +
                  
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # Create a new actor
         
     | 
| 
      
 30 
     | 
    
         
            +
                  def spawn(*args, &block)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    actor = allocate
         
     | 
| 
      
 32 
     | 
    
         
            +
                    proxy = actor.__start_actor
         
     | 
| 
      
 33 
     | 
    
         
            +
                    proxy.send(:initialize, *args, &block)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    proxy
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # Create a new actor and link to the current one
         
     | 
| 
      
 38 
     | 
    
         
            +
                  def spawn_link(*args, &block)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    current_actor = Thread.current[:actor]
         
     | 
| 
      
 40 
     | 
    
         
            +
                    raise NotActorError, "can't link outside actor context" unless current_actor
         
     | 
| 
      
 41 
     | 
    
         
            +
                    
         
     | 
| 
      
 42 
     | 
    
         
            +
                    # FIXME: this is a bit repetitive with the code above
         
     | 
| 
      
 43 
     | 
    
         
            +
                    actor = allocate
         
     | 
| 
      
 44 
     | 
    
         
            +
                    proxy = actor.__start_actor
         
     | 
| 
      
 45 
     | 
    
         
            +
                    current_actor.link actor
         
     | 
| 
      
 46 
     | 
    
         
            +
                    proxy.send(:initialize, *args, &block)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    
         
     | 
| 
      
 48 
     | 
    
         
            +
                    proxy
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # Create a supervisor which ensures an instance of an actor will restart
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # an actor if it fails
         
     | 
| 
      
 53 
     | 
    
         
            +
                  def supervise(*args, &block)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    Celluloid::Supervisor.supervise(self, *args, &block)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                  
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # Create a supervisor which ensures an instance of an actor will restart
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # an actor if it fails, and keep the actor registered under a given name
         
     | 
| 
      
 59 
     | 
    
         
            +
                  def supervise_as(name, *args, &block)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    Celluloid::Supervisor.supervise_as(name, self, *args, &block)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  
         
     | 
| 
      
 63 
     | 
    
         
            +
                  # Trap errors from actors we're linked to when they exit
         
     | 
| 
      
 64 
     | 
    
         
            +
                  def trap_exit(callback)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    @exit_handler = callback.to_sym
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
                
         
     | 
| 
      
 69 
     | 
    
         
            +
                # Instance methods added to the public API
         
     | 
| 
      
 70 
     | 
    
         
            +
                module InstanceMethods
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # Is this object functioning as an actor?
         
     | 
| 
      
 72 
     | 
    
         
            +
                  def actor?
         
     | 
| 
      
 73 
     | 
    
         
            +
                    !!@mailbox
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
                  
         
     | 
| 
      
 76 
     | 
    
         
            +
                  # Is this actor alive?
         
     | 
| 
      
 77 
     | 
    
         
            +
                  def alive?
         
     | 
| 
      
 78 
     | 
    
         
            +
                    @thread.alive?
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
                  
         
     | 
| 
      
 81 
     | 
    
         
            +
                  # Raise an exception in caller context, but stay running
         
     | 
| 
      
 82 
     | 
    
         
            +
                  def abort(cause)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    raise AbortError.new(cause)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                  
         
     | 
| 
      
 86 
     | 
    
         
            +
                  def inspect
         
     | 
| 
      
 87 
     | 
    
         
            +
                    return super unless actor?
         
     | 
| 
      
 88 
     | 
    
         
            +
                    str = "#<Celluloid::Actor(#{self.class}:0x#{self.object_id.to_s(16)})"
         
     | 
| 
      
 89 
     | 
    
         
            +
                    
         
     | 
| 
      
 90 
     | 
    
         
            +
                    ivars = []
         
     | 
| 
      
 91 
     | 
    
         
            +
                    instance_variables.each do |ivar|
         
     | 
| 
      
 92 
     | 
    
         
            +
                      next if %w(@mailbox @links @proxy @thread).include? ivar.to_s
         
     | 
| 
      
 93 
     | 
    
         
            +
                      ivars << "#{ivar}=#{instance_variable_get(ivar).inspect}"
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
                    
         
     | 
| 
      
 96 
     | 
    
         
            +
                    str << " " << ivars.join(' ') unless ivars.empty?      
         
     | 
| 
      
 97 
     | 
    
         
            +
                    str << ">"
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
                
         
     | 
| 
      
 101 
     | 
    
         
            +
                # Internal methods not intended as part of the public API
         
     | 
| 
      
 102 
     | 
    
         
            +
                module InternalMethods
         
     | 
| 
      
 103 
     | 
    
         
            +
                  # Actor-specific initialization and startup
         
     | 
| 
      
 104 
     | 
    
         
            +
                  def __start_actor(*args, &block)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    @mailbox = Mailbox.new
         
     | 
| 
      
 106 
     | 
    
         
            +
                    @links   = Links.new
         
     | 
| 
      
 107 
     | 
    
         
            +
                    @proxy   = ActorProxy.new(self, @mailbox)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    
         
     | 
| 
      
 109 
     | 
    
         
            +
                    @thread  = Thread.new do
         
     | 
| 
      
 110 
     | 
    
         
            +
                      Thread.current[:actor]   = self
         
     | 
| 
      
 111 
     | 
    
         
            +
                      Thread.current[:mailbox] = @mailbox
         
     | 
| 
      
 112 
     | 
    
         
            +
                      __run_actor
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
                    
         
     | 
| 
      
 115 
     | 
    
         
            +
                    @proxy
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
                            
         
     | 
| 
      
 118 
     | 
    
         
            +
                  # Run the actor
         
     | 
| 
      
 119 
     | 
    
         
            +
                  def __run_actor
         
     | 
| 
      
 120 
     | 
    
         
            +
                    __process_messages
         
     | 
| 
      
 121 
     | 
    
         
            +
                  rescue Exception => ex
         
     | 
| 
      
 122 
     | 
    
         
            +
                    __handle_crash(ex)
         
     | 
| 
      
 123 
     | 
    
         
            +
                  end
         
     | 
| 
      
 124 
     | 
    
         
            +
                
         
     | 
| 
      
 125 
     | 
    
         
            +
                  # Process incoming messages
         
     | 
| 
      
 126 
     | 
    
         
            +
                  def __process_messages
         
     | 
| 
      
 127 
     | 
    
         
            +
                    while true # instead of loop, for speed!
         
     | 
| 
      
 128 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 129 
     | 
    
         
            +
                        call = @mailbox.receive
         
     | 
| 
      
 130 
     | 
    
         
            +
                      rescue ExitEvent => event
         
     | 
| 
      
 131 
     | 
    
         
            +
                        __handle_exit(event)
         
     | 
| 
      
 132 
     | 
    
         
            +
                        retry
         
     | 
| 
      
 133 
     | 
    
         
            +
                      end
         
     | 
| 
      
 134 
     | 
    
         
            +
                        
         
     | 
| 
      
 135 
     | 
    
         
            +
                      call.dispatch(self)
         
     | 
| 
      
 136 
     | 
    
         
            +
                    end
         
     | 
| 
      
 137 
     | 
    
         
            +
                  end
         
     | 
| 
      
 138 
     | 
    
         
            +
                  
         
     | 
| 
      
 139 
     | 
    
         
            +
                  # Handle exit events received by this actor
         
     | 
| 
      
 140 
     | 
    
         
            +
                  def __handle_exit(exit_event)
         
     | 
| 
      
 141 
     | 
    
         
            +
                    exit_handler = self.class.exit_handler
         
     | 
| 
      
 142 
     | 
    
         
            +
                    raise exit_event.reason unless exit_handler
         
     | 
| 
      
 143 
     | 
    
         
            +
                    
         
     | 
| 
      
 144 
     | 
    
         
            +
                    send exit_handler, exit_event.actor, exit_event.reason
         
     | 
| 
      
 145 
     | 
    
         
            +
                  end
         
     | 
| 
      
 146 
     | 
    
         
            +
                
         
     | 
| 
      
 147 
     | 
    
         
            +
                  # Handle any exceptions that occur within a running actor
         
     | 
| 
      
 148 
     | 
    
         
            +
                  def __handle_crash(exception)
         
     | 
| 
      
 149 
     | 
    
         
            +
                    __log_error(exception)
         
     | 
| 
      
 150 
     | 
    
         
            +
                    @mailbox.cleanup
         
     | 
| 
      
 151 
     | 
    
         
            +
                    
         
     | 
| 
      
 152 
     | 
    
         
            +
                    # Report the exit event to all actors we're linked to
         
     | 
| 
      
 153 
     | 
    
         
            +
                    exit_event = ExitEvent.new(@proxy, exception)
         
     | 
| 
      
 154 
     | 
    
         
            +
                    
         
     | 
| 
      
 155 
     | 
    
         
            +
                    # Propagate the error to all linked actors
         
     | 
| 
      
 156 
     | 
    
         
            +
                    @links.each do |actor|
         
     | 
| 
      
 157 
     | 
    
         
            +
                      actor.mailbox.system_event exit_event
         
     | 
| 
      
 158 
     | 
    
         
            +
                    end
         
     | 
| 
      
 159 
     | 
    
         
            +
                  rescue Exception => handler_exception
         
     | 
| 
      
 160 
     | 
    
         
            +
                    __log_error(handler_exception, "/!\\ EXCEPTION IN ERROR HANDLER /!\\")
         
     | 
| 
      
 161 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 162 
     | 
    
         
            +
                    Thread.current.exit
         
     | 
| 
      
 163 
     | 
    
         
            +
                  end
         
     | 
| 
      
 164 
     | 
    
         
            +
                  
         
     | 
| 
      
 165 
     | 
    
         
            +
                  # Log errors when an actor crashes
         
     | 
| 
      
 166 
     | 
    
         
            +
                  # FIXME: This should probably thunk to a real logger
         
     | 
| 
      
 167 
     | 
    
         
            +
                  def __log_error(ex, prefix = "!!! CRASH")
         
     | 
| 
      
 168 
     | 
    
         
            +
                    puts "#{prefix} #{self.class}: #{ex.class}: #{ex.to_s}\n#{ex.backtrace.join("\n")}"
         
     | 
| 
      
 169 
     | 
    
         
            +
                  end
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
              
         
     | 
| 
      
 172 
     | 
    
         
            +
                def self.included(klass)
         
     | 
| 
      
 173 
     | 
    
         
            +
                  klass.extend ClassMethods
         
     | 
| 
      
 174 
     | 
    
         
            +
                  klass.send :include, InstanceMethods
         
     | 
| 
      
 175 
     | 
    
         
            +
                  klass.send :include, InternalMethods
         
     | 
| 
      
 176 
     | 
    
         
            +
                  klass.send :include, Linking
         
     | 
| 
      
 177 
     | 
    
         
            +
                end
         
     | 
| 
      
 178 
     | 
    
         
            +
              end
         
     | 
| 
      
 179 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,81 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Celluloid
         
     | 
| 
      
 2 
     | 
    
         
            +
              # A proxy object returned from Celluloid::Actor.spawn/spawn_link which
         
     | 
| 
      
 3 
     | 
    
         
            +
              # dispatches calls and casts to normal Ruby objects which are running inside
         
     | 
| 
      
 4 
     | 
    
         
            +
              # of their own threads.
         
     | 
| 
      
 5 
     | 
    
         
            +
              class ActorProxy
         
     | 
| 
      
 6 
     | 
    
         
            +
                # FIXME: not nearly enough methods are delegated here
         
     | 
| 
      
 7 
     | 
    
         
            +
                attr_reader :mailbox
         
     | 
| 
      
 8 
     | 
    
         
            +
                
         
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize(actor, mailbox)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @actor, @mailbox = actor, mailbox
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
                
         
     | 
| 
      
 13 
     | 
    
         
            +
                def send(meth, *args, &block)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  __call :send, meth, *args, &block
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
                
         
     | 
| 
      
 17 
     | 
    
         
            +
                def respond_to?(meth)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  __call :respond_to?, meth
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
                def methods(include_ancestors = true)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  __call :methods, include_ancestors
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
                
         
     | 
| 
      
 25 
     | 
    
         
            +
                def alive?
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @actor.alive?
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
                
         
     | 
| 
      
 29 
     | 
    
         
            +
                def inspect
         
     | 
| 
      
 30 
     | 
    
         
            +
                  return __call :inspect if alive?
         
     | 
| 
      
 31 
     | 
    
         
            +
                  "#<Celluloid::Actor(#{@actor.class}:0x#{@actor.object_id.to_s(16)}) dead>"
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                
         
     | 
| 
      
 34 
     | 
    
         
            +
                # method_missing black magic to call bang predicate methods asynchronously
         
     | 
| 
      
 35 
     | 
    
         
            +
                def method_missing(meth, *args, &block)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # bang methods are async calls
         
     | 
| 
      
 37 
     | 
    
         
            +
                  if meth.to_s.match(/!$/) 
         
     | 
| 
      
 38 
     | 
    
         
            +
                    unbanged_meth = meth.to_s.sub(/!$/, '')
         
     | 
| 
      
 39 
     | 
    
         
            +
                    our_mailbox = Thread.current.mailbox
         
     | 
| 
      
 40 
     | 
    
         
            +
                    
         
     | 
| 
      
 41 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 42 
     | 
    
         
            +
                      @mailbox << AsyncCall.new(our_mailbox, unbanged_meth, args, block)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    rescue MailboxError
         
     | 
| 
      
 44 
     | 
    
         
            +
                      # Silently swallow asynchronous calls to dead actors. There's no way
         
     | 
| 
      
 45 
     | 
    
         
            +
                      # to reliably generate DeadActorErrors for async calls, so users of
         
     | 
| 
      
 46 
     | 
    
         
            +
                      # async calls should find other ways to deal with actors dying
         
     | 
| 
      
 47 
     | 
    
         
            +
                      # during an async call (i.e. linking/supervisors)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
                    
         
     | 
| 
      
 50 
     | 
    
         
            +
                    return # casts are async and return immediately
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  
         
     | 
| 
      
 53 
     | 
    
         
            +
                  __call(meth, *args, &block)
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
                
         
     | 
| 
      
 56 
     | 
    
         
            +
                # Make a synchronous call to the actor we're proxying to
         
     | 
| 
      
 57 
     | 
    
         
            +
                def __call(meth, *args, &block)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  our_mailbox = Thread.current.mailbox
         
     | 
| 
      
 59 
     | 
    
         
            +
                  call = SyncCall.new(our_mailbox, meth, args, block)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  
         
     | 
| 
      
 61 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 62 
     | 
    
         
            +
                    @mailbox << call
         
     | 
| 
      
 63 
     | 
    
         
            +
                  rescue MailboxError
         
     | 
| 
      
 64 
     | 
    
         
            +
                    raise DeadActorError, "attempted to call a dead actor"
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
                  
         
     | 
| 
      
 67 
     | 
    
         
            +
                  response = our_mailbox.receive do |msg|
         
     | 
| 
      
 68 
     | 
    
         
            +
                    msg.is_a? Response and msg.call == call
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                  
         
     | 
| 
      
 71 
     | 
    
         
            +
                  case response
         
     | 
| 
      
 72 
     | 
    
         
            +
                  when SuccessResponse
         
     | 
| 
      
 73 
     | 
    
         
            +
                    response.value
         
     | 
| 
      
 74 
     | 
    
         
            +
                  when ErrorResponse
         
     | 
| 
      
 75 
     | 
    
         
            +
                    raise response.value
         
     | 
| 
      
 76 
     | 
    
         
            +
                  else
         
     | 
| 
      
 77 
     | 
    
         
            +
                    raise "don't know how to handle #{response.class} messages!"
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
              end
         
     | 
| 
      
 81 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Celluloid
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Calls represent requests to an actor
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Call
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_reader :caller, :method, :arguments, :block
         
     | 
| 
      
 5 
     | 
    
         
            +
                
         
     | 
| 
      
 6 
     | 
    
         
            +
                def initialize(caller, method, arguments, block)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @caller, @method, @arguments, @block = caller, method, arguments, block
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
              
         
     | 
| 
      
 11 
     | 
    
         
            +
              # Synchronous calls wait for a response
         
     | 
| 
      
 12 
     | 
    
         
            +
              class SyncCall < Call
         
     | 
| 
      
 13 
     | 
    
         
            +
                def dispatch(obj)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  unless obj.respond_to? @method
         
     | 
| 
      
 15 
     | 
    
         
            +
                    exception = NoMethodError.new("undefined method `#{@method}' for #{obj.inspect}")
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @caller << ErrorResponse.new(self, exception)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    return
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  
         
     | 
| 
      
 20 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 21 
     | 
    
         
            +
                    result = obj.send @method, *@arguments, &@block
         
     | 
| 
      
 22 
     | 
    
         
            +
                  rescue AbortError => exception
         
     | 
| 
      
 23 
     | 
    
         
            +
                    # Aborting indicates a protocol error on the part of the caller
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # It should crash the caller, but the exception isn't reraised
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @caller << ErrorResponse.new(self, exception.cause)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    return
         
     | 
| 
      
 27 
     | 
    
         
            +
                  rescue Exception => exception
         
     | 
| 
      
 28 
     | 
    
         
            +
                    # Exceptions that occur during synchronous calls are reraised in the
         
     | 
| 
      
 29 
     | 
    
         
            +
                    # context of the caller
         
     | 
| 
      
 30 
     | 
    
         
            +
                    @caller << ErrorResponse.new(self, exception)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    
         
     | 
| 
      
 32 
     | 
    
         
            +
                    # They should also crash the actor where they occurred
         
     | 
| 
      
 33 
     | 
    
         
            +
                    raise exception
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                      
         
     | 
| 
      
 36 
     | 
    
         
            +
                  @caller << SuccessResponse.new(self, result)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  true
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
                
         
     | 
| 
      
 40 
     | 
    
         
            +
                def cleanup
         
     | 
| 
      
 41 
     | 
    
         
            +
                  exception = DeadActorError.new("attempted to call a dead actor")
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @caller << ErrorResponse.new(self, exception)
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
              
         
     | 
| 
      
 46 
     | 
    
         
            +
              # Asynchronous calls don't wait for a response
         
     | 
| 
      
 47 
     | 
    
         
            +
              class AsyncCall < Call
         
     | 
| 
      
 48 
     | 
    
         
            +
                def dispatch(obj)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  obj.send(@method, *@arguments, &@block) if obj.respond_to? @method
         
     | 
| 
      
 50 
     | 
    
         
            +
                rescue AbortError
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # Swallow aborted async calls, as they indicate the caller made a mistake
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # FIXME: this should probably get logged
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
            end
         
     | 
| 
      
 56 
     | 
    
         
            +
                
         
     | 
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Celluloid
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Exceptional system events which need to be processed out of band
         
     | 
| 
      
 3 
     | 
    
         
            +
              class SystemEvent < Exception; end
         
     | 
| 
      
 4 
     | 
    
         
            +
              
         
     | 
| 
      
 5 
     | 
    
         
            +
              # An actor has exited for the given reason
         
     | 
| 
      
 6 
     | 
    
         
            +
              class ExitEvent < SystemEvent
         
     | 
| 
      
 7 
     | 
    
         
            +
                attr_reader :actor, :reason
         
     | 
| 
      
 8 
     | 
    
         
            +
                
         
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize(actor, reason)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @actor, @reason = actor, reason
         
     | 
| 
      
 11 
     | 
    
         
            +
                  super reason.to_s
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Celluloid
         
     | 
| 
      
 2 
     | 
    
         
            +
              def self.Future(*args, &block)
         
     | 
| 
      
 3 
     | 
    
         
            +
                future = Celluloid::Future.spawn(*args, &block)
         
     | 
| 
      
 4 
     | 
    
         
            +
                future.run!
         
     | 
| 
      
 5 
     | 
    
         
            +
                future
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
              
         
     | 
| 
      
 8 
     | 
    
         
            +
              class Future
         
     | 
| 
      
 9 
     | 
    
         
            +
                include Celluloid::Actor
         
     | 
| 
      
 10 
     | 
    
         
            +
                
         
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(*args, &block)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @args, @block = args, block
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
                
         
     | 
| 
      
 15 
     | 
    
         
            +
                def run
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @called = true
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @value = @block[*@args]
         
     | 
| 
      
 18 
     | 
    
         
            +
                rescue Exception => error
         
     | 
| 
      
 19 
     | 
    
         
            +
                  @error = error
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
                
         
     | 
| 
      
 22 
     | 
    
         
            +
                def value
         
     | 
| 
      
 23 
     | 
    
         
            +
                  raise "not run yet" unless @called
         
     | 
| 
      
 24 
     | 
    
         
            +
                  abort @error if @error
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @value
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,75 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'thread'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Celluloid
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Thread safe storage of inter-actor links
         
     | 
| 
      
 6 
     | 
    
         
            +
              class Links
         
     | 
| 
      
 7 
     | 
    
         
            +
                include Enumerable
         
     | 
| 
      
 8 
     | 
    
         
            +
                
         
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @links = Set.new
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @lock  = Mutex.new
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
                
         
     | 
| 
      
 14 
     | 
    
         
            +
                def <<(actor)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  @lock.synchronize do
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @links << actor
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  actor
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
                def include?(actor)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @lock.synchronize do
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @links.include? actor
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
                
         
     | 
| 
      
 27 
     | 
    
         
            +
                def delete(actor)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @lock.synchronize do
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @links.delete actor
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  actor
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
                
         
     | 
| 
      
 34 
     | 
    
         
            +
                def each(&block)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @lock.synchronize do
         
     | 
| 
      
 36 
     | 
    
         
            +
                    @links.each(&block)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
                
         
     | 
| 
      
 40 
     | 
    
         
            +
                def inspect
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @lock.synchronize do
         
     | 
| 
      
 42 
     | 
    
         
            +
                    links = @links.to_a.map { |l| "#{l.class}:#{l.object_id}" }.join(',')
         
     | 
| 
      
 43 
     | 
    
         
            +
                    "#<Celluloid::Links[#{links}]>"
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
              
         
     | 
| 
      
 48 
     | 
    
         
            +
              # Support for linking actors together so they can crash or react to errors
         
     | 
| 
      
 49 
     | 
    
         
            +
              module Linking
         
     | 
| 
      
 50 
     | 
    
         
            +
                # Link this actor to another, allowing it to crash or react to errors
         
     | 
| 
      
 51 
     | 
    
         
            +
                def link(actor)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  actor.notify_link(@proxy)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  self.notify_link(actor)
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
                
         
     | 
| 
      
 56 
     | 
    
         
            +
                # Remove links to another actor
         
     | 
| 
      
 57 
     | 
    
         
            +
                def unlink(actor)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  actor.notify_unlink(@proxy)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  self.notify_unlink(actor)
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
                
         
     | 
| 
      
 62 
     | 
    
         
            +
                def notify_link(actor)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @links << actor
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
                
         
     | 
| 
      
 66 
     | 
    
         
            +
                def notify_unlink(actor)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @links.delete actor
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
                
         
     | 
| 
      
 70 
     | 
    
         
            +
                # Is this actor linked to another?
         
     | 
| 
      
 71 
     | 
    
         
            +
                def linked_to?(actor)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @links.include? actor
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
            end
         
     |