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