celluloid 0.14.1 → 0.15.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|