tribe 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -55,37 +55,80 @@ end
55
55
 
56
56
  require 'tribe'
57
57
 
58
+ #
59
+ # TODO: Temporary benchmarking/demo code.
60
+ #
61
+
62
+ require 'benchmark'
63
+
64
+ $demo_queue = Queue.new
65
+
66
+ DEMO_ACTOR_COUNT = 100
67
+ DEMO_MSG_COUNT = 3000
68
+
58
69
  class MyActor < Tribe::Actor
70
+
59
71
  def pre_init
60
- @count = 0
72
+ reset_count
61
73
  end
62
74
 
63
75
  def increment
64
76
  @count += 1
65
- puts "#{@name}=#{@count}" if @count >= 50000
77
+
78
+ if @count >= DEMO_MSG_COUNT
79
+ puts "#{@name} done."
80
+ $demo_queue.push(@name)
81
+ end
82
+ end
83
+
84
+ def reset_count
85
+ @count = 0
66
86
  end
67
87
 
68
88
  def go(friend_name)
69
89
  friend = Tribe.registry[friend_name]
70
90
 
71
- 50000.times do
91
+ DEMO_MSG_COUNT.times do
72
92
  friend.increment!
73
93
  end
74
94
  end
75
95
  end
76
96
 
77
97
  def tribe_demo
78
- a1 = Tribe.registry['a1'] || MyActor.new(:name => 'a1')
79
- a2 = Tribe.registry['a2'] || MyActor.new(:name => 'a2')
80
- a3 = Tribe.registry['a3'] || MyActor.new(:name => 'a3')
81
- a4 = Tribe.registry['a4'] || MyActor.new(:name => 'a4')
82
- a5 = Tribe.registry['a5'] || MyActor.new(:name => 'a5')
83
- a6 = Tribe.registry['a6'] || MyActor.new(:name => 'a6')
84
-
85
- a1.go!('a2')
86
- a2.go!('a1')
87
- a3.go!('a4')
88
- a4.go!('a3')
89
- a5.go!('a6')
90
- a6.go!('a5')
98
+ actors = []
99
+
100
+ puts 'Create actors...'
101
+ (0...DEMO_ACTOR_COUNT).each do |i|
102
+ name = i.to_s
103
+ actor = Tribe.registry[name] || MyActor.new(:name => name)
104
+ actors.push(actor)
105
+ puts name
106
+ end
107
+
108
+ puts 'Resetting...'
109
+ actors.each do |actor|
110
+ actor.reset_count!
111
+ end
112
+
113
+ puts 'Go...'
114
+ actors.each do |actor|
115
+ friend = actor.name.to_i
116
+ friend += 1
117
+ friend = 0 if friend == DEMO_ACTOR_COUNT
118
+ friend = friend.to_s
119
+
120
+ puts "pair: #{actor.name}, #{friend}"
121
+ actor.go!(friend)
122
+ end
123
+
124
+ puts 'Benchmark...'
125
+ result = Benchmark.realtime do
126
+ DEMO_ACTOR_COUNT.times do
127
+ $demo_queue.pop
128
+ end
129
+ end
130
+
131
+ puts result
132
+
133
+ nil
91
134
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
data/lib/tribe.rb CHANGED
@@ -3,23 +3,29 @@ require 'thread'
3
3
  require 'set'
4
4
 
5
5
  require 'tribe/actor'
6
- require 'tribe/singleton'
7
6
  require 'tribe/worker'
8
- require 'tribe/scheduler'
7
+ require 'tribe/dispatcher'
9
8
  require 'tribe/registry'
10
- require 'tribe/clock'
9
+ require 'tribe/scheduler'
11
10
  require 'tribe/timer'
11
+ require 'tribe/message'
12
+ require 'tribe/mailbox'
13
+ require 'tribe/thread_pool'
12
14
 
13
15
  module Tribe
14
- def self.scheduler
15
- Tribe::Scheduler.instance
16
+ def self.dispatcher
17
+ @dispatcher ||= Tribe::Dispatcher.new
16
18
  end
17
19
 
18
20
  def self.registry
19
- Tribe::Registry.instance
21
+ @registry ||= Tribe::Registry.new
20
22
  end
21
23
 
22
- def self.clock
23
- Tribe::Clock.instance
24
+ def self.scheduler
25
+ @scheduler ||= Tribe::Scheduler.new
24
26
  end
25
27
  end
28
+
29
+ Tribe.dispatcher
30
+ Tribe.registry
31
+ Tribe.scheduler
data/lib/tribe/actor.rb CHANGED
@@ -6,7 +6,7 @@ module Tribe
6
6
  run_hook(:pre_init)
7
7
 
8
8
  @alive = true
9
- @process_lock = Mutex.new
9
+ @mailbox = Mailbox.new
10
10
  @name = options[:name].freeze
11
11
 
12
12
  Tribe.registry.register(self)
@@ -26,20 +26,25 @@ module Tribe
26
26
  end
27
27
 
28
28
  def tell(method, *args)
29
- Tribe.scheduler.send(:schedule) do
30
- process(:method => method, :args => args)
29
+ message = { :method => method, :args => args }
30
+ @mailbox.deliver(message)
31
+
32
+ Tribe.dispatcher.send(:schedule) do
33
+ process
31
34
  end
32
35
 
33
36
  true
34
37
  end
35
38
 
36
39
  private
37
- def process(message)
38
- @process_lock.synchronize do
40
+ def process
41
+ @mailbox.retrieve_each do |message|
39
42
  begin
40
- send(message[:method], *message[:args]) if @alive
43
+ send(message[:method], *message[:args])
44
+ true
41
45
  rescue Exception => e
42
- @alive = false
46
+ puts "Actor died while processing: #{e.message}\n#{e.backtrace.join("\n")}"
47
+ false
43
48
  end
44
49
  end
45
50
  end
@@ -0,0 +1,16 @@
1
+ module Tribe
2
+ class Dispatcher
3
+ def initialize(options = {})
4
+ @pool = ThreadPool.new(:count => options[:count])
5
+ end
6
+
7
+ def shutdown
8
+ @pool.shutdown
9
+ end
10
+
11
+ private
12
+ def schedule(&block)
13
+ @pool.dispatch(&block)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ module Tribe
2
+ class Mailbox
3
+ RETRIEVE_CAP = -1 # Disable's the cap by default.
4
+
5
+ def initialize
6
+ @messages = Queue.new
7
+ @retrieve_lock = Mutex.new
8
+ end
9
+
10
+ # Returns true if it ran to completion, false if interupted.
11
+ # Block must return true on success, false on failure.
12
+ def retrieve_each(max = RETRIEVE_CAP, &block)
13
+ @retrieve_lock.synchronize do
14
+ count = 0
15
+
16
+ @messages.length.times do
17
+ if max >= 0 && count >= max
18
+ break
19
+ else
20
+ count += 1
21
+ end
22
+
23
+ message = @messages.pop
24
+
25
+ unless block.call(message)
26
+ return false
27
+ end
28
+ end
29
+
30
+ return true
31
+ end
32
+ end
33
+
34
+ def deliver(message)
35
+ @messages.push(message)
36
+ true
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,15 @@
1
+ module Tribe
2
+ class Message
3
+ attr_reader :from
4
+ attr_reader :to
5
+ attr_reader :method
6
+ attr_reader :args
7
+
8
+ def initialize(from, to, method, *args)
9
+ @from = from
10
+ @to = to
11
+ @method = method
12
+ @args = args
13
+ end
14
+ end
15
+ end
@@ -1,6 +1,5 @@
1
1
  module Tribe
2
- class Registry < Tribe::Singleton
3
-
2
+ class Registry
4
3
  def initialize
5
4
  @lock = Mutex.new
6
5
 
@@ -1,32 +1,77 @@
1
1
  module Tribe
2
- class Scheduler < Tribe::Singleton
3
- def initialize(opts = {})
4
- @count = opts[:count] || 20
5
- @workers = []
6
- @messages = Queue.new
2
+ class Scheduler
3
+ FREQUENCY = 100 # Hz.
7
4
 
8
- spawn(@count)
5
+ def initialize(options = {})
6
+ @frequency = options[:frequency] || FREQUENCY
7
+ @run = true
8
+ @timers = SortedSet.new
9
+ @messages = Queue.new
10
+ @thread = Thread.new { main }
9
11
  end
10
12
 
11
13
  def shutdown
12
- @count.times do
13
- @messages.push({ :command => :shutdown })
14
- end
14
+ message = { :command => :shutdown }
15
+ @messages.push(message)
15
16
 
16
- @workers.each do |worker|
17
- worker.join
18
- end
17
+ @thread.join
18
+ end
19
+
20
+ def schedule(timer)
21
+ message = { :command => :schedule, :timer => timer }
22
+ @messages.push(message)
23
+
24
+ true
25
+ end
26
+
27
+ def unschedule(timer)
28
+ message = { :command => :unschedule, :timer => timer }
29
+ @messages.push(message)
30
+
31
+ true
19
32
  end
20
33
 
21
34
  private
22
- def schedule(&block)
23
- @messages.push({ :command => :perform, :task => block })
35
+ def main
36
+ sleep_val = 1.0 / @frequency
37
+
38
+ while @run
39
+ sleep(sleep_val)
40
+ process_commands
41
+ fire_timers
42
+ end
24
43
  end
25
44
 
26
- def spawn(count)
27
- count.times do
28
- worker = Worker.new(@messages)
29
- @workers.push(worker)
45
+ def process_commands
46
+ @messages.length.times do
47
+ message = @messages.pop
48
+
49
+ case message[:command]
50
+ when :schedule
51
+ @timers.add(message[:timer])
52
+ when :unschedule
53
+ @timers.delete(message[:timer])
54
+ when :shutdown
55
+ @run = false
56
+ else
57
+ raise("Invalid command: #{message[:command]}")
58
+ end
59
+ end
60
+ end
61
+
62
+ def fire_timers
63
+ count = 0
64
+
65
+ while true
66
+ return 0 if @timers.empty?
67
+
68
+ if (timer = @timers.first).send(:fire?)
69
+ @timers.delete(timer)
70
+ timer.send(:fire)
71
+ count += 1
72
+ else
73
+ return count
74
+ end
30
75
  end
31
76
  end
32
77
  end
@@ -0,0 +1,58 @@
1
+ module Tribe
2
+ class ThreadPool
3
+ THREAD_COUNT = 64
4
+
5
+ def initialize(options = {})
6
+ @count = options[:count] || THREAD_COUNT
7
+
8
+ @threads = []
9
+ @lock = Mutex.new
10
+ @queue = Queue.new
11
+
12
+ spawn(@count)
13
+ end
14
+
15
+ def dispatch(&block)
16
+ @queue.push({ :command => :perform, :task => block })
17
+
18
+ true
19
+ end
20
+
21
+ def shutdown
22
+ @lock.synchronize do
23
+ @count.times do
24
+ @queue.push({ :command => :shutdown })
25
+ end
26
+
27
+ @threads.each { |thread| thread.join }
28
+
29
+ true
30
+ end
31
+ end
32
+
33
+ private
34
+ def spawn(count)
35
+ count.times do
36
+ @lock.synchronize do
37
+ thread = Thread.new { thread_main }
38
+ @threads.push(thread)
39
+ end
40
+ end
41
+ end
42
+
43
+ def thread_main
44
+ while (message = @queue.pop)
45
+ case message[:command]
46
+ when :perform
47
+ begin
48
+ message[:task].call
49
+ rescue Exception => e
50
+ puts "Worker caught exception: #{e.message}\n#{e.backtrace.join("\n")}"
51
+ end
52
+ when :shutdown
53
+ return
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
data/lib/tribe/timer.rb CHANGED
@@ -6,14 +6,14 @@ module Tribe
6
6
  def initialize(seconds, options = {}, &block)
7
7
  @seconds = seconds.to_f
8
8
  @callback = block
9
- @clock = options[:clock] || Tribe.clock
9
+ @scheduler = options[:scheduler] || Tribe.scheduler
10
10
  @repeat = options[:repeat] || false
11
11
 
12
12
  schedule
13
13
  end
14
14
 
15
15
  def cancel
16
- @clock.unschedule(self)
16
+ @scheduler.unschedule(self)
17
17
  end
18
18
 
19
19
  def <=>(timer)
@@ -45,7 +45,7 @@ module Tribe
45
45
 
46
46
  def schedule
47
47
  @fire_at = now + @seconds
48
- @clock.schedule(self)
48
+ @scheduler.schedule(self)
49
49
  end
50
50
  end
51
51
  end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tribe::Dispatcher do
4
+ end
data/tribe.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "tribe"
8
- s.version = "0.0.2"
8
+ s.version = "0.0.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Chad Remesch"]
@@ -28,14 +28,16 @@ Gem::Specification.new do |s|
28
28
  "VERSION",
29
29
  "lib/tribe.rb",
30
30
  "lib/tribe/actor.rb",
31
- "lib/tribe/clock.rb",
31
+ "lib/tribe/dispatcher.rb",
32
+ "lib/tribe/mailbox.rb",
33
+ "lib/tribe/message.rb",
32
34
  "lib/tribe/registry.rb",
33
35
  "lib/tribe/scheduler.rb",
34
- "lib/tribe/singleton.rb",
36
+ "lib/tribe/thread_pool.rb",
35
37
  "lib/tribe/timer.rb",
36
38
  "lib/tribe/worker.rb",
37
39
  "spec/lib/tribe/actor_spec.rb",
38
- "spec/lib/tribe/clock_spec.rb",
40
+ "spec/lib/tribe/dispatcher_spec.rb",
39
41
  "spec/lib/tribe/registry_spec.rb",
40
42
  "spec/lib/tribe/scheduler_spec.rb",
41
43
  "spec/lib/tribe/timer_spec.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tribe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -191,14 +191,16 @@ files:
191
191
  - VERSION
192
192
  - lib/tribe.rb
193
193
  - lib/tribe/actor.rb
194
- - lib/tribe/clock.rb
194
+ - lib/tribe/dispatcher.rb
195
+ - lib/tribe/mailbox.rb
196
+ - lib/tribe/message.rb
195
197
  - lib/tribe/registry.rb
196
198
  - lib/tribe/scheduler.rb
197
- - lib/tribe/singleton.rb
199
+ - lib/tribe/thread_pool.rb
198
200
  - lib/tribe/timer.rb
199
201
  - lib/tribe/worker.rb
200
202
  - spec/lib/tribe/actor_spec.rb
201
- - spec/lib/tribe/clock_spec.rb
203
+ - spec/lib/tribe/dispatcher_spec.rb
202
204
  - spec/lib/tribe/registry_spec.rb
203
205
  - spec/lib/tribe/scheduler_spec.rb
204
206
  - spec/lib/tribe/timer_spec.rb
@@ -220,7 +222,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
220
222
  version: '0'
221
223
  segments:
222
224
  - 0
223
- hash: 4061265025390310383
225
+ hash: 1198643590868831764
224
226
  required_rubygems_version: !ruby/object:Gem::Requirement
225
227
  none: false
226
228
  requirements:
data/lib/tribe/clock.rb DELETED
@@ -1,78 +0,0 @@
1
- module Tribe
2
- class Clock < Tribe::Singleton
3
- FREQUENCY = 100 # Hz.
4
-
5
- def initialize(options = {})
6
- @frequency = options[:frequency] || FREQUENCY
7
- @run = true
8
- @timers = SortedSet.new
9
- @messages = Queue.new
10
- @thread = Thread.new { main }
11
- end
12
-
13
- def shutdown
14
- message = { :command => :shutdown }
15
- @messages.push(message)
16
-
17
- @thread.join
18
- end
19
-
20
- def schedule(timer)
21
- message = { :command => :schedule, :timer => timer }
22
- @messages.push(message)
23
-
24
- true
25
- end
26
-
27
- def unschedule(timer)
28
- message = { :command => :unschedule, :timer => timer }
29
- @messages.push(message)
30
-
31
- true
32
- end
33
-
34
- private
35
- def main
36
- sleep_val = 1.0 / @frequency
37
-
38
- while @run
39
- sleep(sleep_val)
40
- process_commands
41
- fire_timers
42
- end
43
- end
44
-
45
- def process_commands
46
- @messages.length.times do
47
- message = @messages.pop
48
-
49
- case message[:command]
50
- when :schedule
51
- @timers.add(message[:timer])
52
- when :unschedule
53
- @timers.delete(message[:timer])
54
- when :shutdown
55
- @run = false
56
- else
57
- raise("Invalid command: #{message[:command]}")
58
- end
59
- end
60
- end
61
-
62
- def fire_timers
63
- count = 0
64
-
65
- while true
66
- return 0 if @timers.empty?
67
-
68
- if (timer = @timers.first).send(:fire?)
69
- @timers.delete(timer)
70
- timer.send(:fire)
71
- count += 1
72
- else
73
- return count
74
- end
75
- end
76
- end
77
- end
78
- end
@@ -1,5 +0,0 @@
1
- module Tribe
2
- class Singleton
3
- include ::Singleton
4
- end
5
- end
@@ -1,4 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Tribe::Clock do
4
- end