celluloid 0.15.2 → 0.16.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +20 -0
  3. data/README.md +25 -2
  4. data/lib/celluloid/actor.rb +88 -147
  5. data/lib/celluloid/actor_system.rb +107 -0
  6. data/lib/celluloid/call_chain.rb +1 -1
  7. data/lib/celluloid/calls.rb +16 -16
  8. data/lib/celluloid/cell.rb +89 -0
  9. data/lib/celluloid/condition.rb +25 -8
  10. data/lib/celluloid/cpu_counter.rb +28 -18
  11. data/lib/celluloid/evented_mailbox.rb +10 -16
  12. data/lib/celluloid/exceptions.rb +23 -0
  13. data/lib/celluloid/future.rb +1 -1
  14. data/lib/celluloid/handlers.rb +41 -0
  15. data/lib/celluloid/internal_pool.rb +49 -40
  16. data/lib/celluloid/logger.rb +30 -0
  17. data/lib/celluloid/logging/incident_logger.rb +1 -1
  18. data/lib/celluloid/mailbox.rb +35 -31
  19. data/lib/celluloid/method.rb +8 -0
  20. data/lib/celluloid/pool_manager.rb +19 -2
  21. data/lib/celluloid/probe.rb +73 -0
  22. data/lib/celluloid/properties.rb +2 -2
  23. data/lib/celluloid/proxies/actor_proxy.rb +9 -41
  24. data/lib/celluloid/proxies/cell_proxy.rb +68 -0
  25. data/lib/celluloid/proxies/sync_proxy.rb +1 -1
  26. data/lib/celluloid/receivers.rb +5 -13
  27. data/lib/celluloid/registry.rb +1 -8
  28. data/{spec/support → lib/celluloid/rspec}/actor_examples.rb +58 -15
  29. data/{spec/support → lib/celluloid/rspec}/mailbox_examples.rb +9 -3
  30. data/{spec/support → lib/celluloid/rspec}/task_examples.rb +2 -0
  31. data/lib/celluloid/rspec.rb +4 -3
  32. data/lib/celluloid/stack_dump.rb +34 -11
  33. data/lib/celluloid/supervision_group.rb +26 -14
  34. data/lib/celluloid/tasks/task_fiber.rb +6 -0
  35. data/lib/celluloid/tasks/task_thread.rb +2 -1
  36. data/lib/celluloid/tasks.rb +28 -9
  37. data/lib/celluloid/thread_handle.rb +2 -2
  38. data/lib/celluloid.rb +68 -73
  39. data/spec/celluloid/actor_spec.rb +1 -1
  40. data/spec/celluloid/actor_system_spec.rb +69 -0
  41. data/spec/celluloid/block_spec.rb +1 -1
  42. data/spec/celluloid/calls_spec.rb +1 -1
  43. data/spec/celluloid/condition_spec.rb +14 -3
  44. data/spec/celluloid/cpu_counter_spec.rb +82 -0
  45. data/spec/celluloid/fsm_spec.rb +1 -1
  46. data/spec/celluloid/future_spec.rb +1 -1
  47. data/spec/celluloid/notifications_spec.rb +1 -1
  48. data/spec/celluloid/pool_spec.rb +34 -1
  49. data/spec/celluloid/probe_spec.rb +121 -0
  50. data/spec/celluloid/registry_spec.rb +6 -6
  51. data/spec/celluloid/stack_dump_spec.rb +37 -8
  52. data/spec/celluloid/supervision_group_spec.rb +7 -1
  53. data/spec/celluloid/supervisor_spec.rb +12 -1
  54. data/spec/celluloid/tasks/task_fiber_spec.rb +1 -1
  55. data/spec/celluloid/tasks/task_thread_spec.rb +1 -1
  56. data/spec/celluloid/thread_handle_spec.rb +7 -3
  57. data/spec/celluloid/timer_spec.rb +48 -0
  58. data/spec/spec_helper.rb +20 -7
  59. metadata +51 -26
  60. /data/{spec/support → lib/celluloid/rspec}/example_actor_class.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f22a9c7d89b293484d7dcbe03cda32d7055ee42a
4
- data.tar.gz: a13abc0b96f0e4931f6ff20457f9665282a4d08b
3
+ metadata.gz: 6b9e9584ba72b44bbb2ce23372b0b4b86f2f3687
4
+ data.tar.gz: 10ec35b6465a1025defe4a697c3108f1982b5f77
5
5
  SHA512:
6
- metadata.gz: fba64fd21b7f58cf3fab69cdcfe889b4b95478a5bcad61557b1376e3965bc73583eeaf215be7003e886cc0fdd6a239aa79dd3738cd6ad831ce1c114435da4ff0
7
- data.tar.gz: fea6248e5f4e1931485673a458182ca86f380820b2e18acd7d90618203f8c6a193bdf33213161d397153dd43485bb4e1907ff65fb9efd14cc7c6a70cfcb4c309
6
+ metadata.gz: 9394c33b6d7eff1efa0e0f78d24ff5578ce208dac695d0b7b806c999b47a456253c670783599ae448cb9bb3f6bf455be4ac66705fb399d8e53c3adb72a894250
7
+ data.tar.gz: f3e2659328cb38517ec107ad32e7c08d8aaa40fece3d74213273d50f703a6cb0878a43616d933b41fb6d061d928f02ba68850d269be32107062bc5e7b162bcc2
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011-2014 Tony Arcieri
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -94,6 +94,28 @@ The following API documentation is also available:
94
94
  * [Celluloid class methods](http://rubydoc.info/gems/celluloid/Celluloid/ClassMethods)
95
95
  * [All Celluloid classes](http://rubydoc.info/gems/celluloid/index)
96
96
 
97
+ Related Projects
98
+ ----------------
99
+
100
+ Celluloid is the parent project of a related ecosystem of other projects. If you
101
+ like Celluloid we definitely recommend you check them out:
102
+
103
+ * [Celluloid::IO][celluloid-io]: "Evented" IO support for Celluloid actors
104
+ * [Celluloid::ZMQ][celluloid-zmq]: "Evented" 0MQ support for Celluloid actors
105
+ * [DCell][dcell]: The Celluloid actor protocol distributed over 0MQ
106
+ * [Reel][reel]: An "evented" web server based on Celluloid::IO
107
+ * [Lattice][lattice]: A concurrent realtime web framework based on Celluloid::IO
108
+ * [nio4r][nio4r]: "New IO for Ruby": high performance IO selectors
109
+ * [Timers][timers]: A generic Ruby timer library for event-based systems
110
+
111
+ [celluloid-io]: https://github.com/celluloid/celluloid-io/
112
+ [celluloid-zmq]: https://github.com/celluloid/celluloid-zmq/
113
+ [dcell]: https://github.com/celluloid/dcell/
114
+ [reel]: https://github.com/celluloid/reel/
115
+ [lattice]: https://github.com/celluloid/lattice/
116
+ [nio4r]: https://github.com/celluloid/nio4r/
117
+ [timers]: https://github.com/celluloid/timers/
118
+
97
119
  Installation
98
120
  ------------
99
121
 
@@ -131,7 +153,8 @@ Celluloid requires Ruby 1.9 mode on all interpreters.
131
153
  Additional Reading
132
154
  ------------------
133
155
 
134
- * [Concurrent Object-Oriented Programming in Python with ATOM](http://python.org/workshops/1997-10/proceedings/atom/):
156
+ * [Concurrent Object-Oriented Programming in Python with
157
+ ATOM](http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=11A3EACE78AAFF6D6D62A64118AFCA7C?doi=10.1.1.47.5074&rep=rep1&type=pdf):
135
158
  a similar system to Celluloid written in Python
136
159
 
137
160
  Contributing to Celluloid
@@ -145,5 +168,5 @@ Contributing to Celluloid
145
168
  License
146
169
  -------
147
170
 
148
- Copyright (c) 2013 Tony Arcieri. Distributed under the MIT License. See
171
+ Copyright (c) 2011-2014 Tony Arcieri. Distributed under the MIT License. See
149
172
  LICENSE.txt for further details.
@@ -1,56 +1,28 @@
1
+
1
2
  require 'timers'
2
3
 
3
4
  module Celluloid
4
- # Don't do Actor-like things outside Actor scope
5
- class NotActorError < Celluloid::Error; end
6
-
7
- # Trying to do something to a dead actor
8
- class DeadActorError < Celluloid::Error; end
9
-
10
- # A timeout occured before the given request could complete
11
- class TimeoutError < Celluloid::Error; end
12
-
13
- # The sender made an error, not the current actor
14
- class AbortError < Celluloid::Error
15
- attr_reader :cause
16
-
17
- def initialize(cause)
18
- @cause = cause
19
- super "caused by #{cause.inspect}: #{cause.to_s}"
20
- end
21
- end
22
-
23
- LINKING_TIMEOUT = 5 # linking times out after 5 seconds
24
- OWNER_IVAR = :@celluloid_owner # reference to owning actor
25
-
26
5
  # Actors are Celluloid's concurrency primitive. They're implemented as
27
6
  # normal Ruby objects wrapped in threads which communicate with asynchronous
28
7
  # messages.
29
8
  class Actor
30
- attr_reader :subject, :proxy, :tasks, :links, :mailbox, :thread, :name
9
+ attr_reader :behavior, :proxy, :tasks, :links, :mailbox, :thread, :name, :timers
10
+ attr_writer :exit_handler
31
11
 
32
12
  class << self
33
13
  extend Forwardable
34
14
 
35
- def_delegators "Celluloid::Registry.root", :[], :[]=
36
-
37
- def registered
38
- Registry.root.names
39
- end
40
-
41
- def clear_registry
42
- Registry.root.clear
43
- end
15
+ def_delegators "Celluloid.actor_system", :[], :[]=, :delete, :registered, :clear_registry
44
16
 
45
17
  # Obtain the current actor
46
18
  def current
47
19
  actor = Thread.current[:celluloid_actor]
48
20
  raise NotActorError, "not in actor scope" unless actor
49
- actor.proxy
21
+ actor.behavior_proxy
50
22
  end
51
23
 
52
24
  # Obtain the name of the current actor
53
- def name
25
+ def registered_name
54
26
  actor = Thread.current[:celluloid_actor]
55
27
  raise NotActorError, "not in actor scope" unless actor
56
28
  actor.name
@@ -76,12 +48,7 @@ module Celluloid
76
48
 
77
49
  # Obtain all running actors in the system
78
50
  def all
79
- actors = []
80
- Celluloid.internal_pool.each do |t|
81
- next unless t.role == :actor
82
- actors << t.actor.proxy if t.actor && t.actor.respond_to?(:proxy)
83
- end
84
- actors
51
+ Celluloid.actor_system.running
85
52
  end
86
53
 
87
54
  # Watch for exit events from another actor
@@ -131,34 +98,44 @@ module Celluloid
131
98
  end
132
99
  end
133
100
 
134
- # Wrap the given subject with an Actor
135
- def initialize(subject, options = {})
136
- @subject = subject
101
+ def initialize(behavior, options)
102
+ @behavior = behavior
137
103
 
104
+ @actor_system = options.fetch(:actor_system)
138
105
  @mailbox = options.fetch(:mailbox_class, Mailbox).new
139
106
  @mailbox.max_size = options.fetch(:mailbox_size, nil)
140
107
 
141
108
  @task_class = options[:task_class] || Celluloid.task_class
142
- @exit_handler = options[:exit_handler]
143
- @exclusives = options[:exclusive_methods]
144
- @receiver_block_executions = options[:receiver_block_executions]
109
+ @exit_handler = method(:default_exit_handler)
110
+ @exclusive = options.fetch(:exclusive, false)
145
111
 
146
112
  @tasks = TaskSet.new
147
113
  @links = Links.new
148
114
  @signals = Signals.new
149
- @receivers = Receivers.new
150
- @timers = Timers.new
151
- @running = true
152
- @exclusive = false
115
+ @timers = Timers::Group.new
116
+ @receivers = Receivers.new(@timers)
117
+ @handlers = Handlers.new
118
+ @running = false
153
119
  @name = nil
154
120
 
155
- @thread = ThreadHandle.new(:actor) do
121
+ handle(SystemEvent) do |message|
122
+ handle_system_event message
123
+ end
124
+ end
125
+
126
+ def start
127
+ @running = true
128
+ @thread = ThreadHandle.new(@actor_system, :actor) do
156
129
  setup_thread
157
130
  run
158
131
  end
159
132
 
160
- @proxy = (options[:proxy_class] || ActorProxy).new(self)
161
- @subject.instance_variable_set(OWNER_IVAR, self)
133
+ @proxy = ActorProxy.new(@thread, @mailbox)
134
+ Celluloid::Probe.actor_created(self) if $CELLULOID_MONITORING
135
+ end
136
+
137
+ def behavior_proxy
138
+ @behavior.proxy
162
139
  end
163
140
 
164
141
  def setup_thread
@@ -168,18 +145,20 @@ module Celluloid
168
145
 
169
146
  # Run the actor loop
170
147
  def run
171
- begin
172
- while @running
173
- if message = @mailbox.receive(timeout_interval)
174
- handle_message message
175
- else
176
- # No message indicates a timeout
177
- @timers.fire
178
- @receivers.fire_timers
148
+ while @running
149
+ begin
150
+ @timers.wait do |interval|
151
+ interval = 0 if interval and interval < 0
152
+
153
+ if message = @mailbox.check(interval)
154
+ handle_message(message)
155
+
156
+ break unless @running
157
+ end
179
158
  end
159
+ rescue MailboxShutdown
160
+ @running = false
180
161
  end
181
- rescue MailboxShutdown
182
- # If the mailbox detects shutdown, exit the actor
183
162
  end
184
163
 
185
164
  shutdown
@@ -196,31 +175,35 @@ module Celluloid
196
175
  # Perform a linking request with another actor
197
176
  def linking_request(receiver, type)
198
177
  Celluloid.exclusive do
199
- start_time = Time.now
200
178
  receiver.mailbox << LinkingRequest.new(Actor.current, type)
201
179
  system_events = []
202
180
 
203
- loop do
204
- wait_interval = start_time + LINKING_TIMEOUT - Time.now
205
- message = @mailbox.receive(wait_interval) do |msg|
206
- msg.is_a?(LinkingResponse) &&
207
- msg.actor.mailbox.address == receiver.mailbox.address &&
208
- msg.type == type
181
+ Timers::Wait.for(LINKING_TIMEOUT) do |remaining|
182
+ begin
183
+ message = @mailbox.receive(remaining) do |msg|
184
+ msg.is_a?(LinkingResponse) &&
185
+ msg.actor.mailbox.address == receiver.mailbox.address &&
186
+ msg.type == type
187
+ end
188
+ rescue TimeoutError
189
+ next # IO reactor did something, no message in queue yet.
209
190
  end
210
191
 
211
- case message
212
- when LinkingResponse
192
+ if message.instance_of? LinkingResponse
193
+ Celluloid::Probe.actors_linked(self, receiver) if $CELLULOID_MONITORING
194
+
213
195
  # We're done!
214
- system_events.each { |ev| handle_system_event(ev) }
196
+ system_events.each { |ev| @mailbox << ev }
197
+
215
198
  return
216
- when NilClass
217
- raise TimeoutError, "linking timeout of #{LINKING_TIMEOUT} seconds exceeded"
218
- when SystemEvent
199
+ elsif message.is_a? SystemEvent
219
200
  # Queue up pending system events to be processed after we've successfully linked
220
201
  system_events << message
221
- else raise 'wtf'
202
+ else raise "Unexpected message type: #{message.class}. Expected LinkingResponse, NilClass, SystemEvent."
222
203
  end
223
204
  end
205
+
206
+ raise TimeoutError, "linking timeout of #{LINKING_TIMEOUT} seconds exceeded"
224
207
  end
225
208
  end
226
209
 
@@ -234,6 +217,10 @@ module Celluloid
234
217
  @signals.wait name
235
218
  end
236
219
 
220
+ def handle(*patterns, &block)
221
+ @handlers.handle(*patterns, &block)
222
+ end
223
+
237
224
  # Receive an asynchronous message
238
225
  def receive(timeout = nil, &block)
239
226
  loop do
@@ -244,20 +231,6 @@ module Celluloid
244
231
  end
245
232
  end
246
233
 
247
- # How long to wait until the next timer fires
248
- def timeout_interval
249
- i1 = @timers.wait_interval
250
- i2 = @receivers.wait_interval
251
-
252
- if i1 and i2
253
- i1 < i2 ? i1 : i2
254
- elsif i1
255
- i1
256
- else
257
- i2
258
- end
259
- end
260
-
261
234
  # Schedule a block to run at the given time
262
235
  def after(interval, &block)
263
236
  @timers.after(interval) { task(:timer, &block) }
@@ -304,28 +277,7 @@ module Celluloid
304
277
 
305
278
  # Handle standard low-priority messages
306
279
  def handle_message(message)
307
- case message
308
- when SystemEvent
309
- handle_system_event message
310
- when Call
311
- meth = message.method
312
- if meth == :__send__
313
- meth = message.arguments.first
314
- end
315
- if @receiver_block_executions && meth
316
- if @receiver_block_executions.include?(meth.to_sym)
317
- message.execute_block_on_receiver
318
- end
319
- end
320
-
321
- task(:call, :method_name => meth, :dangerous_suspend => meth == :initialize) {
322
- message.dispatch(@subject)
323
- }
324
- when BlockCall
325
- task(:invoke_block) { message.dispatch }
326
- when BlockResponse, Response
327
- message.dispatch
328
- else
280
+ unless @handlers.handle_message(message)
329
281
  unless @receivers.handle_message(message)
330
282
  Logger.debug "Discarded message (unhandled): #{message}" if $CELLULOID_DEBUG
331
283
  end
@@ -335,17 +287,19 @@ module Celluloid
335
287
 
336
288
  # Handle high-priority system event messages
337
289
  def handle_system_event(event)
338
- case event
339
- when ExitEvent
340
- task(:exit_handler, :method_name => @exit_handler) { handle_exit_event event }
341
- when LinkingRequest
290
+ if event.instance_of? ExitEvent
291
+ handle_exit_event(event)
292
+ elsif event.instance_of? LinkingRequest
342
293
  event.process(links)
343
- when NamingRequest
294
+ elsif event.instance_of? NamingRequest
344
295
  @name = event.name
345
- when TerminationRequest
296
+ Celluloid::Probe.actor_named(self) if $CELLULOID_MONITORING
297
+ elsif event.instance_of? TerminationRequest
346
298
  terminate
347
- when SignalConditionRequest
299
+ elsif event.instance_of? SignalConditionRequest
348
300
  event.call
301
+ else
302
+ Logger.debug "Discarded message (unhandled): #{message}" if $CELLULOID_DEBUG
349
303
  end
350
304
  end
351
305
 
@@ -353,47 +307,34 @@ module Celluloid
353
307
  def handle_exit_event(event)
354
308
  @links.delete event.actor
355
309
 
356
- # Run the exit handler if available
357
- return @subject.send(@exit_handler, event.actor, event.reason) if @exit_handler
310
+ @exit_handler.call(event)
311
+ end
358
312
 
359
- # Reraise exceptions from linked actors
360
- # If no reason is given, actor terminated cleanly
313
+ def default_exit_handler(event)
361
314
  raise event.reason if event.reason
362
315
  end
363
316
 
364
317
  # Handle any exceptions that occur within a running actor
365
318
  def handle_crash(exception)
366
- Logger.crash("#{@subject.class} crashed!", exception)
367
- shutdown ExitEvent.new(@proxy, exception)
319
+ # TODO: add meta info
320
+ Logger.crash("Actor crashed!", exception)
321
+ shutdown ExitEvent.new(behavior_proxy, exception)
368
322
  rescue => ex
369
- Logger.crash("#{@subject.class}: ERROR HANDLER CRASHED!", ex)
323
+ Logger.crash("ERROR HANDLER CRASHED!", ex)
370
324
  end
371
325
 
372
326
  # Handle cleaning up this actor after it exits
373
- def shutdown(exit_event = ExitEvent.new(@proxy))
374
- run_finalizer
327
+ def shutdown(exit_event = ExitEvent.new(behavior_proxy))
328
+ @behavior.shutdown
375
329
  cleanup exit_event
376
330
  ensure
377
331
  Thread.current[:celluloid_actor] = nil
378
332
  Thread.current[:celluloid_mailbox] = nil
379
333
  end
380
334
 
381
- # Run the user-defined finalizer, if one is set
382
- def run_finalizer
383
- finalizer = @subject.class.finalizer
384
- return unless finalizer && @subject.respond_to?(finalizer, true)
385
-
386
- task(:finalizer, :method_name => finalizer, :dangerous_suspend => true) do
387
- begin
388
- @subject.__send__(finalizer)
389
- rescue => ex
390
- Logger.crash("#{@subject.class}#finalize crashed!", ex)
391
- end
392
- end
393
- end
394
-
395
335
  # Clean up after this actor
396
336
  def cleanup(exit_event)
337
+ Celluloid::Probe.actor_died(self) if $CELLULOID_MONITORING
397
338
  @mailbox.shutdown
398
339
  @links.each do |actor|
399
340
  if actor.mailbox.alive?
@@ -401,16 +342,16 @@ module Celluloid
401
342
  end
402
343
  end
403
344
 
404
- tasks.to_a.each { |task| task.terminate }
345
+ tasks.to_a.each(&:terminate)
405
346
  rescue => ex
406
- Logger.crash("#{@subject.class}: CLEANUP CRASHED!", ex)
347
+ # TODO: metadata
348
+ Logger.crash("CLEANUP CRASHED!", ex)
407
349
  end
408
350
 
409
351
  # Run a method inside a task unless it's exclusive
410
352
  def task(task_type, meta = nil)
411
- method_name = meta && meta.fetch(:method_name, nil)
412
353
  @task_class.new(task_type, meta) {
413
- if @exclusives && (@exclusives == :all || (method_name && @exclusives.include?(method_name.to_sym)))
354
+ if @exclusive
414
355
  Celluloid.exclusive { yield }
415
356
  else
416
357
  yield
@@ -0,0 +1,107 @@
1
+ module Celluloid
2
+ class ActorSystem
3
+ extend Forwardable
4
+
5
+ def initialize
6
+ @internal_pool = InternalPool.new
7
+ @registry = Registry.new
8
+ end
9
+ attr_reader :registry
10
+
11
+ # Launch default services
12
+ # FIXME: We should set up the supervision hierarchy here
13
+ def start
14
+ within do
15
+ Celluloid::Notifications::Fanout.supervise_as :notifications_fanout
16
+ Celluloid::IncidentReporter.supervise_as :default_incident_reporter, STDERR
17
+ end
18
+ true
19
+ end
20
+
21
+ def within
22
+ old = Thread.current[:celluloid_actor_system]
23
+ Thread.current[:celluloid_actor_system] = self
24
+ yield
25
+ ensure
26
+ Thread.current[:celluloid_actor_system] = old
27
+ end
28
+
29
+ def get_thread
30
+ @internal_pool.get do
31
+ Thread.current[:celluloid_actor_system] = self
32
+ yield
33
+ end
34
+ end
35
+
36
+ def stack_dump
37
+ Celluloid::StackDump.new(@internal_pool)
38
+ end
39
+
40
+ def_delegators "@registry", :[], :get, :[]=, :set, :delete
41
+
42
+ def registered
43
+ @registry.names
44
+ end
45
+
46
+ def clear_registry
47
+ @registry.clear
48
+ end
49
+
50
+ def running
51
+ actors = []
52
+ @internal_pool.each do |t|
53
+ next unless t.role == :actor
54
+ actors << t.actor.behavior_proxy if t.actor && t.actor.respond_to?(:behavior_proxy)
55
+ end
56
+ actors
57
+ end
58
+
59
+ def running?
60
+ @internal_pool.running?
61
+ end
62
+
63
+ # Shut down all running actors
64
+ def shutdown
65
+ actors = running
66
+ Timeout.timeout(shutdown_timeout) do
67
+ Logger.debug "Terminating #{actors.size} #{(actors.size > 1) ? 'actors' : 'actor'}..." if actors.size > 0
68
+
69
+ # Actors cannot self-terminate, you must do it for them
70
+ actors.each do |actor|
71
+ begin
72
+ actor.terminate!
73
+ rescue DeadActorError
74
+ end
75
+ end
76
+
77
+ actors.each do |actor|
78
+ begin
79
+ Actor.join(actor)
80
+ rescue DeadActorError
81
+ end
82
+ end
83
+
84
+ @internal_pool.shutdown
85
+ end
86
+ rescue Timeout::Error
87
+ Logger.error("Couldn't cleanly terminate all actors in #{shutdown_timeout} seconds!")
88
+ actors.each do |actor|
89
+ begin
90
+ Actor.kill(actor)
91
+ rescue DeadActorError, MailboxDead
92
+ end
93
+ end
94
+ ensure
95
+ @internal_pool.kill
96
+ clear_registry
97
+ end
98
+
99
+ def assert_inactive
100
+ @internal_pool.assert_inactive
101
+ end
102
+
103
+ def shutdown_timeout
104
+ Celluloid.shutdown_timeout
105
+ end
106
+ end
107
+ end
@@ -10,4 +10,4 @@ module Celluloid
10
10
  Thread.current[:celluloid_chain_id]
11
11
  end
12
12
  end
13
- end
13
+ end
@@ -21,32 +21,28 @@ module Celluloid
21
21
  end
22
22
 
23
23
  def dispatch(obj)
24
+ check(obj)
24
25
  _block = @block && @block.to_proc
25
26
  obj.public_send(@method, *@arguments, &_block)
26
- rescue NoMethodError => ex
27
- # Abort if the sender made a mistake
28
- raise AbortError.new(ex) unless obj.respond_to? @method
29
-
30
- # Otherwise something blew up. Crash this actor
31
- raise
32
- rescue ArgumentError => ex
33
- # Abort if the sender made a mistake
27
+ end
28
+
29
+ def check(obj)
30
+ raise NoMethodError, "undefined method `#{@method}' for #{obj.inspect}" unless obj.respond_to? @method
31
+
34
32
  begin
35
33
  arity = obj.method(@method).arity
36
34
  rescue NameError
37
- # In theory this shouldn't happen, but just in case
38
- raise AbortError.new(ex)
35
+ return
39
36
  end
40
37
 
41
38
  if arity >= 0
42
- raise AbortError.new(ex) if @arguments.size != arity
39
+ raise ArgumentError, "wrong number of arguments (#{@arguments.size} for #{arity})" if @arguments.size != arity
43
40
  elsif arity < -1
44
41
  mandatory_args = -arity - 1
45
- raise AbortError.new(ex) if arguments.size < mandatory_args
42
+ raise ArgumentError, "wrong number of arguments (#{@arguments.size} for #{mandatory_args}+)" if arguments.size < mandatory_args
46
43
  end
47
-
48
- # Otherwise something blew up. Crash this actor
49
- raise
44
+ rescue => ex
45
+ raise AbortError.new(ex)
50
46
  end
51
47
  end
52
48
 
@@ -88,8 +84,12 @@ module Celluloid
88
84
  @sender << message
89
85
  end
90
86
 
87
+ def response
88
+ Celluloid.suspend(:callwait, self)
89
+ end
90
+
91
91
  def value
92
- Celluloid.suspend(:callwait, self).value
92
+ response.value
93
93
  end
94
94
 
95
95
  def wait