concur 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/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