ActionPool 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ActionPool-0.0.1.gem +0 -0
- data/ActionPool-0.1.0.gem +0 -0
- data/ActionPool-0.2.0.gem +0 -0
- data/CHANGELOG +6 -0
- data/README.rdoc +22 -5
- data/actionpool.gemspec +3 -3
- data/lib/actionpool.rb +6 -0
- data/lib/actionpool/LogHelper.rb +1 -2
- data/lib/actionpool/Pool.rb +76 -30
- data/lib/actionpool/Queue.rb +10 -2
- data/lib/actionpool/Thread.rb +64 -9
- data/tests/cases/general.rb +42 -0
- data/tests/cases/grow.rb +20 -0
- data/tests/cases/nogrow.rb +12 -0
- data/tests/cases/queue.rb +34 -0
- data/tests/cases/resize.rb +29 -0
- data/tests/cases/shutdown.rb +16 -0
- data/tests/cases/thread.rb +24 -0
- data/tests/cases/timeouts.rb +19 -0
- data/tests/run_tests.rb +6 -0
- metadata +19 -7
- data/test.rb +0 -10
- data/test2.rb +0 -18
data/ActionPool-0.0.1.gem
CHANGED
Binary file
|
data/ActionPool-0.1.0.gem
CHANGED
Binary file
|
File without changes
|
data/CHANGELOG
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
0.2.0
|
2
|
+
- Added argument support for passed tasks (thanks simonmenke)
|
3
|
+
- Faster pool resizing
|
4
|
+
- Smarter thread creation to limit unneeded creation
|
5
|
+
- Allow floats for timeouts to provide better control
|
6
|
+
- Test verified support for 1.8.6, 1.8.7, 1.9.1 and JRuby 1.4.0RC1
|
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
== ActionPool
|
2
2
|
|
3
|
-
ActionPool is just a simple thread pool. It allows for various
|
3
|
+
ActionPool is just a simple thread pool. It allows for various constraints and resizing in a pretty easy and unobtrusive manner. You can set limits on how long tasks are worked on, as well as on the life of a thread. For things that like to use lots threads, it can be helpful to reuse threads instead of constantly recreating them.
|
4
4
|
|
5
5
|
=== install (easy):
|
6
6
|
|
@@ -121,7 +121,7 @@ Which results in:
|
|
121
121
|
Thread: #<Thread:0x86c1080>
|
122
122
|
Thread pool woke me up: Wakeup main thread
|
123
123
|
|
124
|
-
Our pool starts with a single thread that is occupied by the sleeping task waiting to raise an exception. As we begin to add new tasks, the pool grows to
|
124
|
+
Our pool starts with a single thread that is occupied by the sleeping task waiting to raise an exception. As we begin to add new tasks, the pool grows to accommodate the growing number of tasks, until it reaches the maximum threshold of 3. At that point, the pool simply processes the tasks until the task list is empty.
|
125
125
|
|
126
126
|
The pool also has the ability to limit the amount of time a thread spends working on a given task. By default, a thread will work on a given task until the task is completed, or the pool is shutdown. However, as the following example shows, it is very easy to limit this time to avoid the pool being bogged down on long running tasks:
|
127
127
|
|
@@ -158,16 +158,33 @@ If you have a number of tasks you would like to schedule at once, it is easy wit
|
|
158
158
|
lock = Mutex.new
|
159
159
|
tasks = [].fill(lambda{ lock.synchronize{ a += 1 } }, 0..19)
|
160
160
|
pool.add_jobs(tasks)
|
161
|
-
|
161
|
+
pool.shutdown
|
162
162
|
puts "Result: #{a}"
|
163
163
|
|
164
164
|
Results:
|
165
165
|
|
166
166
|
Result: 20
|
167
167
|
|
168
|
-
|
168
|
+
Passing arguments to tasks is now available as well:
|
169
169
|
|
170
|
-
|
170
|
+
require 'actionpool'
|
171
|
+
|
172
|
+
pool = ActionPool::Pool.new
|
173
|
+
string = 'Hello world'
|
174
|
+
puts "Original: #{string}. ID: #{string.object_id}"
|
175
|
+
pool << [lambda{|var| puts "Passed: #{var}. ID: #{var.object_id}"}, [string.dup]]
|
176
|
+
pool << [lambda{|a,b| puts "Passed: #{a} | #{b}. ID: #{a.object_id} | #{b.object_id}"}, [string, string.dup]]
|
177
|
+
pool.shutdown
|
178
|
+
|
179
|
+
Results:
|
180
|
+
|
181
|
+
Original: Hello world. ID: 70651630
|
182
|
+
Passed: Hello world. ID: 70651250
|
183
|
+
Passed: Hello world | Hello world. ID: 70651630 | 70651100
|
184
|
+
|
185
|
+
== Last remarks
|
186
|
+
|
187
|
+
If you find any bugs, please report them through {github}[http://github.com/spox/actionpool/issues]. If you are in need of any help, you can generally find me on DALnet and Freenode.
|
171
188
|
|
172
189
|
== License
|
173
190
|
|
data/actionpool.gemspec
CHANGED
@@ -2,12 +2,12 @@ spec = Gem::Specification.new do |s|
|
|
2
2
|
s.name = 'ActionPool'
|
3
3
|
s.author = %q(spox)
|
4
4
|
s.email = %q(spox@modspox.com)
|
5
|
-
s.version = '0.
|
5
|
+
s.version = '0.2.0'
|
6
6
|
s.summary = %q(Thread Pool)
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.files = Dir['**/*']
|
9
9
|
s.rdoc_options = %w(--title ActionPool --main README.rdoc --line-numbers)
|
10
|
-
s.extra_rdoc_files = %w(README.rdoc)
|
10
|
+
s.extra_rdoc_files = %w(README.rdoc CHANGELOG)
|
11
11
|
s.require_paths = %w(lib)
|
12
12
|
s.required_ruby_version = '>= 1.8.6'
|
13
13
|
s.homepage = %q(http://github.com/spox/actionpool)
|
@@ -20,4 +20,4 @@ spec = Gem::Specification.new do |s|
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
s.description = description[1..-1].join(" ")
|
23
|
-
end
|
23
|
+
end
|
data/lib/actionpool.rb
CHANGED
data/lib/actionpool/LogHelper.rb
CHANGED
data/lib/actionpool/Pool.rb
CHANGED
@@ -17,50 +17,69 @@ module ActionPool
|
|
17
17
|
@logger = LogHelper.new(args[:logger])
|
18
18
|
@queue = ActionPool::Queue.new
|
19
19
|
@threads = []
|
20
|
-
@
|
21
|
-
@
|
20
|
+
@lock = Mutex.new
|
21
|
+
@thread_timeout = args[:t_to] ? args[:t_to] : 0
|
22
|
+
@action_timeout = args[:a_to] ? args[:a_to] : 0
|
22
23
|
@min_threads = args[:min_threads] ? args[:min_threads] : 10
|
23
24
|
@max_threads = args[:max_threads] ? args[:max_threads] : 100
|
24
25
|
@respond_to = ::Thread.current
|
25
|
-
|
26
|
+
create_thread
|
26
27
|
end
|
27
28
|
|
28
29
|
# force:: force creation of a new thread
|
29
30
|
# Create a new thread for pool. Returns newly created ActionPool::Thread or
|
30
31
|
# nil if pool has reached maximum threads
|
31
32
|
def create_thread(force=false)
|
32
|
-
|
33
|
-
@
|
34
|
-
|
35
|
-
|
33
|
+
pt = nil
|
34
|
+
@lock.synchronize do
|
35
|
+
if(@threads.size < @max_threads || force)
|
36
|
+
@logger.info('Pool is creating a new thread')
|
37
|
+
(min - size > 0 ? min - size : 1).times do |i|
|
38
|
+
pt = ActionPool::Thread.new(:pool => self, :respond_thread => @respond_to, :a_timeout => @action_timeout, :t_timeout => @thread_timeout, :logger => @logger)
|
39
|
+
@threads << pt
|
40
|
+
end
|
41
|
+
else
|
42
|
+
@logger.info('Pool is at maximum size. Not creating new thread')
|
43
|
+
end
|
44
|
+
end
|
36
45
|
return pt
|
37
46
|
end
|
38
47
|
|
39
48
|
# force:: force immediate stop
|
40
49
|
# Stop the pool
|
41
50
|
def shutdown(force=false)
|
51
|
+
args = [:wait]
|
52
|
+
args += [:force] if force
|
42
53
|
@logger.info("Pool is now shutting down #{force ? 'using force' : ''}")
|
43
|
-
@
|
44
|
-
|
45
|
-
|
46
|
-
sleep(0.1)
|
54
|
+
@queue.wait_empty
|
55
|
+
while(t = @threads.pop) do
|
56
|
+
t.stop(*args)
|
47
57
|
end
|
48
58
|
nil
|
49
59
|
end
|
50
60
|
|
51
|
-
# action:: proc to be executed
|
61
|
+
# action:: proc to be executed or array of [proc, [*args]]
|
52
62
|
# Add a new proc/lambda to be executed (alias for queue)
|
53
63
|
def <<(action)
|
54
|
-
|
64
|
+
case action
|
65
|
+
when Proc
|
66
|
+
queue(action)
|
67
|
+
when Array
|
68
|
+
raise ArgumentError.new('Actions to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]') unless action.size == 2 and action[0].is_a?(Proc) and action[1].is_a?(Array)
|
69
|
+
queue(*action.flatten)
|
70
|
+
else
|
71
|
+
raise ArgumentError.new('Actions to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]')
|
72
|
+
end
|
55
73
|
nil
|
56
74
|
end
|
57
75
|
|
58
76
|
# action:: proc to be executed
|
59
77
|
# Add a new proc/lambda to be executed
|
60
|
-
def queue(action)
|
78
|
+
def queue(action, *args)
|
61
79
|
raise ArgumentError.new('Expecting block') unless action.is_a?(Proc)
|
62
|
-
@queue << action
|
63
|
-
|
80
|
+
@queue << [action, args]
|
81
|
+
::Thread.pass
|
82
|
+
create_thread if @queue.num_waiting < 1 # only start a new thread if we need it
|
64
83
|
end
|
65
84
|
|
66
85
|
# jobs:: Array of proc/lambdas
|
@@ -70,11 +89,18 @@ module ActionPool
|
|
70
89
|
@queue.pause
|
71
90
|
begin
|
72
91
|
jobs.each do |job|
|
73
|
-
|
74
|
-
|
92
|
+
case job
|
93
|
+
when Proc
|
94
|
+
@queue << [job, []]
|
95
|
+
when Array
|
96
|
+
raise ArgumentError.new('Jobs to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]') unless job.size == 2 and job[0].is_a?(Proc) and job[1].is_a?(Array)
|
97
|
+
@queue << job
|
98
|
+
else
|
99
|
+
raise ArgumentError.new('Jobs to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]')
|
100
|
+
end
|
75
101
|
end
|
76
102
|
ensure
|
77
|
-
|
103
|
+
create_thread
|
78
104
|
@queue.unpause
|
79
105
|
end
|
80
106
|
true
|
@@ -82,8 +108,8 @@ module ActionPool
|
|
82
108
|
|
83
109
|
# block:: block to process
|
84
110
|
# Adds a block to be processed
|
85
|
-
def process(&block)
|
86
|
-
queue(block)
|
111
|
+
def process(*args, &block)
|
112
|
+
queue(block, *args)
|
87
113
|
nil
|
88
114
|
end
|
89
115
|
|
@@ -108,6 +134,7 @@ module ActionPool
|
|
108
134
|
m = m.to_i
|
109
135
|
raise ArgumentError.new('Maximum value must be greater than 0') unless m > 0
|
110
136
|
@max_threads = m
|
137
|
+
resize if m < size
|
111
138
|
m
|
112
139
|
end
|
113
140
|
|
@@ -117,13 +144,14 @@ module ActionPool
|
|
117
144
|
m = m.to_i
|
118
145
|
raise ArgumentError.new("Minimum value must be greater than 0 and less than or equal to maximum (#{max})") unless m > 0 && m <= max
|
119
146
|
@min_threads = m
|
120
|
-
resize if m < size
|
121
147
|
m
|
122
148
|
end
|
123
149
|
|
124
150
|
# t:: ActionPool::Thread to remove
|
125
151
|
# Removes a thread from the pool
|
126
152
|
def remove(t)
|
153
|
+
raise ArgumentError.new('Expecting an ActionPool::Thread object') unless t.is_a?(ActionPool::Thread)
|
154
|
+
t.stop
|
127
155
|
if(@threads.include?(t))
|
128
156
|
@threads.delete(t)
|
129
157
|
return true
|
@@ -150,9 +178,10 @@ module ActionPool
|
|
150
178
|
# t:: timeout in seconds (nil for infinite)
|
151
179
|
# Set maximum allowed time thead may idle in pool
|
152
180
|
def thread_timeout=(t)
|
153
|
-
t =
|
154
|
-
raise ArgumentError.new('Value must be great than zero or nil') unless t
|
181
|
+
t = t.to_f
|
182
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
155
183
|
@thread_timeout = t
|
184
|
+
@threads.each{|thread|thread.thread_timeout = t}
|
156
185
|
t
|
157
186
|
end
|
158
187
|
|
@@ -160,9 +189,10 @@ module ActionPool
|
|
160
189
|
# Set maximum allowed time thread may work
|
161
190
|
# on a given action
|
162
191
|
def action_timeout=(t)
|
163
|
-
t =
|
164
|
-
raise ArgumentError.new('Value must be great than zero or nil') unless t
|
192
|
+
t = t.to_f
|
193
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
165
194
|
@action_timeout = t
|
195
|
+
@threads.each{|thread|thread.action_timeout = t}
|
166
196
|
t
|
167
197
|
end
|
168
198
|
|
@@ -176,14 +206,30 @@ module ActionPool
|
|
176
206
|
@queue.size
|
177
207
|
end
|
178
208
|
|
209
|
+
# Flush the thread pool. Mainly used for forcibly resizing
|
210
|
+
# the pool if existing threads have a long thread life waiting
|
211
|
+
# for input.
|
212
|
+
def flush
|
213
|
+
lock = Mutex.new
|
214
|
+
guard = ConditionVariable.new
|
215
|
+
@threads.size.times{ queue{ lock.synchronize{ guard.wait(lock) } } }
|
216
|
+
Thread.pass
|
217
|
+
sleep(0.01)
|
218
|
+
lock.synchronize{ guard.broadcast }
|
219
|
+
end
|
220
|
+
|
179
221
|
private
|
180
|
-
|
222
|
+
|
223
|
+
# Resize the pool
|
181
224
|
def resize
|
182
|
-
@logger.info("Pool is being resized to stated
|
183
|
-
size
|
184
|
-
t =
|
225
|
+
@logger.info("Pool is being resized to stated maximum: #{max}")
|
226
|
+
until(size <= max) do
|
227
|
+
t = nil
|
228
|
+
t = @threads.find{|t|t.waiting?}
|
229
|
+
t = @threads.shift unless t
|
185
230
|
t.stop
|
186
231
|
end
|
232
|
+
flush
|
187
233
|
nil
|
188
234
|
end
|
189
235
|
end
|
data/lib/actionpool/Queue.rb
CHANGED
@@ -9,6 +9,8 @@ module ActionPool
|
|
9
9
|
@wait = false
|
10
10
|
@lock = Mutex.new
|
11
11
|
@guard = ConditionVariable.new
|
12
|
+
@elock = Mutex.new
|
13
|
+
@eguard = ConditionVariable.new
|
12
14
|
end
|
13
15
|
# Stop the queue from returning results to requesting
|
14
16
|
# threads. Threads will wait for results until signalled
|
@@ -19,12 +21,18 @@ module ActionPool
|
|
19
21
|
# will have results given to them.
|
20
22
|
def unpause
|
21
23
|
@wait = false
|
22
|
-
@lock.synchronize{ @guard.
|
24
|
+
@lock.synchronize{ @guard.broadcast }
|
23
25
|
end
|
24
26
|
# Check if queue needs to wait before returning
|
25
27
|
def pop
|
26
28
|
@lock.synchronize{ @guard.wait(@lock) } if @wait
|
27
|
-
super
|
29
|
+
o = super
|
30
|
+
@elock.synchronize{ @eguard.broadcast } if empty?
|
31
|
+
return o
|
32
|
+
end
|
33
|
+
# Park a thread here until queue is empty
|
34
|
+
def wait_empty
|
35
|
+
@elock.synchronize{ @eguard.wait(@elock) } if size > 0
|
28
36
|
end
|
29
37
|
end
|
30
38
|
end
|
data/lib/actionpool/Thread.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'timeout'
|
2
2
|
|
3
3
|
module ActionPool
|
4
|
+
|
4
5
|
class Thread
|
5
6
|
# :pool:: pool thread is associated with
|
6
7
|
# :t_timeout:: max time a thread is allowed to wait for action
|
@@ -14,23 +15,74 @@ module ActionPool
|
|
14
15
|
raise ArgumentError.new('ActionPool::Thread requries thread to respond') unless args[:respond_thread]
|
15
16
|
@pool = args[:pool]
|
16
17
|
@respond_to = args[:respond_thread]
|
17
|
-
@thread_timeout = args[:t_timeout] ? args[:t_timeout] : 0
|
18
|
-
@action_timeout = args[:a_timeout] ? args[:a_timeout] : 0
|
18
|
+
@thread_timeout = args[:t_timeout] ? args[:t_timeout].to_f : 0
|
19
|
+
@action_timeout = args[:a_timeout] ? args[:a_timeout].to_f : 0
|
19
20
|
@kill = false
|
20
21
|
@logger = args[:logger].is_a?(LogHelper) ? args[:logger] : LogHelper.new(args[:logger])
|
21
22
|
@thread = ::Thread.new{ start_thread }
|
22
23
|
end
|
23
24
|
|
24
|
-
# force:: force the thread to stop
|
25
|
+
# :force:: force the thread to stop
|
26
|
+
# :wait:: wait for the thread to stop
|
25
27
|
# Stop the thread
|
26
|
-
def stop(
|
28
|
+
def stop(*args)
|
27
29
|
@kill = true
|
28
|
-
@thread.kill if force
|
30
|
+
@thread.kill if args.include?(:force) || waiting?
|
31
|
+
# killing the thread if it is waiting? is a simple race condition
|
32
|
+
# in that, if the thread has just finished a task, but hasn't
|
33
|
+
# dropped by the queue line yet, we will see it as not waiting. we
|
34
|
+
# can counter that by waiting just a little bit longer than the allowed
|
35
|
+
# time to work on an action, and kill the thread if it is still around
|
36
|
+
if(args.include?(:wait))
|
37
|
+
unless(@thread.join(@action_timeout + 0.01))
|
38
|
+
@thread.kill
|
39
|
+
@thread.join
|
40
|
+
end
|
41
|
+
end
|
29
42
|
nil
|
30
43
|
end
|
31
44
|
|
45
|
+
# Is the thread still alive
|
46
|
+
def alive?
|
47
|
+
@thread.alive?
|
48
|
+
end
|
49
|
+
|
50
|
+
# Is the thread currently waiting for input
|
51
|
+
def waiting?
|
52
|
+
@thread.status == 'sleep'
|
53
|
+
end
|
54
|
+
|
55
|
+
# Seconds thread will wait for input
|
56
|
+
def thread_timeout
|
57
|
+
@thread_timeout
|
58
|
+
end
|
59
|
+
|
60
|
+
# Seconds thread will spend working on a given task
|
61
|
+
def action_timeout
|
62
|
+
@action_timeout
|
63
|
+
end
|
64
|
+
|
65
|
+
# t:: seconds to wait for input (floats allow for values 0 < t < 1)
|
66
|
+
# Set the maximum amount of time to wait for a task
|
67
|
+
def thread_timeout=(t)
|
68
|
+
t = t.to_f
|
69
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
70
|
+
@thread_timeout = t
|
71
|
+
t
|
72
|
+
end
|
73
|
+
|
74
|
+
# t:: seconds to work on a task (floats allow for values 0 < t < 1)
|
75
|
+
# Set the maximum amount of time to work on a given task
|
76
|
+
def action_timeout=(t)
|
77
|
+
t = t.to_f
|
78
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
79
|
+
@action_timeout = t
|
80
|
+
t
|
81
|
+
end
|
82
|
+
|
32
83
|
private
|
33
84
|
|
85
|
+
# Start our thread
|
34
86
|
def start_thread
|
35
87
|
begin
|
36
88
|
@logger.info("New pool thread is starting (#{self})")
|
@@ -44,7 +96,7 @@ module ActionPool
|
|
44
96
|
else
|
45
97
|
action = @pool.action
|
46
98
|
end
|
47
|
-
run(action) unless action.nil?
|
99
|
+
run(*action) unless action.nil?
|
48
100
|
rescue Timeout::Error => boom
|
49
101
|
@kill = true
|
50
102
|
rescue Exception => boom
|
@@ -61,14 +113,17 @@ module ActionPool
|
|
61
113
|
end
|
62
114
|
end
|
63
115
|
|
64
|
-
|
116
|
+
# action:: task to be run
|
117
|
+
# args:: arguments to be passed to task
|
118
|
+
# Run the task
|
119
|
+
def run(action, args)
|
65
120
|
begin
|
66
121
|
if(@action_timeout > 0)
|
67
122
|
Timeout::timeout(@action_timeout) do
|
68
|
-
action.call
|
123
|
+
action.call(*args)
|
69
124
|
end
|
70
125
|
else
|
71
|
-
action.call
|
126
|
+
action.call(*args)
|
72
127
|
end
|
73
128
|
rescue Timeout::Error => boom
|
74
129
|
@logger.warn("Pool thread reached max execution time for action: #{boom}")
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'actionpool'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class GeneralPoolTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@pool = ActionPool::Pool.new
|
7
|
+
end
|
8
|
+
def test_numbers
|
9
|
+
assert_equal(10, @pool.size)
|
10
|
+
assert_equal(10, @pool.min)
|
11
|
+
assert_equal(100, @pool.max)
|
12
|
+
assert_equal(0, @pool.action_timeout)
|
13
|
+
assert_equal(0, @pool.thread_timeout)
|
14
|
+
assert_equal(0, @pool.action_size)
|
15
|
+
end
|
16
|
+
def test_output
|
17
|
+
a = 0
|
18
|
+
lock = Mutex.new
|
19
|
+
run = lambda{ lock.synchronize{ a += 1 } }
|
20
|
+
100.times{ @pool << run }
|
21
|
+
@pool.shutdown
|
22
|
+
assert_equal(100, a)
|
23
|
+
a = 0
|
24
|
+
jobs = [].fill(run,0,100)
|
25
|
+
@pool.add_jobs(jobs)
|
26
|
+
@pool.shutdown
|
27
|
+
assert_equal(100, a)
|
28
|
+
@pool.shutdown(true)
|
29
|
+
end
|
30
|
+
def test_args
|
31
|
+
output = nil
|
32
|
+
@pool << [lambda{|x| output = x}, [2]]
|
33
|
+
assert(2, output)
|
34
|
+
@pool.add_jobs([[lambda{|x| output = x}, [3]]])
|
35
|
+
assert(3, output)
|
36
|
+
output = []
|
37
|
+
@pool.add_jobs([[lambda{|x,y| output << x + y}, [1,1]], [lambda{|x| output << x}, [3]]])
|
38
|
+
assert(output.include?(2))
|
39
|
+
assert(output.include?(3))
|
40
|
+
@pool.shutdown(true)
|
41
|
+
end
|
42
|
+
end
|
data/tests/cases/grow.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'actionpool'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class GrowPoolTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@pool = ActionPool::Pool.new
|
7
|
+
end
|
8
|
+
def test_grow
|
9
|
+
jobs = [].fill(lambda{}, 0..99)
|
10
|
+
@pool.add_jobs(jobs)
|
11
|
+
assert(@pool.size > 10)
|
12
|
+
@pool.shutdown(true)
|
13
|
+
end
|
14
|
+
def test_max
|
15
|
+
@pool.create_thread(true) until @pool.size > @pool.max
|
16
|
+
assert_nil(@pool.create_thread)
|
17
|
+
assert(@pool.create_thread(true))
|
18
|
+
@pool.shutdown(true)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'actionpool'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class QueueTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@queue = ActionPool::Queue.new
|
7
|
+
end
|
8
|
+
def test_pop
|
9
|
+
3.times{|i|@queue << i}
|
10
|
+
3.times{|i|assert(i, @queue.pop)}
|
11
|
+
assert(@queue.empty?)
|
12
|
+
end
|
13
|
+
def test_pause
|
14
|
+
3.times{|i|@queue << i}
|
15
|
+
@queue.pause
|
16
|
+
output = []
|
17
|
+
3.times{Thread.new{output << @queue.pop}}
|
18
|
+
assert(output.empty?)
|
19
|
+
assert_equal(3, @queue.size)
|
20
|
+
@queue.unpause
|
21
|
+
sleep(1)
|
22
|
+
assert(@queue.empty?)
|
23
|
+
assert_equal(3, output.size)
|
24
|
+
3.times{|i|assert(output.include?(i))}
|
25
|
+
@queue << 1
|
26
|
+
output = nil
|
27
|
+
Thread.new{@queue.wait_empty; output = true}
|
28
|
+
assert_nil(output)
|
29
|
+
@queue.pop
|
30
|
+
Thread.pass
|
31
|
+
sleep(0.01)
|
32
|
+
assert(output)
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'actionpool'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class ResizePoolTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@pool = ActionPool::Pool.new
|
7
|
+
end
|
8
|
+
def test_resize
|
9
|
+
stop = false
|
10
|
+
20.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
|
11
|
+
assert(@pool.size > 10)
|
12
|
+
stop = true
|
13
|
+
@pool.shutdown(true)
|
14
|
+
@pool.max = 10
|
15
|
+
assert_equal(10, @pool.max)
|
16
|
+
assert_equal(0, @pool.size)
|
17
|
+
stop = false
|
18
|
+
20.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
|
19
|
+
assert_equal(10, @pool.size)
|
20
|
+
stop = true
|
21
|
+
@pool.shutdown(true)
|
22
|
+
@pool.max = 20
|
23
|
+
stop = false
|
24
|
+
30.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
|
25
|
+
stop = true
|
26
|
+
assert(@pool.size > 10)
|
27
|
+
@pool.shutdown(true)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'actionpool'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class ShutdownPoolTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@pool = ActionPool::Pool.new
|
7
|
+
end
|
8
|
+
def test_shutdown
|
9
|
+
assert_equal(10, @pool.size)
|
10
|
+
@pool.shutdown
|
11
|
+
sleep(0.5)
|
12
|
+
assert_equal(0, @pool.size)
|
13
|
+
@pool << lambda{}
|
14
|
+
assert_equal(10, @pool.size)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'actionpool'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class ThreadTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@pool = ActionPool::Pool.new(:min_threads => 1, :max_threads => 1)
|
7
|
+
@thread = ActionPool::Thread.new(:pool => @pool, :respond_thread => self, :t_timeout => 60, :a_timeout => 0)
|
8
|
+
end
|
9
|
+
def test_thread
|
10
|
+
sleep(0.01)
|
11
|
+
assert(@thread.waiting?)
|
12
|
+
assert_equal(60, @thread.thread_timeout)
|
13
|
+
assert_equal(0, @thread.action_timeout)
|
14
|
+
assert(@thread.alive?)
|
15
|
+
stop = false
|
16
|
+
10.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
|
17
|
+
assert(!@thread.waiting?)
|
18
|
+
@thread.stop(:force)
|
19
|
+
sleep(0.01)
|
20
|
+
assert(!@thread.alive?)
|
21
|
+
stop = true
|
22
|
+
@pool.shutdown(true)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'actionpool'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class TimeoutPoolTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@pool = ActionPool::Pool.new
|
7
|
+
end
|
8
|
+
def test_threadtimeout
|
9
|
+
@pool.thread_timeout = 0.01
|
10
|
+
assert_equal(10, @pool.size)
|
11
|
+
stop = false
|
12
|
+
20.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
|
13
|
+
assert(@pool.size > 10)
|
14
|
+
stop = true
|
15
|
+
sleep(2)
|
16
|
+
assert(@pool.size <= 10)
|
17
|
+
@pool.shutdown(true)
|
18
|
+
end
|
19
|
+
end
|
data/tests/run_tests.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ActionPool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- spox
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-19 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -21,20 +21,32 @@ extensions: []
|
|
21
21
|
|
22
22
|
extra_rdoc_files:
|
23
23
|
- README.rdoc
|
24
|
+
- CHANGELOG
|
24
25
|
files:
|
25
|
-
- ActionPool-0.0.1.gem
|
26
|
-
- README.rdoc
|
27
26
|
- lib
|
28
27
|
- lib/actionpool
|
29
28
|
- lib/actionpool/LogHelper.rb
|
30
|
-
- lib/actionpool/Thread.rb
|
31
29
|
- lib/actionpool/Pool.rb
|
30
|
+
- lib/actionpool/Thread.rb
|
32
31
|
- lib/actionpool/Queue.rb
|
33
32
|
- lib/actionpool.rb
|
33
|
+
- ActionPool-0.2.0.gem
|
34
|
+
- tests
|
35
|
+
- tests/cases
|
36
|
+
- tests/cases/shutdown.rb
|
37
|
+
- tests/cases/nogrow.rb
|
38
|
+
- tests/cases/general.rb
|
39
|
+
- tests/cases/thread.rb
|
40
|
+
- tests/cases/grow.rb
|
41
|
+
- tests/cases/timeouts.rb
|
42
|
+
- tests/cases/resize.rb
|
43
|
+
- tests/cases/queue.rb
|
44
|
+
- tests/run_tests.rb
|
45
|
+
- CHANGELOG
|
34
46
|
- ActionPool-0.1.0.gem
|
35
47
|
- LICENSE
|
36
|
-
-
|
37
|
-
-
|
48
|
+
- README.rdoc
|
49
|
+
- ActionPool-0.0.1.gem
|
38
50
|
- actionpool.gemspec
|
39
51
|
has_rdoc: false
|
40
52
|
homepage: http://github.com/spox/actionpool
|
data/test.rb
DELETED
data/test2.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'actionpool'
|
2
|
-
|
3
|
-
pool = ActionPool::Pool.new(:min_threads => 1, :max_threads => 3)
|
4
|
-
pool.process do
|
5
|
-
sleep(10)
|
6
|
-
raise 'Wakeup main thread'
|
7
|
-
end
|
8
|
-
20.times do
|
9
|
-
pool.process do
|
10
|
-
puts "Thread: #{Thread.current}"
|
11
|
-
sleep(rand(0.0))
|
12
|
-
end
|
13
|
-
end
|
14
|
-
begin
|
15
|
-
sleep
|
16
|
-
rescue Exception => e
|
17
|
-
puts "Thread pool woke me up: #{e}"
|
18
|
-
end
|