celluloid 0.11.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/celluloid.rb CHANGED
@@ -4,14 +4,17 @@ require 'timeout'
4
4
  require 'set'
5
5
 
6
6
  module Celluloid
7
+ extend self # expose all instance methods as singleton methods
8
+
7
9
  SHUTDOWN_TIMEOUT = 120 # How long actors have to terminate
8
- @logger = Logger.new STDERR
9
10
 
10
11
  class << self
11
- attr_accessor :logger # Thread-safe logger class
12
+ attr_accessor :logger # Thread-safe logger class
13
+ attr_accessor :task_class # Default task type to use
12
14
 
13
15
  def included(klass)
14
- klass.send :extend, ClassMethods
16
+ klass.send :extend, ClassMethods
17
+ klass.send :include, InstanceMethods
15
18
  end
16
19
 
17
20
  # Are we currently inside of an actor?
@@ -19,36 +22,6 @@ module Celluloid
19
22
  !!Thread.current[:actor]
20
23
  end
21
24
 
22
- # Is current actor running in exclusive mode?
23
- def exclusive?
24
- actor? and Thread.current[:actor].exclusive?
25
- end
26
-
27
- # Obtain the currently running actor (if one exists)
28
- def current_actor
29
- Actor.current
30
- end
31
-
32
- # Receive an asynchronous message
33
- def receive(timeout = nil, &block)
34
- actor = Thread.current[:actor]
35
- if actor
36
- actor.receive(timeout, &block)
37
- else
38
- Thread.mailbox.receive(timeout, &block)
39
- end
40
- end
41
-
42
- # Sleep letting the actor continue processing messages
43
- def sleep(interval)
44
- actor = Thread.current[:actor]
45
- if actor
46
- actor.sleep(interval)
47
- else
48
- Kernel.sleep interval
49
- end
50
- end
51
-
52
25
  # Generate a Universally Unique Identifier
53
26
  def uuid
54
27
  UUID.generate
@@ -67,8 +40,6 @@ module Celluloid
67
40
  end
68
41
 
69
42
  # Shut down all running actors
70
- # FIXME: This should probably attempt a graceful shutdown of the supervision
71
- # tree before iterating through all actors and telling them to terminate.
72
43
  def shutdown
73
44
  Timeout.timeout(SHUTDOWN_TIMEOUT) do
74
45
  actors = Actor.all
@@ -78,16 +49,16 @@ module Celluloid
78
49
  Supervisor.root.terminate if Supervisor.root
79
50
 
80
51
  # Actors cannot self-terminate, you must do it for them
81
- terminators = Actor.all.each do |actor|
52
+ Actor.all.each do |actor|
82
53
  begin
83
- actor.future(:terminate)
54
+ actor.terminate!
84
55
  rescue DeadActorError, MailboxError
85
56
  end
86
57
  end
87
58
 
88
- terminators.each do |terminator|
59
+ Actor.all.each do |actor|
89
60
  begin
90
- terminator.value
61
+ Actor.join(actor)
91
62
  rescue DeadActorError, MailboxError
92
63
  end
93
64
  end
@@ -104,7 +75,7 @@ module Celluloid
104
75
  module ClassMethods
105
76
  # Create a new actor
106
77
  def new(*args, &block)
107
- proxy = Actor.new(allocate).proxy
78
+ proxy = Actor.new(allocate, actor_options).proxy
108
79
  proxy._send_(:initialize, *args, &block)
109
80
  proxy
110
81
  end
@@ -112,11 +83,10 @@ module Celluloid
112
83
 
113
84
  # Create a new actor and link to the current one
114
85
  def new_link(*args, &block)
115
- current_actor = Actor.current
116
- raise NotActorError, "can't link outside actor context" unless current_actor
86
+ raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
117
87
 
118
- proxy = Actor.new(allocate).proxy
119
- current_actor.link proxy
88
+ proxy = Actor.new(allocate, actor_options).proxy
89
+ Actor.link(proxy)
120
90
  proxy._send_(:initialize, *args, &block)
121
91
  proxy
122
92
  end
@@ -158,43 +128,116 @@ module Celluloid
158
128
  @exit_handler = callback.to_sym
159
129
  end
160
130
 
161
- # Obtain the exit handler for this actor
162
- attr_reader :exit_handler
163
-
164
131
  # Configure a custom mailbox factory
165
132
  def use_mailbox(klass = nil, &block)
166
133
  if block
167
134
  @mailbox_factory = block
168
135
  else
169
- @mailbox_factory = proc { klass.new }
136
+ mailbox_class(klass)
137
+ end
138
+ end
139
+
140
+ # Define the mailbox class for this class
141
+ def mailbox_class(klass)
142
+ @mailbox_factory = proc { klass.new }
143
+ end
144
+
145
+ # Define the default task type for this class
146
+ def task_class(klass = nil)
147
+ if klass
148
+ @task_class = klass
149
+ elsif defined?(@task_class)
150
+ @task_class
151
+ elsif superclass.respond_to? :task_class
152
+ superclass.task_class
153
+ else
154
+ Celluloid.task_class
170
155
  end
171
156
  end
172
157
 
173
158
  # Mark methods as running exclusively
174
159
  def exclusive(*methods)
175
- @exclusive_methods ||= Set.new
176
- @exclusive_methods.merge methods.map(&:to_sym)
160
+ if methods.empty?
161
+ @exclusive_methods = :all
162
+ elsif @exclusive_methods != :all
163
+ @exclusive_methods ||= Set.new
164
+ @exclusive_methods.merge methods.map(&:to_sym)
165
+ end
177
166
  end
178
- attr_reader :exclusive_methods
179
167
 
180
168
  # Create a mailbox for this actor
181
169
  def mailbox_factory
182
170
  if defined?(@mailbox_factory)
183
171
  @mailbox_factory.call
184
- elsif defined?(super)
185
- super
172
+ elsif superclass.respond_to? :mailbox_factory
173
+ superclass.mailbox_factory
186
174
  else
187
175
  Mailbox.new
188
176
  end
189
177
  end
190
178
 
179
+ # Configuration options for Actor#new
180
+ def actor_options
181
+ {
182
+ :mailbox => mailbox_factory,
183
+ :exit_handler => @exit_handler,
184
+ :exclusive_methods => @exclusive_methods,
185
+ :task_class => task_class
186
+ }
187
+ end
188
+
191
189
  def ===(other)
192
190
  other.kind_of? self
193
191
  end
194
192
  end
195
193
 
194
+ # These are methods we don't want added to the Celluloid singleton but to be
195
+ # defined on all classes that use Celluloid
196
+ module InstanceMethods
197
+ # Obtain the Ruby object the actor is wrapping. This should ONLY be used
198
+ # for a limited set of use cases like runtime metaprogramming. Interacting
199
+ # directly with the wrapped object foregoes any kind of thread safety that
200
+ # Celluloid would ordinarily provide you, and the object is guaranteed to
201
+ # be shared with at least the actor thread. Tread carefully.
202
+ def wrapped_object; self; end
203
+
204
+ def inspect
205
+ str = "#<Celluloid::Actor(#{self.class}:0x#{object_id.to_s(16)})"
206
+ ivars = instance_variables.map do |ivar|
207
+ "#{ivar}=#{instance_variable_get(ivar).inspect}"
208
+ end
209
+
210
+ str << " " << ivars.join(' ') unless ivars.empty?
211
+ str << ">"
212
+ end
213
+
214
+ # Process async calls via method_missing
215
+ def method_missing(meth, *args, &block)
216
+ # bang methods are async calls
217
+ if meth.to_s.match(/!$/)
218
+ unbanged_meth = meth.to_s.sub(/!$/, '')
219
+ args.unshift unbanged_meth
220
+
221
+ call = AsyncCall.new(:__send__, args, block)
222
+ begin
223
+ Thread.current[:actor].mailbox << call
224
+ rescue MailboxError
225
+ # Silently swallow asynchronous calls to dead actors. There's no way
226
+ # to reliably generate DeadActorErrors for async calls, so users of
227
+ # async calls should find other ways to deal with actors dying
228
+ # during an async call (i.e. linking/supervisors)
229
+ end
230
+
231
+ return
232
+ end
233
+
234
+ super
235
+ end
236
+ end
237
+
196
238
  #
197
- # Instance methods
239
+ # The following methods are available on both the Celluloid singleton and
240
+ # directly inside of all classes that include Celluloid
198
241
  #
199
242
 
200
243
  # Is this actor alive?
@@ -217,16 +260,6 @@ module Celluloid
217
260
  Thread.current[:actor].terminate
218
261
  end
219
262
 
220
- def inspect
221
- str = "#<Celluloid::Actor(#{self.class}:0x#{object_id.to_s(16)})"
222
- ivars = instance_variables.map do |ivar|
223
- "#{ivar}=#{instance_variable_get(ivar).inspect}"
224
- end
225
-
226
- str << " " << ivars.join(' ') unless ivars.empty?
227
- str << ">"
228
- end
229
-
230
263
  # Send a signal with the given name to all waiting methods
231
264
  def signal(name, value = nil)
232
265
  Thread.current[:actor].signal name, value
@@ -252,51 +285,59 @@ module Celluloid
252
285
  Thread.current[:actor].tasks.to_a
253
286
  end
254
287
 
255
- # Obtain the Ruby object the actor is wrapping. This should ONLY be used
256
- # for a limited set of use cases like runtime metaprogramming. Interacting
257
- # directly with the wrapped object foregoes any kind of thread safety that
258
- # Celluloid would ordinarily provide you, and the object is guaranteed to
259
- # be shared with at least the actor thread. Tread carefully.
260
- def wrapped_object; self; end
261
-
262
288
  # Obtain the Celluloid::Links for this actor
263
289
  def links
264
290
  Thread.current[:actor].links
265
291
  end
266
292
 
293
+ # Watch for exit events from another actor
294
+ def monitor(actor)
295
+ Actor.monitor(actor)
296
+ end
297
+
298
+ # Stop waiting for exit events from another actor
299
+ def unmonitor(actor)
300
+ Actor.unmonitor(actor)
301
+ end
302
+
267
303
  # Link this actor to another, allowing it to crash or react to errors
268
304
  def link(actor)
269
- actor.notify_link Actor.current
270
- notify_link actor
305
+ Actor.link(actor)
271
306
  end
272
307
 
273
308
  # Remove links to another actor
274
309
  def unlink(actor)
275
- actor.notify_unlink Actor.current
276
- notify_unlink actor
277
- end
278
-
279
- def notify_link(actor)
280
- links << actor
310
+ Actor.unlink(actor)
281
311
  end
282
312
 
283
- def notify_unlink(actor)
284
- links.delete actor
313
+ # Are we monitoring another actor?
314
+ def monitoring?(actor)
315
+ Actor.monitoring?(actor)
285
316
  end
286
317
 
287
318
  # Is this actor linked to another?
288
319
  def linked_to?(actor)
289
- Thread.current[:actor].links.include? actor
320
+ Actor.linked_to?(actor)
290
321
  end
291
322
 
292
323
  # Receive an asynchronous message via the actor protocol
293
324
  def receive(timeout = nil, &block)
294
- Celluloid.receive(timeout, &block)
325
+ actor = Thread.current[:actor]
326
+ if actor
327
+ actor.receive(timeout, &block)
328
+ else
329
+ Thread.mailbox.receive(timeout, &block)
330
+ end
295
331
  end
296
332
 
297
- # Sleep while letting the actor continue to receive messages
333
+ # Sleep letting the actor continue processing messages
298
334
  def sleep(interval)
299
- Celluloid.sleep(interval)
335
+ actor = Thread.current[:actor]
336
+ if actor
337
+ actor.sleep(interval)
338
+ else
339
+ Kernel.sleep interval
340
+ end
300
341
  end
301
342
 
302
343
  # Run given block in an exclusive mode: all synchronous calls block the whole
@@ -307,7 +348,8 @@ module Celluloid
307
348
 
308
349
  # Are we currently exclusive
309
350
  def exclusive?
310
- Celluloid.exclusive?
351
+ actor = Thread.current[:actor]
352
+ actor && actor.exclusive?
311
353
  end
312
354
 
313
355
  # Call a block after a given interval, returning a Celluloid::Timer object
@@ -330,62 +372,55 @@ module Celluloid
330
372
  end
331
373
 
332
374
  # Handle async calls within an actor itself
333
- def async(meth, *args, &block)
334
- Actor.async Thread.current[:actor].mailbox, meth, *args, &block
375
+ def async(meth = nil, *args, &block)
376
+ if meth
377
+ Actor.async Thread.current[:actor].mailbox, meth, *args, &block
378
+ else
379
+ Thread.current[:actor].proxy.async
380
+ end
335
381
  end
336
382
 
337
383
  # Handle calls to future within an actor itself
338
- def future(meth, *args, &block)
339
- Actor.future Thread.current[:actor].mailbox, meth, *args, &block
340
- end
341
-
342
- # Process async calls via method_missing
343
- def method_missing(meth, *args, &block)
344
- # bang methods are async calls
345
- if meth.to_s.match(/!$/)
346
- unbanged_meth = meth.to_s.sub(/!$/, '')
347
- args.unshift unbanged_meth
348
-
349
- call = AsyncCall.new(:__send__, args, block)
350
- begin
351
- Thread.current[:actor].mailbox << call
352
- rescue MailboxError
353
- # Silently swallow asynchronous calls to dead actors. There's no way
354
- # to reliably generate DeadActorErrors for async calls, so users of
355
- # async calls should find other ways to deal with actors dying
356
- # during an async call (i.e. linking/supervisors)
357
- end
358
-
359
- return
384
+ def future(meth = nil, *args, &block)
385
+ if meth
386
+ Actor.future Thread.current[:actor].mailbox, meth, *args, &block
387
+ else
388
+ Thread.current[:actor].proxy.future
360
389
  end
361
-
362
- super
363
390
  end
364
391
  end
365
392
 
366
393
  require 'celluloid/version'
367
- require 'celluloid/actor_proxy'
394
+
368
395
  require 'celluloid/calls'
369
396
  require 'celluloid/core_ext'
370
397
  require 'celluloid/cpu_counter'
371
- require 'celluloid/events'
372
398
  require 'celluloid/fiber'
373
399
  require 'celluloid/fsm'
374
400
  require 'celluloid/internal_pool'
375
401
  require 'celluloid/links'
376
402
  require 'celluloid/logger'
377
403
  require 'celluloid/mailbox'
404
+ require 'celluloid/method'
378
405
  require 'celluloid/receivers'
379
406
  require 'celluloid/registry'
380
407
  require 'celluloid/responses'
381
408
  require 'celluloid/signals'
409
+ require 'celluloid/system_events'
382
410
  require 'celluloid/task'
383
411
  require 'celluloid/thread_handle'
384
412
  require 'celluloid/uuid'
385
413
 
414
+ require 'celluloid/proxies/abstract_proxy'
415
+ require 'celluloid/proxies/actor_proxy'
416
+ require 'celluloid/proxies/async_proxy'
417
+ require 'celluloid/proxies/future_proxy'
418
+
386
419
  require 'celluloid/actor'
387
420
  require 'celluloid/future'
388
421
  require 'celluloid/pool_manager'
389
422
  require 'celluloid/supervision_group'
390
423
  require 'celluloid/supervisor'
391
424
  require 'celluloid/notifications'
425
+
426
+ require 'celluloid/boot'