celluloid 0.10.0 → 0.11.0
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.
- data/README.md +17 -7
- data/lib/celluloid.rb +50 -6
- data/lib/celluloid/actor.rb +20 -6
- data/lib/celluloid/actor_proxy.rb +29 -6
- data/lib/celluloid/calls.rb +2 -2
- data/lib/celluloid/core_ext.rb +3 -3
- data/lib/celluloid/events.rb +13 -4
- data/lib/celluloid/fiber.rb +2 -3
- data/lib/celluloid/future.rb +8 -4
- data/lib/celluloid/{thread_pool.rb → internal_pool.rb} +8 -6
- data/lib/celluloid/links.rb +5 -23
- data/lib/celluloid/pool_manager.rb +62 -0
- data/lib/celluloid/registry.rb +2 -0
- data/lib/celluloid/supervision_group.rb +99 -0
- data/lib/celluloid/supervisor.rb +14 -5
- data/lib/celluloid/task.rb +1 -4
- data/lib/celluloid/thread_handle.rb +38 -0
- data/lib/celluloid/version.rb +1 -1
- data/spec/support/actor_examples.rb +70 -19
- metadata +16 -14
- data/lib/celluloid/group.rb +0 -85
- data/lib/celluloid/pool.rb +0 -105
data/README.md
CHANGED
@@ -72,18 +72,28 @@ for more detailed documentation and usage notes.
|
|
72
72
|
Like Celluloid? [Join the Google Group](http://groups.google.com/group/celluloid-ruby)
|
73
73
|
or visit us on IRC at #celluloid on freenode
|
74
74
|
|
75
|
+
### Is it any good?
|
76
|
+
|
77
|
+
[Yes.](http://news.ycombinator.com/item?id=3067434)
|
78
|
+
|
79
|
+
### Is It "Production Ready™"?
|
80
|
+
|
81
|
+
Yes, many users are now running Celluloid in production by using
|
82
|
+
[Sidekiq](https://github.com/mperham/sidekiq)
|
83
|
+
|
75
84
|
Supported Platforms
|
76
85
|
-------------------
|
77
86
|
|
78
|
-
Celluloid works on Ruby 1.9.3, JRuby 1.6
|
79
|
-
|
80
|
-
|
81
|
-
|
87
|
+
Celluloid works on Ruby 1.9.3, JRuby 1.6, and Rubinius 2.0. JRuby or Rubinius
|
88
|
+
are the preferred platforms as they support true thread-level parallelism when
|
89
|
+
executing Ruby code, whereas MRI/YARV is constrained by a global interpreter
|
90
|
+
lock (GIL) and can only execute one thread at a time.
|
82
91
|
|
83
|
-
|
84
|
-
|
92
|
+
Celluloid requires Ruby 1.9 mode on all interpreters. This works out of the
|
93
|
+
box on MRI/YARV, and requires the following flags elsewhere:
|
85
94
|
|
86
|
-
|
95
|
+
* JRuby: --1.9 command line option, or JRUBY_OPTS=--1.9 environment variable
|
96
|
+
* rbx: -X19 command line option
|
87
97
|
|
88
98
|
Additional Reading
|
89
99
|
------------------
|
data/lib/celluloid.rb
CHANGED
@@ -73,8 +73,11 @@ module Celluloid
|
|
73
73
|
actors = Actor.all
|
74
74
|
Logger.info "Terminating #{actors.size} actors..." if actors.size > 0
|
75
75
|
|
76
|
+
# Attempt to shut down the supervision tree, if available
|
77
|
+
Supervisor.root.terminate if Supervisor.root
|
78
|
+
|
76
79
|
# Actors cannot self-terminate, you must do it for them
|
77
|
-
terminators =
|
80
|
+
terminators = Actor.all.each do |actor|
|
78
81
|
begin
|
79
82
|
actor.future(:terminate)
|
80
83
|
rescue DeadActorError, MailboxError
|
@@ -130,6 +133,25 @@ module Celluloid
|
|
130
133
|
Supervisor.supervise_as(name, self, *args, &block)
|
131
134
|
end
|
132
135
|
|
136
|
+
# Create a new pool of workers. Accepts the following options:
|
137
|
+
#
|
138
|
+
# * size: how many workers to create. Default is worker per CPU core
|
139
|
+
# * args: array of arguments to pass when creating a worker
|
140
|
+
#
|
141
|
+
def pool(options = {})
|
142
|
+
PoolManager.new(self, options)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Same as pool, but links to the pool manager
|
146
|
+
def pool_link(options = {})
|
147
|
+
PoolManager.new_link(self, options)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Run an actor in the foreground
|
151
|
+
def run(*args, &block)
|
152
|
+
new(*args, &block).join
|
153
|
+
end
|
154
|
+
|
133
155
|
# Trap errors from actors we're linked to when they exit
|
134
156
|
def trap_exit(callback)
|
135
157
|
@exit_handler = callback.to_sym
|
@@ -174,6 +196,11 @@ module Celluloid
|
|
174
196
|
|
175
197
|
# Raise an exception in caller context, but stay running
|
176
198
|
def abort(cause)
|
199
|
+
cause = case cause
|
200
|
+
when String then RuntimeError.new(cause)
|
201
|
+
when Exception then cause
|
202
|
+
else raise TypeError, "Exception object/String expected, but #{cause.class} received"
|
203
|
+
end
|
177
204
|
raise AbortError.new(cause)
|
178
205
|
end
|
179
206
|
|
@@ -207,6 +234,11 @@ module Celluloid
|
|
207
234
|
Actor.current
|
208
235
|
end
|
209
236
|
|
237
|
+
# Obtain the name of the current actor
|
238
|
+
def name
|
239
|
+
Actor.name
|
240
|
+
end
|
241
|
+
|
210
242
|
# Obtain the running tasks for this actor
|
211
243
|
def tasks
|
212
244
|
Thread.current[:actor].tasks.to_a
|
@@ -280,17 +312,28 @@ module Celluloid
|
|
280
312
|
# messages in its mailbox in the meantime
|
281
313
|
def defer(&block)
|
282
314
|
# This implementation relies on the present implementation of
|
283
|
-
# Celluloid::Future, which uses
|
315
|
+
# Celluloid::Future, which uses a thread from InternalPool to run the block
|
284
316
|
Future.new(&block).value
|
285
317
|
end
|
286
318
|
|
319
|
+
# Handle async calls within an actor itself
|
320
|
+
def async(meth, *args, &block)
|
321
|
+
Actor.async Thread.current[:actor].mailbox, meth, *args, &block
|
322
|
+
end
|
323
|
+
|
324
|
+
# Handle calls to future within an actor itself
|
325
|
+
def future(meth, *args, &block)
|
326
|
+
Actor.future Thread.current[:actor].mailbox, meth, *args, &block
|
327
|
+
end
|
328
|
+
|
287
329
|
# Process async calls via method_missing
|
288
330
|
def method_missing(meth, *args, &block)
|
289
331
|
# bang methods are async calls
|
290
332
|
if meth.to_s.match(/!$/)
|
291
333
|
unbanged_meth = meth.to_s.sub(/!$/, '')
|
292
|
-
|
334
|
+
args.unshift unbanged_meth
|
293
335
|
|
336
|
+
call = AsyncCall.new(nil, :__send__, args, block)
|
294
337
|
begin
|
295
338
|
Thread.current[:actor].mailbox << call
|
296
339
|
rescue MailboxError
|
@@ -315,20 +358,21 @@ require 'celluloid/cpu_counter'
|
|
315
358
|
require 'celluloid/events'
|
316
359
|
require 'celluloid/fiber'
|
317
360
|
require 'celluloid/fsm'
|
361
|
+
require 'celluloid/internal_pool'
|
318
362
|
require 'celluloid/links'
|
319
363
|
require 'celluloid/logger'
|
320
364
|
require 'celluloid/mailbox'
|
321
|
-
require 'celluloid/pool'
|
322
365
|
require 'celluloid/receivers'
|
323
366
|
require 'celluloid/registry'
|
324
367
|
require 'celluloid/responses'
|
325
368
|
require 'celluloid/signals'
|
326
369
|
require 'celluloid/task'
|
370
|
+
require 'celluloid/thread_handle'
|
327
371
|
require 'celluloid/timers'
|
328
|
-
require 'celluloid/thread_pool'
|
329
372
|
require 'celluloid/uuid'
|
330
373
|
|
331
374
|
require 'celluloid/actor'
|
332
375
|
require 'celluloid/future'
|
333
|
-
require 'celluloid/
|
376
|
+
require 'celluloid/pool_manager'
|
377
|
+
require 'celluloid/supervision_group'
|
334
378
|
require 'celluloid/supervisor'
|
data/lib/celluloid/actor.rb
CHANGED
@@ -20,7 +20,7 @@ module Celluloid
|
|
20
20
|
# messages.
|
21
21
|
class Actor
|
22
22
|
extend Registry
|
23
|
-
attr_reader :proxy, :tasks, :links, :mailbox
|
23
|
+
attr_reader :subject, :proxy, :tasks, :links, :mailbox, :thread, :name
|
24
24
|
|
25
25
|
class << self
|
26
26
|
# Obtain the current actor
|
@@ -29,7 +29,14 @@ module Celluloid
|
|
29
29
|
raise NotActorError, "not in actor scope" unless actor
|
30
30
|
actor.proxy
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
|
+
# Obtain the name of the current actor
|
34
|
+
def name
|
35
|
+
actor = Thread.current[:actor]
|
36
|
+
raise NotActorError, "not in actor scope" unless actor
|
37
|
+
actor.name
|
38
|
+
end
|
39
|
+
|
33
40
|
# Invoke a method on the given actor via its mailbox
|
34
41
|
def call(mailbox, meth, *args, &block)
|
35
42
|
call = SyncCall.new(Thread.mailbox, meth, args, block)
|
@@ -87,7 +94,6 @@ module Celluloid
|
|
87
94
|
def initialize(subject)
|
88
95
|
@subject = subject
|
89
96
|
@mailbox = subject.class.mailbox_factory
|
90
|
-
@proxy = ActorProxy.new(@mailbox, subject.class.to_s)
|
91
97
|
@tasks = Set.new
|
92
98
|
@links = Links.new
|
93
99
|
@signals = Signals.new
|
@@ -95,12 +101,15 @@ module Celluloid
|
|
95
101
|
@timers = Timers.new
|
96
102
|
@running = true
|
97
103
|
@exclusive = false
|
104
|
+
@name = nil
|
98
105
|
|
99
|
-
@thread =
|
106
|
+
@thread = ThreadHandle.new do
|
100
107
|
Thread.current[:actor] = self
|
101
108
|
Thread.current[:mailbox] = @mailbox
|
102
109
|
run
|
103
110
|
end
|
111
|
+
|
112
|
+
@proxy = ActorProxy.new(self)
|
104
113
|
end
|
105
114
|
|
106
115
|
# Is this actor running in exclusive mode?
|
@@ -145,6 +154,9 @@ module Celluloid
|
|
145
154
|
rescue ExitEvent => exit_event
|
146
155
|
Task.new(:exit_handler) { handle_exit_event exit_event }.resume
|
147
156
|
retry
|
157
|
+
rescue NamingRequest => ex
|
158
|
+
@name = ex.name
|
159
|
+
retry
|
148
160
|
rescue TerminationRequest
|
149
161
|
break
|
150
162
|
end
|
@@ -162,8 +174,9 @@ module Celluloid
|
|
162
174
|
end
|
163
175
|
|
164
176
|
shutdown
|
165
|
-
rescue => ex
|
177
|
+
rescue Exception => ex
|
166
178
|
handle_crash(ex)
|
179
|
+
raise unless ex.is_a? StandardError
|
167
180
|
end
|
168
181
|
|
169
182
|
# How long to wait until the next timer fires
|
@@ -249,7 +262,8 @@ module Celluloid
|
|
249
262
|
|
250
263
|
# Run the user-defined finalizer, if one is set
|
251
264
|
def run_finalizer
|
252
|
-
|
265
|
+
return unless @subject.respond_to? :finalize
|
266
|
+
Task.new(:finalizer) { @subject.finalize }.resume
|
253
267
|
rescue => ex
|
254
268
|
Logger.crash("#{@subject.class}#finalize crashed!", ex)
|
255
269
|
end
|
@@ -5,8 +5,8 @@ module Celluloid
|
|
5
5
|
class ActorProxy
|
6
6
|
attr_reader :mailbox
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@mailbox, @klass = mailbox,
|
8
|
+
def initialize(actor)
|
9
|
+
@mailbox, @thread, @klass = actor.mailbox, actor.thread, actor.subject.class.to_s
|
10
10
|
end
|
11
11
|
|
12
12
|
def _send_(meth, *args, &block)
|
@@ -17,6 +17,10 @@ module Celluloid
|
|
17
17
|
Actor.call @mailbox, :__send__, :class
|
18
18
|
end
|
19
19
|
|
20
|
+
def name
|
21
|
+
Actor.call @mailbox, :name
|
22
|
+
end
|
23
|
+
|
20
24
|
def is_a?(klass)
|
21
25
|
Actor.call @mailbox, :is_a?, klass
|
22
26
|
end
|
@@ -47,6 +51,12 @@ module Celluloid
|
|
47
51
|
"#<Celluloid::Actor(#{@klass}) dead>"
|
48
52
|
end
|
49
53
|
|
54
|
+
# Make an asynchronous call to an actor, for those who don't like the
|
55
|
+
# predicate syntax. TIMTOWTDI!
|
56
|
+
def async(method_name, *args, &block)
|
57
|
+
Actor.async @mailbox, method_name, *args, &block
|
58
|
+
end
|
59
|
+
|
50
60
|
# Create a Celluloid::Future which calls a given method
|
51
61
|
def future(method_name, *args, &block)
|
52
62
|
Actor.future @mailbox, method_name, *args, &block
|
@@ -64,14 +74,27 @@ module Celluloid
|
|
64
74
|
@mailbox.system_event TerminationRequest.new
|
65
75
|
end
|
66
76
|
|
77
|
+
# Forcibly kill a given actor
|
78
|
+
def kill
|
79
|
+
@thread.kill
|
80
|
+
begin
|
81
|
+
@mailbox.shutdown
|
82
|
+
rescue DeadActorError
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Wait for an actor to terminate
|
87
|
+
def join
|
88
|
+
@thread.join
|
89
|
+
end
|
90
|
+
|
67
91
|
# method_missing black magic to call bang predicate methods asynchronously
|
68
92
|
def method_missing(meth, *args, &block)
|
69
|
-
meth = meth.to_s
|
70
|
-
|
71
93
|
# bang methods are async calls
|
72
94
|
if meth.match(/!$/)
|
73
|
-
meth.
|
74
|
-
|
95
|
+
unbanged_meth = meth.to_s
|
96
|
+
unbanged_meth.slice!(-1, 1)
|
97
|
+
Actor.async @mailbox, unbanged_meth, *args, &block
|
75
98
|
else
|
76
99
|
Actor.call @mailbox, meth, *args, &block
|
77
100
|
end
|
data/lib/celluloid/calls.rb
CHANGED
@@ -91,14 +91,14 @@ module Celluloid
|
|
91
91
|
begin
|
92
92
|
check_signature(obj)
|
93
93
|
rescue Exception => ex
|
94
|
-
Logger.crash("#{obj.class}: async call failed!", ex)
|
94
|
+
Logger.crash("#{obj.class}: async call `#{@method}' failed!", ex)
|
95
95
|
return
|
96
96
|
end
|
97
97
|
|
98
98
|
obj.send(@method, *@arguments, &@block)
|
99
99
|
rescue AbortError => ex
|
100
100
|
# Swallow aborted async calls, as they indicate the caller made a mistake
|
101
|
-
Logger.crash("#{obj.class}: async call aborted!", ex)
|
101
|
+
Logger.crash("#{obj.class}: async call `#{@method}' aborted!", ex)
|
102
102
|
end
|
103
103
|
end
|
104
104
|
end
|
data/lib/celluloid/core_ext.rb
CHANGED
@@ -10,11 +10,11 @@ class Thread
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# Receive a message either as an actor or through the local mailbox
|
13
|
-
def self.receive(&block)
|
13
|
+
def self.receive(timeout = nil, &block)
|
14
14
|
if Celluloid.actor?
|
15
|
-
Celluloid.receive(&block)
|
15
|
+
Celluloid.receive(timeout, &block)
|
16
16
|
else
|
17
|
-
mailbox.receive(&block)
|
17
|
+
mailbox.receive(timeout, &block)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
data/lib/celluloid/events.rb
CHANGED
@@ -1,17 +1,26 @@
|
|
1
1
|
module Celluloid
|
2
2
|
# Exceptional system events which need to be processed out of band
|
3
3
|
class SystemEvent < Exception; end
|
4
|
-
|
4
|
+
|
5
5
|
# An actor has exited for the given reason
|
6
6
|
class ExitEvent < SystemEvent
|
7
7
|
attr_reader :actor, :reason
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(actor, reason = nil)
|
10
10
|
@actor, @reason = actor, reason
|
11
11
|
super reason.to_s
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
|
+
# Name an actor at the time it's registered
|
16
|
+
class NamingRequest < SystemEvent
|
17
|
+
attr_reader :name
|
18
|
+
|
19
|
+
def initialize(name)
|
20
|
+
@name = name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
15
24
|
# Request for an actor to terminate
|
16
25
|
class TerminationRequest < SystemEvent; end
|
17
|
-
end
|
26
|
+
end
|
data/lib/celluloid/fiber.rb
CHANGED
@@ -24,9 +24,8 @@ rescue LoadError => ex
|
|
24
24
|
# Just in case subsequent JRuby releases have broken fibers :/
|
25
25
|
raise ex
|
26
26
|
end
|
27
|
-
elsif defined?
|
28
|
-
|
29
|
-
Fiber = Rubinius::Fiber
|
27
|
+
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
|
28
|
+
raise LoadError, "Celluloid requires Rubinius 1.9 mode. Please pass the -X19 flag."
|
30
29
|
else
|
31
30
|
raise ex
|
32
31
|
end
|
data/lib/celluloid/future.rb
CHANGED
@@ -13,7 +13,7 @@ module Celluloid
|
|
13
13
|
|
14
14
|
if block
|
15
15
|
@call = SyncCall.new(self, :call, args)
|
16
|
-
|
16
|
+
InternalPool.get do
|
17
17
|
begin
|
18
18
|
@call.dispatch(block)
|
19
19
|
rescue
|
@@ -36,7 +36,7 @@ module Celluloid
|
|
36
36
|
end
|
37
37
|
|
38
38
|
# Obtain the value for this Future
|
39
|
-
def value
|
39
|
+
def value(timeout = nil)
|
40
40
|
ready = result = nil
|
41
41
|
|
42
42
|
begin
|
@@ -61,12 +61,16 @@ module Celluloid
|
|
61
61
|
end
|
62
62
|
|
63
63
|
unless ready
|
64
|
-
result = Thread.receive do |msg|
|
64
|
+
result = Thread.receive(timeout) do |msg|
|
65
65
|
msg.is_a?(Future::Result) && msg.future == self
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
result
|
69
|
+
if result
|
70
|
+
result.value
|
71
|
+
else
|
72
|
+
raise "Timed out"
|
73
|
+
end
|
70
74
|
end
|
71
75
|
alias_method :call, :value
|
72
76
|
|
@@ -2,7 +2,7 @@ require 'thread'
|
|
2
2
|
|
3
3
|
module Celluloid
|
4
4
|
# Maintain a thread pool FOR SPEED!!
|
5
|
-
module
|
5
|
+
module InternalPool
|
6
6
|
@pool = []
|
7
7
|
@mutex = Mutex.new
|
8
8
|
|
@@ -15,11 +15,13 @@ module Celluloid
|
|
15
15
|
# Get a thread from the pool, running the given block
|
16
16
|
def get(&block)
|
17
17
|
@mutex.synchronize do
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
begin
|
19
|
+
if @pool.empty?
|
20
|
+
thread = create
|
21
|
+
else
|
22
|
+
thread = @pool.shift
|
23
|
+
end
|
24
|
+
end until thread.status # handle crashed threads
|
23
25
|
|
24
26
|
thread[:queue] << block
|
25
27
|
thread
|
data/lib/celluloid/links.rb
CHANGED
@@ -1,50 +1,32 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
3
|
module Celluloid
|
4
|
-
#
|
4
|
+
# Linked actors send each other system events
|
5
5
|
class Links
|
6
6
|
include Enumerable
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
@links = {}
|
10
|
-
@lock = Mutex.new
|
11
10
|
end
|
12
11
|
|
13
12
|
# Add an actor to the current links
|
14
13
|
def <<(actor)
|
15
|
-
@
|
16
|
-
@links[actor.mailbox.address] = actor
|
17
|
-
end
|
18
|
-
actor
|
14
|
+
@links[actor.mailbox.address] = actor
|
19
15
|
end
|
20
16
|
|
21
17
|
# Do links include the given actor?
|
22
18
|
def include?(actor)
|
23
|
-
@
|
24
|
-
@links.has_key? actor.mailbox.address
|
25
|
-
end
|
19
|
+
@links.has_key? actor.mailbox.address
|
26
20
|
end
|
27
21
|
|
28
22
|
# Remove an actor from the links
|
29
23
|
def delete(actor)
|
30
|
-
@
|
31
|
-
@links.delete actor.mailbox.address
|
32
|
-
end
|
33
|
-
actor
|
24
|
+
@links.delete actor.mailbox.address
|
34
25
|
end
|
35
26
|
|
36
27
|
# Iterate through all links
|
37
28
|
def each
|
38
|
-
@
|
39
|
-
@links.each { |_, actor| yield(actor) }
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Map across links
|
44
|
-
def map
|
45
|
-
result = []
|
46
|
-
each { |actor| result << yield(actor) }
|
47
|
-
result
|
29
|
+
@links.each { |_, actor| yield(actor) }
|
48
30
|
end
|
49
31
|
|
50
32
|
# Send an event message to all actors
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Celluloid
|
2
|
+
# Manages a fixed-size pool of workers
|
3
|
+
# Delegates work (i.e. methods) and supervises workers
|
4
|
+
class PoolManager
|
5
|
+
include Celluloid
|
6
|
+
trap_exit :crash_handler
|
7
|
+
|
8
|
+
def initialize(worker_class, options = {})
|
9
|
+
@size = options[:size]
|
10
|
+
raise ArgumentError, "minimum pool size is 2" if @size && @size < 2
|
11
|
+
|
12
|
+
@size ||= [Celluloid.cores, 2].max
|
13
|
+
@args = options[:args] ? Array(options[:args]) : []
|
14
|
+
|
15
|
+
@worker_class = worker_class
|
16
|
+
@idle = @size.times.map { worker_class.new_link(*@args) }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Execute the given method within a worker
|
20
|
+
def execute(method, *args, &block)
|
21
|
+
worker = provision_worker
|
22
|
+
|
23
|
+
begin
|
24
|
+
worker._send_ method, *args, &block
|
25
|
+
rescue => ex
|
26
|
+
abort ex
|
27
|
+
ensure
|
28
|
+
@idle << worker if worker.alive?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Provision a new worker
|
33
|
+
def provision_worker
|
34
|
+
while @idle.empty?
|
35
|
+
# Using exclusive mode blocks incoming messages, so they don't pile
|
36
|
+
# up as waiting Celluloid::Tasks
|
37
|
+
response = exclusive { receive { |msg| msg.is_a? Response } }
|
38
|
+
Thread.current[:actor].handle_message(response)
|
39
|
+
end
|
40
|
+
@idle.shift
|
41
|
+
end
|
42
|
+
|
43
|
+
# Spawn a new worker for every crashed one
|
44
|
+
def crash_handler(actor, reason)
|
45
|
+
@idle.delete actor
|
46
|
+
return unless reason # don't restart workers that exit cleanly
|
47
|
+
@idle << @worker_class.new_link(*@args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def respond_to?(method)
|
51
|
+
super || (@worker_class ? @worker_class.instance_methods.include?(method.to_sym) : false)
|
52
|
+
end
|
53
|
+
|
54
|
+
def method_missing(method, *args, &block)
|
55
|
+
if respond_to?(method)
|
56
|
+
execute method, *args, &block
|
57
|
+
else
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/celluloid/registry.rb
CHANGED
@@ -0,0 +1,99 @@
|
|
1
|
+
module Celluloid
|
2
|
+
# Supervise collections of actors as a group
|
3
|
+
class SupervisionGroup
|
4
|
+
include Celluloid
|
5
|
+
trap_exit :restart_actor
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# Actors or sub-applications to be supervised
|
9
|
+
def members
|
10
|
+
@members ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
# Start this application (and watch it with a supervisor)
|
14
|
+
alias_method :run!, :supervise
|
15
|
+
|
16
|
+
# Run the application in the foreground with a simple watchdog
|
17
|
+
def run
|
18
|
+
loop do
|
19
|
+
supervisor = run!
|
20
|
+
|
21
|
+
# Take five, toplevel supervisor
|
22
|
+
sleep 5 while supervisor.alive?
|
23
|
+
|
24
|
+
Logger.error "!!! Celluloid::Group #{self} crashed. Restarting..."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Register an actor class or a sub-group to be launched and supervised
|
29
|
+
# Available options are:
|
30
|
+
#
|
31
|
+
# * as: register this application in the Celluloid::Actor[] directory
|
32
|
+
# * args: start the actor with the given arguments
|
33
|
+
def supervise(klass, options = {})
|
34
|
+
members << Member.new(klass, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Register a pool of actors to be launched on group startup
|
38
|
+
def pool(klass)
|
39
|
+
members << Member.new(klass, method: 'pool_link')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Start the group
|
44
|
+
def initialize
|
45
|
+
@actors = {}
|
46
|
+
|
47
|
+
# This is some serious lolcode, but like... start the supervisors for
|
48
|
+
# this group
|
49
|
+
self.class.members.each do |member|
|
50
|
+
actor = member.start
|
51
|
+
@actors[actor] = member
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Terminate the group
|
56
|
+
def finalize
|
57
|
+
@actors.each do |actor, _|
|
58
|
+
begin
|
59
|
+
actor.terminate
|
60
|
+
rescue DeadActorError
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Restart a crashed actor
|
66
|
+
def restart_actor(actor, reason)
|
67
|
+
member = @actors.delete actor
|
68
|
+
raise "a group member went missing. This shouldn't be!" unless member
|
69
|
+
|
70
|
+
# Ignore supervisors that shut down cleanly
|
71
|
+
return unless reason
|
72
|
+
|
73
|
+
actor = member.start
|
74
|
+
@actors[actor] = member
|
75
|
+
end
|
76
|
+
|
77
|
+
# A member of the group
|
78
|
+
class Member
|
79
|
+
def initialize(klass, options = {})
|
80
|
+
@klass = klass
|
81
|
+
|
82
|
+
# Stringify keys :/
|
83
|
+
options = options.inject({}) { |h,(k,v)| h[k.to_s] = v; h }
|
84
|
+
|
85
|
+
@name = options['as']
|
86
|
+
@args = options['args'] ? Array(options['args']) : []
|
87
|
+
@method = options['method'] || 'new_link'
|
88
|
+
end
|
89
|
+
|
90
|
+
def start
|
91
|
+
actor = @klass.send(@method, *@args)
|
92
|
+
Actor[@name] = actor if @name
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Legacy support for the old name
|
98
|
+
Group = SupervisionGroup
|
99
|
+
end
|
data/lib/celluloid/supervisor.rb
CHANGED
@@ -8,12 +8,17 @@ module Celluloid
|
|
8
8
|
# Retrieve the actor this supervisor is supervising
|
9
9
|
attr_reader :actor
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
class << self
|
12
|
+
# Define the root of the supervision tree
|
13
|
+
attr_accessor :root
|
14
|
+
|
15
|
+
def supervise(klass, *args, &block)
|
16
|
+
new(nil, klass, *args, &block)
|
17
|
+
end
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
def supervise_as(name, klass, *args, &block)
|
20
|
+
new(name, klass, *args, &block)
|
21
|
+
end
|
17
22
|
end
|
18
23
|
|
19
24
|
def initialize(name, klass, *args, &block)
|
@@ -23,6 +28,10 @@ module Celluloid
|
|
23
28
|
start_actor
|
24
29
|
end
|
25
30
|
|
31
|
+
def finalize
|
32
|
+
@actor.terminate if @actor and @actor.alive?
|
33
|
+
end
|
34
|
+
|
26
35
|
def start_actor(start_attempts = 3, sleep_interval = 30)
|
27
36
|
failures = 0
|
28
37
|
|
data/lib/celluloid/task.rb
CHANGED
@@ -46,6 +46,7 @@ module Celluloid
|
|
46
46
|
rescue TerminatedError
|
47
47
|
# Task was explicitly terminated
|
48
48
|
ensure
|
49
|
+
@status = :dead
|
49
50
|
actor.tasks.delete self
|
50
51
|
end
|
51
52
|
end
|
@@ -57,10 +58,6 @@ module Celluloid
|
|
57
58
|
nil
|
58
59
|
rescue FiberError
|
59
60
|
raise DeadTaskError, "cannot resume a dead task"
|
60
|
-
rescue RuntimeError => ex
|
61
|
-
# These occur spuriously on 1.9.3 if we shut down an actor with running tasks
|
62
|
-
return if ex.message == ""
|
63
|
-
raise
|
64
61
|
end
|
65
62
|
|
66
63
|
# Terminate this task
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Celluloid
|
2
|
+
# An abstraction around threads from the InternalPool which ensures we don't
|
3
|
+
# accidentally do things to threads which have been returned to the pool,
|
4
|
+
# such as, say, killing them
|
5
|
+
class ThreadHandle
|
6
|
+
def initialize
|
7
|
+
@mutex = Mutex.new
|
8
|
+
@join = ConditionVariable.new
|
9
|
+
|
10
|
+
@thread = InternalPool.get do
|
11
|
+
begin
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
@mutex.synchronize do
|
15
|
+
@thread = nil
|
16
|
+
@join.broadcast
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Is the thread running?
|
23
|
+
def alive?
|
24
|
+
@mutex.synchronize { @thread.alive? if @thread }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Forcibly kill the thread
|
28
|
+
def kill
|
29
|
+
!!@mutex.synchronize { @thread.kill if @thread }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Join to a running thread, blocking until it terminates
|
33
|
+
def join
|
34
|
+
@mutex.synchronize { @join.wait(@mutex) if @thread }
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/celluloid/version.rb
CHANGED
@@ -39,6 +39,10 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
39
39
|
abort example_crash
|
40
40
|
end
|
41
41
|
|
42
|
+
def crash_with_abort_raw(reason)
|
43
|
+
abort reason
|
44
|
+
end
|
45
|
+
|
42
46
|
def internal_hello
|
43
47
|
external_hello
|
44
48
|
end
|
@@ -59,6 +63,16 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
59
63
|
super || delegates?(method_name)
|
60
64
|
end
|
61
65
|
|
66
|
+
def call_private
|
67
|
+
zomg_private!
|
68
|
+
end
|
69
|
+
|
70
|
+
def zomg_private
|
71
|
+
@private_called = true
|
72
|
+
end
|
73
|
+
private :zomg_private
|
74
|
+
attr_reader :private_called
|
75
|
+
|
62
76
|
private
|
63
77
|
|
64
78
|
def delegates?(method_name)
|
@@ -132,25 +146,44 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
132
146
|
end.to raise_exception(ExampleCrash)
|
133
147
|
end
|
134
148
|
|
135
|
-
|
136
|
-
|
149
|
+
describe "when #abort is called" do
|
150
|
+
it "raises exceptions in the caller but keeps running" do
|
151
|
+
actor = actor_class.new "Al Pacino"
|
137
152
|
|
138
|
-
|
139
|
-
|
153
|
+
e = nil
|
154
|
+
line_no = nil
|
140
155
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
156
|
+
expect do
|
157
|
+
begin
|
158
|
+
line_no = __LINE__; actor.crash_with_abort "You die motherfucker!", :bar
|
159
|
+
rescue => ex
|
160
|
+
e = ex
|
161
|
+
raise
|
162
|
+
end
|
163
|
+
end.to raise_exception(ExampleCrash, "You die motherfucker!")
|
164
|
+
|
165
|
+
e.backtrace.any? { |line| line.include?([__FILE__, line_no].join(':')) }.should be_true # Check the backtrace is appropriate to the caller
|
166
|
+
e.foo.should be == :bar # Check the exception maintains instance variables
|
167
|
+
|
168
|
+
actor.should be_alive
|
169
|
+
end
|
149
170
|
|
150
|
-
|
151
|
-
|
171
|
+
it "converts strings to runtime errors" do
|
172
|
+
actor = actor_class.new "Al Pacino"
|
173
|
+
expect do
|
174
|
+
actor.crash_with_abort_raw "foo"
|
175
|
+
end.to raise_exception(RuntimeError, "foo")
|
176
|
+
end
|
152
177
|
|
153
|
-
|
178
|
+
it "crashes the caller if we pass neither String nor Exception" do
|
179
|
+
actor = actor_class.new "Al Pacino"
|
180
|
+
expect do
|
181
|
+
actor.crash_with_abort_raw 10
|
182
|
+
end.to raise_exception(TypeError, "Exception object/String expected, but Fixnum received")
|
183
|
+
|
184
|
+
actor.greet rescue nil # Ensure our actor has died.
|
185
|
+
actor.should_not be_alive
|
186
|
+
end
|
154
187
|
end
|
155
188
|
|
156
189
|
it "raises DeadActorError if methods are synchronously called on a dead actor" do
|
@@ -168,12 +201,24 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
168
201
|
actor.greet.should == "Hi, I'm Charlie Sheen"
|
169
202
|
end
|
170
203
|
|
204
|
+
it "handles asynchronous calls via #async" do
|
205
|
+
actor = actor_class.new "Troy McClure"
|
206
|
+
actor.async :change_name, "Charlie Sheen"
|
207
|
+
actor.greet.should == "Hi, I'm Charlie Sheen"
|
208
|
+
end
|
209
|
+
|
171
210
|
it "handles asynchronous calls to itself" do
|
172
211
|
actor = actor_class.new "Troy McClure"
|
173
212
|
actor.change_name_async "Charlie Sheen"
|
174
213
|
actor.greet.should == "Hi, I'm Charlie Sheen"
|
175
214
|
end
|
176
215
|
|
216
|
+
it "allows an actor to call private methods asynchronously with a bang" do
|
217
|
+
actor = actor_class.new "Troy McClure"
|
218
|
+
actor.call_private
|
219
|
+
actor.private_called.should be_true
|
220
|
+
end
|
221
|
+
|
177
222
|
it "knows if it's inside actor scope" do
|
178
223
|
Celluloid.should_not be_actor
|
179
224
|
actor = actor_class.new "Troy McClure"
|
@@ -224,7 +269,15 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
224
269
|
actor = actor_class.new "Arnold Schwarzenegger"
|
225
270
|
actor.should be_alive
|
226
271
|
actor.terminate
|
227
|
-
|
272
|
+
actor.join
|
273
|
+
actor.should_not be_alive
|
274
|
+
end
|
275
|
+
|
276
|
+
it "kills" do
|
277
|
+
actor = actor_class.new "Woody Harrelson"
|
278
|
+
actor.should be_alive
|
279
|
+
actor.kill
|
280
|
+
actor.join
|
228
281
|
actor.should_not be_alive
|
229
282
|
end
|
230
283
|
|
@@ -388,7 +441,7 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
388
441
|
received_obj.should == message
|
389
442
|
end
|
390
443
|
|
391
|
-
it "times out after the given interval" do
|
444
|
+
it "times out after the given interval", :pending => ENV['CI'] do
|
392
445
|
interval = 0.1
|
393
446
|
started_at = Time.now
|
394
447
|
|
@@ -520,10 +573,8 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
520
573
|
it "knows which tasks are waiting on calls to other actors" do
|
521
574
|
actor = @klass.new
|
522
575
|
|
523
|
-
# an alias for Celluloid::Actor#waiting_tasks
|
524
576
|
tasks = actor.tasks
|
525
577
|
tasks.size.should == 1
|
526
|
-
tasks.first.status.should == :running
|
527
578
|
|
528
579
|
future = actor.future(:blocking_call)
|
529
580
|
sleep 0.1 # hax! waiting for ^^^ call to actually start
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: celluloid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-06-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &70191377653480 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70191377653480
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70191377602840 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70191377602840
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: guard-rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &70191350440520 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70191350440520
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: benchmark_suite
|
49
|
-
requirement: &
|
49
|
+
requirement: &70191350401720 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70191350401720
|
58
58
|
description: Celluloid enables people to build concurrent programs out of concurrent
|
59
59
|
objects just as easily as they build sequential programs out of sequential objects
|
60
60
|
email:
|
@@ -73,19 +73,20 @@ files:
|
|
73
73
|
- lib/celluloid/fiber.rb
|
74
74
|
- lib/celluloid/fsm.rb
|
75
75
|
- lib/celluloid/future.rb
|
76
|
-
- lib/celluloid/
|
76
|
+
- lib/celluloid/internal_pool.rb
|
77
77
|
- lib/celluloid/links.rb
|
78
78
|
- lib/celluloid/logger.rb
|
79
79
|
- lib/celluloid/mailbox.rb
|
80
|
-
- lib/celluloid/
|
80
|
+
- lib/celluloid/pool_manager.rb
|
81
81
|
- lib/celluloid/receivers.rb
|
82
82
|
- lib/celluloid/registry.rb
|
83
83
|
- lib/celluloid/responses.rb
|
84
84
|
- lib/celluloid/rspec.rb
|
85
85
|
- lib/celluloid/signals.rb
|
86
|
+
- lib/celluloid/supervision_group.rb
|
86
87
|
- lib/celluloid/supervisor.rb
|
87
88
|
- lib/celluloid/task.rb
|
88
|
-
- lib/celluloid/
|
89
|
+
- lib/celluloid/thread_handle.rb
|
89
90
|
- lib/celluloid/timers.rb
|
90
91
|
- lib/celluloid/uuid.rb
|
91
92
|
- lib/celluloid/version.rb
|
@@ -113,8 +114,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
114
|
version: 1.3.6
|
114
115
|
requirements: []
|
115
116
|
rubyforge_project:
|
116
|
-
rubygems_version: 1.8.
|
117
|
+
rubygems_version: 1.8.10
|
117
118
|
signing_key:
|
118
119
|
specification_version: 3
|
119
120
|
summary: Actor-based concurrent object framework for Ruby
|
120
121
|
test_files: []
|
122
|
+
has_rdoc:
|
data/lib/celluloid/group.rb
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
module Celluloid
|
2
|
-
# Supervise collections of actors as a group
|
3
|
-
class Group
|
4
|
-
include Celluloid
|
5
|
-
trap_exit :restart_supervisor
|
6
|
-
|
7
|
-
class << self
|
8
|
-
# Actors or sub-applications to be supervised
|
9
|
-
def supervisables
|
10
|
-
@supervisables ||= []
|
11
|
-
end
|
12
|
-
|
13
|
-
# Start this application (and watch it with a supervisor)
|
14
|
-
alias_method :run!, :supervise
|
15
|
-
|
16
|
-
# Run the application in the foreground with a simple watchdog
|
17
|
-
def run
|
18
|
-
loop do
|
19
|
-
supervisor = run!
|
20
|
-
|
21
|
-
# Take five, toplevel supervisor
|
22
|
-
sleep 5 while supervisor.alive?
|
23
|
-
|
24
|
-
Logger.error "!!! Celluloid::Application #{self} crashed. Restarting..."
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# Register an actor class or a sub-application class to be launched and
|
29
|
-
# supervised while this application is running. Available options are:
|
30
|
-
#
|
31
|
-
# * as: register this application in the Celluloid::Actor[] directory
|
32
|
-
# * args: start the actor with the given arguments
|
33
|
-
def supervise(klass, options = {})
|
34
|
-
supervisables << Supervisable.new(klass, options)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Start the application
|
39
|
-
def initialize
|
40
|
-
@supervisors = {}
|
41
|
-
|
42
|
-
# This is some serious lolcode, but like... start the supervisors for
|
43
|
-
# this application
|
44
|
-
self.class.supervisables.each do |supervisable|
|
45
|
-
supervisor = supervisable.supervise
|
46
|
-
@supervisors[supervisor] = supervisable
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# Restart a crashed supervisor
|
51
|
-
def restart_supervisor(supervisor, reason)
|
52
|
-
supervisable = @supervisors.delete supervisor
|
53
|
-
raise "a supervisable went missing. This shouldn't be!" unless supervisable
|
54
|
-
|
55
|
-
# Ignore supervisors that shut down cleanly
|
56
|
-
return unless reason
|
57
|
-
|
58
|
-
supervisor = supervisable.supervise
|
59
|
-
@supervisors[supervisor] = supervisable
|
60
|
-
end
|
61
|
-
|
62
|
-
# A subcomponent of an application to be supervised
|
63
|
-
class Supervisable
|
64
|
-
attr_reader :klass, :as, :args
|
65
|
-
|
66
|
-
def initialize(klass, options = {})
|
67
|
-
@klass = klass
|
68
|
-
|
69
|
-
# Stringify keys :/
|
70
|
-
options = options.inject({}) { |h,(k,v)| h[k.to_s] = v; h }
|
71
|
-
|
72
|
-
@as = options['as']
|
73
|
-
@args = options['args'] || []
|
74
|
-
raise ":args should be an Array" unless @args.kind_of? Array
|
75
|
-
end
|
76
|
-
|
77
|
-
def supervise
|
78
|
-
Supervisor.new_link(@as, @klass, *@args)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Legacy support for the old name. Going away soon!
|
84
|
-
Application = Group
|
85
|
-
end
|
data/lib/celluloid/pool.rb
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
module Celluloid
|
2
|
-
# Pools provide groups of actors which can service requests
|
3
|
-
class Pool
|
4
|
-
include Celluloid
|
5
|
-
trap_exit :crash_handler
|
6
|
-
attr_reader :max_actors
|
7
|
-
|
8
|
-
# Takes a class of actor to pool and a hash of options:
|
9
|
-
#
|
10
|
-
# * initial_size: how many actors to eagerly create
|
11
|
-
# * max_size: maximum number of actors (default one actor per CPU core)
|
12
|
-
# * args: an array of arguments to pass to the actor's initialize
|
13
|
-
def initialize(klass, options = {})
|
14
|
-
raise ArgumentError, "A Pool has a minimum size of 2" if options[:max_size] && options[:max_size] < 2
|
15
|
-
opts = {
|
16
|
-
:initial_size => 1,
|
17
|
-
:max_size => [Celluloid.cores, 2].max,
|
18
|
-
:args => []
|
19
|
-
}.merge(options)
|
20
|
-
|
21
|
-
@klass, @args = klass, opts[:args]
|
22
|
-
@max_actors = opts[:max_size]
|
23
|
-
@idle_actors, @running_actors = 0, 0
|
24
|
-
@actors = []
|
25
|
-
|
26
|
-
opts[:initial_size].times do
|
27
|
-
@actors << spawn
|
28
|
-
@idle_actors += 1
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Get an actor from the pool. Actors taken from the pool must be put back
|
33
|
-
# with Pool#put. Alternatively, you can use get with a block form:
|
34
|
-
#
|
35
|
-
# pool.get { |actor| ... }
|
36
|
-
#
|
37
|
-
# This will automatically return actors to the pool when the block completes
|
38
|
-
def get
|
39
|
-
if @max_actors and @running_actors == @max_actors
|
40
|
-
wait :ready
|
41
|
-
end
|
42
|
-
|
43
|
-
actor = @actors.pop
|
44
|
-
if actor
|
45
|
-
@idle_actors -= 1
|
46
|
-
else
|
47
|
-
actor = spawn
|
48
|
-
end
|
49
|
-
|
50
|
-
if block_given?
|
51
|
-
begin
|
52
|
-
yield actor
|
53
|
-
rescue => ex
|
54
|
-
end
|
55
|
-
|
56
|
-
put actor
|
57
|
-
abort ex if ex
|
58
|
-
nil
|
59
|
-
else
|
60
|
-
actor
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Return an actor to the pool
|
65
|
-
def put(actor)
|
66
|
-
begin
|
67
|
-
raise TypeError, "expecting a #{@klass} actor" unless actor.is_a? @klass
|
68
|
-
rescue DeadActorError
|
69
|
-
# The actor may have died before it was handed back to us
|
70
|
-
# We'll let the crash_handler deal with it in due time
|
71
|
-
return
|
72
|
-
end
|
73
|
-
|
74
|
-
@actors << actor
|
75
|
-
@idle_actors += 1
|
76
|
-
end
|
77
|
-
|
78
|
-
# Number of active actors in this pool
|
79
|
-
def size
|
80
|
-
@running_actors
|
81
|
-
end
|
82
|
-
|
83
|
-
# Number of idle actors in the pool
|
84
|
-
def idle_count
|
85
|
-
@idle_actors
|
86
|
-
end
|
87
|
-
alias_method :idle_size, :idle_count
|
88
|
-
|
89
|
-
# Handle crashed actors
|
90
|
-
def crash_handler(actor, reason)
|
91
|
-
@idle_actors -= 1 if @actors.delete actor
|
92
|
-
@running_actors -= 1
|
93
|
-
|
94
|
-
# If we were maxed out before...
|
95
|
-
signal :ready if @max_actors and @running_actors + 1 == @max_actors
|
96
|
-
end
|
97
|
-
|
98
|
-
# Spawn an actor of the given class
|
99
|
-
def spawn
|
100
|
-
worker = @klass.new_link(*@args)
|
101
|
-
@running_actors += 1
|
102
|
-
worker
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|