tribe 0.0.1 → 0.0.2

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