celluloid 0.15.2 → 0.16.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/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
|