concur 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,6 +21,31 @@
21
21
  puts "pooled_duration=" + pooled_duration.to_s
22
22
  executor.shutdown
23
23
 
24
+ ## EventMachine / Non-blocking I/O
25
+
26
+ Perhaps more important/interesting these days is EventMachine/non-blocking io. When your program is io bound you can
27
+ get similar performance (if not better) on a single thread as you can with multi-threads.
28
+
29
+ # Create an EventMachineExecutor
30
+ executor = Concur::Executor.new_eventmachine_executor()
31
+
32
+ # Now fire off a bunch of http requests
33
+ futures = []
34
+ TestConcur.urls.each do |url|
35
+ params_to_send = {}
36
+ params_to_send[:base_url] = url
37
+ params_to_send[:path] = "/"
38
+ params_to_send[:http_method] = :get
39
+ futures << executor.http_request(params_to_send)
40
+ end
41
+ futures.each do |f|
42
+ puts 'got=' + f.get.inspect
43
+ assert f.get.status >= 200 && f.get.status < 400
44
+ end
45
+
46
+
47
+ See https://github.com/appoxy/concur/blob/master/test/test_concur.rb for more examples.
48
+
24
49
  ## Futures
25
50
 
26
51
  A Future is what is returned from the execute method. Call `future.get` to get the results of the block
@@ -1,5 +1,6 @@
1
1
 
2
2
  require_relative 'executor'
3
+ require_relative 'thread_pool'
3
4
 
4
5
  require 'logger'
5
6
  module Concur
@@ -1,39 +1,81 @@
1
1
  require 'faraday'
2
2
  require_relative 'runnable'
3
3
  require_relative 'future'
4
- require_relative 'thread_pool'
5
4
 
6
5
  module Concur
7
6
 
8
-
9
7
  # Decouples task submission from how each task is run. An Executor can be backed by a thread pool or some
10
8
  # other mechanism, but how you use the Executor won't change. This allows you to change the backend implementation
11
9
  # with minor code changes.
12
10
  #
13
11
  # Inspired by java.util.concurrent.Executor
14
- class Executor
12
+ module Executor
13
+
14
+ class Base
15
+ def initialize(options={})
16
+
17
+ end
18
+
19
+
20
+ def shutdown
21
+
22
+ end
15
23
 
16
- attr_accessor :thread_pool
24
+ def execute
25
+ raise "execute() not implemented, this is an invalid implemention."
26
+ end
27
+
28
+ def http_request(params, &blk)
29
+
30
+ f = StandardFuture.new do
31
+ conn = Faraday.new(:url => params[:base_url]) do |builder|
32
+ # builder.use Faraday::Request::UrlEncoded # convert request params as "www-form-urlencoded"
33
+ # builder.use Faraday::Request::JSON # encode request params as json
34
+ # builder.use Faraday::Response::Logger # log the request to STDOUT
35
+ builder.use Faraday::Adapter::NetHttp # make http requests with Net::HTTP
36
+ #
37
+ # # or, use shortcuts:
38
+ # builder.request :url_encoded
39
+ # builder.request :json
40
+ # builder.response :logger
41
+ # builder.adapter :net_http
42
+ end
43
+ if params[:http_method] == :post
44
+ response = conn.post params[:path]
45
+ else
46
+ response = conn.get params[:path]
47
+ end
48
+ if block_given?
49
+ response = blk.call(response)
50
+ end
51
+ response
52
+ end
53
+ @thread_pool.process(f)
54
+ f
55
+ end
17
56
 
18
- def initialize(options={})
19
57
 
58
+ def queue_size
59
+ 0
60
+ end
20
61
  end
21
62
 
63
+
22
64
  def self.new_single_threaded_executor(options={})
23
- executor = Executor.new
24
- executor.thread_pool = SingleThreaded.new
65
+ #executor = Executor.new
66
+ executor = SingleThreaded.new
25
67
  executor
26
68
  end
27
69
 
28
70
  def self.new_multi_threaded_executor(options={})
29
- executor = Executor.new
30
- executor.thread_pool = MultiThreaded.new
71
+ #executor = Executor.new
72
+ executor = MultiThreaded.new
31
73
  executor
32
74
  end
33
75
 
34
76
  def self.new_thread_pool_executor(max_size, options={})
35
- executor = Executor.new
36
- executor.thread_pool = ThreadPool.new(max_size)
77
+ #executor = Executor.new
78
+ executor = ThreadPool.new(max_size)
37
79
  executor
38
80
  end
39
81
 
@@ -50,60 +92,26 @@ module Concur
50
92
  executor
51
93
  end
52
94
 
53
- def execute(runnable=nil, &blk)
54
- f = StandardFuture.new(runnable, &blk)
55
- @thread_pool.process(f)
56
- f
57
- end
58
-
59
- def shutdown
60
- @thread_pool.shutdown
61
- end
62
-
63
- def http_request(params, &blk)
64
-
65
- f = StandardFuture.new do
66
- conn = Faraday.new(:url => params[:base_url]) do |builder|
67
- # builder.use Faraday::Request::UrlEncoded # convert request params as "www-form-urlencoded"
68
- # builder.use Faraday::Request::JSON # encode request params as json
69
- # builder.use Faraday::Response::Logger # log the request to STDOUT
70
- builder.use Faraday::Adapter::NetHttp # make http requests with Net::HTTP
71
- #
72
- # # or, use shortcuts:
73
- # builder.request :url_encoded
74
- # builder.request :json
75
- # builder.response :logger
76
- # builder.adapter :net_http
77
- end
78
- if params[:http_method] == :post
79
- response = conn.post params[:path]
80
- else
81
- response = conn.get params[:path]
82
- end
83
- if block_given?
84
- response = blk.call(response)
85
- end
86
- response
87
- end
88
- @thread_pool.process(f)
89
- f
90
- end
91
-
92
95
 
93
96
  end
94
97
 
95
98
 
96
99
  # todo: should maybe have these backends extend Executor and just override what's necessary
97
- class SingleThreaded
100
+ class SingleThreaded < Executor::Base
98
101
  def process(f)
99
102
  f.call
100
103
  end
101
104
 
105
+ def execute(f)
106
+ process(f)
107
+ end
108
+
102
109
  def shutdown
103
110
  end
104
111
  end
105
112
 
106
- class MultiThreaded
113
+ # Spins off a new thread per job
114
+ class MultiThreaded < Executor::Base
107
115
  def process(f)
108
116
  @thread = Thread.new do
109
117
  f.thread = @thread
@@ -111,6 +119,10 @@ module Concur
111
119
  end
112
120
  end
113
121
 
122
+ def execute(f)
123
+ process(f)
124
+ end
125
+
114
126
  def shutdown
115
127
 
116
128
  end
@@ -2,7 +2,7 @@ require 'eventmachine'
2
2
  require_relative '../futures/event_machine_future'
3
3
 
4
4
  module Concur
5
- class EventMachineExecutor
5
+ class EventMachineExecutor < Executor::Base
6
6
 
7
7
  def initialize
8
8
  unless EventMachine.reactor_running? # also check EventMachine.reactor_thread? ??
@@ -28,12 +28,12 @@ module Concur
28
28
  end
29
29
 
30
30
  def run
31
- puts 'running StandardFuture'
31
+ #Concur.logger.debug 'running StandardFuture'
32
32
  begin
33
33
  @result = @callable.call
34
- puts 'callable called: ' + @result.inspect
34
+ Concur.logger.debug 'callable result: ' + @result.inspect
35
35
  rescue Exception => ex
36
- puts 'error occurred'
36
+ Concur.logger.debug "Error occurred! #{ex.class.name}: #{ex.message}"
37
37
  @ex = ex
38
38
  end
39
39
  @mutex.synchronize do # do we even need to synchronize? run should only ever be called once
@@ -2,13 +2,16 @@ require 'thread'
2
2
  begin
3
3
  require 'fastthread'
4
4
  rescue LoadError
5
- $stderr.puts "Using the ruby-core thread implementation"
5
+ # $stderr.puts "Using the ruby-core thread implementation"
6
6
  end
7
7
 
8
8
  module Concur
9
9
 
10
10
  # Another example is here: # from: http://stackoverflow.com/questions/81788/deadlock-in-threadpool
11
- class ThreadPool
11
+ class ThreadPool < Executor::Base
12
+
13
+ attr_reader :queue
14
+
12
15
  def initialize(max_size)
13
16
  @max_size = max_size
14
17
  # @thread_queue = SizedQueue.new(max_size)
@@ -24,12 +27,20 @@ module Concur
24
27
  @running = false
25
28
  end
26
29
 
30
+
27
31
  def process(callable, &blk)
28
32
  callable = blk if block_given?
29
33
  @queue.push(callable)
30
34
  start_thread
31
35
  end
32
36
 
37
+ def execute(runnable=nil, &blk)
38
+ f = StandardFuture.new(runnable, &blk)
39
+ process(f)
40
+ f
41
+ end
42
+
43
+
33
44
  def start_thread
34
45
  @mutex.synchronize do
35
46
  if !@queue.empty? && @threads.size <= @max_size
@@ -47,6 +58,12 @@ module Concur
47
58
  end
48
59
  end
49
60
 
61
+
62
+ def queue_size
63
+ @queue.size
64
+ end
65
+
66
+
50
67
  class UberThread < Thread
51
68
 
52
69
  def initialize
@@ -58,5 +75,4 @@ module Concur
58
75
  end
59
76
 
60
77
 
61
-
62
78
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: concur
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.8
5
+ version: 0.1.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Travis Reeder
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-05-23 00:00:00 Z
13
+ date: 2011-11-05 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: eventmachine
@@ -45,6 +45,39 @@ dependencies:
45
45
  version: "0"
46
46
  type: :runtime
47
47
  version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: eventmachine
50
+ prerelease: false
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :runtime
58
+ version_requirements: *id004
59
+ - !ruby/object:Gem::Dependency
60
+ name: em-http-request
61
+ prerelease: false
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ type: :runtime
69
+ version_requirements: *id005
70
+ - !ruby/object:Gem::Dependency
71
+ name: faraday
72
+ prerelease: false
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ type: :runtime
80
+ version_requirements: *id006
48
81
  description: A concurrency library for Ruby inspired by java.util.concurrency. By http://www.appoxy.com
49
82
  email: travis@appoxy.com
50
83
  executables: []
@@ -89,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
122
  requirements: []
90
123
 
91
124
  rubyforge_project:
92
- rubygems_version: 1.7.2
125
+ rubygems_version: 1.8.8
93
126
  signing_key:
94
127
  specification_version: 3
95
128
  summary: A concurrency library for Ruby inspired by java.util.concurrency. By http://www.appoxy.com