tribe 0.0.1 → 0.0.2

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/README.rdoc CHANGED
@@ -1,9 +1,9 @@
1
1
  = tribe
2
2
 
3
- Actor Model for Ruby. Experimental.
3
+ Actor Model for Ruby. Currently experimental and not recommend for production. Many standard features of the Actor Model are missing.
4
4
 
5
5
  == Contributing to tribe
6
-
6
+
7
7
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8
8
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
9
9
  * Fork the project.
data/Rakefile CHANGED
@@ -54,3 +54,38 @@ rescue LoadError
54
54
  end
55
55
 
56
56
  require 'tribe'
57
+
58
+ class MyActor < Tribe::Actor
59
+ def pre_init
60
+ @count = 0
61
+ end
62
+
63
+ def increment
64
+ @count += 1
65
+ puts "#{@name}=#{@count}" if @count >= 50000
66
+ end
67
+
68
+ def go(friend_name)
69
+ friend = Tribe.registry[friend_name]
70
+
71
+ 50000.times do
72
+ friend.increment!
73
+ end
74
+ end
75
+ end
76
+
77
+ 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')
91
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
data/lib/tribe.rb CHANGED
@@ -1,11 +1,14 @@
1
1
  require 'singleton'
2
2
  require 'thread'
3
+ require 'set'
3
4
 
4
5
  require 'tribe/actor'
5
6
  require 'tribe/singleton'
6
7
  require 'tribe/worker'
7
8
  require 'tribe/scheduler'
8
9
  require 'tribe/registry'
10
+ require 'tribe/clock'
11
+ require 'tribe/timer'
9
12
 
10
13
  module Tribe
11
14
  def self.scheduler
@@ -15,4 +18,8 @@ module Tribe
15
18
  def self.registry
16
19
  Tribe::Registry.instance
17
20
  end
21
+
22
+ def self.clock
23
+ Tribe::Clock.instance
24
+ end
18
25
  end
data/lib/tribe/actor.rb CHANGED
@@ -3,10 +3,15 @@ module Tribe
3
3
  attr_reader :name
4
4
 
5
5
  def initialize(options = {})
6
+ run_hook(:pre_init)
7
+
8
+ @alive = true
6
9
  @process_lock = Mutex.new
7
10
  @name = options[:name].freeze
8
11
 
9
12
  Tribe.registry.register(self)
13
+
14
+ run_hook(:post_init)
10
15
  end
11
16
 
12
17
  def method_missing(method, *args, &block)
@@ -14,14 +19,14 @@ module Tribe
14
19
  bang = m[-1] == '!'
15
20
 
16
21
  if bang && respond_to?(m.chop!)
17
- async(m, *args)
22
+ tell(m, *args)
18
23
  else
19
24
  super
20
25
  end
21
26
  end
22
27
 
23
- def async(method, *args)
24
- Tribe.scheduler.schedule do
28
+ def tell(method, *args)
29
+ Tribe.scheduler.send(:schedule) do
25
30
  process(:method => method, :args => args)
26
31
  end
27
32
 
@@ -31,12 +36,21 @@ module Tribe
31
36
  private
32
37
  def process(message)
33
38
  @process_lock.synchronize do
34
- send(message[:method], *message[:args])
39
+ begin
40
+ send(message[:method], *message[:args]) if @alive
41
+ rescue Exception => e
42
+ @alive = false
43
+ end
35
44
  end
36
45
  end
37
46
 
38
- def shutdown
39
- Tribe.registry.unregistry(self)
47
+ def terminate
48
+ @alive = false
49
+ Tribe.registry.unregister(self)
50
+ end
51
+
52
+ def run_hook(hook)
53
+ send(hook) if respond_to?(hook, true)
40
54
  end
41
55
  end
42
56
  end
@@ -0,0 +1,78 @@
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
@@ -2,28 +2,44 @@ module Tribe
2
2
  class Registry < Tribe::Singleton
3
3
 
4
4
  def initialize
5
+ @lock = Mutex.new
6
+
5
7
  @actors_by_oid = {}
6
8
  @actors_by_name = {}
7
9
  end
8
10
 
9
11
  def register(actor)
10
- @actors_by_oid[actor.object_id] = actor
12
+ @lock.synchronize do
13
+ @actors_by_oid[actor.object_id] = actor
11
14
 
12
- if actor.name
13
- @actors_by_name[actor.name] = actor
15
+ if actor.name
16
+ if @actors_by_name[actor.name]
17
+ raise "Actor already exists. name=#{actor.name}"
18
+ else
19
+ @actors_by_name[actor.name] = actor
20
+ end
21
+ end
14
22
  end
23
+
24
+ true
15
25
  end
16
26
 
17
27
  def unregister(actor)
18
- @actors_by_oid.delete(actor.object_id)
28
+ @lock.synchronize do
29
+ @actors_by_oid.delete(actor.object_id)
19
30
 
20
- if actor.name
21
- @actors_by_name.delete(actor.name)
31
+ if actor.name
32
+ @actors_by_name.delete(actor.name)
33
+ end
22
34
  end
35
+
36
+ true
23
37
  end
24
38
 
25
39
  def [](val)
26
- @actors_by_name[val]
40
+ @lock.synchronize do
41
+ return @actors_by_name[val]
42
+ end
27
43
  end
28
44
  end
29
45
  end
@@ -8,10 +8,6 @@ module Tribe
8
8
  spawn(@count)
9
9
  end
10
10
 
11
- def schedule(&block)
12
- @messages.push({ :command => :perform, :task => block })
13
- end
14
-
15
11
  def shutdown
16
12
  @count.times do
17
13
  @messages.push({ :command => :shutdown })
@@ -23,6 +19,10 @@ module Tribe
23
19
  end
24
20
 
25
21
  private
22
+ def schedule(&block)
23
+ @messages.push({ :command => :perform, :task => block })
24
+ end
25
+
26
26
  def spawn(count)
27
27
  count.times do
28
28
  worker = Worker.new(@messages)
@@ -0,0 +1,51 @@
1
+ module Tribe
2
+ class Timer
3
+ attr_reader :fire_at
4
+ attr_reader :repeat
5
+
6
+ def initialize(seconds, options = {}, &block)
7
+ @seconds = seconds.to_f
8
+ @callback = block
9
+ @clock = options[:clock] || Tribe.clock
10
+ @repeat = options[:repeat] || false
11
+
12
+ schedule
13
+ end
14
+
15
+ def cancel
16
+ @clock.unschedule(self)
17
+ end
18
+
19
+ def <=>(timer)
20
+ if self.object_id == timer.object_id
21
+ return 0
22
+ else
23
+ return self.fire_at <=> timer.fire_at
24
+ end
25
+ end
26
+
27
+ private
28
+ def fire?(current_time = nil)
29
+ current_time ||= now
30
+
31
+ now >= @fire_at
32
+ end
33
+
34
+ def fire
35
+ @callback.call
36
+ rescue Exception => e
37
+ puts "Timer caught exception: #{e.message}\n#{e.backtrace.join("\n")}"
38
+ ensure
39
+ schedule if @repeat
40
+ end
41
+
42
+ def now
43
+ Time.now.to_f
44
+ end
45
+
46
+ def schedule
47
+ @fire_at = now + @seconds
48
+ @clock.schedule(self)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tribe::Clock do
4
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Tribe::Timer do
4
+ end
data/tribe.gemspec ADDED
@@ -0,0 +1,91 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "tribe"
8
+ s.version = "0.0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Chad Remesch"]
12
+ s.date = "2012-06-10"
13
+ s.description = "Actor Model for Ruby. Can support millions of actors (limited by memory). Currently experimental and not recommended for production."
14
+ s.email = "chad@remesch.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "Guardfile",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "lib/tribe.rb",
30
+ "lib/tribe/actor.rb",
31
+ "lib/tribe/clock.rb",
32
+ "lib/tribe/registry.rb",
33
+ "lib/tribe/scheduler.rb",
34
+ "lib/tribe/singleton.rb",
35
+ "lib/tribe/timer.rb",
36
+ "lib/tribe/worker.rb",
37
+ "spec/lib/tribe/actor_spec.rb",
38
+ "spec/lib/tribe/clock_spec.rb",
39
+ "spec/lib/tribe/registry_spec.rb",
40
+ "spec/lib/tribe/scheduler_spec.rb",
41
+ "spec/lib/tribe/timer_spec.rb",
42
+ "spec/lib/tribe/worker_spec.rb",
43
+ "spec/spec_helper.rb",
44
+ "tribe.gemspec"
45
+ ]
46
+ s.homepage = "http://github.com/chadrem/tribe"
47
+ s.licenses = ["MIT"]
48
+ s.require_paths = ["lib"]
49
+ s.rubygems_version = "1.8.23"
50
+ s.summary = "Actor Model for Ruby"
51
+
52
+ if s.respond_to? :specification_version then
53
+ s.specification_version = 3
54
+
55
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
56
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
57
+ s.add_development_dependency(%q<rspec>, ["~> 2.10.0"])
58
+ s.add_development_dependency(%q<bundler>, [">= 1.1.0"])
59
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
60
+ s.add_development_dependency(%q<simplecov>, [">= 0"])
61
+ s.add_development_dependency(%q<rb-fsevent>, [">= 0"])
62
+ s.add_development_dependency(%q<guard>, ["~> 1.1.1"])
63
+ s.add_development_dependency(%q<guard-rspec>, ["~> 1.0.0"])
64
+ s.add_development_dependency(%q<growl>, [">= 0"])
65
+ s.add_development_dependency(%q<debugger>, [">= 0"])
66
+ else
67
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
68
+ s.add_dependency(%q<rspec>, ["~> 2.10.0"])
69
+ s.add_dependency(%q<bundler>, [">= 1.1.0"])
70
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
71
+ s.add_dependency(%q<simplecov>, [">= 0"])
72
+ s.add_dependency(%q<rb-fsevent>, [">= 0"])
73
+ s.add_dependency(%q<guard>, ["~> 1.1.1"])
74
+ s.add_dependency(%q<guard-rspec>, ["~> 1.0.0"])
75
+ s.add_dependency(%q<growl>, [">= 0"])
76
+ s.add_dependency(%q<debugger>, [">= 0"])
77
+ end
78
+ else
79
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
80
+ s.add_dependency(%q<rspec>, ["~> 2.10.0"])
81
+ s.add_dependency(%q<bundler>, [">= 1.1.0"])
82
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
83
+ s.add_dependency(%q<simplecov>, [">= 0"])
84
+ s.add_dependency(%q<rb-fsevent>, [">= 0"])
85
+ s.add_dependency(%q<guard>, ["~> 1.1.1"])
86
+ s.add_dependency(%q<guard-rspec>, ["~> 1.0.0"])
87
+ s.add_dependency(%q<growl>, [">= 0"])
88
+ s.add_dependency(%q<debugger>, [">= 0"])
89
+ end
90
+ end
91
+
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.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -191,15 +191,20 @@ files:
191
191
  - VERSION
192
192
  - lib/tribe.rb
193
193
  - lib/tribe/actor.rb
194
+ - lib/tribe/clock.rb
194
195
  - lib/tribe/registry.rb
195
196
  - lib/tribe/scheduler.rb
196
197
  - lib/tribe/singleton.rb
198
+ - lib/tribe/timer.rb
197
199
  - lib/tribe/worker.rb
198
200
  - spec/lib/tribe/actor_spec.rb
201
+ - spec/lib/tribe/clock_spec.rb
199
202
  - spec/lib/tribe/registry_spec.rb
200
203
  - spec/lib/tribe/scheduler_spec.rb
204
+ - spec/lib/tribe/timer_spec.rb
201
205
  - spec/lib/tribe/worker_spec.rb
202
206
  - spec/spec_helper.rb
207
+ - tribe.gemspec
203
208
  homepage: http://github.com/chadrem/tribe
204
209
  licenses:
205
210
  - MIT
@@ -215,7 +220,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
215
220
  version: '0'
216
221
  segments:
217
222
  - 0
218
- hash: -1241874824708307471
223
+ hash: 4061265025390310383
219
224
  required_rubygems_version: !ruby/object:Gem::Requirement
220
225
  none: false
221
226
  requirements: