celluloid 0.11.1 → 0.12.0.pre

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