celluloid 0.12.3 → 0.12.4.pre

Sign up to get free protection for your applications and to get access to all the features.
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: