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.
- checksums.yaml +4 -4
- data/.gitignore +9 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -1
- data/README.md +254 -223
- data/Rakefile +7 -11
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/tribe.rb +1 -9
- data/lib/tribe/actable.rb +192 -96
- data/lib/tribe/actor_state.rb +1 -0
- data/lib/tribe/benchmark/throughput.rb +4 -4
- data/lib/tribe/exceptions.rb +1 -0
- data/lib/tribe/future.rb +12 -11
- data/lib/tribe/mailbox.rb +6 -6
- data/lib/tribe/root.rb +5 -1
- data/lib/tribe/safe_set.rb +25 -7
- data/lib/tribe/version.rb +1 -1
- data/tribe.gemspec +22 -15
- metadata +61 -16
data/Rakefile
CHANGED
@@ -1,14 +1,10 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
data/bin/console
ADDED
@@ -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
|
data/bin/setup
ADDED
data/lib/tribe.rb
CHANGED
@@ -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
|
data/lib/tribe/actable.rb
CHANGED
@@ -16,16 +16,20 @@ module Tribe
|
|
16
16
|
end
|
17
17
|
|
18
18
|
@logger = Workers::LogProxy.new(options[:logger])
|
19
|
-
@
|
20
|
-
@
|
21
|
-
@
|
22
|
-
@
|
23
|
-
@
|
24
|
-
@
|
25
|
-
@
|
26
|
-
@
|
27
|
-
|
28
|
-
@
|
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
|
-
@
|
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
|
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
|
76
|
+
return direct_message!(:__shutdown__)
|
73
77
|
end
|
74
78
|
|
75
79
|
def perform!(&block)
|
76
|
-
return
|
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
|
-
@
|
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
|
-
@
|
108
|
+
@_actable.mailbox.alive?
|
109
|
+
end
|
110
|
+
|
111
|
+
def dead?
|
112
|
+
!alive?
|
102
113
|
end
|
103
114
|
|
104
115
|
def name
|
105
|
-
return @
|
116
|
+
return @_actable.name
|
106
117
|
end
|
107
118
|
|
108
119
|
def identifier
|
109
|
-
return @
|
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
|
114
|
-
# Notes: These methods are designed to be overriden
|
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
|
-
@
|
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 && @
|
216
|
+
if event.future && @_actable.active_event
|
130
217
|
event.future.result = result
|
131
218
|
end
|
132
|
-
@
|
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 @
|
140
|
-
@
|
230
|
+
if @_actable.parent
|
231
|
+
@_actable.parent.direct_message!(:__child_died__, [self, exception])
|
141
232
|
end
|
142
233
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
@
|
164
|
-
@
|
165
|
-
@
|
166
|
-
@
|
167
|
-
@
|
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
|
-
|
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
|
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
|
-
@
|
204
|
-
@
|
311
|
+
@_actable.scheduler ||= Workers.scheduler
|
312
|
+
@_actable.timers ||= Tribe::SafeSet.new
|
205
313
|
|
206
|
-
timer = Workers::Timer.new(delay, :scheduler => @
|
207
|
-
@
|
208
|
-
|
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
|
-
@
|
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
|
-
@
|
219
|
-
@
|
326
|
+
@_actable.scheduler ||= Workers.scheduler
|
327
|
+
@_actable.timers ||= Tribe::SafeSet.new
|
220
328
|
|
221
|
-
timer = Workers::PeriodicTimer.new(delay, :scheduler => @
|
222
|
-
|
329
|
+
timer = Workers::PeriodicTimer.new(delay, :scheduler => @_actable.scheduler) do
|
330
|
+
direct_message!(command, data)
|
223
331
|
unless alive?
|
224
|
-
@
|
332
|
+
@_actable.timers.delete(timer)
|
225
333
|
timer.cancel
|
226
334
|
end
|
227
335
|
end
|
228
336
|
|
229
|
-
@
|
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!(@
|
241
|
-
@
|
343
|
+
dest.deliver_event!(@_actable.active_event)
|
344
|
+
@_actable.active_event = nil
|
242
345
|
|
243
346
|
return nil
|
244
347
|
end
|
245
348
|
|
246
|
-
#
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
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
|
-
|
265
|
-
|
266
|
-
|
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
|