tribe 0.4.0 → 0.6.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.
data/Rakefile CHANGED
@@ -1,14 +1,10 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rake/testtask"
2
3
 
3
- desc 'Start an IRB console with Workers loaded'
4
- task :console do
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
6
-
7
- require 'tribe'
8
- require 'tribe/benchmark'
9
- require 'irb'
10
-
11
- ARGV.clear
12
-
13
- IRB.start
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
14
8
  end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "tribe"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -2,6 +2,7 @@ require 'set'
2
2
 
3
3
  require 'workers'
4
4
 
5
+ require 'tribe/version'
5
6
  require 'tribe/safe_set'
6
7
  require 'tribe/exceptions'
7
8
  require 'tribe/event'
@@ -19,16 +20,7 @@ module Tribe
19
20
  return @registry ||= Tribe::Registry.new
20
21
  end
21
22
 
22
- def self.registry=(val)
23
- @registry.dispose if @registry
24
- @registry = val
25
- end
26
-
27
23
  def self.root
28
24
  @root ||= Tribe::Root.new(:name => 'root', :permit_root => true)
29
25
  end
30
26
  end
31
-
32
- # Force initialization.
33
- Tribe.registry
34
- Tribe.root
@@ -16,16 +16,20 @@ module Tribe
16
16
  end
17
17
 
18
18
  @logger = Workers::LogProxy.new(options[:logger])
19
- @_as = Tribe::ActorState.new
20
- @_as.dedicated = options[:dedicated] || false
21
- @_as.pool = @_as.dedicated ? Workers::Pool.new(:size => 1) : (options[:pool] || Workers.pool)
22
- @_as.mailbox = Tribe::Mailbox.new(@_as.pool)
23
- @_as.registry = options[:registry] || Tribe.registry
24
- @_as.scheduler = options[:scheduler]
25
- @_as.name = options[:name]
26
- @_as.parent = options[:parent]
27
-
28
- @_as.registry.register(self)
19
+ @_actable = Tribe::ActorState.new
20
+ @_actable.dedicated = options[:dedicated] || false
21
+ @_actable.pool = @_actable.dedicated ? Workers::Pool.new(:size => 1) : (options[:pool] || Workers.pool)
22
+ @_actable.mailbox = Tribe::Mailbox.new(@_actable.pool)
23
+ @_actable.registry = options[:registry] || Tribe.registry
24
+ @_actable.scheduler = options[:scheduler]
25
+ @_actable.name = options[:name]
26
+ @_actable.parent = options[:parent]
27
+ @_actable.children = Tribe::SafeSet.new
28
+ @_actable.supervisees = Tribe::SafeSet.new
29
+
30
+ @_actable.registry.register(self)
31
+
32
+ direct_message!(:__initialize__)
29
33
  end
30
34
 
31
35
  #
@@ -38,14 +42,14 @@ module Tribe
38
42
  public
39
43
 
40
44
  def deliver_event!(event)
41
- @_as.mailbox.push(event) do
45
+ @_actable.mailbox.push(event) do
42
46
  process_events
43
47
  end
44
48
 
45
49
  return nil
46
50
  end
47
51
 
48
- def deliver_message!(command, data = nil, src = nil)
52
+ def direct_message!(command, data = nil, src = nil)
49
53
  deliver_event!(Tribe::Event.new(command, data, src))
50
54
 
51
55
  return nil
@@ -69,17 +73,16 @@ module Tribe
69
73
  end
70
74
 
71
75
  def shutdown!
72
- return deliver_message!(:_shutdown)
76
+ return direct_message!(:__shutdown__)
73
77
  end
74
78
 
75
79
  def perform!(&block)
76
- return deliver_message!(:_perform, block)
80
+ return direct_message!(:__perform__, block)
77
81
  end
78
82
 
79
- def spawn(klass, actor_options = {}, spawn_options = {})
83
+ def spawn!(klass, actor_options = {}, spawn_options = {})
80
84
  actor_options[:parent] = self
81
85
 
82
- @_as.children ||= []
83
86
  child = nil
84
87
 
85
88
  if spawn_options[:no_raise_on_failure]
@@ -92,33 +95,117 @@ module Tribe
92
95
  child = klass.new(actor_options)
93
96
  end
94
97
 
95
- @_as.children << child
98
+ @_actable.children.add(child)
99
+
100
+ if spawn_options[:supervise]
101
+ @_actable.supervisees.add(child)
102
+ end
96
103
 
97
104
  return child
98
105
  end
99
106
 
100
107
  def alive?
101
- @_as.mailbox.alive?
108
+ @_actable.mailbox.alive?
109
+ end
110
+
111
+ def dead?
112
+ !alive?
102
113
  end
103
114
 
104
115
  def name
105
- return @_as.name
116
+ return @_actable.name
106
117
  end
107
118
 
108
119
  def identifier
109
- return @_as.name ? "#{object_id}:#{@_as.name}" : object_id
120
+ return @_actable.name ? "#{object_id}:#{@_actable.name}" : object_id
121
+ end
122
+
123
+ def exception
124
+ return @_actable.exception
125
+ end
126
+
127
+ def registry
128
+ return @_actable.registry
129
+ end
130
+
131
+ def pool
132
+ return @_actable.pool
110
133
  end
111
134
 
112
135
  #
113
- # Private event handlers.
114
- # Notes: These methods are designed to be overriden (make sure you call super).
136
+ # Private command handlers.
137
+ # Notes: These methods are designed to be overriden in order to respond to actor system events.
115
138
  #
116
139
 
117
140
  private
118
141
 
142
+ def on_initialize(event)
143
+ end
144
+
145
+ def on_exception(event)
146
+ end
147
+
148
+ def on_shutdown(event)
149
+ end
150
+
151
+ def on_child_died(event)
152
+ end
153
+
154
+ def on_child_shutdown(event)
155
+ end
156
+
157
+ def on_parent_died(event)
158
+ end
159
+
160
+ #
161
+ # Private event system methods.
162
+ # Notes: These methods are designed to be overriden by advanced users only. Overriding is very rare!
163
+ #
164
+
165
+ private
166
+
167
+ # All system commands are prefixed with an underscore.
168
+ def process_events
169
+ while (event = @_actable.mailbox.obtain_and_shift)
170
+ event_handler(event)
171
+ end
172
+
173
+ rescue Exception => exception
174
+ cleanup_handler(exception)
175
+ exception_handler(exception)
176
+ ensure
177
+ @_actable.mailbox.release do
178
+ process_events
179
+ end
180
+
181
+ return nil
182
+ end
183
+
119
184
  def event_handler(event)
185
+ case event.command
186
+ when :__initialize__
187
+ initialize_handler(event)
188
+ when :__shutdown__
189
+ cleanup_handler
190
+ shutdown_handler(event)
191
+ when :__perform__
192
+ perform_handler(event)
193
+ when :__child_died__
194
+ child_died_handler(event.data[0], event.data[1])
195
+ when :__child_shutdown__
196
+ child_shutdown_handler(event.data)
197
+ when :__parent_died__
198
+ parent_died_handler(event.data[0], event.data[1])
199
+ when :initialize, :shutdown, :perform, :child_died, :child_shutdown, :parent_died
200
+ raise ActorReservedCommand.new("Reserved commands are not allowed (command=#{event.command}).")
201
+ else
202
+ custom_event_handler(event)
203
+ end
204
+ end
205
+
206
+ def custom_event_handler(event)
120
207
  result = nil
121
- @_as.active_event = event
208
+ @_actable.active_event = event
122
209
 
123
210
  begin
124
211
  result = send("on_#{event.command}", event)
@@ -126,30 +213,44 @@ module Tribe
126
213
  result = e
127
214
  raise
128
215
  ensure
129
- if event.future && @_as.active_event
216
+ if event.future && @_actable.active_event
130
217
  event.future.result = result
131
218
  end
132
- @_as.active_event = nil
219
+ @_actable.active_event = nil
133
220
  end
134
221
 
135
222
  return nil
136
223
  end
137
224
 
225
+ def initialize_handler(event)
226
+ on_initialize(event)
227
+ end
228
+
138
229
  def exception_handler(exception)
139
- if @_as.parent
140
- @_as.parent.deliver_message!(:_child_died, [self, exception])
230
+ if @_actable.parent
231
+ @_actable.parent.direct_message!(:__child_died__, [self, exception])
141
232
  end
142
233
 
143
- if @_as.children
144
- @_as.children.each { |c| c.deliver_message!(:_parent_died, [self, exception]) }
145
- @_as.children.clear
146
- @_as.children = nil
147
- end
234
+ @_actable.children.each { |c| c.direct_message!(:__parent_died__, [self, exception]) }
235
+ @_actable.children.clear
236
+ @_actable.supervisees.clear
237
+
238
+ on_exception(Event.new(:exception, {:exception => exception}))
148
239
 
149
240
  return nil
150
241
  end
151
242
 
152
243
  def shutdown_handler(event)
244
+ if @_actable.parent
245
+ @_actable.parent.direct_message!(:__child_shutdown__, self)
246
+ end
247
+
248
+ @_actable.children.each { |c| c.shutdown! }
249
+ @_actable.children.clear
250
+ @_actable.supervisees.clear
251
+
252
+ on_shutdown(Event.new(:shutdown, {}))
253
+
153
254
  return nil
154
255
  end
155
256
 
@@ -160,27 +261,42 @@ module Tribe
160
261
  end
161
262
 
162
263
  def cleanup_handler(exception = nil)
163
- @_as.exception = exception
164
- @_as.pool.shutdown if @_as.dedicated
165
- @_as.mailbox.kill
166
- @_as.registry.unregister(self)
167
- @_as.timers.each { |t| t.cancel } if @_as.timers
168
-
169
- if @_as.children && exception.nil?
170
- @_as.children.each { |c| c.shutdown! }
171
- @_as.children.clear
172
- @_as.children = nil
173
- end
264
+ @_actable.exception = exception
265
+ @_actable.pool.shutdown if @_actable.dedicated
266
+ @_actable.mailbox.kill
267
+ @_actable.registry.unregister(self)
268
+ @_actable.timers.each { |t| t.cancel } if @_actable.timers
174
269
 
175
270
  return nil
176
271
  end
177
272
 
178
273
  def child_died_handler(child, exception)
179
- raise Tribe::ActorChildDied.new("#{child.identifier} died.")
274
+ @_actable.children.delete(child)
275
+ supervising = !!@_actable.supervisees.delete?(child)
276
+
277
+ on_child_died(Event.new(:child_died, {:child => child, :exception => exception}))
278
+
279
+ if !supervising
280
+ raise Tribe::ActorChildDied.new("#{child.identifier} died.")
281
+ end
282
+
283
+ return nil
284
+ end
285
+
286
+ def child_shutdown_handler(child)
287
+ @_actable.children.delete(child)
288
+ @_actable.supervisees.delete(child)
289
+
290
+ on_child_shutdown(Event.new(:child_shutdown, {:child => child}))
291
+
292
+ return nil
180
293
  end
181
294
 
182
295
  def parent_died_handler(parent, exception)
296
+ on_parent_died(Event.new(:parent_died, {:parent => parent, :exception => exception}))
183
297
  raise Tribe::ActorParentDied.new("#{parent.identifier} died.")
298
+
299
+ return nil
184
300
  end
185
301
 
186
302
  #
@@ -190,86 +306,66 @@ module Tribe
190
306
 
191
307
  private
192
308
 
193
- def registry
194
- return @_as.registry
195
- end
196
-
197
- def pool
198
- return @_as.pool
199
- end
200
-
201
- def timer(delay, command, data = nil)
309
+ def timer!(delay, command, data = nil)
202
310
  # Lazy instantiation for performance.
203
- @_as.scheduler ||= Workers.scheduler
204
- @_as.timers ||= Tribe::SafeSet.new
311
+ @_actable.scheduler ||= Workers.scheduler
312
+ @_actable.timers ||= Tribe::SafeSet.new
205
313
 
206
- timer = Workers::Timer.new(delay, :scheduler => @_as.scheduler) do
207
- @_as.timers.delete(timer)
208
- deliver_message!(command, data)
314
+ timer = Workers::Timer.new(delay, :scheduler => @_actable.scheduler) do
315
+ @_actable.timers.delete(timer)
316
+ direct_message!(command, data)
209
317
  end
210
318
 
211
- @_as.timers.add(timer)
319
+ @_actable.timers.add(timer)
212
320
 
213
321
  return timer
214
322
  end
215
323
 
216
- def periodic_timer(delay, command, data = nil)
324
+ def periodic_timer!(delay, command, data = nil)
217
325
  # Lazy instantiation for performance.
218
- @_as.scheduler ||= Workers.scheduler
219
- @_as.timers ||= Tribe::SafeSet.new
326
+ @_actable.scheduler ||= Workers.scheduler
327
+ @_actable.timers ||= Tribe::SafeSet.new
220
328
 
221
- timer = Workers::PeriodicTimer.new(delay, :scheduler => @_as.scheduler) do
222
- deliver_message!(command, data)
329
+ timer = Workers::PeriodicTimer.new(delay, :scheduler => @_actable.scheduler) do
330
+ direct_message!(command, data)
223
331
  unless alive?
224
- @_as.timers.delete(timer)
332
+ @_actable.timers.delete(timer)
225
333
  timer.cancel
226
334
  end
227
335
  end
228
336
 
229
- @_as.timers.add(timer)
337
+ @_actable.timers.add(timer)
230
338
 
231
339
  return timer
232
340
  end
233
341
 
234
- #
235
- # Private internal methods.
236
- # Notes: These are used by the actor system and you should never call them directly.
237
- #
238
-
239
342
  def forward!(dest)
240
- dest.deliver_event!(@_as.active_event)
241
- @_as.active_event = nil
343
+ dest.deliver_event!(@_actable.active_event)
344
+ @_actable.active_event = nil
242
345
 
243
346
  return nil
244
347
  end
245
348
 
246
- # All system commands are prefixed with an underscore.
247
- def process_events
248
- while (event = @_as.mailbox.obtain_and_shift)
249
- case event.command
250
- when :_shutdown
251
- cleanup_handler
252
- shutdown_handler(event)
253
- when :_perform
254
- perform_handler(event)
255
- when :_child_died
256
- child_died_handler(event.data[0], event.data[1])
257
- when :_parent_died
258
- parent_died_handler(event.data[0], event.data[1])
259
- else
260
- event_handler(event)
349
+ # Wrap blocking code using this method to automatically expand/contract the pool.
350
+ # This way you avoid potential deadlock with blocking code.
351
+ # Not needed for dedicated actors since they already have their own thread.
352
+ def blocking!
353
+ if @_actable.dedicated
354
+ yield
355
+ else
356
+ pool.expand(1)
357
+ begin
358
+ yield
359
+ ensure
360
+ pool.contract(1)
261
361
  end
262
362
  end
363
+ end
263
364
 
264
- rescue Exception => exception
265
- cleanup_handler(exception)
266
- exception_handler(exception)
267
- ensure
268
- @_as.mailbox.release do
269
- process_events
365
+ def wait!(future)
366
+ blocking! do
367
+ future.wait
270
368
  end
271
-
272
- return nil
273
369
  end
274
370
  end
275
371
  end