celluloid 0.12.0 → 0.13.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 +36 -23
- data/lib/celluloid/actor.rb +74 -47
- data/lib/celluloid/autostart.rb +16 -0
- data/lib/celluloid/calls.rb +52 -52
- data/lib/celluloid/condition.rb +70 -0
- data/lib/celluloid/core_ext.rb +1 -1
- data/lib/celluloid/cpu_counter.rb +7 -1
- data/lib/celluloid/fsm.rb +49 -16
- data/lib/celluloid/future.rb +5 -2
- data/lib/celluloid/internal_pool.rb +62 -44
- data/lib/celluloid/legacy.rb +46 -0
- data/lib/celluloid/links.rb +0 -5
- data/lib/celluloid/logger.rb +14 -4
- data/lib/celluloid/logging/incident.rb +21 -0
- data/lib/celluloid/logging/incident_logger.rb +129 -0
- data/lib/celluloid/logging/incident_reporter.rb +48 -0
- data/lib/celluloid/logging/log_event.rb +20 -0
- data/lib/celluloid/logging/ring_buffer.rb +65 -0
- data/lib/celluloid/logging.rb +5 -0
- data/lib/celluloid/mailbox.rb +2 -1
- data/lib/celluloid/method.rb +5 -0
- data/lib/celluloid/pool_manager.rb +21 -8
- data/lib/celluloid/proxies/actor_proxy.rb +11 -11
- data/lib/celluloid/proxies/async_proxy.rb +5 -1
- data/lib/celluloid/responses.rb +8 -6
- data/lib/celluloid/stack_dump.rb +94 -0
- data/lib/celluloid/supervision_group.rb +5 -3
- data/lib/celluloid/system_events.rb +11 -0
- data/lib/celluloid/tasks/task_fiber.rb +17 -9
- data/lib/celluloid/tasks/task_thread.rb +24 -14
- data/lib/celluloid/tasks.rb +59 -0
- data/lib/celluloid/thread_handle.rb +10 -1
- data/lib/celluloid/version.rb +1 -1
- data/lib/celluloid.rb +146 -87
- data/spec/support/actor_examples.rb +242 -114
- data/spec/support/example_actor_class.rb +17 -5
- data/spec/support/task_examples.rb +11 -2
- metadata +58 -14
- data/lib/celluloid/boot.rb +0 -9
- data/lib/celluloid/task.rb +0 -22
data/lib/celluloid.rb
CHANGED
|
@@ -6,11 +6,14 @@ require 'set'
|
|
|
6
6
|
module Celluloid
|
|
7
7
|
extend self # expose all instance methods as singleton methods
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
# Warning message added to Celluloid objects accessed outside their actors
|
|
10
|
+
BARE_OBJECT_WARNING_MESSAGE = "WARNING: BARE CELLULOID OBJECT "
|
|
10
11
|
|
|
11
12
|
class << self
|
|
12
|
-
attr_accessor :
|
|
13
|
-
attr_accessor :
|
|
13
|
+
attr_accessor :internal_pool # Internal thread pool
|
|
14
|
+
attr_accessor :logger # Thread-safe logger class
|
|
15
|
+
attr_accessor :task_class # Default task type to use
|
|
16
|
+
attr_accessor :shutdown_timeout # How long actors have to terminate
|
|
14
17
|
|
|
15
18
|
def included(klass)
|
|
16
19
|
klass.send :extend, ClassMethods
|
|
@@ -19,7 +22,7 @@ module Celluloid
|
|
|
19
22
|
|
|
20
23
|
# Are we currently inside of an actor?
|
|
21
24
|
def actor?
|
|
22
|
-
!!Thread.current[:
|
|
25
|
+
!!Thread.current[:celluloid_actor]
|
|
23
26
|
end
|
|
24
27
|
|
|
25
28
|
# Generate a Universally Unique Identifier
|
|
@@ -29,21 +32,44 @@ module Celluloid
|
|
|
29
32
|
|
|
30
33
|
# Obtain the number of CPUs in the system
|
|
31
34
|
def cores
|
|
32
|
-
|
|
35
|
+
CPUCounter.cores
|
|
33
36
|
end
|
|
34
37
|
alias_method :cpus, :cores
|
|
35
38
|
alias_method :ncpus, :cores
|
|
36
39
|
|
|
40
|
+
# Perform a stack dump of all actors to the given output object
|
|
41
|
+
def stack_dump(output = STDERR)
|
|
42
|
+
Celluloid::StackDump.new.dump(output)
|
|
43
|
+
end
|
|
44
|
+
alias_method :dump, :stack_dump
|
|
45
|
+
|
|
37
46
|
# Define an exception handler for actor crashes
|
|
38
47
|
def exception_handler(&block)
|
|
39
48
|
Logger.exception_handler(&block)
|
|
40
49
|
end
|
|
41
50
|
|
|
51
|
+
def suspend(status, waiter)
|
|
52
|
+
task = Thread.current[:celluloid_task]
|
|
53
|
+
if task && !Celluloid.exclusive?
|
|
54
|
+
waiter.before_suspend(task) if waiter.respond_to?(:before_suspend)
|
|
55
|
+
Task.suspend(status)
|
|
56
|
+
else
|
|
57
|
+
waiter.wait
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Launch default services
|
|
62
|
+
# FIXME: We should set up the supervision hierarchy here
|
|
63
|
+
def boot
|
|
64
|
+
Celluloid::Notifications::Fanout.supervise_as :notifications_fanout
|
|
65
|
+
Celluloid::IncidentReporter.supervise_as :default_incident_reporter, STDERR
|
|
66
|
+
end
|
|
67
|
+
|
|
42
68
|
# Shut down all running actors
|
|
43
69
|
def shutdown
|
|
44
|
-
Timeout.timeout(
|
|
70
|
+
Timeout.timeout(shutdown_timeout) do
|
|
45
71
|
actors = Actor.all
|
|
46
|
-
Logger.
|
|
72
|
+
Logger.debug "Terminating #{actors.size} actors..." if actors.size > 0
|
|
47
73
|
|
|
48
74
|
# Attempt to shut down the supervision tree, if available
|
|
49
75
|
Supervisor.root.terminate if Supervisor.root
|
|
@@ -63,14 +89,13 @@ module Celluloid
|
|
|
63
89
|
end
|
|
64
90
|
end
|
|
65
91
|
|
|
66
|
-
Logger.
|
|
92
|
+
Logger.debug "Shutdown completed cleanly"
|
|
67
93
|
end
|
|
94
|
+
rescue Timeout::Error => ex
|
|
95
|
+
Logger.error("Couldn't cleanly terminate all actors in #{shutdown_timeout} seconds!")
|
|
68
96
|
end
|
|
69
97
|
end
|
|
70
98
|
|
|
71
|
-
# Terminate all actors at exit
|
|
72
|
-
at_exit { shutdown }
|
|
73
|
-
|
|
74
99
|
# Class methods added to classes which include Celluloid
|
|
75
100
|
module ClassMethods
|
|
76
101
|
# Create a new actor
|
|
@@ -120,26 +145,55 @@ module Celluloid
|
|
|
120
145
|
|
|
121
146
|
# Run an actor in the foreground
|
|
122
147
|
def run(*args, &block)
|
|
123
|
-
new(*args, &block)
|
|
148
|
+
Actor.join(new(*args, &block))
|
|
124
149
|
end
|
|
125
150
|
|
|
126
151
|
# Trap errors from actors we're linked to when they exit
|
|
127
|
-
def
|
|
128
|
-
|
|
152
|
+
def exit_handler(callback = nil)
|
|
153
|
+
if callback
|
|
154
|
+
@exit_handler = callback.to_sym
|
|
155
|
+
elsif defined?(@exit_handler)
|
|
156
|
+
@exit_handler
|
|
157
|
+
elsif superclass.respond_to? :exit_handler
|
|
158
|
+
superclass.exit_handler
|
|
159
|
+
end
|
|
129
160
|
end
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
161
|
+
alias_method :trap_exit, :exit_handler
|
|
162
|
+
|
|
163
|
+
# Define a callback to run when the actor is finalized.
|
|
164
|
+
def finalizer(callback = nil)
|
|
165
|
+
if callback
|
|
166
|
+
@finalizer = callback.to_sym
|
|
167
|
+
elsif defined?(@finalizer)
|
|
168
|
+
@finalizer
|
|
169
|
+
elsif superclass.respond_to? :finalizer
|
|
170
|
+
superclass.finalizer
|
|
137
171
|
end
|
|
138
172
|
end
|
|
139
173
|
|
|
140
174
|
# Define the mailbox class for this class
|
|
141
|
-
def mailbox_class(klass)
|
|
142
|
-
|
|
175
|
+
def mailbox_class(klass = nil)
|
|
176
|
+
if klass
|
|
177
|
+
@mailbox_class = klass
|
|
178
|
+
elsif defined?(@mailbox_class)
|
|
179
|
+
@mailbox_class
|
|
180
|
+
elsif superclass.respond_to? :mailbox_class
|
|
181
|
+
superclass.mailbox_class
|
|
182
|
+
else
|
|
183
|
+
Celluloid::Mailbox
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def proxy_class(klass = nil)
|
|
188
|
+
if klass
|
|
189
|
+
@proxy_class = klass
|
|
190
|
+
elsif defined?(@proxy_class)
|
|
191
|
+
@proxy_class
|
|
192
|
+
elsif superclass.respond_to? :proxy_class
|
|
193
|
+
superclass.proxy_class
|
|
194
|
+
else
|
|
195
|
+
Celluloid::ActorProxy
|
|
196
|
+
end
|
|
143
197
|
end
|
|
144
198
|
|
|
145
199
|
# Define the default task type for this class
|
|
@@ -165,24 +219,20 @@ module Celluloid
|
|
|
165
219
|
end
|
|
166
220
|
end
|
|
167
221
|
|
|
168
|
-
#
|
|
169
|
-
def
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
elsif superclass.respond_to? :mailbox_factory
|
|
173
|
-
superclass.mailbox_factory
|
|
174
|
-
else
|
|
175
|
-
Mailbox.new
|
|
176
|
-
end
|
|
222
|
+
# Mark methods as running blocks on the receiver
|
|
223
|
+
def execute_block_on_receiver(*methods)
|
|
224
|
+
# A noop method in preparation
|
|
225
|
+
# See https://github.com/celluloid/celluloid/pull/55
|
|
177
226
|
end
|
|
178
227
|
|
|
179
228
|
# Configuration options for Actor#new
|
|
180
229
|
def actor_options
|
|
181
230
|
{
|
|
182
|
-
:mailbox =>
|
|
183
|
-
:
|
|
184
|
-
:
|
|
185
|
-
:
|
|
231
|
+
:mailbox => mailbox_class.new,
|
|
232
|
+
:proxy_class => proxy_class,
|
|
233
|
+
:task_class => task_class,
|
|
234
|
+
:exit_handler => exit_handler,
|
|
235
|
+
:exclusive_methods => defined?(@exclusive_methods) ? @exclusive_methods : nil
|
|
186
236
|
}
|
|
187
237
|
end
|
|
188
238
|
|
|
@@ -194,44 +244,45 @@ module Celluloid
|
|
|
194
244
|
# These are methods we don't want added to the Celluloid singleton but to be
|
|
195
245
|
# defined on all classes that use Celluloid
|
|
196
246
|
module InstanceMethods
|
|
197
|
-
# Obtain the Ruby object the actor is wrapping. This
|
|
198
|
-
#
|
|
199
|
-
# directly with the
|
|
247
|
+
# Obtain the bare Ruby object the actor is wrapping. This is useful for
|
|
248
|
+
# only a limited set of use cases like runtime metaprogramming. Interacting
|
|
249
|
+
# directly with the bare object foregoes any kind of thread safety that
|
|
200
250
|
# Celluloid would ordinarily provide you, and the object is guaranteed to
|
|
201
251
|
# be shared with at least the actor thread. Tread carefully.
|
|
202
|
-
|
|
252
|
+
#
|
|
253
|
+
# Bare objects can be identified via #inspect output:
|
|
254
|
+
#
|
|
255
|
+
# >> actor
|
|
256
|
+
# => #<Celluloid::Actor(Foo:0x3fefcb77c194)>
|
|
257
|
+
# >> actor.bare_object
|
|
258
|
+
# => #<WARNING: BARE CELLULOID OBJECT (Foo:0x3fefcb77c194)>
|
|
259
|
+
#
|
|
260
|
+
def bare_object; self; end
|
|
261
|
+
alias_method :wrapped_object, :bare_object
|
|
262
|
+
|
|
263
|
+
# Are we being invoked in a different thread from our owner?
|
|
264
|
+
def leaked?
|
|
265
|
+
@celluloid_owner != Thread.current[:celluloid_actor]
|
|
266
|
+
end
|
|
203
267
|
|
|
204
268
|
def inspect
|
|
205
|
-
str = "#<
|
|
206
|
-
|
|
207
|
-
|
|
269
|
+
str = "#<"
|
|
270
|
+
|
|
271
|
+
if leaked?
|
|
272
|
+
str << Celluloid::BARE_OBJECT_WARNING_MESSAGE
|
|
273
|
+
else
|
|
274
|
+
str << "Celluloid::ActorProxy"
|
|
208
275
|
end
|
|
209
276
|
|
|
210
|
-
str << "
|
|
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
|
|
277
|
+
str << "(#{self.class}:0x#{object_id.to_s(16)})"
|
|
278
|
+
str << " " unless instance_variables.empty?
|
|
230
279
|
|
|
231
|
-
|
|
280
|
+
instance_variables.each do |ivar|
|
|
281
|
+
next if ivar == Celluloid::OWNER_IVAR
|
|
282
|
+
str << "#{ivar}=#{instance_variable_get(ivar).inspect} "
|
|
232
283
|
end
|
|
233
284
|
|
|
234
|
-
|
|
285
|
+
str.sub!(/\s$/, '>')
|
|
235
286
|
end
|
|
236
287
|
end
|
|
237
288
|
|
|
@@ -240,11 +291,6 @@ module Celluloid
|
|
|
240
291
|
# directly inside of all classes that include Celluloid
|
|
241
292
|
#
|
|
242
293
|
|
|
243
|
-
# Is this actor alive?
|
|
244
|
-
def alive?
|
|
245
|
-
Thread.current[:actor].alive?
|
|
246
|
-
end
|
|
247
|
-
|
|
248
294
|
# Raise an exception in caller context, but stay running
|
|
249
295
|
def abort(cause)
|
|
250
296
|
cause = case cause
|
|
@@ -257,17 +303,17 @@ module Celluloid
|
|
|
257
303
|
|
|
258
304
|
# Terminate this actor
|
|
259
305
|
def terminate
|
|
260
|
-
Thread.current[:
|
|
306
|
+
Thread.current[:celluloid_actor].terminate
|
|
261
307
|
end
|
|
262
308
|
|
|
263
309
|
# Send a signal with the given name to all waiting methods
|
|
264
310
|
def signal(name, value = nil)
|
|
265
|
-
Thread.current[:
|
|
311
|
+
Thread.current[:celluloid_actor].signal name, value
|
|
266
312
|
end
|
|
267
313
|
|
|
268
314
|
# Wait for the given signal
|
|
269
315
|
def wait(name)
|
|
270
|
-
Thread.current[:
|
|
316
|
+
Thread.current[:celluloid_actor].wait name
|
|
271
317
|
end
|
|
272
318
|
|
|
273
319
|
# Obtain the current_actor
|
|
@@ -275,6 +321,11 @@ module Celluloid
|
|
|
275
321
|
Actor.current
|
|
276
322
|
end
|
|
277
323
|
|
|
324
|
+
# Obtain the UUID of the current call chain
|
|
325
|
+
def call_chain_id
|
|
326
|
+
Thread.current[:celluloid_chain_id]
|
|
327
|
+
end
|
|
328
|
+
|
|
278
329
|
# Obtain the name of the current actor
|
|
279
330
|
def name
|
|
280
331
|
Actor.name
|
|
@@ -282,12 +333,12 @@ module Celluloid
|
|
|
282
333
|
|
|
283
334
|
# Obtain the running tasks for this actor
|
|
284
335
|
def tasks
|
|
285
|
-
Thread.current[:
|
|
336
|
+
Thread.current[:celluloid_actor].tasks.to_a
|
|
286
337
|
end
|
|
287
338
|
|
|
288
339
|
# Obtain the Celluloid::Links for this actor
|
|
289
340
|
def links
|
|
290
|
-
Thread.current[:
|
|
341
|
+
Thread.current[:celluloid_actor].links
|
|
291
342
|
end
|
|
292
343
|
|
|
293
344
|
# Watch for exit events from another actor
|
|
@@ -322,7 +373,7 @@ module Celluloid
|
|
|
322
373
|
|
|
323
374
|
# Receive an asynchronous message via the actor protocol
|
|
324
375
|
def receive(timeout = nil, &block)
|
|
325
|
-
actor = Thread.current[:
|
|
376
|
+
actor = Thread.current[:celluloid_actor]
|
|
326
377
|
if actor
|
|
327
378
|
actor.receive(timeout, &block)
|
|
328
379
|
else
|
|
@@ -332,7 +383,7 @@ module Celluloid
|
|
|
332
383
|
|
|
333
384
|
# Sleep letting the actor continue processing messages
|
|
334
385
|
def sleep(interval)
|
|
335
|
-
actor = Thread.current[:
|
|
386
|
+
actor = Thread.current[:celluloid_actor]
|
|
336
387
|
if actor
|
|
337
388
|
actor.sleep(interval)
|
|
338
389
|
else
|
|
@@ -343,23 +394,23 @@ module Celluloid
|
|
|
343
394
|
# Run given block in an exclusive mode: all synchronous calls block the whole
|
|
344
395
|
# actor, not only current message processing.
|
|
345
396
|
def exclusive(&block)
|
|
346
|
-
Thread.current[:
|
|
397
|
+
Thread.current[:celluloid_actor].exclusive(&block)
|
|
347
398
|
end
|
|
348
399
|
|
|
349
400
|
# Are we currently exclusive
|
|
350
401
|
def exclusive?
|
|
351
|
-
actor = Thread.current[:
|
|
402
|
+
actor = Thread.current[:celluloid_actor]
|
|
352
403
|
actor && actor.exclusive?
|
|
353
404
|
end
|
|
354
405
|
|
|
355
406
|
# Call a block after a given interval, returning a Celluloid::Timer object
|
|
356
407
|
def after(interval, &block)
|
|
357
|
-
Thread.current[:
|
|
408
|
+
Thread.current[:celluloid_actor].after(interval, &block)
|
|
358
409
|
end
|
|
359
410
|
|
|
360
411
|
# Call a block every given interval, returning a Celluloid::Timer object
|
|
361
412
|
def every(interval, &block)
|
|
362
|
-
Thread.current[:
|
|
413
|
+
Thread.current[:celluloid_actor].every(interval, &block)
|
|
363
414
|
end
|
|
364
415
|
|
|
365
416
|
# Perform a blocking or computationally intensive action inside an
|
|
@@ -374,18 +425,18 @@ module Celluloid
|
|
|
374
425
|
# Handle async calls within an actor itself
|
|
375
426
|
def async(meth = nil, *args, &block)
|
|
376
427
|
if meth
|
|
377
|
-
Actor.async Thread.current[:
|
|
428
|
+
Actor.async Thread.current[:celluloid_actor].mailbox, meth, *args, &block
|
|
378
429
|
else
|
|
379
|
-
Thread.current[:
|
|
430
|
+
Thread.current[:celluloid_actor].proxy.async
|
|
380
431
|
end
|
|
381
432
|
end
|
|
382
433
|
|
|
383
434
|
# Handle calls to future within an actor itself
|
|
384
435
|
def future(meth = nil, *args, &block)
|
|
385
436
|
if meth
|
|
386
|
-
Actor.future Thread.current[:
|
|
437
|
+
Actor.future Thread.current[:celluloid_actor].mailbox, meth, *args, &block
|
|
387
438
|
else
|
|
388
|
-
Thread.current[:
|
|
439
|
+
Thread.current[:celluloid_actor].proxy.future
|
|
389
440
|
end
|
|
390
441
|
end
|
|
391
442
|
end
|
|
@@ -393,6 +444,7 @@ end
|
|
|
393
444
|
require 'celluloid/version'
|
|
394
445
|
|
|
395
446
|
require 'celluloid/calls'
|
|
447
|
+
require 'celluloid/condition'
|
|
396
448
|
require 'celluloid/core_ext'
|
|
397
449
|
require 'celluloid/cpu_counter'
|
|
398
450
|
require 'celluloid/fiber'
|
|
@@ -406,8 +458,9 @@ require 'celluloid/receivers'
|
|
|
406
458
|
require 'celluloid/registry'
|
|
407
459
|
require 'celluloid/responses'
|
|
408
460
|
require 'celluloid/signals'
|
|
461
|
+
require 'celluloid/stack_dump'
|
|
409
462
|
require 'celluloid/system_events'
|
|
410
|
-
require 'celluloid/
|
|
463
|
+
require 'celluloid/tasks'
|
|
411
464
|
require 'celluloid/thread_handle'
|
|
412
465
|
require 'celluloid/uuid'
|
|
413
466
|
|
|
@@ -422,5 +475,11 @@ require 'celluloid/pool_manager'
|
|
|
422
475
|
require 'celluloid/supervision_group'
|
|
423
476
|
require 'celluloid/supervisor'
|
|
424
477
|
require 'celluloid/notifications'
|
|
478
|
+
require 'celluloid/logging'
|
|
479
|
+
|
|
480
|
+
require 'celluloid/legacy' unless defined?(CELLULOID_FUTURE)
|
|
425
481
|
|
|
426
|
-
|
|
482
|
+
# Configure default systemwide settings
|
|
483
|
+
Celluloid.task_class = Celluloid::TaskFiber
|
|
484
|
+
Celluloid.logger = Logger.new(STDERR)
|
|
485
|
+
Celluloid.shutdown_timeout = 10
|