celluloid-0.15.2/0000755000004100000410000000000012245176057013614 5ustar www-datawww-datacelluloid-0.15.2/spec/0000755000004100000410000000000012245176057014546 5ustar www-datawww-datacelluloid-0.15.2/spec/spec_helper.rb0000644000004100000410000000120712245176057017364 0ustar www-datawww-datarequire 'coveralls' Coveralls.wear! require 'rubygems' require 'bundler/setup' require 'celluloid/rspec' logfile = File.open(File.expand_path("../../log/test.log", __FILE__), 'a') logfile.sync = true logger = Celluloid.logger = Logger.new(logfile) Celluloid.shutdown_timeout = 1 Dir['./spec/support/*.rb'].map {|f| require f } RSpec.configure do |config| config.filter_run :focus => true config.run_all_when_everything_filtered = true config.before do Celluloid.logger = logger if Celluloid.running? Celluloid.shutdown sleep 0.01 Celluloid.internal_pool.assert_inactive end Celluloid.boot end end celluloid-0.15.2/spec/support/0000755000004100000410000000000012245176057016262 5ustar www-datawww-datacelluloid-0.15.2/spec/support/mailbox_examples.rb0000644000004100000410000000402712245176057022143 0ustar www-datawww-datashared_context "a Celluloid Mailbox" do it "receives messages" do message = :ohai subject << message subject.receive.should == message end it "prioritizes system events over other messages" do subject << :dummy1 subject << :dummy2 subject << Celluloid::SystemEvent.new subject.receive.should be_a(Celluloid::SystemEvent) end it "selectively receives messages with a block" do class Foo; end class Bar; end class Baz; end foo, bar, baz = Foo.new, Bar.new, Baz.new subject << baz subject << foo subject << bar subject.receive { |msg| msg.is_a? Foo }.should eq(foo) subject.receive { |msg| msg.is_a? Bar }.should eq(bar) subject.receive.should eq(baz) end it "waits for a given timeout interval" do interval = 0.1 started_at = Time.now subject.receive(interval) { false } (Time.now - started_at).should be_within(Celluloid::TIMER_QUANTUM).of interval end it "has a size" do subject.should respond_to(:size) subject.size.should be_zero subject << :foo subject << :foo subject.should have(2).entries end it "discards messages received when when full" do subject.max_size = 2 subject << :first subject << :second subject << :third subject.to_a.should =~ [:first, :second] end it "logs discarded messages" do Celluloid.logger = double.as_null_object Celluloid.logger.should_receive(:debug).with("Discarded message (mailbox is dead): third") subject.max_size = 2 subject << :first subject << :second subject << :third end it "discard messages when dead" do Celluloid.logger = double.as_null_object Celluloid.logger.should_receive(:debug).with("Discarded message (mailbox is dead): first") Celluloid.logger.should_receive(:debug).with("Discarded message (mailbox is dead): second") Celluloid.logger.should_receive(:debug).with("Discarded message (mailbox is dead): third") subject << :first subject << :second subject.shutdown subject << :third end end celluloid-0.15.2/spec/support/example_actor_class.rb0000644000004100000410000000373712245176057022631 0ustar www-datawww-datamodule ExampleActorClass def self.create(included_module, task_klass) Class.new do include included_module task_class task_klass attr_reader :name finalizer :my_finalizer execute_block_on_receiver :run_on_receiver def initialize(name) @name = name @delegate = [:bar] end def sleepy(duration) sleep duration end def change_name(new_name) @name = new_name end def change_name_async(new_name) async.change_name new_name end def greet "Hi, I'm #{@name}" end def actor? Celluloid.actor? end def run(*args) yield(*args) end def run_on_receiver(*args) yield(*args) end def crash raise ExampleCrash, "the spec purposely crashed me :(" end def crash_with_abort(reason, foo = nil) example_crash = ExampleCrash.new(reason) example_crash.foo = foo abort example_crash end def crash_with_abort_raw(reason) abort reason end def internal_hello external_hello end def external_hello "Hello" end def inspect_thunk inspect end def send(string) string.reverse end def shutdown terminate end def method_missing(method_name, *args, &block) if delegates?(method_name) @delegate.send method_name, *args, &block else super end end def respond_to?(method_name, include_private = false) super || delegates?(method_name) end def call_private async.zomg_private end def zomg_private @private_called = true end private :zomg_private attr_reader :private_called def my_finalizer end private def delegates?(method_name) @delegate.respond_to?(method_name) end end end end celluloid-0.15.2/spec/support/actor_examples.rb0000644000004100000410000006262612245176057021631 0ustar www-datawww-datashared_examples "a Celluloid Actor" do |included_module| describe "using Fibers" do include_examples "Celluloid::Actor examples", included_module, Celluloid::TaskFiber end describe "using Threads" do include_examples "Celluloid::Actor examples", included_module, Celluloid::TaskThread end end shared_examples "Celluloid::Actor examples" do |included_module, task_klass| class ExampleCrash < StandardError attr_accessor :foo end let(:actor_class) { ExampleActorClass.create(included_module, task_klass) } it "returns the actor's class, not the proxy's" do actor = actor_class.new "Troy McClure" actor.class.should eq(actor_class) end it "compares with the actor's class in a case statement" do case actor_class.new("Troy McClure") when actor_class true else false end.should be_true end it "can be stored in hashes" do actor = actor_class.new "Troy McClure" actor.hash.should_not eq(Kernel.hash) actor.object_id.should_not eq(Kernel.object_id) end it "implements respond_to? correctly" do actor = actor_class.new 'Troy McClure' actor.should respond_to(:alive?) end it "supports synchronous calls" do actor = actor_class.new "Troy McClure" actor.greet.should eq("Hi, I'm Troy McClure") end it "supports synchronous calls with blocks" do actor = actor_class.new "Blocky Ralboa" block_executed = false actor.run { block_executed = true } block_executed.should be_true end it "supports synchronous calls via #method" do method = actor_class.new("Troy McClure").method(:greet) method.call.should eq("Hi, I'm Troy McClure") end it "supports #arity calls via #method" do method = actor_class.new("Troy McClure").method(:greet) method.arity.should be(0) method = actor_class.new("Troy McClure").method(:change_name) method.arity.should be(1) end it "supports future(:method) syntax for synchronous future calls" do actor = actor_class.new "Troy McClure" future = actor.future :greet future.value.should eq("Hi, I'm Troy McClure") end it "supports future.method syntax for synchronous future calls" do actor = actor_class.new "Troy McClure" future = actor.future.greet future.value.should eq("Hi, I'm Troy McClure") end it "handles circular synchronous calls" do klass = Class.new do include included_module task_class task_klass def greet_by_proxy(actor) actor.greet end def to_s "a ponycopter!" end end ponycopter = klass.new actor = actor_class.new ponycopter ponycopter.greet_by_proxy(actor).should eq("Hi, I'm a ponycopter!") end it "detects recursion" do klass1 = Class.new do include included_module task_class task_klass def recursion_test(recurse_through = nil) if recurse_through recurse_through.recursion_thunk(Celluloid::Actor.current) else Celluloid.detect_recursion end end end klass2 = Class.new do include included_module task_class task_klass def recursion_thunk(other) other.recursion_test end end actor1 = klass1.new actor2 = klass2.new actor1.recursion_test.should be_false actor1.recursion_test(actor2).should be_true end it "properly handles method_missing" do actor = actor_class.new "Method Missing" actor.should respond_to(:first) actor.first.should be :bar end it "properly handles respond_to with include_private" do actor = actor_class.new "Method missing privates" actor.respond_to?(:zomg_private).should be_false actor.respond_to?(:zomg_private, true).should be_true end it "warns about suspending the initialize" do klass = Class.new do include included_module task_class task_klass def initialize sleep 0.1 end end Celluloid.logger = double.as_null_object Celluloid.logger.should_receive(:warn).with(/Dangerously suspending task: type=:call, meta={:method_name=>:initialize}, status=:sleeping/) actor = klass.new actor.terminate Celluloid::Actor.join(actor) unless defined?(JRUBY_VERSION) end it "calls the user defined finalizer" do actor = actor_class.new "Mr. Bean" actor.wrapped_object.should_receive(:my_finalizer) actor.terminate Celluloid::Actor.join(actor) end it "warns about suspending the finalizer" do klass = Class.new do include included_module task_class task_klass finalizer :cleanup def cleanup sleep 0.1 end end Celluloid.logger = double.as_null_object Celluloid.logger.should_receive(:warn).with(/Dangerously suspending task: type=:finalizer, meta={:method_name=>:cleanup}, status=:sleeping/) actor = klass.new actor.terminate Celluloid::Actor.join(actor) end it "supports async(:method) syntax for asynchronous calls" do actor = actor_class.new "Troy McClure" actor.async :change_name, "Charlie Sheen" actor.greet.should eq("Hi, I'm Charlie Sheen") end it "supports async.method syntax for asynchronous calls" do actor = actor_class.new "Troy McClure" actor.async.change_name "Charlie Sheen" actor.greet.should eq("Hi, I'm Charlie Sheen") end it "supports async.method syntax for asynchronous calls to itself" do actor = actor_class.new "Troy McClure" actor.change_name_async "Charlie Sheen" actor.greet.should eq("Hi, I'm Charlie Sheen") end it "allows an actor to call private methods asynchronously" do actor = actor_class.new "Troy McClure" actor.call_private actor.private_called.should be_true end it "knows if it's inside actor scope" do Celluloid.should_not be_actor actor = actor_class.new "Troy McClure" actor.run do Celluloid.actor? end.should be_false actor.run_on_receiver do Celluloid.actor? end.should be_true actor.should be_actor end it "inspects properly" do actor = actor_class.new "Troy McClure" actor.inspect.should match(/Celluloid::ActorProxy\(/) actor.inspect.should match(/#{actor_class}/) actor.inspect.should include('@name="Troy McClure"') actor.inspect.should_not include("@celluloid") end it "inspects properly when dead" do actor = actor_class.new "Troy McClure" actor.terminate actor.inspect.should match(/Celluloid::ActorProxy\(/) actor.inspect.should match(/#{actor_class}/) actor.inspect.should include('dead') end it "supports recursive inspect with other actors" do klass = Class.new do include included_module task_class task_klass attr_accessor :other def initialize(other = nil) @other = other end end itchy = klass.new scratchy = klass.new(itchy) itchy.other = scratchy inspection = itchy.inspect inspection.should match(/Celluloid::ActorProxy\(/) inspection.should include("...") end it "allows access to the wrapped object" do actor = actor_class.new "Troy McClure" actor.wrapped_object.should be_a actor_class end it "warns about leaked wrapped objects via #inspect" do actor = actor_class.new "Troy McClure" actor.inspect.should_not include Celluloid::BARE_OBJECT_WARNING_MESSAGE actor.inspect_thunk.should_not include Celluloid::BARE_OBJECT_WARNING_MESSAGE actor.wrapped_object.inspect.should include Celluloid::BARE_OBJECT_WARNING_MESSAGE end it "can override #send" do actor = actor_class.new "Troy McClure" actor.send('foo').should eq('oof') end context "mocking methods" do let(:actor) { actor_class.new "Troy McClure" } before do actor.wrapped_object.should_receive(:external_hello).once.and_return "World" end it "works externally via the proxy" do actor.external_hello.should eq("World") end it "works internally when called on self" do actor.internal_hello.should eq("World") end end context :exceptions do it "reraises exceptions which occur during synchronous calls in the sender" do actor = actor_class.new "James Dean" # is this in bad taste? expect do actor.crash end.to raise_exception(ExampleCrash) end it "includes both sender and receiver in exception traces" do ExampleReceiver = Class.new do include included_module task_class task_klass def receiver_method raise ExampleCrash, "the spec purposely crashed me :(" end end ExampleCaller = Class.new do include included_module task_class task_klass def sender_method ExampleReceiver.new.receiver_method end end ex = nil begin ExampleCaller.new.sender_method rescue => ex end ex.should be_a ExampleCrash ex.backtrace.grep(/`sender_method'/).should be_true ex.backtrace.grep(/`receiver_method'/).should be_true end it "raises DeadActorError if methods are synchronously called on a dead actor" do actor = actor_class.new "James Dean" actor.crash rescue nil sleep 0.1 # hax to prevent a race between exit handling and the next call expect do actor.greet end.to raise_exception(Celluloid::DeadActorError) end end context :abort do it "raises exceptions in the sender but keeps running" do actor = actor_class.new "Al Pacino" expect do actor.crash_with_abort "You die motherfucker!", :bar end.to raise_exception(ExampleCrash, "You die motherfucker!") actor.should be_alive end it "converts strings to runtime errors" do actor = actor_class.new "Al Pacino" expect do actor.crash_with_abort_raw "foo" end.to raise_exception(RuntimeError, "foo") end it "crashes the sender if we pass neither String nor Exception" do actor = actor_class.new "Al Pacino" expect do actor.crash_with_abort_raw 10 end.to raise_exception(TypeError, "Exception object/String expected, but Fixnum received") Celluloid::Actor.join(actor) actor.should_not be_alive end end context :termination do it "terminates" do actor = actor_class.new "Arnold Schwarzenegger" actor.should be_alive actor.terminate Celluloid::Actor.join(actor) actor.should_not be_alive end it "can be terminated by a SyncCall" do actor = actor_class.new "Arnold Schwarzenegger" actor.should be_alive actor.shutdown Celluloid::Actor.join(actor) actor.should_not be_alive end it "kills" do # THOU SHALT ALWAYS KILL actor = actor_class.new "Woody Harrelson" actor.should be_alive Celluloid::Actor.kill(actor) Celluloid::Actor.join(actor) actor.should_not be_alive end it "raises DeadActorError if called after terminated" do actor = actor_class.new "Arnold Schwarzenegger" actor.terminate expect do actor.greet end.to raise_exception(Celluloid::DeadActorError) end it "raises the right DeadActorError if terminate! called after terminated" do actor = actor_class.new "Arnold Schwarzenegger" actor.terminate expect do actor.terminate! end.to raise_exception(Celluloid::DeadActorError, "actor already terminated") end it "logs a warning when terminating tasks" do Celluloid.logger = double.as_null_object Celluloid.logger.should_receive(:warn).with("Terminating task: type=:call, meta={:method_name=>:sleepy}, status=:sleeping") actor = actor_class.new "Arnold Schwarzenegger" actor.async.sleepy 10 actor.greet # make sure the actor has started sleeping actor.terminate end end context :current_actor do it "knows the current actor" do actor = actor_class.new "Roger Daltrey" actor.current_actor.should eq actor end it "raises NotActorError if called outside an actor" do expect do Celluloid.current_actor end.to raise_exception(Celluloid::NotActorError) end end context :linking do before :each do @kevin = actor_class.new "Kevin Bacon" # Some six degrees action here @charlie = actor_class.new "Charlie Sheen" end let(:supervisor_class) do Class.new do # like a boss include included_module task_class task_klass trap_exit :lambaste_subordinate def initialize(name) @name = name @subordinate_lambasted = false end def subordinate_lambasted?; @subordinate_lambasted; end def lambaste_subordinate(actor, reason) @subordinate_lambasted = true end end end it "links to other actors" do @kevin.link @charlie @kevin.monitoring?(@charlie).should be_true @kevin.linked_to?(@charlie).should be_true @charlie.monitoring?(@kevin).should be_true @charlie.linked_to?(@kevin).should be_true end it "unlinks from other actors" do @kevin.link @charlie @kevin.unlink @charlie @kevin.monitoring?(@charlie).should be_false @kevin.linked_to?(@charlie).should be_false @charlie.monitoring?(@kevin).should be_false @charlie.linked_to?(@kevin).should be_false end it "monitors other actors unidirectionally" do @kevin.monitor @charlie @kevin.monitoring?(@charlie).should be_true @kevin.linked_to?(@charlie).should be_false @charlie.monitoring?(@kevin).should be_false @charlie.linked_to?(@kevin).should be_false end it "unmonitors other actors" do @kevin.monitor @charlie @kevin.unmonitor @charlie @kevin.monitoring?(@charlie).should be_false @kevin.linked_to?(@charlie).should be_false @charlie.monitoring?(@kevin).should be_false @charlie.linked_to?(@kevin).should be_false end it "traps exit messages from other actors" do chuck = supervisor_class.new "Chuck Lorre" chuck.link @charlie expect do @charlie.crash end.to raise_exception(ExampleCrash) sleep 0.1 # hax to prevent a race between exit handling and the next call chuck.should be_subordinate_lambasted end it "traps exit messages from other actors in subclasses" do supervisor_subclass = Class.new(supervisor_class) chuck = supervisor_subclass.new "Chuck Lorre" chuck.link @charlie expect do @charlie.crash end.to raise_exception(ExampleCrash) sleep 0.1 # hax to prevent a race between exit handling and the next call chuck.should be_subordinate_lambasted end it "unlinks from a dead linked actor" do chuck = supervisor_class.new "Chuck Lorre" chuck.link @charlie expect do @charlie.crash end.to raise_exception(ExampleCrash) sleep 0.1 # hax to prevent a race between exit handling and the next call chuck.links.count.should be(0) end end context :signaling do before do @signaler = Class.new do include included_module task_class task_klass def initialize @waiting = false @signaled = false end def wait_for_signal raise "already signaled" if @signaled @waiting = true value = wait :ponycopter @waiting = false @signaled = true value end def send_signal(value) signal :ponycopter, value end def waiting?; @waiting end def signaled?; @signaled end end end it "allows methods within the same object to signal each other" do obj = @signaler.new obj.should_not be_signaled obj.async.wait_for_signal obj.should_not be_signaled obj.should be_waiting obj.send_signal :foobar obj.should be_signaled obj.should_not be_waiting end it "sends values along with signals" do obj = @signaler.new obj.should_not be_signaled future = obj.future(:wait_for_signal) obj.should be_waiting obj.should_not be_signaled obj.send_signal(:foobar).should be_true future.value.should be(:foobar) end end context :exclusive do subject do Class.new do include included_module task_class task_klass attr_reader :tasks def initialize @tasks = [] end def log_task(task) @tasks << task end def exclusive_with_block_log_task(task) exclusive do sleep Celluloid::TIMER_QUANTUM log_task(task) end end def exclusive_log_task(task) sleep Celluloid::TIMER_QUANTUM log_task(task) end exclusive :exclusive_log_task def check_not_exclusive Celluloid.exclusive? end def check_exclusive exclusive { Celluloid.exclusive? } end def nested_exclusive_example exclusive { exclusive { nil }; Celluloid.exclusive? } end end.new end it "executes methods in the proper order with block form" do subject.async.exclusive_with_block_log_task(:one) subject.async.log_task(:two) sleep Celluloid::TIMER_QUANTUM * 2 subject.tasks.should eq([:one, :two]) end it "executes methods in the proper order with a class-level annotation" do subject.async.exclusive_log_task :one subject.async.log_task :two sleep Celluloid::TIMER_QUANTUM * 2 subject.tasks.should eq([:one, :two]) end it "knows when it's in exclusive mode" do subject.check_not_exclusive.should be_false subject.check_exclusive.should be_true end it "remains in exclusive mode inside nested blocks" do subject.nested_exclusive_example.should be_true end end context "exclusive classes" do subject do Class.new do include included_module task_class task_klass exclusive attr_reader :tasks def initialize @tasks = [] end def eat_donuts sleep Celluloid::TIMER_QUANTUM @tasks << 'donuts' end def drink_coffee @tasks << 'coffee' end end end it "executes two methods in an exclusive order" do actor = subject.new actor.async.eat_donuts actor.async.drink_coffee sleep Celluloid::TIMER_QUANTUM * 2 actor.tasks.should eq(['donuts', 'coffee']) end end context :receiving do before do @receiver = Class.new do include included_module task_class task_klass execute_block_on_receiver :signal_myself def signal_myself(obj, &block) current_actor.mailbox << obj receive(&block) end end end let(:receiver) { @receiver.new } let(:message) { Object.new } it "allows unconditional receive" do receiver.signal_myself(message).should eq(message) end it "allows arbitrary selective receive" do received_obj = receiver.signal_myself(message) { |o| o == message } received_obj.should eq(message) end it "times out after the given interval", :pending => ENV['CI'] do interval = 0.1 started_at = Time.now receiver.receive(interval) { false }.should be_nil (Time.now - started_at).should be_within(Celluloid::TIMER_QUANTUM).of interval end end context :timers do before do @klass = Class.new do include included_module task_class task_klass def initialize @sleeping = false @fired = false end def do_sleep(n) @sleeping = true sleep n @sleeping = false end def sleeping?; @sleeping end def fire_after(n) after(n) { @fired = true } end def fire_every(n) @fired = 0 every(n) { @fired += 1 } end def fired?; !!@fired end def fired; @fired end end end it "suspends execution of a method (but not the actor) for a given time" do actor = @klass.new # Sleep long enough to ensure we're actually seeing behavior when asleep # but not so long as to delay the test suite unnecessarily interval = Celluloid::TIMER_QUANTUM * 10 started_at = Time.now future = actor.future(:do_sleep, interval) sleep(interval / 2) # wonky! :/ actor.should be_sleeping future.value (Time.now - started_at).should be_within(Celluloid::TIMER_QUANTUM).of interval end it "schedules timers which fire in the future" do actor = @klass.new interval = Celluloid::TIMER_QUANTUM * 10 actor.fire_after(interval) actor.should_not be_fired sleep(interval + Celluloid::TIMER_QUANTUM) # wonky! #/ actor.should be_fired end it "schedules recurring timers which fire in the future" do actor = @klass.new interval = Celluloid::TIMER_QUANTUM * 10 actor.fire_every(interval) actor.fired.should be_zero sleep(interval + Celluloid::TIMER_QUANTUM) # wonky! #/ actor.fired.should be 1 2.times { sleep(interval + Celluloid::TIMER_QUANTUM) } # wonky! #/ actor.fired.should be 3 end it "cancels timers before they fire" do actor = @klass.new interval = Celluloid::TIMER_QUANTUM * 10 timer = actor.fire_after(interval) actor.should_not be_fired timer.cancel sleep(interval + Celluloid::TIMER_QUANTUM) # wonky! #/ actor.should_not be_fired end it "allows delays from outside the actor" do actor = @klass.new interval = Celluloid::TIMER_QUANTUM * 10 fired = false actor.after(interval) do fired = true end fired.should be_false sleep(interval + Celluloid::TIMER_QUANTUM) # wonky! #/ fired.should be_true end end context :tasks do before do @klass = Class.new do include included_module task_class task_klass attr_reader :blocker def initialize @blocker = Blocker.new end def blocking_call @blocker.block end end class Blocker include Celluloid def block wait :unblock end def unblock signal :unblock end end end it "knows which tasks are waiting on calls to other actors" do actor = @klass.new tasks = actor.tasks tasks.size.should be 1 actor.future(:blocking_call) sleep 0.1 # hax! waiting for ^^^ call to actually start tasks = actor.tasks tasks.size.should be 2 blocking_task = tasks.find { |t| t.status != :running } blocking_task.should be_a task_klass blocking_task.status.should be :callwait actor.blocker.unblock sleep 0.1 # hax again :( actor.tasks.size.should be 1 end end context :mailbox_class do class ExampleMailbox < Celluloid::Mailbox; end subject do Class.new do include included_module task_class task_klass mailbox_class ExampleMailbox end end it "uses user-specified mailboxes" do subject.new.mailbox.should be_a ExampleMailbox end it "retains custom mailboxes when subclassed" do subclass = Class.new(subject) subclass.new.mailbox.should be_a ExampleMailbox end end context :mailbox_size do subject do Class.new do include included_module task_class task_klass mailbox_size 100 end end it "configures the mailbox limit" do subject.new.mailbox.max_size.should == 100 end end context :proxy_class do class ExampleProxy < Celluloid::ActorProxy def subclass_proxy? true end end subject do Class.new do include included_module task_class task_klass proxy_class ExampleProxy end end it "uses user-specified proxy" do subject.new.should be_subclass_proxy end it "retains custom proxy when subclassed" do subclass = Class.new(subject) subclass.new.should be_subclass_proxy end end context :task_class do class ExampleTask < Celluloid::TaskFiber; end subject do Class.new do include included_module task_class ExampleTask end end it "overrides the task class" do subject.new.tasks.first.should be_a ExampleTask end it "retains custom task classes when subclassed" do subclass = Class.new(subject) subclass.new.tasks.first.should be_a ExampleTask end end context :timeouts do let :actor_class do Class.new do include included_module def name sleep 0.5 :foo end def ask_name_with_timeout(other, duration) timeout(duration) { other.name } end end end it "allows timing out tasks, raising Celluloid::Task::TimeoutError" do a1 = actor_class.new a2 = actor_class.new expect { a1.ask_name_with_timeout a2, 0.3 }.to raise_error(Celluloid::Task::TimeoutError) end it "does not raise when it completes in time" do a1 = actor_class.new a2 = actor_class.new a1.ask_name_with_timeout(a2, 0.6).should == :foo end end context "raw message sends" do it "logs on unhandled messages" do Celluloid.logger = double.as_null_object Celluloid.logger.should_receive(:debug).with("Discarded message (unhandled): first") actor = actor_class.new "Irma Gladden" actor.mailbox << :first sleep Celluloid::TIMER_QUANTUM end end end celluloid-0.15.2/spec/support/task_examples.rb0000644000004100000410000000165112245176057021452 0ustar www-datawww-dataclass MockActor attr_reader :tasks def initialize @tasks = [] end def setup_thread end end shared_context "a Celluloid Task" do |task_class| let(:task_type) { :foobar } let(:suspend_state) { :doing_something } let(:actor) { MockActor.new } subject { task_class.new(task_type, {}) { Celluloid::Task.suspend(suspend_state) } } before :each do Thread.current[:celluloid_actor] = actor end after :each do Thread.current[:celluloid_actor] = nil end it "begins with status :new" do subject.status.should be :new end it "resumes" do subject.should be_running subject.resume subject.status.should eq(suspend_state) subject.resume subject.should_not be_running end it "raises exceptions outside" do task = task_class.new(task_type, {}) do raise "failure" end expect do task.resume end.to raise_exception("failure") end end celluloid-0.15.2/spec/celluloid/0000755000004100000410000000000012245176057016522 5ustar www-datawww-datacelluloid-0.15.2/spec/celluloid/uuid_spec.rb0000644000004100000410000000042312245176057021026 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::UUID do U = Celluloid::UUID it "generates unique IDs across the BLOCK_SIZE boundary" do upper_bound = U::BLOCK_SIZE * 2 + 10 uuids = (1..upper_bound).map{ U.generate } uuids.size.should == uuids.uniq.size end end celluloid-0.15.2/spec/celluloid/pool_spec.rb0000644000004100000410000000224612245176057021036 0ustar www-datawww-datarequire 'spec_helper' describe "Celluloid.pool" do class ExampleError < StandardError; end class MyWorker include Celluloid def process(queue = nil) if queue queue << :done else :done end end def crash raise ExampleError, "zomgcrash" end end subject { MyWorker.pool } it "processes work units synchronously" do subject.process.should be :done end it "processes work units asynchronously" do queue = Queue.new subject.async.process(queue) queue.pop.should be :done end it "handles crashes" do expect { subject.crash }.to raise_error(ExampleError) subject.process.should be :done end it "uses a fixed-sized number of threads" do subject # eagerly evaluate the pool to spawn it actors = Celluloid::Actor.all 100.times.map { subject.future(:process) }.map(&:value) new_actors = Celluloid::Actor.all - actors new_actors.should eq [] end it "terminates" do expect { subject.terminate }.to_not raise_exception end it "handles many requests" do futures = 10.times.map do subject.future.process end futures.map(&:value) end end celluloid-0.15.2/spec/celluloid/thread_handle_spec.rb0000644000004100000410000000076512245176057022653 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::ThreadHandle do it "knows thread liveliness" do queue = Queue.new handle = Celluloid::ThreadHandle.new { queue.pop } handle.should be_alive queue << :die sleep 0.01 # hax handle.should_not be_alive end it "joins to thread handles" do Celluloid::ThreadHandle.new { sleep 0.01 }.join end it "supports passing a role" do Celluloid::ThreadHandle.new(:useful) { Thread.current.role.should == :useful }.join end end celluloid-0.15.2/spec/celluloid/block_spec.rb0000644000004100000410000000226712245176057021162 0ustar www-datawww-datarequire 'spec_helper' describe "Blocks" do class MyBlockActor include Celluloid def initialize(name) @name = name end attr_reader :name def ask_for_something(other) sender_actor = current_actor $data << [:outside, @name, current_actor.name] other.do_something_and_callback do |value| $data << [:yielded, @name, current_actor.name] $data << self.receive_result(:self) $data << current_actor.receive_result(:current_actor) $data << sender_actor.receive_result(:sender) "somevalue" end end def do_something_and_callback $data << [:something, @name, current_actor.name] $data << yield(:foo) end def receive_result(result) [result, @name, current_actor.name] end end it "works" do $data = [] a1 = MyBlockActor.new("one") a2 = MyBlockActor.new("two") a1.ask_for_something a2 expected = [ [:outside, "one", "one"], [:something, "two", "two"], [:yielded, "one", "one"], [:self, "one", "one"], [:current_actor, "one", "one"], [:sender, "one", "one"], "somevalue", ] $data.should eq(expected) end end celluloid-0.15.2/spec/celluloid/notifications_spec.rb0000644000004100000410000000541512245176057022737 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Notifications do class Admirer include Celluloid include Celluloid::Notifications attr_reader :mourning attr_reader :mourning_count def someone_died(topic, name) @mourning = name @mourning_count ||= 0 @mourning_count += 1 end end class President include Celluloid include Celluloid::Notifications def die publish("death", "Mr. President") end end it 'notifies relevant subscribers' do marilyn = Admirer.new jackie = Admirer.new marilyn.subscribe("death", :someone_died) jackie.subscribe("alive", :someone_died) president = President.new president.die marilyn.mourning.should eq("Mr. President") jackie.mourning.should_not eq("Mr. President") end it 'allows multiple subscriptions from the same actor' do marilyn = Admirer.new marilyn.subscribe("death", :someone_died) marilyn.subscribe("death", :someone_died) president = President.new president.die marilyn.mourning_count.should be(2) end it 'notifies subscribers' do marilyn = Admirer.new jackie = Admirer.new marilyn.subscribe("death", :someone_died) jackie.subscribe("death", :someone_died) president = President.new president.die marilyn.mourning.should eq("Mr. President") jackie.mourning.should eq("Mr. President") end it 'publishes even if there are no subscribers' do president = President.new president.die end it 'allows regex subscriptions' do marilyn = Admirer.new marilyn.subscribe(/(death|assassination)/, :someone_died) president = President.new president.die marilyn.mourning.should eq("Mr. President") end it 'allows unsubscribing' do marilyn = Admirer.new subscription = marilyn.subscribe("death", :someone_died) marilyn.unsubscribe(subscription) president = President.new president.die marilyn.mourning.should be_nil end it 'prunes dead subscriptions' do marilyn = Admirer.new jackie = Admirer.new marilyn.subscribe("death", :someone_died) jackie.subscribe("death", :someone_died) listeners = Celluloid::Notifications.notifier.listeners_for("death").size marilyn.terminate after_listeners = Celluloid::Notifications.notifier.listeners_for("death").size after_listeners.should == listeners - 1 end it 'prunes multiple subscriptions from a dead actor' do marilyn = Admirer.new marilyn.subscribe("death", :someone_died) marilyn.subscribe("death", :someone_died) listeners = Celluloid::Notifications.notifier.listeners_for("death").size marilyn.terminate after_listeners = Celluloid::Notifications.notifier.listeners_for("death").size after_listeners.should eq(listeners - 2) end end celluloid-0.15.2/spec/celluloid/future_spec.rb0000644000004100000410000000174312245176057021400 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Future do it "creates future objects that can be retrieved later" do future = Celluloid::Future.new { 40 + 2 } future.value.should == 42 end it "passes arguments to future blocks" do future = Celluloid::Future.new(40) { |n| n + 2 } future.value.should == 42 end it "reraises exceptions that occur when the value is retrieved" do class ExampleError < StandardError; end future = Celluloid::Future.new { raise ExampleError, "oh noes crash!" } expect { future.value }.to raise_exception(ExampleError) end it "knows if it's got a value yet" do future = Celluloid::Future.new { sleep Celluloid::TIMER_QUANTUM * 5 } future.should_not be_ready sleep Celluloid::TIMER_QUANTUM * 6 future.should be_ready end it "raises TimeoutError when the future times out" do future = Celluloid::Future.new { sleep 2 } expect { future.value(1) }.to raise_exception(Celluloid::TimeoutError) end end celluloid-0.15.2/spec/celluloid/supervision_group_spec.rb0000644000004100000410000000245712245176057023673 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::SupervisionGroup do before :all do class MyActor include Celluloid def running?; :yep; end end class MyGroup < Celluloid::SupervisionGroup supervise MyActor, :as => :example end end it "runs applications" do MyGroup.run! sleep 0.01 # startup time hax Celluloid::Actor[:example].should be_running end it "accepts a private actor registry" do my_registry = Celluloid::Registry.new MyGroup.run!(my_registry) sleep 0.01 my_registry[:example].should be_running end it "removes actors from the registry when terminating" do group = MyGroup.run! group.terminate Celluloid::Actor[:example].should be_nil end context "pool" do before :all do class MyActor attr_reader :args def initialize *args @args = *args end end class MyGroup pool MyActor, :as => :example_pool, :args => 'foo', :size => 3 end end it "runs applications and passes pool options and actor args" do MyGroup.run! sleep 0.001 # startup time hax Celluloid::Actor[:example_pool].should be_running Celluloid::Actor[:example_pool].args.should eq ['foo'] Celluloid::Actor[:example_pool].size.should be 3 end end end celluloid-0.15.2/spec/celluloid/fsm_spec.rb0000644000004100000410000000475112245176057020655 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::FSM do before :all do class TestMachine include Celluloid::FSM def initialize super @fired = false end state :callbacked do @fired = true end state :pre_done, :to => :done state :another, :done def fired?; @fired end end class DummyActor include Celluloid end class CustomDefaultMachine include Celluloid::FSM default_state :foobar end end subject { TestMachine.new } it "starts in the default state" do subject.state.should eq(TestMachine.default_state) end it "transitions between states" do subject.state.should_not be :done subject.transition :done subject.state.should be :done end it "fires callbacks for states" do subject.should_not be_fired subject.transition :callbacked subject.should be_fired end it "allows custom default states" do CustomDefaultMachine.new.state.should be :foobar end it "supports constraints on valid state transitions" do subject.transition :pre_done expect { subject.transition :another }.to raise_exception ArgumentError end it "transitions to states after a specified delay" do interval = Celluloid::TIMER_QUANTUM * 10 subject.attach DummyActor.new subject.transition :another subject.transition :done, :delay => interval subject.state.should be :another sleep interval + Celluloid::TIMER_QUANTUM subject.state.should be :done end it "cancels delayed state transitions if another transition is made" do interval = Celluloid::TIMER_QUANTUM * 10 subject.attach DummyActor.new subject.transition :another subject.transition :done, :delay => interval subject.state.should be :another subject.transition :pre_done sleep interval + Celluloid::TIMER_QUANTUM subject.state.should be :pre_done end context "actor is not set" do context "transition is delayed" do it "raises an unattached error" do expect { subject.transition :another, :delay => 100 } \ .to raise_error(Celluloid::FSM::UnattachedError) end end end context "transitioning to an invalid state" do it "raises an argument error" do expect { subject.transition :invalid_state }.to raise_error(ArgumentError) end it "should not call transition! if the state is :default" do subject.should_not_receive :transition! subject.transition :default end end end celluloid-0.15.2/spec/celluloid/condition_spec.rb0000644000004100000410000000255612245176057022057 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Condition do class ConditionExample include Celluloid attr_reader :condition, :signaled_times def initialize @condition = Condition.new @waiting = false @signaled_times = 0 end def signal_condition(condition, value) condition.signal value end def wait_for_condition @waiting = true begin value = @condition.wait @signaled_times += 1 ensure @waiting = false end value end def waiting?; @waiting end end let(:actor) { ConditionExample.new } after { actor.terminate rescue nil } it "sends signals" do 3.times { actor.async.wait_for_condition } actor.signaled_times.should be_zero actor.condition.signal actor.signaled_times.should be(1) end it "broadcasts signals" do 3.times { actor.async.wait_for_condition } actor.signaled_times.should be_zero actor.condition.broadcast actor.signaled_times.should be(3) end it "sends values along with signals" do future = actor.future(:wait_for_condition) actor.condition.signal(:example_value) future.value.should be(:example_value) end it "supports waiting outside actors" do condition = Celluloid::Condition.new actor.async.signal_condition condition, :value condition.wait.should eq(:value) end end celluloid-0.15.2/spec/celluloid/mailbox_spec.rb0000644000004100000410000000014212245176057021511 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Mailbox do it_behaves_like "a Celluloid Mailbox" end celluloid-0.15.2/spec/celluloid/internal_pool_spec.rb0000644000004100000410000000231012245176057022722 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::InternalPool do it "gets threads from the pool" do subject.get { sleep 1 }.should be_a Thread end it "puts threads back into the pool" do subject.idle_size.should be_zero subject.busy_size.should be_zero queue = Queue.new subject.get { queue.pop } subject.idle_size.should be_zero subject.busy_size.should eq 1 queue << nil sleep 0.01 # hax subject.idle_size.should eq 1 subject.busy_size.should eq 0 end it "cleans thread locals from old threads" do thread = subject.get { Thread.current[:foo] = :bar } sleep 0.01 #hax thread[:foo].should be_nil end it "doesn't fail if a third-party thread is spawned" do subject.idle_size.should be_zero subject.busy_size.should be_zero subject.get { ::Thread.new { sleep 0.5 } }.should be_a(Celluloid::Thread) sleep 0.01 # hax subject.idle_size.should eq 1 subject.busy_size.should eq 0 end it "doesn't leak dead threads" do subject.max_idle = 0 # Instruct the pool to immediately shut down the thread. subject.get { true }.should be_a(Celluloid::Thread) sleep 0.01 # hax subject.to_a.should have(0).items end end celluloid-0.15.2/spec/celluloid/evented_mailbox_spec.rb0000644000004100000410000000111212245176057023221 0ustar www-datawww-datarequire 'spec_helper' class TestEventedMailbox < Celluloid::EventedMailbox class Reactor def initialize @condition = ConditionVariable.new @mutex = Mutex.new end def wakeup @mutex.synchronize do @condition.signal end end def run_once(timeout) @mutex.synchronize do @condition.wait(@mutex, timeout) end end def shutdown end end def initialize super(Reactor) end end describe Celluloid::EventedMailbox do subject { TestEventedMailbox.new } it_behaves_like "a Celluloid Mailbox" end celluloid-0.15.2/spec/celluloid/logging/0000755000004100000410000000000012245176057020150 5ustar www-datawww-datacelluloid-0.15.2/spec/celluloid/logging/ring_buffer_spec.rb0000644000004100000410000000145712245176057024006 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::RingBuffer do subject { Celluloid::RingBuffer.new(2) } it { should be_empty } it { should_not be_full } it 'should push and shift' do subject.push('foo') subject.push('foo2') subject.shift.should eq('foo') subject.shift.should eq('foo2') end it 'should push past the end' do subject.push('foo') subject.push('foo2') subject.push('foo3') subject.should be_full end it 'should shift the most recent' do (1..5).each { |i| subject.push(i) } subject.shift.should be 4 subject.shift.should be 5 subject.shift.should be_nil end it 'should return nil when shifting empty' do subject.should be_empty subject.shift.should be_nil end it 'should be thread-safe' do #TODO how to test? end end celluloid-0.15.2/spec/celluloid/tasks/0000755000004100000410000000000012245176057017647 5ustar www-datawww-datacelluloid-0.15.2/spec/celluloid/tasks/task_thread_spec.rb0000644000004100000410000000017112245176057023476 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::TaskThread do it_behaves_like "a Celluloid Task", Celluloid::TaskThread end celluloid-0.15.2/spec/celluloid/tasks/task_fiber_spec.rb0000644000004100000410000000016712245176057023323 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::TaskFiber do it_behaves_like "a Celluloid Task", Celluloid::TaskFiber end celluloid-0.15.2/spec/celluloid/calls_spec.rb0000644000004100000410000000166612245176057021170 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::SyncCall do class CallExampleActor include Celluloid def initialize(next_actor = nil) @next = next_actor end def actual_method; end def chained_call_ids [call_chain_id, @next.call_chain_id] end end let(:actor) { CallExampleActor.new } it "aborts with NoMethodError when a nonexistent method is called" do expect do actor.the_method_that_wasnt_there end.to raise_exception(NoMethodError) actor.should be_alive end it "aborts with ArgumentError when a method is called with too many arguments" do expect do actor.actual_method("with too many arguments") end.to raise_exception(ArgumentError) actor.should be_alive end it "preserves call chains across synchronous calls" do actor2 = CallExampleActor.new(actor) uuid, next_actor_uuid = actor2.chained_call_ids uuid.should eq next_actor_uuid end end celluloid-0.15.2/spec/celluloid/registry_spec.rb0000644000004100000410000000341312245176057021732 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Registry do class Marilyn include Celluloid def sing_for(person) "o/~ Happy birthday, #{person}" end end it "registers Actors" do Celluloid::Actor[:marilyn] = Marilyn.new Celluloid::Actor[:marilyn].sing_for("Mr. President").should == "o/~ Happy birthday, Mr. President" end it "refuses to register non-Actors" do expect do Celluloid::Actor[:impostor] = Object.new end.to raise_error TypeError end it "lists all registered actors" do Celluloid::Actor[:marilyn] = Marilyn.new Celluloid::Actor.registered.should include :marilyn end it "knows its name once registered" do Celluloid::Actor[:marilyn] = Marilyn.new Celluloid::Actor[:marilyn].name.should == :marilyn end describe :delete do before do Celluloid::Actor[:marilyn] ||= Marilyn.new end it "removes reference to actors' name from the registry" do Celluloid::Registry.root.delete(:marilyn) Celluloid::Actor.registered.should_not include :marilyn end it "returns actor removed from the registry" do rval = Celluloid::Registry.root.delete(:marilyn) rval.should be_kind_of(Marilyn) end end describe :clear do it "should return a hash of registered actors and remove them from the registry" do Celluloid::Actor[:marilyn] ||= Marilyn.new rval = Celluloid::Actor.clear_registry begin rval.should be_kind_of(Hash) rval.should have_key(:marilyn) rval[:marilyn].wrapped_object.should be_instance_of(Marilyn) Celluloid::Actor.registered.should be_empty ensure # Repopulate the registry once we're done rval.each { |key, actor| Celluloid::Actor[key] = actor } end end end end celluloid-0.15.2/spec/celluloid/stack_dump_spec.rb0000644000004100000410000000131512245176057022213 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::StackDump do class BlockingActor include Celluloid def blocking Kernel.sleep end end before(:each) do [Celluloid::TaskFiber, Celluloid::TaskThread].each do |task_klass| actor_klass = Class.new(BlockingActor) do task_class task_klass end actor = actor_klass.new actor.async.blocking end Celluloid.internal_pool.get do Thread.current.role = :testing sleep end end it 'should include all actors' do subject.actors.size.should == Celluloid::Actor.all.size end it 'should include threads that are not actors' do pending "bugs" subject.threads.size.should == 2 end end celluloid-0.15.2/spec/celluloid/actor_spec.rb0000644000004100000410000000014212245176057021166 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid do it_behaves_like "a Celluloid Actor", Celluloid end celluloid-0.15.2/spec/celluloid/properties_spec.rb0000644000004100000410000000212512245176057022255 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Properties do let(:default_value) { 42 } let(:changed_value) { 43 } let(:example_class) do Class.new do extend Celluloid::Properties property :baz, :default => 42 end end let(:example_subclass) do Class.new(example_class) end let(:example_subclass_subclass) do Class.new(example_subclass) end it "adds properties to classes" do example_class.baz.should eq default_value example_class.baz changed_value example_class.baz.should eq changed_value end it "allows properties to be inherited" do example_subclass.baz.should eq default_value example_subclass.baz changed_value example_subclass.baz.should eq changed_value example_class.baz.should eq default_value end it "allows properties to be deeply inherited" do example_subclass_subclass.baz.should eq default_value example_subclass_subclass.baz changed_value example_subclass_subclass.baz.should eq changed_value example_subclass.baz.should eq default_value example_class.baz.should eq default_value end endcelluloid-0.15.2/spec/celluloid/supervisor_spec.rb0000644000004100000410000000510712245176057022305 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Supervisor do class SubordinateDead < StandardError; end class Subordinate include Celluloid attr_reader :state def initialize(state) @state = state end def crack_the_whip case @state when :idle @state = :working else raise SubordinateDead, "the spec purposely crashed me :(" end end end it "restarts actors when they die" do supervisor = Celluloid::Supervisor.supervise(Subordinate, :idle) subordinate = supervisor.actors.first subordinate.state.should be(:idle) subordinate.crack_the_whip subordinate.state.should be(:working) expect do subordinate.crack_the_whip end.to raise_exception(SubordinateDead) sleep 0.1 # hax to prevent race :( subordinate.should_not be_alive new_subordinate = supervisor.actors.first new_subordinate.should_not eq subordinate new_subordinate.state.should eq :idle end it "registers actors and reregisters them when they die" do Celluloid::Supervisor.supervise_as(:subordinate, Subordinate, :idle) subordinate = Celluloid::Actor[:subordinate] subordinate.state.should be(:idle) subordinate.crack_the_whip subordinate.state.should be(:working) expect do subordinate.crack_the_whip end.to raise_exception(SubordinateDead) sleep 0.1 # hax to prevent race :( subordinate.should_not be_alive new_subordinate = Celluloid::Actor[:subordinate] new_subordinate.should_not eq subordinate new_subordinate.state.should eq :idle end it "creates supervisors via Actor.supervise" do supervisor = Subordinate.supervise(:working) subordinate = supervisor.actors.first subordinate.state.should be(:working) expect do subordinate.crack_the_whip end.to raise_exception(SubordinateDead) sleep 0.1 # hax to prevent race :( subordinate.should_not be_alive new_subordinate = supervisor.actors.first new_subordinate.should_not eq subordinate new_subordinate.state.should eq :working end it "creates supervisors and registers actors via Actor.supervise_as" do supervisor = Subordinate.supervise_as(:subordinate, :working) subordinate = Celluloid::Actor[:subordinate] subordinate.state.should be(:working) expect do subordinate.crack_the_whip end.to raise_exception(SubordinateDead) sleep 0.1 # hax to prevent race :( subordinate.should_not be_alive new_subordinate = supervisor.actors.first new_subordinate.should_not eq subordinate new_subordinate.state.should be(:working) end end celluloid-0.15.2/spec/celluloid/links_spec.rb0000644000004100000410000000205012245176057021176 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Links do subject { Celluloid::Links.new } let(:mailbox_mock) do Class.new(Array) do attr_reader :address def initialize address @address = address end end end let(:first_actor) do Struct.new(:mailbox).new(mailbox_mock.new('foo123')) end let(:second_actor) do Struct.new(:mailbox).new(mailbox_mock.new('bar456')) end it 'is Enumerable' do subject.should be_an(Enumerable) end it 'adds actors by their mailbox address' do subject.include?(first_actor).should be_false subject << first_actor subject.include?(first_actor).should be_true end it 'removes actors by their mailbox address' do subject << first_actor subject.include?(first_actor).should be_true subject.delete first_actor subject.include?(first_actor).should be_false end it 'iterates over all actors' do subject << first_actor subject << second_actor subject.inject([]) { |all, a| all << a }.should == [first_actor, second_actor] end end celluloid-0.15.2/lib/0000755000004100000410000000000012245176057014362 5ustar www-datawww-datacelluloid-0.15.2/lib/celluloid.rb0000644000004100000410000003424212245176057016670 0ustar www-datawww-datarequire 'logger' require 'thread' require 'timeout' require 'set' if defined?(JRUBY_VERSION) && JRUBY_VERSION == "1.7.3" raise "Celluloid is broken on JRuby 1.7.3. Please upgrade to 1.7.4+" end module Celluloid VERSION = '0.15.2' Error = Class.new StandardError extend self # expose all instance methods as singleton methods # Warning message added to Celluloid objects accessed outside their actors BARE_OBJECT_WARNING_MESSAGE = "WARNING: BARE CELLULOID OBJECT " class << self attr_accessor :internal_pool # Internal thread pool attr_accessor :logger # Thread-safe logger class attr_accessor :task_class # Default task type to use attr_accessor :shutdown_timeout # How long actors have to terminate def included(klass) klass.send :extend, ClassMethods klass.send :include, InstanceMethods klass.send :extend, Properties klass.property :mailbox_class, :default => Celluloid::Mailbox klass.property :proxy_class, :default => Celluloid::ActorProxy klass.property :task_class, :default => Celluloid.task_class klass.property :mailbox_size klass.property :execute_block_on_receiver, :default => [:after, :every, :receive], :multi => true klass.property :finalizer klass.property :exit_handler klass.send(:define_singleton_method, :trap_exit) do |*args| exit_handler(*args) end end # Are we currently inside of an actor? def actor? !!Thread.current[:celluloid_actor] end # Retrieve the mailbox for the current thread or lazily initialize it def mailbox Thread.current[:celluloid_mailbox] ||= Celluloid::Mailbox.new end # Generate a Universally Unique Identifier def uuid UUID.generate end # Obtain the number of CPUs in the system def cores 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::StackDump.new.dump(output) end alias_method :dump, :stack_dump # Detect if a particular call is recursing through multiple actors def detect_recursion actor = Thread.current[:celluloid_actor] return unless actor task = Thread.current[:celluloid_task] return unless task chain_id = CallChain.current_id actor.tasks.to_a.any? { |t| t != task && t.chain_id == chain_id } end # Define an exception handler for actor crashes def exception_handler(&block) Logger.exception_handler(&block) end def suspend(status, waiter) task = Thread.current[:celluloid_task] if task && !Celluloid.exclusive? waiter.before_suspend(task) if waiter.respond_to?(:before_suspend) Task.suspend(status) else waiter.wait end end def boot init start end def init self.internal_pool = InternalPool.new end # Launch default services # FIXME: We should set up the supervision hierarchy here def start Celluloid::Notifications::Fanout.supervise_as :notifications_fanout Celluloid::IncidentReporter.supervise_as :default_incident_reporter, STDERR end def running? internal_pool end def register_shutdown return if @shutdown_registered # 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) Celluloid.shutdown exit exit_status if exit_status else Celluloid.shutdown end end @shutdown_registered = true end # Shut down all running actors def shutdown actors = Actor.all Timeout.timeout(shutdown_timeout) do internal_pool.shutdown Logger.debug "Terminating #{actors.size} #{(actors.size > 1) ? 'actors' : 'actor'}..." if actors.size > 0 # Attempt to shut down the supervision tree, if available Supervisor.root.terminate if Supervisor.root # Actors cannot self-terminate, you must do it for them actors.each do |actor| begin actor.terminate! rescue DeadActorError end end actors.each do |actor| begin Actor.join(actor) rescue DeadActorError end end end rescue Timeout::Error Logger.error("Couldn't cleanly terminate all actors in #{shutdown_timeout} seconds!") actors.each do |actor| begin Actor.kill(actor) rescue DeadActorError, MailboxDead end end ensure internal_pool.kill end def version VERSION 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 proxy._send_(:initialize, *args, &block) proxy end alias_method :spawn, :new # Create a new actor and link to the current one def new_link(*args, &block) raise NotActorError, "can't link outside actor context" unless Celluloid.actor? proxy = Actor.new(allocate, actor_options).proxy Actor.link(proxy) proxy._send_(:initialize, *args, &block) proxy end alias_method :spawn_link, :new_link # Create a supervisor which ensures an instance of an actor will restart # an actor if it fails def supervise(*args, &block) Supervisor.supervise(self, *args, &block) end # Create a supervisor which ensures an instance of an actor will restart # an actor if it fails, and keep the actor registered under a given name def supervise_as(name, *args, &block) Supervisor.supervise_as(name, self, *args, &block) end # Create a new pool of workers. Accepts the following options: # # * size: how many workers to create. Default is worker per CPU core # * args: array of arguments to pass when creating a worker # def pool(options = {}) PoolManager.new(self, options) end # Same as pool, but links to the pool manager def pool_link(options = {}) PoolManager.new_link(self, options) end # Run an actor in the foreground def run(*args, &block) Actor.join(new(*args, &block)) end # Mark methods as running exclusively def exclusive(*methods) if methods.empty? @exclusive_methods = :all elsif !defined?(@exclusive_methods) || @exclusive_methods != :all @exclusive_methods ||= Set.new @exclusive_methods.merge methods.map(&:to_sym) end end # Configuration options for Actor#new def actor_options { :mailbox_class => mailbox_class, :mailbox_size => mailbox_size, :proxy_class => proxy_class, :task_class => task_class, :exit_handler => exit_handler, :exclusive_methods => defined?(@exclusive_methods) ? @exclusive_methods : nil, :receiver_block_executions => execute_block_on_receiver } end def ===(other) other.kind_of? self end end # These are methods we don't want added to the Celluloid singleton but to be # defined on all classes that use Celluloid module InstanceMethods # Obtain the bare Ruby object the actor is wrapping. This is useful for # only a limited set of use cases like runtime metaprogramming. Interacting # directly with the bare object foregoes any kind of thread safety that # Celluloid would ordinarily provide you, and the object is guaranteed to # be shared with at least the actor thread. Tread carefully. # # Bare objects can be identified via #inspect output: # # >> actor # => # # >> actor.bare_object # => # # 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[:celluloid_actor] end def tap yield current_actor current_actor end # Obtain the name of the current actor def name Actor.name end def inspect return "..." if Celluloid.detect_recursion str = "#<" if leaked? str << Celluloid::BARE_OBJECT_WARNING_MESSAGE else str << "Celluloid::ActorProxy" end str << "(#{self.class}:0x#{object_id.to_s(16)})" str << " " unless instance_variables.empty? instance_variables.each do |ivar| next if ivar == Celluloid::OWNER_IVAR str << "#{ivar}=#{instance_variable_get(ivar).inspect} " end str.sub!(/\s$/, '>') end end # # The following methods are available on both the Celluloid singleton and # directly inside of all classes that include Celluloid # # Raise an exception in sender context, but stay running def abort(cause) cause = case cause when String then RuntimeError.new(cause) when Exception then cause else raise TypeError, "Exception object/String expected, but #{cause.class} received" end raise AbortError.new(cause) end # Terminate this actor def terminate Thread.current[:celluloid_actor].proxy.terminate! end # Send a signal with the given name to all waiting methods def signal(name, value = nil) Thread.current[:celluloid_actor].signal name, value end # Wait for the given signal def 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 CallChain.current_id end # Obtain the running tasks for this actor def tasks Thread.current[:celluloid_actor].tasks.to_a end # Obtain the Celluloid::Links for this actor def links Thread.current[:celluloid_actor].links end # Watch for exit events from another actor def monitor(actor) Actor.monitor(actor) end # Stop waiting for exit events from another actor def unmonitor(actor) Actor.unmonitor(actor) end # Link this actor to another, allowing it to crash or react to errors def link(actor) Actor.link(actor) end # Remove links to another actor def unlink(actor) Actor.unlink(actor) end # Are we monitoring another actor? def monitoring?(actor) Actor.monitoring?(actor) end # Is this actor linked to another? def linked_to?(actor) Actor.linked_to?(actor) end # Receive an asynchronous message via the actor protocol def receive(timeout = nil, &block) actor = Thread.current[:celluloid_actor] if actor actor.receive(timeout, &block) else Celluloid.mailbox.receive(timeout, &block) end end # Sleep letting the actor continue processing messages def sleep(interval) actor = Thread.current[:celluloid_actor] if actor actor.sleep(interval) else Kernel.sleep interval end end # Timeout on task suspension (eg Sync calls to other actors) def timeout(duration) Thread.current[:celluloid_actor].timeout(duration) do yield 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[:celluloid_task].exclusive(&block) end # Are we currently exclusive def exclusive? task = Thread.current[:celluloid_task] task && task.exclusive? end # Call a block after a given interval, returning a Celluloid::Timer object def 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[:celluloid_actor].every(interval, &block) end # Perform a blocking or computationally intensive action inside an # asynchronous thread pool, allowing the sender to continue processing other # messages in its mailbox in the meantime def defer(&block) # This implementation relies on the present implementation of # Celluloid::Future, which uses a thread from InternalPool to run the block Future.new(&block).value end # Handle async calls within an actor itself def async(meth = nil, *args, &block) Thread.current[:celluloid_actor].proxy.async meth, *args, &block end # Handle calls to future within an actor itself def future(meth = nil, *args, &block) Thread.current[:celluloid_actor].proxy.future meth, *args, &block end end require 'celluloid/calls' require 'celluloid/call_chain' require 'celluloid/condition' require 'celluloid/thread' require 'celluloid/core_ext' require 'celluloid/cpu_counter' require 'celluloid/fiber' require 'celluloid/fsm' require 'celluloid/internal_pool' require 'celluloid/links' require 'celluloid/logger' require 'celluloid/mailbox' require 'celluloid/evented_mailbox' require 'celluloid/method' require 'celluloid/properties' require 'celluloid/receivers' require 'celluloid/registry' require 'celluloid/responses' require 'celluloid/signals' require 'celluloid/stack_dump' require 'celluloid/system_events' require 'celluloid/tasks' require 'celluloid/task_set' require 'celluloid/thread_handle' require 'celluloid/uuid' require 'celluloid/proxies/abstract_proxy' require 'celluloid/proxies/sync_proxy' require 'celluloid/proxies/actor_proxy' require 'celluloid/proxies/async_proxy' require 'celluloid/proxies/future_proxy' require 'celluloid/proxies/block_proxy' require 'celluloid/actor' require 'celluloid/future' require 'celluloid/pool_manager' require 'celluloid/supervision_group' require 'celluloid/supervisor' require 'celluloid/notifications' require 'celluloid/logging' require 'celluloid/legacy' unless defined?(CELLULOID_FUTURE) # Configure default systemwide settings Celluloid.task_class = Celluloid::TaskFiber Celluloid.logger = Logger.new(STDERR) Celluloid.shutdown_timeout = 10 unless $CELLULOID_TEST Celluloid.register_shutdown Celluloid.init end celluloid-0.15.2/lib/celluloid/0000755000004100000410000000000012245176057016336 5ustar www-datawww-datacelluloid-0.15.2/lib/celluloid/core_ext.rb0000644000004100000410000000017112245176057020472 0ustar www-datawww-datarequire 'celluloid/fiber' class Thread attr_accessor :uuid_counter, :uuid_limit def celluloid? false end end celluloid-0.15.2/lib/celluloid/proxies/0000755000004100000410000000000012245176057020027 5ustar www-datawww-datacelluloid-0.15.2/lib/celluloid/proxies/sync_proxy.rb0000644000004100000410000000156012245176057022573 0ustar www-datawww-datamodule Celluloid # A proxy which sends synchronous calls to an actor class SyncProxy < AbstractProxy attr_reader :mailbox # Used for reflecting on proxy objects themselves def __class__; SyncProxy; end def initialize(mailbox, klass) @mailbox, @klass = mailbox, klass end def inspect "#" end def respond_to?(meth, include_private = false) __class__.instance_methods.include?(meth) || super end def method_missing(meth, *args, &block) unless @mailbox.alive? raise DeadActorError, "attempted to call a dead actor" end if @mailbox == ::Thread.current[:celluloid_mailbox] args.unshift meth meth = :__send__ end call = SyncCall.new(::Celluloid.mailbox, meth, args, block) @mailbox << call call.value end end end celluloid-0.15.2/lib/celluloid/proxies/actor_proxy.rb0000644000004100000410000000341212245176057022725 0ustar www-datawww-datamodule Celluloid # A proxy object returned from Celluloid::Actor.new/new_link which converts # the normal Ruby method protocol into an inter-actor message protocol class ActorProxy < SyncProxy attr_reader :thread # Used for reflecting on proxy objects themselves def __class__; ActorProxy; end def initialize(actor) @thread = actor.thread super(actor.mailbox, actor.subject.class.to_s) @sync_proxy = SyncProxy.new(@mailbox, @klass) @async_proxy = AsyncProxy.new(@mailbox, @klass) @future_proxy = FutureProxy.new(@mailbox, @klass) end def _send_(meth, *args, &block) method_missing :__send__, meth, *args, &block end def inspect method_missing :inspect rescue DeadActorError "#" end def method(name) Method.new(self, name) end def alive? @mailbox.alive? end alias_method :sync, :method_missing # Obtain an async proxy or explicitly invoke a named async method def async(method_name = nil, *args, &block) if method_name @async_proxy.method_missing method_name, *args, &block else @async_proxy end end # Obtain a future proxy or explicitly invoke a named future method def future(method_name = nil, *args, &block) if method_name @future_proxy.method_missing method_name, *args, &block else @future_proxy end end # Terminate the associated actor def terminate terminate! Actor.join(self) nil end # Terminate the associated actor asynchronously def terminate! ::Kernel.raise DeadActorError, "actor already terminated" unless alive? @mailbox << TerminationRequest.new end end end celluloid-0.15.2/lib/celluloid/proxies/future_proxy.rb0000644000004100000410000000140612245176057023130 0ustar www-datawww-datamodule Celluloid # A proxy which creates future calls to an actor class FutureProxy < AbstractProxy attr_reader :mailbox # Used for reflecting on proxy objects themselves def __class__; FutureProxy; end def initialize(mailbox, klass) @mailbox, @klass = mailbox, klass end def inspect "#" end def method_missing(meth, *args, &block) unless @mailbox.alive? raise DeadActorError, "attempted to call a dead actor" end if block_given? # FIXME: nicer exception raise "Cannot use blocks with futures yet" end future = Future.new call = SyncCall.new(future, meth, args, block) @mailbox << call future end end end celluloid-0.15.2/lib/celluloid/proxies/block_proxy.rb0000644000004100000410000000131512245176057022707 0ustar www-datawww-datamodule Celluloid class BlockProxy def initialize(call, mailbox, block) @call = call @mailbox = mailbox @block = block @execution = :sender end attr_writer :execution attr_reader :call, :block def to_proc if @execution == :sender lambda do |*values| if task = Thread.current[:celluloid_task] @mailbox << BlockCall.new(self, Actor.current.mailbox, values) # TODO: if respond fails, the Task will never be resumed task.suspend(:invokeblock) else # FIXME: better exception raise "No task to suspend" end end else @block end end end end celluloid-0.15.2/lib/celluloid/proxies/abstract_proxy.rb0000644000004100000410000000127412245176057023424 0ustar www-datawww-datamodule Celluloid # Base class of all Celluloid proxies class AbstractProxy < BasicObject # Used for reflecting on proxy objects themselves def __class__; AbstractProxy; end # Needed for storing proxies in data structures needed = [:object_id, :__id__, :hash] - instance_methods if needed.any? include ::Kernel.dup.module_eval { undef_method(*(instance_methods - needed)) self } # rubinius bug? These methods disappear when we include hacked kernel define_method :==, ::BasicObject.instance_method(:==) unless instance_methods.include?(:==) alias_method(:equal?, :==) unless instance_methods.include?(:equal?) end end end celluloid-0.15.2/lib/celluloid/proxies/async_proxy.rb0000644000004100000410000000132412245176057022732 0ustar www-datawww-datamodule Celluloid # A proxy which sends asynchronous calls to an actor class AsyncProxy < AbstractProxy attr_reader :mailbox # Used for reflecting on proxy objects themselves def __class__; AsyncProxy; end def initialize(mailbox, klass) @mailbox, @klass = mailbox, klass end def inspect "#" end def method_missing(meth, *args, &block) if @mailbox == ::Thread.current[:celluloid_mailbox] args.unshift meth meth = :__send__ end if block_given? # FIXME: nicer exception raise "Cannot use blocks with async yet" end @mailbox << AsyncCall.new(meth, args, block) end end end celluloid-0.15.2/lib/celluloid/condition.rb0000644000004100000410000000343612245176057020657 0ustar www-datawww-datamodule Celluloid class ConditionError < Celluloid::Error; end # ConditionVariable-like signaling between tasks and threads class Condition class Waiter def initialize(condition, task, mailbox) @condition = condition @task = task @mailbox = mailbox end attr_reader :condition, :task def <<(message) @mailbox << message end def wait message = @mailbox.receive do |msg| msg.is_a?(SignalConditionRequest) && msg.task == Thread.current end message.value end end def initialize @mutex = Mutex.new @waiters = [] end # Wait for the given signal and return the associated value def wait raise ConditionError, "cannot wait for signals while exclusive" if Celluloid.exclusive? if Thread.current[:celluloid_actor] task = Task.current else task = Thread.current end waiter = Waiter.new(self, task, Celluloid.mailbox) @mutex.synchronize do @waiters << waiter end result = Celluloid.suspend :condwait, waiter raise result if result.is_a? ConditionError result end # Send a signal to the first task waiting on this condition def signal(value = nil) @mutex.synchronize do if waiter = @waiters.shift waiter << SignalConditionRequest.new(waiter.task, value) else Logger.debug("Celluloid::Condition signaled spuriously") end end end # Broadcast a value to all waiting tasks and threads def broadcast(value = nil) @mutex.synchronize do @waiters.each { |waiter| waiter << SignalConditionRequest.new(waiter.task, value) } @waiters.clear end end alias_method :inspect, :to_s end end celluloid-0.15.2/lib/celluloid/uuid.rb0000644000004100000410000000212412245176057017630 0ustar www-datawww-datarequire 'securerandom' module Celluloid # Clearly Ruby doesn't have enough UUID libraries # This one aims to be fast and simple with good support for multiple threads # If there's a better UUID library I can use with similar multithreaded # performance, I certainly wouldn't mind using a gem for this! module UUID values = SecureRandom.hex(9).match(/(.{8})(.{4})(.{3})(.{3})/) PREFIX = "#{values[1]}-#{values[2]}-4#{values[3]}-8#{values[4]}".freeze BLOCK_SIZE = 0x10000 @counter = 0 @counter_mutex = Mutex.new def self.generate thread = Thread.current unless thread.uuid_limit @counter_mutex.synchronize do block_base = @counter @counter += BLOCK_SIZE thread.uuid_counter = block_base thread.uuid_limit = @counter - 1 end end counter = thread.uuid_counter if thread.uuid_counter >= thread.uuid_limit thread.uuid_counter = thread.uuid_limit = nil else thread.uuid_counter += 1 end "#{PREFIX}-#{sprintf("%012x", counter)}".freeze end end end celluloid-0.15.2/lib/celluloid/autostart.rb0000644000004100000410000000004512245176057020710 0ustar www-datawww-datarequire 'celluloid' Celluloid.start celluloid-0.15.2/lib/celluloid/call_chain.rb0000644000004100000410000000044312245176057020741 0ustar www-datawww-datamodule Celluloid class CallChain def self.current_id=(value) Thread.current[:celluloid_chain_id] = value task = Thread.current[:celluloid_task] task.chain_id = value if task end def self.current_id Thread.current[:celluloid_chain_id] end end endcelluloid-0.15.2/lib/celluloid/system_events.rb0000644000004100000410000000277712245176057021610 0ustar www-datawww-datamodule Celluloid # High-priority internal system events class SystemEvent; end # Request to link with another actor class LinkingRequest < SystemEvent attr_reader :actor, :type def initialize(actor, type) @actor, @type = actor, type.to_sym raise ArgumentError, "type must be link or unlink" unless [:link, :unlink].include?(@type) end def process(links) case type when :link then links << actor when :unlink then links.delete actor end actor.mailbox << LinkingResponse.new(Actor.current, type) end end # Response to a link request class LinkingResponse attr_reader :actor, :type def initialize(actor, type) @actor, @type = actor, type.to_sym raise ArgumentError, "type must be link or unlink" unless [:link, :unlink].include?(@type) end end # An actor has exited for the given reason class ExitEvent < SystemEvent attr_reader :actor, :reason def initialize(actor, reason = nil) @actor, @reason = actor, reason end end # Name an actor at the time it's registered class NamingRequest < SystemEvent attr_reader :name def initialize(name) @name = name end end # Request for an actor to terminate class TerminationRequest < SystemEvent; end # Signal a condition class SignalConditionRequest < SystemEvent def initialize(task, value) @task, @value = task, value end attr_reader :task, :value def call @task.resume(@value) end end end celluloid-0.15.2/lib/celluloid/evented_mailbox.rb0000644000004100000410000000365312245176057022037 0ustar www-datawww-datamodule Celluloid # An alternative implementation of Celluloid::Mailbox using Reactor class EventedMailbox < Celluloid::Mailbox attr_reader :reactor def initialize(reactor_class) super() # @condition won't be used in the class. @reactor = reactor_class.new end # Add a message to the Mailbox def <<(message) @mutex.lock begin if mailbox_full || @dead dead_letter(message) return end if message.is_a?(SystemEvent) # SystemEvents are high priority messages so they get added to the # head of our message queue instead of the end @messages.unshift message else @messages << message end current_actor = Thread.current[:celluloid_actor] @reactor.wakeup unless current_actor && current_actor.mailbox == self rescue IOError Logger.crash "reactor crashed", $! dead_letter(message) ensure @mutex.unlock rescue nil end nil end # Receive a message from the Mailbox def receive(timeout = nil, &block) message = next_message(block) until message if timeout now = Time.now wait_until ||= now + timeout wait_interval = wait_until - now return if wait_interval < 0 else wait_interval = nil end @reactor.run_once(wait_interval) message = next_message(block) end message rescue IOError raise MailboxShutdown, "mailbox shutdown called during receive" end # Obtain the next message from the mailbox that matches the given block def next_message(block) @mutex.lock begin super(&block) ensure @mutex.unlock rescue nil end end # Cleanup any IO objects this Mailbox may be using def shutdown super do @reactor.shutdown end end end end celluloid-0.15.2/lib/celluloid/internal_pool.rb0000644000004100000410000000554612245176057021542 0ustar www-datawww-datarequire 'thread' module Celluloid # Maintain a thread pool FOR SPEED!! class InternalPool attr_accessor :max_idle def initialize @group = ThreadGroup.new @mutex = Mutex.new @threads = [] # TODO: should really adjust this based on usage @max_idle = 16 @running = true end def busy_size @threads.select(&:busy).size end def idle_size @threads.reject(&:busy).size end def assert_running unless running? raise Error, "Thread pool is not running" end end def assert_inactive if active? message = "Thread pool is still active" if defined?(JRUBY_VERSION) Celluloid.logger.warn message else raise Error, message end end end def running? @running end def active? to_a.any? end def each @threads.each do |thread| yield thread end end def to_a @threads end # Get a thread from the pool, running the given block def get(&block) @mutex.synchronize do assert_running begin idle = @threads.reject(&:busy) if idle.empty? thread = create else thread = idle.first end end until thread.status # handle crashed threads thread.busy = true thread[:celluloid_queue] << block thread end end # Return a thread to the pool def put(thread) @mutex.synchronize do thread.busy = false if idle_size >= @max_idle thread[:celluloid_queue] << nil @threads.delete(thread) else clean_thread_locals(thread) end end end # Create a new thread with an associated queue of procs to run def create queue = Queue.new thread = Thread.new do while proc = queue.pop begin proc.call rescue => ex Logger.crash("thread crashed", ex) end put thread end end thread[:celluloid_queue] = queue @threads << thread @group.add(thread) thread end # Clean the thread locals of an incoming thread def clean_thread_locals(thread) thread.keys.each do |key| next if key == :celluloid_queue # Ruby seems to lack an API for deleting thread locals. WTF, Ruby? thread[key] = nil end end def shutdown @mutex.synchronize do finalize @threads.each do |thread| thread[:celluloid_queue] << nil end end end def kill @mutex.synchronize do finalize @running = false @threads.shift.kill until @threads.empty? @group.list.each(&:kill) end end private def finalize @max_idle = 0 end end end celluloid-0.15.2/lib/celluloid/mailbox.rb0000644000004100000410000000664112245176057020325 0ustar www-datawww-datarequire 'thread' module Celluloid class MailboxDead < Celluloid::Error; end # you can't receive from the dead class MailboxShutdown < Celluloid::Error; end # raised if the mailbox can no longer be used # Actors communicate with asynchronous messages. Messages are buffered in # Mailboxes until Actors can act upon them. class Mailbox include Enumerable # A unique address at which this mailbox can be found attr_reader :address attr_accessor :max_size def initialize @address = Celluloid.uuid @messages = [] @mutex = Mutex.new @dead = false @condition = ConditionVariable.new @max_size = nil end # Add a message to the Mailbox def <<(message) @mutex.lock begin if mailbox_full || @dead dead_letter(message) return end if message.is_a?(SystemEvent) # SystemEvents are high priority messages so they get added to the # head of our message queue instead of the end @messages.unshift message else @messages << message end @condition.signal nil ensure @mutex.unlock rescue nil end end # Receive a message from the Mailbox def receive(timeout = nil, &block) message = nil @mutex.lock begin raise MailboxDead, "attempted to receive from a dead mailbox" if @dead begin message = next_message(&block) unless message if timeout now = Time.now wait_until ||= now + timeout wait_interval = wait_until - now return if wait_interval <= 0 else wait_interval = nil end @condition.wait(@mutex, wait_interval) end end until message message ensure @mutex.unlock rescue nil end end # Retrieve the next message in the mailbox def next_message message = nil if block_given? index = @messages.index do |msg| yield(msg) || msg.is_a?(SystemEvent) end message = @messages.slice!(index, 1).first if index else message = @messages.shift end message end # Shut down this mailbox and clean up its contents def shutdown raise MailboxDead, "mailbox already shutdown" if @dead @mutex.lock begin yield if block_given? messages = @messages @messages = [] @dead = true ensure @mutex.unlock rescue nil end messages.each do |msg| dead_letter msg msg.cleanup if msg.respond_to? :cleanup end true end # Is the mailbox alive? def alive? !@dead end # Cast to an array def to_a @mutex.synchronize { @messages.dup } end # Iterate through the mailbox def each(&block) to_a.each(&block) end # Inspect the contents of the Mailbox def inspect "#<#{self.class}:#{object_id.to_s(16)} @messages=[#{map { |m| m.inspect }.join(', ')}]>" end # Number of messages in the Mailbox def size @mutex.synchronize { @messages.size } end private def dead_letter(message) Logger.debug "Discarded message (mailbox is dead): #{message}" if $CELLULOID_DEBUG end def mailbox_full @max_size && @messages.size >= @max_size end end end celluloid-0.15.2/lib/celluloid/future.rb0000644000004100000410000000444312245176057020202 0ustar www-datawww-datarequire 'thread' module Celluloid # Celluloid::Future objects allow methods and blocks to run in the # background, their values requested later class Future def self.new(*args, &block) return super unless block future = new Celluloid::ThreadHandle.new(:future) do begin call = SyncCall.new(future, :call, args) call.dispatch(block) rescue # Exceptions in blocks will get raised when the value is retrieved end end future end attr_reader :address def initialize @address = Celluloid.uuid @mutex = Mutex.new @ready = false @result = nil @forwards = nil end # Check if this future has a value yet def ready? @ready end # Obtain the value for this Future def value(timeout = nil) ready = result = nil begin @mutex.lock if @ready ready = true result = @result else case @forwards when Array @forwards << Celluloid.mailbox when NilClass @forwards = Celluloid.mailbox else @forwards = [@forwards, Celluloid.mailbox] end end ensure @mutex.unlock end unless ready result = Celluloid.receive(timeout) do |msg| msg.is_a?(Future::Result) && msg.future == self end end if result result.value else raise TimeoutError, "Timed out" end end alias_method :call, :value # Signal this future with the given result value def signal(value) result = Result.new(value, self) @mutex.synchronize do raise "the future has already happened!" if @ready if @forwards @forwards.is_a?(Array) ? @forwards.each { |f| f << result } : @forwards << result end @result = result @ready = true end end alias_method :<<, :signal # Inspect this Celluloid::Future alias_method :inspect, :to_s # Wrapper for result values to distinguish them in mailboxes class Result attr_reader :future def initialize(result, future) @result, @future = result, future end def value @result.value end end end end celluloid-0.15.2/lib/celluloid/calls.rb0000644000004100000410000001015712245176057017765 0ustar www-datawww-datamodule Celluloid # Calls represent requests to an actor class Call attr_reader :method, :arguments, :block def initialize(method, arguments = [], block = nil) @method, @arguments = method, arguments if block if Celluloid.exclusive? # FIXME: nicer exception raise "Cannot execute blocks on sender in exclusive mode" end @block = BlockProxy.new(self, Celluloid.mailbox, block) else @block = nil end end def execute_block_on_receiver @block && @block.execution = :receiver end def dispatch(obj) _block = @block && @block.to_proc obj.public_send(@method, *@arguments, &_block) rescue NoMethodError => ex # Abort if the sender made a mistake raise AbortError.new(ex) unless obj.respond_to? @method # Otherwise something blew up. Crash this actor raise rescue ArgumentError => ex # Abort if the sender made a mistake begin arity = obj.method(@method).arity rescue NameError # In theory this shouldn't happen, but just in case raise AbortError.new(ex) end if arity >= 0 raise AbortError.new(ex) if @arguments.size != arity elsif arity < -1 mandatory_args = -arity - 1 raise AbortError.new(ex) if arguments.size < mandatory_args end # Otherwise something blew up. Crash this actor raise end end # Synchronous calls wait for a response class SyncCall < Call attr_reader :sender, :task, :chain_id def initialize(sender, method, arguments = [], block = nil, task = Thread.current[:celluloid_task], chain_id = CallChain.current_id) super(method, arguments, block) @sender = sender @task = task @chain_id = chain_id || Celluloid.uuid end def dispatch(obj) CallChain.current_id = @chain_id result = super(obj) respond SuccessResponse.new(self, result) rescue Exception => ex # Exceptions that occur during synchronous calls are reraised in the # context of the sender respond ErrorResponse.new(self, ex) # Aborting indicates a protocol error on the part of the sender # It should crash the sender, but the exception isn't reraised # Otherwise, it's a bug in this actor and should be reraised raise unless ex.is_a?(AbortError) ensure CallChain.current_id = nil end def cleanup exception = DeadActorError.new("attempted to call a dead actor") respond ErrorResponse.new(self, exception) end def respond(message) @sender << message end def value Celluloid.suspend(:callwait, self).value end def wait loop do message = Celluloid.mailbox.receive do |msg| msg.respond_to?(:call) and msg.call == self end if message.is_a?(SystemEvent) Thread.current[:celluloid_actor].handle_system_event(message) else # FIXME: add check for receiver block execution if message.respond_to?(:value) # FIXME: disable block execution if on :sender and (exclusive or outside of task) # probably now in Call break message else message.dispatch end end end end end # Asynchronous calls don't wait for a response class AsyncCall < Call def dispatch(obj) CallChain.current_id = Celluloid.uuid super(obj) rescue AbortError => ex # Swallow aborted async calls, as they indicate the sender made a mistake Logger.debug("#{obj.class}: async call `#@method` aborted!\n#{Logger.format_exception(ex.cause)}") ensure CallChain.current_id = nil end end class BlockCall def initialize(block_proxy, sender, arguments, task = Thread.current[:celluloid_task]) @block_proxy = block_proxy @sender = sender @arguments = arguments @task = task end attr_reader :task def call @block_proxy.call end def dispatch response = @block_proxy.block.call(*@arguments) @sender << BlockResponse.new(self, response) end end end celluloid-0.15.2/lib/celluloid/links.rb0000644000004100000410000000134312245176057020004 0ustar www-datawww-datamodule Celluloid # Linked actors send each other system events class Links include Enumerable def initialize @links = {} end # Add an actor to the current links def <<(actor) @links[actor.mailbox.address] = actor end # Do links include the given actor? def include?(actor) @links.has_key? actor.mailbox.address end # Remove an actor from the links def delete(actor) @links.delete actor.mailbox.address end # Iterate through all links def each @links.each { |_, actor| yield(actor) } end # Generate a string representation def inspect links = self.map(&:inspect).join(',') "#<#{self.class}[#{links}]>" end end end celluloid-0.15.2/lib/celluloid/thread_handle.rb0000644000004100000410000000251012245176057021443 0ustar www-datawww-datamodule Celluloid # An abstraction around threads from the InternalPool which ensures we don't # accidentally do things to threads which have been returned to the pool, # such as, say, killing them class ThreadHandle def initialize(role = nil) @mutex = Mutex.new @join = ConditionVariable.new @thread = Celluloid.internal_pool.get do Thread.current.role = role begin yield ensure @mutex.synchronize do @thread = nil @join.broadcast end end end end # Is the thread running? def alive? @mutex.synchronize { @thread.alive? if @thread } end # Forcibly kill the thread def kill !!@mutex.synchronize { @thread.kill if @thread } self end # Join to a running thread, blocking until it terminates def join(limit = nil) raise ThreadError, "Target thread must not be current thread" if @thread == Thread.current @mutex.synchronize { @join.wait(@mutex, limit) if @thread } self end # Obtain the backtrace for this thread def backtrace @thread.backtrace rescue NoMethodError # undefined method `backtrace' for nil:NilClass # Swallow this in case this ThreadHandle was terminated and @thread was # set to nil end end end celluloid-0.15.2/lib/celluloid/thread.rb0000644000004100000410000000132512245176057020133 0ustar www-datawww-datarequire 'celluloid/fiber' module Celluloid class Thread < ::Thread def celluloid? true end attr_accessor :busy # Obtain the role of this thread def role self[:celluloid_role] end def role=(role) self[:celluloid_role] = role end # Obtain the Celluloid::Actor object for this thread def actor self[:celluloid_actor] end # Obtain the Celluloid task object for this thread def task self[:celluloid_task] end # Obtain the Celluloid mailbox for this thread def mailbox self[:celluloid_mailbox] end # Obtain the call chain ID for this thread def call_chain_id self[:celluloid_chain_id] end end end celluloid-0.15.2/lib/celluloid/properties.rb0000644000004100000410000000137512245176057021065 0ustar www-datawww-datamodule Celluloid # Properties define inheritable attributes of classes, somewhat similar to # Rails cattr_*/mattr_* or class_attribute module Properties def property(name, opts = {}) default = opts.fetch(:default, nil) multi = opts.fetch(:multi, false) ivar_name = "@#{name}".to_sym ancestors.first.send(:define_singleton_method, name) do |value = nil, *extra| if value value = value ? [value, *extra] : [] if multi instance_variable_set(ivar_name, value) elsif instance_variables.include?(ivar_name) instance_variable_get(ivar_name) elsif superclass.respond_to? name superclass.send(name) else default end end end end endcelluloid-0.15.2/lib/celluloid/logger.rb0000644000004100000410000000273112245176057020145 0ustar www-datawww-datamodule Celluloid module Logger @exception_handlers = [] module_function # Send a debug message def debug(string) Celluloid.logger.debug(string) if Celluloid.logger end # Send a info message def info(string) Celluloid.logger.info(string) if Celluloid.logger end # Send a warning message def warn(string) Celluloid.logger.warn(string) if Celluloid.logger end # Send an error message def error(string) Celluloid.logger.error(string) if Celluloid.logger end # Handle a crash def crash(string, exception) string << "\n" << format_exception(exception) error string @exception_handlers.each do |handler| begin handler.call(exception) rescue => ex error "EXCEPTION HANDLER CRASHED:\n" << format_exception(ex) end end end # Note a deprecation def deprecate(message) trace = caller.join("\n\t") warn "DEPRECATION WARNING: #{message}\n\t#{trace}" end # Define an exception handler # NOTE: These should be defined at application start time def exception_handler(&block) @exception_handlers << block nil end # Format an exception message def format_exception(exception) str = "#{exception.class}: #{exception.to_s}\n\t" if exception.backtrace str << exception.backtrace.join("\n\t") else str << "EMPTY BACKTRACE\n\t" end end end end celluloid-0.15.2/lib/celluloid/actor.rb0000644000004100000410000002720412245176057020000 0ustar www-datawww-datarequire 'timers' module Celluloid # Don't do Actor-like things outside Actor scope class NotActorError < Celluloid::Error; end # Trying to do something to a dead actor class DeadActorError < Celluloid::Error; end # A timeout occured before the given request could complete class TimeoutError < Celluloid::Error; end # The sender made an error, not the current actor class AbortError < Celluloid::Error attr_reader :cause def initialize(cause) @cause = cause super "caused by #{cause.inspect}: #{cause.to_s}" end end LINKING_TIMEOUT = 5 # linking times out after 5 seconds OWNER_IVAR = :@celluloid_owner # reference to owning actor # Actors are Celluloid's concurrency primitive. They're implemented as # normal Ruby objects wrapped in threads which communicate with asynchronous # messages. class Actor attr_reader :subject, :proxy, :tasks, :links, :mailbox, :thread, :name class << self extend Forwardable def_delegators "Celluloid::Registry.root", :[], :[]= def registered Registry.root.names end def clear_registry Registry.root.clear end # Obtain the current actor def current actor = Thread.current[:celluloid_actor] raise NotActorError, "not in actor scope" unless actor actor.proxy end # Obtain the name of the current actor def name actor = Thread.current[:celluloid_actor] raise NotActorError, "not in actor scope" unless actor actor.name end # Invoke a method on the given actor via its mailbox def call(mailbox, meth, *args, &block) proxy = SyncProxy.new(mailbox, "UnknownClass") proxy.method_missing(meth, *args, &block) end # Invoke a method asynchronously on an actor via its mailbox def async(mailbox, meth, *args, &block) proxy = AsyncProxy.new(mailbox, "UnknownClass") proxy.method_missing(meth, *args, &block) end # Call a method asynchronously and retrieve its value later def future(mailbox, meth, *args, &block) proxy = FutureProxy.new(mailbox, "UnknownClass") proxy.method_missing(meth, *args, &block) end # Obtain all running actors in the system def all actors = [] Celluloid.internal_pool.each do |t| next unless t.role == :actor actors << t.actor.proxy if t.actor && t.actor.respond_to?(:proxy) end actors end # Watch for exit events from another actor def monitor(actor) raise NotActorError, "can't link outside actor context" unless Celluloid.actor? Thread.current[:celluloid_actor].linking_request(actor, :link) end # Stop waiting for exit events from another actor def unmonitor(actor) raise NotActorError, "can't link outside actor context" unless Celluloid.actor? Thread.current[:celluloid_actor].linking_request(actor, :unlink) end # Link to another actor def link(actor) monitor actor Thread.current[:celluloid_actor].links << actor end # Unlink from another actor def unlink(actor) unmonitor actor Thread.current[:celluloid_actor].links.delete actor end # Are we monitoring the given actor? def monitoring?(actor) actor.links.include? Actor.current end # Are we bidirectionally linked to the given actor? def linked_to?(actor) monitoring?(actor) && Thread.current[:celluloid_actor].links.include?(actor) end # Forcibly kill a given actor def kill(actor) actor.thread.kill actor.mailbox.shutdown if actor.mailbox.alive? end # Wait for an actor to terminate def join(actor, timeout = nil) actor.thread.join(timeout) actor end end # Wrap the given subject with an Actor def initialize(subject, options = {}) @subject = subject @mailbox = options.fetch(:mailbox_class, Mailbox).new @mailbox.max_size = options.fetch(:mailbox_size, nil) @task_class = options[:task_class] || Celluloid.task_class @exit_handler = options[:exit_handler] @exclusives = options[:exclusive_methods] @receiver_block_executions = options[:receiver_block_executions] @tasks = TaskSet.new @links = Links.new @signals = Signals.new @receivers = Receivers.new @timers = Timers.new @running = true @exclusive = false @name = nil @thread = ThreadHandle.new(:actor) do setup_thread run end @proxy = (options[:proxy_class] || ActorProxy).new(self) @subject.instance_variable_set(OWNER_IVAR, self) end def setup_thread Thread.current[:celluloid_actor] = self Thread.current[:celluloid_mailbox] = @mailbox end # Run the actor loop def run begin while @running if message = @mailbox.receive(timeout_interval) handle_message message else # No message indicates a timeout @timers.fire @receivers.fire_timers end end rescue MailboxShutdown # If the mailbox detects shutdown, exit the actor end shutdown rescue Exception => ex handle_crash(ex) raise unless ex.is_a? StandardError end # Terminate this actor def terminate @running = false end # Perform a linking request with another actor def linking_request(receiver, type) Celluloid.exclusive do start_time = Time.now receiver.mailbox << LinkingRequest.new(Actor.current, type) system_events = [] loop do wait_interval = start_time + LINKING_TIMEOUT - Time.now message = @mailbox.receive(wait_interval) do |msg| msg.is_a?(LinkingResponse) && msg.actor.mailbox.address == receiver.mailbox.address && msg.type == type end case message when LinkingResponse # We're done! system_events.each { |ev| handle_system_event(ev) } return when NilClass raise TimeoutError, "linking timeout of #{LINKING_TIMEOUT} seconds exceeded" when SystemEvent # Queue up pending system events to be processed after we've successfully linked system_events << message else raise 'wtf' end end end end # Send a signal with the given name to all waiting methods def signal(name, value = nil) @signals.broadcast name, value end # Wait for the given signal def wait(name) @signals.wait name end # Receive an asynchronous message def receive(timeout = nil, &block) loop do message = @receivers.receive(timeout, &block) break message unless message.is_a?(SystemEvent) handle_system_event(message) end end # How long to wait until the next timer fires def timeout_interval i1 = @timers.wait_interval i2 = @receivers.wait_interval if i1 and i2 i1 < i2 ? i1 : i2 elsif i1 i1 else i2 end end # Schedule a block to run at the given time def after(interval, &block) @timers.after(interval) { task(:timer, &block) } end # Schedule a block to run at the given time def every(interval, &block) @timers.every(interval) { task(:timer, &block) } end def timeout(duration) bt = caller task = Task.current timer = @timers.after(duration) do exception = Task::TimeoutError.new("execution expired") exception.set_backtrace bt task.resume exception end yield ensure timer.cancel if timer end class Sleeper def initialize(timers, interval) @timers = timers @interval = interval end def before_suspend(task) @timers.after(@interval) { task.resume } end def wait Kernel.sleep(@interval) end end # Sleep for the given amount of time def sleep(interval) sleeper = Sleeper.new(@timers, interval) Celluloid.suspend(:sleeping, sleeper) end # Handle standard low-priority messages def handle_message(message) case message when SystemEvent handle_system_event message when Call meth = message.method if meth == :__send__ meth = message.arguments.first end if @receiver_block_executions && meth if @receiver_block_executions.include?(meth.to_sym) message.execute_block_on_receiver end end task(:call, :method_name => meth, :dangerous_suspend => meth == :initialize) { message.dispatch(@subject) } when BlockCall task(:invoke_block) { message.dispatch } when BlockResponse, Response message.dispatch else unless @receivers.handle_message(message) Logger.debug "Discarded message (unhandled): #{message}" if $CELLULOID_DEBUG end end message end # Handle high-priority system event messages def handle_system_event(event) case event when ExitEvent task(:exit_handler, :method_name => @exit_handler) { handle_exit_event event } when LinkingRequest event.process(links) when NamingRequest @name = event.name when TerminationRequest terminate when SignalConditionRequest event.call end end # Handle exit events received by this actor def handle_exit_event(event) @links.delete event.actor # Run the exit handler if available return @subject.send(@exit_handler, event.actor, event.reason) if @exit_handler # Reraise exceptions from linked actors # If no reason is given, actor terminated cleanly raise event.reason if event.reason end # Handle any exceptions that occur within a running actor def handle_crash(exception) Logger.crash("#{@subject.class} crashed!", exception) shutdown ExitEvent.new(@proxy, exception) rescue => ex Logger.crash("#{@subject.class}: ERROR HANDLER CRASHED!", ex) end # Handle cleaning up this actor after it exits def shutdown(exit_event = ExitEvent.new(@proxy)) run_finalizer cleanup exit_event ensure Thread.current[:celluloid_actor] = nil Thread.current[:celluloid_mailbox] = nil end # Run the user-defined finalizer, if one is set def run_finalizer finalizer = @subject.class.finalizer return unless finalizer && @subject.respond_to?(finalizer, true) task(:finalizer, :method_name => finalizer, :dangerous_suspend => true) do begin @subject.__send__(finalizer) rescue => ex Logger.crash("#{@subject.class}#finalize crashed!", ex) end end end # Clean up after this actor def cleanup(exit_event) @mailbox.shutdown @links.each do |actor| if actor.mailbox.alive? actor.mailbox << exit_event end end tasks.to_a.each { |task| task.terminate } rescue => ex Logger.crash("#{@subject.class}: CLEANUP CRASHED!", ex) end # Run a method inside a task unless it's exclusive def task(task_type, meta = nil) method_name = meta && meta.fetch(:method_name, nil) @task_class.new(task_type, meta) { if @exclusives && (@exclusives == :all || (method_name && @exclusives.include?(method_name.to_sym))) Celluloid.exclusive { yield } else yield end }.resume end end end celluloid-0.15.2/lib/celluloid/fsm.rb0000644000004100000410000001206412245176057017453 0ustar www-datawww-datamodule Celluloid # Simple finite state machines with integrated Celluloid timeout support # Inspired by Erlang's gen_fsm (http://www.erlang.org/doc/man/gen_fsm.html) # # Basic usage: # # class MyMachine # include Celluloid::FSM # NOTE: this does NOT pull in the Celluloid module # end # # Inside an actor: # # # # machine = MyMachine.new(current_actor) module FSM class UnattachedError < Celluloid::Error; end # Not attached to an actor DEFAULT_STATE = :default # Default state name unless one is explicitly set # Included hook to extend class methods def self.included(klass) klass.send :extend, ClassMethods end module ClassMethods # Obtain or set the default state # Passing a state name sets the default state def default_state(new_default = nil) if new_default @default_state = new_default.to_sym else defined?(@default_state) ? @default_state : DEFAULT_STATE end end # Obtain the valid states for this FSM def states @states ||= {} end # Declare an FSM state and optionally provide a callback block to fire # Options: # * to: a state or array of states this state can transition to def state(*args, &block) if args.last.is_a? Hash # Stringify keys :/ options = args.pop.inject({}) { |h,(k,v)| h[k.to_s] = v; h } else options = {} end args.each do |name| name = name.to_sym default_state name if options['default'] states[name] = State.new(name, options['to'], &block) end end end attr_reader :actor # Be kind and call super if you must redefine initialize def initialize(actor = nil) @state = self.class.default_state @delayed_transition = nil @actor = actor @actor ||= Celluloid.current_actor if Celluloid.actor? end # Obtain the current state of the FSM attr_reader :state # Attach this FSM to an actor. This allows FSMs to wait for and initiate # events in the context of a particular actor def attach(actor) @actor = actor end alias_method :actor=, :attach # Transition to another state # Options: # * delay: don't transition immediately, wait the given number of seconds. # This will return a Celluloid::Timer object you can use to # cancel the pending state transition. # # Note: making additional state transitions will cancel delayed transitions def transition(state_name, options = {}) new_state = validate_and_sanitize_new_state(state_name) return unless new_state if handle_delayed_transitions(new_state, options[:delay]) return @delayed_transition end transition_with_callbacks!(new_state) end # Immediate state transition with no sanity checks, or callbacks. "Dangerous!" def transition!(state_name) @state = state_name end protected def validate_and_sanitize_new_state(state_name) state_name = state_name.to_sym return if current_state_name == state_name if current_state and not current_state.valid_transition? state_name valid = current_state.transitions.map(&:to_s).join(", ") raise ArgumentError, "#{self.class} can't change state from '#{@state}' to '#{state_name}', only to: #{valid}" end new_state = states[state_name] unless new_state return if state_name == default_state raise ArgumentError, "invalid state for #{self.class}: #{state_name}" end new_state end def transition_with_callbacks!(state_name) transition! state_name.name state_name.call(self) end def states self.class.states end def default_state self.class.default_state end def current_state states[@state] end def current_state_name current_state && current_state.name || '' end def handle_delayed_transitions(new_state, delay) if delay raise UnattachedError, "can't delay unless attached" unless @actor @delayed_transition.cancel if @delayed_transition @delayed_transition = @actor.after(delay) do transition_with_callbacks!(new_state) end return @delayed_transition end if defined?(@delayed_transition) and @delayed_transition @delayed_transition.cancel @delayed_transition = nil end end # FSM states as declared by Celluloid::FSM.state class State attr_reader :name, :transitions def initialize(name, transitions = nil, &block) @name, @block = name, block @transitions = nil @transitions = Array(transitions).map { |t| t.to_sym } if transitions end def call(obj) obj.instance_eval(&@block) if @block end def valid_transition?(new_state) # All transitions are allowed unless expressly return true unless @transitions @transitions.include? new_state.to_sym end end end end celluloid-0.15.2/lib/celluloid/test.rb0000644000004100000410000000005412245176057017641 0ustar www-datawww-data$CELLULOID_TEST = true require 'celluloid' celluloid-0.15.2/lib/celluloid/logging.rb0000644000004100000410000000031512245176057020310 0ustar www-datawww-datarequire 'celluloid/logging/log_event' require 'celluloid/logging/incident' require 'celluloid/logging/ring_buffer' require 'celluloid/logging/incident_logger' require 'celluloid/logging/incident_reporter' celluloid-0.15.2/lib/celluloid/fiber.rb0000644000004100000410000000171212245176057017753 0ustar www-datawww-data# Fibers are hard... let's go shopping! begin require 'fiber' rescue LoadError => ex if defined? JRUBY_VERSION if RUBY_VERSION < "1.9.2" raise LoadError, "Celluloid requires JRuby 1.9 mode. Please pass the --1.9 flag or set JRUBY_OPTS=--1.9" end # Fibers are broken on JRuby 1.6.5. This works around the issue if JRUBY_VERSION[/^1\.6\.5/] require 'jruby' org.jruby.ext.fiber.FiberExtLibrary.new.load(JRuby.runtime, false) class org::jruby::ext::fiber::ThreadFiber field_accessor :state end class Fiber def alive? JRuby.reference(self).state != org.jruby.ext.fiber.ThreadFiberState::FINISHED end end else # Just in case subsequent JRuby releases have broken fibers :/ raise ex end elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx" raise LoadError, "Celluloid requires Rubinius 1.9 mode. Please pass the -X19 flag." else raise ex end end celluloid-0.15.2/lib/celluloid/notifications.rb0000644000004100000410000000375212245176057021543 0ustar www-datawww-datamodule Celluloid module Notifications def self.notifier Actor[:notifications_fanout] or raise DeadActorError, "notifications fanout actor not running" end def publish(pattern, *args) Celluloid::Notifications.notifier.publish(pattern, *args) end def subscribe(pattern, method) Celluloid::Notifications.notifier.subscribe(Actor.current, pattern, method) end def unsubscribe(*args) Celluloid::Notifications.notifier.unsubscribe(*args) end class Fanout include Celluloid trap_exit :prune def initialize @subscribers = [] @listeners_for = {} end def subscribe(actor, pattern, method) subscriber = Subscriber.new(actor, pattern, method).tap do |s| @subscribers << s end link actor @listeners_for.clear subscriber end def unsubscribe(subscriber) @subscribers.reject! { |s| s.matches?(subscriber) } @listeners_for.clear end def publish(pattern, *args) listeners_for(pattern).each { |s| s.publish(pattern, *args) } end def listeners_for(pattern) @listeners_for[pattern] ||= @subscribers.select { |s| s.subscribed_to?(pattern) } end def listening?(pattern) listeners_for(pattern).any? end def prune(actor, reason=nil) @subscribers.reject! { |s| s.actor == actor } @listeners_for.clear end end class Subscriber attr_accessor :actor, :pattern, :method def initialize(actor, pattern, method) @actor = actor @pattern = pattern @method = method end def publish(pattern, *args) actor.async method, pattern, *args end def subscribed_to?(pattern) !pattern || @pattern === pattern.to_s end def matches?(subscriber_or_pattern) self === subscriber_or_pattern || @pattern && @pattern === subscriber_or_pattern end end end end celluloid-0.15.2/lib/celluloid/logging/0000755000004100000410000000000012245176057017764 5ustar www-datawww-datacelluloid-0.15.2/lib/celluloid/logging/ring_buffer.rb0000644000004100000410000000206512245176057022604 0ustar www-datawww-datamodule Celluloid class RingBuffer def initialize(size) @size = size @start = 0 @count = 0 @buffer = Array.new(size) @mutex = Mutex.new end def full? @count == @size end def empty? @count == 0 end def push(value) @mutex.synchronize do stop = (@start + @count) % @size @buffer[stop] = value if full? @start = (@start + 1) % @size else @count += 1 end value end end alias :<< :push def shift @mutex.synchronize do remove_element end end def flush values = [] @mutex.synchronize do while !empty? values << remove_element end end values end def clear @buffer = Array.new(@size) @start = 0 @count = 0 end private def remove_element return nil if empty? value, @buffer[@start] = @buffer[@start], nil @start = (@start + 1) % @size @count -= 1 value end end end celluloid-0.15.2/lib/celluloid/logging/incident_reporter.rb0000644000004100000410000000213612245176057024032 0ustar www-datawww-datarequire 'logger' module Celluloid # Subscribes to log incident topics to report on them. class IncidentReporter include Celluloid include Celluloid::Notifications # get the time from the event class Formatter < ::Logger::Formatter def call(severity, time, progname, msg) super(severity, msg.time, progname, msg.message) end end def initialize(*args) subscribe(/log\.incident/, :report) @logger = ::Logger.new(*args) @logger.formatter = Formatter.new @silenced = false end def report(topic, incident) return if @silenced header = "INCIDENT" header << " AT #{incident.triggering_event.time}" if incident.triggering_event @logger << header @logger << "\n" @logger << "====================\n" incident.events.each do |event| @logger.add(event.severity, event, event.progname) end @logger << "====================\n" end def silence @silenced = true end def unsilence @silenced = false end def silenced? @silenced end end end celluloid-0.15.2/lib/celluloid/logging/log_event.rb0000644000004100000410000000111412245176057022270 0ustar www-datawww-datamodule Celluloid # Wraps a single log event. class LogEvent attr_accessor :id, :severity, :message, :progname, :time def initialize(severity, message, progname, time=Time.now, &block) # This id should be ordered. For now relies on Celluloid::UUID to be ordered. # May want to use a generation/counter strategy for independence of uuid. @id = Celluloid::UUID.generate @severity = severity @message = block_given? ? yield : message @progname = progname @time = time end def <=>(other) @id <=> other.id end end end celluloid-0.15.2/lib/celluloid/logging/incident_logger.rb0000644000004100000410000000730312245176057023450 0ustar www-datawww-datarequire 'logger' module Celluloid # A logger that holds all messages in circular buffers, then flushes the buffers # when an event occurs at a configurable severity threshold. # # Unlike ruby's Logger, this class only supports a single progname. class IncidentLogger module Severity include ::Logger::Severity TRACE = -1 def severity_to_string(severity) case severity when TRACE then 'TRACE' when DEBUG then 'DEBUG' when INFO then 'INFO' when WARN then 'WARN' when ERROR then 'ERROR' when FATAL then 'FATAL' when UNKNOWN then 'UNKNOWN' end end end include Severity # The progname (facility) for this instance. attr_accessor :progname # The logging level. Messages below this severity will not be logged at all. attr_accessor :level # The incident threshold. Messages at or above this severity will generate an # incident and be published to incident reporters. attr_accessor :threshold # The buffer size limit. Each log level will retain this number of messages # at maximum. attr_accessor :sizelimit attr_accessor :buffers # Create a new IncidentLogger. def initialize(progname=nil, options={}) @progname = progname || "default" @level = options[:level] || DEBUG @threshold = options[:threshold] || ERROR @sizelimit = options[:sizelimit] || 100 @buffer_mutex = Mutex.new @buffers = Hash.new do |progname_hash, _progname| @buffer_mutex.synchronize do progname_hash[_progname] = Hash.new do |severity_hash, severity| severity_hash[severity] = RingBuffer.new(@sizelimit) end end end # When the IncidentLogger itself encounters an error, it falls back to logging to stderr @fallback_logger = ::Logger.new(STDERR) @fallback_logger.progname = "FALLBACK" end # add an event. def add(severity, message=nil, progname=nil, &block) progname ||= @progname severity ||= UNKNOWN if severity < @level return event.id end if message.nil? && !block_given? message = progname progname = @progname end event = LogEvent.new(severity, message, progname, &block) @buffers[progname][severity] << event if severity >= @threshold begin Celluloid::Notifications.notifier.async.publish(incident_topic, create_incident(event)) rescue => ex @fallback_logger.error(ex) end end event.id end alias :log :add # See docs for Logger#info def trace (progname=nil, &block); add(TRACE, nil, progname, &block); end def debug (progname=nil, &block); add(DEBUG, nil, progname, &block); end def info (progname=nil, &block); add(INFO, nil, progname, &block); end def warn (progname=nil, &block); add(WARN, nil, progname, &block); end def error (progname=nil, &block); add(ERROR, nil, progname, &block); end def fatal (progname=nil, &block); add(FATAL, nil, progname, &block); end def unknown (progname=nil, &block); add(UNKNOWN, nil, progname, &block); end def flush messages = [] @buffer_mutex.synchronize do @buffers.each do |progname, severities| severities.each do |severity, buffer| messages += buffer.flush end end end messages.sort end def clear @buffer_mutex.synchronize do @buffers.each { |buffer| buffer.clear } end end def create_incident(event=nil) Incident.new(flush, event) end def incident_topic "log.incident.#{@progname}" end end end celluloid-0.15.2/lib/celluloid/logging/incident.rb0000644000004100000410000000116512245176057022111 0ustar www-datawww-datamodule Celluloid # Wraps all events and context for a single incident. class Incident attr_accessor :pid attr_accessor :events, :triggering_event def initialize(events, triggering_event=nil) @events = events @triggering_event = triggering_event @pid = $$ end # Merge two incidents together. This may be useful if two incidents occur at the same time. def merge(*other_incidents) merged_events = other_incidents.flatten.inject(events) do |events, incident| events += incident.events end Incident.new(merged_events.sort, triggering_event) end end end celluloid-0.15.2/lib/celluloid/stack_dump.rb0000644000004100000410000000532412245176057021021 0ustar www-datawww-datamodule Celluloid class StackDump module DisplayBacktrace def display_backtrace(backtrace, output, indent = nil) backtrace ||= ["EMPTY BACKTRACE"] backtrace.each do |line| output << indent if indent output << "\t" << line << "\n" end output << "\n\n" end end class TaskState < Struct.new(:task_class, :type, :meta, :status, :backtrace) end class ActorState include DisplayBacktrace attr_accessor :subject_id, :subject_class, :name attr_accessor :status, :tasks attr_accessor :backtrace def dump string = "" string << "Celluloid::Actor 0x#{subject_id.to_s(16)}: #{subject_class}" string << " [#{name}]" if name string << "\n" if status == :idle string << "State: Idle (waiting for messages)\n" display_backtrace backtrace, string else string << "State: Running (executing tasks)\n" display_backtrace backtrace, string string << "\tTasks:\n" tasks.each_with_index do |task, i| string << "\t #{i+1}) #{task.task_class}[#{task.type}]: #{task.status}\n" string << "\t #{task.meta.inspect}\n" display_backtrace task.backtrace, string, "\t" end end string end end class ThreadState < Struct.new(:thread_id, :backtrace) include DisplayBacktrace def dump string = "" string << "Thread 0x#{thread_id.to_s(16)}:\n" display_backtrace backtrace, string string end end attr_accessor :actors, :threads def initialize @actors = [] @threads = [] snapshot end def snapshot Celluloid.internal_pool.each do |thread| if thread.role == :actor @actors << snapshot_actor(thread.actor) if thread.actor else @threads << snapshot_thread(thread) end end end def snapshot_actor(actor) state = ActorState.new state.subject_id = actor.subject.object_id state.subject_class = actor.subject.class tasks = actor.tasks if tasks.empty? state.status = :idle else state.status = :running state.tasks = tasks.to_a.map { |t| TaskState.new(t.class, t.type, t.meta, t.status, t.backtrace) } end state.backtrace = actor.thread.backtrace if actor.thread state end def snapshot_thread(thread) ThreadState.new(thread.object_id, thread.backtrace) end def dump(output = STDERR) @actors.each do |actor| output.print actor.dump end @threads.each do |thread| output.print thread.dump end end end end celluloid-0.15.2/lib/celluloid/task_set.rb0000644000004100000410000000200612245176057020476 0ustar www-datawww-datarequire 'set' require 'forwardable' module Celluloid if defined? JRUBY_VERSION require 'jruby/synchronized' class TaskSet extend Forwardable include JRuby::Synchronized def_delegators :@tasks, :<<, :delete, :first, :empty?, :to_a def initialize @tasks = Set.new end end elsif defined? Rubinius class TaskSet def initialize @tasks = Set.new end def <<(task) Rubinius.synchronize(self) { @tasks << task } end def delete(task) Rubinius.synchronize(self) { @tasks.delete task } end def first Rubinius.synchronize(self) { @tasks.first } end def empty? Rubinius.synchronize(self) { @tasks.empty? } end def to_a Rubinius.synchronize(self) { @tasks.to_a } end end else # Assume we're on MRI, where we have the GIL. But what about IronRuby? # Or MacRuby. Do people care? This will break Celluloid::StackDumps TaskSet = Set end end celluloid-0.15.2/lib/celluloid/registry.rb0000644000004100000410000000254612245176057020542 0ustar www-datawww-datarequire 'thread' module Celluloid # The Registry allows us to refer to specific actors by human-meaningful names class Registry class << self attr_reader :root end def initialize @registry = {} @registry_lock = Mutex.new end # Register an Actor def []=(name, actor) actor_singleton = class << actor; self; end unless actor_singleton.ancestors.include? ActorProxy raise TypeError, "not an actor" end @registry_lock.synchronize do @registry[name.to_sym] = actor end actor.mailbox << NamingRequest.new(name.to_sym) end # Retrieve an actor by name def [](name) @registry_lock.synchronize do @registry[name.to_sym] end end alias_method :get, :[] alias_method :set, :[]= def delete(name) @registry_lock.synchronize do @registry.delete name.to_sym end end # List all registered actors by name def names @registry_lock.synchronize { @registry.keys } end # removes and returns all registered actors as a hash of `name => actor` # can be used in testing to clear the registry def clear hash = nil @registry_lock.synchronize do hash = @registry.dup @registry.clear end hash end # Create the default registry @root = new end end celluloid-0.15.2/lib/celluloid/tasks/0000755000004100000410000000000012245176057017463 5ustar www-datawww-datacelluloid-0.15.2/lib/celluloid/tasks/task_thread.rb0000644000004100000410000000221412245176057022300 0ustar www-datawww-datamodule Celluloid # Tasks with a Thread backend class TaskThread < Task # Run the given block within a task def initialize(type, meta) @resume_queue = Queue.new @exception_queue = Queue.new @yield_mutex = Mutex.new @yield_cond = ConditionVariable.new super end def create @thread = Celluloid::ThreadHandle.new(:task) do begin ex = @resume_queue.pop raise ex if ex.is_a?(Task::TerminatedError) yield rescue Exception => ex @exception_queue << ex ensure @yield_cond.signal end end end def signal @yield_cond.signal @resume_queue.pop end def deliver(value) raise DeadTaskError, "cannot resume a dead task" unless @thread.alive? @yield_mutex.synchronize do @resume_queue.push(value) @yield_cond.wait(@yield_mutex) while @exception_queue.size > 0 raise @exception_queue.pop end end rescue ThreadError raise DeadTaskError, "cannot resume a dead task" end def backtrace @thread.backtrace end end end celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb0000644000004100000410000000172712245176057022130 0ustar www-datawww-datamodule Celluloid class FiberStackError < Celluloid::Error; end # Tasks with a Fiber backend class TaskFiber < Task def create queue = Thread.current[:celluloid_queue] @fiber = Fiber.new do # FIXME: cannot use the writer as specs run inside normal Threads Thread.current[:celluloid_role] = :actor Thread.current[:celluloid_queue] = queue yield end end def signal Fiber.yield end # Resume a suspended task, giving it a value to return if needed def deliver(value) @fiber.resume value rescue SystemStackError => ex raise FiberStackError, "#{ex} (please see https://github.com/celluloid/celluloid/wiki/Fiber-stack-errors)" rescue FiberError => ex raise DeadTaskError, "cannot resume a dead task (#{ex})" end # Terminate this task def terminate super rescue FiberError # If we're getting this the task should already be dead end end end celluloid-0.15.2/lib/celluloid/method.rb0000644000004100000410000000077412245176057020153 0ustar www-datawww-datamodule Celluloid # Method handles that route through an actor proxy class Method def initialize(proxy, name) raise NameError, "undefined method `#{name}'" unless proxy.respond_to? name @proxy, @name = proxy, name @klass = @proxy.class end def arity @proxy.method_missing(:method, @name).arity end def call(*args, &block) @proxy.__send__(@name, *args, &block) end def inspect "#" end end end celluloid-0.15.2/lib/celluloid/signals.rb0000644000004100000410000000112212245176057020317 0ustar www-datawww-datamodule Celluloid # Event signaling between methods of the same object class Signals def initialize @conditions = {} end # Wait for the given signal and return the associated value def wait(name) raise "cannot wait for signals while exclusive" if Celluloid.exclusive? @conditions[name] ||= Condition.new @conditions[name].wait end # Send a signal to all method calls waiting for the given name def broadcast(name, value = nil) if condition = @conditions.delete(name) condition.broadcast(value) end end end end celluloid-0.15.2/lib/celluloid/receivers.rb0000644000004100000410000000276512245176057020664 0ustar www-datawww-datarequire 'set' require 'timers' module Celluloid # Allow methods to directly interact with the actor protocol class Receivers def initialize @receivers = Set.new @timers = Timers.new end # Receive an asynchronous message def receive(timeout = nil, &block) if Celluloid.exclusive? Celluloid.mailbox.receive(timeout, &block) else receiver = Receiver.new block if timeout receiver.timer = @timers.after(timeout) do @receivers.delete receiver receiver.resume end end @receivers << receiver Task.suspend :receiving end end # How long to wait until the next timer fires def wait_interval @timers.wait_interval end # Fire any pending timers def fire_timers @timers.fire end # Handle incoming messages def handle_message(message) receiver = @receivers.find { |r| r.match(message) } return unless receiver @receivers.delete receiver @timers.cancel receiver.timer if receiver.timer receiver.resume message end end # Methods blocking on a call to receive class Receiver attr_accessor :timer def initialize(block) @block = block @task = Task.current @timer = nil end # Match a message with this receiver's block def match(message) @block ? @block.call(message) : true end def resume(message = nil) @task.resume message end end end celluloid-0.15.2/lib/celluloid/pool_manager.rb0000644000004100000410000000554012245176057021332 0ustar www-datawww-datarequire 'set' module Celluloid # Manages a fixed-size pool of workers # Delegates work (i.e. methods) and supervises workers # Don't use this class directly. Instead use MyKlass.pool class PoolManager include Celluloid trap_exit :__crash_handler__ finalizer :__shutdown__ def initialize(worker_class, options = {}) @size = options[:size] || [Celluloid.cores, 2].max raise ArgumentError, "minimum pool size is 2" if @size < 2 @worker_class = worker_class @args = options[:args] ? Array(options[:args]) : [] @idle = @size.times.map { worker_class.new_link(*@args) } # FIXME: Another data structure (e.g. Set) would be more appropriate # here except it causes MRI to crash :o @busy = [] end def __shutdown__ terminators = (@idle + @busy).each do |actor| begin actor.future(:terminate) rescue DeadActorError end end terminators.compact.each { |terminator| terminator.value rescue nil } end def _send_(method, *args, &block) worker = __provision_worker__ begin worker._send_ method, *args, &block rescue DeadActorError # if we get a dead actor out of the pool wait :respawn_complete worker = __provision_worker__ retry rescue Exception => ex abort ex ensure if worker.alive? @idle << worker @busy.delete worker end end end def name _send_ @mailbox, :name end def is_a?(klass) _send_ :is_a?, klass end def kind_of?(klass) _send_ :kind_of?, klass end def methods(include_ancestors = true) _send_ :methods, include_ancestors end def to_s _send_ :to_s end def inspect _send_ :inspect end def size @size end def busy_size @busy.length end def idle_size @idle.length end # Provision a new worker def __provision_worker__ Task.current.guard_warnings = true while @idle.empty? # Wait for responses from one of the busy workers response = exclusive { receive { |msg| msg.is_a?(Response) } } Thread.current[:celluloid_actor].handle_message(response) end worker = @idle.shift @busy << worker worker end # Spawn a new worker for every crashed one def __crash_handler__(actor, reason) @busy.delete actor @idle.delete actor return unless reason @idle << @worker_class.new_link(*@args) signal :respawn_complete end def respond_to?(method, include_private = false) super || @worker_class.instance_methods.include?(method.to_sym) end def method_missing(method, *args, &block) if respond_to?(method) _send_ method, *args, &block else super end end end end celluloid-0.15.2/lib/celluloid/supervision_group.rb0000644000004100000410000000773212245176057022476 0ustar www-datawww-datamodule Celluloid # Supervise collections of actors as a group class SupervisionGroup include Celluloid trap_exit :restart_actor class << self # Actors or sub-applications to be supervised def blocks @blocks ||= [] end # Start this application (and watch it with a supervisor) def run!(registry = nil) group = new(registry) do |_group| blocks.each do |block| block.call(_group) end end group end # Run the application in the foreground with a simple watchdog def run(registry = nil) loop do supervisor = run!(registry) # Take five, toplevel supervisor sleep 5 while supervisor.alive? Logger.error "!!! Celluloid::SupervisionGroup #{self} crashed. Restarting..." end end # Register an actor class or a sub-group to be launched and supervised # Available options are: # # * as: register this application in the Celluloid::Actor[] directory # * args: start the actor with the given arguments def supervise(klass, options = {}) blocks << lambda do |group| group.add klass, options end end # Register a pool of actors to be launched on group startup # Available options are: # # * as: register this application in the Celluloid::Actor[] directory # * args: start the actor pool with the given arguments def pool(klass, options = {}) blocks << lambda do |group| group.pool klass, options end end end # Start the group def initialize(registry = nil) @members = [] @registry = registry || Registry.root yield current_actor if block_given? end execute_block_on_receiver :initialize, :supervise, :supervise_as def supervise(klass, *args, &block) add(klass, :args => args, :block => block) end def supervise_as(name, klass, *args, &block) add(klass, :args => args, :block => block, :as => name) end def pool(klass, options = {}) options[:method] = 'pool_link' add(klass, options) end def add(klass, options) member = Member.new(@registry, klass, options) @members << member member end def actors @members.map(&:actor) end finalizer :finalize # Terminate the group def finalize @members.reverse_each(&:terminate) end # Restart a crashed actor def restart_actor(actor, reason) member = @members.find do |_member| _member.actor == actor end raise "a group member went missing. This shouldn't be!" unless member member.restart(reason) end # A member of the group class Member def initialize(registry, klass, options = {}) @registry = registry @klass = klass # Stringify keys :/ options = options.inject({}) { |h,(k,v)| h[k.to_s] = v; h } @name = options['as'] @block = options['block'] @args = options['args'] ? Array(options['args']) : [] @method = options['method'] || 'new_link' @pool = @method == 'pool_link' @pool_size = options['size'] if @pool start end attr_reader :name, :actor def start # when it is a pool, then we don't splat the args # and we need to extract the pool size if set if @pool options = {:args => @args} options[:size] = @pool_size if @pool_size @args = [options] end @actor = @klass.send(@method, *@args, &@block) @registry[@name] = @actor if @name end def restart(reason) @actor = nil @registry.delete(@name) if @name # Ignore supervisors that shut down cleanly return unless reason start end def terminate @registry.delete(@name) if @name @actor.terminate if @actor rescue DeadActorError end end end end celluloid-0.15.2/lib/celluloid/legacy.rb0000644000004100000410000000023012245176057020122 0ustar www-datawww-dataclass Thread def self.mailbox Celluloid.mailbox end def self.receive(timeout = nil, &block) Celluloid.receive(timeout, &block) end end celluloid-0.15.2/lib/celluloid/rspec.rb0000644000004100000410000000057012245176057020001 0ustar www-datawww-datarequire File.expand_path('../../../spec/support/example_actor_class', __FILE__) require File.expand_path('../../../spec/support/actor_examples', __FILE__) require File.expand_path('../../../spec/support/mailbox_examples', __FILE__) module Celluloid # Timer accuracy enforced by the tests (50ms) TIMER_QUANTUM = 0.05 end $CELLULOID_DEBUG = true require 'celluloid/test' celluloid-0.15.2/lib/celluloid/cpu_counter.rb0000644000004100000410000000113712245176057021213 0ustar www-datawww-datarequire 'rbconfig' module Celluloid module CPUCounter case RbConfig::CONFIG['host_os'][/^[A-Za-z]+/] when 'darwin' @cores = Integer(`/usr/sbin/sysctl hw.ncpu`[/\d+/]) when 'linux' @cores = if File.exists?("/sys/devices/system/cpu/present") File.read("/sys/devices/system/cpu/present").split('-').last.to_i+1 else Dir["/sys/devices/system/cpu/cpu*"].select { |n| n=~/cpu\d+/ }.count end when 'mingw', 'mswin' @cores = Integer(ENV["NUMBER_OF_PROCESSORS"][/\d+/]) else @cores = nil end def self.cores; @cores; end end end celluloid-0.15.2/lib/celluloid/responses.rb0000644000004100000410000000142312245176057020704 0ustar www-datawww-datamodule Celluloid # Responses to calls class Response attr_reader :call, :value def initialize(call, value) @call, @value = call, value end def dispatch @call.task.resume self end end # Call completed successfully class SuccessResponse < Response; end # Call was aborted due to sender error class ErrorResponse < Response def value ex = super ex = ex.cause if ex.is_a? AbortError if ex.backtrace ex.backtrace << "(celluloid):0:in `remote procedure call'" ex.backtrace.concat(caller) end raise ex end end class BlockResponse def initialize(call, result) @call = call @result = result end def dispatch @call.task.resume(@result) end end end celluloid-0.15.2/lib/celluloid/tasks.rb0000644000004100000410000001020312245176057020004 0ustar www-datawww-datamodule Celluloid # Asked to do task-related things outside a task class NotTaskError < Celluloid::Error; end # Trying to resume a dead task class DeadTaskError < Celluloid::Error; end # Errors which should be resumed automatically class ResumableError < Celluloid::Error; end # Tasks are interruptable/resumable execution contexts used to run methods class Task class TerminatedError < ResumableError; end # kill a running task after terminate class TimeoutError < ResumableError; end # kill a running task after timeout # Obtain the current task def self.current Thread.current[:celluloid_task] or raise NotTaskError, "not within a task context" end # Suspend the running task, deferring to the scheduler def self.suspend(status) Task.current.suspend(status) end attr_reader :type, :meta, :status attr_accessor :chain_id, :guard_warnings # Create a new task def initialize(type, meta) @type = type @meta = meta @status = :new @exclusive = false @dangerous_suspend = @meta ? @meta.delete(:dangerous_suspend) : false @guard_warnings = false actor = Thread.current[:celluloid_actor] @chain_id = CallChain.current_id raise NotActorError, "can't create tasks outside of actors" unless actor guard "can't create tasks inside of tasks" if Thread.current[:celluloid_task] create do begin @status = :running actor.setup_thread Thread.current[:celluloid_task] = self CallChain.current_id = @chain_id actor.tasks << self yield rescue Task::TerminatedError # Task was explicitly terminated ensure @status = :dead actor.tasks.delete self end end end def create(&block) raise "Implement #{self.class}#create" end # Suspend the current task, changing the status to the given argument def suspend(status) raise "Cannot suspend while in exclusive mode" if exclusive? raise "Cannot suspend a task from outside of itself" unless Task.current == self @status = status if $CELLULOID_DEBUG && @dangerous_suspend warning = "Dangerously suspending task: " warning << [ "type=#{@type.inspect}", "meta=#{@meta.inspect}", "status=#{@status.inspect}" ].join(", ") Logger.warn [warning, *caller[2..8]].join("\n\t") end value = signal @status = :running raise value if value.is_a?(Celluloid::ResumableError) value end # Resume a suspended task, giving it a value to return if needed def resume(value = nil) guard "Cannot resume a task from inside of a task" if Thread.current[:celluloid_task] deliver(value) nil end # Execute a code block in exclusive mode. def exclusive if @exclusive yield else begin @exclusive = true yield ensure @exclusive = false end end end # Terminate this task def terminate raise "Cannot terminate an exclusive task" if exclusive? if running? Celluloid.logger.warn "Terminating task: type=#{@type.inspect}, meta=#{@meta.inspect}, status=#{@status.inspect}" exception = Task::TerminatedError.new("task was terminated") exception.set_backtrace(caller) resume exception else raise DeadTaskError, "task is already dead" end end # Is this task running in exclusive mode? def exclusive? @exclusive end def backtrace end # Is the current task still running? def running?; @status != :dead; end # Nicer string inspect for tasks def inspect "#<#{self.class}:0x#{object_id.to_s(16)} @type=#{@type.inspect}, @meta=#{@meta.inspect}, @status=#{@status.inspect}>" end def guard(message) if @guard_warnings Logger.warn message if $CELLULOID_DEBUG else raise message if $CELLULOID_DEBUG end end end end require 'celluloid/tasks/task_fiber' require 'celluloid/tasks/task_thread' celluloid-0.15.2/lib/celluloid/supervisor.rb0000644000004100000410000000105712245176057021107 0ustar www-datawww-datamodule Celluloid # Supervisors are actors that watch over other actors and restart them if # they crash class Supervisor class << self # Define the root of the supervision tree attr_accessor :root def supervise(klass, *args, &block) SupervisionGroup.new do |group| group.supervise klass, *args, &block end end def supervise_as(name, klass, *args, &block) SupervisionGroup.new do |group| group.supervise_as name, klass, *args, &block end end end end end celluloid-0.15.2/metadata.yml0000644000004100000410000001240712245176057016123 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: celluloid version: !ruby/object:Gem::Version version: 0.15.2 platform: ruby authors: - Tony Arcieri autorequire: bindir: bin cert_chain: [] date: 2013-10-06 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: timers requirement: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: 1.1.0 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ~> - !ruby/object:Gem::Version version: 1.1.0 - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: guard-rspec requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: benchmark_suite requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' description: Celluloid enables people to build concurrent programs out of concurrent objects just as easily as they build sequential programs out of sequential objects email: - tony.arcieri@gmail.com executables: [] extensions: [] extra_rdoc_files: [] files: - README.md - lib/celluloid/actor.rb - lib/celluloid/autostart.rb - lib/celluloid/call_chain.rb - lib/celluloid/calls.rb - lib/celluloid/condition.rb - lib/celluloid/core_ext.rb - lib/celluloid/cpu_counter.rb - lib/celluloid/evented_mailbox.rb - lib/celluloid/fiber.rb - lib/celluloid/fsm.rb - lib/celluloid/future.rb - lib/celluloid/internal_pool.rb - lib/celluloid/legacy.rb - lib/celluloid/links.rb - lib/celluloid/logger.rb - lib/celluloid/logging/incident.rb - lib/celluloid/logging/incident_logger.rb - lib/celluloid/logging/incident_reporter.rb - lib/celluloid/logging/log_event.rb - lib/celluloid/logging/ring_buffer.rb - lib/celluloid/logging.rb - lib/celluloid/mailbox.rb - lib/celluloid/method.rb - lib/celluloid/notifications.rb - lib/celluloid/pool_manager.rb - lib/celluloid/properties.rb - lib/celluloid/proxies/abstract_proxy.rb - lib/celluloid/proxies/actor_proxy.rb - lib/celluloid/proxies/async_proxy.rb - lib/celluloid/proxies/block_proxy.rb - lib/celluloid/proxies/future_proxy.rb - lib/celluloid/proxies/sync_proxy.rb - lib/celluloid/receivers.rb - lib/celluloid/registry.rb - lib/celluloid/responses.rb - lib/celluloid/rspec.rb - lib/celluloid/signals.rb - lib/celluloid/stack_dump.rb - lib/celluloid/supervision_group.rb - lib/celluloid/supervisor.rb - lib/celluloid/system_events.rb - lib/celluloid/task_set.rb - lib/celluloid/tasks/task_fiber.rb - lib/celluloid/tasks/task_thread.rb - lib/celluloid/tasks.rb - lib/celluloid/test.rb - lib/celluloid/thread.rb - lib/celluloid/thread_handle.rb - lib/celluloid/uuid.rb - lib/celluloid.rb - spec/celluloid/actor_spec.rb - spec/celluloid/block_spec.rb - spec/celluloid/calls_spec.rb - spec/celluloid/condition_spec.rb - spec/celluloid/evented_mailbox_spec.rb - spec/celluloid/fsm_spec.rb - spec/celluloid/future_spec.rb - spec/celluloid/internal_pool_spec.rb - spec/celluloid/links_spec.rb - spec/celluloid/logging/ring_buffer_spec.rb - spec/celluloid/mailbox_spec.rb - spec/celluloid/notifications_spec.rb - spec/celluloid/pool_spec.rb - spec/celluloid/properties_spec.rb - spec/celluloid/registry_spec.rb - spec/celluloid/stack_dump_spec.rb - spec/celluloid/supervision_group_spec.rb - spec/celluloid/supervisor_spec.rb - spec/celluloid/tasks/task_fiber_spec.rb - spec/celluloid/tasks/task_thread_spec.rb - spec/celluloid/thread_handle_spec.rb - spec/celluloid/uuid_spec.rb - spec/spec_helper.rb - spec/support/actor_examples.rb - spec/support/example_actor_class.rb - spec/support/mailbox_examples.rb - spec/support/task_examples.rb homepage: https://github.com/celluloid/celluloid licenses: - MIT metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: 1.9.2 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: 1.3.6 requirements: [] rubyforge_project: rubygems_version: 2.0.3 signing_key: specification_version: 4 summary: Actor-based concurrent object framework for Ruby test_files: [] celluloid-0.15.2/checksums.yaml.gz0000444000004100000410000000041512245176057017102 0ustar www-datawww-dataQRe;R0 D"#Y9> PBvy{z/cYb,b㚕FMٛy/H a"l2{ʰ\`LsÕllj]Y/KxK i=PbO:fBWZsU$#Dd&)nۧ`Ҽa,6M l@yVZԖjEkPE%i [c.y pWѢcelluloid-0.15.2/README.md0000644000004100000410000001525112245176057015077 0ustar www-datawww-data![Celluloid](https://raw.github.com/celluloid/celluloid-logos/master/celluloid/celluloid.png) ========= [![Gem Version](https://badge.fury.io/rb/celluloid.png)](http://rubygems.org/gems/celluloid) [![Build Status](https://secure.travis-ci.org/celluloid/celluloid.png?branch=master)](http://travis-ci.org/celluloid/celluloid) [![Code Climate](https://codeclimate.com/github/celluloid/celluloid.png)](https://codeclimate.com/github/celluloid/celluloid) [![Coverage Status](https://coveralls.io/repos/celluloid/celluloid/badge.png?branch=master)](https://coveralls.io/r/celluloid/celluloid) > "I thought of objects being like biological cells and/or individual > computers on a network, only able to communicate with messages" > _--Alan Kay, creator of Smalltalk, on the meaning of "object oriented programming"_ Celluloid provides a simple and natural way to build fault-tolerant concurrent programs in Ruby. With Celluloid, you can build systems out of concurrent objects just as easily as you build sequential programs out of regular objects. Recommended for any developer, including novices, Celluloid should help ease your worries about building multithreaded Ruby programs. Much of the difficulty with building concurrent programs in Ruby arises because the object-oriented mechanisms for structuring code, such as classes and inheritance, are separate from the concurrency mechanisms, such as threads and locks. Celluloid combines these into a single structure, an active object running within a thread, called an "actor", or in Celluloid vernacular, a "cell". By combining concurrency with object oriented programming, Celluloid frees you up from worry about where to use threads and locks. Celluloid combines them together into a single concurrent object oriented programming model, encapsulating state in concurrent objects and thus avoiding many of the problems associated with multithreaded programming. Celluloid provides many features which make concurrent programming simple, easy, and fun: * __Automatic "deadlock-free" synchronization:__ Celluloid uses a concurrent object model which combines method dispatch and thread synchronization. Each actor is a concurrent object running in its own thread, and every method invocation is wrapped in a fiber that can be suspended whenever it calls out to other actors, and resumed when the response is available. This means methods which are waiting for responses from other actors, external messages, or other system events (including I/O with Celluloid::IO) can be suspended and will never block other methods that are ready to run. This won't prevent bugs in Celluloid, bugs in other thread-safe libraries you use, and even certain "dangerous" features of Celluloid from causing your program to deadlock, but in general, programs built with Celluloid will be naturally immune to deadlocks. * __Fault-tolerance:__ Celluloid has taken to heart many of Erlang's ideas about fault-tolerance in order to enable self-healing applications. The central idea: have you tried turning it off and on again? Celluloid takes care of rebooting subcomponents of your application when they crash, whether it's a single actor, or large (potentially multi-tiered) groups of actors that are all interdependent. This means rather than worrying about rescuing every last exception, you can just sit back, relax, and let parts of your program crash, knowing Celluloid will automatically reboot them in a clean state. Celluloid provides its own implementation of the core fault-tolerance concepts in Erlang including [linking](https://github.com/celluloid/celluloid/wiki/Linking), [supervisors](https://github.com/celluloid/celluloid/wiki/Supervisors), and [supervision groups](https://github.com/celluloid/celluloid/wiki/Supervision-Groups). * __[Futures](https://github.com/celluloid/celluloid/wiki/futures):__ Ever wanted to call a method "in the background" and retrieve the value it returns later? Celluloid futures do just that. It's like calling ahead to a restaurant to place an order, so they can work on preparing your food while you're on your way to pick it up. When you ask for a method's return value, it's returned immediately if the method has already completed, or otherwise the current method is suspended until the value becomes available. You can also build distributed systems with Celluloid using its [sister project DCell](https://github.com/celluloid/dcell). Evented IO similar to EventMachine (with a synchronous API instead of callback/deferrable soup) is available through the [Celluloid::IO](https://github.com/celluloid/celluloid-io) library. Like Celluloid? [Join the mailing list/Google Group](http://groups.google.com/group/celluloid-ruby) or visit us on IRC at #celluloid on freenode ### Is it any good? [Yes](http://news.ycombinator.com/item?id=3067434) ### Is It "Production Ready™"? Yes, many users are now running Celluloid in production by using [Sidekiq](http://sidekiq.org) and [Adhearsion](http://adhearsion.com/) Documentation ------------- [Please see the Celluloid Wiki](https://github.com/celluloid/celluloid/wiki) for more detailed documentation and usage notes. The following API documentation is also available: * [YARD API documentation](http://rubydoc.info/gems/celluloid/frames) * [Celluloid module (primary API)](http://rubydoc.info/gems/celluloid/Celluloid) * [Celluloid class methods](http://rubydoc.info/gems/celluloid/Celluloid/ClassMethods) * [All Celluloid classes](http://rubydoc.info/gems/celluloid/index) Installation ------------ Add this line to your application's Gemfile: ```ruby gem 'celluloid' ``` And then execute: $ bundle Or install it yourself as: $ gem install celluloid Inside of your Ruby program, require Celluloid with: ```ruby require 'celluloid/autostart' ``` Supported Platforms ------------------- Celluloid works on Ruby 1.9.3, 2.0.0, JRuby 1.6+, and Rubinius 2.0. JRuby or Rubinius are the preferred platforms as they support true thread-level parallelism when executing Ruby code, whereas MRI/YARV is constrained by a global interpreter lock (GIL) and can only execute one thread at a time. Celluloid requires Ruby 1.9 mode on all interpreters. Additional Reading ------------------ * [Concurrent Object-Oriented Programming in Python with ATOM](http://python.org/workshops/1997-10/proceedings/atom/): a similar system to Celluloid written in Python Contributing to Celluloid ------------------------- * Fork this repository on github * Make your changes and send us a pull request * If we like them we'll merge them * If we've accepted a patch, feel free to ask for commit access License ------- Copyright (c) 2013 Tony Arcieri. Distributed under the MIT License. See LICENSE.txt for further details.