tribe 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .gitignore
19
+ .DS_Store
20
+ *.swp
21
+ .rvmrc
data/Gemfile CHANGED
@@ -1,15 +1,4 @@
1
- source 'http://rubygems.org'
1
+ source 'https://rubygems.org'
2
2
 
3
- group :development do
4
- gem 'rdoc', '~> 3.12'
5
- gem 'rspec', '~> 2.10.0'
6
- gem 'bundler', '>= 1.1.0'
7
- gem 'jeweler', '~> 1.8.3'
8
- gem 'simplecov', '>= 0', :require => false
9
- gem 'rb-fsevent', :require => false
10
- gem 'guard', '~> 1.1.1'
11
- gem 'guard-rspec', '~> 1.0.0'
12
- #gem 'guard-bundler', '~> 0.1.3'
13
- gem 'growl'
14
- platform(:ruby) { gem 'debugger' }
15
- end
3
+ # Specify your gem's dependencies in tribe.gemspec
4
+ gemspec
data/Gemfile.lock CHANGED
@@ -1,70 +1,16 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ tribe (0.0.4)
5
+ workers (= 0.0.6)
6
+
1
7
  GEM
2
- remote: http://rubygems.org/
8
+ remote: https://rubygems.org/
3
9
  specs:
4
- columnize (0.3.6)
5
- debugger (1.1.4)
6
- columnize (>= 0.3.1)
7
- debugger-linecache (~> 1.1.1)
8
- debugger-ruby_core_source (~> 1.1.3)
9
- debugger-linecache (1.1.1)
10
- debugger-ruby_core_source (>= 1.1.1)
11
- debugger-ruby_core_source (1.1.3)
12
- diff-lcs (1.1.3)
13
- ffi (1.0.11)
14
- ffi (1.0.11-java)
15
- git (1.2.5)
16
- growl (1.0.3)
17
- guard (1.1.1)
18
- listen (>= 0.4.2)
19
- thor (>= 0.14.6)
20
- guard-rspec (1.0.0)
21
- guard (>= 1.1)
22
- jeweler (1.8.3)
23
- bundler (~> 1.0)
24
- git (>= 1.2.5)
25
- rake
26
- rdoc
27
- json (1.7.3)
28
- json (1.7.3-java)
29
- listen (0.4.4)
30
- rb-fchange (~> 0.0.5)
31
- rb-fsevent (~> 0.9.1)
32
- rb-inotify (~> 0.8.8)
33
- multi_json (1.3.6)
34
- rake (0.9.2.2)
35
- rb-fchange (0.0.5)
36
- ffi
37
- rb-fsevent (0.9.1)
38
- rb-inotify (0.8.8)
39
- ffi (>= 0.5.0)
40
- rdoc (3.12)
41
- json (~> 1.4)
42
- rspec (2.10.0)
43
- rspec-core (~> 2.10.0)
44
- rspec-expectations (~> 2.10.0)
45
- rspec-mocks (~> 2.10.0)
46
- rspec-core (2.10.1)
47
- rspec-expectations (2.10.0)
48
- diff-lcs (~> 1.1.3)
49
- rspec-mocks (2.10.1)
50
- simplecov (0.6.4)
51
- multi_json (~> 1.0)
52
- simplecov-html (~> 0.5.3)
53
- simplecov-html (0.5.3)
54
- thor (0.15.2)
10
+ workers (0.0.6)
55
11
 
56
12
  PLATFORMS
57
- java
58
13
  ruby
59
14
 
60
15
  DEPENDENCIES
61
- bundler (>= 1.1.0)
62
- debugger
63
- growl
64
- guard (~> 1.1.1)
65
- guard-rspec (~> 1.0.0)
66
- jeweler (~> 1.8.3)
67
- rb-fsevent
68
- rdoc (~> 3.12)
69
- rspec (~> 2.10.0)
70
- simplecov
16
+ tribe!
@@ -1,5 +1,7 @@
1
1
  Copyright (c) 2012 Chad Remesch
2
2
 
3
+ MIT License
4
+
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
5
7
  "Software"), to deal in the Software without restriction, including
@@ -17,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Tribe
2
+
3
+ Actor Model for Ruby. Currently experimental and not recommend for production.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'tribe'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install tribe
18
+
19
+ ## Contributing
20
+
21
+ 1. Fork it
22
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
23
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
24
+ 4. Push to the branch (`git push origin my-new-feature`)
25
+ 5. Create new Pull Request
26
+
27
+ ## Copyright
28
+
29
+ Copyright (c) 2012 Chad Remesch. See LICENSE.txt for further details.
data/Rakefile CHANGED
@@ -1,134 +1,106 @@
1
- # encoding: utf-8
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'benchmark'
2
4
 
3
- require 'rubygems'
4
- require 'bundler'
5
5
  begin
6
6
  Bundler.setup(:default, :development)
7
7
  rescue Bundler::BundlerError => e
8
- $stderr.puts e.message
9
- $stderr.puts "Run `bundle install` to install missing gems"
8
+ $stderr.puts(e.message)
9
+ $stderr.puts("Run `bundle install` to install missing gems")
10
10
  exit e.status_code
11
11
  end
12
- require 'rake'
13
-
14
- require 'jeweler'
15
- Jeweler::Tasks.new do |gem|
16
- # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
- gem.name = "tribe"
18
- gem.homepage = "http://github.com/chadrem/tribe"
19
- gem.license = "MIT"
20
- gem.summary = %Q{Actor Model for Ruby}
21
- gem.description = %Q{Actor Model for Ruby. Can support millions of actors (limited by memory). Currently experimental and not recommended for production.}
22
- gem.email = "chad@remesch.com"
23
- gem.authors = ["Chad Remesch"]
24
- # dependencies defined in Gemfile
25
- end
26
- Jeweler::RubygemsDotOrgTasks.new
27
-
28
- require 'rspec/core'
29
- require 'rspec/core/rake_task'
30
- RSpec::Core::RakeTask.new(:spec) do |spec|
31
- spec.pattern = FileList['spec/**/*_spec.rb']
32
- end
33
12
 
34
- RSpec::Core::RakeTask.new(:rcov) do |spec|
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.rcov = true
13
+ task :environment do
14
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
15
+ require 'tribe'
37
16
  end
38
17
 
39
- task :default => :spec
18
+ desc 'Start an IRB console with offier loaded'
19
+ task :console => :environment do
20
+ require 'irb'
40
21
 
41
- require 'rdoc/task'
42
- Rake::RDocTask.new do |rdoc|
43
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
22
+ ARGV.clear
44
23
 
45
- rdoc.rdoc_dir = 'rdoc'
46
- rdoc.title = "tribe #{version}"
47
- rdoc.rdoc_files.include('README*')
48
- rdoc.rdoc_files.include('lib/**/*.rb')
49
- end
50
-
51
- begin
52
- require 'debugger'
53
- rescue LoadError
24
+ IRB.start
54
25
  end
55
26
 
56
- require 'tribe'
27
+ desc 'Demo mode (temporary)'
28
+ task :demo => :environment do
29
+ $demo_queue = Queue.new
30
+ $demo_mutex = Mutex.new
57
31
 
58
- #
59
- # TODO: Temporary benchmarking/demo code.
60
- #
61
-
62
- require 'benchmark'
63
-
64
- $demo_queue = Queue.new
32
+ def locked_puts(msg)
33
+ $demo_mutex.synchronize { puts msg }
34
+ end
65
35
 
66
- DEMO_ACTOR_COUNT = 100
67
- DEMO_MSG_COUNT = 3000
36
+ DEMO_ACTOR_COUNT = 100
37
+ DEMO_MSG_COUNT = 3000
68
38
 
69
- class MyActor < Tribe::Actor
39
+ class MyActor < Tribe::Actor
70
40
 
71
- def pre_init
72
- reset_count
73
- end
41
+ def pre_init
42
+ reset_count
43
+ end
74
44
 
75
- def increment
76
- @count += 1
45
+ def increment
46
+ @count += 1
77
47
 
78
- if @count >= DEMO_MSG_COUNT
79
- puts "#{@name} done."
80
- $demo_queue.push(@name)
48
+ if @count >= DEMO_MSG_COUNT
49
+ locked_puts("#{@name} done.")
50
+ $demo_queue.push(@name)
51
+ end
81
52
  end
82
- end
83
53
 
84
- def reset_count
85
- @count = 0
86
- end
54
+ def reset_count
55
+ @count = 0
56
+ end
87
57
 
88
- def go(friend_name)
89
- friend = Tribe.registry[friend_name]
58
+ def go(friend_name)
59
+ friend = Tribe.registry[friend_name]
90
60
 
91
- DEMO_MSG_COUNT.times do
92
- friend.increment!
61
+ DEMO_MSG_COUNT.times do
62
+ friend.increment!
63
+ end
93
64
  end
94
65
  end
95
- end
96
66
 
97
- def tribe_demo
98
- actors = []
67
+ def tribe_demo
68
+ actors = []
99
69
 
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
70
+ locked_puts('Create actors...')
71
+ (0...DEMO_ACTOR_COUNT).each do |i|
72
+ name = i.to_s
73
+ actor = Tribe.registry[name] || MyActor.new(:name => name)
74
+ actors.push(actor)
75
+ locked_puts(name)
76
+ end
107
77
 
108
- puts 'Resetting...'
109
- actors.each do |actor|
110
- actor.reset_count!
111
- end
78
+ locked_puts('Resetting...')
79
+ actors.each do |actor|
80
+ actor.reset_count!
81
+ end
112
82
 
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
83
+ locked_puts('Go...')
84
+ actors.each do |actor|
85
+ friend = actor.name.to_i
86
+ friend += 1
87
+ friend = 0 if friend == DEMO_ACTOR_COUNT
88
+ friend = friend.to_s
119
89
 
120
- puts "pair: #{actor.name}, #{friend}"
121
- actor.go!(friend)
122
- end
90
+ locked_puts("pair: #{actor.name}, #{friend}")
91
+ actor.go!(friend)
92
+ end
123
93
 
124
- puts 'Benchmark...'
125
- result = Benchmark.realtime do
126
- DEMO_ACTOR_COUNT.times do
127
- $demo_queue.pop
94
+ locked_puts('Benchmark...')
95
+ result = Benchmark.realtime do
96
+ DEMO_ACTOR_COUNT.times do
97
+ $demo_queue.pop
98
+ end
128
99
  end
129
- end
130
100
 
131
- puts result
101
+ nil
102
+ end
132
103
 
133
- nil
104
+ tribe_demo
134
105
  end
106
+
data/lib/tribe/actor.rb CHANGED
@@ -1,61 +1,79 @@
1
1
  module Tribe
2
2
  class Actor
3
- attr_reader :name
3
+ include Workers::Helpers
4
4
 
5
5
  def initialize(options = {})
6
- run_hook(:pre_init)
7
-
6
+ @logger = Workers::LogProxy.new(options[:logger])
7
+ @dedicated = options[:dedicated] || false
8
+ @mailbox = options[:mailbox] || Tribe::Mailbox.new
9
+ @registry = options[:registry] || Tribe.registry
10
+ @name = options[:name]
11
+ @pool = @dedicated ? Workers::Pool.new(:size => 1) : (options[:pool] || Workers.pool)
8
12
  @alive = true
9
- @mailbox = Mailbox.new
10
- @name = options[:name].freeze
11
-
12
- Tribe.registry.register(self)
13
13
 
14
- run_hook(:post_init)
14
+ @registry.register(self)
15
15
  end
16
16
 
17
- def method_missing(method, *args, &block)
18
- m = method.to_s
19
- bang = m[-1] == '!'
17
+ def enqueue(command, data = nil)
18
+ return false unless @alive
20
19
 
21
- if bang && respond_to?(m.chop!)
22
- tell(m, *args)
23
- else
24
- super
20
+ @mailbox.push(Workers::Event.new(command, data))
21
+
22
+ @pool.perform do
23
+ process_events
25
24
  end
26
- end
27
25
 
28
- def tell(method, *args)
29
- message = { :method => method, :args => args }
30
- @mailbox.deliver(message)
26
+ return true
27
+ end
31
28
 
32
- Tribe.dispatcher.send(:schedule) do
33
- process
29
+ def alive?
30
+ @mailbox.synchronize do
31
+ return @alive
34
32
  end
33
+ end
34
+
35
+ def name
36
+ return @name
37
+ end
35
38
 
36
- true
39
+ def identifier
40
+ return @name ? "#{object_id}:#{@name}" : object_id
37
41
  end
38
42
 
39
43
  private
40
- def process
41
- @mailbox.retrieve_each do |message|
42
- begin
43
- send(message[:method], *message[:args])
44
- true
45
- rescue Exception => e
46
- puts "Actor died while processing: #{e.message}\n#{e.backtrace.join("\n")}"
47
- false
44
+
45
+ def process_events
46
+ while (event = @mailbox.shift)
47
+ case event.command
48
+ when :shutdown
49
+ shutdown_handler(event)
50
+ @pool.shutdown if @dedicated
51
+ @mailbox.synchronize do
52
+ @alive = false
53
+ end
54
+ else
55
+ process_event(event)
48
56
  end
49
57
  end
58
+ rescue Exception => e
59
+ @alive = false
60
+ exception_handler(e)
50
61
  end
51
62
 
52
- def terminate
53
- @alive = false
54
- Tribe.registry.unregister(self)
63
+ #
64
+ # Subclass and override the below methods.
65
+ #
66
+
67
+ def process_event(event)
68
+ puts "Actor (#{identifier}) received event (#{event.inspect})."
69
+ end
70
+
71
+ def exception_handler(e)
72
+ puts concat_e("Actor (#{identifier}) died.", e)
55
73
  end
56
74
 
57
- def run_hook(hook)
58
- send(hook) if respond_to?(hook, true)
75
+ def shutdown_handler(event)
76
+ puts "Actor (#{identifier}) is shutting down."
59
77
  end
60
78
  end
61
79
  end
@@ -0,0 +1,9 @@
1
+ module Tribe
2
+ class DedicatedActor < Tribe::Actor
3
+ def initialize(options = {})
4
+ options[:dedicated] = true
5
+
6
+ super(options)
7
+ end
8
+ end
9
+ end
data/lib/tribe/mailbox.rb CHANGED
@@ -1,39 +1,26 @@
1
1
  module Tribe
2
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
3
+ def initialize(options = {})
4
+ @messages = []
5
+ @mutex = Mutex.new
8
6
  end
9
7
 
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
8
+ def push(event)
9
+ @mutex.synchronize do
10
+ @messages.push(event)
11
+ end
12
+ end
29
13
 
30
- return true
14
+ def shift
15
+ @mutex.synchronize do
16
+ @messages.shift
31
17
  end
32
18
  end
33
19
 
34
- def deliver(message)
35
- @messages.push(message)
36
- true
20
+ def synchronize(&block)
21
+ @mutex.synchronize do
22
+ block.call
23
+ end
37
24
  end
38
25
  end
39
26
  end
@@ -1,44 +1,45 @@
1
1
  module Tribe
2
2
  class Registry
3
3
  def initialize
4
- @lock = Mutex.new
5
-
6
- @actors_by_oid = {}
4
+ @mutex = Mutex.new
7
5
  @actors_by_name = {}
8
6
  end
9
7
 
10
8
  def register(actor)
11
- @lock.synchronize do
12
- @actors_by_oid[actor.object_id] = actor
13
-
14
- if actor.name
15
- if @actors_by_name[actor.name]
16
- raise "Actor already exists. name=#{actor.name}"
17
- else
18
- @actors_by_name[actor.name] = actor
19
- end
9
+ @mutex.synchronize do
10
+ return false unless actor.name
11
+
12
+ if @actors_by_name[actor.name]
13
+ raise "Actor already exists (#{actor.name})."
14
+ else
15
+ @actors_by_name[actor.name] = actor
20
16
  end
21
- end
22
17
 
23
- true
18
+ return true
19
+ end
24
20
  end
25
21
 
26
22
  def unregister(actor)
27
- @lock.synchronize do
28
- @actors_by_oid.delete(actor.object_id)
23
+ @mutex.synchronize do
24
+ return false unless actor.name
25
+ return false unless @actors_by_name[actor.name]
29
26
 
30
- if actor.name
31
- @actors_by_name.delete(actor.name)
32
- end
33
- end
27
+ @actors_by_name.delete(actor.name)
34
28
 
35
- true
29
+ return true
30
+ end
36
31
  end
37
32
 
38
33
  def [](val)
39
- @lock.synchronize do
34
+ @mutex.synchronize do
40
35
  return @actors_by_name[val]
41
36
  end
42
37
  end
38
+
39
+ def dispose
40
+ @mutex.synchronize do
41
+ @actors_by_name.clear
42
+ end
43
+ end
43
44
  end
44
45
  end
@@ -0,0 +1,3 @@
1
+ module Tribe
2
+ VERSION = '0.0.4'
3
+ end
data/lib/tribe.rb CHANGED
@@ -1,31 +1,20 @@
1
- require 'singleton'
2
- require 'thread'
3
- require 'set'
1
+ require 'workers'
4
2
 
3
+ require 'tribe/mailbox'
5
4
  require 'tribe/actor'
6
- require 'tribe/worker'
7
- require 'tribe/dispatcher'
5
+ require 'tribe/dedicated_actor'
8
6
  require 'tribe/registry'
9
- require 'tribe/scheduler'
10
- require 'tribe/timer'
11
- require 'tribe/message'
12
- require 'tribe/mailbox'
13
- require 'tribe/thread_pool'
14
7
 
15
8
  module Tribe
16
- def self.dispatcher
17
- @dispatcher ||= Tribe::Dispatcher.new
18
- end
19
-
20
9
  def self.registry
21
- @registry ||= Tribe::Registry.new
22
- end
10
+ return @registry ||= Tribe::Registry.new
11
+ end
23
12
 
24
- def self.scheduler
25
- @scheduler ||= Tribe::Scheduler.new
26
- end
13
+ def self.registry=(val)
14
+ @registry.dispose if @registry
15
+ @registry = val
16
+ end
27
17
  end
28
18
 
29
- Tribe.dispatcher
19
+ # Force initialization of defaults.
30
20
  Tribe.registry
31
- Tribe.scheduler