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
data/lib/celluloid/logger.rb
CHANGED
@@ -1,8 +1,38 @@
|
|
1
1
|
module Celluloid
|
2
2
|
module Logger
|
3
|
+
class WithBacktrace
|
4
|
+
def initialize(backtrace)
|
5
|
+
@backtrace = backtrace
|
6
|
+
end
|
7
|
+
|
8
|
+
def debug(string)
|
9
|
+
Celluloid.logger.debug(decorate(string))
|
10
|
+
end
|
11
|
+
|
12
|
+
def info(string)
|
13
|
+
Celluloid.logger.info(decorate(string))
|
14
|
+
end
|
15
|
+
|
16
|
+
def warn(string)
|
17
|
+
Celluloid.logger.warn(decorate(string))
|
18
|
+
end
|
19
|
+
|
20
|
+
def error(string)
|
21
|
+
Celluloid.logger.error(decorate(string))
|
22
|
+
end
|
23
|
+
|
24
|
+
def decorate(string)
|
25
|
+
[string, @backtrace].join("\n\t")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
3
29
|
@exception_handlers = []
|
4
30
|
module_function
|
5
31
|
|
32
|
+
def with_backtrace(backtrace)
|
33
|
+
yield WithBacktrace.new(backtrace) if Celluloid.logger
|
34
|
+
end
|
35
|
+
|
6
36
|
# Send a debug message
|
7
37
|
def debug(string)
|
8
38
|
Celluloid.logger.debug(string) if Celluloid.logger
|
data/lib/celluloid/mailbox.rb
CHANGED
@@ -58,10 +58,11 @@ module Celluloid
|
|
58
58
|
|
59
59
|
unless message
|
60
60
|
if timeout
|
61
|
+
# TODO: use hitimes/timers instead of Time.now
|
61
62
|
now = Time.now
|
62
63
|
wait_until ||= now + timeout
|
63
64
|
wait_interval = wait_until - now
|
64
|
-
|
65
|
+
raise(TimeoutError, "mailbox timeout exceeded", nil) if wait_interval <= 0
|
65
66
|
else
|
66
67
|
wait_interval = nil
|
67
68
|
end
|
@@ -76,23 +77,6 @@ module Celluloid
|
|
76
77
|
end
|
77
78
|
end
|
78
79
|
|
79
|
-
# Retrieve the next message in the mailbox
|
80
|
-
def next_message
|
81
|
-
message = nil
|
82
|
-
|
83
|
-
if block_given?
|
84
|
-
index = @messages.index do |msg|
|
85
|
-
yield(msg) || msg.is_a?(SystemEvent)
|
86
|
-
end
|
87
|
-
|
88
|
-
message = @messages.slice!(index, 1).first if index
|
89
|
-
else
|
90
|
-
message = @messages.shift
|
91
|
-
end
|
92
|
-
|
93
|
-
message
|
94
|
-
end
|
95
|
-
|
96
80
|
# Shut down this mailbox and clean up its contents
|
97
81
|
def shutdown
|
98
82
|
raise MailboxDead, "mailbox already shutdown" if @dead
|
@@ -141,6 +125,23 @@ module Celluloid
|
|
141
125
|
|
142
126
|
private
|
143
127
|
|
128
|
+
# Retrieve the next message in the mailbox
|
129
|
+
def next_message
|
130
|
+
message = nil
|
131
|
+
|
132
|
+
if block_given?
|
133
|
+
index = @messages.index do |msg|
|
134
|
+
yield(msg) || msg.is_a?(SystemEvent)
|
135
|
+
end
|
136
|
+
|
137
|
+
message = @messages.slice!(index, 1).first if index
|
138
|
+
else
|
139
|
+
message = @messages.shift
|
140
|
+
end
|
141
|
+
|
142
|
+
message
|
143
|
+
end
|
144
|
+
|
144
145
|
def dead_letter(message)
|
145
146
|
Logger.debug "Discarded message (mailbox is dead): #{message}" if $CELLULOID_DEBUG
|
146
147
|
end
|
data/lib/celluloid/method.rb
CHANGED
@@ -13,6 +13,14 @@ module Celluloid
|
|
13
13
|
@proxy.method_missing(:method, @name).arity
|
14
14
|
end
|
15
15
|
|
16
|
+
def name
|
17
|
+
@proxy.method_missing(:method, @name).name
|
18
|
+
end
|
19
|
+
|
20
|
+
def parameters
|
21
|
+
@proxy.method_missing(:method, @name).parameters
|
22
|
+
end
|
23
|
+
|
16
24
|
def call(*args, &block)
|
17
25
|
@proxy.__send__(@name, *args, &block)
|
18
26
|
end
|
@@ -10,7 +10,7 @@ module Celluloid
|
|
10
10
|
finalizer :__shutdown__
|
11
11
|
|
12
12
|
def initialize(worker_class, options = {})
|
13
|
-
@size = options[:size] || [Celluloid.cores, 2].max
|
13
|
+
@size = options[:size] || [Celluloid.cores || 2, 2].max
|
14
14
|
raise ArgumentError, "minimum pool size is 2" if @size < 2
|
15
15
|
|
16
16
|
@worker_class = worker_class
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
|
3
|
+
$CELLULOID_MONITORING = true
|
4
|
+
|
5
|
+
module Celluloid
|
6
|
+
class Probe
|
7
|
+
include Celluloid
|
8
|
+
include Celluloid::Notifications
|
9
|
+
|
10
|
+
NOTIFICATIONS_TOPIC_BASE = 'celluloid.events.%s'
|
11
|
+
INITIAL_EVENTS = Queue.new
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def run
|
15
|
+
# spawn the actor if not found
|
16
|
+
supervise_as(:probe_actor) unless Actor[:probe_actor] && Actor[:probe_actor].alive?
|
17
|
+
end
|
18
|
+
|
19
|
+
def actor_created(actor)
|
20
|
+
trigger_event(:actor_created, actor)
|
21
|
+
end
|
22
|
+
|
23
|
+
def actor_named(actor)
|
24
|
+
trigger_event(:actor_named, actor)
|
25
|
+
end
|
26
|
+
|
27
|
+
def actor_died(actor)
|
28
|
+
trigger_event(:actor_died, actor)
|
29
|
+
end
|
30
|
+
|
31
|
+
def actors_linked(a, b)
|
32
|
+
a = find_actor(a)
|
33
|
+
b = find_actor(b)
|
34
|
+
trigger_event(:actors_linked, a, b)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def trigger_event(name, *args)
|
40
|
+
return unless $CELLULOID_MONITORING
|
41
|
+
probe_actor = Actor[:probe_actor]
|
42
|
+
if probe_actor
|
43
|
+
probe_actor.async.dispatch_event(name, args)
|
44
|
+
else
|
45
|
+
INITIAL_EVENTS << [name, args]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_actor(obj)
|
50
|
+
if obj.__send__(:class) == Actor
|
51
|
+
obj
|
52
|
+
elsif owner = obj.instance_variable_get(OWNER_IVAR)
|
53
|
+
owner
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def initialize
|
59
|
+
async.first_run
|
60
|
+
end
|
61
|
+
|
62
|
+
def first_run
|
63
|
+
until INITIAL_EVENTS.size == 0
|
64
|
+
event = INITIAL_EVENTS.pop
|
65
|
+
dispatch_event(*event)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def dispatch_event(cmd, args)
|
70
|
+
publish(NOTIFICATIONS_TOPIC_BASE % cmd, args)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/celluloid/properties.rb
CHANGED
@@ -9,7 +9,7 @@ module Celluloid
|
|
9
9
|
|
10
10
|
ancestors.first.send(:define_singleton_method, name) do |value = nil, *extra|
|
11
11
|
if value
|
12
|
-
value = value ? [value, *extra] : [] if multi
|
12
|
+
value = value ? [value, *send(name), *extra].uniq : [] if multi
|
13
13
|
instance_variable_set(ivar_name, value)
|
14
14
|
elsif instance_variables.include?(ivar_name)
|
15
15
|
instance_variable_get(ivar_name)
|
@@ -21,4 +21,4 @@ module Celluloid
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
|
-
end
|
24
|
+
end
|
@@ -1,59 +1,27 @@
|
|
1
1
|
module Celluloid
|
2
|
-
# A proxy
|
3
|
-
|
4
|
-
|
5
|
-
attr_reader :thread
|
2
|
+
# A proxy which controls the Actor lifecycle
|
3
|
+
class ActorProxy < AbstractProxy
|
4
|
+
attr_reader :thread, :mailbox
|
6
5
|
|
7
6
|
# Used for reflecting on proxy objects themselves
|
8
7
|
def __class__; ActorProxy; end
|
9
8
|
|
10
|
-
def initialize(
|
11
|
-
@thread =
|
12
|
-
|
13
|
-
super(actor.mailbox, actor.subject.class.to_s)
|
14
|
-
@sync_proxy = SyncProxy.new(@mailbox, @klass)
|
15
|
-
@async_proxy = AsyncProxy.new(@mailbox, @klass)
|
16
|
-
@future_proxy = FutureProxy.new(@mailbox, @klass)
|
17
|
-
end
|
18
|
-
|
19
|
-
def _send_(meth, *args, &block)
|
20
|
-
method_missing :__send__, meth, *args, &block
|
9
|
+
def initialize(thread, mailbox)
|
10
|
+
@thread = thread
|
11
|
+
@mailbox = mailbox
|
21
12
|
end
|
22
13
|
|
23
14
|
def inspect
|
24
|
-
|
15
|
+
# TODO: use a system event to fetch actor state: tasks?
|
16
|
+
"#<Celluloid::ActorProxy(#{@mailbox.address}) alive>"
|
25
17
|
rescue DeadActorError
|
26
|
-
"#<Celluloid::ActorProxy(#{@
|
27
|
-
end
|
28
|
-
|
29
|
-
def method(name)
|
30
|
-
Method.new(self, name)
|
18
|
+
"#<Celluloid::ActorProxy(#{@mailbox.address}) dead>"
|
31
19
|
end
|
32
20
|
|
33
21
|
def alive?
|
34
22
|
@mailbox.alive?
|
35
23
|
end
|
36
24
|
|
37
|
-
alias_method :sync, :method_missing
|
38
|
-
|
39
|
-
# Obtain an async proxy or explicitly invoke a named async method
|
40
|
-
def async(method_name = nil, *args, &block)
|
41
|
-
if method_name
|
42
|
-
@async_proxy.method_missing method_name, *args, &block
|
43
|
-
else
|
44
|
-
@async_proxy
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Obtain a future proxy or explicitly invoke a named future method
|
49
|
-
def future(method_name = nil, *args, &block)
|
50
|
-
if method_name
|
51
|
-
@future_proxy.method_missing method_name, *args, &block
|
52
|
-
else
|
53
|
-
@future_proxy
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
25
|
# Terminate the associated actor
|
58
26
|
def terminate
|
59
27
|
terminate!
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Celluloid
|
2
|
+
# A proxy object returned from Celluloid::Actor.new/new_link which converts
|
3
|
+
# the normal Ruby method protocol into an inter-actor message protocol
|
4
|
+
class CellProxy < SyncProxy
|
5
|
+
# Used for reflecting on proxy objects themselves
|
6
|
+
def __class__; CellProxy; end
|
7
|
+
|
8
|
+
def initialize(actor_proxy, mailbox, klass)
|
9
|
+
super(mailbox, klass)
|
10
|
+
@actor_proxy = actor_proxy
|
11
|
+
@sync_proxy = SyncProxy.new(mailbox, klass)
|
12
|
+
@async_proxy = AsyncProxy.new(mailbox, klass)
|
13
|
+
@future_proxy = FutureProxy.new(mailbox, klass)
|
14
|
+
end
|
15
|
+
|
16
|
+
def _send_(meth, *args, &block)
|
17
|
+
method_missing :__send__, meth, *args, &block
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
method_missing :inspect
|
22
|
+
rescue DeadActorError
|
23
|
+
"#<Celluloid::CellProxy(#{@klass}) dead>"
|
24
|
+
end
|
25
|
+
|
26
|
+
def method(name)
|
27
|
+
Method.new(self, name)
|
28
|
+
end
|
29
|
+
|
30
|
+
alias_method :sync, :method_missing
|
31
|
+
|
32
|
+
# Obtain an async proxy or explicitly invoke a named async method
|
33
|
+
def async(method_name = nil, *args, &block)
|
34
|
+
if method_name
|
35
|
+
@async_proxy.method_missing method_name, *args, &block
|
36
|
+
else
|
37
|
+
@async_proxy
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Obtain a future proxy or explicitly invoke a named future method
|
42
|
+
def future(method_name = nil, *args, &block)
|
43
|
+
if method_name
|
44
|
+
@future_proxy.method_missing method_name, *args, &block
|
45
|
+
else
|
46
|
+
@future_proxy
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def alive?
|
51
|
+
@actor_proxy.alive?
|
52
|
+
end
|
53
|
+
|
54
|
+
def thread
|
55
|
+
@actor_proxy.thread
|
56
|
+
end
|
57
|
+
|
58
|
+
# Terminate the associated actor
|
59
|
+
def terminate
|
60
|
+
@actor_proxy.terminate
|
61
|
+
end
|
62
|
+
|
63
|
+
# Terminate the associated actor asynchronously
|
64
|
+
def terminate!
|
65
|
+
@actor_proxy.terminate!
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -15,7 +15,7 @@ module Celluloid
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def respond_to?(meth, include_private = false)
|
18
|
-
__class__.instance_methods.include?(meth) ||
|
18
|
+
__class__.instance_methods.include?(meth) || method_missing(:respond_to?, meth, include_private)
|
19
19
|
end
|
20
20
|
|
21
21
|
def method_missing(meth, *args, &block)
|
data/lib/celluloid/receivers.rb
CHANGED
data/lib/celluloid/registry.rb
CHANGED
@@ -3,10 +3,6 @@ require 'thread'
|
|
3
3
|
module Celluloid
|
4
4
|
# The Registry allows us to refer to specific actors by human-meaningful names
|
5
5
|
class Registry
|
6
|
-
class << self
|
7
|
-
attr_reader :root
|
8
|
-
end
|
9
|
-
|
10
6
|
def initialize
|
11
7
|
@registry = {}
|
12
8
|
@registry_lock = Mutex.new
|
@@ -15,7 +11,7 @@ module Celluloid
|
|
15
11
|
# Register an Actor
|
16
12
|
def []=(name, actor)
|
17
13
|
actor_singleton = class << actor; self; end
|
18
|
-
unless actor_singleton.ancestors.include?
|
14
|
+
unless actor_singleton.ancestors.include? AbstractProxy
|
19
15
|
raise TypeError, "not an actor"
|
20
16
|
end
|
21
17
|
|
@@ -57,8 +53,5 @@ module Celluloid
|
|
57
53
|
end
|
58
54
|
hash
|
59
55
|
end
|
60
|
-
|
61
|
-
# Create the default registry
|
62
|
-
@root = new
|
63
56
|
end
|
64
57
|
end
|
data/lib/celluloid/stack_dump.rb
CHANGED
@@ -16,17 +16,21 @@ module Celluloid
|
|
16
16
|
|
17
17
|
class ActorState
|
18
18
|
include DisplayBacktrace
|
19
|
-
|
20
|
-
attr_accessor :subject_id, :subject_class, :name
|
19
|
+
attr_accessor :name, :id, :cell
|
21
20
|
attr_accessor :status, :tasks
|
22
21
|
attr_accessor :backtrace
|
23
22
|
|
24
23
|
def dump
|
25
24
|
string = ""
|
26
|
-
string << "Celluloid::Actor 0x#{
|
25
|
+
string << "Celluloid::Actor 0x#{id.to_s(16)}"
|
27
26
|
string << " [#{name}]" if name
|
28
27
|
string << "\n"
|
29
28
|
|
29
|
+
if cell
|
30
|
+
string << cell.dump
|
31
|
+
string << "\n"
|
32
|
+
end
|
33
|
+
|
30
34
|
if status == :idle
|
31
35
|
string << "State: Idle (waiting for messages)\n"
|
32
36
|
display_backtrace backtrace, string
|
@@ -46,12 +50,18 @@ module Celluloid
|
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
49
|
-
class
|
53
|
+
class CellState < Struct.new(:subject_id, :subject_class)
|
54
|
+
def dump
|
55
|
+
"Celluloid::Cell 0x#{subject_id.to_s(16)}: #{subject_class}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class ThreadState < Struct.new(:thread_id, :backtrace, :role)
|
50
60
|
include DisplayBacktrace
|
51
61
|
|
52
62
|
def dump
|
53
63
|
string = ""
|
54
|
-
string << "Thread 0x#{thread_id.to_s(16)}:\n"
|
64
|
+
string << "Thread 0x#{thread_id.to_s(16)} (#{role}):\n"
|
55
65
|
display_backtrace backtrace, string
|
56
66
|
string
|
57
67
|
end
|
@@ -59,7 +69,9 @@ module Celluloid
|
|
59
69
|
|
60
70
|
attr_accessor :actors, :threads
|
61
71
|
|
62
|
-
def initialize
|
72
|
+
def initialize(internal_pool)
|
73
|
+
@internal_pool = internal_pool
|
74
|
+
|
63
75
|
@actors = []
|
64
76
|
@threads = []
|
65
77
|
|
@@ -67,7 +79,7 @@ module Celluloid
|
|
67
79
|
end
|
68
80
|
|
69
81
|
def snapshot
|
70
|
-
|
82
|
+
@internal_pool.each do |thread|
|
71
83
|
if thread.role == :actor
|
72
84
|
@actors << snapshot_actor(thread.actor) if thread.actor
|
73
85
|
else
|
@@ -78,8 +90,12 @@ module Celluloid
|
|
78
90
|
|
79
91
|
def snapshot_actor(actor)
|
80
92
|
state = ActorState.new
|
81
|
-
state.
|
82
|
-
|
93
|
+
state.id = actor.object_id
|
94
|
+
|
95
|
+
# TODO: delegate to the behavior
|
96
|
+
if actor.behavior.is_a?(Cell)
|
97
|
+
state.cell = snapshot_cell(actor.behavior)
|
98
|
+
end
|
83
99
|
|
84
100
|
tasks = actor.tasks
|
85
101
|
if tasks.empty?
|
@@ -93,11 +109,18 @@ module Celluloid
|
|
93
109
|
state
|
94
110
|
end
|
95
111
|
|
112
|
+
def snapshot_cell(behavior)
|
113
|
+
state = CellState.new
|
114
|
+
state.subject_id = behavior.subject.object_id
|
115
|
+
state.subject_class = behavior.subject.class
|
116
|
+
state
|
117
|
+
end
|
118
|
+
|
96
119
|
def snapshot_thread(thread)
|
97
|
-
ThreadState.new(thread.object_id, thread.backtrace)
|
120
|
+
ThreadState.new(thread.object_id, thread.backtrace, thread.role)
|
98
121
|
end
|
99
122
|
|
100
|
-
def
|
123
|
+
def print(output = STDERR)
|
101
124
|
@actors.each do |actor|
|
102
125
|
output.print actor.dump
|
103
126
|
end
|