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 CHANGED
@@ -2,6 +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
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) is available through the
67
- [Celluloid::IO](https://github.com/celluloid/celluloid-io) library.
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](https://github.com/mperham/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
  ------------
@@ -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
@@ -55,7 +55,7 @@ module Celluloid
55
55
  def shutdown
56
56
  Timeout.timeout(SHUTDOWN_TIMEOUT) do
57
57
  actors = Actor.all
58
- Logger.info "Terminating #{actors.size} actors..." if actors.size > 0
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.info "Shutdown completed cleanly"
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).join
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 = "#<#{Celluloid::BARE_OBJECT_WARNING_MESSAGE}(#{self.class}:0x#{object_id.to_s(16)})"
244
- ivars = instance_variables.map do |ivar|
245
- "#{ivar}=#{instance_variable_get(ivar).inspect}"
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 << " " << ivars.join(' ') unless ivars.empty?
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[:actor].mailbox << call
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[:actor].terminate
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[:actor].signal name, value
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[:actor].wait name
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[:actor].tasks.to_a
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[:actor].links
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[:actor]
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[:actor]
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[:actor].exclusive(&block)
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[:actor]
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[:actor].after(interval, &block)
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[:actor].every(interval, &block)
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[:actor].mailbox, meth, *args, &block
440
+ Actor.async Thread.current[:celluloid_actor].mailbox, meth, *args, &block
411
441
  else
412
- Thread.current[:actor].proxy.async
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[:actor].mailbox, meth, *args, &block
449
+ Actor.future Thread.current[:celluloid_actor].mailbox, meth, *args, &block
420
450
  else
421
- Thread.current[:actor].proxy.future
451
+ Thread.current[:celluloid_actor].proxy.future
422
452
  end
423
453
  end
424
454
  end
@@ -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[:actor]
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[:actor]
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[:task]
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[:actor].handle_system_event(message)
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[:actor]
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[:actor].linking_request(actor, :link)
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[:actor].linking_request(actor, :unlink)
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[:actor].links << actor
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[:actor].links.delete actor
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[:actor].links.include?(actor)
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[:actor] = self
181
- Thread.current[:mailbox] = @mailbox
182
+ Thread.current[:celluloid_actor] = self
183
+ Thread.current[:celluloid_mailbox] = @mailbox
182
184
  run
183
185
  end
184
186
 
185
- @proxy = ActorProxy.new(self)
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
- if task = Thread.current[:task]
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[:actor] = nil
370
- Thread.current[:mailbox] = nil
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
@@ -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[:task])
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
- begin
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
- obj.send(@method, *@arguments, &@block)
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.crash("#{obj.class}: async call `#{@method}' aborted!", ex.cause)
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
@@ -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[:mailbox] ||= Celluloid::Mailbox.new
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[:queue] << block
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[:queue] << nil
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[:queue] = queue
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[:actor].handle_message(response)
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).sub(::Celluloid::BARE_OBJECT_WARNING_MESSAGE, "Celluloid::Actor")
26
+ Actor.call(@mailbox, :inspect)
24
27
  rescue DeadActorError
25
28
  "#<Celluloid::Actor(#{@klass}) dead>"
26
29
  end
@@ -14,7 +14,7 @@ module Celluloid
14
14
 
15
15
  # Obtain the current task
16
16
  def self.current
17
- Thread.current[:task] or raise NotTaskError, "not within a task context"
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[:actor], Thread.current[:mailbox]
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[:actor] = actor
17
- Thread.current[:mailbox] = mailbox
18
- Thread.current[:task] = self
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 FiberError
47
- raise DeadTaskError, "cannot resume a dead task"
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[:actor], Thread.current[:mailbox]
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[:actor] = actor
23
- Thread.current[:mailbox] = mailbox
24
- Thread.current[:task] = self
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
@@ -1,4 +1,4 @@
1
1
  module Celluloid
2
- VERSION = '0.12.3'
2
+ VERSION = '0.12.4.pre'
3
3
  def self.version; VERSION; end
4
4
  end
@@ -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
- describe 'mocking methods' do
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 'works externally via the proxy' do
225
+ it "works externally via the proxy" do
225
226
  actor.external_hello.should == "World"
226
227
  end
227
228
 
228
- it 'works internally when called on self' do
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 exclusive_example(input = nil, sleep = false)
444
- sleep Celluloid::TIMER_QUANTUM if sleep
445
- @tasks << input
446
- exclusive?
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 "supports exclusive methods" do
457
- subject.exclusive_example.should be_true
458
- end
459
-
460
- it "remains in exclusive mode inside nested blocks" do
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 method in an exclusive order" do
465
- subject.exclusive_example! :one, true
466
- subject.exclusive_example! :two
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
 
@@ -51,6 +51,10 @@ module ExampleActorClass
51
51
  "Hello"
52
52
  end
53
53
 
54
+ def inspect_thunk
55
+ inspect
56
+ end
57
+
54
58
  def method_missing(method_name, *args, &block)
55
59
  if delegates?(method_name)
56
60
  @delegate.send method_name, *args, &block
@@ -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[:actor] = actor
17
+ Thread.current[:celluloid_actor] = actor
18
18
  end
19
19
 
20
20
  after :each do
21
- Thread.current[:actor] = nil
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.3
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-10-25 00:00:00.000000000 Z
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: &70118227807960 !ruby/object:Gem::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: *70118227807960
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: &70118227807500 !ruby/object:Gem::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: *70118227807500
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: &70118227807120 !ruby/object:Gem::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: *70118227807120
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: &70118227806660 !ruby/object:Gem::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: *70118227806660
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: &70118227806240 !ruby/object:Gem::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: *70118227806240
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.10
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: