celluloid 0.14.1 → 0.15.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -2
- data/lib/celluloid.rb +92 -108
- data/lib/celluloid/actor.rb +42 -64
- data/lib/celluloid/autostart.rb +1 -1
- data/lib/celluloid/call_chain.rb +13 -0
- data/lib/celluloid/calls.rb +5 -8
- data/lib/celluloid/condition.rb +8 -10
- data/lib/celluloid/cpu_counter.rb +1 -1
- data/lib/celluloid/evented_mailbox.rb +7 -10
- data/lib/celluloid/fsm.rb +1 -1
- data/lib/celluloid/future.rb +1 -2
- data/lib/celluloid/internal_pool.rb +77 -20
- data/lib/celluloid/legacy.rb +0 -38
- data/lib/celluloid/mailbox.rb +17 -10
- data/lib/celluloid/pool_manager.rb +1 -1
- data/lib/celluloid/properties.rb +24 -0
- data/lib/celluloid/proxies/abstract_proxy.rb +3 -0
- data/lib/celluloid/proxies/actor_proxy.rb +3 -32
- data/lib/celluloid/proxies/async_proxy.rb +4 -8
- data/lib/celluloid/proxies/future_proxy.rb +8 -6
- data/lib/celluloid/proxies/sync_proxy.rb +12 -7
- data/lib/celluloid/rspec.rb +3 -1
- data/lib/celluloid/signals.rb +7 -35
- data/lib/celluloid/stack_dump.rb +50 -37
- data/lib/celluloid/supervision_group.rb +5 -5
- data/lib/celluloid/task_set.rb +49 -0
- data/lib/celluloid/tasks.rb +67 -42
- data/lib/celluloid/tasks/task_fiber.rb +3 -1
- data/lib/celluloid/tasks/task_thread.rb +2 -3
- data/lib/celluloid/thread.rb +2 -0
- data/spec/celluloid/actor_spec.rb +5 -0
- data/spec/celluloid/block_spec.rb +54 -0
- data/spec/celluloid/calls_spec.rb +42 -0
- data/spec/celluloid/condition_spec.rb +65 -0
- data/spec/celluloid/evented_mailbox_spec.rb +34 -0
- data/spec/celluloid/fsm_spec.rb +107 -0
- data/spec/celluloid/future_spec.rb +32 -0
- data/spec/celluloid/internal_pool_spec.rb +52 -0
- data/spec/celluloid/links_spec.rb +45 -0
- data/spec/celluloid/logging/ring_buffer_spec.rb +38 -0
- data/spec/celluloid/mailbox_spec.rb +5 -0
- data/spec/celluloid/notifications_spec.rb +120 -0
- data/spec/celluloid/pool_spec.rb +52 -0
- data/spec/celluloid/properties_spec.rb +42 -0
- data/spec/celluloid/registry_spec.rb +64 -0
- data/spec/celluloid/stack_dump_spec.rb +35 -0
- data/spec/celluloid/supervision_group_spec.rb +53 -0
- data/spec/celluloid/supervisor_spec.rb +92 -0
- data/spec/celluloid/tasks/task_fiber_spec.rb +5 -0
- data/spec/celluloid/tasks/task_thread_spec.rb +5 -0
- data/spec/celluloid/thread_handle_spec.rb +22 -0
- data/spec/celluloid/uuid_spec.rb +11 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/actor_examples.rb +161 -10
- data/spec/support/example_actor_class.rb +8 -0
- data/spec/support/mailbox_examples.rb +15 -3
- data/spec/support/task_examples.rb +2 -2
- metadata +28 -3
- data/lib/celluloid/version.rb +0 -4
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Celluloid::Future do
|
4
|
+
it "creates future objects that can be retrieved later" do
|
5
|
+
future = Celluloid::Future.new { 40 + 2 }
|
6
|
+
future.value.should == 42
|
7
|
+
end
|
8
|
+
|
9
|
+
it "passes arguments to future blocks" do
|
10
|
+
future = Celluloid::Future.new(40) { |n| n + 2 }
|
11
|
+
future.value.should == 42
|
12
|
+
end
|
13
|
+
|
14
|
+
it "reraises exceptions that occur when the value is retrieved" do
|
15
|
+
class ExampleError < StandardError; end
|
16
|
+
|
17
|
+
future = Celluloid::Future.new { raise ExampleError, "oh noes crash!" }
|
18
|
+
expect { future.value }.to raise_exception(ExampleError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "knows if it's got a value yet" do
|
22
|
+
future = Celluloid::Future.new { sleep Celluloid::TIMER_QUANTUM * 5 }
|
23
|
+
future.should_not be_ready
|
24
|
+
sleep Celluloid::TIMER_QUANTUM * 6
|
25
|
+
future.should be_ready
|
26
|
+
end
|
27
|
+
|
28
|
+
it "raises TimeoutError when the future times out" do
|
29
|
+
future = Celluloid::Future.new { sleep 2 }
|
30
|
+
expect { future.value(1) }.to raise_exception(Celluloid::TimeoutError)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Celluloid::InternalPool do
|
4
|
+
it "gets threads from the pool" do
|
5
|
+
subject.get { sleep 1 }.should be_a Thread
|
6
|
+
end
|
7
|
+
|
8
|
+
it "puts threads back into the pool" do
|
9
|
+
subject.idle_size.should be_zero
|
10
|
+
subject.busy_size.should be_zero
|
11
|
+
|
12
|
+
queue = Queue.new
|
13
|
+
subject.get { queue.pop }
|
14
|
+
|
15
|
+
subject.idle_size.should be_zero
|
16
|
+
subject.busy_size.should eq 1
|
17
|
+
|
18
|
+
queue << nil
|
19
|
+
sleep 0.01 # hax
|
20
|
+
|
21
|
+
subject.idle_size.should eq 1
|
22
|
+
subject.busy_size.should eq 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it "cleans thread locals from old threads" do
|
26
|
+
thread = subject.get { Thread.current[:foo] = :bar }
|
27
|
+
|
28
|
+
sleep 0.01 #hax
|
29
|
+
thread[:foo].should be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it "doesn't fail if a third-party thread is spawned" do
|
33
|
+
subject.idle_size.should be_zero
|
34
|
+
subject.busy_size.should be_zero
|
35
|
+
|
36
|
+
subject.get { ::Thread.new { sleep 0.5 } }.should be_a(Celluloid::Thread)
|
37
|
+
|
38
|
+
sleep 0.01 # hax
|
39
|
+
|
40
|
+
subject.idle_size.should eq 1
|
41
|
+
subject.busy_size.should eq 0
|
42
|
+
end
|
43
|
+
|
44
|
+
it "doesn't leak dead threads" do
|
45
|
+
subject.max_idle = 0 # Instruct the pool to immediately shut down the thread.
|
46
|
+
subject.get { true }.should be_a(Celluloid::Thread)
|
47
|
+
|
48
|
+
sleep 0.01 # hax
|
49
|
+
|
50
|
+
subject.to_a.should have(0).items
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Celluloid::Links do
|
4
|
+
subject { Celluloid::Links.new }
|
5
|
+
|
6
|
+
let(:mailbox_mock) do
|
7
|
+
Class.new(Array) do
|
8
|
+
attr_reader :address
|
9
|
+
def initialize address
|
10
|
+
@address = address
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:first_actor) do
|
16
|
+
Struct.new(:mailbox).new(mailbox_mock.new('foo123'))
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:second_actor) do
|
20
|
+
Struct.new(:mailbox).new(mailbox_mock.new('bar456'))
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'is Enumerable' do
|
24
|
+
subject.should be_an(Enumerable)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'adds actors by their mailbox address' do
|
28
|
+
subject.include?(first_actor).should be_false
|
29
|
+
subject << first_actor
|
30
|
+
subject.include?(first_actor).should be_true
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'removes actors by their mailbox address' do
|
34
|
+
subject << first_actor
|
35
|
+
subject.include?(first_actor).should be_true
|
36
|
+
subject.delete first_actor
|
37
|
+
subject.include?(first_actor).should be_false
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'iterates over all actors' do
|
41
|
+
subject << first_actor
|
42
|
+
subject << second_actor
|
43
|
+
subject.inject([]) { |all, a| all << a }.should == [first_actor, second_actor]
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Celluloid::RingBuffer do
|
4
|
+
subject { Celluloid::RingBuffer.new(2) }
|
5
|
+
|
6
|
+
it { should be_empty }
|
7
|
+
it { should_not be_full }
|
8
|
+
|
9
|
+
it 'should push and shift' do
|
10
|
+
subject.push('foo')
|
11
|
+
subject.push('foo2')
|
12
|
+
subject.shift.should eq('foo')
|
13
|
+
subject.shift.should eq('foo2')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should push past the end' do
|
17
|
+
subject.push('foo')
|
18
|
+
subject.push('foo2')
|
19
|
+
subject.push('foo3')
|
20
|
+
subject.should be_full
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should shift the most recent' do
|
24
|
+
(1..5).each { |i| subject.push(i) }
|
25
|
+
subject.shift.should be 4
|
26
|
+
subject.shift.should be 5
|
27
|
+
subject.shift.should be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should return nil when shifting empty' do
|
31
|
+
subject.should be_empty
|
32
|
+
subject.shift.should be_nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should be thread-safe' do
|
36
|
+
#TODO how to test?
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Celluloid::Notifications do
|
4
|
+
class Admirer
|
5
|
+
include Celluloid
|
6
|
+
include Celluloid::Notifications
|
7
|
+
|
8
|
+
attr_reader :mourning
|
9
|
+
attr_reader :mourning_count
|
10
|
+
|
11
|
+
def someone_died(topic, name)
|
12
|
+
@mourning = name
|
13
|
+
@mourning_count ||= 0
|
14
|
+
@mourning_count += 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class President
|
19
|
+
include Celluloid
|
20
|
+
include Celluloid::Notifications
|
21
|
+
|
22
|
+
def die
|
23
|
+
publish("death", "Mr. President")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'notifies relevant subscribers' do
|
28
|
+
marilyn = Admirer.new
|
29
|
+
jackie = Admirer.new
|
30
|
+
|
31
|
+
marilyn.subscribe("death", :someone_died)
|
32
|
+
jackie.subscribe("alive", :someone_died)
|
33
|
+
|
34
|
+
president = President.new
|
35
|
+
|
36
|
+
president.die
|
37
|
+
marilyn.mourning.should eq("Mr. President")
|
38
|
+
jackie.mourning.should_not eq("Mr. President")
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'allows multiple subscriptions from the same actor' do
|
42
|
+
marilyn = Admirer.new
|
43
|
+
|
44
|
+
marilyn.subscribe("death", :someone_died)
|
45
|
+
marilyn.subscribe("death", :someone_died)
|
46
|
+
|
47
|
+
president = President.new
|
48
|
+
|
49
|
+
president.die
|
50
|
+
marilyn.mourning_count.should be(2)
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
it 'notifies subscribers' do
|
55
|
+
marilyn = Admirer.new
|
56
|
+
jackie = Admirer.new
|
57
|
+
|
58
|
+
marilyn.subscribe("death", :someone_died)
|
59
|
+
jackie.subscribe("death", :someone_died)
|
60
|
+
|
61
|
+
president = President.new
|
62
|
+
|
63
|
+
president.die
|
64
|
+
marilyn.mourning.should eq("Mr. President")
|
65
|
+
jackie.mourning.should eq("Mr. President")
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'publishes even if there are no subscribers' do
|
69
|
+
president = President.new
|
70
|
+
president.die
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'allows regex subscriptions' do
|
74
|
+
marilyn = Admirer.new
|
75
|
+
|
76
|
+
marilyn.subscribe(/(death|assassination)/, :someone_died)
|
77
|
+
|
78
|
+
president = President.new
|
79
|
+
president.die
|
80
|
+
marilyn.mourning.should eq("Mr. President")
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'allows unsubscribing' do
|
84
|
+
marilyn = Admirer.new
|
85
|
+
|
86
|
+
subscription = marilyn.subscribe("death", :someone_died)
|
87
|
+
marilyn.unsubscribe(subscription)
|
88
|
+
|
89
|
+
president = President.new
|
90
|
+
president.die
|
91
|
+
marilyn.mourning.should be_nil
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'prunes dead subscriptions' do
|
95
|
+
marilyn = Admirer.new
|
96
|
+
jackie = Admirer.new
|
97
|
+
|
98
|
+
marilyn.subscribe("death", :someone_died)
|
99
|
+
jackie.subscribe("death", :someone_died)
|
100
|
+
|
101
|
+
listeners = Celluloid::Notifications.notifier.listeners_for("death").size
|
102
|
+
marilyn.terminate
|
103
|
+
after_listeners = Celluloid::Notifications.notifier.listeners_for("death").size
|
104
|
+
|
105
|
+
after_listeners.should == listeners - 1
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'prunes multiple subscriptions from a dead actor' do
|
109
|
+
marilyn = Admirer.new
|
110
|
+
|
111
|
+
marilyn.subscribe("death", :someone_died)
|
112
|
+
marilyn.subscribe("death", :someone_died)
|
113
|
+
|
114
|
+
listeners = Celluloid::Notifications.notifier.listeners_for("death").size
|
115
|
+
marilyn.terminate
|
116
|
+
after_listeners = Celluloid::Notifications.notifier.listeners_for("death").size
|
117
|
+
|
118
|
+
after_listeners.should eq(listeners - 2)
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Celluloid.pool" do
|
4
|
+
class ExampleError < StandardError; end
|
5
|
+
|
6
|
+
class MyWorker
|
7
|
+
include Celluloid
|
8
|
+
|
9
|
+
def process(queue = nil)
|
10
|
+
if queue
|
11
|
+
queue << :done
|
12
|
+
else
|
13
|
+
:done
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def crash
|
18
|
+
raise ExampleError, "zomgcrash"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
subject { MyWorker.pool }
|
23
|
+
|
24
|
+
it "processes work units synchronously" do
|
25
|
+
subject.process.should be :done
|
26
|
+
end
|
27
|
+
|
28
|
+
it "processes work units asynchronously" do
|
29
|
+
queue = Queue.new
|
30
|
+
subject.async.process(queue)
|
31
|
+
queue.pop.should be :done
|
32
|
+
end
|
33
|
+
|
34
|
+
it "handles crashes" do
|
35
|
+
expect { subject.crash }.to raise_error(ExampleError)
|
36
|
+
subject.process.should be :done
|
37
|
+
end
|
38
|
+
|
39
|
+
it "uses a fixed-sized number of threads" do
|
40
|
+
subject # eagerly evaluate the pool to spawn it
|
41
|
+
|
42
|
+
actors = Celluloid::Actor.all
|
43
|
+
100.times.map { subject.future(:process) }.map(&:value)
|
44
|
+
|
45
|
+
new_actors = Celluloid::Actor.all - actors
|
46
|
+
new_actors.should eq []
|
47
|
+
end
|
48
|
+
|
49
|
+
it "terminates" do
|
50
|
+
expect { subject.terminate }.to_not raise_exception
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Celluloid::Properties do
|
4
|
+
let(:default_value) { 42 }
|
5
|
+
let(:changed_value) { 43 }
|
6
|
+
|
7
|
+
let(:example_class) do
|
8
|
+
Class.new do
|
9
|
+
extend Celluloid::Properties
|
10
|
+
property :baz, :default => 42
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:example_subclass) do
|
15
|
+
Class.new(example_class)
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:example_subclass_subclass) do
|
19
|
+
Class.new(example_subclass)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "adds properties to classes" do
|
23
|
+
example_class.baz.should eq default_value
|
24
|
+
example_class.baz changed_value
|
25
|
+
example_class.baz.should eq changed_value
|
26
|
+
end
|
27
|
+
|
28
|
+
it "allows properties to be inherited" do
|
29
|
+
example_subclass.baz.should eq default_value
|
30
|
+
example_subclass.baz changed_value
|
31
|
+
example_subclass.baz.should eq changed_value
|
32
|
+
example_class.baz.should eq default_value
|
33
|
+
end
|
34
|
+
|
35
|
+
it "allows properties to be deeply inherited" do
|
36
|
+
example_subclass_subclass.baz.should eq default_value
|
37
|
+
example_subclass_subclass.baz changed_value
|
38
|
+
example_subclass_subclass.baz.should eq changed_value
|
39
|
+
example_subclass.baz.should eq default_value
|
40
|
+
example_class.baz.should eq default_value
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Celluloid::Registry do
|
4
|
+
class Marilyn
|
5
|
+
include Celluloid
|
6
|
+
|
7
|
+
def sing_for(person)
|
8
|
+
"o/~ Happy birthday, #{person}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "registers Actors" do
|
13
|
+
Celluloid::Actor[:marilyn] = Marilyn.new
|
14
|
+
Celluloid::Actor[:marilyn].sing_for("Mr. President").should == "o/~ Happy birthday, Mr. President"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "refuses to register non-Actors" do
|
18
|
+
expect do
|
19
|
+
Celluloid::Actor[:impostor] = Object.new
|
20
|
+
end.to raise_error TypeError
|
21
|
+
end
|
22
|
+
|
23
|
+
it "lists all registered actors" do
|
24
|
+
Celluloid::Actor[:marilyn] = Marilyn.new
|
25
|
+
Celluloid::Actor.registered.should include :marilyn
|
26
|
+
end
|
27
|
+
|
28
|
+
it "knows its name once registered" do
|
29
|
+
Celluloid::Actor[:marilyn] = Marilyn.new
|
30
|
+
Celluloid::Actor[:marilyn].name.should == :marilyn
|
31
|
+
end
|
32
|
+
|
33
|
+
describe :delete do
|
34
|
+
before do
|
35
|
+
Celluloid::Actor[:marilyn] ||= Marilyn.new
|
36
|
+
end
|
37
|
+
|
38
|
+
it "removes reference to actors' name from the registry" do
|
39
|
+
Celluloid::Registry.root.delete(:marilyn)
|
40
|
+
Celluloid::Actor.registered.should_not include :marilyn
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns actor removed from the registry" do
|
44
|
+
rval = Celluloid::Registry.root.delete(:marilyn)
|
45
|
+
rval.should be_kind_of(Marilyn)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe :clear do
|
50
|
+
it "should return a hash of registered actors and remove them from the registry" do
|
51
|
+
Celluloid::Actor[:marilyn] ||= Marilyn.new
|
52
|
+
rval = Celluloid::Actor.clear_registry
|
53
|
+
begin
|
54
|
+
rval.should be_kind_of(Hash)
|
55
|
+
rval.should have_key(:marilyn)
|
56
|
+
rval[:marilyn].wrapped_object.should be_instance_of(Marilyn)
|
57
|
+
Celluloid::Actor.registered.should be_empty
|
58
|
+
ensure
|
59
|
+
# Repopulate the registry once we're done
|
60
|
+
rval.each { |key, actor| Celluloid::Actor[key] = actor }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|