celluloid 0.12.4 → 0.13.0.pre

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  =========
3
3
  [![Build Status](https://secure.travis-ci.org/celluloid/celluloid.png?branch=master)](http://travis-ci.org/celluloid/celluloid)
4
4
  [![Dependency Status](https://gemnasium.com/celluloid/celluloid.png)](https://gemnasium.com/celluloid/celluloid)
5
- [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/celluloid/celluloid)
5
+ [![Code Climate](https://codeclimate.com/github/celluloid/celluloid.png)](https://codeclimate.com/github/celluloid/celluloid)
6
6
 
7
7
  > "I thought of objects being like biological cells and/or individual
8
8
  > computers on a network, only able to communicate with messages"
@@ -45,7 +45,7 @@ features which make concurrent programming simple, easy, and fun:
45
45
  The central idea: have you tried turning it off and on again? Celluloid
46
46
  takes care of rebooting subcomponents of your application when they crash,
47
47
  whether it's a single actor, or large (potentially multi-tiered) groups of
48
- actors that are all interdependent. This means rather that worrying about
48
+ actors that are all interdependent. This means rather than worrying about
49
49
  rescuing every last exception, you can just sit back, relax, and let parts
50
50
  of your program crash, knowing Celluloid will automatically reboot them in
51
51
  a clean state. Celluloid provides its own implementation of the core
@@ -141,5 +141,5 @@ Contributing to Celluloid
141
141
  License
142
142
  -------
143
143
 
144
- Copyright (c) 2012 Tony Arcieri. Distributed under the MIT License. See
144
+ Copyright (c) 2013 Tony Arcieri. Distributed under the MIT License. See
145
145
  LICENSE.txt for further details.
@@ -2,20 +2,20 @@ require 'logger'
2
2
  require 'thread'
3
3
  require 'timeout'
4
4
  require 'set'
5
- require 'facter'
6
5
 
7
6
  module Celluloid
8
7
  extend self # expose all instance methods as singleton methods
9
8
 
10
9
  # How long actors have to terminate
11
- SHUTDOWN_TIMEOUT = 120
10
+ SHUTDOWN_TIMEOUT = 10
12
11
 
13
12
  # Warning message added to Celluloid objects accessed outside their actors
14
13
  BARE_OBJECT_WARNING_MESSAGE = "WARNING: BARE CELLULOID OBJECT "
15
14
 
16
15
  class << self
17
- attr_accessor :logger # Thread-safe logger class
18
- attr_accessor :task_class # Default task type to use
16
+ attr_accessor :internal_pool # Internal thread pool
17
+ attr_accessor :logger # Thread-safe logger class
18
+ attr_accessor :task_class # Default task type to use
19
19
 
20
20
  def included(klass)
21
21
  klass.send :extend, ClassMethods
@@ -24,7 +24,7 @@ module Celluloid
24
24
 
25
25
  # Are we currently inside of an actor?
26
26
  def actor?
27
- !!Thread.current[:actor]
27
+ !!Thread.current[:celluloid_actor]
28
28
  end
29
29
 
30
30
  # Generate a Universally Unique Identifier
@@ -34,15 +34,14 @@ module Celluloid
34
34
 
35
35
  # Obtain the number of CPUs in the system
36
36
  def cores
37
- core_count = Facter.fact(:processorcount).value
38
- Integer(core_count)
37
+ CPUCounter.cores
39
38
  end
40
39
  alias_method :cpus, :cores
41
40
  alias_method :ncpus, :cores
42
41
 
43
42
  # Perform a stack dump of all actors to the given output object
44
43
  def stack_dump(output = STDERR)
45
- Celluloid::StackDumper.dump(output)
44
+ Celluloid::StackDump.new.dump(output)
46
45
  end
47
46
  alias_method :dump, :stack_dump
48
47
 
@@ -77,19 +76,8 @@ module Celluloid
77
76
 
78
77
  Logger.debug "Shutdown completed cleanly"
79
78
  end
80
- end
81
- end
82
-
83
- # Terminate all actors at exit
84
- at_exit do
85
- if defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" && RUBY_VERSION >= "1.9"
86
- # workaround for MRI bug losing exit status in at_exit block
87
- # http://bugs.ruby-lang.org/issues/5218
88
- exit_status = $!.status if $!.is_a?(SystemExit)
89
- shutdown
90
- exit exit_status if exit_status
91
- else
92
- shutdown
79
+ rescue Timeout::Error => ex
80
+ Logger.error("Couldn't cleanly terminate all actors in #{SHUTDOWN_TIMEOUT} seconds!")
93
81
  end
94
82
  end
95
83
 
@@ -157,22 +145,40 @@ module Celluloid
157
145
  end
158
146
  alias_method :trap_exit, :exit_handler
159
147
 
160
- # Configure a custom mailbox factory
161
- def use_mailbox(klass = nil, &block)
162
- if block
163
- @mailbox_factory = block
164
- else
165
- mailbox_class(klass)
148
+ # Define a callback to run when the actor is finalized.
149
+ def finalizer(callback = nil)
150
+ if callback
151
+ @finalizer = callback.to_sym
152
+ elsif defined?(@finalizer)
153
+ @finalizer
154
+ elsif superclass.respond_to? :finalizer
155
+ superclass.finalizer
166
156
  end
167
157
  end
168
158
 
169
159
  # Define the mailbox class for this class
170
- def mailbox_class(klass)
171
- @mailbox_factory = proc { klass.new }
160
+ def mailbox_class(klass = nil)
161
+ if klass
162
+ @mailbox_class = klass
163
+ elsif defined?(@mailbox_class)
164
+ @mailbox_class
165
+ elsif superclass.respond_to? :mailbox_class
166
+ superclass.mailbox_class
167
+ else
168
+ Celluloid::Mailbox
169
+ end
172
170
  end
173
171
 
174
- def proxy_class(klass)
175
- @proxy_factory = proc { klass }
172
+ def proxy_class(klass = nil)
173
+ if klass
174
+ @proxy_class = klass
175
+ elsif defined?(@proxy_class)
176
+ @proxy_class
177
+ elsif superclass.respond_to? :proxy_class
178
+ superclass.proxy_class
179
+ else
180
+ Celluloid::ActorProxy
181
+ end
176
182
  end
177
183
 
178
184
  # Define the default task type for this class
@@ -198,35 +204,14 @@ module Celluloid
198
204
  end
199
205
  end
200
206
 
201
- # Create a mailbox for this actor
202
- def mailbox_factory
203
- if defined?(@mailbox_factory)
204
- @mailbox_factory.call
205
- elsif superclass.respond_to? :mailbox_factory
206
- superclass.mailbox_factory
207
- else
208
- Mailbox.new
209
- end
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
221
-
222
207
  # Configuration options for Actor#new
223
208
  def actor_options
224
209
  {
225
- :mailbox => mailbox_factory,
226
- :proxy_class => proxy_factory,
210
+ :mailbox => mailbox_class.new,
211
+ :proxy_class => proxy_class,
212
+ :task_class => task_class,
227
213
  :exit_handler => exit_handler,
228
- :exclusive_methods => @exclusive_methods,
229
- :task_class => task_class
214
+ :exclusive_methods => @exclusive_methods
230
215
  }
231
216
  end
232
217
 
@@ -256,7 +241,7 @@ module Celluloid
256
241
 
257
242
  # Are we being invoked in a different thread from our owner?
258
243
  def leaked?
259
- @celluloid_owner != Thread.current[:actor]
244
+ @celluloid_owner != Thread.current[:celluloid_actor]
260
245
  end
261
246
 
262
247
  def inspect
@@ -265,7 +250,7 @@ module Celluloid
265
250
  if leaked?
266
251
  str << Celluloid::BARE_OBJECT_WARNING_MESSAGE
267
252
  else
268
- str << "Celluloid::Actor"
253
+ str << "Celluloid::ActorProxy"
269
254
  end
270
255
 
271
256
  str << "(#{self.class}:0x#{object_id.to_s(16)})"
@@ -278,29 +263,6 @@ module Celluloid
278
263
 
279
264
  str.sub!(/\s$/, '>')
280
265
  end
281
-
282
- # Process async calls via method_missing
283
- def method_missing(meth, *args, &block)
284
- # bang methods are async calls
285
- if meth.to_s.match(/!$/)
286
- unbanged_meth = meth.to_s.sub(/!$/, '')
287
- args.unshift unbanged_meth
288
-
289
- call = AsyncCall.new(:__send__, args, block)
290
- begin
291
- Thread.current[:actor].mailbox << call
292
- rescue MailboxError
293
- # Silently swallow asynchronous calls to dead actors. There's no way
294
- # to reliably generate DeadActorErrors for async calls, so users of
295
- # async calls should find other ways to deal with actors dying
296
- # during an async call (i.e. linking/supervisors)
297
- end
298
-
299
- return
300
- end
301
-
302
- super
303
- end
304
266
  end
305
267
 
306
268
  #
@@ -320,17 +282,17 @@ module Celluloid
320
282
 
321
283
  # Terminate this actor
322
284
  def terminate
323
- Thread.current[:actor].terminate
285
+ Thread.current[:celluloid_actor].terminate
324
286
  end
325
287
 
326
288
  # Send a signal with the given name to all waiting methods
327
289
  def signal(name, value = nil)
328
- Thread.current[:actor].signal name, value
290
+ Thread.current[:celluloid_actor].signal name, value
329
291
  end
330
292
 
331
293
  # Wait for the given signal
332
294
  def wait(name)
333
- Thread.current[:actor].wait name
295
+ Thread.current[:celluloid_actor].wait name
334
296
  end
335
297
 
336
298
  # Obtain the current_actor
@@ -338,6 +300,11 @@ module Celluloid
338
300
  Actor.current
339
301
  end
340
302
 
303
+ # Obtain the UUID of the current call chain
304
+ def call_chain_id
305
+ Thread.current[:celluloid_chain_id]
306
+ end
307
+
341
308
  # Obtain the name of the current actor
342
309
  def name
343
310
  Actor.name
@@ -345,12 +312,12 @@ module Celluloid
345
312
 
346
313
  # Obtain the running tasks for this actor
347
314
  def tasks
348
- Thread.current[:actor].tasks.to_a
315
+ Thread.current[:celluloid_actor].tasks.to_a
349
316
  end
350
317
 
351
318
  # Obtain the Celluloid::Links for this actor
352
319
  def links
353
- Thread.current[:actor].links
320
+ Thread.current[:celluloid_actor].links
354
321
  end
355
322
 
356
323
  # Watch for exit events from another actor
@@ -385,7 +352,7 @@ module Celluloid
385
352
 
386
353
  # Receive an asynchronous message via the actor protocol
387
354
  def receive(timeout = nil, &block)
388
- actor = Thread.current[:actor]
355
+ actor = Thread.current[:celluloid_actor]
389
356
  if actor
390
357
  actor.receive(timeout, &block)
391
358
  else
@@ -395,7 +362,7 @@ module Celluloid
395
362
 
396
363
  # Sleep letting the actor continue processing messages
397
364
  def sleep(interval)
398
- actor = Thread.current[:actor]
365
+ actor = Thread.current[:celluloid_actor]
399
366
  if actor
400
367
  actor.sleep(interval)
401
368
  else
@@ -406,23 +373,23 @@ module Celluloid
406
373
  # Run given block in an exclusive mode: all synchronous calls block the whole
407
374
  # actor, not only current message processing.
408
375
  def exclusive(&block)
409
- Thread.current[:actor].exclusive(&block)
376
+ Thread.current[:celluloid_actor].exclusive(&block)
410
377
  end
411
378
 
412
379
  # Are we currently exclusive
413
380
  def exclusive?
414
- actor = Thread.current[:actor]
381
+ actor = Thread.current[:celluloid_actor]
415
382
  actor && actor.exclusive?
416
383
  end
417
384
 
418
385
  # Call a block after a given interval, returning a Celluloid::Timer object
419
386
  def after(interval, &block)
420
- Thread.current[:actor].after(interval, &block)
387
+ Thread.current[:celluloid_actor].after(interval, &block)
421
388
  end
422
389
 
423
390
  # Call a block every given interval, returning a Celluloid::Timer object
424
391
  def every(interval, &block)
425
- Thread.current[:actor].every(interval, &block)
392
+ Thread.current[:celluloid_actor].every(interval, &block)
426
393
  end
427
394
 
428
395
  # Perform a blocking or computationally intensive action inside an
@@ -437,18 +404,18 @@ module Celluloid
437
404
  # Handle async calls within an actor itself
438
405
  def async(meth = nil, *args, &block)
439
406
  if meth
440
- Actor.async Thread.current[:actor].mailbox, meth, *args, &block
407
+ Actor.async Thread.current[:celluloid_actor].mailbox, meth, *args, &block
441
408
  else
442
- Thread.current[:actor].proxy.async
409
+ Thread.current[:celluloid_actor].proxy.async
443
410
  end
444
411
  end
445
412
 
446
413
  # Handle calls to future within an actor itself
447
414
  def future(meth = nil, *args, &block)
448
415
  if meth
449
- Actor.future Thread.current[:actor].mailbox, meth, *args, &block
416
+ Actor.future Thread.current[:celluloid_actor].mailbox, meth, *args, &block
450
417
  else
451
- Thread.current[:actor].proxy.future
418
+ Thread.current[:celluloid_actor].proxy.future
452
419
  end
453
420
  end
454
421
  end
@@ -456,7 +423,9 @@ end
456
423
  require 'celluloid/version'
457
424
 
458
425
  require 'celluloid/calls'
426
+ require 'celluloid/condition'
459
427
  require 'celluloid/core_ext'
428
+ require 'celluloid/cpu_counter'
460
429
  require 'celluloid/fiber'
461
430
  require 'celluloid/fsm'
462
431
  require 'celluloid/internal_pool'
@@ -468,9 +437,9 @@ require 'celluloid/receivers'
468
437
  require 'celluloid/registry'
469
438
  require 'celluloid/responses'
470
439
  require 'celluloid/signals'
471
- require 'celluloid/stack_dumper'
440
+ require 'celluloid/stack_dump'
472
441
  require 'celluloid/system_events'
473
- require 'celluloid/task'
442
+ require 'celluloid/tasks'
474
443
  require 'celluloid/thread_handle'
475
444
  require 'celluloid/uuid'
476
445
 
@@ -487,4 +456,5 @@ require 'celluloid/supervisor'
487
456
  require 'celluloid/notifications'
488
457
  require 'celluloid/logging'
489
458
 
459
+ require 'celluloid/legacy' unless defined?(CELLULOID_FUTURE)
490
460
  require 'celluloid/boot'
@@ -44,14 +44,14 @@ module Celluloid
44
44
 
45
45
  # Obtain the current actor
46
46
  def current
47
- actor = Thread.current[:actor]
47
+ actor = Thread.current[:celluloid_actor]
48
48
  raise NotActorError, "not in actor scope" unless actor
49
49
  actor.proxy
50
50
  end
51
51
 
52
52
  # Obtain the name of the current actor
53
53
  def name
54
- actor = Thread.current[:actor]
54
+ actor = Thread.current[:celluloid_actor]
55
55
  raise NotActorError, "not in actor scope" unless actor
56
56
  actor.name
57
57
  end
@@ -66,7 +66,7 @@ module Celluloid
66
66
  raise DeadActorError, "attempted to call a dead actor"
67
67
  end
68
68
 
69
- if Thread.current[:task] && !Celluloid.exclusive?
69
+ if Thread.current[:celluloid_task] && !Celluloid.exclusive?
70
70
  Task.suspend(:callwait).value
71
71
  else
72
72
  response = loop do
@@ -74,7 +74,7 @@ module Celluloid
74
74
  msg.respond_to?(:call) and msg.call == call
75
75
  end
76
76
  break message unless message.is_a?(SystemEvent)
77
- Thread.current[:actor].handle_system_event(message)
77
+ Thread.current[:celluloid_actor].handle_system_event(message)
78
78
  end
79
79
 
80
80
  response.value
@@ -104,7 +104,7 @@ module Celluloid
104
104
  def all
105
105
  actors = []
106
106
  Thread.list.each do |t|
107
- actor = t[:actor]
107
+ actor = t[:celluloid_actor]
108
108
  actors << actor.proxy if actor and actor.respond_to?(:proxy)
109
109
  end
110
110
  actors
@@ -113,25 +113,25 @@ module Celluloid
113
113
  # Watch for exit events from another actor
114
114
  def monitor(actor)
115
115
  raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
116
- Thread.current[:actor].linking_request(actor, :link)
116
+ Thread.current[:celluloid_actor].linking_request(actor, :link)
117
117
  end
118
118
 
119
119
  # Stop waiting for exit events from another actor
120
120
  def unmonitor(actor)
121
121
  raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
122
- Thread.current[:actor].linking_request(actor, :unlink)
122
+ Thread.current[:celluloid_actor].linking_request(actor, :unlink)
123
123
  end
124
124
 
125
125
  # Link to another actor
126
126
  def link(actor)
127
127
  monitor actor
128
- Thread.current[:actor].links << actor
128
+ Thread.current[:celluloid_actor].links << actor
129
129
  end
130
130
 
131
131
  # Unlink from another actor
132
132
  def unlink(actor)
133
133
  unmonitor actor
134
- Thread.current[:actor].links.delete actor
134
+ Thread.current[:celluloid_actor].links.delete actor
135
135
  end
136
136
 
137
137
  # Are we monitoring the given actor?
@@ -141,7 +141,7 @@ module Celluloid
141
141
 
142
142
  # Are we bidirectionally linked to the given actor?
143
143
  def linked_to?(actor)
144
- monitoring?(actor) && Thread.current[:actor].links.include?(actor)
144
+ monitoring?(actor) && Thread.current[:celluloid_actor].links.include?(actor)
145
145
  end
146
146
 
147
147
  # Forcibly kill a given actor
@@ -164,7 +164,6 @@ module Celluloid
164
164
  def initialize(subject, options = {})
165
165
  @subject = subject
166
166
  @mailbox = options[:mailbox] || Mailbox.new
167
- @proxy_class = options[:proxy_class] || ActorProxy
168
167
  @exit_handler = options[:exit_handler]
169
168
  @exclusives = options[:exclusive_methods]
170
169
  @task_class = options[:task_class] || Celluloid.task_class
@@ -179,12 +178,12 @@ module Celluloid
179
178
  @name = nil
180
179
 
181
180
  @thread = ThreadHandle.new do
182
- Thread.current[:actor] = self
183
- Thread.current[:mailbox] = @mailbox
181
+ Thread.current[:celluloid_actor] = self
182
+ Thread.current[:celluloid_mailbox] = @mailbox
184
183
  run
185
184
  end
186
185
 
187
- @proxy = @proxy_class.new(self)
186
+ @proxy = (options[:proxy_class] || ActorProxy).new(self)
188
187
  @subject.instance_variable_set(OWNER_IVAR, self)
189
188
  end
190
189
 
@@ -238,13 +237,15 @@ module Celluloid
238
237
  def linking_request(receiver, type)
239
238
  exclusive do
240
239
  start_time = Time.now
241
-
242
240
  receiver.mailbox << LinkingRequest.new(Actor.current, type)
243
241
  system_events = []
242
+
244
243
  loop do
245
244
  wait_interval = start_time + LINKING_TIMEOUT - Time.now
246
245
  message = @mailbox.receive(wait_interval) do |msg|
247
- msg.is_a?(LinkingResponse) && msg.actor == receiver && msg.type == type
246
+ msg.is_a?(LinkingResponse) &&
247
+ msg.actor.mailbox.address == receiver.mailbox.address &&
248
+ msg.type == type
248
249
  end
249
250
 
250
251
  case message
@@ -309,7 +310,7 @@ module Celluloid
309
310
 
310
311
  # Sleep for the given amount of time
311
312
  def sleep(interval)
312
- task = Thread.current[:task]
313
+ task = Thread.current[:celluloid_task]
313
314
  if task && !Celluloid.exclusive?
314
315
  @timers.after(interval) { task.resume }
315
316
  Task.suspend :sleeping
@@ -344,6 +345,8 @@ module Celluloid
344
345
  @name = event.name
345
346
  when TerminationRequest
346
347
  @running = false
348
+ when SignalConditionRequest
349
+ event.call
347
350
  end
348
351
  end
349
352
 
@@ -372,14 +375,24 @@ module Celluloid
372
375
  run_finalizer
373
376
  cleanup exit_event
374
377
  ensure
375
- Thread.current[:actor] = nil
376
- Thread.current[:mailbox] = nil
378
+ Thread.current[:celluloid_actor] = nil
379
+ Thread.current[:celluloid_mailbox] = nil
377
380
  end
378
381
 
379
382
  # Run the user-defined finalizer, if one is set
380
383
  def run_finalizer
381
- return unless @subject.respond_to? :finalize
382
- task(:finalizer, :finalize) { @subject.finalize }
384
+ # FIXME: remove before Celluloid 1.0
385
+ if @subject.respond_to?(:finalize) && @subject.class.finalizer != :finalize
386
+ Logger.warn("DEPRECATION WARNING: #{@subject.class}#finalize is deprecated and will be removed in Celluloid 1.0. " +
387
+ "Define finalizers with '#{@subject.class}.finalizer :callback.'")
388
+
389
+ task(:finalizer, :finalize) { @subject.finalize }
390
+ end
391
+
392
+ finalizer = @subject.class.finalizer
393
+ if finalizer && @subject.respond_to?(finalizer)
394
+ task(:finalizer, :finalize) { @subject.__send__(finalizer) }
395
+ end
383
396
  rescue => ex
384
397
  Logger.crash("#{@subject.class}#finalize crashed!", ex)
385
398
  end
@@ -387,7 +400,14 @@ module Celluloid
387
400
  # Clean up after this actor
388
401
  def cleanup(exit_event)
389
402
  @mailbox.shutdown
390
- @links.send_event exit_event
403
+ @links.each do |actor|
404
+ begin
405
+ actor.mailbox << exit_event
406
+ rescue MailboxError
407
+ # We're exiting/crashing, they're dead. Give up :(
408
+ end
409
+ end
410
+
391
411
  tasks.each { |task| task.terminate }
392
412
  rescue => ex
393
413
  Logger.crash("#{@subject.class}: CLEANUP CRASHED!", ex)