concur 0.0.8 → 0.1.0
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/README.markdown +25 -0
- data/lib/concur.rb +1 -0
- data/lib/executor.rb +64 -52
- data/lib/executors/event_machine_executor.rb +1 -1
- data/lib/future.rb +3 -3
- data/lib/thread_pool.rb +19 -3
- metadata +36 -3
data/README.markdown
CHANGED
@@ -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
|
data/lib/concur.rb
CHANGED
data/lib/executor.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
-
|
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? ??
|
data/lib/future.rb
CHANGED
@@ -28,12 +28,12 @@ module Concur
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def run
|
31
|
-
|
31
|
+
#Concur.logger.debug 'running StandardFuture'
|
32
32
|
begin
|
33
33
|
@result = @callable.call
|
34
|
-
|
34
|
+
Concur.logger.debug 'callable result: ' + @result.inspect
|
35
35
|
rescue Exception => ex
|
36
|
-
|
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
|
data/lib/thread_pool.rb
CHANGED
@@ -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
|
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
|
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.
|
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
|