tribe 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 50de6f25aa1a85a300729b25e432f661619f2468
4
- data.tar.gz: d8f1c9329ceefad25909e6db346288a6aea39ab6
3
+ metadata.gz: 865a31569730f5bc15800bc24f0dc791faea2971
4
+ data.tar.gz: c7a793ff5df24f85ddf2d0f877fd5a892f946fe6
5
5
  SHA512:
6
- metadata.gz: 9f58370c1c6b6b9ba42b7858bcac28016f4a5dc170432d7882163573330c12b949ad037daa36424db3839d8dad7f642294302be4d91903ea80f431ee4a9a721a
7
- data.tar.gz: d894eb6eca95825d89ab91d4823545fa737a6cc588320ffb80b3f4239e5a3c946e13191f3941210b5f2f0436e2dc22692c2532d3646563452790a4edf6588f9a
6
+ metadata.gz: ec0d7842dd09a91e305da5719b19bea9a5aa584edcf39a522036f5668c05fac9efaafb442f8706c6766080b15b69e3f47d6edf778b56443439b2a0b567703ce8
7
+ data.tar.gz: 28bd5c028b349ac7ed29d45593aae3a888e55d280fa7a927f6d3614e4bd4476c382fce95401ede14e04b2ddd0408b777a0b00fd3630703a2d0ed37ee0c960fb4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tribe (0.3.2)
4
+ tribe (0.4.0)
5
5
  workers (= 0.2.2)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -13,6 +13,9 @@ Event-driven servers can be built using [Tribe EM] (https://github.com/chadrem/t
13
13
 
14
14
  - [Installation](#installation)
15
15
  - [Actors](#actors)
16
+ - [Root](#root-actor)
17
+ - [Handlers](#handlers)
18
+ - [Messages](#messages)
16
19
  - [Registries](#registries)
17
20
  - [Timers](#timers)
18
21
  - [Futures](#futures)
@@ -42,18 +45,34 @@ Or install it yourself as:
42
45
 
43
46
  ## Actors
44
47
 
45
- Actors are light-weight objects that use asynchronous message passing for communcation.
46
- There are two types of methods that you create in your actors:
48
+ Actors are the building blocks of your application.
49
+ There are three ways to create an actor class:
50
+
51
+ - Inherit from Tribe::Actor (uses the shared thread pool).
52
+ - Inherit from Tribe::DedicatedActor (uses a dedicated thread).
53
+ - Mixin Tribe::Actable and call the Actable#init_actable method in your constructor.
54
+
55
+
56
+ #### Root
57
+
58
+ Well designed applications built with the actor model tend to organize their actors in a tree like structure.
59
+ To encourage this, Tribe has a special built-in actor known as the root actor.
60
+ You should use this actor to spawn all of your application specific actors.
61
+
62
+ Tribe.root
63
+
64
+ #### Handlers
65
+
66
+ There are two types of methods that you create in your actor classes:
47
67
 
48
68
  1. *Command handlers* are prefixed with "on_" and define the types of commands your actor will process.
49
69
  2. *System handlers* are postfixed with "_handler" and are built into the actor system. These are hooks into the Tribe's actor system.
50
70
 
51
- To send a message you use the Actable#message! method and specify a command with an optional data parameter.
52
- The return value will always be nil since messaging is asynchronous.
71
+ #### Messages
53
72
 
54
- Note that the actors have a number of methods that end in ! (exclamation point or “bang”).
55
- All of these mthods are asynchronous and designed to be thread safe.
56
- More information on them will be provided throughout this readme.
73
+ Actors communicate asynchronously using a number of methods that end in ! (exclamation point or “bang”).
74
+ The most basic type of communication is a message and they are sent using using the Actable#message! and Actable#deliver_message! methods.
75
+ These methods always return nil since they are fire-and-forget.
57
76
 
58
77
  # Create your custom actor class.
59
78
  class MyActor < Tribe::Actor
@@ -73,15 +92,15 @@ More information on them will be provided throughout this readme.
73
92
  end
74
93
  end
75
94
 
76
- # Create some named actors.
95
+ # Create some named actors that are children of the root actor.
77
96
  100.times do |i|
78
- MyActor.new(:name => "my_actor_#{i}")
97
+ Tribe.root.spawn(MyActor, :name => "my_actor_#{i}")
79
98
  end
80
99
 
81
100
  # Send an event to each actor.
82
101
  100.times do |i|
83
102
  actor = Tribe.registry["my_actor_#{i}"]
84
- actor.message!(:my_custom, 'hello world')
103
+ actor.deliver_message!(:my_custom, 'hello world')
85
104
  end
86
105
 
87
106
  # Shutdown the actors.
@@ -90,27 +109,12 @@ More information on them will be provided throughout this readme.
90
109
  actor.shutdown!
91
110
  end
92
111
 
93
- #### Implementation
94
- Because actors use a shared thread pool, it is important that they don't block for long periods of time (short periods are fine).
95
- Actors that block for long periods of time should use a dedicated thread (:dedicated => true or subclass from Tribe::DedicatedActor).
96
-
97
- #### Options (defaults below)
98
-
99
- actor = Tribe::Actor.new(
100
- :logger => nil, # Ruby logger instance.
101
- :dedicated => false, # If true, the actor runs with a worker pool that has one thread.
102
- :pool => Workers.pool, # The workers pool used to execute events.
103
- :registry => Tribe.registry, # The registry used to store a reference to the actor if it has a name.
104
- :name => nil, # The name of the actor (must be unique in the registry).
105
- :parent => nil # Set the parent actor (used by the supervision feature).
106
- )
107
-
108
112
  ## Registries
109
113
 
110
114
  Registries hold references to named actors so that you can easily find them.
111
115
  In general you shouldn't have to create your own since there is a global one (Tribe.registry).
112
116
 
113
- actor = Tribe::Actor.new(:name => 'some_actor')
117
+ actor = Tribe.root.spawn(Tribe::Actor, :name => 'some_actor')
114
118
 
115
119
  if actor == Tribe.registry['some_actor']
116
120
  puts 'Successfully found some_actor in the registry.'
@@ -140,7 +144,7 @@ Both one-shot and periodic timers are provided.
140
144
 
141
145
  # Create some named actors.
142
146
  10.times do |i|
143
- MyActor.new(:name => "my_actor_#{i}")
147
+ Tribe.root.spawn(MyActor, :name => "my_actor_#{i}")
144
148
  end
145
149
 
146
150
  # Sleep in order to observe the timers.
@@ -154,9 +158,9 @@ Both one-shot and periodic timers are provided.
154
158
 
155
159
  ## Futures
156
160
 
157
- As mentioned above, message passing with the Actable#message! method is asynchronous and always returns nil.
161
+ Message passing with the Actable#message! and Actable#deliver_message! methods is asynchronous and always returns nil.
158
162
  This can be a pain since in many cases you will be interested in the result.
159
- The Actable#future! method solves this problem by returning a Tribe::Future object instead of nil.
163
+ The Actable#future! method solves this problem by returning a Tribe::Future object.
160
164
  You can then use this object to obtain the result when it becomes available.
161
165
 
162
166
  #### Non-blocking
@@ -173,19 +177,14 @@ No waiting for a result is involved and the actor will continue to process other
173
177
 
174
178
  def on_start(event)
175
179
  friend = registry['actor_b']
176
-
177
- future = friend.future!(:compute, 10)
180
+ future = future!(friend, :compute, 10)
178
181
 
179
182
  future.success do |result|
180
- perform! do
181
- puts "ActorA (#{identifier}) future result: #{result}"
182
- end
183
+ puts "ActorA (#{identifier}) future result: #{result}"
183
184
  end
184
185
 
185
186
  future.failure do |exception|
186
- perform! do
187
- puts "ActorA (#{identifier}) future failure: #{exception}"
188
- end
187
+ puts "ActorA (#{identifier}) future failure: #{exception}"
189
188
  end
190
189
  end
191
190
  end
@@ -206,18 +205,14 @@ No waiting for a result is involved and the actor will continue to process other
206
205
  end
207
206
  end
208
207
 
209
- actor_a = ActorA.new(:name => 'actor_a')
210
- actor_b = ActorB.new(:name => 'actor_b')
208
+ actor_a = Tribe.root.spawn(ActorA, :name => 'actor_a')
209
+ actor_b = Tribe.root.spawn(ActorB, :name => 'actor_b')
211
210
 
212
- actor_a.message!(:start)
211
+ actor_a.deliver_message!(:start)
213
212
 
214
213
  actor_a.shutdown!
215
214
  actor_b.shutdown!
216
215
 
217
- *Important*: You must use Actable#perform! inside the above callbacks.
218
- This ensures that your code executes within the context of the correct actor.
219
- Failure to do so will result in unexpected behavior (thread safety will be lost)!
220
-
221
216
  #### Blocking
222
217
 
223
218
  Blocking futures are synchronous.
@@ -232,8 +227,7 @@ The actor won't process any other events until the future has a result.
232
227
 
233
228
  def on_start(event)
234
229
  friend = registry['actor_b']
235
-
236
- future = friend.future!(:compute, 10)
230
+ future = future!(friend, :compute, 10)
237
231
 
238
232
  future.wait # The current thread will sleep until a result is available.
239
233
 
@@ -261,10 +255,10 @@ The actor won't process any other events until the future has a result.
261
255
  end
262
256
  end
263
257
 
264
- actor_a = ActorA.new(:name => 'actor_a')
265
- actor_b = ActorB.new(:name => 'actor_b')
258
+ actor_a = Tribe.root.spawn(ActorA, :name => 'actor_a')
259
+ actor_b = Tribe.root.spawn(ActorB, :name => 'actor_b')
266
260
 
267
- actor_a.message!(:start)
261
+ actor_a.deliver_message!(:start)
268
262
 
269
263
  actor_a.shutdown!
270
264
  actor_b.shutdown!
@@ -274,7 +268,7 @@ The actor won't process any other events until the future has a result.
274
268
  Futures can be confgured to timeout after a specified number of seconds.
275
269
  When a timeout occurs, the result of the future will be a Tribe::FutureTimeout exception.
276
270
 
277
- # Manually create a future (Use Actable#future! in your actors).
271
+ # Manually create a future for this example (Use Actable#future! in your actors).
278
272
  future = Tribe::Future.new
279
273
 
280
274
  # Set a timeout (in seconds).
@@ -342,11 +336,11 @@ This lets you build routers that delegate work to other actors.
342
336
  end
343
337
 
344
338
  # Create the router.
345
- router = MyRouter.new(:name => 'router')
339
+ router = Tribe.root.spawn(MyRouter, :name => 'router')
346
340
 
347
341
  # Send an event to the router and it will forward it to a random processor.
348
342
  100.times do |i|
349
- router.message!(:process, i)
343
+ router.deliver_message!(:process, i)
350
344
  end
351
345
 
352
346
  # Shutdown the router.
@@ -355,11 +349,12 @@ This lets you build routers that delegate work to other actors.
355
349
  ## Linking
356
350
 
357
351
  Linking allows actors to group together into a tree structure such that they all live or die as one group.
358
- Such linking is useful for breaking complex problems into smaller (and easier to manage) components.
359
- To create a linked actor you use the Actable#spawn method to create it.
360
- If any actor in a tree of linked actors dies, it will cause all actors above and below it to die too.
352
+ Such linking is useful for breaking up complex problems into multiple smaller units.
353
+ To create a linked actor you use the Actable#spawn method.
354
+ By default, if a linked actor dies, it will cause its parent and children to die too.
355
+ Thus the entire tree lives or dies together.
361
356
 
362
- # Create the root level actor class.
357
+ # Create the top-level actor class.
363
358
  class Level1 < Tribe::Actor
364
359
  private
365
360
  def on_spawn(event)
@@ -367,7 +362,7 @@ If any actor in a tree of linked actors dies, it will cause all actors above and
367
362
  name = "level2_#{i}"
368
363
  puts name
369
364
  actor = spawn(Level2, :name => name)
370
- actor.message!(:spawn, i)
365
+ message!(actor, :spawn, i)
371
366
  end
372
367
  end
373
368
  end
@@ -379,7 +374,7 @@ If any actor in a tree of linked actors dies, it will cause all actors above and
379
374
  5.times do |i|
380
375
  name = "level3_#{event.data}_#{i}"
381
376
  actor = spawn(Level3, :name => name)
382
- actor.message!(:spawn)
377
+ message!(actor, :spawn)
383
378
  end
384
379
  end
385
380
  end
@@ -392,18 +387,19 @@ If any actor in a tree of linked actors dies, it will cause all actors above and
392
387
  end
393
388
  end
394
389
 
395
- # Create the root actor.
396
- root = Level1.new(:name => 'level1')
390
+ # Create the top-level actor.
391
+ top = Tribe.root.spawn(Level1, :name => 'level1')
397
392
 
398
393
  # Tell the root actor to create the tree of children.
399
- root.message!(:spawn)
394
+ top.deliver_message!(:spawn)
400
395
 
401
396
  ## Supervisors
402
397
 
403
- As mentioned above, a failure in a linked actor will cause all associated actors (parent and children) to die.
404
- Supervisors can be used to block the failure from propogating and allow you to restart the failed section of the tree.
398
+ A failure in a linked actor will cause all associated actors (parent and children) to die.
399
+ Supervisors can be used to block the failure from propogating.
400
+ You then have the option to re-create the failed actor.
405
401
 
406
- # Create the root level actor class.
402
+ # Create the top-level actor class.
407
403
  class Level1 < Tribe::Actor
408
404
  private
409
405
  def on_spawn(event)
@@ -414,7 +410,7 @@ Supervisors can be used to block the failure from propogating and allow you to r
414
410
 
415
411
  def create_subtree
416
412
  actor = spawn(Level2)
417
- actor.message!(:spawn)
413
+ message!(actor, :spawn)
418
414
  end
419
415
 
420
416
  def child_died_handler(actor, exception)
@@ -429,7 +425,7 @@ Supervisors can be used to block the failure from propogating and allow you to r
429
425
  def on_spawn(event)
430
426
  5.times do |i|
431
427
  actor = spawn(Level3)
432
- actor.message!(:spawn)
428
+ message!(actor, :spawn)
433
429
  end
434
430
  end
435
431
  end
@@ -443,13 +439,14 @@ Supervisors can be used to block the failure from propogating and allow you to r
443
439
  end
444
440
  end
445
441
 
446
- # Create the root actor.
447
- root = Level1.new(:name => 'root')
442
+ # Create the top-level actor.
443
+ top = Tribe.root.spawn(Level1, :name => 'root')
448
444
 
449
- # Tell the root actor to create the tree of children.
450
- root.message!(:spawn)
445
+ # Tell the top-level actor to create the tree of children.
446
+ top.deliver_message!(:spawn)
451
447
 
452
448
  #### Important!
449
+
453
450
  Restarting named actors is NOT currently supported, but will be in a future update.
454
451
  Attempting to do so may result in Tribe::RegistryError exceptions when trying to spawn a replacement child.
455
452
 
data/lib/tribe/actable.rb CHANGED
@@ -37,35 +37,64 @@ module Tribe
37
37
 
38
38
  public
39
39
 
40
- def event!(event)
41
- push_event(event)
40
+ def deliver_event!(event)
41
+ @_as.mailbox.push(event) do
42
+ process_events
43
+ end
44
+
45
+ return nil
46
+ end
47
+
48
+ def deliver_message!(command, data = nil, src = nil)
49
+ deliver_event!(Tribe::Event.new(command, data, src))
42
50
 
43
51
  return nil
44
52
  end
45
53
 
46
- def message!(command, data = nil, source = nil)
47
- event = Tribe::Event.new(command, data, source)
54
+ def message!(dest, command, data = nil)
55
+ event = Tribe::Event.new(command, data, self)
48
56
 
49
- push_event(event)
57
+ dest.deliver_event!(event)
50
58
 
51
59
  return nil
52
60
  end
53
61
 
54
- def future!(command, data = nil, source = nil)
55
- event = Tribe::Event.new(command, data, source)
56
- event.future = future = Tribe::Future.new
62
+ def future!(dest, command, data = nil)
63
+ event = Tribe::Event.new(command, data, self)
64
+ event.future = future = Tribe::Future.new(self)
57
65
 
58
- push_event(event)
66
+ dest.deliver_event!(event)
59
67
 
60
68
  return future
61
69
  end
62
70
 
63
71
  def shutdown!
64
- return message!(:_shutdown)
72
+ return deliver_message!(:_shutdown)
65
73
  end
66
74
 
67
75
  def perform!(&block)
68
- return message!(:_perform, block)
76
+ return deliver_message!(:_perform, block)
77
+ end
78
+
79
+ def spawn(klass, actor_options = {}, spawn_options = {})
80
+ actor_options[:parent] = self
81
+
82
+ @_as.children ||= []
83
+ child = nil
84
+
85
+ if spawn_options[:no_raise_on_failure]
86
+ begin
87
+ child = klass.new(actor_options)
88
+ rescue Exception => e
89
+ return false
90
+ end
91
+ else
92
+ child = klass.new(actor_options)
93
+ end
94
+
95
+ @_as.children << child
96
+
97
+ return child
69
98
  end
70
99
 
71
100
  def alive?
@@ -108,11 +137,11 @@ module Tribe
108
137
 
109
138
  def exception_handler(exception)
110
139
  if @_as.parent
111
- @_as.parent.message!(:_child_died, [self, exception])
140
+ @_as.parent.deliver_message!(:_child_died, [self, exception])
112
141
  end
113
142
 
114
143
  if @_as.children
115
- @_as.children.each { |c| c.message!(:_parent_died, [self, exception]) }
144
+ @_as.children.each { |c| c.deliver_message!(:_parent_died, [self, exception]) }
116
145
  @_as.children.clear
117
146
  @_as.children = nil
118
147
  end
@@ -176,7 +205,7 @@ module Tribe
176
205
 
177
206
  timer = Workers::Timer.new(delay, :scheduler => @_as.scheduler) do
178
207
  @_as.timers.delete(timer)
179
- message!(command, data)
208
+ deliver_message!(command, data)
180
209
  end
181
210
 
182
211
  @_as.timers.add(timer)
@@ -190,7 +219,7 @@ module Tribe
190
219
  @_as.timers ||= Tribe::SafeSet.new
191
220
 
192
221
  timer = Workers::PeriodicTimer.new(delay, :scheduler => @_as.scheduler) do
193
- message!(command, data)
222
+ deliver_message!(command, data)
194
223
  unless alive?
195
224
  @_as.timers.delete(timer)
196
225
  timer.cancel
@@ -208,27 +237,12 @@ module Tribe
208
237
  #
209
238
 
210
239
  def forward!(dest)
211
- dest.event!(@_as.active_event)
240
+ dest.deliver_event!(@_as.active_event)
212
241
  @_as.active_event = nil
213
242
 
214
243
  return nil
215
244
  end
216
245
 
217
- def spawn(klass, options = {})
218
- options[:parent] = self
219
-
220
- @_as.children ||= []
221
- @_as.children << (actor = klass.new(options))
222
-
223
- return actor
224
- end
225
-
226
- def push_event(event)
227
- @_as.mailbox.push(event) do
228
- process_events
229
- end
230
- end
231
-
232
246
  # All system commands are prefixed with an underscore.
233
247
  def process_events
234
248
  while (event = @_as.mailbox.obtain_and_shift)
data/lib/tribe/future.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  module Tribe
2
2
  class Future
3
- def initialize
3
+ def initialize(actor = nil)
4
4
  @state = :initialized
5
5
  @mutex = Mutex.new
6
6
  @condition = ConditionVariable.new
7
7
  @result = nil
8
8
  @success_callback = nil
9
9
  @failure_callback = nil
10
+ @actor = actor
10
11
 
11
12
  return nil
12
13
  end
@@ -34,9 +35,25 @@ module Tribe
34
35
  @condition.signal
35
36
 
36
37
  if val.is_a?(Exception)
37
- @failure_callback.call(val) if @failure_callback
38
+ if @failure_callback
39
+ if @actor
40
+ @actor.perform! do
41
+ @failure_callback.call(val)
42
+ end
43
+ else
44
+ @failure_callback.call(val)
45
+ end
46
+ end
38
47
  else
39
- @success_callback.call(val) if @success_callback
48
+ if @success_callback
49
+ if @actor
50
+ @actor.perform! do
51
+ @success_callback.call(val)
52
+ end
53
+ else
54
+ @success_callback.call(val)
55
+ end
56
+ end
40
57
  end
41
58
 
42
59
  return nil
@@ -79,7 +96,15 @@ module Tribe
79
96
  when :initialized
80
97
  @success_callback = block
81
98
  when :finished
82
- yield(@result) unless @result.is_a?(Exception)
99
+ unless @result.is_a?(Exception)
100
+ if @actor
101
+ @actor.perform! do
102
+ block.call(@result)
103
+ end
104
+ else
105
+ block.call(@result)
106
+ end
107
+ end
83
108
  else
84
109
  raise Tribe::FutureError.new('Invalid state.')
85
110
  end
@@ -94,7 +119,15 @@ module Tribe
94
119
  when :initialized
95
120
  @failure_callback = block
96
121
  when :finished
97
- yield(@result) if @result.is_a?(Exception)
122
+ if @result.is_a?(Exception)
123
+ if @actor
124
+ @actor.perform! do
125
+ block.call(@result)
126
+ end
127
+ else
128
+ block.call(@result)
129
+ end
130
+ end
98
131
  else
99
132
  raise Tribe::FutureError.new('Invalid state.')
100
133
  end
data/lib/tribe/root.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Tribe
2
+ class Root < Tribe::Actor
3
+ private
4
+
5
+ def initialize(options = {})
6
+ unless options[:permit_root]
7
+ raise 'Application code should never create the root actor.'
8
+ end
9
+
10
+ options.delete(:permit_root)
11
+
12
+ super
13
+ end
14
+
15
+ def child_died_handler(child, exception)
16
+ # Let the children die silently since the root actor should live forever.
17
+ end
18
+ end
19
+ end
data/lib/tribe/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Tribe
2
- VERSION = '0.3.2'
2
+ VERSION = '0.4.0'
3
3
  end
data/lib/tribe.rb CHANGED
@@ -12,6 +12,7 @@ require 'tribe/actor'
12
12
  require 'tribe/dedicated_actor'
13
13
  require 'tribe/registry'
14
14
  require 'tribe/future'
15
+ require 'tribe/root'
15
16
 
16
17
  module Tribe
17
18
  def self.registry
@@ -22,7 +23,12 @@ module Tribe
22
23
  @registry.dispose if @registry
23
24
  @registry = val
24
25
  end
26
+
27
+ def self.root
28
+ @root ||= Tribe::Root.new(:name => 'root', :permit_root => true)
29
+ end
25
30
  end
26
31
 
27
- # Force initialization of defaults.
32
+ # Force initialization.
28
33
  Tribe.registry
34
+ Tribe.root
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tribe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chad Remesch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-11 00:00:00.000000000 Z
11
+ date: 2013-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: workers
@@ -49,6 +49,7 @@ files:
49
49
  - lib/tribe/future.rb
50
50
  - lib/tribe/mailbox.rb
51
51
  - lib/tribe/registry.rb
52
+ - lib/tribe/root.rb
52
53
  - lib/tribe/safe_set.rb
53
54
  - lib/tribe/version.rb
54
55
  - tribe.gemspec