celluloid 0.12.3 → 0.12.4.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +18 -6
- data/lib/celluloid.rb +55 -25
- data/lib/celluloid/actor.rb +22 -16
- data/lib/celluloid/calls.rb +16 -61
- data/lib/celluloid/core_ext.rb +1 -1
- data/lib/celluloid/internal_pool.rb +3 -3
- data/lib/celluloid/pool_manager.rb +9 -1
- data/lib/celluloid/proxies/actor_proxy.rb +5 -2
- data/lib/celluloid/task.rb +1 -1
- data/lib/celluloid/tasks/task_fiber.rb +10 -6
- data/lib/celluloid/tasks/task_thread.rb +4 -4
- data/lib/celluloid/version.rb +1 -1
- data/spec/support/actor_examples.rb +76 -17
- data/spec/support/example_actor_class.rb +4 -0
- data/spec/support/task_examples.rb +2 -2
- metadata +39 -15
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
=========
|
3
3
|
[](http://travis-ci.org/celluloid/celluloid)
|
4
4
|
[](https://gemnasium.com/celluloid/celluloid)
|
5
|
+
[](https://codeclimate.com/github/celluloid/celluloid)
|
5
6
|
|
6
7
|
> "I thought of objects being like biological cells and/or individual
|
7
8
|
> computers on a network, only able to communicate with messages"
|
@@ -63,11 +64,9 @@ features which make concurrent programming simple, easy, and fun:
|
|
63
64
|
|
64
65
|
You can also build distributed systems with Celluloid using its
|
65
66
|
[sister project DCell](https://github.com/celluloid/dcell). Evented IO similar
|
66
|
-
to EventMachine (with a synchronous API
|
67
|
-
[Celluloid::IO](https://github.com/celluloid/celluloid-io)
|
68
|
-
|
69
|
-
[Please see the Celluloid Wiki](https://github.com/celluloid/celluloid/wiki)
|
70
|
-
for more detailed documentation and usage notes.
|
67
|
+
to EventMachine (with a synchronous API instead of callback/deferrable soup)
|
68
|
+
is available through the [Celluloid::IO](https://github.com/celluloid/celluloid-io)
|
69
|
+
library.
|
71
70
|
|
72
71
|
Like Celluloid? [Join the Google Group](http://groups.google.com/group/celluloid-ruby)
|
73
72
|
or visit us on IRC at #celluloid on freenode
|
@@ -75,7 +74,20 @@ or visit us on IRC at #celluloid on freenode
|
|
75
74
|
### Is It "Production Ready™"?
|
76
75
|
|
77
76
|
Yes, many users are now running Celluloid in production by using
|
78
|
-
[Sidekiq](
|
77
|
+
[Sidekiq](http://sidekiq.org) and [Adhearsion](http://adhearsion.com/)
|
78
|
+
|
79
|
+
Documentation
|
80
|
+
-------------
|
81
|
+
|
82
|
+
[Please see the Celluloid Wiki](https://github.com/celluloid/celluloid/wiki)
|
83
|
+
for more detailed documentation and usage notes.
|
84
|
+
|
85
|
+
The following API documentation is also available:
|
86
|
+
|
87
|
+
* [YARD API documentation](http://rubydoc.info/gems/celluloid/frames)
|
88
|
+
* [Celluloid module (primary API)](http://rubydoc.info/gems/celluloid/Celluloid)
|
89
|
+
* [Celluloid class methods](http://rubydoc.info/gems/celluloid/Celluloid/ClassMethods)
|
90
|
+
* [All Celluloid classes](http://rubydoc.info/gems/celluloid/index)
|
79
91
|
|
80
92
|
Installation
|
81
93
|
------------
|
data/lib/celluloid.rb
CHANGED
@@ -24,7 +24,7 @@ module Celluloid
|
|
24
24
|
|
25
25
|
# Are we currently inside of an actor?
|
26
26
|
def actor?
|
27
|
-
!!Thread.current[:
|
27
|
+
!!Thread.current[:celluloid_actor]
|
28
28
|
end
|
29
29
|
|
30
30
|
# Generate a Universally Unique Identifier
|
@@ -55,7 +55,7 @@ module Celluloid
|
|
55
55
|
def shutdown
|
56
56
|
Timeout.timeout(SHUTDOWN_TIMEOUT) do
|
57
57
|
actors = Actor.all
|
58
|
-
Logger.
|
58
|
+
Logger.debug "Terminating #{actors.size} actors..." if actors.size > 0
|
59
59
|
|
60
60
|
# Attempt to shut down the supervision tree, if available
|
61
61
|
Supervisor.root.terminate if Supervisor.root
|
@@ -75,7 +75,7 @@ module Celluloid
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
-
Logger.
|
78
|
+
Logger.debug "Shutdown completed cleanly"
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
@@ -142,7 +142,7 @@ module Celluloid
|
|
142
142
|
|
143
143
|
# Run an actor in the foreground
|
144
144
|
def run(*args, &block)
|
145
|
-
new(*args, &block)
|
145
|
+
Actor.join(new(*args, &block))
|
146
146
|
end
|
147
147
|
|
148
148
|
# Trap errors from actors we're linked to when they exit
|
@@ -170,6 +170,10 @@ module Celluloid
|
|
170
170
|
def mailbox_class(klass)
|
171
171
|
@mailbox_factory = proc { klass.new }
|
172
172
|
end
|
173
|
+
|
174
|
+
def proxy_class(klass)
|
175
|
+
@proxy_factory = proc { klass }
|
176
|
+
end
|
173
177
|
|
174
178
|
# Define the default task type for this class
|
175
179
|
def task_class(klass = nil)
|
@@ -204,11 +208,22 @@ module Celluloid
|
|
204
208
|
Mailbox.new
|
205
209
|
end
|
206
210
|
end
|
211
|
+
|
212
|
+
def proxy_factory
|
213
|
+
if defined?(@proxy_factory)
|
214
|
+
@proxy_factory.call
|
215
|
+
elsif superclass.respond_to?(:proxy_factory)
|
216
|
+
superclass.proxy_factory
|
217
|
+
else
|
218
|
+
nil
|
219
|
+
end
|
220
|
+
end
|
207
221
|
|
208
222
|
# Configuration options for Actor#new
|
209
223
|
def actor_options
|
210
224
|
{
|
211
225
|
:mailbox => mailbox_factory,
|
226
|
+
:proxy_class => proxy_factory,
|
212
227
|
:exit_handler => exit_handler,
|
213
228
|
:exclusive_methods => @exclusive_methods,
|
214
229
|
:task_class => task_class
|
@@ -239,14 +254,29 @@ module Celluloid
|
|
239
254
|
def bare_object; self; end
|
240
255
|
alias_method :wrapped_object, :bare_object
|
241
256
|
|
257
|
+
# Are we being invoked in a different thread from our owner?
|
258
|
+
def leaked?
|
259
|
+
@celluloid_owner != Thread.current[:celluloid_actor]
|
260
|
+
end
|
261
|
+
|
242
262
|
def inspect
|
243
|
-
str = "
|
244
|
-
|
245
|
-
|
263
|
+
str = "#<"
|
264
|
+
|
265
|
+
if leaked?
|
266
|
+
str << Celluloid::BARE_OBJECT_WARNING_MESSAGE
|
267
|
+
else
|
268
|
+
str << "Celluloid::Actor"
|
269
|
+
end
|
270
|
+
|
271
|
+
str << "(#{self.class}:0x#{object_id.to_s(16)})"
|
272
|
+
str << " " unless instance_variables.empty?
|
273
|
+
|
274
|
+
instance_variables.each do |ivar|
|
275
|
+
next if ivar == Celluloid::OWNER_IVAR
|
276
|
+
str << "#{ivar}=#{instance_variable_get(ivar).inspect} "
|
246
277
|
end
|
247
278
|
|
248
|
-
str
|
249
|
-
str << ">"
|
279
|
+
str.sub!(/\s$/, '>')
|
250
280
|
end
|
251
281
|
|
252
282
|
# Process async calls via method_missing
|
@@ -258,7 +288,7 @@ module Celluloid
|
|
258
288
|
|
259
289
|
call = AsyncCall.new(:__send__, args, block)
|
260
290
|
begin
|
261
|
-
Thread.current[:
|
291
|
+
Thread.current[:celluloid_actor].mailbox << call
|
262
292
|
rescue MailboxError
|
263
293
|
# Silently swallow asynchronous calls to dead actors. There's no way
|
264
294
|
# to reliably generate DeadActorErrors for async calls, so users of
|
@@ -290,17 +320,17 @@ module Celluloid
|
|
290
320
|
|
291
321
|
# Terminate this actor
|
292
322
|
def terminate
|
293
|
-
Thread.current[:
|
323
|
+
Thread.current[:celluloid_actor].terminate
|
294
324
|
end
|
295
325
|
|
296
326
|
# Send a signal with the given name to all waiting methods
|
297
327
|
def signal(name, value = nil)
|
298
|
-
Thread.current[:
|
328
|
+
Thread.current[:celluloid_actor].signal name, value
|
299
329
|
end
|
300
330
|
|
301
331
|
# Wait for the given signal
|
302
332
|
def wait(name)
|
303
|
-
Thread.current[:
|
333
|
+
Thread.current[:celluloid_actor].wait name
|
304
334
|
end
|
305
335
|
|
306
336
|
# Obtain the current_actor
|
@@ -315,12 +345,12 @@ module Celluloid
|
|
315
345
|
|
316
346
|
# Obtain the running tasks for this actor
|
317
347
|
def tasks
|
318
|
-
Thread.current[:
|
348
|
+
Thread.current[:celluloid_actor].tasks.to_a
|
319
349
|
end
|
320
350
|
|
321
351
|
# Obtain the Celluloid::Links for this actor
|
322
352
|
def links
|
323
|
-
Thread.current[:
|
353
|
+
Thread.current[:celluloid_actor].links
|
324
354
|
end
|
325
355
|
|
326
356
|
# Watch for exit events from another actor
|
@@ -355,7 +385,7 @@ module Celluloid
|
|
355
385
|
|
356
386
|
# Receive an asynchronous message via the actor protocol
|
357
387
|
def receive(timeout = nil, &block)
|
358
|
-
actor = Thread.current[:
|
388
|
+
actor = Thread.current[:celluloid_actor]
|
359
389
|
if actor
|
360
390
|
actor.receive(timeout, &block)
|
361
391
|
else
|
@@ -365,7 +395,7 @@ module Celluloid
|
|
365
395
|
|
366
396
|
# Sleep letting the actor continue processing messages
|
367
397
|
def sleep(interval)
|
368
|
-
actor = Thread.current[:
|
398
|
+
actor = Thread.current[:celluloid_actor]
|
369
399
|
if actor
|
370
400
|
actor.sleep(interval)
|
371
401
|
else
|
@@ -376,23 +406,23 @@ module Celluloid
|
|
376
406
|
# Run given block in an exclusive mode: all synchronous calls block the whole
|
377
407
|
# actor, not only current message processing.
|
378
408
|
def exclusive(&block)
|
379
|
-
Thread.current[:
|
409
|
+
Thread.current[:celluloid_actor].exclusive(&block)
|
380
410
|
end
|
381
411
|
|
382
412
|
# Are we currently exclusive
|
383
413
|
def exclusive?
|
384
|
-
actor = Thread.current[:
|
414
|
+
actor = Thread.current[:celluloid_actor]
|
385
415
|
actor && actor.exclusive?
|
386
416
|
end
|
387
417
|
|
388
418
|
# Call a block after a given interval, returning a Celluloid::Timer object
|
389
419
|
def after(interval, &block)
|
390
|
-
Thread.current[:
|
420
|
+
Thread.current[:celluloid_actor].after(interval, &block)
|
391
421
|
end
|
392
422
|
|
393
423
|
# Call a block every given interval, returning a Celluloid::Timer object
|
394
424
|
def every(interval, &block)
|
395
|
-
Thread.current[:
|
425
|
+
Thread.current[:celluloid_actor].every(interval, &block)
|
396
426
|
end
|
397
427
|
|
398
428
|
# Perform a blocking or computationally intensive action inside an
|
@@ -407,18 +437,18 @@ module Celluloid
|
|
407
437
|
# Handle async calls within an actor itself
|
408
438
|
def async(meth = nil, *args, &block)
|
409
439
|
if meth
|
410
|
-
Actor.async Thread.current[:
|
440
|
+
Actor.async Thread.current[:celluloid_actor].mailbox, meth, *args, &block
|
411
441
|
else
|
412
|
-
Thread.current[:
|
442
|
+
Thread.current[:celluloid_actor].proxy.async
|
413
443
|
end
|
414
444
|
end
|
415
445
|
|
416
446
|
# Handle calls to future within an actor itself
|
417
447
|
def future(meth = nil, *args, &block)
|
418
448
|
if meth
|
419
|
-
Actor.future Thread.current[:
|
449
|
+
Actor.future Thread.current[:celluloid_actor].mailbox, meth, *args, &block
|
420
450
|
else
|
421
|
-
Thread.current[:
|
451
|
+
Thread.current[:celluloid_actor].proxy.future
|
422
452
|
end
|
423
453
|
end
|
424
454
|
end
|
data/lib/celluloid/actor.rb
CHANGED
@@ -21,6 +21,7 @@ module Celluloid
|
|
21
21
|
end
|
22
22
|
|
23
23
|
LINKING_TIMEOUT = 5 # linking times out after 5 seconds
|
24
|
+
OWNER_IVAR = :@celluloid_owner # reference to owning actor
|
24
25
|
|
25
26
|
# Actors are Celluloid's concurrency primitive. They're implemented as
|
26
27
|
# normal Ruby objects wrapped in threads which communicate with asynchronous
|
@@ -43,14 +44,14 @@ module Celluloid
|
|
43
44
|
|
44
45
|
# Obtain the current actor
|
45
46
|
def current
|
46
|
-
actor = Thread.current[:
|
47
|
+
actor = Thread.current[:celluloid_actor]
|
47
48
|
raise NotActorError, "not in actor scope" unless actor
|
48
49
|
actor.proxy
|
49
50
|
end
|
50
51
|
|
51
52
|
# Obtain the name of the current actor
|
52
53
|
def name
|
53
|
-
actor = Thread.current[:
|
54
|
+
actor = Thread.current[:celluloid_actor]
|
54
55
|
raise NotActorError, "not in actor scope" unless actor
|
55
56
|
actor.name
|
56
57
|
end
|
@@ -65,7 +66,7 @@ module Celluloid
|
|
65
66
|
raise DeadActorError, "attempted to call a dead actor"
|
66
67
|
end
|
67
68
|
|
68
|
-
if Thread.current[:
|
69
|
+
if Thread.current[:celluloid_task] && !Celluloid.exclusive?
|
69
70
|
Task.suspend(:callwait).value
|
70
71
|
else
|
71
72
|
response = loop do
|
@@ -73,7 +74,7 @@ module Celluloid
|
|
73
74
|
msg.respond_to?(:call) and msg.call == call
|
74
75
|
end
|
75
76
|
break message unless message.is_a?(SystemEvent)
|
76
|
-
Thread.current[:
|
77
|
+
Thread.current[:celluloid_actor].handle_system_event(message)
|
77
78
|
end
|
78
79
|
|
79
80
|
response.value
|
@@ -103,7 +104,7 @@ module Celluloid
|
|
103
104
|
def all
|
104
105
|
actors = []
|
105
106
|
Thread.list.each do |t|
|
106
|
-
actor = t[:
|
107
|
+
actor = t[:celluloid_actor]
|
107
108
|
actors << actor.proxy if actor and actor.respond_to?(:proxy)
|
108
109
|
end
|
109
110
|
actors
|
@@ -112,25 +113,25 @@ module Celluloid
|
|
112
113
|
# Watch for exit events from another actor
|
113
114
|
def monitor(actor)
|
114
115
|
raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
|
115
|
-
Thread.current[:
|
116
|
+
Thread.current[:celluloid_actor].linking_request(actor, :link)
|
116
117
|
end
|
117
118
|
|
118
119
|
# Stop waiting for exit events from another actor
|
119
120
|
def unmonitor(actor)
|
120
121
|
raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
|
121
|
-
Thread.current[:
|
122
|
+
Thread.current[:celluloid_actor].linking_request(actor, :unlink)
|
122
123
|
end
|
123
124
|
|
124
125
|
# Link to another actor
|
125
126
|
def link(actor)
|
126
127
|
monitor actor
|
127
|
-
Thread.current[:
|
128
|
+
Thread.current[:celluloid_actor].links << actor
|
128
129
|
end
|
129
130
|
|
130
131
|
# Unlink from another actor
|
131
132
|
def unlink(actor)
|
132
133
|
unmonitor actor
|
133
|
-
Thread.current[:
|
134
|
+
Thread.current[:celluloid_actor].links.delete actor
|
134
135
|
end
|
135
136
|
|
136
137
|
# Are we monitoring the given actor?
|
@@ -140,7 +141,7 @@ module Celluloid
|
|
140
141
|
|
141
142
|
# Are we bidirectionally linked to the given actor?
|
142
143
|
def linked_to?(actor)
|
143
|
-
monitoring?(actor) && Thread.current[:
|
144
|
+
monitoring?(actor) && Thread.current[:celluloid_actor].links.include?(actor)
|
144
145
|
end
|
145
146
|
|
146
147
|
# Forcibly kill a given actor
|
@@ -163,6 +164,7 @@ module Celluloid
|
|
163
164
|
def initialize(subject, options = {})
|
164
165
|
@subject = subject
|
165
166
|
@mailbox = options[:mailbox] || Mailbox.new
|
167
|
+
@proxy_class = options[:proxy_class] || ActorProxy
|
166
168
|
@exit_handler = options[:exit_handler]
|
167
169
|
@exclusives = options[:exclusive_methods]
|
168
170
|
@task_class = options[:task_class] || Celluloid.task_class
|
@@ -177,12 +179,13 @@ module Celluloid
|
|
177
179
|
@name = nil
|
178
180
|
|
179
181
|
@thread = ThreadHandle.new do
|
180
|
-
Thread.current[:
|
181
|
-
Thread.current[:
|
182
|
+
Thread.current[:celluloid_actor] = self
|
183
|
+
Thread.current[:celluloid_mailbox] = @mailbox
|
182
184
|
run
|
183
185
|
end
|
184
186
|
|
185
|
-
@proxy =
|
187
|
+
@proxy = @proxy_class.new(self)
|
188
|
+
@subject.instance_variable_set(OWNER_IVAR, self)
|
186
189
|
end
|
187
190
|
|
188
191
|
# Run the actor loop
|
@@ -306,7 +309,8 @@ module Celluloid
|
|
306
309
|
|
307
310
|
# Sleep for the given amount of time
|
308
311
|
def sleep(interval)
|
309
|
-
|
312
|
+
task = Thread.current[:celluloid_task]
|
313
|
+
if task && !Celluloid.exclusive?
|
310
314
|
@timers.after(interval) { task.resume }
|
311
315
|
Task.suspend :sleeping
|
312
316
|
else
|
@@ -345,6 +349,8 @@ module Celluloid
|
|
345
349
|
|
346
350
|
# Handle exit events received by this actor
|
347
351
|
def handle_exit_event(event)
|
352
|
+
@links.delete event.actor
|
353
|
+
|
348
354
|
# Run the exit handler if available
|
349
355
|
return @subject.send(@exit_handler, event.actor, event.reason) if @exit_handler
|
350
356
|
|
@@ -366,8 +372,8 @@ module Celluloid
|
|
366
372
|
run_finalizer
|
367
373
|
cleanup exit_event
|
368
374
|
ensure
|
369
|
-
Thread.current[:
|
370
|
-
Thread.current[:
|
375
|
+
Thread.current[:celluloid_actor] = nil
|
376
|
+
Thread.current[:celluloid_mailbox] = nil
|
371
377
|
end
|
372
378
|
|
373
379
|
# Run the user-defined finalizer, if one is set
|
data/lib/celluloid/calls.rb
CHANGED
@@ -7,70 +7,29 @@ module Celluloid
|
|
7
7
|
@method, @arguments, @block = method, arguments, block
|
8
8
|
end
|
9
9
|
|
10
|
-
def check_signature(obj)
|
11
|
-
unless obj.respond_to? @method
|
12
|
-
raise NoMethodError, "undefined method `#{@method}' for #{obj.to_s}"
|
13
|
-
end
|
14
|
-
|
15
|
-
begin
|
16
|
-
arity = obj.method(@method).arity
|
17
|
-
rescue NameError
|
18
|
-
# If the object claims it responds to a method, but it doesn't exist,
|
19
|
-
# then we have to assume method_missing will do what it says
|
20
|
-
@arguments.unshift(@method)
|
21
|
-
@method = :method_missing
|
22
|
-
return
|
23
|
-
end
|
24
|
-
|
25
|
-
if arity >= 0
|
26
|
-
if arguments.size != arity
|
27
|
-
raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{arity})"
|
28
|
-
end
|
29
|
-
elsif arity < -1
|
30
|
-
mandatory_args = -arity - 1
|
31
|
-
if arguments.size < mandatory_args
|
32
|
-
raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{mandatory_args})"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
10
|
end
|
37
11
|
|
38
12
|
# Synchronous calls wait for a response
|
39
13
|
class SyncCall < Call
|
40
14
|
attr_reader :caller, :task
|
41
15
|
|
42
|
-
def initialize(caller, method, arguments = [], block = nil, task = Thread.current[:
|
16
|
+
def initialize(caller, method, arguments = [], block = nil, task = Thread.current[:celluloid_task])
|
43
17
|
super(method, arguments, block)
|
44
18
|
@caller = caller
|
45
19
|
@task = task
|
46
20
|
end
|
47
21
|
|
48
22
|
def dispatch(obj)
|
49
|
-
|
50
|
-
check_signature(obj)
|
51
|
-
rescue => ex
|
52
|
-
respond ErrorResponse.new(self, AbortError.new(ex))
|
53
|
-
return
|
54
|
-
end
|
55
|
-
|
56
|
-
begin
|
57
|
-
result = obj.send @method, *@arguments, &@block
|
58
|
-
rescue Exception => exception
|
59
|
-
# Exceptions that occur during synchronous calls are reraised in the
|
60
|
-
# context of the caller
|
61
|
-
respond ErrorResponse.new(self, exception)
|
62
|
-
|
63
|
-
if exception.is_a? AbortError
|
64
|
-
# Aborting indicates a protocol error on the part of the caller
|
65
|
-
# It should crash the caller, but the exception isn't reraised
|
66
|
-
return
|
67
|
-
else
|
68
|
-
# Otherwise, it's a bug in this actor and should be reraised
|
69
|
-
raise exception
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
23
|
+
result = obj.public_send(@method, *@arguments, &@block)
|
73
24
|
respond SuccessResponse.new(self, result)
|
25
|
+
rescue Exception => ex
|
26
|
+
# Exceptions that occur during synchronous calls are reraised in the
|
27
|
+
# context of the caller
|
28
|
+
respond ErrorResponse.new(self, ex)
|
29
|
+
# Aborting indicates a protocol error on the part of the caller
|
30
|
+
# It should crash the caller, but the exception isn't reraised
|
31
|
+
# Otherwise, it's a bug in this actor and should be reraised
|
32
|
+
ex.is_a?(AbortError) ? nil : raise
|
74
33
|
end
|
75
34
|
|
76
35
|
def cleanup
|
@@ -84,23 +43,19 @@ module Celluloid
|
|
84
43
|
# It's possible the caller exited or crashed before we could send a
|
85
44
|
# response to them.
|
86
45
|
end
|
46
|
+
|
87
47
|
end
|
88
48
|
|
89
49
|
# Asynchronous calls don't wait for a response
|
90
50
|
class AsyncCall < Call
|
91
|
-
def dispatch(obj)
|
92
|
-
begin
|
93
|
-
check_signature(obj)
|
94
|
-
rescue Exception => ex
|
95
|
-
Logger.crash("#{obj.class}: async call `#{@method}' failed!", ex)
|
96
|
-
return
|
97
|
-
end
|
98
51
|
|
99
|
-
|
52
|
+
def dispatch(obj)
|
53
|
+
obj.public_send(@method, *@arguments, &@block)
|
100
54
|
rescue AbortError => ex
|
101
55
|
# Swallow aborted async calls, as they indicate the caller made a mistake
|
102
|
-
Logger.
|
56
|
+
Logger.debug("#{obj.class}: async call `#@method` aborted!\n#{Logger.format_exception(ex.cause)}")
|
103
57
|
end
|
58
|
+
|
104
59
|
end
|
105
|
-
end
|
106
60
|
|
61
|
+
end
|
data/lib/celluloid/core_ext.rb
CHANGED
@@ -6,7 +6,7 @@ class Thread
|
|
6
6
|
|
7
7
|
# Retrieve the mailbox for the current thread or lazily initialize it
|
8
8
|
def self.mailbox
|
9
|
-
current[:
|
9
|
+
current[:celluloid_mailbox] ||= Celluloid::Mailbox.new
|
10
10
|
end
|
11
11
|
|
12
12
|
# Receive a message either as an actor or through the local mailbox
|
@@ -23,7 +23,7 @@ module Celluloid
|
|
23
23
|
end
|
24
24
|
end until thread.status # handle crashed threads
|
25
25
|
|
26
|
-
thread[:
|
26
|
+
thread[:celluloid_queue] << block
|
27
27
|
thread
|
28
28
|
end
|
29
29
|
end
|
@@ -32,7 +32,7 @@ module Celluloid
|
|
32
32
|
def put(thread)
|
33
33
|
@mutex.synchronize do
|
34
34
|
if @pool.size >= @max_idle
|
35
|
-
thread[:
|
35
|
+
thread[:celluloid_queue] << nil
|
36
36
|
else
|
37
37
|
@pool << thread
|
38
38
|
end
|
@@ -54,7 +54,7 @@ module Celluloid
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
thread[:
|
57
|
+
thread[:celluloid_queue] = queue
|
58
58
|
thread
|
59
59
|
end
|
60
60
|
end
|
@@ -80,12 +80,20 @@ module Celluloid
|
|
80
80
|
@size
|
81
81
|
end
|
82
82
|
|
83
|
+
def busy_size
|
84
|
+
@busy.length
|
85
|
+
end
|
86
|
+
|
87
|
+
def idle_size
|
88
|
+
@idle.length
|
89
|
+
end
|
90
|
+
|
83
91
|
# Provision a new worker
|
84
92
|
def __provision_worker
|
85
93
|
while @idle.empty?
|
86
94
|
# Wait for responses from one of the busy workers
|
87
95
|
response = exclusive { receive { |msg| msg.is_a?(Response) } }
|
88
|
-
Thread.current[:
|
96
|
+
Thread.current[:celluloid_actor].handle_message(response)
|
89
97
|
end
|
90
98
|
|
91
99
|
worker = @idle.shift
|
@@ -10,7 +10,10 @@ module Celluloid
|
|
10
10
|
@async_proxy = AsyncProxy.new(actor)
|
11
11
|
@future_proxy = FutureProxy.new(actor)
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
|
+
# allow querying the real class
|
15
|
+
alias :__class__ :class
|
16
|
+
|
14
17
|
def class
|
15
18
|
Actor.call @mailbox, :__send__, :class
|
16
19
|
end
|
@@ -20,7 +23,7 @@ module Celluloid
|
|
20
23
|
end
|
21
24
|
|
22
25
|
def inspect
|
23
|
-
Actor.call(@mailbox, :inspect)
|
26
|
+
Actor.call(@mailbox, :inspect)
|
24
27
|
rescue DeadActorError
|
25
28
|
"#<Celluloid::Actor(#{@klass}) dead>"
|
26
29
|
end
|
data/lib/celluloid/task.rb
CHANGED
@@ -14,7 +14,7 @@ module Celluloid
|
|
14
14
|
|
15
15
|
# Obtain the current task
|
16
16
|
def self.current
|
17
|
-
Thread.current[:
|
17
|
+
Thread.current[:celluloid_task] or raise NotTaskError, "not within a task context"
|
18
18
|
end
|
19
19
|
|
20
20
|
# Suspend the running task, deferring to the scheduler
|
@@ -1,4 +1,6 @@
|
|
1
1
|
module Celluloid
|
2
|
+
class FiberStackError < StandardError; end
|
3
|
+
|
2
4
|
# Tasks with a Fiber backend
|
3
5
|
class TaskFiber
|
4
6
|
attr_reader :type, :status
|
@@ -8,14 +10,14 @@ module Celluloid
|
|
8
10
|
@type = type
|
9
11
|
@status = :new
|
10
12
|
|
11
|
-
actor, mailbox = Thread.current[:
|
13
|
+
actor, mailbox = Thread.current[:celluloid_actor], Thread.current[:celluloid_mailbox]
|
12
14
|
raise NotActorError, "can't create tasks outside of actors" unless actor
|
13
15
|
|
14
16
|
@fiber = Fiber.new do
|
15
17
|
@status = :running
|
16
|
-
Thread.current[:
|
17
|
-
Thread.current[:
|
18
|
-
Thread.current[:
|
18
|
+
Thread.current[:celluloid_actor] = actor
|
19
|
+
Thread.current[:celluloid_mailbox] = mailbox
|
20
|
+
Thread.current[:celluloid_task] = self
|
19
21
|
actor.tasks << self
|
20
22
|
|
21
23
|
begin
|
@@ -43,8 +45,10 @@ module Celluloid
|
|
43
45
|
def resume(value = nil)
|
44
46
|
@fiber.resume value
|
45
47
|
nil
|
46
|
-
rescue
|
47
|
-
raise
|
48
|
+
rescue SystemStackError => ex
|
49
|
+
raise FiberStackError, "#{ex} (please see https://github.com/celluloid/celluloid/wiki/Fiber-stack-errors)"
|
50
|
+
rescue FiberError => ex
|
51
|
+
raise DeadTaskError, "cannot resume a dead task (#{ex})"
|
48
52
|
end
|
49
53
|
|
50
54
|
# Terminate this task
|
@@ -12,16 +12,16 @@ module Celluloid
|
|
12
12
|
@yield_mutex = Mutex.new
|
13
13
|
@yield_cond = ConditionVariable.new
|
14
14
|
|
15
|
-
actor, mailbox = Thread.current[:
|
15
|
+
actor, mailbox = Thread.current[:celluloid_actor], Thread.current[:celluloid_mailbox]
|
16
16
|
raise NotActorError, "can't create tasks outside of actors" unless actor
|
17
17
|
|
18
18
|
@thread = InternalPool.get do
|
19
19
|
begin
|
20
20
|
unless @resume_queue.pop.is_a?(Task::TerminatedError)
|
21
21
|
@status = :running
|
22
|
-
Thread.current[:
|
23
|
-
Thread.current[:
|
24
|
-
Thread.current[:
|
22
|
+
Thread.current[:celluloid_actor] = actor
|
23
|
+
Thread.current[:celluloid_mailbox] = mailbox
|
24
|
+
Thread.current[:celluloid_task] = self
|
25
25
|
actor.tasks << self
|
26
26
|
|
27
27
|
yield
|
data/lib/celluloid/version.rb
CHANGED
@@ -211,21 +211,22 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
211
211
|
actor = actor_class.new "Troy McClure"
|
212
212
|
|
213
213
|
actor.inspect.should_not include Celluloid::BARE_OBJECT_WARNING_MESSAGE
|
214
|
+
actor.inspect_thunk.should_not include Celluloid::BARE_OBJECT_WARNING_MESSAGE
|
214
215
|
actor.wrapped_object.inspect.should include Celluloid::BARE_OBJECT_WARNING_MESSAGE
|
215
216
|
end
|
216
217
|
|
217
|
-
|
218
|
+
context "mocking methods" do
|
218
219
|
let(:actor) { actor_class.new "Troy McClure" }
|
219
220
|
|
220
221
|
before do
|
221
222
|
actor.wrapped_object.should_receive(:external_hello).once.and_return "World"
|
222
223
|
end
|
223
224
|
|
224
|
-
it
|
225
|
+
it "works externally via the proxy" do
|
225
226
|
actor.external_hello.should == "World"
|
226
227
|
end
|
227
228
|
|
228
|
-
it
|
229
|
+
it "works internally when called on self" do
|
229
230
|
actor.internal_hello.should == "World"
|
230
231
|
end
|
231
232
|
end
|
@@ -364,6 +365,18 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
364
365
|
sleep 0.1 # hax to prevent a race between exit handling and the next call
|
365
366
|
chuck.should be_subordinate_lambasted
|
366
367
|
end
|
368
|
+
|
369
|
+
it "unlinks from a dead linked actor" do
|
370
|
+
chuck = supervisor_class.new "Chuck Lorre"
|
371
|
+
chuck.link @charlie
|
372
|
+
|
373
|
+
expect do
|
374
|
+
@charlie.crash
|
375
|
+
end.to raise_exception(ExampleCrash)
|
376
|
+
|
377
|
+
sleep 0.1 # hax to prevent a race between exit handling and the next call
|
378
|
+
chuck.links.count.should == 0
|
379
|
+
end
|
367
380
|
end
|
368
381
|
|
369
382
|
context :signaling do
|
@@ -440,12 +453,30 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
440
453
|
@tasks = []
|
441
454
|
end
|
442
455
|
|
443
|
-
def
|
444
|
-
|
445
|
-
|
446
|
-
|
456
|
+
def log_task(task)
|
457
|
+
@tasks << task
|
458
|
+
end
|
459
|
+
|
460
|
+
def exclusive_with_block_log_task(task)
|
461
|
+
exclusive do
|
462
|
+
sleep Celluloid::TIMER_QUANTUM
|
463
|
+
log_task(task)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def exclusive_log_task(task)
|
468
|
+
sleep Celluloid::TIMER_QUANTUM
|
469
|
+
log_task(task)
|
470
|
+
end
|
471
|
+
exclusive :exclusive_log_task
|
472
|
+
|
473
|
+
def check_not_exclusive
|
474
|
+
Celluloid.exclusive?
|
475
|
+
end
|
476
|
+
|
477
|
+
def check_exclusive
|
478
|
+
exclusive { Celluloid.exclusive? }
|
447
479
|
end
|
448
|
-
exclusive :exclusive_example
|
449
480
|
|
450
481
|
def nested_exclusive_example
|
451
482
|
exclusive { exclusive { nil }; Celluloid.exclusive? }
|
@@ -453,20 +484,28 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
453
484
|
end.new
|
454
485
|
end
|
455
486
|
|
456
|
-
it "
|
457
|
-
subject.
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
subject.nested_exclusive_example.should be_true
|
487
|
+
it "executes methods in the proper order with block form" do
|
488
|
+
subject.async.exclusive_with_block_log_task(:one)
|
489
|
+
subject.async.log_task(:two)
|
490
|
+
sleep Celluloid::TIMER_QUANTUM * 2
|
491
|
+
subject.tasks.should == [:one, :two]
|
462
492
|
end
|
463
493
|
|
464
|
-
it "executes the
|
465
|
-
subject.
|
466
|
-
subject.
|
494
|
+
it "executes methods in the proper order with a class-level annotation" do
|
495
|
+
subject.async.exclusive_log_task :one
|
496
|
+
subject.async.log_task :two
|
467
497
|
sleep Celluloid::TIMER_QUANTUM * 2
|
468
498
|
subject.tasks.should == [:one, :two]
|
469
499
|
end
|
500
|
+
|
501
|
+
it "knows when it's in exclusive mode" do
|
502
|
+
subject.check_not_exclusive.should be_false
|
503
|
+
subject.check_exclusive.should be_true
|
504
|
+
end
|
505
|
+
|
506
|
+
it "remains in exclusive mode inside nested blocks" do
|
507
|
+
subject.nested_exclusive_example.should be_true
|
508
|
+
end
|
470
509
|
end
|
471
510
|
|
472
511
|
context "exclusive classes" do
|
@@ -677,6 +716,26 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
677
716
|
end
|
678
717
|
end
|
679
718
|
|
719
|
+
context :proxy_class do
|
720
|
+
class ExampleProxy < Celluloid::ActorProxy; end
|
721
|
+
|
722
|
+
subject do
|
723
|
+
Class.new do
|
724
|
+
include included_module
|
725
|
+
proxy_class ExampleProxy
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
it "uses user-specified proxy" do
|
730
|
+
subject.new.__class__.should == ExampleProxy
|
731
|
+
end
|
732
|
+
|
733
|
+
it "retains custom proxy when subclassed" do
|
734
|
+
subclass = Class.new(subject)
|
735
|
+
subclass.new.__class__.should == ExampleProxy
|
736
|
+
end
|
737
|
+
end
|
738
|
+
|
680
739
|
context :use_mailbox do
|
681
740
|
class ExampleMailbox < Celluloid::Mailbox; end
|
682
741
|
|
@@ -14,11 +14,11 @@ shared_context "a Celluloid Task" do |task_class|
|
|
14
14
|
subject { task_class.new(task_type) { Celluloid::Task.suspend(suspend_state) } }
|
15
15
|
|
16
16
|
before :each do
|
17
|
-
Thread.current[:
|
17
|
+
Thread.current[:celluloid_actor] = actor
|
18
18
|
end
|
19
19
|
|
20
20
|
after :each do
|
21
|
-
Thread.current[:
|
21
|
+
Thread.current[:celluloid_actor] = nil
|
22
22
|
end
|
23
23
|
|
24
24
|
it "begins with status :new" do
|
metadata
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: celluloid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.12.
|
5
|
-
prerelease:
|
4
|
+
version: 0.12.4.pre
|
5
|
+
prerelease: 7
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Tony Arcieri
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: timers
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,15 @@ dependencies:
|
|
21
21
|
version: 1.0.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.0
|
25
30
|
- !ruby/object:Gem::Dependency
|
26
31
|
name: facter
|
27
|
-
requirement:
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
28
33
|
none: false
|
29
34
|
requirements:
|
30
35
|
- - ! '>='
|
@@ -32,10 +37,15 @@ dependencies:
|
|
32
37
|
version: 1.6.12
|
33
38
|
type: :runtime
|
34
39
|
prerelease: false
|
35
|
-
version_requirements:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.6.12
|
36
46
|
- !ruby/object:Gem::Dependency
|
37
47
|
name: rake
|
38
|
-
requirement:
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
39
49
|
none: false
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
@@ -43,10 +53,15 @@ dependencies:
|
|
43
53
|
version: '0'
|
44
54
|
type: :development
|
45
55
|
prerelease: false
|
46
|
-
version_requirements:
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
47
62
|
- !ruby/object:Gem::Dependency
|
48
63
|
name: rspec
|
49
|
-
requirement:
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
50
65
|
none: false
|
51
66
|
requirements:
|
52
67
|
- - ! '>='
|
@@ -54,10 +69,15 @@ dependencies:
|
|
54
69
|
version: '0'
|
55
70
|
type: :development
|
56
71
|
prerelease: false
|
57
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
58
78
|
- !ruby/object:Gem::Dependency
|
59
79
|
name: benchmark_suite
|
60
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
61
81
|
none: false
|
62
82
|
requirements:
|
63
83
|
- - ! '>='
|
@@ -65,7 +85,12 @@ dependencies:
|
|
65
85
|
version: '0'
|
66
86
|
type: :development
|
67
87
|
prerelease: false
|
68
|
-
version_requirements:
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
69
94
|
description: Celluloid enables people to build concurrent programs out of concurrent
|
70
95
|
objects just as easily as they build sequential programs out of sequential objects
|
71
96
|
email:
|
@@ -140,9 +165,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
165
|
version: 1.3.6
|
141
166
|
requirements: []
|
142
167
|
rubyforge_project:
|
143
|
-
rubygems_version: 1.8.
|
168
|
+
rubygems_version: 1.8.24
|
144
169
|
signing_key:
|
145
170
|
specification_version: 3
|
146
171
|
summary: Actor-based concurrent object framework for Ruby
|
147
172
|
test_files: []
|
148
|
-
has_rdoc:
|