celluloid 0.11.1 → 0.12.0.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
@@ -1,4 +1,4 @@
1
- ![Celluloid](https://github.com/celluloid/celluloid/raw/master/logo.png)
1
+ ![Celluloid](https://raw.github.com/celluloid/celluloid-logos/master/celluloid/celluloid.png)
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)
@@ -17,7 +17,7 @@ Much of the difficulty with building concurrent programs in Ruby arises because
17
17
  the object-oriented mechanisms for structuring code, such as classes and
18
18
  inheritance, are separate from the concurrency mechanisms, such as threads and
19
19
  locks. Celluloid combines these into a single structure, an active object
20
- running within a thread, called an "actor".
20
+ running within a thread, called an "actor", or in Celluloid vernacular, a "cell".
21
21
 
22
22
  By combining concurrency with object oriented programming, Celluloid frees you
23
23
  up from worry about where to use threads and locks. Celluloid combines them
@@ -72,15 +72,32 @@ for more detailed documentation and usage notes.
72
72
  Like Celluloid? [Join the Google Group](http://groups.google.com/group/celluloid-ruby)
73
73
  or visit us on IRC at #celluloid on freenode
74
74
 
75
- ### Is it any good?
76
-
77
- [Yes.](http://news.ycombinator.com/item?id=3067434)
78
-
79
75
  ### Is It "Production Ready™"?
80
76
 
81
77
  Yes, many users are now running Celluloid in production by using
82
78
  [Sidekiq](https://github.com/mperham/sidekiq)
83
79
 
80
+ Installation
81
+ ------------
82
+
83
+ Add this line to your application's Gemfile:
84
+
85
+ gem 'celluloid'
86
+
87
+ And then execute:
88
+
89
+ $ bundle
90
+
91
+ Or install it yourself as:
92
+
93
+ $ gem install celluloid
94
+
95
+ Inside of your Ruby program do:
96
+
97
+ require 'celluloid'
98
+
99
+ ...to pull it in as a dependency.
100
+
84
101
  Supported Platforms
85
102
  -------------------
86
103
 
@@ -4,14 +4,17 @@ require 'timeout'
4
4
  require 'set'
5
5
 
6
6
  module Celluloid
7
+ extend self # expose all instance methods as singleton methods
8
+
7
9
  SHUTDOWN_TIMEOUT = 120 # How long actors have to terminate
8
- @logger = Logger.new STDERR
9
10
 
10
11
  class << self
11
- attr_accessor :logger # Thread-safe logger class
12
+ attr_accessor :logger # Thread-safe logger class
13
+ attr_accessor :task_class # Default task type to use
12
14
 
13
15
  def included(klass)
14
- klass.send :extend, ClassMethods
16
+ klass.send :extend, ClassMethods
17
+ klass.send :include, InstanceMethods
15
18
  end
16
19
 
17
20
  # Are we currently inside of an actor?
@@ -19,36 +22,6 @@ module Celluloid
19
22
  !!Thread.current[:actor]
20
23
  end
21
24
 
22
- # Is current actor running in exclusive mode?
23
- def exclusive?
24
- actor? and Thread.current[:actor].exclusive?
25
- end
26
-
27
- # Obtain the currently running actor (if one exists)
28
- def current_actor
29
- Actor.current
30
- end
31
-
32
- # Receive an asynchronous message
33
- def receive(timeout = nil, &block)
34
- actor = Thread.current[:actor]
35
- if actor
36
- actor.receive(timeout, &block)
37
- else
38
- Thread.mailbox.receive(timeout, &block)
39
- end
40
- end
41
-
42
- # Sleep letting the actor continue processing messages
43
- def sleep(interval)
44
- actor = Thread.current[:actor]
45
- if actor
46
- actor.sleep(interval)
47
- else
48
- Kernel.sleep interval
49
- end
50
- end
51
-
52
25
  # Generate a Universally Unique Identifier
53
26
  def uuid
54
27
  UUID.generate
@@ -67,8 +40,6 @@ module Celluloid
67
40
  end
68
41
 
69
42
  # Shut down all running actors
70
- # FIXME: This should probably attempt a graceful shutdown of the supervision
71
- # tree before iterating through all actors and telling them to terminate.
72
43
  def shutdown
73
44
  Timeout.timeout(SHUTDOWN_TIMEOUT) do
74
45
  actors = Actor.all
@@ -78,16 +49,16 @@ module Celluloid
78
49
  Supervisor.root.terminate if Supervisor.root
79
50
 
80
51
  # Actors cannot self-terminate, you must do it for them
81
- terminators = Actor.all.each do |actor|
52
+ Actor.all.each do |actor|
82
53
  begin
83
- actor.future(:terminate)
54
+ actor.terminate!
84
55
  rescue DeadActorError, MailboxError
85
56
  end
86
57
  end
87
58
 
88
- terminators.each do |terminator|
59
+ Actor.all.each do |actor|
89
60
  begin
90
- terminator.value
61
+ Actor.join(actor)
91
62
  rescue DeadActorError, MailboxError
92
63
  end
93
64
  end
@@ -104,7 +75,7 @@ module Celluloid
104
75
  module ClassMethods
105
76
  # Create a new actor
106
77
  def new(*args, &block)
107
- proxy = Actor.new(allocate).proxy
78
+ proxy = Actor.new(allocate, actor_options).proxy
108
79
  proxy._send_(:initialize, *args, &block)
109
80
  proxy
110
81
  end
@@ -112,11 +83,10 @@ module Celluloid
112
83
 
113
84
  # Create a new actor and link to the current one
114
85
  def new_link(*args, &block)
115
- current_actor = Actor.current
116
- raise NotActorError, "can't link outside actor context" unless current_actor
86
+ raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
117
87
 
118
- proxy = Actor.new(allocate).proxy
119
- current_actor.link proxy
88
+ proxy = Actor.new(allocate, actor_options).proxy
89
+ Actor.link(proxy)
120
90
  proxy._send_(:initialize, *args, &block)
121
91
  proxy
122
92
  end
@@ -158,9 +128,6 @@ module Celluloid
158
128
  @exit_handler = callback.to_sym
159
129
  end
160
130
 
161
- # Obtain the exit handler for this actor
162
- attr_reader :exit_handler
163
-
164
131
  # Configure a custom mailbox factory
165
132
  def use_mailbox(klass = nil, &block)
166
133
  if block
@@ -170,31 +137,94 @@ module Celluloid
170
137
  end
171
138
  end
172
139
 
140
+ # Define the default task type for this class
141
+ def task_class(klass)
142
+ @task_class = klass
143
+ end
144
+
173
145
  # Mark methods as running exclusively
174
146
  def exclusive(*methods)
175
- @exclusive_methods ||= Set.new
176
- @exclusive_methods.merge methods.map(&:to_sym)
147
+ if methods.empty?
148
+ @exclusive_methods = :all
149
+ elsif @exclusive_methods != :all
150
+ @exclusive_methods ||= Set.new
151
+ @exclusive_methods.merge methods.map(&:to_sym)
152
+ end
177
153
  end
178
- attr_reader :exclusive_methods
179
154
 
180
155
  # Create a mailbox for this actor
181
156
  def mailbox_factory
182
157
  if defined?(@mailbox_factory)
183
158
  @mailbox_factory.call
184
- elsif defined?(super)
185
- super
159
+ elsif superclass.respond_to? :mailbox_factory
160
+ superclass.mailbox_factory
186
161
  else
187
162
  Mailbox.new
188
163
  end
189
164
  end
190
165
 
166
+ # Configuration options for Actor#new
167
+ def actor_options
168
+ {
169
+ :mailbox => mailbox_factory,
170
+ :exit_handler => @exit_handler,
171
+ :exclusive_methods => @exclusive_methods,
172
+ :task_class => @task_class,
173
+ }
174
+ end
175
+
191
176
  def ===(other)
192
177
  other.kind_of? self
193
178
  end
194
179
  end
195
180
 
181
+ # These are methods we don't want added to the Celluloid singleton but to be
182
+ # defined on all classes that use Celluloid
183
+ module InstanceMethods
184
+ # Obtain the Ruby object the actor is wrapping. This should ONLY be used
185
+ # for a limited set of use cases like runtime metaprogramming. Interacting
186
+ # directly with the wrapped object foregoes any kind of thread safety that
187
+ # Celluloid would ordinarily provide you, and the object is guaranteed to
188
+ # be shared with at least the actor thread. Tread carefully.
189
+ def wrapped_object; self; end
190
+
191
+ def inspect
192
+ str = "#<Celluloid::Actor(#{self.class}:0x#{object_id.to_s(16)})"
193
+ ivars = instance_variables.map do |ivar|
194
+ "#{ivar}=#{instance_variable_get(ivar).inspect}"
195
+ end
196
+
197
+ str << " " << ivars.join(' ') unless ivars.empty?
198
+ str << ">"
199
+ end
200
+
201
+ # Process async calls via method_missing
202
+ def method_missing(meth, *args, &block)
203
+ # bang methods are async calls
204
+ if meth.to_s.match(/!$/)
205
+ unbanged_meth = meth.to_s.sub(/!$/, '')
206
+ args.unshift unbanged_meth
207
+
208
+ call = AsyncCall.new(:__send__, args, block)
209
+ begin
210
+ Thread.current[:actor].mailbox << call
211
+ rescue MailboxError
212
+ # Silently swallow asynchronous calls to dead actors. There's no way
213
+ # to reliably generate DeadActorErrors for async calls, so users of
214
+ # async calls should find other ways to deal with actors dying
215
+ # during an async call (i.e. linking/supervisors)
216
+ end
217
+
218
+ return
219
+ end
220
+
221
+ super
222
+ end
223
+ end
224
+
196
225
  #
197
- # Instance methods
226
+ # The following methods are available on both the Celluloid singleton and
227
+ # directly inside of all classes that include Celluloid
198
228
  #
199
229
 
200
230
  # Is this actor alive?
@@ -217,16 +247,6 @@ module Celluloid
217
247
  Thread.current[:actor].terminate
218
248
  end
219
249
 
220
- def inspect
221
- str = "#<Celluloid::Actor(#{self.class}:0x#{object_id.to_s(16)})"
222
- ivars = instance_variables.map do |ivar|
223
- "#{ivar}=#{instance_variable_get(ivar).inspect}"
224
- end
225
-
226
- str << " " << ivars.join(' ') unless ivars.empty?
227
- str << ">"
228
- end
229
-
230
250
  # Send a signal with the given name to all waiting methods
231
251
  def signal(name, value = nil)
232
252
  Thread.current[:actor].signal name, value
@@ -252,51 +272,59 @@ module Celluloid
252
272
  Thread.current[:actor].tasks.to_a
253
273
  end
254
274
 
255
- # Obtain the Ruby object the actor is wrapping. This should ONLY be used
256
- # for a limited set of use cases like runtime metaprogramming. Interacting
257
- # directly with the wrapped object foregoes any kind of thread safety that
258
- # Celluloid would ordinarily provide you, and the object is guaranteed to
259
- # be shared with at least the actor thread. Tread carefully.
260
- def wrapped_object; self; end
261
-
262
275
  # Obtain the Celluloid::Links for this actor
263
276
  def links
264
277
  Thread.current[:actor].links
265
278
  end
266
279
 
280
+ # Watch for exit events from another actor
281
+ def monitor(actor)
282
+ Actor.monitor(actor)
283
+ end
284
+
285
+ # Stop waiting for exit events from another actor
286
+ def unmonitor(actor)
287
+ Actor.unmonitor(actor)
288
+ end
289
+
267
290
  # Link this actor to another, allowing it to crash or react to errors
268
291
  def link(actor)
269
- actor.notify_link Actor.current
270
- notify_link actor
292
+ Actor.link(actor)
271
293
  end
272
294
 
273
295
  # Remove links to another actor
274
296
  def unlink(actor)
275
- actor.notify_unlink Actor.current
276
- notify_unlink actor
277
- end
278
-
279
- def notify_link(actor)
280
- links << actor
297
+ Actor.unlink(actor)
281
298
  end
282
299
 
283
- def notify_unlink(actor)
284
- links.delete actor
300
+ # Are we monitoring another actor?
301
+ def monitoring?(actor)
302
+ Actor.monitoring?(actor)
285
303
  end
286
304
 
287
305
  # Is this actor linked to another?
288
306
  def linked_to?(actor)
289
- Thread.current[:actor].links.include? actor
307
+ Actor.linked_to?(actor)
290
308
  end
291
309
 
292
310
  # Receive an asynchronous message via the actor protocol
293
311
  def receive(timeout = nil, &block)
294
- Celluloid.receive(timeout, &block)
312
+ actor = Thread.current[:actor]
313
+ if actor
314
+ actor.receive(timeout, &block)
315
+ else
316
+ Thread.mailbox.receive(timeout, &block)
317
+ end
295
318
  end
296
319
 
297
- # Sleep while letting the actor continue to receive messages
320
+ # Sleep letting the actor continue processing messages
298
321
  def sleep(interval)
299
- Celluloid.sleep(interval)
322
+ actor = Thread.current[:actor]
323
+ if actor
324
+ actor.sleep(interval)
325
+ else
326
+ Kernel.sleep interval
327
+ end
300
328
  end
301
329
 
302
330
  # Run given block in an exclusive mode: all synchronous calls block the whole
@@ -307,7 +335,8 @@ module Celluloid
307
335
 
308
336
  # Are we currently exclusive
309
337
  def exclusive?
310
- Celluloid.exclusive?
338
+ actor = Thread.current[:actor]
339
+ actor && actor.exclusive?
311
340
  end
312
341
 
313
342
  # Call a block after a given interval, returning a Celluloid::Timer object
@@ -338,29 +367,6 @@ module Celluloid
338
367
  def future(meth, *args, &block)
339
368
  Actor.future Thread.current[:actor].mailbox, meth, *args, &block
340
369
  end
341
-
342
- # Process async calls via method_missing
343
- def method_missing(meth, *args, &block)
344
- # bang methods are async calls
345
- if meth.to_s.match(/!$/)
346
- unbanged_meth = meth.to_s.sub(/!$/, '')
347
- args.unshift unbanged_meth
348
-
349
- call = AsyncCall.new(:__send__, args, block)
350
- begin
351
- Thread.current[:actor].mailbox << call
352
- rescue MailboxError
353
- # Silently swallow asynchronous calls to dead actors. There's no way
354
- # to reliably generate DeadActorErrors for async calls, so users of
355
- # async calls should find other ways to deal with actors dying
356
- # during an async call (i.e. linking/supervisors)
357
- end
358
-
359
- return
360
- end
361
-
362
- super
363
- end
364
370
  end
365
371
 
366
372
  require 'celluloid/version'
@@ -368,17 +374,18 @@ require 'celluloid/actor_proxy'
368
374
  require 'celluloid/calls'
369
375
  require 'celluloid/core_ext'
370
376
  require 'celluloid/cpu_counter'
371
- require 'celluloid/events'
372
377
  require 'celluloid/fiber'
373
378
  require 'celluloid/fsm'
374
379
  require 'celluloid/internal_pool'
375
380
  require 'celluloid/links'
376
381
  require 'celluloid/logger'
377
382
  require 'celluloid/mailbox'
383
+ require 'celluloid/method'
378
384
  require 'celluloid/receivers'
379
385
  require 'celluloid/registry'
380
386
  require 'celluloid/responses'
381
387
  require 'celluloid/signals'
388
+ require 'celluloid/system_events'
382
389
  require 'celluloid/task'
383
390
  require 'celluloid/thread_handle'
384
391
  require 'celluloid/uuid'
@@ -389,3 +396,5 @@ require 'celluloid/pool_manager'
389
396
  require 'celluloid/supervision_group'
390
397
  require 'celluloid/supervisor'
391
398
  require 'celluloid/notifications'
399
+
400
+ require 'celluloid/boot'
@@ -7,6 +7,9 @@ module Celluloid
7
7
  # Trying to do something to a dead actor
8
8
  class DeadActorError < StandardError; end
9
9
 
10
+ # A timeout occured before the given request could complete
11
+ class TimeoutError < StandardError; end
12
+
10
13
  # The caller made an error, not the current actor
11
14
  class AbortError < StandardError
12
15
  attr_reader :cause
@@ -17,6 +20,8 @@ module Celluloid
17
20
  end
18
21
  end
19
22
 
23
+ LINKING_TIMEOUT = 5 # linking times out after 5 seconds
24
+
20
25
  # Actors are Celluloid's concurrency primitive. They're implemented as
21
26
  # normal Ruby objects wrapped in threads which communicate with asynchronous
22
27
  # messages.
@@ -64,9 +69,13 @@ module Celluloid
64
69
  # The current task will be automatically resumed when we get a response
65
70
  Task.suspend(:callwait).value
66
71
  else
67
- # Otherwise we're inside a normal thread, so block
68
- response = Thread.mailbox.receive do |msg|
69
- msg.respond_to?(:call) and msg.call == call
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)
70
79
  end
71
80
 
72
81
  response.value
@@ -97,18 +106,68 @@ module Celluloid
97
106
  actors = []
98
107
  Thread.list.each do |t|
99
108
  actor = t[:actor]
100
- actors << actor.proxy if actor
109
+ actors << actor.proxy if actor and actor.respond_to?(:proxy)
101
110
  end
102
111
  actors
103
112
  end
113
+
114
+ # Watch for exit events from another actor
115
+ def monitor(actor)
116
+ raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
117
+ Thread.current[:actor].linking_request(actor, :link)
118
+ end
119
+
120
+ # Stop waiting for exit events from another actor
121
+ def unmonitor(actor)
122
+ raise NotActorError, "can't link outside actor context" unless Celluloid.actor?
123
+ Thread.current[:actor].linking_request(actor, :unlink)
124
+ end
125
+
126
+ # Link to another actor
127
+ def link(actor)
128
+ monitor actor
129
+ Thread.current[:actor].links << actor
130
+ end
131
+
132
+ # Unlink from another actor
133
+ def unlink(actor)
134
+ unmonitor actor
135
+ Thread.current[:actor].links.delete actor
136
+ end
137
+
138
+ # Are we monitoring the given actor?
139
+ def monitoring?(actor)
140
+ actor.links.include? Actor.current
141
+ end
142
+
143
+ # Are we bidirectionally linked to the given actor?
144
+ def linked_to?(actor)
145
+ monitoring?(actor) && Thread.current[:actor].links.include?(actor)
146
+ end
147
+
148
+ # Forcibly kill a given actor
149
+ def kill(actor)
150
+ actor.thread.kill
151
+ begin
152
+ actor.mailbox.shutdown
153
+ rescue DeadActorError
154
+ end
155
+ end
156
+
157
+ # Wait for an actor to terminate
158
+ def join(actor)
159
+ actor.thread.join
160
+ actor
161
+ end
104
162
  end
105
163
 
106
164
  # Wrap the given subject with an Actor
107
- def initialize(subject)
165
+ def initialize(subject, options = {})
108
166
  @subject = subject
109
- @mailbox = subject.class.mailbox_factory
110
- @exit_handler = subject.class.exit_handler
111
- @exclusives = subject.class.exclusive_methods
167
+ @mailbox = options[:mailbox] || Mailbox.new
168
+ @exit_handler = options[:exit_handler]
169
+ @exclusives = options[:exclusive_methods]
170
+ @task_class = options[:task_class] || Celluloid.task_class
112
171
 
113
172
  @tasks = Set.new
114
173
  @links = Links.new
@@ -128,6 +187,33 @@ module Celluloid
128
187
  @proxy = ActorProxy.new(self)
129
188
  end
130
189
 
190
+ # Run the actor loop
191
+ def run
192
+ begin
193
+ while @running
194
+ if message = @mailbox.receive(timeout)
195
+ handle_message message
196
+ else
197
+ # No message indicates a timeout
198
+ @timers.fire
199
+ @receivers.fire_timers
200
+ end
201
+ end
202
+ rescue MailboxShutdown
203
+ # If the mailbox detects shutdown, exit the actor
204
+ end
205
+
206
+ shutdown
207
+ rescue Exception => ex
208
+ handle_crash(ex)
209
+ raise unless ex.is_a? StandardError
210
+ end
211
+
212
+ # Terminate this actor
213
+ def terminate
214
+ @running = false
215
+ end
216
+
131
217
  # Is this actor running in exclusive mode?
132
218
  def exclusive?
133
219
  @exclusive
@@ -141,9 +227,33 @@ module Celluloid
141
227
  @exclusive = false
142
228
  end
143
229
 
144
- # Terminate this actor
145
- def terminate
146
- @running = false
230
+ # Perform a linking request with another actor
231
+ def linking_request(receiver, type)
232
+ exclusive do
233
+ start_time = Time.now
234
+
235
+ receiver.mailbox << LinkingRequest.new(Actor.current, type)
236
+ system_events = []
237
+ loop do
238
+ wait_interval = start_time + LINKING_TIMEOUT - Time.now
239
+ message = @mailbox.receive(wait_interval) do |msg|
240
+ msg.is_a?(LinkingResponse) && msg.actor == receiver && msg.type == type
241
+ end
242
+
243
+ case message
244
+ when LinkingResponse
245
+ # We're done!
246
+ system_events.each { |ev| handle_system_event(ev) }
247
+ return
248
+ when NilClass
249
+ raise TimeoutError, "linking timeout of #{LINKING_TIMEOUT} seconds exceeded"
250
+ when SystemEvent
251
+ # Queue up pending system events to be processed after we've successfully linked
252
+ system_events << message
253
+ else raise 'wtf'
254
+ end
255
+ end
256
+ end
147
257
  end
148
258
 
149
259
  # Send a signal with the given name to all waiting methods
@@ -158,37 +268,12 @@ module Celluloid
158
268
 
159
269
  # Receive an asynchronous message
160
270
  def receive(timeout = nil, &block)
161
- begin
162
- @receivers.receive(timeout, &block)
163
- rescue SystemEvent => event
164
- handle_system_event(event)
165
- retry
166
- end
167
- end
271
+ loop do
272
+ message = @receivers.receive(timeout, &block)
273
+ break message unless message.is_a?(SystemEvent)
168
274
 
169
- # Run the actor loop
170
- def run
171
- begin
172
- while @running
173
- if message = @mailbox.receive(timeout)
174
- handle_message message
175
- else
176
- # No message indicates a timeout
177
- @timers.fire
178
- @receivers.fire_timers
179
- end
180
- end
181
- rescue SystemEvent => event
182
- handle_system_event event
183
- retry
184
- rescue MailboxShutdown
185
- # If the mailbox detects shutdown, exit the actor
275
+ handle_system_event(message)
186
276
  end
187
-
188
- shutdown
189
- rescue Exception => ex
190
- handle_crash(ex)
191
- raise unless ex.is_a? StandardError
192
277
  end
193
278
 
194
279
  # How long to wait until the next timer fires
@@ -206,17 +291,13 @@ module Celluloid
206
291
  end
207
292
 
208
293
  # Schedule a block to run at the given time
209
- def after(interval)
210
- @timers.after(interval) do
211
- Task.new(:timer) { yield }.resume
212
- end
294
+ def after(interval, &block)
295
+ @timers.after(interval) { task(:timer, &block) }
213
296
  end
214
297
 
215
298
  # Schedule a block to run at the given time
216
- def every(interval)
217
- @timers.every(interval) do
218
- Task.new(:timer) { yield }.resume
219
- end
299
+ def every(interval, &block)
300
+ @timers.every(interval) { task(:timer, &block) }
220
301
  end
221
302
 
222
303
  # Sleep for the given amount of time
@@ -233,12 +314,10 @@ module Celluloid
233
314
  # Handle standard low-priority messages
234
315
  def handle_message(message)
235
316
  case message
317
+ when SystemEvent
318
+ handle_system_event message
236
319
  when Call
237
- if @exclusives && @exclusives.include?(message.method)
238
- exclusive { message.dispatch(@subject) }
239
- else
240
- Task.new(:message_handler) { message.dispatch(@subject) }.resume
241
- end
320
+ task(:message_handler, message.method) { message.dispatch(@subject) }
242
321
  when Response
243
322
  message.dispatch
244
323
  else
@@ -251,7 +330,9 @@ module Celluloid
251
330
  def handle_system_event(event)
252
331
  case event
253
332
  when ExitEvent
254
- Task.new(:exit_handler) { handle_exit_event event }.resume
333
+ task(:exit_handler, @exit_handler) { handle_exit_event event }
334
+ when LinkingRequest
335
+ event.process(links)
255
336
  when NamingRequest
256
337
  @name = event.name
257
338
  when TerminationRequest
@@ -289,7 +370,7 @@ module Celluloid
289
370
  # Run the user-defined finalizer, if one is set
290
371
  def run_finalizer
291
372
  return unless @subject.respond_to? :finalize
292
- Task.new(:finalizer) { @subject.finalize }.resume
373
+ task(:finalizer, :finalize) { @subject.finalize }
293
374
  rescue => ex
294
375
  Logger.crash("#{@subject.class}#finalize crashed!", ex)
295
376
  end
@@ -302,5 +383,14 @@ module Celluloid
302
383
  rescue => ex
303
384
  Logger.crash("#{@subject.class}: CLEANUP CRASHED!", ex)
304
385
  end
386
+
387
+ # Run a method inside a task unless it's exclusive
388
+ def task(task_type, method_name = nil, &block)
389
+ if @exclusives && (@exclusives == :all || @exclusives.include?(method_name))
390
+ exclusive { block.call }
391
+ else
392
+ @task_class.new(:message_handler, &block).resume
393
+ end
394
+ end
305
395
  end
306
396
  end