celluloid 0.11.1 → 0.12.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 +23 -6
- data/lib/celluloid/actor.rb +146 -56
- data/lib/celluloid/boot.rb +9 -0
- data/lib/celluloid/calls.rb +3 -3
- data/lib/celluloid/core_ext.rb +1 -6
- data/lib/celluloid/cpu_counter.rb +2 -0
- data/lib/celluloid/future.rb +5 -0
- data/lib/celluloid/links.rb +1 -3
- data/lib/celluloid/mailbox.rb +14 -20
- data/lib/celluloid/method.rb +19 -0
- data/lib/celluloid/notifications.rb +16 -17
- data/lib/celluloid/pool_manager.rb +40 -11
- data/lib/celluloid/proxies/abstract_proxy.rb +17 -0
- data/lib/celluloid/{actor_proxy.rb → proxies/actor_proxy.rb} +38 -39
- data/lib/celluloid/proxies/async_proxy.rb +19 -0
- data/lib/celluloid/proxies/future_proxy.rb +19 -0
- data/lib/celluloid/registry.rb +8 -5
- data/lib/celluloid/rspec.rb +6 -0
- data/lib/celluloid/supervision_group.rb +23 -10
- data/lib/celluloid/system_events.rb +54 -0
- data/lib/celluloid/task.rb +7 -63
- data/lib/celluloid/tasks/task_fiber.rb +65 -0
- data/lib/celluloid/tasks/task_thread.rb +78 -0
- data/lib/celluloid/thread_handle.rb +2 -1
- data/lib/celluloid/version.rb +1 -1
- data/lib/celluloid.rb +149 -114
- data/spec/support/actor_examples.rb +182 -26
- data/spec/support/example_actor_class.rb +6 -2
- data/spec/support/mailbox_examples.rb +3 -18
- data/spec/support/task_examples.rb +36 -0
- metadata +21 -13
- data/lib/celluloid/events.rb +0 -26
data/lib/celluloid.rb
CHANGED
|
@@ -4,14 +4,17 @@ require 'timeout'
|
|
|
4
4
|
require 'set'
|
|
5
5
|
|
|
6
6
|
module Celluloid
|
|
7
|
+
extend self # expose all instance methods as singleton methods
|
|
8
|
+
|
|
7
9
|
SHUTDOWN_TIMEOUT = 120 # How long actors have to terminate
|
|
8
|
-
@logger = Logger.new STDERR
|
|
9
10
|
|
|
10
11
|
class << self
|
|
11
|
-
attr_accessor :logger
|
|
12
|
+
attr_accessor :logger # Thread-safe logger class
|
|
13
|
+
attr_accessor :task_class # Default task type to use
|
|
12
14
|
|
|
13
15
|
def included(klass)
|
|
14
|
-
klass.send :extend,
|
|
16
|
+
klass.send :extend, ClassMethods
|
|
17
|
+
klass.send :include, InstanceMethods
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
# Are we currently inside of an actor?
|
|
@@ -19,36 +22,6 @@ module Celluloid
|
|
|
19
22
|
!!Thread.current[:actor]
|
|
20
23
|
end
|
|
21
24
|
|
|
22
|
-
# Is current actor running in exclusive mode?
|
|
23
|
-
def exclusive?
|
|
24
|
-
actor? and Thread.current[:actor].exclusive?
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Obtain the currently running actor (if one exists)
|
|
28
|
-
def current_actor
|
|
29
|
-
Actor.current
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Receive an asynchronous message
|
|
33
|
-
def receive(timeout = nil, &block)
|
|
34
|
-
actor = Thread.current[:actor]
|
|
35
|
-
if actor
|
|
36
|
-
actor.receive(timeout, &block)
|
|
37
|
-
else
|
|
38
|
-
Thread.mailbox.receive(timeout, &block)
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Sleep letting the actor continue processing messages
|
|
43
|
-
def sleep(interval)
|
|
44
|
-
actor = Thread.current[:actor]
|
|
45
|
-
if actor
|
|
46
|
-
actor.sleep(interval)
|
|
47
|
-
else
|
|
48
|
-
Kernel.sleep interval
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
25
|
# Generate a Universally Unique Identifier
|
|
53
26
|
def uuid
|
|
54
27
|
UUID.generate
|
|
@@ -67,8 +40,6 @@ module Celluloid
|
|
|
67
40
|
end
|
|
68
41
|
|
|
69
42
|
# Shut down all running actors
|
|
70
|
-
# FIXME: This should probably attempt a graceful shutdown of the supervision
|
|
71
|
-
# tree before iterating through all actors and telling them to terminate.
|
|
72
43
|
def shutdown
|
|
73
44
|
Timeout.timeout(SHUTDOWN_TIMEOUT) do
|
|
74
45
|
actors = Actor.all
|
|
@@ -78,16 +49,16 @@ module Celluloid
|
|
|
78
49
|
Supervisor.root.terminate if Supervisor.root
|
|
79
50
|
|
|
80
51
|
# Actors cannot self-terminate, you must do it for them
|
|
81
|
-
|
|
52
|
+
Actor.all.each do |actor|
|
|
82
53
|
begin
|
|
83
|
-
actor.
|
|
54
|
+
actor.terminate!
|
|
84
55
|
rescue DeadActorError, MailboxError
|
|
85
56
|
end
|
|
86
57
|
end
|
|
87
58
|
|
|
88
|
-
|
|
59
|
+
Actor.all.each do |actor|
|
|
89
60
|
begin
|
|
90
|
-
|
|
61
|
+
Actor.join(actor)
|
|
91
62
|
rescue DeadActorError, MailboxError
|
|
92
63
|
end
|
|
93
64
|
end
|
|
@@ -104,7 +75,7 @@ module Celluloid
|
|
|
104
75
|
module ClassMethods
|
|
105
76
|
# Create a new actor
|
|
106
77
|
def new(*args, &block)
|
|
107
|
-
proxy = Actor.new(allocate).proxy
|
|
78
|
+
proxy = Actor.new(allocate, actor_options).proxy
|
|
108
79
|
proxy._send_(:initialize, *args, &block)
|
|
109
80
|
proxy
|
|
110
81
|
end
|
|
@@ -112,11 +83,10 @@ module Celluloid
|
|
|
112
83
|
|
|
113
84
|
# Create a new actor and link to the current one
|
|
114
85
|
def new_link(*args, &block)
|
|
115
|
-
|
|
116
|
-
raise NotActorError, "can't link outside actor context" unless current_actor
|
|
86
|
+
raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
|
|
117
87
|
|
|
118
|
-
proxy = Actor.new(allocate).proxy
|
|
119
|
-
|
|
88
|
+
proxy = Actor.new(allocate, actor_options).proxy
|
|
89
|
+
Actor.link(proxy)
|
|
120
90
|
proxy._send_(:initialize, *args, &block)
|
|
121
91
|
proxy
|
|
122
92
|
end
|
|
@@ -158,43 +128,116 @@ module Celluloid
|
|
|
158
128
|
@exit_handler = callback.to_sym
|
|
159
129
|
end
|
|
160
130
|
|
|
161
|
-
# Obtain the exit handler for this actor
|
|
162
|
-
attr_reader :exit_handler
|
|
163
|
-
|
|
164
131
|
# Configure a custom mailbox factory
|
|
165
132
|
def use_mailbox(klass = nil, &block)
|
|
166
133
|
if block
|
|
167
134
|
@mailbox_factory = block
|
|
168
135
|
else
|
|
169
|
-
|
|
136
|
+
mailbox_class(klass)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Define the mailbox class for this class
|
|
141
|
+
def mailbox_class(klass)
|
|
142
|
+
@mailbox_factory = proc { klass.new }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Define the default task type for this class
|
|
146
|
+
def task_class(klass = nil)
|
|
147
|
+
if klass
|
|
148
|
+
@task_class = klass
|
|
149
|
+
elsif defined?(@task_class)
|
|
150
|
+
@task_class
|
|
151
|
+
elsif superclass.respond_to? :task_class
|
|
152
|
+
superclass.task_class
|
|
153
|
+
else
|
|
154
|
+
Celluloid.task_class
|
|
170
155
|
end
|
|
171
156
|
end
|
|
172
157
|
|
|
173
158
|
# Mark methods as running exclusively
|
|
174
159
|
def exclusive(*methods)
|
|
175
|
-
|
|
176
|
-
|
|
160
|
+
if methods.empty?
|
|
161
|
+
@exclusive_methods = :all
|
|
162
|
+
elsif @exclusive_methods != :all
|
|
163
|
+
@exclusive_methods ||= Set.new
|
|
164
|
+
@exclusive_methods.merge methods.map(&:to_sym)
|
|
165
|
+
end
|
|
177
166
|
end
|
|
178
|
-
attr_reader :exclusive_methods
|
|
179
167
|
|
|
180
168
|
# Create a mailbox for this actor
|
|
181
169
|
def mailbox_factory
|
|
182
170
|
if defined?(@mailbox_factory)
|
|
183
171
|
@mailbox_factory.call
|
|
184
|
-
elsif
|
|
185
|
-
|
|
172
|
+
elsif superclass.respond_to? :mailbox_factory
|
|
173
|
+
superclass.mailbox_factory
|
|
186
174
|
else
|
|
187
175
|
Mailbox.new
|
|
188
176
|
end
|
|
189
177
|
end
|
|
190
178
|
|
|
179
|
+
# Configuration options for Actor#new
|
|
180
|
+
def actor_options
|
|
181
|
+
{
|
|
182
|
+
:mailbox => mailbox_factory,
|
|
183
|
+
:exit_handler => @exit_handler,
|
|
184
|
+
:exclusive_methods => @exclusive_methods,
|
|
185
|
+
:task_class => task_class
|
|
186
|
+
}
|
|
187
|
+
end
|
|
188
|
+
|
|
191
189
|
def ===(other)
|
|
192
190
|
other.kind_of? self
|
|
193
191
|
end
|
|
194
192
|
end
|
|
195
193
|
|
|
194
|
+
# These are methods we don't want added to the Celluloid singleton but to be
|
|
195
|
+
# defined on all classes that use Celluloid
|
|
196
|
+
module InstanceMethods
|
|
197
|
+
# Obtain the Ruby object the actor is wrapping. This should ONLY be used
|
|
198
|
+
# for a limited set of use cases like runtime metaprogramming. Interacting
|
|
199
|
+
# directly with the wrapped object foregoes any kind of thread safety that
|
|
200
|
+
# Celluloid would ordinarily provide you, and the object is guaranteed to
|
|
201
|
+
# be shared with at least the actor thread. Tread carefully.
|
|
202
|
+
def wrapped_object; self; end
|
|
203
|
+
|
|
204
|
+
def inspect
|
|
205
|
+
str = "#<Celluloid::Actor(#{self.class}:0x#{object_id.to_s(16)})"
|
|
206
|
+
ivars = instance_variables.map do |ivar|
|
|
207
|
+
"#{ivar}=#{instance_variable_get(ivar).inspect}"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
str << " " << ivars.join(' ') unless ivars.empty?
|
|
211
|
+
str << ">"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Process async calls via method_missing
|
|
215
|
+
def method_missing(meth, *args, &block)
|
|
216
|
+
# bang methods are async calls
|
|
217
|
+
if meth.to_s.match(/!$/)
|
|
218
|
+
unbanged_meth = meth.to_s.sub(/!$/, '')
|
|
219
|
+
args.unshift unbanged_meth
|
|
220
|
+
|
|
221
|
+
call = AsyncCall.new(:__send__, args, block)
|
|
222
|
+
begin
|
|
223
|
+
Thread.current[:actor].mailbox << call
|
|
224
|
+
rescue MailboxError
|
|
225
|
+
# Silently swallow asynchronous calls to dead actors. There's no way
|
|
226
|
+
# to reliably generate DeadActorErrors for async calls, so users of
|
|
227
|
+
# async calls should find other ways to deal with actors dying
|
|
228
|
+
# during an async call (i.e. linking/supervisors)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
return
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
super
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
196
238
|
#
|
|
197
|
-
#
|
|
239
|
+
# The following methods are available on both the Celluloid singleton and
|
|
240
|
+
# directly inside of all classes that include Celluloid
|
|
198
241
|
#
|
|
199
242
|
|
|
200
243
|
# Is this actor alive?
|
|
@@ -217,16 +260,6 @@ module Celluloid
|
|
|
217
260
|
Thread.current[:actor].terminate
|
|
218
261
|
end
|
|
219
262
|
|
|
220
|
-
def inspect
|
|
221
|
-
str = "#<Celluloid::Actor(#{self.class}:0x#{object_id.to_s(16)})"
|
|
222
|
-
ivars = instance_variables.map do |ivar|
|
|
223
|
-
"#{ivar}=#{instance_variable_get(ivar).inspect}"
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
str << " " << ivars.join(' ') unless ivars.empty?
|
|
227
|
-
str << ">"
|
|
228
|
-
end
|
|
229
|
-
|
|
230
263
|
# Send a signal with the given name to all waiting methods
|
|
231
264
|
def signal(name, value = nil)
|
|
232
265
|
Thread.current[:actor].signal name, value
|
|
@@ -252,51 +285,59 @@ module Celluloid
|
|
|
252
285
|
Thread.current[:actor].tasks.to_a
|
|
253
286
|
end
|
|
254
287
|
|
|
255
|
-
# Obtain the Ruby object the actor is wrapping. This should ONLY be used
|
|
256
|
-
# for a limited set of use cases like runtime metaprogramming. Interacting
|
|
257
|
-
# directly with the wrapped object foregoes any kind of thread safety that
|
|
258
|
-
# Celluloid would ordinarily provide you, and the object is guaranteed to
|
|
259
|
-
# be shared with at least the actor thread. Tread carefully.
|
|
260
|
-
def wrapped_object; self; end
|
|
261
|
-
|
|
262
288
|
# Obtain the Celluloid::Links for this actor
|
|
263
289
|
def links
|
|
264
290
|
Thread.current[:actor].links
|
|
265
291
|
end
|
|
266
292
|
|
|
293
|
+
# Watch for exit events from another actor
|
|
294
|
+
def monitor(actor)
|
|
295
|
+
Actor.monitor(actor)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Stop waiting for exit events from another actor
|
|
299
|
+
def unmonitor(actor)
|
|
300
|
+
Actor.unmonitor(actor)
|
|
301
|
+
end
|
|
302
|
+
|
|
267
303
|
# Link this actor to another, allowing it to crash or react to errors
|
|
268
304
|
def link(actor)
|
|
269
|
-
|
|
270
|
-
notify_link actor
|
|
305
|
+
Actor.link(actor)
|
|
271
306
|
end
|
|
272
307
|
|
|
273
308
|
# Remove links to another actor
|
|
274
309
|
def unlink(actor)
|
|
275
|
-
|
|
276
|
-
notify_unlink actor
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
def notify_link(actor)
|
|
280
|
-
links << actor
|
|
310
|
+
Actor.unlink(actor)
|
|
281
311
|
end
|
|
282
312
|
|
|
283
|
-
|
|
284
|
-
|
|
313
|
+
# Are we monitoring another actor?
|
|
314
|
+
def monitoring?(actor)
|
|
315
|
+
Actor.monitoring?(actor)
|
|
285
316
|
end
|
|
286
317
|
|
|
287
318
|
# Is this actor linked to another?
|
|
288
319
|
def linked_to?(actor)
|
|
289
|
-
|
|
320
|
+
Actor.linked_to?(actor)
|
|
290
321
|
end
|
|
291
322
|
|
|
292
323
|
# Receive an asynchronous message via the actor protocol
|
|
293
324
|
def receive(timeout = nil, &block)
|
|
294
|
-
|
|
325
|
+
actor = Thread.current[:actor]
|
|
326
|
+
if actor
|
|
327
|
+
actor.receive(timeout, &block)
|
|
328
|
+
else
|
|
329
|
+
Thread.mailbox.receive(timeout, &block)
|
|
330
|
+
end
|
|
295
331
|
end
|
|
296
332
|
|
|
297
|
-
# Sleep
|
|
333
|
+
# Sleep letting the actor continue processing messages
|
|
298
334
|
def sleep(interval)
|
|
299
|
-
|
|
335
|
+
actor = Thread.current[:actor]
|
|
336
|
+
if actor
|
|
337
|
+
actor.sleep(interval)
|
|
338
|
+
else
|
|
339
|
+
Kernel.sleep interval
|
|
340
|
+
end
|
|
300
341
|
end
|
|
301
342
|
|
|
302
343
|
# Run given block in an exclusive mode: all synchronous calls block the whole
|
|
@@ -307,7 +348,8 @@ module Celluloid
|
|
|
307
348
|
|
|
308
349
|
# Are we currently exclusive
|
|
309
350
|
def exclusive?
|
|
310
|
-
|
|
351
|
+
actor = Thread.current[:actor]
|
|
352
|
+
actor && actor.exclusive?
|
|
311
353
|
end
|
|
312
354
|
|
|
313
355
|
# Call a block after a given interval, returning a Celluloid::Timer object
|
|
@@ -330,62 +372,55 @@ module Celluloid
|
|
|
330
372
|
end
|
|
331
373
|
|
|
332
374
|
# Handle async calls within an actor itself
|
|
333
|
-
def async(meth, *args, &block)
|
|
334
|
-
|
|
375
|
+
def async(meth = nil, *args, &block)
|
|
376
|
+
if meth
|
|
377
|
+
Actor.async Thread.current[:actor].mailbox, meth, *args, &block
|
|
378
|
+
else
|
|
379
|
+
Thread.current[:actor].proxy.async
|
|
380
|
+
end
|
|
335
381
|
end
|
|
336
382
|
|
|
337
383
|
# Handle calls to future within an actor itself
|
|
338
|
-
def future(meth, *args, &block)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
def method_missing(meth, *args, &block)
|
|
344
|
-
# bang methods are async calls
|
|
345
|
-
if meth.to_s.match(/!$/)
|
|
346
|
-
unbanged_meth = meth.to_s.sub(/!$/, '')
|
|
347
|
-
args.unshift unbanged_meth
|
|
348
|
-
|
|
349
|
-
call = AsyncCall.new(:__send__, args, block)
|
|
350
|
-
begin
|
|
351
|
-
Thread.current[:actor].mailbox << call
|
|
352
|
-
rescue MailboxError
|
|
353
|
-
# Silently swallow asynchronous calls to dead actors. There's no way
|
|
354
|
-
# to reliably generate DeadActorErrors for async calls, so users of
|
|
355
|
-
# async calls should find other ways to deal with actors dying
|
|
356
|
-
# during an async call (i.e. linking/supervisors)
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
return
|
|
384
|
+
def future(meth = nil, *args, &block)
|
|
385
|
+
if meth
|
|
386
|
+
Actor.future Thread.current[:actor].mailbox, meth, *args, &block
|
|
387
|
+
else
|
|
388
|
+
Thread.current[:actor].proxy.future
|
|
360
389
|
end
|
|
361
|
-
|
|
362
|
-
super
|
|
363
390
|
end
|
|
364
391
|
end
|
|
365
392
|
|
|
366
393
|
require 'celluloid/version'
|
|
367
|
-
|
|
394
|
+
|
|
368
395
|
require 'celluloid/calls'
|
|
369
396
|
require 'celluloid/core_ext'
|
|
370
397
|
require 'celluloid/cpu_counter'
|
|
371
|
-
require 'celluloid/events'
|
|
372
398
|
require 'celluloid/fiber'
|
|
373
399
|
require 'celluloid/fsm'
|
|
374
400
|
require 'celluloid/internal_pool'
|
|
375
401
|
require 'celluloid/links'
|
|
376
402
|
require 'celluloid/logger'
|
|
377
403
|
require 'celluloid/mailbox'
|
|
404
|
+
require 'celluloid/method'
|
|
378
405
|
require 'celluloid/receivers'
|
|
379
406
|
require 'celluloid/registry'
|
|
380
407
|
require 'celluloid/responses'
|
|
381
408
|
require 'celluloid/signals'
|
|
409
|
+
require 'celluloid/system_events'
|
|
382
410
|
require 'celluloid/task'
|
|
383
411
|
require 'celluloid/thread_handle'
|
|
384
412
|
require 'celluloid/uuid'
|
|
385
413
|
|
|
414
|
+
require 'celluloid/proxies/abstract_proxy'
|
|
415
|
+
require 'celluloid/proxies/actor_proxy'
|
|
416
|
+
require 'celluloid/proxies/async_proxy'
|
|
417
|
+
require 'celluloid/proxies/future_proxy'
|
|
418
|
+
|
|
386
419
|
require 'celluloid/actor'
|
|
387
420
|
require 'celluloid/future'
|
|
388
421
|
require 'celluloid/pool_manager'
|
|
389
422
|
require 'celluloid/supervision_group'
|
|
390
423
|
require 'celluloid/supervisor'
|
|
391
424
|
require 'celluloid/notifications'
|
|
425
|
+
|
|
426
|
+
require 'celluloid/boot'
|