concur 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/concur.rb CHANGED
@@ -1,3 +1,15 @@
1
1
 
2
2
  require_relative 'executor'
3
- require_relative 'executor'
3
+
4
+ require 'logger'
5
+ module Concur
6
+ @@logger = Logger.new(STDOUT)
7
+
8
+ def self.logger
9
+ @@logger
10
+ end
11
+ def self.logger=(logger)
12
+ @@logger = logger
13
+ end
14
+
15
+ end
data/lib/executor.rb CHANGED
@@ -1,10 +1,49 @@
1
1
  require_relative 'runnable'
2
2
  require_relative 'future'
3
+ require_relative 'thread_pool'
4
+
3
5
 
4
6
  module Concur
7
+
8
+
9
+ # Decouples task submission from how each task is run. An Executor can be backed by a thread pool or some
10
+ # other mechanism, but how you use the Executor won't change. This allows you to change the backend implementation
11
+ # with minor code changes.
12
+ #
13
+ # Inspired by java.util.concurrent.Executor
5
14
  class Executor
6
- def queue(runnable)
7
- Future.new(runnable)
15
+
16
+ attr_accessor :thread_pool
17
+
18
+ def initialize(options={})
19
+
20
+ end
21
+
22
+ def self.new_thread_pool_executor(options={})
23
+ executor = Executor.new
24
+ executor.thread_pool = ThreadPool.new(options[:max_size])
25
+ p executor.thread_pool
26
+ executor
27
+ end
28
+
29
+ def execute(runnable, &blk)
30
+ puts 'executing ' + runnable.inspect
31
+ f = Future.new(runnable, &blk)
32
+ if @thread_pool
33
+ @thread_pool.process(f)
34
+ else
35
+ @thread = Thread.new do
36
+ f.thread = @thread
37
+ f.call
38
+ end
39
+ end
40
+ f
41
+ end
42
+
43
+ def shutdown
44
+ if @thread_pool
45
+ @thread_pool.shutdown
46
+ end
8
47
  end
9
48
  end
10
49
  end
data/lib/future.rb CHANGED
@@ -1,27 +1,49 @@
1
- =begin
2
- An Future is a class that captures the results of a threaded object so you can retreive these results later.
1
+ module Concur
3
2
 
4
- Example usage:
3
+ #An Future is a class that captures the results of a threaded object so you can retreive the results later.
4
+ #This is what is returned from Executors.
5
+ class Future
6
+ attr_accessor :thread, :pool
5
7
 
6
- searcher = Searcher.new
7
- # todo: not sure if this will be usage in the end.... but
8
- future = Future.new(searcher)
9
- do_some_other_stuff_in_the_meantime()
10
- search_results = future.get
8
+ def initialize(runnable=nil, &block)
11
9
 
12
- `get` retrieves the results.
13
- =end
14
- module Concur
15
- class Future
16
- def initialize(runnable)
17
- @thread = Thread.new do
18
- @result = runnable.run
10
+ # if block
11
+ # @thread = Thread.new do
12
+ # @result = yield
13
+ # end
14
+ # else
15
+ # @thread = Thread.new do
16
+ # @result = runnable.run
17
+ # end
18
+ # end
19
+
20
+ @mutex = Mutex.new
21
+ @cv = ConditionVariable.new
22
+ @callable = runnable
23
+ if block_given?
24
+ @callable = block
19
25
  end
26
+
27
+ end
28
+
29
+ def run
30
+ @result = @callable.call
31
+ @complete = true
32
+ @cv.signal
33
+ end
34
+
35
+ def call
36
+ run
20
37
  end
21
38
 
22
39
  # Returns results. Will wait for thread to complete execution if not already complete.
23
40
  def get
24
- @thread.join
41
+ # @thread.value
42
+ @mutex.synchronize do
43
+ unless @complete
44
+ @cv.wait(@mutex)
45
+ end
46
+ end
25
47
  @result
26
48
  end
27
49
  end
data/lib/runnable.rb CHANGED
@@ -1,10 +1,13 @@
1
1
 
2
2
  # A mixin for runnable classes.
3
-
4
3
  module Concur
5
4
  module Runnable
6
5
  def run
7
6
  raise "No run method defined in your runable!"
8
7
  end
8
+
9
+ def call
10
+ run
11
+ end
9
12
  end
10
13
  end
@@ -0,0 +1,63 @@
1
+ require 'thread'
2
+ begin
3
+ require 'fastthread'
4
+ rescue LoadError
5
+ $stderr.puts "Using the ruby-core thread implementation"
6
+ end
7
+
8
+ module Concur
9
+
10
+ # Another example is here: # from: http://stackoverflow.com/questions/81788/deadlock-in-threadpool
11
+ class ThreadPool
12
+ def initialize(max_size)
13
+ @max_size = max_size
14
+ # @thread_queue = SizedQueue.new(max_size)
15
+ @running = true
16
+ @mutex = Mutex.new
17
+ @cv = ConditionVariable.new
18
+ @queue = Queue.new
19
+ @threads = []
20
+
21
+ end
22
+
23
+ def shutdown
24
+ @running = false
25
+ end
26
+
27
+ def process(callable, &blk)
28
+ callable = blk if block_given?
29
+ @queue.push(callable)
30
+ start_thread
31
+ end
32
+
33
+ def start_thread
34
+ @mutex.synchronize do
35
+ if !@queue.empty? && @threads.size <= @max_size
36
+ t = UberThread.new do
37
+ while @running
38
+ f = @queue.pop
39
+ f.thread = t
40
+ f.pool = self
41
+ f.call
42
+ end
43
+ Concur.logger.info "Thread dying " + t.inspect
44
+ end
45
+ Concur.logger.info "Created new thread " + t.inspect
46
+ @threads << t
47
+ end
48
+ end
49
+ end
50
+
51
+ class UberThread < Thread
52
+
53
+ def initialize
54
+ super
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+
62
+
63
+ end
@@ -1,11 +1,14 @@
1
1
  require 'rspec'
2
2
 
3
- require_relative '../lib/executor'
3
+ require_relative '../lib/concur'
4
4
  require_relative 'job'
5
5
 
6
6
  describe Concur::Executor do
7
- describe "#score" do
7
+ describe "#score" do
8
8
  it "runs faster in parallel" do
9
+
10
+ job = Job.new(1)
11
+ puts 'runnable? ' + job.is_a?(Concur::Runnable).to_s
9
12
  start_time = Time.now
10
13
  20.times do |i|
11
14
  job = Job.new(i)
@@ -14,24 +17,66 @@ describe Concur::Executor do
14
17
  non_concurrent_duration = Time.now - start_time
15
18
  puts "duration=" + non_concurrent_duration.to_s
16
19
 
17
- puts
20
+ puts '---------------'
18
21
 
19
22
  puts "Now for concurrent"
20
- executor = Concur::Executor.new
23
+ executor = Concur::Executor.new # unbounded
21
24
  start_time = Time.now
25
+
22
26
  jobs = []
23
27
  20.times do |i|
24
28
  job = Job.new(i)
25
- future = executor.queue(job)
29
+ future = executor.execute(job)
26
30
  jobs << future
27
31
  end
28
32
  jobs.each do |j|
29
33
  puts "uber fast result=#{j.get}"
30
34
  end
31
35
  concurrent_duration = Time.now - start_time
32
- puts "duration=" + concurrent_duration .to_s
36
+ puts "duration=" + concurrent_duration.to_s
37
+
38
+ concurrent_duration.should be < (non_concurrent_duration/2)
39
+
40
+ puts "Now for pooled"
41
+ executor = Concur::Executor.new_thread_pool_executor(:max_size=>10)
42
+ start_time = Time.now
43
+
44
+ jobs = []
45
+ 20.times do |i|
46
+ job = Job.new(i)
47
+ future = executor.execute(job)
48
+ jobs << future
49
+ end
50
+ jobs.each do |j|
51
+ puts "uber fast result=#{j.get}"
52
+ end
53
+ pooled_duration = Time.now - start_time
54
+ puts "pooled_duration=" + pooled_duration.to_s
55
+
56
+ pooled_duration.should be < (non_concurrent_duration/2)
57
+ # pooled_duration.should_be > concurrent_duration
58
+
59
+ executor.shutdown
33
60
 
34
- concurrent_duration.should be < (non_concurrent_duration-2)
35
61
  end
36
62
  end
37
63
  end
64
+
65
+ describe Concur::Future do
66
+
67
+ describe "#new" do
68
+
69
+ it "can accept blocks" do
70
+ future = Concur::Future.new do
71
+ puts "i'm in the block"
72
+ "result of block"
73
+ end
74
+ puts 'get=' + future.get
75
+ future.get.should == "result of block"
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
82
+
data/test/job.rb CHANGED
@@ -9,7 +9,7 @@ class Job
9
9
 
10
10
  def run
11
11
  sleep 0.25
12
- puts "#{@i} finished"
12
+ puts "Finished #{@i}"
13
13
  "response #{@i}"
14
14
  end
15
15
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: concur
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:
@@ -9,9 +9,20 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-02-23 00:00:00.000000000 -08:00
12
+ date: 2011-03-10 00:00:00.000000000 -08:00
13
13
  default_executable:
14
- dependencies: []
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ requirement: &22290888 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *22290888
15
26
  description: A concurrency library for Ruby like java.util.concurrency By http://www.appoxy.com
16
27
  email: travis@appoxy.com
17
28
  executables: []
@@ -23,6 +34,7 @@ files:
23
34
  - lib/executor.rb
24
35
  - lib/future.rb
25
36
  - lib/runnable.rb
37
+ - lib/thread_pool.rb
26
38
  - README.markdown
27
39
  - test/executor_spec.rb
28
40
  - test/job.rb
@@ -47,7 +59,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
59
  version: '0'
48
60
  requirements: []
49
61
  rubyforge_project:
50
- rubygems_version: 1.5.2
62
+ rubygems_version: 1.6.0
51
63
  signing_key:
52
64
  specification_version: 3
53
65
  summary: A concurrency library for Ruby like java.util.concurrency. By http://www.appoxy.com