celluloid 0.15.2 → 0.16.0.pre2

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 (57) 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 +86 -133
  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 +27 -18
  11. data/lib/celluloid/evented_mailbox.rb +8 -15
  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 +0 -3
  16. data/lib/celluloid/logger.rb +30 -0
  17. data/lib/celluloid/logging/incident_logger.rb +1 -1
  18. data/lib/celluloid/mailbox.rb +19 -18
  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 +3 -1
  27. data/lib/celluloid/registry.rb +1 -8
  28. data/lib/celluloid/stack_dump.rb +34 -11
  29. data/lib/celluloid/supervision_group.rb +26 -14
  30. data/lib/celluloid/tasks/task_fiber.rb +6 -0
  31. data/lib/celluloid/tasks/task_thread.rb +2 -1
  32. data/lib/celluloid/tasks.rb +6 -9
  33. data/lib/celluloid/thread_handle.rb +2 -2
  34. data/lib/celluloid.rb +68 -73
  35. data/spec/celluloid/actor_spec.rb +1 -1
  36. data/spec/celluloid/actor_system_spec.rb +69 -0
  37. data/spec/celluloid/block_spec.rb +1 -1
  38. data/spec/celluloid/calls_spec.rb +1 -1
  39. data/spec/celluloid/condition_spec.rb +14 -3
  40. data/spec/celluloid/cpu_counter_spec.rb +82 -0
  41. data/spec/celluloid/fsm_spec.rb +1 -1
  42. data/spec/celluloid/future_spec.rb +1 -1
  43. data/spec/celluloid/notifications_spec.rb +1 -1
  44. data/spec/celluloid/pool_spec.rb +34 -1
  45. data/spec/celluloid/probe_spec.rb +121 -0
  46. data/spec/celluloid/registry_spec.rb +6 -6
  47. data/spec/celluloid/stack_dump_spec.rb +37 -8
  48. data/spec/celluloid/supervision_group_spec.rb +7 -1
  49. data/spec/celluloid/supervisor_spec.rb +12 -1
  50. data/spec/celluloid/tasks/task_fiber_spec.rb +1 -1
  51. data/spec/celluloid/tasks/task_thread_spec.rb +1 -1
  52. data/spec/celluloid/thread_handle_spec.rb +7 -3
  53. data/spec/spec_helper.rb +20 -7
  54. data/spec/support/actor_examples.rb +33 -15
  55. data/spec/support/mailbox_examples.rb +9 -3
  56. data/spec/support/task_examples.rb +2 -0
  57. metadata +46 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f22a9c7d89b293484d7dcbe03cda32d7055ee42a
4
- data.tar.gz: a13abc0b96f0e4931f6ff20457f9665282a4d08b
3
+ metadata.gz: c9c58eede0a8ebeb315bf6f2fa925e19173dd5e9
4
+ data.tar.gz: 525f0ae4823a55ca1e23bf1ff5b6c8493592551f
5
5
  SHA512:
6
- metadata.gz: fba64fd21b7f58cf3fab69cdcfe889b4b95478a5bcad61557b1376e3965bc73583eeaf215be7003e886cc0fdd6a239aa79dd3738cd6ad831ce1c114435da4ff0
7
- data.tar.gz: fea6248e5f4e1931485673a458182ca86f380820b2e18acd7d90618203f8c6a193bdf33213161d397153dd43485bb4e1907ff65fb9efd14cc7c6a70cfcb4c309
6
+ metadata.gz: 2ac75588d4accd17305ea04b6329563783db6972262584898e3143bdf6086ba47dc8bcf8c93ad6c7a8b44115c2c718fbcc615219da968ffdfa8a7a5f17b5f908
7
+ data.tar.gz: e1185014388060dcd75a41b583a6df01f7bcbb37e21e1619091e364374cb861a31df7c7110b371887540c2482ff54cfae0cf7a3df534fc188157e220d5f8cd12
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
115
  @receivers = Receivers.new
150
- @timers = Timers.new
151
- @running = true
152
- @exclusive = false
116
+ @timers = Timers::Group.new
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,16 @@ 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
179
- end
148
+ while @running
149
+ begin
150
+ message = @mailbox.receive(timeout_interval)
151
+ handle_message message
152
+ rescue TimeoutError
153
+ @timers.fire
154
+ @receivers.fire_timers
155
+ rescue MailboxShutdown
156
+ @running = false
180
157
  end
181
- rescue MailboxShutdown
182
- # If the mailbox detects shutdown, exit the actor
183
158
  end
184
159
 
185
160
  shutdown
@@ -196,31 +171,37 @@ module Celluloid
196
171
  # Perform a linking request with another actor
197
172
  def linking_request(receiver, type)
198
173
  Celluloid.exclusive do
199
- start_time = Time.now
174
+ linking_timeout = Timers::Timeout.new(LINKING_TIMEOUT)
175
+
200
176
  receiver.mailbox << LinkingRequest.new(Actor.current, type)
201
177
  system_events = []
202
178
 
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
179
+ linking_timeout.while_time_remaining do |remaining|
180
+ begin
181
+ message = @mailbox.receive(remaining) do |msg|
182
+ msg.is_a?(LinkingResponse) &&
183
+ msg.actor.mailbox.address == receiver.mailbox.address &&
184
+ msg.type == type
185
+ end
186
+ rescue TimeoutError
187
+ next # IO reactor did something, no message in queue yet.
209
188
  end
210
189
 
211
- case message
212
- when LinkingResponse
190
+ if message.instance_of? LinkingResponse
191
+ Celluloid::Probe.actors_linked(self, receiver) if $CELLULOID_MONITORING
192
+
213
193
  # We're done!
214
- system_events.each { |ev| handle_system_event(ev) }
194
+ system_events.each { |ev| @mailbox << ev }
195
+
215
196
  return
216
- when NilClass
217
- raise TimeoutError, "linking timeout of #{LINKING_TIMEOUT} seconds exceeded"
218
- when SystemEvent
197
+ elsif message.is_a? SystemEvent
219
198
  # Queue up pending system events to be processed after we've successfully linked
220
199
  system_events << message
221
- else raise 'wtf'
200
+ else raise "Unexpected message type: #{message.class}. Expected LinkingResponse, NilClass, SystemEvent."
222
201
  end
223
202
  end
203
+
204
+ raise TimeoutError, "linking timeout of #{LINKING_TIMEOUT} seconds exceeded"
224
205
  end
225
206
  end
226
207
 
@@ -234,6 +215,10 @@ module Celluloid
234
215
  @signals.wait name
235
216
  end
236
217
 
218
+ def handle(*patterns, &block)
219
+ @handlers.handle(*patterns, &block)
220
+ end
221
+
237
222
  # Receive an asynchronous message
238
223
  def receive(timeout = nil, &block)
239
224
  loop do
@@ -304,28 +289,7 @@ module Celluloid
304
289
 
305
290
  # Handle standard low-priority messages
306
291
  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
292
+ unless @handlers.handle_message(message)
329
293
  unless @receivers.handle_message(message)
330
294
  Logger.debug "Discarded message (unhandled): #{message}" if $CELLULOID_DEBUG
331
295
  end
@@ -335,17 +299,19 @@ module Celluloid
335
299
 
336
300
  # Handle high-priority system event messages
337
301
  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
302
+ if event.instance_of? ExitEvent
303
+ handle_exit_event(event)
304
+ elsif event.instance_of? LinkingRequest
342
305
  event.process(links)
343
- when NamingRequest
306
+ elsif event.instance_of? NamingRequest
344
307
  @name = event.name
345
- when TerminationRequest
308
+ Celluloid::Probe.actor_named(self) if $CELLULOID_MONITORING
309
+ elsif event.instance_of? TerminationRequest
346
310
  terminate
347
- when SignalConditionRequest
311
+ elsif event.instance_of? SignalConditionRequest
348
312
  event.call
313
+ else
314
+ Logger.debug "Discarded message (unhandled): #{message}" if $CELLULOID_DEBUG
349
315
  end
350
316
  end
351
317
 
@@ -353,47 +319,34 @@ module Celluloid
353
319
  def handle_exit_event(event)
354
320
  @links.delete event.actor
355
321
 
356
- # Run the exit handler if available
357
- return @subject.send(@exit_handler, event.actor, event.reason) if @exit_handler
322
+ @exit_handler.call(event)
323
+ end
358
324
 
359
- # Reraise exceptions from linked actors
360
- # If no reason is given, actor terminated cleanly
325
+ def default_exit_handler(event)
361
326
  raise event.reason if event.reason
362
327
  end
363
328
 
364
329
  # Handle any exceptions that occur within a running actor
365
330
  def handle_crash(exception)
366
- Logger.crash("#{@subject.class} crashed!", exception)
367
- shutdown ExitEvent.new(@proxy, exception)
331
+ # TODO: add meta info
332
+ Logger.crash("Actor crashed!", exception)
333
+ shutdown ExitEvent.new(behavior_proxy, exception)
368
334
  rescue => ex
369
- Logger.crash("#{@subject.class}: ERROR HANDLER CRASHED!", ex)
335
+ Logger.crash("ERROR HANDLER CRASHED!", ex)
370
336
  end
371
337
 
372
338
  # Handle cleaning up this actor after it exits
373
- def shutdown(exit_event = ExitEvent.new(@proxy))
374
- run_finalizer
339
+ def shutdown(exit_event = ExitEvent.new(behavior_proxy))
340
+ @behavior.shutdown
375
341
  cleanup exit_event
376
342
  ensure
377
343
  Thread.current[:celluloid_actor] = nil
378
344
  Thread.current[:celluloid_mailbox] = nil
379
345
  end
380
346
 
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
347
  # Clean up after this actor
396
348
  def cleanup(exit_event)
349
+ Celluloid::Probe.actor_died(self) if $CELLULOID_MONITORING
397
350
  @mailbox.shutdown
398
351
  @links.each do |actor|
399
352
  if actor.mailbox.alive?
@@ -401,16 +354,16 @@ module Celluloid
401
354
  end
402
355
  end
403
356
 
404
- tasks.to_a.each { |task| task.terminate }
357
+ tasks.to_a.each(&:terminate)
405
358
  rescue => ex
406
- Logger.crash("#{@subject.class}: CLEANUP CRASHED!", ex)
359
+ # TODO: metadata
360
+ Logger.crash("CLEANUP CRASHED!", ex)
407
361
  end
408
362
 
409
363
  # Run a method inside a task unless it's exclusive
410
364
  def task(task_type, meta = nil)
411
- method_name = meta && meta.fetch(:method_name, nil)
412
365
  @task_class.new(task_type, meta) {
413
- if @exclusives && (@exclusives == :all || (method_name && @exclusives.include?(method_name.to_sym)))
366
+ if @exclusive
414
367
  Celluloid.exclusive { yield }
415
368
  else
416
369
  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