celluloid-0.16.0/0000755000004100000410000000000012403202037013573 5ustar www-datawww-datacelluloid-0.16.0/LICENSE.txt0000644000004100000410000000204512403202037015417 0ustar www-datawww-dataCopyright (c) 2011-2014 Tony Arcieri Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. celluloid-0.16.0/spec/0000755000004100000410000000000012403202037014525 5ustar www-datawww-datacelluloid-0.16.0/spec/spec_helper.rb0000644000004100000410000000153612403202037017350 0ustar www-datawww-datarequire 'coveralls' Coveralls.wear! require 'rubygems' require 'bundler/setup' require 'celluloid/rspec' require 'celluloid/probe' logfile = File.open(File.expand_path("../../log/test.log", __FILE__), 'a') logfile.sync = true 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.around do |ex| Celluloid.actor_system = nil Thread.list.each do |thread| next if thread == Thread.current thread.kill end ex.run end config.around actor_system: :global do |ex| Celluloid.boot ex.run Celluloid.shutdown end config.around actor_system: :within do |ex| Celluloid::ActorSystem.new.within do ex.run end end end celluloid-0.16.0/spec/celluloid/0000755000004100000410000000000012403202037016501 5ustar www-datawww-datacelluloid-0.16.0/spec/celluloid/uuid_spec.rb0000644000004100000410000000042312403202037021005 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.16.0/spec/celluloid/timer_spec.rb0000644000004100000410000000146512403202037021166 0ustar www-datawww-datarequire 'spec_helper' class EveryActor include Celluloid def initialize @trace = [] @times = [] @start = Time.now every(1) { log(1) } every(2) { log(2) } every(1) { log(11) } every(2) { log(22) } end def log(t) @trace << t offset = Time.now - @start @times << offset # puts "log(#{t}) @ #{offset}" end attr :trace attr :times end describe Celluloid::Actor do it "run every(t) task several times" do Celluloid.boot every_actor = EveryActor.new sleep 5.5 times = every_actor.times trace = every_actor.trace Celluloid.shutdown expect(trace.count(1)).to be == 5 expect(trace.count(11)).to be == 5 expect(trace.count(2)).to be == 2 expect(trace.count(22)).to be == 2 end end celluloid-0.16.0/spec/celluloid/actor_system_spec.rb0000644000004100000410000000256212403202037022561 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::ActorSystem do class TestActor include Celluloid end it "supports non-global ActorSystem" do subject.within do Celluloid.actor_system.should == subject end end it "starts default actors" do subject.start subject.registered.should == [:notifications_fanout, :default_incident_reporter] end it "support getting threads" do queue = Queue.new thread = subject.get_thread do Celluloid.actor_system.should == subject queue << nil end queue.pop end it "allows a stack dump" do subject.stack_dump.should be_a(Celluloid::StackDump) end it "returns named actors" do subject.registered.should be_empty subject.within do TestActor.supervise_as :test end subject.registered.should == [:test] end it "returns running actors" do subject.running.should be_empty first = subject.within do TestActor.new end second = subject.within do TestActor.new end subject.running.should == [first, second] end it "shuts down" do subject.shutdown lambda { subject.get_thread }. should raise_error("Thread pool is not running") end it "warns nicely when no actor system is started" do lambda { TestActor.new }. should raise_error("Celluloid is not yet started; use Celluloid.boot") end end celluloid-0.16.0/spec/celluloid/pool_spec.rb0000644000004100000410000000366412403202037021022 0ustar www-datawww-datarequire 'spec_helper' describe "Celluloid.pool", actor_system: :global do class ExampleError < StandardError; end class MyWorker include Celluloid def process(queue = nil) if queue queue << :done else :done end end def sleepy_work t = Time.now.to_f sleep 0.25 t end def crash raise ExampleError, "zomgcrash" end end def test_concurrency_of(pool) baseline = Time.now.to_f values = 10.times.map { pool.future.sleepy_work }.map(&:value) values.select {|t| t - baseline < 0.1 }.length 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 context "#size=" do subject { MyWorker.pool size: 4 } it "should adjust the pool size up", pending: 'flaky' do expect(test_concurrency_of(subject)).to eq(4) subject.size = 6 subject.size.should == 6 test_concurrency_of(subject).should == 6 end it "should adjust the pool size down" do test_concurrency_of(subject).should == 4 subject.size = 2 subject.size.should == 2 test_concurrency_of(subject).should == 2 end end end celluloid-0.16.0/spec/celluloid/thread_handle_spec.rb0000644000004100000410000000113512403202037022622 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::ThreadHandle do let(:actor_system) do Celluloid::ActorSystem.new end it "knows thread liveliness" do queue = Queue.new handle = Celluloid::ThreadHandle.new(actor_system) { 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(actor_system) { sleep 0.01 }.join end it "supports passing a role" do Celluloid::ThreadHandle.new(actor_system, :useful) { Thread.current.role.should == :useful }.join end end celluloid-0.16.0/spec/celluloid/block_spec.rb0000644000004100000410000000231612403202037021134 0ustar www-datawww-datarequire 'spec_helper' describe "Blocks", actor_system: :global 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.16.0/spec/celluloid/notifications_spec.rb0000644000004100000410000000544412403202037022720 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Notifications, actor_system: :global 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.16.0/spec/celluloid/future_spec.rb0000644000004100000410000000177212403202037021361 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Future, actor_system: :global 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.16.0/spec/celluloid/supervision_group_spec.rb0000644000004100000410000000273312403202037023647 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::SupervisionGroup, actor_system: :global 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 it "allows external access to the internal registry" do supervisor = MyGroup.run! supervisor[:example].should be_a MyActor end end end celluloid-0.16.0/spec/celluloid/fsm_spec.rb0000644000004100000410000000500012403202037020620 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::FSM, actor_system: :global 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.16.0/spec/celluloid/condition_spec.rb0000644000004100000410000000332512403202037022031 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Condition, actor_system: :global 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(timeout = nil) @waiting = true begin value = @condition.wait(timeout) @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 it "times out inside normal Threads" do condition = Celluloid::Condition.new lambda { condition.wait(1) }. should raise_error(Celluloid::ConditionError) end it "times out inside Tasks" do lambda { actor.wait_for_condition(1) }. should raise_error(Celluloid::ConditionError) end end celluloid-0.16.0/spec/celluloid/mailbox_spec.rb0000644000004100000410000000014212403202037021470 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Mailbox do it_behaves_like "a Celluloid Mailbox" end celluloid-0.16.0/spec/celluloid/internal_pool_spec.rb0000644000004100000410000000231012403202037022701 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.16.0/spec/celluloid/evented_mailbox_spec.rb0000644000004100000410000000111212403202037023200 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.16.0/spec/celluloid/cpu_counter_spec.rb0000644000004100000410000000475112403202037022375 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::CPUCounter do describe :cores do subject { described_class.cores } let(:num_cores) { 1024 } before do described_class.stub(:`) { fail 'backtick stub called' } ::IO.stub(:open).and_raise('IO.open stub called!') described_class.instance_variable_set('@cores', nil) end after { ENV['NUMBER_OF_PROCESSORS'] = nil } context 'from valid env value' do before { ENV['NUMBER_OF_PROCESSORS'] = num_cores.to_s } it { should eq num_cores } end context 'from invalid env value' do before { ENV['NUMBER_OF_PROCESSORS'] = '' } specify { expect { subject }.to raise_error(ArgumentError) } end context 'with no env value' do before { ENV['NUMBER_OF_PROCESSORS'] = nil } context 'when /sys/devices/system/cpu/present exists' do before do ::IO.should_receive(:read).with('/sys/devices/system/cpu/present') .and_return("dunno-whatever-#{num_cores - 1}") end it { should eq num_cores } end context 'when /sys/devices/system/cpu/present does NOT exist' do before do ::IO.should_receive(:read).with('/sys/devices/system/cpu/present') .and_raise(Errno::ENOENT) end context 'when /sys/devices/system/cpu/cpu* files exist' do before do cpu_entries = (1..num_cores).map { |n| "cpu#{n}" } cpu_entries << 'non-cpu-entry-to-ignore' Dir.should_receive(:[]).with('/sys/devices/system/cpu/cpu*') .and_return(cpu_entries) end it { should eq num_cores } end context 'when /sys/devices/system/cpu/cpu* files DO NOT exist' do before do Dir.should_receive(:[]).with('/sys/devices/system/cpu/cpu*') .and_return([]) end context 'when sysctl blows up' do before { described_class.stub(:`).and_raise(Errno::EINTR) } specify { expect { subject }.to raise_error } end context 'when sysctl fails' do before { described_class.stub(:`).and_return(`false`) } it { should be nil } end context 'when sysctl succeeds' do before do described_class.should_receive(:`).with('sysctl -n hw.ncpu') .and_return(num_cores.to_s) `true` end it { should eq num_cores } end end end end end end celluloid-0.16.0/spec/celluloid/logging/0000755000004100000410000000000012403202037020127 5ustar www-datawww-datacelluloid-0.16.0/spec/celluloid/logging/ring_buffer_spec.rb0000644000004100000410000000145712403202037023765 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.16.0/spec/celluloid/tasks/0000755000004100000410000000000012403202037017626 5ustar www-datawww-datacelluloid-0.16.0/spec/celluloid/tasks/task_thread_spec.rb0000644000004100000410000000022012403202037023450 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::TaskThread, actor_system: :within do it_behaves_like "a Celluloid Task", Celluloid::TaskThread end celluloid-0.16.0/spec/celluloid/tasks/task_fiber_spec.rb0000644000004100000410000000021612403202037023275 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::TaskFiber, actor_system: :within do it_behaves_like "a Celluloid Task", Celluloid::TaskFiber end celluloid-0.16.0/spec/celluloid/calls_spec.rb0000644000004100000410000000171512403202037021142 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::SyncCall, actor_system: :global 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.16.0/spec/celluloid/registry_spec.rb0000644000004100000410000000342512403202037021714 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Registry, actor_system: :global 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].registered_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::Actor.delete(:marilyn) Celluloid::Actor.registered.should_not include :marilyn end it "returns actor removed from the registry" do rval = Celluloid::Actor.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.16.0/spec/celluloid/stack_dump_spec.rb0000644000004100000410000000271112403202037022173 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::StackDump do let(:actor_system) do Celluloid::ActorSystem.new end subject do actor_system.stack_dump end 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_system.within do actor_klass.new end actor.async.blocking end @active_thread = actor_system.get_thread do sleep end @active_thread.role = :other_thing @idle_thread = actor_system.get_thread do end sleep 0.01 end describe '#actors' do it 'should include all actors' do subject.actors.size.should == actor_system.running.size end end describe '#threads' do it 'should include threads that are not actors', pending: 'flaky' do expect(subject.threads.size).to eq(3) end it 'should include idle threads' do subject.threads.map(&:thread_id).should include(@idle_thread.object_id) end it 'should include threads checked out of the pool for roles other than :actor' do subject.threads.map(&:thread_id).should include(@active_thread.object_id) end it 'should have the correct roles', pending: 'flaky' do expect(subject.threads.map(&:role)).to include(nil, :other_thing, :task) end end end celluloid-0.16.0/spec/celluloid/probe_spec.rb0000644000004100000410000000645012403202037021154 0ustar www-datawww-datarequire 'spec_helper' class DummyActor; include Celluloid; end class TestProbeClient include Celluloid include Celluloid::Notifications attr_reader :buffer def initialize() @condition = Condition.new subscribe(/celluloid\.events\..+/, :event_received) @buffer = [] end def wait @condition.wait end def wait_event(topic, expected_actor1 = nil, expected_actor2 = nil) loop do wait while ev = @buffer.shift() if (ev[0] == topic) && (ev[1].mailbox.address == expected_actor1.mailbox.address) && (expected_actor2.nil? || (ev[2].mailbox.address == expected_actor2.mailbox.address) ) return ev end end end end def event_received(topic, args) @buffer << [topic, args[0], args[1]] @condition.signal end end describe "Probe", actor_system: :global do describe 'on boot' do it 'should capture system actor spawn' do client = TestProbeClient.new Celluloid::Probe.run create_events = [] received_named_events = { :default_incident_reporter => nil, :notifications_fanout => nil } # wait for the events we seek Timeout.timeout(5) do loop do client.wait while ev = client.buffer.shift if ev[0] == 'celluloid.events.actor_created' create_events << ev elsif ev[0] == 'celluloid.events.actor_named' if received_named_events.keys.include?(ev[1].name) received_named_events[ev[1].name] = ev[1].mailbox.address end end end if received_named_events.all?{|_, v| v != nil } break end end end received_named_events.all?{|_, v| v != nil }.should == true # now check we got the create events for every actors received_named_events.each do |_, mailbox_address| found = create_events.detect{|_, aa| aa.mailbox.address == mailbox_address } found.should_not == nil end end end describe 'after boot' do it 'should send a notification when an actor is spawned' do client = TestProbeClient.new Celluloid::Probe.run a = DummyActor.new event = Timeout.timeout(5) do client.wait_event('celluloid.events.actor_created', a) end event.should_not == nil end it 'should send a notification when an actor is named' do client = TestProbeClient.new Celluloid::Probe.run a = DummyActor.new Celluloid::Actor['a name'] = a event = Timeout.timeout(5) do client.wait_event('celluloid.events.actor_named', a) end event.should_not == nil end it 'should send a notification when actor dies' do client = TestProbeClient.new Celluloid::Probe.run a = DummyActor.new a.terminate event = Timeout.timeout(5) do client.wait_event('celluloid.events.actor_died', a) end event.should_not == nil end it 'should send a notification when actors are linked' do client = TestProbeClient.new Celluloid::Probe.run a = DummyActor.new b = DummyActor.new a.link(b) event = Timeout.timeout(5) do client.wait_event('celluloid.events.actors_linked', a, b) end event.should_not == nil end end end celluloid-0.16.0/spec/celluloid/actor_spec.rb0000644000004100000410000000017112403202037021147 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid, actor_system: :global do it_behaves_like "a Celluloid Actor", Celluloid end celluloid-0.16.0/spec/celluloid/properties_spec.rb0000644000004100000410000000212512403202037022234 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.16.0/spec/celluloid/supervisor_spec.rb0000644000004100000410000000554512403202037022272 0ustar www-datawww-datarequire 'spec_helper' describe Celluloid::Supervisor, actor_system: :global 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 it "removes an actor if it terminates cleanly" do supervisor = Subordinate.supervise(:working) subordinate = supervisor.actors.first supervisor.actors.should == [subordinate] subordinate.terminate supervisor.actors.should be_empty end end celluloid-0.16.0/spec/celluloid/links_spec.rb0000644000004100000410000000205012403202037021155 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.16.0/lib/0000755000004100000410000000000012403202037014341 5ustar www-datawww-datacelluloid-0.16.0/lib/celluloid.rb0000644000004100000410000003366512403202037016657 0ustar www-datawww-datarequire 'logger' require 'thread' require 'timeout' require 'set' module Celluloid # Expose all instance methods as singleton methods extend self VERSION = '0.16.0' # Linking times out after 5 seconds LINKING_TIMEOUT = 5 # Warning message added to Celluloid objects accessed outside their actors BARE_OBJECT_WARNING_MESSAGE = "WARNING: BARE CELLULOID OBJECT " class << self attr_writer :actor_system # Default Actor System 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 actor_system if Thread.current.celluloid? Thread.current[:celluloid_actor_system] or raise Error, "actor system not running" else Thread.current[:celluloid_actor_system] || @actor_system or raise Error, "Celluloid is not yet started; use Celluloid.boot" end end 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::CellProxy klass.property :task_class, :default => Celluloid.task_class klass.property :mailbox_size klass.property :exclusive_actor, :default => false klass.property :exclusive_methods, :multi => true klass.property :execute_block_on_receiver, :default => [:after, :every, :receive], :multi => true klass.property :finalizer klass.property :exit_handler_name klass.send(:define_singleton_method, :trap_exit) do |*args| exit_handler_name(*args) end klass.send(:define_singleton_method, :exclusive) do |*args| if args.any? exclusive_methods(*exclusive_methods, *args) else exclusive_actor true end 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) actor_system.stack_dump.print(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 @actor_system = ActorSystem.new end def start actor_system.start end def running? actor_system && actor_system.running? 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 actor_system.shutdown 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 = Cell.new(allocate, behavior_options, 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 = Cell.new(allocate, behavior_options, 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 def actor_system Celluloid.actor_system end # Configuration options for Actor#new def actor_options { :actor_system => actor_system, :mailbox_class => mailbox_class, :mailbox_size => mailbox_size, :task_class => task_class, :exclusive => exclusive_actor, } end def behavior_options { :proxy_class => proxy_class, :exclusive_methods => exclusive_methods, :exit_handler_name => exit_handler_name, :finalizer => finalizer, :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 registered_name Actor.registered_name end alias_method :name, :registered_name def inspect return "..." if Celluloid.detect_recursion str = "#<" if leaked? str << Celluloid::BARE_OBJECT_WARNING_MESSAGE else str << "Celluloid::CellProxy" 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].behavior_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].behavior_proxy.async meth, *args, &block end # Handle calls to future within an actor itself def future(meth = nil, *args, &block) Thread.current[:celluloid_actor].behavior_proxy.future meth, *args, &block end end 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 require 'celluloid/exceptions' 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/handlers' 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/cell_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/cell' require 'celluloid/future' require 'celluloid/actor_system' require 'celluloid/pool_manager' require 'celluloid/supervision_group' require 'celluloid/supervisor' require 'celluloid/notifications' require 'celluloid/logging' require 'celluloid/legacy' unless defined?(CELLULOID_FUTURE) $CELLULOID_MONITORING = false # 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.16.0/lib/celluloid/0000755000004100000410000000000012403202037016315 5ustar www-datawww-datacelluloid-0.16.0/lib/celluloid/core_ext.rb0000644000004100000410000000017112403202037020451 0ustar www-datawww-datarequire 'celluloid/fiber' class Thread attr_accessor :uuid_counter, :uuid_limit def celluloid? false end end celluloid-0.16.0/lib/celluloid/proxies/0000755000004100000410000000000012403202037020006 5ustar www-datawww-datacelluloid-0.16.0/lib/celluloid/proxies/sync_proxy.rb0000644000004100000410000000163612403202037022556 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) || method_missing(:respond_to?, meth, include_private) 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.16.0/lib/celluloid/proxies/actor_proxy.rb0000644000004100000410000000162512403202037022710 0ustar www-datawww-datamodule Celluloid # A proxy which controls the Actor lifecycle class ActorProxy < AbstractProxy attr_reader :thread, :mailbox # Used for reflecting on proxy objects themselves def __class__; ActorProxy; end def initialize(thread, mailbox) @thread = thread @mailbox = mailbox end def inspect # TODO: use a system event to fetch actor state: tasks? "#" rescue DeadActorError "#" end def alive? @mailbox.alive? 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.16.0/lib/celluloid/proxies/future_proxy.rb0000644000004100000410000000140612403202037023107 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.16.0/lib/celluloid/proxies/cell_proxy.rb0000644000004100000410000000326112403202037022515 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 CellProxy < SyncProxy # Used for reflecting on proxy objects themselves def __class__; CellProxy; end def initialize(actor_proxy, mailbox, klass) super(mailbox, klass) @actor_proxy = actor_proxy @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 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 def alive? @actor_proxy.alive? end def thread @actor_proxy.thread end # Terminate the associated actor def terminate @actor_proxy.terminate end # Terminate the associated actor asynchronously def terminate! @actor_proxy.terminate! end end end celluloid-0.16.0/lib/celluloid/proxies/block_proxy.rb0000644000004100000410000000131512403202037022666 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.16.0/lib/celluloid/proxies/abstract_proxy.rb0000644000004100000410000000127412403202037023403 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.16.0/lib/celluloid/proxies/async_proxy.rb0000644000004100000410000000132412403202037022711 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.16.0/lib/celluloid/condition.rb0000644000004100000410000000457212403202037020640 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, timeout) @condition = condition @task = task @mailbox = mailbox @timeout = timeout end attr_reader :condition, :task def <<(message) @mailbox << message end def wait begin message = @mailbox.receive(@timeout) do |msg| msg.is_a?(SignalConditionRequest) && msg.task == Thread.current end rescue TimeoutError raise ConditionError, "timeout after #{@timeout.inspect} seconds" end until message message.value end end def initialize @mutex = Mutex.new @waiters = [] end # Wait for the given signal and return the associated value def wait(timeout = nil) raise ConditionError, "cannot wait for signals while exclusive" if Celluloid.exclusive? if actor = Thread.current[:celluloid_actor] task = Task.current if timeout bt = caller timer = actor.timers.after(timeout) do exception = ConditionError.new("timeout after #{timeout.inspect} seconds") exception.set_backtrace bt task.resume exception end end else task = Thread.current end waiter = Waiter.new(self, task, Celluloid.mailbox, timeout) @mutex.synchronize do @waiters << waiter end result = Celluloid.suspend :condwait, waiter timer.cancel if timer 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.with_backtrace(caller(3)) do |logger| logger.debug("Celluloid::Condition signaled spuriously") end 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.16.0/lib/celluloid/uuid.rb0000644000004100000410000000212412403202037017607 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.16.0/lib/celluloid/autostart.rb0000644000004100000410000000004512403202037020667 0ustar www-datawww-datarequire 'celluloid' Celluloid.start celluloid-0.16.0/lib/celluloid/actor_system.rb0000644000004100000410000000461512403202037021364 0ustar www-datawww-datamodule Celluloid class ActorSystem extend Forwardable def initialize @internal_pool = InternalPool.new @registry = Registry.new end attr_reader :registry # Launch default services # FIXME: We should set up the supervision hierarchy here def start within do Celluloid::Notifications::Fanout.supervise_as :notifications_fanout Celluloid::IncidentReporter.supervise_as :default_incident_reporter, STDERR end true end def within old = Thread.current[:celluloid_actor_system] Thread.current[:celluloid_actor_system] = self yield ensure Thread.current[:celluloid_actor_system] = old end def get_thread @internal_pool.get do Thread.current[:celluloid_actor_system] = self yield end end def stack_dump Celluloid::StackDump.new(@internal_pool) end def_delegators "@registry", :[], :get, :[]=, :set, :delete def registered @registry.names end def clear_registry @registry.clear end def running actors = [] @internal_pool.each do |t| next unless t.role == :actor actors << t.actor.behavior_proxy if t.actor && t.actor.respond_to?(:behavior_proxy) end actors end def running? @internal_pool.running? end # Shut down all running actors def shutdown actors = running Timeout.timeout(shutdown_timeout) do Logger.debug "Terminating #{actors.size} #{(actors.size > 1) ? 'actors' : 'actor'}..." if actors.size > 0 # 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 @internal_pool.shutdown 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 clear_registry end def assert_inactive @internal_pool.assert_inactive end def shutdown_timeout Celluloid.shutdown_timeout end end end celluloid-0.16.0/lib/celluloid/call_chain.rb0000644000004100000410000000044312403202037020720 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 end celluloid-0.16.0/lib/celluloid/system_events.rb0000644000004100000410000000277712403202037021567 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.16.0/lib/celluloid/evented_mailbox.rb0000644000004100000410000000360712403202037022015 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 check(timeout = nil, &block) # Get a message if it is available and process it immediately if possible: if message = next_message(block) return message end # ... otherwise, run the reactor once, either blocking or will return # after the given timeout: @reactor.run_once(timeout) # No message was received: return nil 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.16.0/lib/celluloid/internal_pool.rb0000644000004100000410000000632012403202037021510 0ustar www-datawww-datarequire 'thread' module Celluloid # Maintain a thread pool FOR SPEED!! class InternalPool attr_accessor :max_idle def initialize @mutex = Mutex.new @idle_threads = [] @all_threads = [] @busy_size = 0 @idle_size = 0 # TODO: should really adjust this based on usage @max_idle = 16 @running = true end def busy_size @busy_size end def idle_size @idle_size end def assert_running raise Error, "Thread pool is not running" unless running? 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? busy_size + idle_size > 0 end def each to_a.each {|thread| yield thread } end def to_a @mutex.synchronize { @all_threads.dup } end # Get a thread from the pool, running the given block def get(&block) @mutex.synchronize do assert_running begin if @idle_threads.empty? thread = create else thread = @idle_threads.pop @idle_size = @idle_threads.length end end until thread.status # handle crashed threads thread.busy = true @busy_size += 1 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 + 1 >= @max_idle thread[:celluloid_queue] << nil @busy_size -= 1 @all_threads.delete(thread) else @idle_threads.push thread @busy_size -= 1 @idle_size = @idle_threads.length clean_thread_locals(thread) end end end def shutdown @mutex.synchronize do finalize @all_threads.each do |thread| thread[:celluloid_queue] << nil end @all_threads.clear @idle_threads.clear @busy_size = 0 @idle_size = 0 end end def kill @mutex.synchronize do finalize @running = false @all_threads.shift.kill until @all_threads.empty? @idle_threads.clear @busy_size = 0 @idle_size = 0 end end private # 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 # @idle_threads << thread @all_threads << 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 finalize @max_idle = 0 end end end celluloid-0.16.0/lib/celluloid/mailbox.rb0000644000004100000410000000716312403202037020304 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. May return nil and may return before # the specified timeout. def check(timeout = nil, &block) message = nil @mutex.lock begin raise MailboxDead, "attempted to receive from a dead mailbox" if @dead Timers::Wait.for(timeout) do |remaining| message = next_message(&block) break message if message @condition.wait(@mutex, remaining) end ensure @mutex.unlock rescue nil end return message end # Receive a letter from the mailbox. Guaranteed to return a message. If # timeout is exceeded, raise a TimeoutError. def receive(timeout = nil, &block) Timers::Wait.for(timeout) do |remaining| if message = check(timeout, &block) return message end end raise TimeoutError.new("receive timeout exceeded") 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 # 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 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.16.0/lib/celluloid/handlers.rb0000644000004100000410000000141112403202037020437 0ustar www-datawww-datarequire 'set' module Celluloid class Handlers def initialize @handlers = Set.new end def handle(*patterns, &block) patterns.each do |pattern| handler = Handler.new pattern, block @handlers << handler end end # Handle incoming messages def handle_message(message) if handler = @handlers.find { |h| h.match(message) } handler.call message handler end end end # Methods blocking on a call to receive class Handler def initialize(pattern, block) @pattern = pattern @block = block end # Match a message with this receiver's block def match(message) @pattern === message end def call(message) @block.call message end end end celluloid-0.16.0/lib/celluloid/future.rb0000644000004100000410000000447312403202037020164 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(Celluloid.actor_system, :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.16.0/lib/celluloid/calls.rb0000644000004100000410000001007312403202037017741 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) check(obj) _block = @block && @block.to_proc obj.public_send(@method, *@arguments, &_block) end def check(obj) raise NoMethodError, "undefined method `#{@method}' for #{obj.inspect}" unless obj.respond_to? @method begin arity = obj.method(@method).arity rescue NameError return end if arity >= 0 raise ArgumentError, "wrong number of arguments (#{@arguments.size} for #{arity})" if @arguments.size != arity elsif arity < -1 mandatory_args = -arity - 1 raise ArgumentError, "wrong number of arguments (#{@arguments.size} for #{mandatory_args}+)" if arguments.size < mandatory_args end rescue => ex raise AbortError.new(ex) 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 response Celluloid.suspend(:callwait, self) end def value response.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.16.0/lib/celluloid/links.rb0000644000004100000410000000134312403202037017763 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.16.0/lib/celluloid/thread_handle.rb0000644000004100000410000000252212403202037021425 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(actor_system, role = nil) @mutex = Mutex.new @join = ConditionVariable.new @thread = actor_system.get_thread 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.16.0/lib/celluloid/thread.rb0000644000004100000410000000132512403202037020112 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.16.0/lib/celluloid/properties.rb0000644000004100000410000000142012403202037021033 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, *send(name), *extra].uniq : [] 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 end celluloid-0.16.0/lib/celluloid/logger.rb0000644000004100000410000000410712403202037020123 0ustar www-datawww-datamodule Celluloid module Logger class WithBacktrace def initialize(backtrace) @backtrace = backtrace end def debug(string) Celluloid.logger.debug(decorate(string)) end def info(string) Celluloid.logger.info(decorate(string)) end def warn(string) Celluloid.logger.warn(decorate(string)) end def error(string) Celluloid.logger.error(decorate(string)) end def decorate(string) [string, @backtrace].join("\n\t") end end @exception_handlers = [] module_function def with_backtrace(backtrace) yield WithBacktrace.new(backtrace) if Celluloid.logger end # 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.16.0/lib/celluloid/actor.rb0000644000004100000410000002375112403202037017762 0ustar www-datawww-data require 'timers' module Celluloid # 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 :behavior, :proxy, :tasks, :links, :mailbox, :thread, :name, :timers attr_writer :exit_handler class << self extend Forwardable def_delegators "Celluloid.actor_system", :[], :[]=, :delete, :registered, :clear_registry # Obtain the current actor def current actor = Thread.current[:celluloid_actor] raise NotActorError, "not in actor scope" unless actor actor.behavior_proxy end # Obtain the name of the current actor def registered_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 Celluloid.actor_system.running 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 def initialize(behavior, options) @behavior = behavior @actor_system = options.fetch(:actor_system) @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 = method(:default_exit_handler) @exclusive = options.fetch(:exclusive, false) @tasks = TaskSet.new @links = Links.new @signals = Signals.new @timers = Timers::Group.new @receivers = Receivers.new(@timers) @handlers = Handlers.new @running = false @name = nil handle(SystemEvent) do |message| handle_system_event message end end def start @running = true @thread = ThreadHandle.new(@actor_system, :actor) do setup_thread run end @proxy = ActorProxy.new(@thread, @mailbox) Celluloid::Probe.actor_created(self) if $CELLULOID_MONITORING end def behavior_proxy @behavior.proxy end def setup_thread Thread.current[:celluloid_actor] = self Thread.current[:celluloid_mailbox] = @mailbox end # Run the actor loop def run while @running begin @timers.wait do |interval| interval = 0 if interval and interval < 0 if message = @mailbox.check(interval) handle_message(message) break unless @running end end rescue MailboxShutdown @running = false end 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 receiver.mailbox << LinkingRequest.new(Actor.current, type) system_events = [] Timers::Wait.for(LINKING_TIMEOUT) do |remaining| begin message = @mailbox.receive(remaining) do |msg| msg.is_a?(LinkingResponse) && msg.actor.mailbox.address == receiver.mailbox.address && msg.type == type end rescue TimeoutError next # IO reactor did something, no message in queue yet. end if message.instance_of? LinkingResponse Celluloid::Probe.actors_linked(self, receiver) if $CELLULOID_MONITORING # We're done! system_events.each { |ev| @mailbox << ev } return elsif message.is_a? SystemEvent # Queue up pending system events to be processed after we've successfully linked system_events << message else raise "Unexpected message type: #{message.class}. Expected LinkingResponse, NilClass, SystemEvent." end end raise TimeoutError, "linking timeout of #{LINKING_TIMEOUT} seconds exceeded" 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 def handle(*patterns, &block) @handlers.handle(*patterns, &block) 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 # 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) unless @handlers.handle_message(message) 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) if event.instance_of? ExitEvent handle_exit_event(event) elsif event.instance_of? LinkingRequest event.process(links) elsif event.instance_of? NamingRequest @name = event.name Celluloid::Probe.actor_named(self) if $CELLULOID_MONITORING elsif event.instance_of? TerminationRequest terminate elsif event.instance_of? SignalConditionRequest event.call else Logger.debug "Discarded message (unhandled): #{message}" if $CELLULOID_DEBUG end end # Handle exit events received by this actor def handle_exit_event(event) @links.delete event.actor @exit_handler.call(event) end def default_exit_handler(event) raise event.reason if event.reason end # Handle any exceptions that occur within a running actor def handle_crash(exception) # TODO: add meta info Logger.crash("Actor crashed!", exception) shutdown ExitEvent.new(behavior_proxy, exception) rescue => ex Logger.crash("ERROR HANDLER CRASHED!", ex) end # Handle cleaning up this actor after it exits def shutdown(exit_event = ExitEvent.new(behavior_proxy)) @behavior.shutdown cleanup exit_event ensure Thread.current[:celluloid_actor] = nil Thread.current[:celluloid_mailbox] = nil end # Clean up after this actor def cleanup(exit_event) Celluloid::Probe.actor_died(self) if $CELLULOID_MONITORING @mailbox.shutdown @links.each do |actor| if actor.mailbox.alive? actor.mailbox << exit_event end end tasks.to_a.each(&:terminate) rescue => ex # TODO: metadata Logger.crash("CLEANUP CRASHED!", ex) end # Run a method inside a task unless it's exclusive def task(task_type, meta = nil) @task_class.new(task_type, meta) { if @exclusive Celluloid.exclusive { yield } else yield end }.resume end end end celluloid-0.16.0/lib/celluloid/fsm.rb0000644000004100000410000001206412403202037017432 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.16.0/lib/celluloid/test.rb0000644000004100000410000000005412403202037017620 0ustar www-datawww-data$CELLULOID_TEST = true require 'celluloid' celluloid-0.16.0/lib/celluloid/logging.rb0000644000004100000410000000031512403202037020267 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.16.0/lib/celluloid/fiber.rb0000644000004100000410000000171212403202037017732 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.16.0/lib/celluloid/notifications.rb0000644000004100000410000000375212403202037021522 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.16.0/lib/celluloid/logging/0000755000004100000410000000000012403202037017743 5ustar www-datawww-datacelluloid-0.16.0/lib/celluloid/logging/ring_buffer.rb0000644000004100000410000000206512403202037022563 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.16.0/lib/celluloid/logging/incident_reporter.rb0000644000004100000410000000213612403202037024011 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.16.0/lib/celluloid/logging/log_event.rb0000644000004100000410000000111412403202037022247 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.16.0/lib/celluloid/logging/incident_logger.rb0000644000004100000410000000727712403202037023441 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.16.0/lib/celluloid/logging/incident.rb0000644000004100000410000000116512403202037022070 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.16.0/lib/celluloid/stack_dump.rb0000644000004100000410000000635512403202037021005 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 :name, :id, :cell attr_accessor :status, :tasks attr_accessor :backtrace def dump string = "" string << "Celluloid::Actor 0x#{id.to_s(16)}" string << " [#{name}]" if name string << "\n" if cell string << cell.dump string << "\n" end 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 CellState < Struct.new(:subject_id, :subject_class) def dump "Celluloid::Cell 0x#{subject_id.to_s(16)}: #{subject_class}" end end class ThreadState < Struct.new(:thread_id, :backtrace, :role) include DisplayBacktrace def dump string = "" string << "Thread 0x#{thread_id.to_s(16)} (#{role}):\n" display_backtrace backtrace, string string end end attr_accessor :actors, :threads def initialize(internal_pool) @internal_pool = internal_pool @actors = [] @threads = [] snapshot end def snapshot @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.id = actor.object_id # TODO: delegate to the behavior if actor.behavior.is_a?(Cell) state.cell = snapshot_cell(actor.behavior) end 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_cell(behavior) state = CellState.new state.subject_id = behavior.subject.object_id state.subject_class = behavior.subject.class state end def snapshot_thread(thread) ThreadState.new(thread.object_id, thread.backtrace, thread.role) end def print(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.16.0/lib/celluloid/task_set.rb0000644000004100000410000000200612403202037020455 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.16.0/lib/celluloid/registry.rb0000644000004100000410000000240312403202037020511 0ustar www-datawww-datarequire 'thread' module Celluloid # The Registry allows us to refer to specific actors by human-meaningful names class Registry 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? AbstractProxy 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 end end celluloid-0.16.0/lib/celluloid/tasks/0000755000004100000410000000000012403202037017442 5ustar www-datawww-datacelluloid-0.16.0/lib/celluloid/tasks/task_thread.rb0000644000004100000410000000237712403202037022271 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 # TODO: move this to ActorSystem#get_thread (ThreadHandle inside InternalPool) @thread = ThreadHandle.new(Thread.current[:celluloid_actor_system], :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.16.0/lib/celluloid/tasks/task_fiber.rb0000644000004100000410000000236412403202037022105 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] actor_system = Thread.current[:celluloid_actor_system] @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 Thread.current[:celluloid_actor_system] = actor_system 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 def backtrace ["#{self.class} backtrace unavailable. Please try `Celluloid.task_class = Celluloid::TaskThread` if you need backtraces here."] end end end celluloid-0.16.0/lib/celluloid/method.rb0000644000004100000410000000122612403202037020123 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 name @proxy.method_missing(:method, @name).name end def parameters @proxy.method_missing(:method, @name).parameters end def call(*args, &block) @proxy.__send__(@name, *args, &block) end def inspect "#" end end end celluloid-0.16.0/lib/celluloid/signals.rb0000644000004100000410000000112212403202037020276 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.16.0/lib/celluloid/receivers.rb0000644000004100000410000000250512403202037020633 0ustar www-datawww-datarequire 'set' require 'timers' module Celluloid # Allow methods to directly interact with the actor protocol class Receivers def initialize(timers) @receivers = Set.new @timers = timers 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 # Handle incoming messages def handle_message(message) receiver = @receivers.find { |r| r.match(message) } return unless receiver @receivers.delete receiver receiver.timer.cancel if receiver.timer receiver.resume message 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.16.0/lib/celluloid/pool_manager.rb0000644000004100000410000000636612403202037021320 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, 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).map 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 size=(new_size) new_size = [0, new_size].max if new_size > size delta = new_size - size delta.times { @idle << @worker_class.new_link(*@args) } else (size - new_size).times do worker = __provision_worker__ unlink worker @busy.delete worker worker.terminate end end @size = new_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.16.0/lib/celluloid/supervision_group.rb0000644000004100000410000001007612403202037022450 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 finalizer :finalize # Start the group def initialize(registry = nil) @members = [] @registry = registry || Celluloid.actor_system.registry 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.actor end def actors @members.map(&:actor) end def [](actor_name) @registry[actor_name] 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 if reason member.restart else member.cleanup @members.delete(member) end 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 @actor = nil cleanup start end def terminate cleanup @actor.terminate if @actor rescue DeadActorError end def cleanup @registry.delete(@name) if @name end end private def finalize @members.reverse_each(&:terminate) if @members end end end celluloid-0.16.0/lib/celluloid/legacy.rb0000644000004100000410000000023012403202037020101 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.16.0/lib/celluloid/rspec/0000755000004100000410000000000012403202037017431 5ustar www-datawww-datacelluloid-0.16.0/lib/celluloid/rspec/mailbox_examples.rb0000644000004100000410000000413512403202037023312 0ustar www-datawww-datashared_context "a Celluloid Mailbox" do after do Celluloid.logger.stub(:debug) subject.shutdown if subject.alive? end 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 expect do subject.receive(interval) { false } end.to raise_exception(Celluloid::TimeoutError) (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.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.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.16.0/lib/celluloid/rspec/example_actor_class.rb0000644000004100000410000000373712403202037024000 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.16.0/lib/celluloid/rspec/actor_examples.rb0000644000004100000410000006474212403202037023001 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 #name calls via #method" do method = actor_class.new("Troy McClure").method(:greet) method.name.should == :greet end it "supports #parameters via #method" do method = actor_class.new("Troy McClure").method(:greet) method.parameters.should == [] method = actor_class.new("Troy McClure").method(:change_name) method.parameters.should == [[:req, :new_name]] 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.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.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::CellProxy\(/) 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::CellProxy\(/) 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::CellProxy\(/) 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 "when executing under JRuby" do let(:klass) { Class.new do include included_module task_class task_klass def current_thread_name java_thread.get_name end def java_thread Thread.current.to_java.getNativeThread end end } it "sets execution info" do klass.new.current_thread_name.should == "Class#current_thread_name" end it "unsets execution info after task completion" do klass.new.java_thread.get_name.should == "" end end if RUBY_PLATFORM == "java" 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 example_receiver = Class.new do include included_module task_class task_klass define_method(:receiver_method) do raise ExampleCrash, "the spec purposely crashed me :(" end end excample_caller = Class.new do include included_module task_class task_klass define_method(:sender_method) do example_receiver.new.receiver_method end end ex = nil begin excample_caller.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 "terminates cleanly on Celluloid shutdown" do Celluloid::Actor.stub(:kill).and_call_original actor = actor_class.new "Arnold Schwarzenegger" Celluloid.shutdown Celluloid::Actor.should_not have_received(:kill) 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.should_receive(:warn).with(/^Terminating task: type=:call, meta={:method_name=>:sleepy}, status=:sleeping\n/) 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::CellProxy 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.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.16.0/lib/celluloid/rspec/task_examples.rb0000644000004100000410000000204012403202037022612 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_system] = Celluloid.actor_system Thread.current[:celluloid_actor] = actor end after :each do Thread.current[:celluloid_actor] = nil Thread.current[:celluloid_actor_system] = 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.16.0/lib/celluloid/rspec.rb0000644000004100000410000000047212403202037017761 0ustar www-datawww-datarequire 'celluloid/rspec/example_actor_class' require 'celluloid/rspec/actor_examples' require 'celluloid/rspec/mailbox_examples' require 'celluloid/rspec/task_examples' module Celluloid # Timer accuracy enforced by the tests (50ms) TIMER_QUANTUM = 0.05 end $CELLULOID_DEBUG = true require 'celluloid/test' celluloid-0.16.0/lib/celluloid/probe.rb0000644000004100000410000000303412403202037017751 0ustar www-datawww-datarequire 'celluloid' $CELLULOID_MONITORING = true module Celluloid class Probe include Celluloid include Celluloid::Notifications NOTIFICATIONS_TOPIC_BASE = 'celluloid.events.%s' INITIAL_EVENTS = Queue.new class << self def run # spawn the actor if not found supervise_as(:probe_actor) unless Actor[:probe_actor] && Actor[:probe_actor].alive? end def actor_created(actor) trigger_event(:actor_created, actor) end def actor_named(actor) trigger_event(:actor_named, actor) end def actor_died(actor) trigger_event(:actor_died, actor) end def actors_linked(a, b) a = find_actor(a) b = find_actor(b) trigger_event(:actors_linked, a, b) end private def trigger_event(name, *args) return unless $CELLULOID_MONITORING probe_actor = Actor[:probe_actor] if probe_actor probe_actor.async.dispatch_event(name, args) else INITIAL_EVENTS << [name, args] end end def find_actor(obj) if obj.__send__(:class) == Actor obj elsif owner = obj.instance_variable_get(OWNER_IVAR) owner end end end def initialize async.first_run end def first_run until INITIAL_EVENTS.size == 0 event = INITIAL_EVENTS.pop dispatch_event(*event) end end def dispatch_event(cmd, args) publish(NOTIFICATIONS_TOPIC_BASE % cmd, args) end end end celluloid-0.16.0/lib/celluloid/exceptions.rb0000644000004100000410000000116012403202037021021 0ustar www-datawww-datamodule Celluloid # Base class of all Celluloid errors Error = Class.new(StandardError) # Don't do Actor-like things outside Actor scope NotActorError = Class.new(Celluloid::Error) # Trying to do something to a dead actor DeadActorError = Class.new(Celluloid::Error) # A timeout occured before the given request could complete TimeoutError = Class.new(Celluloid::Error) # 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 end celluloid-0.16.0/lib/celluloid/cpu_counter.rb0000644000004100000410000000137512403202037021176 0ustar www-datawww-datamodule Celluloid module CPUCounter class << self def cores @cores ||= count_cores end private def count_cores result = from_env || from_sysdev || from_sysctl Integer(result.to_s[/\d+/], 10) if result end def from_env result = ENV['NUMBER_OF_PROCESSORS'] result if result end def from_sysdev ::IO.read('/sys/devices/system/cpu/present').split('-').last.to_i + 1 rescue Errno::ENOENT result = Dir['/sys/devices/system/cpu/cpu*'].count { |n| n =~ /cpu\d+/ } result unless result.zero? end def from_sysctl result = `sysctl -n hw.ncpu` result if $?.success? rescue Errno::ENOENT end end end end celluloid-0.16.0/lib/celluloid/cell.rb0000644000004100000410000000512112403202037017560 0ustar www-datawww-datamodule Celluloid OWNER_IVAR = :@celluloid_owner # reference to owning actor # Wrap the given subject with an Cell class Cell class ExitHandler def initialize(behavior, subject, method_name) @behavior = behavior @subject = subject @method_name = method_name end def call(event) @behavior.task(:exit_handler, @method_name) do @subject.send(@method_name, event.actor, event.reason) end end end def initialize(subject, options, actor_options) @actor = Actor.new(self, actor_options) @subject = subject @receiver_block_executions = options[:receiver_block_executions] @exclusive_methods = options[:exclusive_methods] @finalizer = options[:finalizer] @subject.instance_variable_set(OWNER_IVAR, @actor) if exit_handler_name = options[:exit_handler_name] @actor.exit_handler = ExitHandler.new(self, @subject, exit_handler_name) end @actor.handle(Call) do |message| invoke(message) end @actor.handle(BlockCall) do |message| task(:invoke_block) { message.dispatch } end @actor.handle(BlockResponse, Response) do |message| message.dispatch end @actor.start @proxy = (options[:proxy_class] || CellProxy).new(@actor.proxy, @actor.mailbox, @subject.class.to_s) end attr_reader :proxy, :subject def invoke(call) meth = call.method if meth == :__send__ meth = call.arguments.first end if @receiver_block_executions && meth if @receiver_block_executions.include?(meth.to_sym) call.execute_block_on_receiver end end task(:call, meth, :dangerous_suspend => meth == :initialize) { call.dispatch(@subject) } end def task(task_type, method_name = nil, meta = nil, &block) meta ||= {} meta.merge!(:method_name => method_name) @actor.task(task_type, meta) do if @exclusive_methods && method_name && @exclusive_methods.include?(method_name.to_sym) Celluloid.exclusive { yield } else yield end end end # Run the user-defined finalizer, if one is set def shutdown return unless @finalizer && @subject.respond_to?(@finalizer, true) task(:finalizer, @finalizer, :dangerous_suspend => true) do begin @subject.__send__(@finalizer) rescue => ex Logger.crash("#{@subject.class} finalizer crashed!", ex) end end end end end celluloid-0.16.0/lib/celluloid/responses.rb0000644000004100000410000000142312403202037020663 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.16.0/lib/celluloid/tasks.rb0000644000004100000410000001164012403202037017771 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 name_current_thread thread_metadata Thread.current[:celluloid_task] = self CallChain.current_id = @chain_id actor.tasks << self yield rescue Task::TerminatedError # Task was explicitly terminated ensure name_current_thread nil @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 Logger.with_backtrace(caller[2...8]) do |logger| logger.warn "Dangerously suspending task: type=#{@type.inspect}, meta=#{@meta.inspect}, status=#{@status.inspect}" end 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? Logger.with_backtrace(backtrace) do |logger| logger.warn "Terminating task: type=#{@type.inspect}, meta=#{@meta.inspect}, status=#{@status.inspect}" end 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 private def name_current_thread(new_name) return unless RUBY_PLATFORM == "java" if new_name.nil? new_name = Thread.current[:celluloid_original_thread_name] Thread.current[:celluloid_original_thread_name] = nil else Thread.current[:celluloid_original_thread_name] = Thread.current.to_java.getNativeThread.get_name end Thread.current.to_java.getNativeThread.set_name(new_name) end def thread_metadata method = @meta && @meta[:method_name] || "" klass = Thread.current[:celluloid_actor] && Thread.current[:celluloid_actor].behavior.subject.bare_object.class || "" format("[Celluloid] %s#%s", klass, method) end end end require 'celluloid/tasks/task_fiber' require 'celluloid/tasks/task_thread' celluloid-0.16.0/lib/celluloid/supervisor.rb0000644000004100000410000000105712403202037021066 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.16.0/metadata.yml0000644000004100000410000001373412403202037016106 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: celluloid version: !ruby/object:Gem::Version version: 0.16.0 platform: ruby authors: - Tony Arcieri autorequire: bindir: bin cert_chain: [] date: 2014-09-05 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: timers requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 4.0.0 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 4.0.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: 2.14.1 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 2.14.1 - !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' - !ruby/object:Gem::Dependency name: rubocop 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: - LICENSE.txt - README.md - lib/celluloid.rb - lib/celluloid/actor.rb - lib/celluloid/actor_system.rb - lib/celluloid/autostart.rb - lib/celluloid/call_chain.rb - lib/celluloid/calls.rb - lib/celluloid/cell.rb - lib/celluloid/condition.rb - lib/celluloid/core_ext.rb - lib/celluloid/cpu_counter.rb - lib/celluloid/evented_mailbox.rb - lib/celluloid/exceptions.rb - lib/celluloid/fiber.rb - lib/celluloid/fsm.rb - lib/celluloid/future.rb - lib/celluloid/handlers.rb - lib/celluloid/internal_pool.rb - lib/celluloid/legacy.rb - lib/celluloid/links.rb - lib/celluloid/logger.rb - lib/celluloid/logging.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/mailbox.rb - lib/celluloid/method.rb - lib/celluloid/notifications.rb - lib/celluloid/pool_manager.rb - lib/celluloid/probe.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/cell_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/rspec/actor_examples.rb - lib/celluloid/rspec/example_actor_class.rb - lib/celluloid/rspec/mailbox_examples.rb - lib/celluloid/rspec/task_examples.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.rb - lib/celluloid/tasks/task_fiber.rb - lib/celluloid/tasks/task_thread.rb - lib/celluloid/test.rb - lib/celluloid/thread.rb - lib/celluloid/thread_handle.rb - lib/celluloid/uuid.rb - spec/celluloid/actor_spec.rb - spec/celluloid/actor_system_spec.rb - spec/celluloid/block_spec.rb - spec/celluloid/calls_spec.rb - spec/celluloid/condition_spec.rb - spec/celluloid/cpu_counter_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/probe_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/timer_spec.rb - spec/celluloid/uuid_spec.rb - spec/spec_helper.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.2.2 signing_key: specification_version: 4 summary: Actor-based concurrent object framework for Ruby test_files: [] celluloid-0.16.0/README.md0000644000004100000410000001740112403202037015055 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) Related Projects ---------------- Celluloid is the parent project of a related ecosystem of other projects. If you like Celluloid we definitely recommend you check them out: * [Celluloid::IO][celluloid-io]: "Evented" IO support for Celluloid actors * [Celluloid::ZMQ][celluloid-zmq]: "Evented" 0MQ support for Celluloid actors * [DCell][dcell]: The Celluloid actor protocol distributed over 0MQ * [Reel][reel]: An "evented" web server based on Celluloid::IO * [Lattice][lattice]: A concurrent realtime web framework based on Celluloid::IO * [nio4r][nio4r]: "New IO for Ruby": high performance IO selectors * [Timers][timers]: A generic Ruby timer library for event-based systems [celluloid-io]: https://github.com/celluloid/celluloid-io/ [celluloid-zmq]: https://github.com/celluloid/celluloid-zmq/ [dcell]: https://github.com/celluloid/dcell/ [reel]: https://github.com/celluloid/reel/ [lattice]: https://github.com/celluloid/lattice/ [nio4r]: https://github.com/celluloid/nio4r/ [timers]: https://github.com/celluloid/timers/ 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://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=11A3EACE78AAFF6D6D62A64118AFCA7C?doi=10.1.1.47.5074&rep=rep1&type=pdf): 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) 2011-2014 Tony Arcieri. Distributed under the MIT License. See LICENSE.txt for further details.