celluloid 0.12.0 → 0.13.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.
Files changed (40) hide show
  1. data/README.md +36 -23
  2. data/lib/celluloid/actor.rb +74 -47
  3. data/lib/celluloid/autostart.rb +16 -0
  4. data/lib/celluloid/calls.rb +52 -52
  5. data/lib/celluloid/condition.rb +70 -0
  6. data/lib/celluloid/core_ext.rb +1 -1
  7. data/lib/celluloid/cpu_counter.rb +7 -1
  8. data/lib/celluloid/fsm.rb +49 -16
  9. data/lib/celluloid/future.rb +5 -2
  10. data/lib/celluloid/internal_pool.rb +62 -44
  11. data/lib/celluloid/legacy.rb +46 -0
  12. data/lib/celluloid/links.rb +0 -5
  13. data/lib/celluloid/logger.rb +14 -4
  14. data/lib/celluloid/logging/incident.rb +21 -0
  15. data/lib/celluloid/logging/incident_logger.rb +129 -0
  16. data/lib/celluloid/logging/incident_reporter.rb +48 -0
  17. data/lib/celluloid/logging/log_event.rb +20 -0
  18. data/lib/celluloid/logging/ring_buffer.rb +65 -0
  19. data/lib/celluloid/logging.rb +5 -0
  20. data/lib/celluloid/mailbox.rb +2 -1
  21. data/lib/celluloid/method.rb +5 -0
  22. data/lib/celluloid/pool_manager.rb +21 -8
  23. data/lib/celluloid/proxies/actor_proxy.rb +11 -11
  24. data/lib/celluloid/proxies/async_proxy.rb +5 -1
  25. data/lib/celluloid/responses.rb +8 -6
  26. data/lib/celluloid/stack_dump.rb +94 -0
  27. data/lib/celluloid/supervision_group.rb +5 -3
  28. data/lib/celluloid/system_events.rb +11 -0
  29. data/lib/celluloid/tasks/task_fiber.rb +17 -9
  30. data/lib/celluloid/tasks/task_thread.rb +24 -14
  31. data/lib/celluloid/tasks.rb +59 -0
  32. data/lib/celluloid/thread_handle.rb +10 -1
  33. data/lib/celluloid/version.rb +1 -1
  34. data/lib/celluloid.rb +146 -87
  35. data/spec/support/actor_examples.rb +242 -114
  36. data/spec/support/example_actor_class.rb +17 -5
  37. data/spec/support/task_examples.rb +11 -2
  38. metadata +58 -14
  39. data/lib/celluloid/boot.rb +0 -9
  40. data/lib/celluloid/task.rb +0 -22
data/README.md CHANGED
@@ -1,7 +1,10 @@
1
1
  ![Celluloid](https://raw.github.com/celluloid/celluloid-logos/master/celluloid/celluloid.png)
2
2
  =========
3
+ [![Gem Version](https://badge.fury.io/rb/celluloid.png)](http://rubygems.org/gems/celluloid)
3
4
  [![Build Status](https://secure.travis-ci.org/celluloid/celluloid.png?branch=master)](http://travis-ci.org/celluloid/celluloid)
4
5
  [![Dependency Status](https://gemnasium.com/celluloid/celluloid.png)](https://gemnasium.com/celluloid/celluloid)
6
+ [![Code Climate](https://codeclimate.com/github/celluloid/celluloid.png)](https://codeclimate.com/github/celluloid/celluloid)
7
+ [![Coverage Status](https://coveralls.io/repos/celluloid/celluloid/badge.png?branch=master)](https://coveralls.io/r/celluloid/celluloid)
5
8
 
6
9
  > "I thought of objects being like biological cells and/or individual
7
10
  > computers on a network, only able to communicate with messages"
@@ -44,7 +47,7 @@ features which make concurrent programming simple, easy, and fun:
44
47
  The central idea: have you tried turning it off and on again? Celluloid
45
48
  takes care of rebooting subcomponents of your application when they crash,
46
49
  whether it's a single actor, or large (potentially multi-tiered) groups of
47
- actors that are all interdependent. This means rather that worrying about
50
+ actors that are all interdependent. This means rather than worrying about
48
51
  rescuing every last exception, you can just sit back, relax, and let parts
49
52
  of your program crash, knowing Celluloid will automatically reboot them in
50
53
  a clean state. Celluloid provides its own implementation of the core
@@ -63,19 +66,34 @@ features which make concurrent programming simple, easy, and fun:
63
66
 
64
67
  You can also build distributed systems with Celluloid using its
65
68
  [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.
69
+ to EventMachine (with a synchronous API instead of callback/deferrable soup)
70
+ is available through the [Celluloid::IO](https://github.com/celluloid/celluloid-io)
71
+ library.
71
72
 
72
73
  Like Celluloid? [Join the Google Group](http://groups.google.com/group/celluloid-ruby)
73
74
  or visit us on IRC at #celluloid on freenode
74
75
 
76
+ ### Is it any good?
77
+
78
+ [Yes](http://news.ycombinator.com/item?id=3067434)
79
+
75
80
  ### Is It "Production Ready™"?
76
81
 
77
82
  Yes, many users are now running Celluloid in production by using
78
- [Sidekiq](https://github.com/mperham/sidekiq)
83
+ [Sidekiq](http://sidekiq.org) and [Adhearsion](http://adhearsion.com/)
84
+
85
+ Documentation
86
+ -------------
87
+
88
+ [Please see the Celluloid Wiki](https://github.com/celluloid/celluloid/wiki)
89
+ for more detailed documentation and usage notes.
90
+
91
+ The following API documentation is also available:
92
+
93
+ * [YARD API documentation](http://rubydoc.info/gems/celluloid/frames)
94
+ * [Celluloid module (primary API)](http://rubydoc.info/gems/celluloid/Celluloid)
95
+ * [Celluloid class methods](http://rubydoc.info/gems/celluloid/Celluloid/ClassMethods)
96
+ * [All Celluloid classes](http://rubydoc.info/gems/celluloid/index)
79
97
 
80
98
  Installation
81
99
  ------------
@@ -92,25 +110,20 @@ Or install it yourself as:
92
110
 
93
111
  $ gem install celluloid
94
112
 
95
- Inside of your Ruby program do:
96
-
97
- require 'celluloid'
113
+ Inside of your Ruby program, require Celluloid with:
98
114
 
99
- ...to pull it in as a dependency.
115
+ require 'celluloid/autostart'
100
116
 
101
117
  Supported Platforms
102
118
  -------------------
103
119
 
104
- Celluloid works on Ruby 1.9.3, JRuby 1.6, and Rubinius 2.0. JRuby or Rubinius
105
- are the preferred platforms as they support true thread-level parallelism when
106
- executing Ruby code, whereas MRI/YARV is constrained by a global interpreter
107
- lock (GIL) and can only execute one thread at a time.
120
+ Celluloid works on Ruby 1.9.3, 2.0.0, JRuby 1.6+, and Rubinius 2.0.
108
121
 
109
- Celluloid requires Ruby 1.9 mode on all interpreters. This works out of the
110
- box on MRI/YARV, and requires the following flags elsewhere:
122
+ JRuby or Rubinius are the preferred platforms as they support true thread-level
123
+ parallelism when executing Ruby code, whereas MRI/YARV is constrained by a global
124
+ interpreter lock (GIL) and can only execute one thread at a time.
111
125
 
112
- * JRuby: --1.9 command line option, or JRUBY_OPTS=--1.9 environment variable
113
- * rbx: -X19 command line option
126
+ Celluloid requires Ruby 1.9 mode on all interpreters.
114
127
 
115
128
  Additional Reading
116
129
  ------------------
@@ -122,12 +135,12 @@ Contributing to Celluloid
122
135
  -------------------------
123
136
 
124
137
  * Fork this repository on github
125
- * Make your changes and send me a pull request
126
- * If I like them I'll merge them
127
- * If I've accepted a patch, feel free to ask for commit access
138
+ * Make your changes and send us a pull request
139
+ * If we like them we'll merge them
140
+ * If we've accepted a patch, feel free to ask for commit access
128
141
 
129
142
  License
130
143
  -------
131
144
 
132
- Copyright (c) 2012 Tony Arcieri. Distributed under the MIT License. See
145
+ Copyright (c) 2013 Tony Arcieri. Distributed under the MIT License. See
133
146
  LICENSE.txt for further details.
@@ -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,21 +66,7 @@ module Celluloid
65
66
  raise DeadActorError, "attempted to call a dead actor"
66
67
  end
67
68
 
68
- if Celluloid.actor? and not Celluloid.exclusive?
69
- # The current task will be automatically resumed when we get a response
70
- Task.suspend(:callwait).value
71
- else
72
- # Otherwise we're inside a normal thread or exclusive, so block
73
- response = loop do
74
- message = Thread.mailbox.receive do |msg|
75
- msg.respond_to?(:call) and msg.call == call
76
- end
77
- break message unless message.is_a?(SystemEvent)
78
- Thread.current[:actor].handle_system_event(message)
79
- end
80
-
81
- response.value
82
- end
69
+ Celluloid.suspend(:callwait, call).value
83
70
  end
84
71
 
85
72
  # Invoke a method asynchronously on an actor via its mailbox
@@ -105,7 +92,7 @@ module Celluloid
105
92
  def all
106
93
  actors = []
107
94
  Thread.list.each do |t|
108
- actor = t[:actor]
95
+ actor = t[:celluloid_actor]
109
96
  actors << actor.proxy if actor and actor.respond_to?(:proxy)
110
97
  end
111
98
  actors
@@ -114,25 +101,25 @@ module Celluloid
114
101
  # Watch for exit events from another actor
115
102
  def monitor(actor)
116
103
  raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
117
- Thread.current[:actor].linking_request(actor, :link)
104
+ Thread.current[:celluloid_actor].linking_request(actor, :link)
118
105
  end
119
106
 
120
107
  # Stop waiting for exit events from another actor
121
108
  def unmonitor(actor)
122
109
  raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
123
- Thread.current[:actor].linking_request(actor, :unlink)
110
+ Thread.current[:celluloid_actor].linking_request(actor, :unlink)
124
111
  end
125
112
 
126
113
  # Link to another actor
127
114
  def link(actor)
128
115
  monitor actor
129
- Thread.current[:actor].links << actor
116
+ Thread.current[:celluloid_actor].links << actor
130
117
  end
131
118
 
132
119
  # Unlink from another actor
133
120
  def unlink(actor)
134
121
  unmonitor actor
135
- Thread.current[:actor].links.delete actor
122
+ Thread.current[:celluloid_actor].links.delete actor
136
123
  end
137
124
 
138
125
  # Are we monitoring the given actor?
@@ -142,7 +129,7 @@ module Celluloid
142
129
 
143
130
  # Are we bidirectionally linked to the given actor?
144
131
  def linked_to?(actor)
145
- monitoring?(actor) && Thread.current[:actor].links.include?(actor)
132
+ monitoring?(actor) && Thread.current[:celluloid_actor].links.include?(actor)
146
133
  end
147
134
 
148
135
  # Forcibly kill a given actor
@@ -169,7 +156,7 @@ module Celluloid
169
156
  @exclusives = options[:exclusive_methods]
170
157
  @task_class = options[:task_class] || Celluloid.task_class
171
158
 
172
- @tasks = Set.new
159
+ @tasks = TaskSet.new
173
160
  @links = Links.new
174
161
  @signals = Signals.new
175
162
  @receivers = Receivers.new
@@ -179,12 +166,13 @@ module Celluloid
179
166
  @name = nil
180
167
 
181
168
  @thread = ThreadHandle.new do
182
- Thread.current[:actor] = self
183
- Thread.current[:mailbox] = @mailbox
169
+ Thread.current[:celluloid_actor] = self
170
+ Thread.current[:celluloid_mailbox] = @mailbox
184
171
  run
185
172
  end
186
173
 
187
- @proxy = ActorProxy.new(self)
174
+ @proxy = (options[:proxy_class] || ActorProxy).new(self)
175
+ @subject.instance_variable_set(OWNER_IVAR, self)
188
176
  end
189
177
 
190
178
  # Run the actor loop
@@ -221,23 +209,31 @@ module Celluloid
221
209
 
222
210
  # Execute a code block in exclusive mode.
223
211
  def exclusive
224
- @exclusive = true
225
- yield
226
- ensure
227
- @exclusive = false
212
+ if @exclusive
213
+ yield
214
+ else
215
+ begin
216
+ @exclusive = true
217
+ yield
218
+ ensure
219
+ @exclusive = false
220
+ end
221
+ end
228
222
  end
229
223
 
230
224
  # Perform a linking request with another actor
231
225
  def linking_request(receiver, type)
232
226
  exclusive do
233
227
  start_time = Time.now
234
-
235
228
  receiver.mailbox << LinkingRequest.new(Actor.current, type)
236
229
  system_events = []
230
+
237
231
  loop do
238
232
  wait_interval = start_time + LINKING_TIMEOUT - Time.now
239
233
  message = @mailbox.receive(wait_interval) do |msg|
240
- msg.is_a?(LinkingResponse) && msg.actor == receiver && msg.type == type
234
+ msg.is_a?(LinkingResponse) &&
235
+ msg.actor.mailbox.address == receiver.mailbox.address &&
236
+ msg.type == type
241
237
  end
242
238
 
243
239
  case message
@@ -300,15 +296,25 @@ module Celluloid
300
296
  @timers.every(interval) { task(:timer, &block) }
301
297
  end
302
298
 
299
+ class Sleeper
300
+ def initialize(timers, interval)
301
+ @timers = timers
302
+ @interval = interval
303
+ end
304
+
305
+ def before_suspend(task)
306
+ @timers.after(@interval) { task.resume }
307
+ end
308
+
309
+ def wait
310
+ Kernel.sleep(@interval)
311
+ end
312
+ end
313
+
303
314
  # Sleep for the given amount of time
304
315
  def sleep(interval)
305
- if Celluloid.exclusive?
306
- Kernel.sleep(interval)
307
- else
308
- task = Task.current
309
- @timers.after(interval) { task.resume }
310
- Task.suspend :sleeping
311
- end
316
+ sleeper = Sleeper.new(@timers, interval)
317
+ Celluloid.suspend(:sleeping, sleeper)
312
318
  end
313
319
 
314
320
  # Handle standard low-priority messages
@@ -337,11 +343,15 @@ module Celluloid
337
343
  @name = event.name
338
344
  when TerminationRequest
339
345
  @running = false
346
+ when SignalConditionRequest
347
+ event.call
340
348
  end
341
349
  end
342
350
 
343
351
  # Handle exit events received by this actor
344
352
  def handle_exit_event(event)
353
+ @links.delete event.actor
354
+
345
355
  # Run the exit handler if available
346
356
  return @subject.send(@exit_handler, event.actor, event.reason) if @exit_handler
347
357
 
@@ -363,14 +373,24 @@ module Celluloid
363
373
  run_finalizer
364
374
  cleanup exit_event
365
375
  ensure
366
- Thread.current[:actor] = nil
367
- Thread.current[:mailbox] = nil
376
+ Thread.current[:celluloid_actor] = nil
377
+ Thread.current[:celluloid_mailbox] = nil
368
378
  end
369
379
 
370
380
  # Run the user-defined finalizer, if one is set
371
381
  def run_finalizer
372
- return unless @subject.respond_to? :finalize
373
- task(:finalizer, :finalize) { @subject.finalize }
382
+ # FIXME: remove before Celluloid 1.0
383
+ if @subject.respond_to?(:finalize) && @subject.class.finalizer != :finalize
384
+ Logger.warn("DEPRECATION WARNING: #{@subject.class}#finalize is deprecated and will be removed in Celluloid 1.0. " +
385
+ "Define finalizers with '#{@subject.class}.finalizer :callback.'")
386
+
387
+ task(:finalizer, :finalize) { @subject.finalize }
388
+ end
389
+
390
+ finalizer = @subject.class.finalizer
391
+ if finalizer && @subject.respond_to?(finalizer, true)
392
+ task(:finalizer, :finalize) { @subject.__send__(finalizer) }
393
+ end
374
394
  rescue => ex
375
395
  Logger.crash("#{@subject.class}#finalize crashed!", ex)
376
396
  end
@@ -378,7 +398,14 @@ module Celluloid
378
398
  # Clean up after this actor
379
399
  def cleanup(exit_event)
380
400
  @mailbox.shutdown
381
- @links.send_event exit_event
401
+ @links.each do |actor|
402
+ begin
403
+ actor.mailbox << exit_event
404
+ rescue MailboxError
405
+ # We're exiting/crashing, they're dead. Give up :(
406
+ end
407
+ end
408
+
382
409
  tasks.each { |task| task.terminate }
383
410
  rescue => ex
384
411
  Logger.crash("#{@subject.class}: CLEANUP CRASHED!", ex)
@@ -386,10 +413,10 @@ module Celluloid
386
413
 
387
414
  # Run a method inside a task unless it's exclusive
388
415
  def task(task_type, method_name = nil, &block)
389
- if @exclusives && (@exclusives == :all || @exclusives.include?(method_name))
416
+ if @exclusives && (@exclusives == :all || (method_name && @exclusives.include?(method_name.to_sym)))
390
417
  exclusive { block.call }
391
418
  else
392
- @task_class.new(:message_handler, &block).resume
419
+ @task_class.new(task_type, &block).resume
393
420
  end
394
421
  end
395
422
  end
@@ -0,0 +1,16 @@
1
+ require 'celluloid'
2
+
3
+ Celluloid.boot
4
+
5
+ # Terminate all actors at exit
6
+ at_exit do
7
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" && RUBY_VERSION >= "1.9"
8
+ # workaround for MRI bug losing exit status in at_exit block
9
+ # http://bugs.ruby-lang.org/issues/5218
10
+ exit_status = $!.status if $!.is_a?(SystemExit)
11
+ Celluloid.shutdown
12
+ exit exit_status if exit_status
13
+ else
14
+ Celluloid.shutdown
15
+ end
16
+ end
@@ -7,70 +7,72 @@ 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
10
+ def dispatch(obj)
11
+ obj.public_send(@method, *@arguments, &@block)
12
+ rescue NoMethodError => ex
13
+ # Abort if the caller made a mistake
14
+ raise AbortError.new(ex) unless obj.respond_to? @method
14
15
 
16
+ # Otherwise something blew up. Crash this actor
17
+ raise
18
+ rescue ArgumentError => ex
19
+ # Abort if the caller made a mistake
15
20
  begin
16
21
  arity = obj.method(@method).arity
17
22
  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
+ # In theory this shouldn't happen, but just in case
24
+ raise AbortError.new(ex)
23
25
  end
24
26
 
25
27
  if arity >= 0
26
- if arguments.size != arity
27
- raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{arity})"
28
- end
28
+ raise AbortError.new(ex) if @arguments.size != arity
29
29
  elsif arity < -1
30
30
  mandatory_args = -arity - 1
31
- if arguments.size < mandatory_args
32
- raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{mandatory_args})"
31
+ raise AbortError.new(ex) if arguments.size < mandatory_args
32
+ end
33
+
34
+ # Otherwise something blew up. Crash this actor
35
+ raise
36
+ end
37
+
38
+ def wait
39
+ loop do
40
+ message = Thread.mailbox.receive do |msg|
41
+ msg.respond_to?(:call) and msg.call == self
33
42
  end
43
+ break message unless message.is_a?(SystemEvent)
44
+ Thread.current[:celluloid_actor].handle_system_event(message)
34
45
  end
35
46
  end
36
47
  end
37
48
 
38
49
  # Synchronous calls wait for a response
39
50
  class SyncCall < Call
40
- attr_reader :caller, :task
51
+ attr_reader :caller, :task, :chain_id
41
52
 
42
- def initialize(caller, method, arguments = [], block = nil, task = Thread.current[:task])
53
+ def initialize(caller, method, arguments = [], block = nil, task = Thread.current[:celluloid_task], chain_id = Thread.current[:celluloid_chain_id])
43
54
  super(method, arguments, block)
44
- @caller = caller
45
- @task = task
55
+
56
+ @caller = caller
57
+ @task = task
58
+ @chain_id = chain_id || Celluloid.uuid
46
59
  end
47
60
 
48
61
  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
-
62
+ Thread.current[:celluloid_chain_id] = @chain_id
63
+ result = super(obj)
73
64
  respond SuccessResponse.new(self, result)
65
+ rescue Exception => ex
66
+ # Exceptions that occur during synchronous calls are reraised in the
67
+ # context of the caller
68
+ respond ErrorResponse.new(self, ex)
69
+
70
+ # Aborting indicates a protocol error on the part of the caller
71
+ # It should crash the caller, but the exception isn't reraised
72
+ # Otherwise, it's a bug in this actor and should be reraised
73
+ raise unless ex.is_a?(AbortError)
74
+ ensure
75
+ Thread.current[:celluloid_chain_id] = nil
74
76
  end
75
77
 
76
78
  def cleanup
@@ -88,19 +90,17 @@ module Celluloid
88
90
 
89
91
  # Asynchronous calls don't wait for a response
90
92
  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
93
 
99
- obj.send(@method, *@arguments, &@block)
94
+ def dispatch(obj)
95
+ Thread.current[:celluloid_chain_id] = Celluloid.uuid
96
+ super(obj)
100
97
  rescue AbortError => ex
101
98
  # Swallow aborted async calls, as they indicate the caller made a mistake
102
- Logger.crash("#{obj.class}: async call `#{@method}' aborted!", ex.cause)
99
+ Logger.debug("#{obj.class}: async call `#@method` aborted!\n#{Logger.format_exception(ex.cause)}")
100
+ ensure
101
+ Thread.current[:celluloid_chain_id] = nil
103
102
  end
103
+
104
104
  end
105
- end
106
105
 
106
+ end
@@ -0,0 +1,70 @@
1
+ module Celluloid
2
+ class ConditionError < StandardError; end
3
+
4
+ # ConditionVariable-like signaling between tasks and actors
5
+ class Condition
6
+ attr_reader :owner
7
+
8
+ def initialize
9
+ @mutex = Mutex.new
10
+ @owner = Thread.current[:celluloid_actor]
11
+ @tasks = []
12
+ end
13
+
14
+ # Wait for the given signal and return the associated value
15
+ def wait
16
+ raise ConditionError, "cannot wait for signals while exclusive" if Celluloid.exclusive?
17
+
18
+ @mutex.synchronize do
19
+ actor = Thread.current[:celluloid_actor]
20
+ raise ConditionError, "can't wait for conditions outside actors" unless actor
21
+ raise ConditionError, "can't wait unless owner" unless actor == @owner
22
+ @tasks << Task.current
23
+ end
24
+
25
+ result = Task.suspend :condwait
26
+ raise result if result.is_a? ConditionError
27
+ result
28
+ end
29
+
30
+ # Send a signal to the first task waiting on this condition
31
+ def signal(value = nil)
32
+ @mutex.synchronize do
33
+ raise ConditionError, "no owner for this condition" unless @owner
34
+
35
+ if task = @tasks.shift
36
+ @owner.mailbox << SignalConditionRequest.new(task, value)
37
+ else
38
+ Logger.debug("Celluloid::Condition signaled spuriously")
39
+ end
40
+ end
41
+ end
42
+
43
+ # Broadcast a value to all waiting tasks
44
+ def broadcast(value = nil)
45
+ @mutex.synchronize do
46
+ raise ConditionError, "no owner for this condition" unless @owner
47
+
48
+ @tasks.each { |task| @owner.mailbox << SignalConditionRequest.new(task, value) }
49
+ @tasks.clear
50
+ end
51
+ end
52
+
53
+ # Change the owner of this condition
54
+ def owner=(actor)
55
+ @mutex.synchronize do
56
+ if @owner != actor
57
+ @tasks.each do |task|
58
+ ex = ConditionError.new("ownership changed")
59
+ @owner.mailbox << SignalConditionRequest.new(task, ex)
60
+ end
61
+ @tasks.clear
62
+ end
63
+
64
+ @owner = actor
65
+ end
66
+ end
67
+
68
+ alias_method :inspect, :to_s
69
+ end
70
+ 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
@@ -6,7 +6,11 @@ module Celluloid
6
6
  when 'darwin'
7
7
  @cores = Integer(`sysctl hw.ncpu`[/\d+/])
8
8
  when 'linux'
9
- @cores = File.read("/proc/cpuinfo").scan(/core id\s+: \d+/).uniq.size
9
+ @cores = if File.exists?("/sys/devices/system/cpu/present")
10
+ File.read("/sys/devices/system/cpu/present").split('-').last.to_i+1
11
+ else
12
+ Dir["/sys/devices/system/cpu/cpu*"].select { |n| n=~/cpu\d+/ }.count
13
+ end
10
14
  when 'mingw', 'mswin'
11
15
  @cores = Integer(`SET NUMBER_OF_PROCESSORS`[/\d+/])
12
16
  else
@@ -16,3 +20,5 @@ module Celluloid
16
20
  def self.cores; @cores; end
17
21
  end
18
22
  end
23
+
24
+