celluloid 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
- Celluloid
1
+ ![Celluloid](https://github.com/tarcieri/celluloid/raw/master/logo.png)
2
2
  =========
3
- [![Build Status](http://travis-ci.org/tarcieri/celluloid.png)](http://travis-ci.org/tarcieri/celluloid) [![Dependency Status](https://gemnasium.com/tarcieri/celluloid.png)](https://gemnasium.com/tarcieri/celluloid)
3
+ [![Build Status](https://secure.travis-ci.org/tarcieri/celluloid.png?branch=master)](http://travis-ci.org/tarcieri/celluloid)
4
+ [![Dependency Status](https://gemnasium.com/tarcieri/celluloid.png)](https://gemnasium.com/tarcieri/celluloid)
4
5
 
5
6
  > "I thought of objects being like biological cells and/or individual
6
7
  > computers on a network, only able to communicate with messages"
@@ -137,12 +138,18 @@ and [linking](https://github.com/tarcieri/celluloid/wiki/linking)
137
138
  [Please see the Celluloid Wiki](https://github.com/tarcieri/celluloid/wiki)
138
139
  for additional usage information.
139
140
 
141
+ Suggested Reading
142
+ -----------------
143
+
144
+ * [Concurrent Object-Oriented Programming in Python with ATOM](http://python.org/workshops/1997-10/proceedings/atom/)
145
+
140
146
  Contributing to Celluloid
141
147
  -------------------------
142
148
 
143
- * Fork Celluloid on github
149
+ * Fork this repository on github
144
150
  * Make your changes and send me a pull request
145
- * If I like them I'll merge them and give you commit access to my repository
151
+ * If I like them I'll merge them
152
+ * If I've accepted a patch, feel free to ask for commit access
146
153
 
147
154
  License
148
155
  -------
@@ -1,7 +1,9 @@
1
1
  require 'logger'
2
2
  require 'thread'
3
+ require 'timeout'
3
4
 
4
5
  module Celluloid
6
+ SHUTDOWN_TIMEOUT = 60 # How long actors have to terminate
5
7
  @logger = Logger.new STDERR
6
8
 
7
9
  class << self
@@ -16,6 +18,11 @@ module Celluloid
16
18
  !!Thread.current[:actor]
17
19
  end
18
20
 
21
+ # Is current actor running in exclusive mode?
22
+ def exclusive?
23
+ actor? and Thread.current[:actor].exclusive?
24
+ end
25
+
19
26
  # Obtain the currently running actor (if one exists)
20
27
  def current_actor
21
28
  actor = Thread.current[:actor]
@@ -43,14 +50,43 @@ module Celluloid
43
50
  Kernel.sleep interval
44
51
  end
45
52
  end
53
+
54
+ # Define an exception handler for actor crashes
55
+ def exception_handler(&block)
56
+ Logger.exception_handler(&block)
57
+ end
58
+
59
+ # Shut down all running actors
60
+ # FIXME: This should probably attempt a graceful shutdown of the supervision
61
+ # tree before iterating through all actors and telling them to terminate.
62
+ def shutdown
63
+ Timeout.timeout(SHUTDOWN_TIMEOUT) do
64
+ futures = Actor.all.each do |actor|
65
+ begin
66
+ actor.future(:terminate)
67
+ rescue DeadActorError, MailboxError
68
+ end
69
+ end
70
+
71
+ futures.each do |future|
72
+ begin
73
+ future.value
74
+ rescue DeadActorError, MailboxError
75
+ end
76
+ end
77
+ end
78
+ end
46
79
  end
47
80
 
81
+ # Terminate all actors at exit
82
+ at_exit { shutdown }
83
+
48
84
  # Class methods added to classes which include Celluloid
49
85
  module ClassMethods
50
86
  # Create a new actor
51
87
  def new(*args, &block)
52
88
  proxy = Actor.new(allocate).proxy
53
- proxy.send(:initialize, *args, &block)
89
+ proxy.send(:__send__, :initialize, *args, &block)
54
90
  proxy
55
91
  end
56
92
  alias_method :spawn, :new
@@ -62,7 +98,7 @@ module Celluloid
62
98
 
63
99
  proxy = Actor.new(allocate).proxy
64
100
  current_actor.link proxy
65
- proxy.send(:initialize, *args, &block)
101
+ proxy.send(:__send__, :initialize, *args, &block)
66
102
  proxy
67
103
  end
68
104
  alias_method :spawn_link, :new_link
@@ -90,11 +126,26 @@ module Celluloid
90
126
  # Configure a custom mailbox factory
91
127
  def use_mailbox(klass = nil, &block)
92
128
  if block
93
- define_method(:mailbox_factory, &block)
129
+ @mailbox_factory = block
130
+ else
131
+ @mailbox_factory = proc { klass.new }
132
+ end
133
+ end
134
+
135
+ # Create a mailbox for this actor
136
+ def mailbox_factory
137
+ if defined?(@mailbox_factory)
138
+ @mailbox_factory.call
139
+ elsif defined?(super)
140
+ super
94
141
  else
95
- define_method(:mailbox_factory) { klass.new }
142
+ Mailbox.new
96
143
  end
97
144
  end
145
+
146
+ def ===(other)
147
+ other.kind_of? self
148
+ end
98
149
  end
99
150
 
100
151
  #
@@ -193,7 +244,13 @@ module Celluloid
193
244
  Celluloid.sleep(interval)
194
245
  end
195
246
 
196
- # Call a block after a given interval
247
+ # Run given block in an exclusive mode: all synchronous calls block the whole
248
+ # actor, not only current message processing.
249
+ def exclusive(&block)
250
+ Thread.current[:actor].exclusive(&block)
251
+ end
252
+
253
+ # Call a block after a given interval, returning a Celluloid::Timer object
197
254
  def after(interval, &block)
198
255
  Thread.current[:actor].after(interval, &block)
199
256
  end
@@ -207,11 +264,6 @@ module Celluloid
207
264
  Future.new(&block).value
208
265
  end
209
266
 
210
- # Deprecated, do not use
211
- def async
212
- raise "Celluloid#async is defunct. Please use #defer instead"
213
- end
214
-
215
267
  # Process async calls via method_missing
216
268
  def method_missing(meth, *args, &block)
217
269
  # bang methods are async calls
@@ -245,6 +297,7 @@ require 'celluloid/fsm'
245
297
  require 'celluloid/links'
246
298
  require 'celluloid/logger'
247
299
  require 'celluloid/mailbox'
300
+ require 'celluloid/pool'
248
301
  require 'celluloid/receivers'
249
302
  require 'celluloid/registry'
250
303
  require 'celluloid/responses'
@@ -22,58 +22,64 @@ module Celluloid
22
22
  extend Registry
23
23
  attr_reader :proxy, :tasks, :links, :mailbox
24
24
 
25
- # Invoke a method on the given actor via its mailbox
26
- def self.call(mailbox, meth, *args, &block)
27
- call = SyncCall.new(Thread.mailbox, meth, args, block)
25
+ class << self
26
+ # Invoke a method on the given actor via its mailbox
27
+ def call(mailbox, meth, *args, &block)
28
+ call = SyncCall.new(Thread.mailbox, meth, args, block)
28
29
 
29
- begin
30
- mailbox << call
31
- rescue MailboxError
32
- raise DeadActorError, "attempted to call a dead actor"
33
- end
30
+ begin
31
+ mailbox << call
32
+ rescue MailboxError
33
+ raise DeadActorError, "attempted to call a dead actor"
34
+ end
34
35
 
35
- if Celluloid.actor?
36
- # The current task will be automatically resumed when we get a response
37
- Task.suspend :callwait
38
- else
39
- # Otherwise we're inside a normal thread, so block
40
- response = Thread.mailbox.receive do |msg|
41
- msg.respond_to?(:call) and msg.call == call
36
+ if Celluloid.actor? and not Celluloid.exclusive?
37
+ # The current task will be automatically resumed when we get a response
38
+ Task.suspend(:callwait).value
39
+ else
40
+ # Otherwise we're inside a normal thread, so block
41
+ response = Thread.mailbox.receive do |msg|
42
+ msg.respond_to?(:call) and msg.call == call
43
+ end
44
+
45
+ response.value
42
46
  end
47
+ end
43
48
 
44
- response.value
49
+ # Invoke a method asynchronously on an actor via its mailbox
50
+ def async(mailbox, meth, *args, &block)
51
+ begin
52
+ mailbox << AsyncCall.new(Thread.mailbox, meth, args, block)
53
+ rescue MailboxError
54
+ # Silently swallow asynchronous calls to dead actors. There's no way
55
+ # to reliably generate DeadActorErrors for async calls, so users of
56
+ # async calls should find other ways to deal with actors dying
57
+ # during an async call (i.e. linking/supervisors)
58
+ end
45
59
  end
46
- end
47
60
 
48
- # Invoke a method asynchronously on an actor via its mailbox
49
- def self.async(mailbox, meth, *args, &block)
50
- begin
51
- mailbox << AsyncCall.new(Thread.mailbox, meth, args, block)
52
- rescue MailboxError
53
- # Silently swallow asynchronous calls to dead actors. There's no way
54
- # to reliably generate DeadActorErrors for async calls, so users of
55
- # async calls should find other ways to deal with actors dying
56
- # during an async call (i.e. linking/supervisors)
61
+ # Call a method asynchronously and retrieve its value later
62
+ def future(mailbox, meth, *args, &block)
63
+ future = Future.new
64
+ future.execute(mailbox, meth, args, block)
65
+ future
57
66
  end
58
- end
59
67
 
60
- # Call a method asynchronously and retrieve its value later
61
- def self.future(mailbox, meth, *args, &block)
62
- future = Future.new
63
- future.execute(mailbox, meth, args, block)
64
- future
68
+ # Obtain all running actors in the system
69
+ def all
70
+ actors = []
71
+ Thread.list.each do |t|
72
+ actor = t[:actor]
73
+ actors << actor.proxy if actor
74
+ end
75
+ actors
76
+ end
65
77
  end
66
78
 
67
79
  # Wrap the given subject with an Actor
68
80
  def initialize(subject)
69
- @subject = subject
70
-
71
- if subject.respond_to? :mailbox_factory
72
- @mailbox = subject.mailbox_factory
73
- else
74
- @mailbox = Mailbox.new
75
- end
76
-
81
+ @subject = subject
82
+ @mailbox = subject.class.mailbox_factory
77
83
  @proxy = ActorProxy.new(@mailbox, subject.class.to_s)
78
84
  @tasks = Set.new
79
85
  @links = Links.new
@@ -81,11 +87,11 @@ module Celluloid
81
87
  @receivers = Receivers.new
82
88
  @timers = Timers.new
83
89
  @running = true
90
+ @exclusive = false
84
91
 
85
92
  @thread = ThreadPool.get do
86
93
  Thread.current[:actor] = self
87
94
  Thread.current[:mailbox] = @mailbox
88
-
89
95
  run
90
96
  end
91
97
  end
@@ -95,6 +101,19 @@ module Celluloid
95
101
  @running
96
102
  end
97
103
 
104
+ # Is this actor running in exclusive mode?
105
+ def exclusive?
106
+ @exclusive
107
+ end
108
+
109
+ # Execute a code block in exclusive mode.
110
+ def exclusive
111
+ @exclusive = true
112
+ yield
113
+ ensure
114
+ @exclusive = false
115
+ end
116
+
98
117
  # Terminate this actor
99
118
  def terminate
100
119
  @running = false
@@ -118,32 +137,30 @@ module Celluloid
118
137
 
119
138
  # Run the actor loop
120
139
  def run
121
- while @running
122
- begin
123
- message = @mailbox.receive(timeout)
124
- rescue ExitEvent => exit_event
125
- Task.new(:exit_handler) { handle_exit_event exit_event }.resume
126
- retry
127
- end
128
-
129
- if message
130
- handle_message message
131
- else
132
- # No message indicates a timeout
133
- @timers.fire
134
- @receivers.fire_timers
140
+ begin
141
+ while @running
142
+ begin
143
+ message = @mailbox.receive(timeout)
144
+ rescue ExitEvent => exit_event
145
+ Task.new(:exit_handler) { handle_exit_event exit_event }.resume
146
+ retry
147
+ end
148
+
149
+ if message
150
+ handle_message message
151
+ else
152
+ # No message indicates a timeout
153
+ @timers.fire
154
+ @receivers.fire_timers
155
+ end
135
156
  end
157
+ rescue MailboxShutdown
158
+ # If the mailbox detects shutdown, exit the actor
136
159
  end
137
160
 
138
- cleanup ExitEvent.new(@proxy)
139
- rescue MailboxShutdown
140
- # If the mailbox detects shutdown, exit the actor
141
- @running = false
142
- rescue Exception => ex
143
- @running = false
161
+ shutdown
162
+ rescue => ex
144
163
  handle_crash(ex)
145
- ensure
146
- ThreadPool.put @thread
147
164
  end
148
165
 
149
166
  # How long to wait until the next timer fires
@@ -169,9 +186,13 @@ module Celluloid
169
186
 
170
187
  # Sleep for the given amount of time
171
188
  def sleep(interval)
172
- task = Task.current
173
- @timers.add(interval) { task.resume }
174
- Task.suspend :sleeping
189
+ if Celluloid.exclusive?
190
+ Kernel.sleep(interval)
191
+ else
192
+ task = Task.current
193
+ @timers.add(interval) { task.resume }
194
+ Task.suspend :sleeping
195
+ end
175
196
  end
176
197
 
177
198
  # Handle an incoming message
@@ -180,7 +201,7 @@ module Celluloid
180
201
  when Call
181
202
  Task.new(:message_handler) { message.dispatch(@subject) }.resume
182
203
  when Response
183
- message.call.task.resume message.value
204
+ message.call.task.resume message
184
205
  else
185
206
  @receivers.handle_message(message)
186
207
  end
@@ -202,22 +223,34 @@ module Celluloid
202
223
  # Handle any exceptions that occur within a running actor
203
224
  def handle_crash(exception)
204
225
  Logger.crash("#{@subject.class} crashed!", exception)
205
- cleanup ExitEvent.new(@proxy, exception)
206
- rescue Exception => ex
226
+ shutdown ExitEvent.new(@proxy, exception)
227
+ rescue => ex
207
228
  Logger.crash("#{@subject.class}: ERROR HANDLER CRASHED!", ex)
208
229
  end
209
230
 
210
231
  # Handle cleaning up this actor after it exits
232
+ def shutdown(exit_event = ExitEvent.new(@proxy))
233
+ run_finalizer
234
+ cleanup exit_event
235
+ ensure
236
+ Thread.current[:actor] = nil
237
+ Thread.current[:mailbox] = nil
238
+ end
239
+
240
+ # Run the user-defined finalizer, if one is set
241
+ def run_finalizer
242
+ @subject.finalize if @subject.respond_to? :finalize
243
+ rescue => ex
244
+ Logger.crash("#{@subject.class}#finalize crashed!", ex)
245
+ end
246
+
247
+ # Clean up after this actor
211
248
  def cleanup(exit_event)
212
249
  @mailbox.shutdown
213
250
  @links.send_event exit_event
214
251
  tasks.each { |task| task.terminate }
215
-
216
- begin
217
- @subject.finalize if @subject.respond_to? :finalize
218
- rescue Exception => ex
219
- Logger.crash("#{@subject.class}#finalize crashed!", ex)
220
- end
252
+ rescue => ex
253
+ Logger.crash("#{@subject.class}: CLEANUP CRASHED!", ex)
221
254
  end
222
255
  end
223
256
  end
@@ -17,6 +17,14 @@ module Celluloid
17
17
  Actor.call @mailbox, :send, :class
18
18
  end
19
19
 
20
+ def is_a?(klass)
21
+ Actor.call @mailbox, :is_a?, klass
22
+ end
23
+
24
+ def kind_of?(klass)
25
+ Actor.call @mailbox, :kind_of?, klass
26
+ end
27
+
20
28
  def respond_to?(meth)
21
29
  Actor.call @mailbox, :respond_to?, meth
22
30
  end
@@ -18,8 +18,6 @@ module Celluloid
18
18
  @call.dispatch(block)
19
19
  rescue
20
20
  # Exceptions in blocks will get raised when the value is retrieved
21
- ensure
22
- ThreadPool.put Thread.current
23
21
  end
24
22
  end
25
23
  else
@@ -52,6 +52,9 @@ module Celluloid
52
52
  supervisable = @supervisors.delete supervisor
53
53
  raise "a supervisable went missing. This shouldn't be!" unless supervisable
54
54
 
55
+ # Ignore supervisors that shut down cleanly
56
+ return unless reason
57
+
55
58
  supervisor = supervisable.supervise
56
59
  @supervisors[supervisor] = supervisable
57
60
  end
@@ -1,5 +1,6 @@
1
1
  module Celluloid
2
2
  module Logger
3
+ @exception_handlers = []
3
4
  module_function
4
5
 
5
6
  # Send a debug message
@@ -24,9 +25,29 @@ module Celluloid
24
25
 
25
26
  # Handle a crash
26
27
  def crash(string, exception)
27
- string += "\n#{exception.class}: #{exception.to_s}\n"
28
- string << exception.backtrace.join("\n")
29
- error(string)
28
+ string << "\n" << format_exception(exception)
29
+ error string
30
+
31
+ @exception_handlers.each do |handler|
32
+ begin
33
+ handler.call(exception)
34
+ rescue => ex
35
+ error "EXCEPTION HANDLER CRASHED:\n" << format_exception(ex)
36
+ end
37
+ end
38
+ end
39
+
40
+ # Format an exception message
41
+ def format_exception(exception)
42
+ str = "#{exception.class}: #{exception.to_s}\n"
43
+ str << exception.backtrace.join("\n")
44
+ end
45
+
46
+ # Define an exception handler
47
+ # NOTE: These should be defined at application start time
48
+ def exception_handler(&block)
49
+ @exception_handlers << block
50
+ nil
30
51
  end
31
52
  end
32
53
  end
@@ -14,59 +14,65 @@ module Celluloid
14
14
 
15
15
  def initialize
16
16
  @messages = []
17
- @lock = Mutex.new
17
+ @mutex = Mutex.new
18
18
  @dead = false
19
19
  @condition = ConditionVariable.new
20
20
  end
21
21
 
22
22
  # Add a message to the Mailbox
23
23
  def <<(message)
24
- @lock.lock
25
- raise MailboxError, "dead recipient" if @dead
24
+ @mutex.lock
25
+ begin
26
+ raise MailboxError, "dead recipient" if @dead
26
27
 
27
- @messages << message
28
- @condition.signal
29
- nil
30
- ensure @lock.unlock
28
+ @messages << message
29
+ @condition.signal
30
+ nil
31
+ ensure @mutex.unlock
32
+ end
31
33
  end
32
34
 
33
35
  # Add a high-priority system event to the Mailbox
34
36
  def system_event(event)
35
- @lock.lock
36
- unless @dead # Silently fail if messages are sent to dead actors
37
- @messages.unshift event
38
- @condition.signal
37
+ @mutex.lock
38
+ begin
39
+ unless @dead # Silently fail if messages are sent to dead actors
40
+ @messages.unshift event
41
+ @condition.signal
42
+ end
43
+ nil
44
+ ensure @mutex.unlock
39
45
  end
40
- nil
41
- ensure @lock.unlock
42
46
  end
43
47
 
44
48
  # Receive a message from the Mailbox
45
49
  def receive(timeout = nil, &block)
46
50
  message = nil
47
51
 
48
- @lock.lock
49
- raise MailboxError, "attempted to receive from a dead mailbox" if @dead
50
-
52
+ @mutex.lock
51
53
  begin
52
- message = next_message(&block)
53
-
54
- unless message
55
- if timeout
56
- now = Time.now
57
- wait_until ||= now + timeout
58
- wait_interval = wait_until - now
59
- return if wait_interval < 0
60
- else
61
- wait_interval = nil
54
+ raise MailboxError, "attempted to receive from a dead mailbox" if @dead
55
+
56
+ begin
57
+ message = next_message(&block)
58
+
59
+ unless message
60
+ if timeout
61
+ now = Time.now
62
+ wait_until ||= now + timeout
63
+ wait_interval = wait_until - now
64
+ return if wait_interval <= 0
65
+ else
66
+ wait_interval = nil
67
+ end
68
+
69
+ @condition.wait(@mutex, wait_interval)
62
70
  end
71
+ end until message
63
72
 
64
- @condition.wait(@lock, wait_interval)
65
- end
66
- end until message
67
-
68
- message
69
- ensure @lock.unlock
73
+ message
74
+ ensure @mutex.unlock
75
+ end
70
76
  end
71
77
 
72
78
  # Retrieve the next message in the mailbox
@@ -89,16 +95,16 @@ module Celluloid
89
95
 
90
96
  # Shut down this mailbox and clean up its contents
91
97
  def shutdown
92
- messages = nil
93
-
94
- @lock.lock
95
- messages = @messages
96
- @messages = []
97
- @dead = true
98
+ @mutex.lock
99
+ begin
100
+ messages = @messages
101
+ @messages = []
102
+ @dead = true
103
+ ensure @mutex.unlock
104
+ end
98
105
 
99
106
  messages.each { |msg| msg.cleanup if msg.respond_to? :cleanup }
100
107
  true
101
- ensure @lock.unlock
102
108
  end
103
109
 
104
110
  # Is the mailbox alive?
@@ -108,9 +114,7 @@ module Celluloid
108
114
 
109
115
  # Cast to an array
110
116
  def to_a
111
- @lock.lock
112
- @messages.dup
113
- ensure @lock.unlock
117
+ @mutex.synchronize { @messages.dup }
114
118
  end
115
119
 
116
120
  # Iterate through the mailbox
@@ -0,0 +1,103 @@
1
+ module Celluloid
2
+ # Pools provide groups of actors which can service requests
3
+ class Pool
4
+ include Celluloid
5
+ trap_exit :crash_handler
6
+
7
+ # Takes a class of actor to pool and a hash of options:
8
+ #
9
+ # * initial_size: how many actors to eagerly create
10
+ # * max_size: maximum number of actors (default nil, unlimited)
11
+ # * args: an array of arguments to pass to the actor's initialize
12
+ def initialize(klass, options = {})
13
+ opts = {
14
+ :initial_size => 1,
15
+ :max_size => nil,
16
+ :args => []
17
+ }.merge(options)
18
+
19
+ @klass, @args = klass, opts[:args]
20
+ @max_actors = opts[:max_size]
21
+ @idle_actors, @running_actors = 0, 0
22
+ @actors = []
23
+
24
+ opts[:initial_size].times do
25
+ @actors << spawn
26
+ @idle_actors += 1
27
+ end
28
+ end
29
+
30
+ # Get an actor from the pool. Actors taken from the pool must be put back
31
+ # with Pool#put. Alternatively, you can use get with a block form:
32
+ #
33
+ # pool.get { |actor| ... }
34
+ #
35
+ # This will automatically return actors to the pool when the block completes
36
+ def get
37
+ if @max_actors and @running_actors == @max_actors
38
+ wait :ready
39
+ end
40
+
41
+ actor = @actors.pop
42
+ if actor
43
+ @idle_actors -= 1
44
+ else
45
+ actor = spawn
46
+ end
47
+
48
+ if block_given?
49
+ begin
50
+ yield actor
51
+ rescue => ex
52
+ end
53
+
54
+ put actor
55
+ abort ex if ex
56
+ nil
57
+ else
58
+ actor
59
+ end
60
+ end
61
+
62
+ # Return an actor to the pool
63
+ def put(actor)
64
+ begin
65
+ raise TypeError, "expecting a #{@klass} actor" unless actor.is_a? @klass
66
+ rescue DeadActorError
67
+ # The actor may have died before it was handed back to us
68
+ # We'll let the crash_handler deal with it in due time
69
+ return
70
+ end
71
+
72
+ @actors << actor
73
+ @idle_actors += 1
74
+ end
75
+
76
+ # Number of active actors in this pool
77
+ def size
78
+ @running_actors
79
+ end
80
+
81
+ # Number of idle actors in the pool
82
+ def idle_count
83
+ @idle_actors
84
+ end
85
+ alias_method :idle_size, :idle_count
86
+
87
+ # Handle crashed actors
88
+ def crash_handler(actor, reason)
89
+ @idle_actors -= 1 if @actors.delete actor
90
+ @running_actors -= 1
91
+
92
+ # If we were maxed out before...
93
+ signal :ready if @max_actors and @running_actors + 1 == @max_actors
94
+ end
95
+
96
+ # Spawn an actor of the given class
97
+ def spawn
98
+ worker = @klass.new_link(*@args)
99
+ @running_actors += 1
100
+ worker
101
+ end
102
+ end
103
+ end
@@ -10,17 +10,21 @@ module Celluloid
10
10
 
11
11
  # Receive an asynchronous message
12
12
  def receive(timeout = nil, &block)
13
- receiver = Receiver.new block
13
+ if Celluloid.exclusive?
14
+ Thread.mailbox.receive(timeout, &block)
15
+ else
16
+ receiver = Receiver.new block
14
17
 
15
- if timeout
16
- receiver.timer = @timers.add(timeout) do
17
- @receivers.delete receiver
18
- receiver.resume
18
+ if timeout
19
+ receiver.timer = @timers.add(timeout) do
20
+ @receivers.delete receiver
21
+ receiver.resume
22
+ end
19
23
  end
20
- end
21
24
 
22
- @receivers << receiver
23
- Task.suspend :receiving
25
+ @receivers << receiver
26
+ Task.suspend :receiving
27
+ end
24
28
  end
25
29
 
26
30
  # How long to wait until the next timer fires
@@ -9,8 +9,9 @@ module Celluloid
9
9
 
10
10
  # Wait for the given signal and return the associated value
11
11
  def wait(signal)
12
- tasks = @waiting[signal]
12
+ raise "cannot wait for signals while exclusive" if Celluloid.exclusive?
13
13
 
14
+ tasks = @waiting[signal]
14
15
  case tasks
15
16
  when Array
16
17
  tasks << Task.current
@@ -28,7 +28,7 @@ module Celluloid
28
28
 
29
29
  begin
30
30
  @actor = @klass.new_link(*@args, &@block)
31
- rescue => ex
31
+ rescue
32
32
  failures += 1
33
33
  if failures >= start_attempts
34
34
  failures = 0
@@ -45,6 +45,9 @@ module Celluloid
45
45
 
46
46
  # When actors die, regardless of the reason, restart them
47
47
  def restart_actor(actor, reason)
48
+ # If the actor we're supervising exited cleanly, exit the supervisor cleanly too
49
+ terminate unless reason
50
+
48
51
  start_actor if @started
49
52
  end
50
53
 
@@ -4,7 +4,7 @@ module Celluloid
4
4
  # Maintain a thread pool FOR SPEED!!
5
5
  module ThreadPool
6
6
  @pool = []
7
- @lock = Mutex.new
7
+ @mutex = Mutex.new
8
8
 
9
9
  # TODO: should really adjust this based on usage
10
10
  @max_idle = 16
@@ -14,7 +14,7 @@ module Celluloid
14
14
 
15
15
  # Get a thread from the pool, running the given block
16
16
  def get(&block)
17
- @lock.synchronize do
17
+ @mutex.synchronize do
18
18
  if @pool.empty?
19
19
  thread = create
20
20
  else
@@ -28,7 +28,7 @@ module Celluloid
28
28
 
29
29
  # Return a thread to the pool
30
30
  def put(thread)
31
- @lock.synchronize do
31
+ @mutex.synchronize do
32
32
  if @pool.size >= @max_idle
33
33
  thread[:queue] << nil
34
34
  else
@@ -41,14 +41,17 @@ module Celluloid
41
41
  def create
42
42
  queue = Queue.new
43
43
  thread = Thread.new do
44
- begin
45
- while func = queue.pop
46
- func.call
44
+ while proc = queue.pop
45
+ begin
46
+ proc.call
47
+ rescue => ex
48
+ Logger.crash("thread crashed", ex)
47
49
  end
48
- rescue Exception => ex
49
- Logger.crash("#{self} internal failure", ex)
50
+
51
+ put thread
50
52
  end
51
53
  end
54
+
52
55
  thread[:queue] = queue
53
56
  thread
54
57
  end
@@ -1,4 +1,4 @@
1
1
  module Celluloid
2
- VERSION = '0.8.0'
2
+ VERSION = '0.9.0'
3
3
  def self.version; VERSION; end
4
4
  end
@@ -49,6 +49,15 @@ shared_context "a Celluloid Actor" do |included_module|
49
49
  actor.class.should == actor_class
50
50
  end
51
51
 
52
+ it "compares with the actor's class in a case statement" do
53
+ case actor_class.new("Troy McClure")
54
+ when actor_class
55
+ true
56
+ else
57
+ false
58
+ end.should be_true
59
+ end
60
+
52
61
  it "handles synchronous calls" do
53
62
  actor = actor_class.new "Troy McClure"
54
63
  actor.greet.should == "Hi, I'm Troy McClure"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: celluloid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-24 00:00:00.000000000 Z
12
+ date: 2012-02-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- requirement: &70145052114120 !ruby/object:Gem::Requirement
16
+ requirement: &70248586219120 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,21 +21,21 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70145052114120
24
+ version_requirements: *70248586219120
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70145052113520 !ruby/object:Gem::Requirement
27
+ requirement: &70248586218540 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
- - - ~>
30
+ - - ! '>='
31
31
  - !ruby/object:Gem::Version
32
- version: 2.7.0
32
+ version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70145052113520
35
+ version_requirements: *70248586218540
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: benchmark_suite
38
- requirement: &70145052113020 !ruby/object:Gem::Requirement
38
+ requirement: &70248586217360 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70145052113020
46
+ version_requirements: *70248586217360
47
47
  description: Celluloid is a concurrent object framework inspired by the Actor Model
48
48
  email:
49
49
  - tony.arcieri@gmail.com
@@ -64,6 +64,7 @@ files:
64
64
  - lib/celluloid/links.rb
65
65
  - lib/celluloid/logger.rb
66
66
  - lib/celluloid/mailbox.rb
67
+ - lib/celluloid/pool.rb
67
68
  - lib/celluloid/receivers.rb
68
69
  - lib/celluloid/registry.rb
69
70
  - lib/celluloid/responses.rb