celluloid 0.15.2 → 0.16.0.pre2
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 +25 -2
- data/lib/celluloid/actor.rb +86 -133
- data/lib/celluloid/actor_system.rb +107 -0
- data/lib/celluloid/call_chain.rb +1 -1
- 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 +27 -18
- data/lib/celluloid/evented_mailbox.rb +8 -15
- 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 +19 -2
- 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 +3 -1
- 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/task_fiber.rb +6 -0
- data/lib/celluloid/tasks/task_thread.rb +2 -1
- data/lib/celluloid/tasks.rb +6 -9
- data/lib/celluloid/thread_handle.rb +2 -2
- data/lib/celluloid.rb +68 -73
- 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 +82 -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 +34 -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 +46 -22
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
OWNER_IVAR = :@celluloid_owner # reference to owning actor
|
|
3
|
+
|
|
4
|
+
# Wrap the given subject with an Cell
|
|
5
|
+
class Cell
|
|
6
|
+
class ExitHandler
|
|
7
|
+
def initialize(behavior, subject, method_name)
|
|
8
|
+
@behavior = behavior
|
|
9
|
+
@subject = subject
|
|
10
|
+
@method_name = method_name
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(event)
|
|
14
|
+
@behavior.task(:exit_handler, @method_name) do
|
|
15
|
+
@subject.send(@method_name, event.actor, event.reason)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(subject, options, actor_options)
|
|
21
|
+
@actor = Actor.new(self, actor_options)
|
|
22
|
+
@subject = subject
|
|
23
|
+
@receiver_block_executions = options[:receiver_block_executions]
|
|
24
|
+
@exclusive_methods = options[:exclusive_methods]
|
|
25
|
+
@finalizer = options[:finalizer]
|
|
26
|
+
|
|
27
|
+
@subject.instance_variable_set(OWNER_IVAR, @actor)
|
|
28
|
+
|
|
29
|
+
if exit_handler_name = options[:exit_handler_name]
|
|
30
|
+
@actor.exit_handler = ExitHandler.new(self, @subject, exit_handler_name)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@actor.handle(Call) do |message|
|
|
34
|
+
invoke(message)
|
|
35
|
+
end
|
|
36
|
+
@actor.handle(BlockCall) do |message|
|
|
37
|
+
task(:invoke_block) { message.dispatch }
|
|
38
|
+
end
|
|
39
|
+
@actor.handle(BlockResponse, Response) do |message|
|
|
40
|
+
message.dispatch
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
@actor.start
|
|
44
|
+
@proxy = (options[:proxy_class] || CellProxy).new(@actor.proxy, @actor.mailbox, @subject.class.to_s)
|
|
45
|
+
end
|
|
46
|
+
attr_reader :proxy, :subject
|
|
47
|
+
|
|
48
|
+
def invoke(call)
|
|
49
|
+
meth = call.method
|
|
50
|
+
if meth == :__send__
|
|
51
|
+
meth = call.arguments.first
|
|
52
|
+
end
|
|
53
|
+
if @receiver_block_executions && meth
|
|
54
|
+
if @receiver_block_executions.include?(meth.to_sym)
|
|
55
|
+
call.execute_block_on_receiver
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
task(:call, meth, :dangerous_suspend => meth == :initialize) {
|
|
60
|
+
call.dispatch(@subject)
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def task(task_type, method_name = nil, meta = nil, &block)
|
|
65
|
+
meta ||= {}
|
|
66
|
+
meta.merge!(:method_name => method_name)
|
|
67
|
+
@actor.task(task_type, meta) do
|
|
68
|
+
if @exclusive_methods && method_name && @exclusive_methods.include?(method_name.to_sym)
|
|
69
|
+
Celluloid.exclusive { yield }
|
|
70
|
+
else
|
|
71
|
+
yield
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Run the user-defined finalizer, if one is set
|
|
77
|
+
def shutdown
|
|
78
|
+
return unless @finalizer && @subject.respond_to?(@finalizer, true)
|
|
79
|
+
|
|
80
|
+
task(:finalizer, @finalizer, :dangerous_suspend => true) do
|
|
81
|
+
begin
|
|
82
|
+
@subject.__send__(@finalizer)
|
|
83
|
+
rescue => ex
|
|
84
|
+
Logger.crash("#{@subject.class} finalizer crashed!", ex)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
data/lib/celluloid/condition.rb
CHANGED
|
@@ -4,10 +4,11 @@ module Celluloid
|
|
|
4
4
|
# ConditionVariable-like signaling between tasks and threads
|
|
5
5
|
class Condition
|
|
6
6
|
class Waiter
|
|
7
|
-
def initialize(condition, task, mailbox)
|
|
7
|
+
def initialize(condition, task, mailbox, timeout)
|
|
8
8
|
@condition = condition
|
|
9
9
|
@task = task
|
|
10
10
|
@mailbox = mailbox
|
|
11
|
+
@timeout = timeout
|
|
11
12
|
end
|
|
12
13
|
attr_reader :condition, :task
|
|
13
14
|
|
|
@@ -16,9 +17,14 @@ module Celluloid
|
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def wait
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
begin
|
|
21
|
+
message = @mailbox.receive(@timeout) do |msg|
|
|
22
|
+
msg.is_a?(SignalConditionRequest) && msg.task == Thread.current
|
|
23
|
+
end
|
|
24
|
+
rescue TimeoutError
|
|
25
|
+
raise ConditionError, "timeout after #{@timeout.inspect} seconds"
|
|
26
|
+
end until message
|
|
27
|
+
|
|
22
28
|
message.value
|
|
23
29
|
end
|
|
24
30
|
end
|
|
@@ -29,21 +35,30 @@ module Celluloid
|
|
|
29
35
|
end
|
|
30
36
|
|
|
31
37
|
# Wait for the given signal and return the associated value
|
|
32
|
-
def wait
|
|
38
|
+
def wait(timeout = nil)
|
|
33
39
|
raise ConditionError, "cannot wait for signals while exclusive" if Celluloid.exclusive?
|
|
34
40
|
|
|
35
|
-
if Thread.current[:celluloid_actor]
|
|
41
|
+
if actor = Thread.current[:celluloid_actor]
|
|
36
42
|
task = Task.current
|
|
43
|
+
if timeout
|
|
44
|
+
bt = caller
|
|
45
|
+
timer = actor.timers.after(timeout) do
|
|
46
|
+
exception = ConditionError.new("timeout after #{timeout.inspect} seconds")
|
|
47
|
+
exception.set_backtrace bt
|
|
48
|
+
task.resume exception
|
|
49
|
+
end
|
|
50
|
+
end
|
|
37
51
|
else
|
|
38
52
|
task = Thread.current
|
|
39
53
|
end
|
|
40
|
-
waiter = Waiter.new(self, task, Celluloid.mailbox)
|
|
54
|
+
waiter = Waiter.new(self, task, Celluloid.mailbox, timeout)
|
|
41
55
|
|
|
42
56
|
@mutex.synchronize do
|
|
43
57
|
@waiters << waiter
|
|
44
58
|
end
|
|
45
59
|
|
|
46
60
|
result = Celluloid.suspend :condwait, waiter
|
|
61
|
+
timer.cancel if timer
|
|
47
62
|
raise result if result.is_a? ConditionError
|
|
48
63
|
result
|
|
49
64
|
end
|
|
@@ -54,7 +69,9 @@ module Celluloid
|
|
|
54
69
|
if waiter = @waiters.shift
|
|
55
70
|
waiter << SignalConditionRequest.new(waiter.task, value)
|
|
56
71
|
else
|
|
57
|
-
Logger.
|
|
72
|
+
Logger.with_backtrace(caller(3)) do |logger|
|
|
73
|
+
logger.debug("Celluloid::Condition signaled spuriously")
|
|
74
|
+
end
|
|
58
75
|
end
|
|
59
76
|
end
|
|
60
77
|
end
|
|
@@ -1,24 +1,33 @@
|
|
|
1
|
-
require 'rbconfig'
|
|
2
|
-
|
|
3
1
|
module Celluloid
|
|
4
2
|
module CPUCounter
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
when 'linux'
|
|
9
|
-
@cores = if File.exists?("/sys/devices/system/cpu/present")
|
|
10
|
-
File.read("/sys/devices/system/cpu/present").split('-').last.to_i+1
|
|
11
|
-
else
|
|
12
|
-
Dir["/sys/devices/system/cpu/cpu*"].select { |n| n=~/cpu\d+/ }.count
|
|
3
|
+
class << self
|
|
4
|
+
def cores
|
|
5
|
+
@cores ||= count_cores
|
|
13
6
|
end
|
|
14
|
-
when 'mingw', 'mswin'
|
|
15
|
-
@cores = Integer(ENV["NUMBER_OF_PROCESSORS"][/\d+/])
|
|
16
|
-
else
|
|
17
|
-
@cores = nil
|
|
18
|
-
end
|
|
19
7
|
|
|
20
|
-
|
|
21
|
-
end
|
|
22
|
-
end
|
|
8
|
+
private
|
|
23
9
|
|
|
10
|
+
def count_cores
|
|
11
|
+
result = from_env || from_sysdev || from_sysctl
|
|
12
|
+
Integer(result.to_s[/\d+/], 10) if result
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def from_env
|
|
16
|
+
result = ENV['NUMBER_OF_PROCESSORS']
|
|
17
|
+
result if result
|
|
18
|
+
end
|
|
24
19
|
|
|
20
|
+
def from_sysdev
|
|
21
|
+
::IO.read('/sys/devices/system/cpu/present').split('-').last.to_i + 1
|
|
22
|
+
rescue Errno::ENOENT
|
|
23
|
+
result = Dir['/sys/devices/system/cpu/cpu*'].count { |n| n =~ /cpu\d+/ }
|
|
24
|
+
result unless result.zero?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def from_sysctl
|
|
28
|
+
result = `sysctl -n hw.ncpu`
|
|
29
|
+
result if $?.success?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -38,23 +38,16 @@ module Celluloid
|
|
|
38
38
|
|
|
39
39
|
# Receive a message from the Mailbox
|
|
40
40
|
def receive(timeout = nil, &block)
|
|
41
|
-
message
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if timeout
|
|
45
|
-
now = Time.now
|
|
46
|
-
wait_until ||= now + timeout
|
|
47
|
-
wait_interval = wait_until - now
|
|
48
|
-
return if wait_interval < 0
|
|
49
|
-
else
|
|
50
|
-
wait_interval = nil
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
@reactor.run_once(wait_interval)
|
|
54
|
-
message = next_message(block)
|
|
41
|
+
# Get a message if it is available and process it immediately if possible:
|
|
42
|
+
if message = next_message(block)
|
|
43
|
+
return message
|
|
55
44
|
end
|
|
56
45
|
|
|
57
|
-
|
|
46
|
+
# ... otherwise, run the reactor once, either blocking or will return after the given timeout.
|
|
47
|
+
@reactor.run_once(timeout)
|
|
48
|
+
|
|
49
|
+
# This is a hack to get the main Actor#run loop to recompute the timeout:
|
|
50
|
+
raise TimeoutError
|
|
58
51
|
rescue IOError
|
|
59
52
|
raise MailboxShutdown, "mailbox shutdown called during receive"
|
|
60
53
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
# Base class of all Celluloid errors
|
|
3
|
+
Error = Class.new(StandardError)
|
|
4
|
+
|
|
5
|
+
# Don't do Actor-like things outside Actor scope
|
|
6
|
+
NotActorError = Class.new(Celluloid::Error)
|
|
7
|
+
|
|
8
|
+
# Trying to do something to a dead actor
|
|
9
|
+
DeadActorError = Class.new(Celluloid::Error)
|
|
10
|
+
|
|
11
|
+
# A timeout occured before the given request could complete
|
|
12
|
+
TimeoutError = Class.new(Celluloid::Error)
|
|
13
|
+
|
|
14
|
+
# The sender made an error, not the current actor
|
|
15
|
+
class AbortError < Celluloid::Error
|
|
16
|
+
attr_reader :cause
|
|
17
|
+
|
|
18
|
+
def initialize(cause)
|
|
19
|
+
@cause = cause
|
|
20
|
+
super "caused by #{cause.inspect}: #{cause.to_s}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/celluloid/future.rb
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
module Celluloid
|
|
4
|
+
class Handlers
|
|
5
|
+
def initialize
|
|
6
|
+
@handlers = Set.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def handle(*patterns, &block)
|
|
10
|
+
patterns.each do |pattern|
|
|
11
|
+
handler = Handler.new pattern, block
|
|
12
|
+
@handlers << handler
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Handle incoming messages
|
|
17
|
+
def handle_message(message)
|
|
18
|
+
if handler = @handlers.find { |h| h.match(message) }
|
|
19
|
+
handler.call message
|
|
20
|
+
handler
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Methods blocking on a call to receive
|
|
26
|
+
class Handler
|
|
27
|
+
def initialize(pattern, block)
|
|
28
|
+
@pattern = pattern
|
|
29
|
+
@block = block
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Match a message with this receiver's block
|
|
33
|
+
def match(message)
|
|
34
|
+
@pattern === message
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def call(message)
|
|
38
|
+
@block.call message
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -6,7 +6,6 @@ module Celluloid
|
|
|
6
6
|
attr_accessor :max_idle
|
|
7
7
|
|
|
8
8
|
def initialize
|
|
9
|
-
@group = ThreadGroup.new
|
|
10
9
|
@mutex = Mutex.new
|
|
11
10
|
@threads = []
|
|
12
11
|
|
|
@@ -108,7 +107,6 @@ module Celluloid
|
|
|
108
107
|
|
|
109
108
|
thread[:celluloid_queue] = queue
|
|
110
109
|
@threads << thread
|
|
111
|
-
@group.add(thread)
|
|
112
110
|
thread
|
|
113
111
|
end
|
|
114
112
|
|
|
@@ -137,7 +135,6 @@ module Celluloid
|
|
|
137
135
|
@running = false
|
|
138
136
|
|
|
139
137
|
@threads.shift.kill until @threads.empty?
|
|
140
|
-
@group.list.each(&:kill)
|
|
141
138
|
end
|
|
142
139
|
end
|
|
143
140
|
|
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
|
|
@@ -24,7 +24,7 @@ module Celluloid
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def __shutdown__
|
|
27
|
-
terminators = (@idle + @busy).
|
|
27
|
+
terminators = (@idle + @busy).map do |actor|
|
|
28
28
|
begin
|
|
29
29
|
actor.future(:terminate)
|
|
30
30
|
rescue DeadActorError
|
|
@@ -81,6 +81,23 @@ module Celluloid
|
|
|
81
81
|
@size
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
+
def size=(new_size)
|
|
85
|
+
new_size = [0, new_size].max
|
|
86
|
+
|
|
87
|
+
if new_size > size
|
|
88
|
+
delta = new_size - size
|
|
89
|
+
delta.times { @idle << @worker_class.new_link(*@args) }
|
|
90
|
+
else
|
|
91
|
+
(size - new_size).times do
|
|
92
|
+
worker = __provision_worker__
|
|
93
|
+
unlink worker
|
|
94
|
+
@busy.delete worker
|
|
95
|
+
worker.terminate
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
@size = new_size
|
|
99
|
+
end
|
|
100
|
+
|
|
84
101
|
def busy_size
|
|
85
102
|
@busy.length
|
|
86
103
|
end
|
|
@@ -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!
|