lib/celluloid.rb in celluloid-0.12.4 vs lib/celluloid.rb in celluloid-0.13.0.pre

- old
+ new

@@ -1,50 +1,49 @@ require 'logger' require 'thread' require 'timeout' require 'set' -require 'facter' module Celluloid extend self # expose all instance methods as singleton methods # How long actors have to terminate - SHUTDOWN_TIMEOUT = 120 + SHUTDOWN_TIMEOUT = 10 # Warning message added to Celluloid objects accessed outside their actors BARE_OBJECT_WARNING_MESSAGE = "WARNING: BARE CELLULOID OBJECT " class << self - attr_accessor :logger # Thread-safe logger class - attr_accessor :task_class # Default task type to use + attr_accessor :internal_pool # Internal thread pool + attr_accessor :logger # Thread-safe logger class + attr_accessor :task_class # Default task type to use def included(klass) klass.send :extend, ClassMethods klass.send :include, InstanceMethods end # Are we currently inside of an actor? def actor? - !!Thread.current[:actor] + !!Thread.current[:celluloid_actor] end # Generate a Universally Unique Identifier def uuid UUID.generate end # Obtain the number of CPUs in the system def cores - core_count = Facter.fact(:processorcount).value - Integer(core_count) + CPUCounter.cores end alias_method :cpus, :cores alias_method :ncpus, :cores # Perform a stack dump of all actors to the given output object def stack_dump(output = STDERR) - Celluloid::StackDumper.dump(output) + Celluloid::StackDump.new.dump(output) end alias_method :dump, :stack_dump # Define an exception handler for actor crashes def exception_handler(&block) @@ -75,26 +74,15 @@ end end Logger.debug "Shutdown completed cleanly" end + rescue Timeout::Error => ex + Logger.error("Couldn't cleanly terminate all actors in #{SHUTDOWN_TIMEOUT} seconds!") end end - # Terminate all actors at exit - at_exit do - if defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" && RUBY_VERSION >= "1.9" - # workaround for MRI bug losing exit status in at_exit block - # http://bugs.ruby-lang.org/issues/5218 - exit_status = $!.status if $!.is_a?(SystemExit) - shutdown - exit exit_status if exit_status - else - shutdown - end - end - # Class methods added to classes which include Celluloid module ClassMethods # Create a new actor def new(*args, &block) proxy = Actor.new(allocate, actor_options).proxy @@ -155,26 +143,44 @@ superclass.exit_handler end end alias_method :trap_exit, :exit_handler - # Configure a custom mailbox factory - def use_mailbox(klass = nil, &block) - if block - @mailbox_factory = block - else - mailbox_class(klass) + # Define a callback to run when the actor is finalized. + def finalizer(callback = nil) + if callback + @finalizer = callback.to_sym + elsif defined?(@finalizer) + @finalizer + elsif superclass.respond_to? :finalizer + superclass.finalizer end end # Define the mailbox class for this class - def mailbox_class(klass) - @mailbox_factory = proc { klass.new } + def mailbox_class(klass = nil) + if klass + @mailbox_class = klass + elsif defined?(@mailbox_class) + @mailbox_class + elsif superclass.respond_to? :mailbox_class + superclass.mailbox_class + else + Celluloid::Mailbox + end end - def proxy_class(klass) - @proxy_factory = proc { klass } + def proxy_class(klass = nil) + if klass + @proxy_class = klass + elsif defined?(@proxy_class) + @proxy_class + elsif superclass.respond_to? :proxy_class + superclass.proxy_class + else + Celluloid::ActorProxy + end end # Define the default task type for this class def task_class(klass = nil) if klass @@ -196,39 +202,18 @@ @exclusive_methods ||= Set.new @exclusive_methods.merge methods.map(&:to_sym) end end - # Create a mailbox for this actor - def mailbox_factory - if defined?(@mailbox_factory) - @mailbox_factory.call - elsif superclass.respond_to? :mailbox_factory - superclass.mailbox_factory - else - Mailbox.new - end - end - - def proxy_factory - if defined?(@proxy_factory) - @proxy_factory.call - elsif superclass.respond_to?(:proxy_factory) - superclass.proxy_factory - else - nil - end - end - # Configuration options for Actor#new def actor_options { - :mailbox => mailbox_factory, - :proxy_class => proxy_factory, + :mailbox => mailbox_class.new, + :proxy_class => proxy_class, + :task_class => task_class, :exit_handler => exit_handler, - :exclusive_methods => @exclusive_methods, - :task_class => task_class + :exclusive_methods => @exclusive_methods } end def ===(other) other.kind_of? self @@ -254,20 +239,20 @@ def bare_object; self; end alias_method :wrapped_object, :bare_object # Are we being invoked in a different thread from our owner? def leaked? - @celluloid_owner != Thread.current[:actor] + @celluloid_owner != Thread.current[:celluloid_actor] end def inspect str = "#<" if leaked? str << Celluloid::BARE_OBJECT_WARNING_MESSAGE else - str << "Celluloid::Actor" + str << "Celluloid::ActorProxy" end str << "(#{self.class}:0x#{object_id.to_s(16)})" str << " " unless instance_variables.empty? @@ -276,33 +261,10 @@ str << "#{ivar}=#{instance_variable_get(ivar).inspect} " end str.sub!(/\s$/, '>') end - - # Process async calls via method_missing - def method_missing(meth, *args, &block) - # bang methods are async calls - if meth.to_s.match(/!$/) - unbanged_meth = meth.to_s.sub(/!$/, '') - args.unshift unbanged_meth - - call = AsyncCall.new(:__send__, args, block) - begin - Thread.current[:actor].mailbox << call - rescue MailboxError - # Silently swallow asynchronous calls to dead actors. There's no way - # to reliably generate DeadActorErrors for async calls, so users of - # async calls should find other ways to deal with actors dying - # during an async call (i.e. linking/supervisors) - end - - return - end - - super - end end # # The following methods are available on both the Celluloid singleton and # directly inside of all classes that include Celluloid @@ -318,41 +280,46 @@ raise AbortError.new(cause) end # Terminate this actor def terminate - Thread.current[:actor].terminate + Thread.current[:celluloid_actor].terminate end # Send a signal with the given name to all waiting methods def signal(name, value = nil) - Thread.current[:actor].signal name, value + Thread.current[:celluloid_actor].signal name, value end # Wait for the given signal def wait(name) - Thread.current[:actor].wait name + Thread.current[:celluloid_actor].wait name end # Obtain the current_actor def current_actor Actor.current end + # Obtain the UUID of the current call chain + def call_chain_id + Thread.current[:celluloid_chain_id] + end + # Obtain the name of the current actor def name Actor.name end # Obtain the running tasks for this actor def tasks - Thread.current[:actor].tasks.to_a + Thread.current[:celluloid_actor].tasks.to_a end # Obtain the Celluloid::Links for this actor def links - Thread.current[:actor].links + Thread.current[:celluloid_actor].links end # Watch for exit events from another actor def monitor(actor) Actor.monitor(actor) @@ -383,48 +350,48 @@ Actor.linked_to?(actor) end # Receive an asynchronous message via the actor protocol def receive(timeout = nil, &block) - actor = Thread.current[:actor] + actor = Thread.current[:celluloid_actor] if actor actor.receive(timeout, &block) else Thread.mailbox.receive(timeout, &block) end end # Sleep letting the actor continue processing messages def sleep(interval) - actor = Thread.current[:actor] + actor = Thread.current[:celluloid_actor] if actor actor.sleep(interval) else Kernel.sleep interval end end # Run given block in an exclusive mode: all synchronous calls block the whole # actor, not only current message processing. def exclusive(&block) - Thread.current[:actor].exclusive(&block) + Thread.current[:celluloid_actor].exclusive(&block) end # Are we currently exclusive def exclusive? - actor = Thread.current[:actor] + actor = Thread.current[:celluloid_actor] actor && actor.exclusive? end # Call a block after a given interval, returning a Celluloid::Timer object def after(interval, &block) - Thread.current[:actor].after(interval, &block) + Thread.current[:celluloid_actor].after(interval, &block) end # Call a block every given interval, returning a Celluloid::Timer object def every(interval, &block) - Thread.current[:actor].every(interval, &block) + Thread.current[:celluloid_actor].every(interval, &block) end # Perform a blocking or computationally intensive action inside an # asynchronous thread pool, allowing the caller to continue processing other # messages in its mailbox in the meantime @@ -435,30 +402,32 @@ end # Handle async calls within an actor itself def async(meth = nil, *args, &block) if meth - Actor.async Thread.current[:actor].mailbox, meth, *args, &block + Actor.async Thread.current[:celluloid_actor].mailbox, meth, *args, &block else - Thread.current[:actor].proxy.async + Thread.current[:celluloid_actor].proxy.async end end # Handle calls to future within an actor itself def future(meth = nil, *args, &block) if meth - Actor.future Thread.current[:actor].mailbox, meth, *args, &block + Actor.future Thread.current[:celluloid_actor].mailbox, meth, *args, &block else - Thread.current[:actor].proxy.future + Thread.current[:celluloid_actor].proxy.future end end end require 'celluloid/version' require 'celluloid/calls' +require 'celluloid/condition' require 'celluloid/core_ext' +require 'celluloid/cpu_counter' require 'celluloid/fiber' require 'celluloid/fsm' require 'celluloid/internal_pool' require 'celluloid/links' require 'celluloid/logger' @@ -466,13 +435,13 @@ require 'celluloid/method' require 'celluloid/receivers' require 'celluloid/registry' require 'celluloid/responses' require 'celluloid/signals' -require 'celluloid/stack_dumper' +require 'celluloid/stack_dump' require 'celluloid/system_events' -require 'celluloid/task' +require 'celluloid/tasks' require 'celluloid/thread_handle' require 'celluloid/uuid' require 'celluloid/proxies/abstract_proxy' require 'celluloid/proxies/actor_proxy' @@ -485,6 +454,7 @@ require 'celluloid/supervision_group' require 'celluloid/supervisor' require 'celluloid/notifications' require 'celluloid/logging' +require 'celluloid/legacy' unless defined?(CELLULOID_FUTURE) require 'celluloid/boot'