celluloid 0.15.2 → 0.16.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +20 -0
- data/README.md +29 -2
- data/lib/celluloid.rb +68 -73
- data/lib/celluloid/actor.rb +69 -123
- data/lib/celluloid/actor_system.rb +107 -0
- data/lib/celluloid/calls.rb +16 -16
- data/lib/celluloid/cell.rb +89 -0
- data/lib/celluloid/condition.rb +25 -8
- data/lib/celluloid/cpu_counter.rb +2 -0
- data/lib/celluloid/evented_mailbox.rb +2 -1
- data/lib/celluloid/exceptions.rb +23 -0
- data/lib/celluloid/future.rb +1 -1
- data/lib/celluloid/handlers.rb +41 -0
- data/lib/celluloid/internal_pool.rb +0 -3
- data/lib/celluloid/logger.rb +30 -0
- data/lib/celluloid/logging/incident_logger.rb +1 -1
- data/lib/celluloid/mailbox.rb +19 -18
- data/lib/celluloid/method.rb +8 -0
- data/lib/celluloid/pool_manager.rb +1 -1
- data/lib/celluloid/probe.rb +73 -0
- data/lib/celluloid/properties.rb +2 -2
- data/lib/celluloid/proxies/actor_proxy.rb +9 -41
- data/lib/celluloid/proxies/cell_proxy.rb +68 -0
- data/lib/celluloid/proxies/sync_proxy.rb +1 -1
- data/lib/celluloid/receivers.rb +1 -0
- data/lib/celluloid/registry.rb +1 -8
- data/lib/celluloid/stack_dump.rb +34 -11
- data/lib/celluloid/supervision_group.rb +26 -14
- data/lib/celluloid/tasks.rb +6 -9
- data/lib/celluloid/tasks/task_fiber.rb +6 -0
- data/lib/celluloid/tasks/task_thread.rb +2 -1
- data/lib/celluloid/thread_handle.rb +2 -2
- data/spec/celluloid/actor_spec.rb +1 -1
- data/spec/celluloid/actor_system_spec.rb +69 -0
- data/spec/celluloid/block_spec.rb +1 -1
- data/spec/celluloid/calls_spec.rb +1 -1
- data/spec/celluloid/condition_spec.rb +14 -3
- data/spec/celluloid/cpu_counter_spec.rb +9 -0
- data/spec/celluloid/fsm_spec.rb +1 -1
- data/spec/celluloid/future_spec.rb +1 -1
- data/spec/celluloid/notifications_spec.rb +1 -1
- data/spec/celluloid/pool_spec.rb +1 -1
- data/spec/celluloid/probe_spec.rb +121 -0
- data/spec/celluloid/registry_spec.rb +6 -6
- data/spec/celluloid/stack_dump_spec.rb +37 -8
- data/spec/celluloid/supervision_group_spec.rb +7 -1
- data/spec/celluloid/supervisor_spec.rb +12 -1
- data/spec/celluloid/tasks/task_fiber_spec.rb +1 -1
- data/spec/celluloid/tasks/task_thread_spec.rb +1 -1
- data/spec/celluloid/thread_handle_spec.rb +7 -3
- data/spec/spec_helper.rb +20 -7
- data/spec/support/actor_examples.rb +33 -15
- data/spec/support/mailbox_examples.rb +9 -3
- data/spec/support/task_examples.rb +2 -0
- metadata +32 -22
@@ -5,6 +5,7 @@ module Celluloid
|
|
5
5
|
trap_exit :restart_actor
|
6
6
|
|
7
7
|
class << self
|
8
|
+
|
8
9
|
# Actors or sub-applications to be supervised
|
9
10
|
def blocks
|
10
11
|
@blocks ||= []
|
@@ -55,10 +56,12 @@ module Celluloid
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
59
|
+
finalizer :finalize
|
60
|
+
|
58
61
|
# Start the group
|
59
62
|
def initialize(registry = nil)
|
60
63
|
@members = []
|
61
|
-
@registry = registry ||
|
64
|
+
@registry = registry || Celluloid.actor_system.registry
|
62
65
|
|
63
66
|
yield current_actor if block_given?
|
64
67
|
end
|
@@ -81,18 +84,15 @@ module Celluloid
|
|
81
84
|
def add(klass, options)
|
82
85
|
member = Member.new(@registry, klass, options)
|
83
86
|
@members << member
|
84
|
-
member
|
87
|
+
member.actor
|
85
88
|
end
|
86
89
|
|
87
90
|
def actors
|
88
91
|
@members.map(&:actor)
|
89
92
|
end
|
90
93
|
|
91
|
-
|
92
|
-
|
93
|
-
# Terminate the group
|
94
|
-
def finalize
|
95
|
-
@members.reverse_each(&:terminate)
|
94
|
+
def [](actor_name)
|
95
|
+
@registry[actor_name]
|
96
96
|
end
|
97
97
|
|
98
98
|
# Restart a crashed actor
|
@@ -102,7 +102,12 @@ module Celluloid
|
|
102
102
|
end
|
103
103
|
raise "a group member went missing. This shouldn't be!" unless member
|
104
104
|
|
105
|
-
|
105
|
+
if reason
|
106
|
+
member.restart
|
107
|
+
else
|
108
|
+
member.cleanup
|
109
|
+
@members.delete(member)
|
110
|
+
end
|
106
111
|
end
|
107
112
|
|
108
113
|
# A member of the group
|
@@ -137,21 +142,28 @@ module Celluloid
|
|
137
142
|
@registry[@name] = @actor if @name
|
138
143
|
end
|
139
144
|
|
140
|
-
def restart
|
145
|
+
def restart
|
141
146
|
@actor = nil
|
142
|
-
|
143
|
-
|
144
|
-
# Ignore supervisors that shut down cleanly
|
145
|
-
return unless reason
|
147
|
+
cleanup
|
146
148
|
|
147
149
|
start
|
148
150
|
end
|
149
151
|
|
150
152
|
def terminate
|
151
|
-
|
153
|
+
cleanup
|
152
154
|
@actor.terminate if @actor
|
153
155
|
rescue DeadActorError
|
154
156
|
end
|
157
|
+
|
158
|
+
def cleanup
|
159
|
+
@registry.delete(@name) if @name
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def finalize
|
166
|
+
@members.reverse_each(&:terminate) if @members
|
155
167
|
end
|
156
168
|
end
|
157
169
|
end
|
data/lib/celluloid/tasks.rb
CHANGED
@@ -74,14 +74,9 @@ module Celluloid
|
|
74
74
|
@status = status
|
75
75
|
|
76
76
|
if $CELLULOID_DEBUG && @dangerous_suspend
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
"meta=#{@meta.inspect}",
|
81
|
-
"status=#{@status.inspect}"
|
82
|
-
].join(", ")
|
83
|
-
|
84
|
-
Logger.warn [warning, *caller[2..8]].join("\n\t")
|
77
|
+
Logger.with_backtrace(caller[2...8]) do |logger|
|
78
|
+
logger.warn "Dangerously suspending task: type=#{@type.inspect}, meta=#{@meta.inspect}, status=#{@status.inspect}"
|
79
|
+
end
|
85
80
|
end
|
86
81
|
|
87
82
|
value = signal
|
@@ -118,7 +113,9 @@ module Celluloid
|
|
118
113
|
raise "Cannot terminate an exclusive task" if exclusive?
|
119
114
|
|
120
115
|
if running?
|
121
|
-
|
116
|
+
Logger.with_backtrace(backtrace) do |logger|
|
117
|
+
logger.warn "Terminating task: type=#{@type.inspect}, meta=#{@meta.inspect}, status=#{@status.inspect}"
|
118
|
+
end
|
122
119
|
exception = Task::TerminatedError.new("task was terminated")
|
123
120
|
exception.set_backtrace(caller)
|
124
121
|
resume exception
|
@@ -6,10 +6,12 @@ module Celluloid
|
|
6
6
|
|
7
7
|
def create
|
8
8
|
queue = Thread.current[:celluloid_queue]
|
9
|
+
actor_system = Thread.current[:celluloid_actor_system]
|
9
10
|
@fiber = Fiber.new do
|
10
11
|
# FIXME: cannot use the writer as specs run inside normal Threads
|
11
12
|
Thread.current[:celluloid_role] = :actor
|
12
13
|
Thread.current[:celluloid_queue] = queue
|
14
|
+
Thread.current[:celluloid_actor_system] = actor_system
|
13
15
|
yield
|
14
16
|
end
|
15
17
|
end
|
@@ -33,5 +35,9 @@ module Celluloid
|
|
33
35
|
rescue FiberError
|
34
36
|
# If we're getting this the task should already be dead
|
35
37
|
end
|
38
|
+
|
39
|
+
def backtrace
|
40
|
+
"#{self.class} backtrace unavailable. Please try `Celluloid.task_class = Celluloid::TaskThread` if you need backtraces here."
|
41
|
+
end
|
36
42
|
end
|
37
43
|
end
|
@@ -12,7 +12,8 @@ module Celluloid
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def create
|
15
|
-
|
15
|
+
# TODO: move this to ActorSystem#get_thread (ThreadHandle inside InternalPool)
|
16
|
+
@thread = ThreadHandle.new(Thread.current[:celluloid_actor_system], :task) do
|
16
17
|
begin
|
17
18
|
ex = @resume_queue.pop
|
18
19
|
raise ex if ex.is_a?(Task::TerminatedError)
|
@@ -3,11 +3,11 @@ module Celluloid
|
|
3
3
|
# accidentally do things to threads which have been returned to the pool,
|
4
4
|
# such as, say, killing them
|
5
5
|
class ThreadHandle
|
6
|
-
def initialize(role = nil)
|
6
|
+
def initialize(actor_system, role = nil)
|
7
7
|
@mutex = Mutex.new
|
8
8
|
@join = ConditionVariable.new
|
9
9
|
|
10
|
-
@thread =
|
10
|
+
@thread = actor_system.get_thread do
|
11
11
|
Thread.current.role = role
|
12
12
|
begin
|
13
13
|
yield
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Celluloid::ActorSystem do
|
4
|
+
class TestActor
|
5
|
+
include Celluloid
|
6
|
+
end
|
7
|
+
|
8
|
+
it "supports non-global ActorSystem" do
|
9
|
+
subject.within do
|
10
|
+
Celluloid.actor_system.should == subject
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "starts default actors" do
|
15
|
+
subject.start
|
16
|
+
|
17
|
+
subject.registered.should == [:notifications_fanout, :default_incident_reporter]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "support getting threads" do
|
21
|
+
queue = Queue.new
|
22
|
+
thread = subject.get_thread do
|
23
|
+
Celluloid.actor_system.should == subject
|
24
|
+
queue << nil
|
25
|
+
end
|
26
|
+
queue.pop
|
27
|
+
end
|
28
|
+
|
29
|
+
it "allows a stack dump" do
|
30
|
+
subject.stack_dump.should be_a(Celluloid::StackDump)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns named actors" do
|
34
|
+
subject.registered.should be_empty
|
35
|
+
|
36
|
+
subject.within do
|
37
|
+
TestActor.supervise_as :test
|
38
|
+
end
|
39
|
+
|
40
|
+
subject.registered.should == [:test]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns running actors" do
|
44
|
+
subject.running.should be_empty
|
45
|
+
|
46
|
+
first = subject.within do
|
47
|
+
TestActor.new
|
48
|
+
end
|
49
|
+
|
50
|
+
second = subject.within do
|
51
|
+
TestActor.new
|
52
|
+
end
|
53
|
+
|
54
|
+
subject.running.should == [first, second]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "shuts down" do
|
58
|
+
subject.shutdown
|
59
|
+
|
60
|
+
lambda { subject.get_thread }.
|
61
|
+
should raise_error("Thread pool is not running")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "warns nicely when no actor system is started" do
|
65
|
+
lambda { TestActor.new }.
|
66
|
+
should raise_error("Celluloid is not yet started; use Celluloid.boot")
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Celluloid::Condition do
|
3
|
+
describe Celluloid::Condition, actor_system: :global do
|
4
4
|
class ConditionExample
|
5
5
|
include Celluloid
|
6
6
|
|
@@ -17,10 +17,10 @@ describe Celluloid::Condition do
|
|
17
17
|
condition.signal value
|
18
18
|
end
|
19
19
|
|
20
|
-
def wait_for_condition
|
20
|
+
def wait_for_condition(timeout = nil)
|
21
21
|
@waiting = true
|
22
22
|
begin
|
23
|
-
value = @condition.wait
|
23
|
+
value = @condition.wait(timeout)
|
24
24
|
@signaled_times += 1
|
25
25
|
ensure
|
26
26
|
@waiting = false
|
@@ -62,4 +62,15 @@ describe Celluloid::Condition do
|
|
62
62
|
actor.async.signal_condition condition, :value
|
63
63
|
condition.wait.should eq(:value)
|
64
64
|
end
|
65
|
+
|
66
|
+
it "times out inside normal Threads" do
|
67
|
+
condition = Celluloid::Condition.new
|
68
|
+
lambda { condition.wait(1) }.
|
69
|
+
should raise_error(Celluloid::ConditionError)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "times out inside Tasks" do
|
73
|
+
lambda { actor.wait_for_condition(1) }.
|
74
|
+
should raise_error(Celluloid::ConditionError)
|
75
|
+
end
|
65
76
|
end
|
data/spec/celluloid/fsm_spec.rb
CHANGED
data/spec/celluloid/pool_spec.rb
CHANGED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyActor; include Celluloid; end
|
4
|
+
|
5
|
+
class TestProbeClient
|
6
|
+
include Celluloid
|
7
|
+
include Celluloid::Notifications
|
8
|
+
|
9
|
+
attr_reader :buffer
|
10
|
+
|
11
|
+
def initialize()
|
12
|
+
@condition = Condition.new
|
13
|
+
subscribe(/celluloid\.events\..+/, :event_received)
|
14
|
+
@buffer = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def wait
|
18
|
+
@condition.wait
|
19
|
+
end
|
20
|
+
|
21
|
+
def wait_event(topic, expected_actor1 = nil, expected_actor2 = nil)
|
22
|
+
loop do
|
23
|
+
wait
|
24
|
+
while ev = @buffer.shift()
|
25
|
+
if (ev[0] == topic) && (ev[1].mailbox.address == expected_actor1.mailbox.address) &&
|
26
|
+
(expected_actor2.nil? || (ev[2].mailbox.address == expected_actor2.mailbox.address) )
|
27
|
+
return ev
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def event_received(topic, args)
|
34
|
+
@buffer << [topic, args[0], args[1]]
|
35
|
+
@condition.signal
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "Probe", actor_system: :global do
|
40
|
+
describe 'on boot' do
|
41
|
+
it 'should capture system actor spawn' do
|
42
|
+
client = TestProbeClient.new
|
43
|
+
Celluloid::Probe.run
|
44
|
+
create_events = []
|
45
|
+
received_named_events = {
|
46
|
+
:default_incident_reporter => nil,
|
47
|
+
:notifications_fanout => nil
|
48
|
+
}
|
49
|
+
# wait for the events we seek
|
50
|
+
Timeout.timeout(5) do
|
51
|
+
loop do
|
52
|
+
client.wait
|
53
|
+
while ev = client.buffer.shift
|
54
|
+
if ev[0] == 'celluloid.events.actor_created'
|
55
|
+
create_events << ev
|
56
|
+
elsif ev[0] == 'celluloid.events.actor_named'
|
57
|
+
if received_named_events.keys.include?(ev[1].name)
|
58
|
+
received_named_events[ev[1].name] = ev[1].mailbox.address
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
if received_named_events.all?{|_, v| v != nil }
|
63
|
+
break
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
received_named_events.all?{|_, v| v != nil }.should == true
|
68
|
+
# now check we got the create events for every actors
|
69
|
+
received_named_events.each do |_, mailbox_address|
|
70
|
+
found = create_events.detect{|_, aa| aa.mailbox.address == mailbox_address }
|
71
|
+
found.should_not == nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe 'after boot' do
|
77
|
+
it 'should send a notification when an actor is spawned' do
|
78
|
+
client = TestProbeClient.new
|
79
|
+
Celluloid::Probe.run
|
80
|
+
a = DummyActor.new
|
81
|
+
event = Timeout.timeout(5) do
|
82
|
+
client.wait_event('celluloid.events.actor_created', a)
|
83
|
+
end
|
84
|
+
event.should_not == nil
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should send a notification when an actor is named' do
|
88
|
+
client = TestProbeClient.new
|
89
|
+
Celluloid::Probe.run
|
90
|
+
a = DummyActor.new
|
91
|
+
Celluloid::Actor['a name'] = a
|
92
|
+
event = Timeout.timeout(5) do
|
93
|
+
client.wait_event('celluloid.events.actor_named', a)
|
94
|
+
end
|
95
|
+
event.should_not == nil
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should send a notification when actor dies' do
|
99
|
+
client = TestProbeClient.new
|
100
|
+
Celluloid::Probe.run
|
101
|
+
a = DummyActor.new
|
102
|
+
a.terminate
|
103
|
+
event = Timeout.timeout(5) do
|
104
|
+
client.wait_event('celluloid.events.actor_died', a)
|
105
|
+
end
|
106
|
+
event.should_not == nil
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should send a notification when actors are linked' do
|
110
|
+
client = TestProbeClient.new
|
111
|
+
Celluloid::Probe.run
|
112
|
+
a = DummyActor.new
|
113
|
+
b = DummyActor.new
|
114
|
+
a.link(b)
|
115
|
+
event = Timeout.timeout(5) do
|
116
|
+
client.wait_event('celluloid.events.actors_linked', a, b)
|
117
|
+
end
|
118
|
+
event.should_not == nil
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|