ActionPool 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/actionpool.gemspec +2 -10
- data/lib/actionpool/Pool.rb +67 -23
- data/lib/actionpool/Queue.rb +25 -10
- data/lib/actionpool/Thread.rb +43 -21
- data/tests/cases/general.rb +10 -0
- data/tests/cases/grow.rb +8 -4
- data/tests/cases/nogrow.rb +3 -0
- data/tests/cases/resize.rb +8 -2
- data/tests/cases/shutdown.rb +21 -1
- data/tests/cases/thread.rb +3 -0
- data/tests/cases/timeouts.rb +24 -6
- metadata +8 -10
data/CHANGELOG
CHANGED
data/actionpool.gemspec
CHANGED
@@ -2,7 +2,7 @@ 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.2.
|
5
|
+
s.version = '0.2.2'
|
6
6
|
s.summary = %q(Thread Pool)
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.files = Dir['**/*']
|
@@ -11,13 +11,5 @@ spec = Gem::Specification.new do |s|
|
|
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)
|
14
|
-
description =
|
15
|
-
File.open("README.rdoc") do |file|
|
16
|
-
file.each do |line|
|
17
|
-
line.chomp!
|
18
|
-
break if line.empty?
|
19
|
-
description << "#{line.gsub(/\[\d\]/, '')}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
s.description = description[1..-1].join(" ")
|
14
|
+
s.description = "The ActionPool is an easy to use thread pool for ruby."
|
23
15
|
end
|
data/lib/actionpool/Pool.rb
CHANGED
@@ -4,6 +4,9 @@ require 'actionpool/LogHelper'
|
|
4
4
|
require 'thread'
|
5
5
|
|
6
6
|
module ActionPool
|
7
|
+
# Raised when pool is closed
|
8
|
+
class PoolClosed < StandardError
|
9
|
+
end
|
7
10
|
class Pool
|
8
11
|
|
9
12
|
# :min_threads:: minimum number of threads in pool
|
@@ -23,35 +26,68 @@ module ActionPool
|
|
23
26
|
@max_threads = args[:max_threads] ? args[:max_threads] : 100
|
24
27
|
@min_threads = args[:min_threads] ? args[:min_threads] : 10
|
25
28
|
@min_threads = @max_threads if @max_threads < @min_threads
|
26
|
-
@respond_to = ::Thread.current
|
27
|
-
|
29
|
+
@respond_to = args[:respond_thread] || ::Thread.current
|
30
|
+
@open = true
|
31
|
+
fill_pool
|
28
32
|
end
|
29
33
|
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
# Pool is closed
|
35
|
+
def pool_closed?
|
36
|
+
!@open
|
37
|
+
end
|
38
|
+
|
39
|
+
# Pool is open
|
40
|
+
def pool_open?
|
41
|
+
@open
|
42
|
+
end
|
43
|
+
|
44
|
+
# arg:: :open or :closed
|
45
|
+
# Set pool status
|
46
|
+
def status(arg)
|
47
|
+
@open = arg == :open
|
48
|
+
fill_pool if @open
|
49
|
+
end
|
50
|
+
|
51
|
+
# args:: :force forces a new thread. :nowait will create a thread if threads are waiting
|
52
|
+
# Create a new thread for pool.
|
53
|
+
# Returns newly created thread of nil if pool is at maximum size
|
54
|
+
def create_thread(*args)
|
55
|
+
return if pool_closed?
|
56
|
+
thread = nil
|
35
57
|
@lock.synchronize do
|
36
|
-
if(@threads.size < @max_threads || force)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
58
|
+
if(((size == working || args.include?(:nowait)) && @threads.size < @max_threads) || args.include?(:force))
|
59
|
+
thread = ActionPool::Thread.new(:pool => self, :respond_thread => @respond_to, :a_timeout => @action_timeout, :t_timeout => @thread_timeout, :logger => @logger)
|
60
|
+
@threads << thread
|
61
|
+
end
|
62
|
+
end
|
63
|
+
return thread
|
64
|
+
end
|
65
|
+
|
66
|
+
# Fills the pool with the minimum number of threads
|
67
|
+
# Returns array of created threads
|
68
|
+
def fill_pool
|
69
|
+
threads = []
|
70
|
+
@lock.synchronize do
|
71
|
+
required = min - size
|
72
|
+
if(required > 0)
|
73
|
+
required.times do
|
74
|
+
thread = ActionPool::Thread.new(:pool => self, :respond_thread => @respond_to, :a_timeout => @action_timeout, :t_timeout => @thread_timeout, :logger => @logger)
|
75
|
+
@threads << thread
|
76
|
+
threads << thread
|
41
77
|
end
|
42
|
-
else
|
43
|
-
@logger.info('Pool is at maximum size. Not creating new thread')
|
44
78
|
end
|
45
79
|
end
|
46
|
-
return
|
80
|
+
return threads
|
47
81
|
end
|
48
82
|
|
49
83
|
# force:: force immediate stop
|
50
84
|
# Stop the pool
|
51
85
|
def shutdown(force=false)
|
52
|
-
|
53
|
-
args
|
86
|
+
status(:closed)
|
87
|
+
args = []
|
88
|
+
args.push(:force) if force
|
54
89
|
@logger.info("Pool is now shutting down #{force ? 'using force' : ''}")
|
90
|
+
@queue.clear if force
|
55
91
|
@queue.wait_empty
|
56
92
|
while(t = @threads.pop) do
|
57
93
|
t.stop(*args)
|
@@ -67,7 +103,7 @@ module ActionPool
|
|
67
103
|
queue(action)
|
68
104
|
when Array
|
69
105
|
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)
|
70
|
-
queue(
|
106
|
+
queue(action[0], action[1])
|
71
107
|
else
|
72
108
|
raise ArgumentError.new('Actions to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]')
|
73
109
|
end
|
@@ -77,15 +113,17 @@ module ActionPool
|
|
77
113
|
# action:: proc to be executed
|
78
114
|
# Add a new proc/lambda to be executed
|
79
115
|
def queue(action, *args)
|
116
|
+
raise PoolClosed.new("Pool #{self} is currently closed") if pool_closed?
|
80
117
|
raise ArgumentError.new('Expecting block') unless action.is_a?(Proc)
|
81
118
|
@queue << [action, args]
|
82
119
|
::Thread.pass
|
83
|
-
create_thread
|
120
|
+
create_thread
|
84
121
|
end
|
85
122
|
|
86
123
|
# jobs:: Array of proc/lambdas
|
87
124
|
# Will queue a list of jobs into the pool
|
88
125
|
def add_jobs(jobs)
|
126
|
+
raise PoolClosed.new("Pool #{self} is currently closed") if pool_closed?
|
89
127
|
raise ArgumentError.new("Expecting an array but received: #{jobs.class}") unless jobs.is_a?(Array)
|
90
128
|
@queue.pause
|
91
129
|
begin
|
@@ -95,13 +133,14 @@ module ActionPool
|
|
95
133
|
@queue << [job, []]
|
96
134
|
when Array
|
97
135
|
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)
|
98
|
-
@queue << job
|
136
|
+
@queue << [job.shift, job]
|
99
137
|
else
|
100
138
|
raise ArgumentError.new('Jobs to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]')
|
101
139
|
end
|
102
140
|
end
|
103
141
|
ensure
|
104
|
-
|
142
|
+
num = jobs.size - @threads.select{|t|t.waiting?}.size
|
143
|
+
num.times{ create_thread(:nowait) } if num > 0
|
105
144
|
@queue.unpause
|
106
145
|
end
|
107
146
|
true
|
@@ -181,7 +220,7 @@ module ActionPool
|
|
181
220
|
# Set maximum allowed time thead may idle in pool
|
182
221
|
def thread_timeout=(t)
|
183
222
|
t = t.to_f
|
184
|
-
raise ArgumentError.new('Value must be
|
223
|
+
raise ArgumentError.new('Value must be greater than zero or nil') unless t >= 0
|
185
224
|
@thread_timeout = t
|
186
225
|
@threads.each{|thread|thread.thread_timeout = t}
|
187
226
|
t
|
@@ -192,7 +231,7 @@ module ActionPool
|
|
192
231
|
# on a given action
|
193
232
|
def action_timeout=(t)
|
194
233
|
t = t.to_f
|
195
|
-
raise ArgumentError.new('Value must be
|
234
|
+
raise ArgumentError.new('Value must be greater than zero or nil') unless t >= 0
|
196
235
|
@action_timeout = t
|
197
236
|
@threads.each{|thread|thread.action_timeout = t}
|
198
237
|
t
|
@@ -220,6 +259,11 @@ module ActionPool
|
|
220
259
|
lock.synchronize{ guard.broadcast }
|
221
260
|
end
|
222
261
|
|
262
|
+
# Returns current number of threads in the pool working
|
263
|
+
def working
|
264
|
+
@threads.find_all{|t|!t.waiting?}.size
|
265
|
+
end
|
266
|
+
|
223
267
|
private
|
224
268
|
|
225
269
|
# Resize the pool
|
data/lib/actionpool/Queue.rb
CHANGED
@@ -7,32 +7,47 @@ module ActionPool
|
|
7
7
|
def initialize
|
8
8
|
super
|
9
9
|
@wait = false
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
10
|
+
@pause_lock = Mutex.new
|
11
|
+
@empty_lock = Mutex.new
|
12
|
+
@pause_guard = ConditionVariable.new
|
13
|
+
@empty_guard = ConditionVariable.new
|
14
14
|
end
|
15
15
|
# Stop the queue from returning results to requesting
|
16
16
|
# threads. Threads will wait for results until signalled
|
17
17
|
def pause
|
18
|
-
@wait = true
|
18
|
+
@pause_lock.synchronize{@wait = true}
|
19
19
|
end
|
20
20
|
# Allow the queue to return results. Any threads waiting
|
21
21
|
# will have results given to them.
|
22
22
|
def unpause
|
23
|
-
@
|
24
|
-
|
23
|
+
@pause_lock.synchronize do
|
24
|
+
@wait = false
|
25
|
+
@pause_guard.broadcast
|
26
|
+
end
|
25
27
|
end
|
26
28
|
# Check if queue needs to wait before returning
|
27
29
|
def pop
|
28
|
-
@
|
30
|
+
@pause_lock.synchronize do
|
31
|
+
@pause_guard.wait(@pause_lock) if @wait
|
32
|
+
end
|
29
33
|
o = super
|
30
|
-
@
|
34
|
+
@empty_lock.synchronize do
|
35
|
+
@empty_guard.broadcast if empty?
|
36
|
+
end
|
31
37
|
return o
|
32
38
|
end
|
39
|
+
# Clear queue
|
40
|
+
def clear
|
41
|
+
super
|
42
|
+
@empty_lock.synchronize do
|
43
|
+
@empty_guard.broadcast
|
44
|
+
end
|
45
|
+
end
|
33
46
|
# Park a thread here until queue is empty
|
34
47
|
def wait_empty
|
35
|
-
@
|
48
|
+
@empty_lock.synchronize do
|
49
|
+
@empty_guard.wait(@empty_lock) if size > 0
|
50
|
+
end
|
36
51
|
end
|
37
52
|
end
|
38
53
|
end
|
data/lib/actionpool/Thread.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'timeout'
|
2
2
|
|
3
3
|
module ActionPool
|
4
|
-
|
4
|
+
# Exception class used for waking up a thread
|
5
|
+
class Wakeup < StandardError
|
6
|
+
end
|
7
|
+
# Raised within a thread when the timeout is changed
|
8
|
+
class Retimeout < StandardError
|
9
|
+
end
|
5
10
|
class Thread
|
6
11
|
# :pool:: pool thread is associated with
|
7
12
|
# :t_timeout:: max time a thread is allowed to wait for action
|
@@ -19,6 +24,7 @@ module ActionPool
|
|
19
24
|
@action_timeout = args[:a_timeout] ? args[:a_timeout].to_f : 0
|
20
25
|
@kill = false
|
21
26
|
@logger = args[:logger].is_a?(LogHelper) ? args[:logger] : LogHelper.new(args[:logger])
|
27
|
+
@lock = Mutex.new
|
22
28
|
@thread = ::Thread.new{ start_thread }
|
23
29
|
end
|
24
30
|
|
@@ -27,29 +33,30 @@ module ActionPool
|
|
27
33
|
# Stop the thread
|
28
34
|
def stop(*args)
|
29
35
|
@kill = true
|
30
|
-
@thread.
|
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
|
36
|
+
@thread.raise Wakeup.new if args.include?(:force) || waiting?
|
42
37
|
nil
|
43
38
|
end
|
44
39
|
|
40
|
+
# Currently waiting
|
41
|
+
def waiting?
|
42
|
+
@lock.synchronize{@status} == :wait
|
43
|
+
end
|
44
|
+
|
45
45
|
# Is the thread still alive
|
46
46
|
def alive?
|
47
47
|
@thread.alive?
|
48
48
|
end
|
49
49
|
|
50
|
-
#
|
51
|
-
def
|
52
|
-
@
|
50
|
+
# Current thread status
|
51
|
+
def status
|
52
|
+
@lock.synchronize{ return @status }
|
53
|
+
end
|
54
|
+
|
55
|
+
# arg:: :wait or :run
|
56
|
+
# Set current status
|
57
|
+
def status(arg)
|
58
|
+
raise InvalidType.new('Status can only be set to :wait or :run') unless arg == :wait || arg == :run
|
59
|
+
@lock.synchronize{ @status = arg }
|
53
60
|
end
|
54
61
|
|
55
62
|
# Seconds thread will wait for input
|
@@ -68,11 +75,13 @@ module ActionPool
|
|
68
75
|
t = t.to_f
|
69
76
|
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
70
77
|
@thread_timeout = t
|
78
|
+
@thread.raise Retimeout.new if waiting?
|
71
79
|
t
|
72
80
|
end
|
73
81
|
|
74
82
|
# t:: seconds to work on a task (floats allow for values 0 < t < 1)
|
75
83
|
# Set the maximum amount of time to work on a given task
|
84
|
+
# Note: Modification of this will not affect actions already in process
|
76
85
|
def action_timeout=(t)
|
77
86
|
t = t.to_f
|
78
87
|
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
@@ -87,29 +96,42 @@ module ActionPool
|
|
87
96
|
begin
|
88
97
|
@logger.info("New pool thread is starting (#{self})")
|
89
98
|
until(@kill) do
|
99
|
+
status(:wait)
|
90
100
|
begin
|
91
101
|
action = nil
|
92
|
-
if(@pool.size > @pool.min)
|
102
|
+
if(@pool.size > @pool.min && !@thread_timeout.zero?)
|
93
103
|
Timeout::timeout(@thread_timeout) do
|
94
104
|
action = @pool.action
|
95
105
|
end
|
96
106
|
else
|
97
107
|
action = @pool.action
|
98
108
|
end
|
99
|
-
run
|
109
|
+
status(:run)
|
110
|
+
run(action[0], action[1]) unless action.nil?
|
111
|
+
status(:wait)
|
100
112
|
rescue Timeout::Error => boom
|
101
113
|
@kill = true
|
114
|
+
rescue Wakeup
|
115
|
+
@logger.info("Thread #{::Thread.current} was woken up.")
|
116
|
+
rescue Retimeout
|
117
|
+
@logger.warn('Thread was woken up to reset thread timeout')
|
102
118
|
rescue Exception => boom
|
103
119
|
@logger.error("Pool thread caught an exception: #{boom}\n#{boom.backtrace.join("\n")}")
|
104
120
|
@respond_to.raise boom
|
105
121
|
end
|
106
122
|
end
|
123
|
+
rescue Retimeout
|
124
|
+
@logger.warn('Thread was woken up to reset thread timeout')
|
125
|
+
retry
|
126
|
+
rescue Wakeup
|
127
|
+
@logger.info("Thread #{::Thread.current} was woken up.")
|
107
128
|
rescue Exception => boom
|
108
129
|
@logger.error("Pool thread caught an exception: #{boom}\n#{boom.backtrace.join("\n")}")
|
109
130
|
@respond_to.raise boom
|
110
131
|
ensure
|
111
132
|
@logger.info("Pool thread is shutting down (#{self})")
|
112
133
|
@pool.remove(self)
|
134
|
+
@pool.create_thread
|
113
135
|
end
|
114
136
|
end
|
115
137
|
|
@@ -118,12 +140,12 @@ module ActionPool
|
|
118
140
|
# Run the task
|
119
141
|
def run(action, args)
|
120
142
|
begin
|
121
|
-
|
143
|
+
unless(@action_timeout.zero?)
|
122
144
|
Timeout::timeout(@action_timeout) do
|
123
|
-
action.call(*args)
|
145
|
+
action.call(*args[0])
|
124
146
|
end
|
125
147
|
else
|
126
|
-
action.call(*args)
|
148
|
+
action.call(*args[0])
|
127
149
|
end
|
128
150
|
rescue Timeout::Error => boom
|
129
151
|
@logger.warn("Pool thread reached max execution time for action: #{boom}")
|
data/tests/cases/general.rb
CHANGED
@@ -5,6 +5,9 @@ class GeneralPoolTest < Test::Unit::TestCase
|
|
5
5
|
def setup
|
6
6
|
@pool = ActionPool::Pool.new
|
7
7
|
end
|
8
|
+
def teardown
|
9
|
+
@pool.shutdown(true)
|
10
|
+
end
|
8
11
|
def test_numbers
|
9
12
|
assert_equal(10, @pool.size)
|
10
13
|
assert_equal(10, @pool.min)
|
@@ -20,6 +23,7 @@ class GeneralPoolTest < Test::Unit::TestCase
|
|
20
23
|
100.times{ @pool << run }
|
21
24
|
@pool.shutdown
|
22
25
|
assert_equal(100, a)
|
26
|
+
@pool.status :open
|
23
27
|
a = 0
|
24
28
|
jobs = [].fill(run,0,100)
|
25
29
|
@pool.add_jobs(jobs)
|
@@ -28,15 +32,21 @@ class GeneralPoolTest < Test::Unit::TestCase
|
|
28
32
|
@pool.shutdown(true)
|
29
33
|
end
|
30
34
|
def test_args
|
35
|
+
@pool.status :open
|
31
36
|
output = nil
|
32
37
|
@pool << [lambda{|x| output = x}, [2]]
|
33
38
|
assert(2, output)
|
34
39
|
@pool.add_jobs([[lambda{|x| output = x}, [3]]])
|
35
40
|
assert(3, output)
|
41
|
+
@pool << [lambda{|x,y| output = x+y}, [1,2]]
|
42
|
+
assert(3, output)
|
36
43
|
output = []
|
37
44
|
@pool.add_jobs([[lambda{|x,y| output << x + y}, [1,1]], [lambda{|x| output << x}, [3]]])
|
38
45
|
assert(output.include?(2))
|
39
46
|
assert(output.include?(3))
|
47
|
+
@pool << [lambda{|x,y| output = [x,y]}, ['test', [1,2]]]
|
48
|
+
assert_equal(output[0], 'test')
|
49
|
+
assert(output[1].is_a?(Array))
|
40
50
|
@pool.shutdown(true)
|
41
51
|
end
|
42
52
|
end
|
data/tests/cases/grow.rb
CHANGED
@@ -5,16 +5,20 @@ class GrowPoolTest < Test::Unit::TestCase
|
|
5
5
|
def setup
|
6
6
|
@pool = ActionPool::Pool.new
|
7
7
|
end
|
8
|
+
def teardown
|
9
|
+
@pool.shutdown(true)
|
10
|
+
end
|
8
11
|
def test_grow
|
9
|
-
jobs = [].fill(lambda{}, 0..
|
12
|
+
jobs = [].fill(lambda{sleep}, 0..20)
|
10
13
|
@pool.add_jobs(jobs)
|
14
|
+
Thread.pass
|
11
15
|
assert(@pool.size > 10)
|
12
16
|
@pool.shutdown(true)
|
13
17
|
end
|
14
18
|
def test_max
|
15
|
-
@pool.create_thread(
|
16
|
-
|
17
|
-
assert(
|
19
|
+
@pool.create_thread(:force) until @pool.size > @pool.max
|
20
|
+
assert(@pool.create_thread.nil?)
|
21
|
+
assert(!@pool.create_thread(:force).nil?)
|
18
22
|
@pool.shutdown(true)
|
19
23
|
end
|
20
24
|
end
|
data/tests/cases/nogrow.rb
CHANGED
data/tests/cases/resize.rb
CHANGED
@@ -5,20 +5,26 @@ class ResizePoolTest < Test::Unit::TestCase
|
|
5
5
|
def setup
|
6
6
|
@pool = ActionPool::Pool.new
|
7
7
|
end
|
8
|
+
def teardown
|
9
|
+
@pool.shutdown(true)
|
10
|
+
end
|
8
11
|
def test_resize
|
9
12
|
stop = false
|
10
|
-
20.times{ @pool << lambda{
|
13
|
+
20.times{ @pool << lambda{ sleep } }
|
14
|
+
Thread.pass
|
15
|
+
sleep(0.1)
|
11
16
|
assert(@pool.size > 10)
|
12
17
|
stop = true
|
13
18
|
@pool.shutdown(true)
|
19
|
+
@pool.status :open
|
14
20
|
@pool.max = 10
|
15
21
|
assert_equal(10, @pool.max)
|
16
|
-
assert_equal(0, @pool.size)
|
17
22
|
stop = false
|
18
23
|
20.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
|
19
24
|
assert_equal(10, @pool.size)
|
20
25
|
stop = true
|
21
26
|
@pool.shutdown(true)
|
27
|
+
@pool.status :open
|
22
28
|
@pool.max = 20
|
23
29
|
stop = false
|
24
30
|
30.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
|
data/tests/cases/shutdown.rb
CHANGED
@@ -5,12 +5,32 @@ class ShutdownPoolTest < Test::Unit::TestCase
|
|
5
5
|
def setup
|
6
6
|
@pool = ActionPool::Pool.new
|
7
7
|
end
|
8
|
+
def teardown
|
9
|
+
@pool.shutdown(true)
|
10
|
+
end
|
11
|
+
def test_close
|
12
|
+
result = 0
|
13
|
+
@pool << lambda{ result = 5 }
|
14
|
+
sleep(0.01)
|
15
|
+
assert(5, result)
|
16
|
+
@pool.status :closed
|
17
|
+
assert_raise(ActionPool::PoolClosed) do
|
18
|
+
@pool << lambda{}
|
19
|
+
end
|
20
|
+
assert_raise(ActionPool::PoolClosed) do
|
21
|
+
@pool.add_jobs [lambda{}, lambda{}]
|
22
|
+
end
|
23
|
+
@pool.shutdown(true)
|
24
|
+
end
|
8
25
|
def test_shutdown
|
9
26
|
assert_equal(10, @pool.size)
|
10
27
|
@pool.shutdown
|
11
28
|
sleep(0.5)
|
12
29
|
assert_equal(0, @pool.size)
|
13
|
-
@pool
|
30
|
+
@pool.status :open
|
31
|
+
@pool << lambda{ sleep }
|
32
|
+
sleep(0.01)
|
14
33
|
assert_equal(10, @pool.size)
|
34
|
+
@pool.shutdown(true)
|
15
35
|
end
|
16
36
|
end
|
data/tests/cases/thread.rb
CHANGED
@@ -6,6 +6,9 @@ class ThreadTest < Test::Unit::TestCase
|
|
6
6
|
@pool = ActionPool::Pool.new(:min_threads => 1, :max_threads => 1)
|
7
7
|
@thread = ActionPool::Thread.new(:pool => @pool, :respond_thread => self, :t_timeout => 60, :a_timeout => 0)
|
8
8
|
end
|
9
|
+
def teardown
|
10
|
+
@pool.shutdown(true)
|
11
|
+
end
|
9
12
|
def test_thread
|
10
13
|
sleep(0.01)
|
11
14
|
assert(@thread.waiting?)
|
data/tests/cases/timeouts.rb
CHANGED
@@ -5,15 +5,33 @@ class TimeoutPoolTest < Test::Unit::TestCase
|
|
5
5
|
def setup
|
6
6
|
@pool = ActionPool::Pool.new
|
7
7
|
end
|
8
|
-
def
|
9
|
-
@pool.
|
8
|
+
def teardown
|
9
|
+
@pool.shutdown(true)
|
10
|
+
end
|
11
|
+
def test_actiontimeout
|
12
|
+
@pool.action_timeout = 0.01
|
10
13
|
assert_equal(10, @pool.size)
|
11
14
|
stop = false
|
12
|
-
|
13
|
-
|
15
|
+
@pool.add_jobs [].fill(lambda{loop{ 1+1 }}, 0, 20)
|
16
|
+
::Thread.pass
|
17
|
+
assert(@pool.working > 10)
|
14
18
|
stop = true
|
15
|
-
sleep(
|
16
|
-
assert(@pool.
|
19
|
+
sleep(0.5)
|
20
|
+
assert(@pool.working == 0)
|
21
|
+
@pool.shutdown(true)
|
22
|
+
end
|
23
|
+
def test_threadtimeout
|
24
|
+
@pool.thread_timeout = 0.01
|
25
|
+
assert_equal(10, @pool.size)
|
26
|
+
lock = Mutex.new
|
27
|
+
guard = ConditionVariable.new
|
28
|
+
@pool.add_jobs [].fill(lambda{ lock.synchronize{ guard.wait(lock) } }, 0, 20)
|
29
|
+
::Thread.pass
|
30
|
+
assert_equal(30, @pool.size)
|
31
|
+
lock.synchronize{ guard.broadcast }
|
32
|
+
::Thread.pass
|
33
|
+
sleep(0.1)
|
34
|
+
assert(10, @pool.size)
|
17
35
|
@pool.shutdown(true)
|
18
36
|
end
|
19
37
|
end
|
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.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- spox
|
@@ -9,11 +9,11 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11
|
12
|
+
date: 2009-12-11 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
16
|
-
description:
|
16
|
+
description: The ActionPool is an easy to use thread pool for ruby.
|
17
17
|
email: spox@modspox.com
|
18
18
|
executables: []
|
19
19
|
|
@@ -25,8 +25,6 @@ extra_rdoc_files:
|
|
25
25
|
files:
|
26
26
|
- Rakefile
|
27
27
|
- actionpool.gemspec
|
28
|
-
- tests
|
29
|
-
- tests/cases
|
30
28
|
- tests/cases/general.rb
|
31
29
|
- tests/cases/resize.rb
|
32
30
|
- tests/cases/nogrow.rb
|
@@ -36,8 +34,6 @@ files:
|
|
36
34
|
- tests/cases/queue.rb
|
37
35
|
- tests/cases/grow.rb
|
38
36
|
- tests/run_tests.rb
|
39
|
-
- lib
|
40
|
-
- lib/actionpool
|
41
37
|
- lib/actionpool/Pool.rb
|
42
38
|
- lib/actionpool/Thread.rb
|
43
39
|
- lib/actionpool/LogHelper.rb
|
@@ -46,8 +42,10 @@ files:
|
|
46
42
|
- CHANGELOG
|
47
43
|
- LICENSE
|
48
44
|
- README.rdoc
|
49
|
-
has_rdoc:
|
45
|
+
has_rdoc: true
|
50
46
|
homepage: http://github.com/spox/actionpool
|
47
|
+
licenses: []
|
48
|
+
|
51
49
|
post_install_message:
|
52
50
|
rdoc_options:
|
53
51
|
- --title
|
@@ -72,9 +70,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
70
|
requirements: []
|
73
71
|
|
74
72
|
rubyforge_project:
|
75
|
-
rubygems_version: 1.3.
|
73
|
+
rubygems_version: 1.3.5
|
76
74
|
signing_key:
|
77
|
-
specification_version:
|
75
|
+
specification_version: 3
|
78
76
|
summary: Thread Pool
|
79
77
|
test_files: []
|
80
78
|
|