ActionPool 0.2.1 → 0.2.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/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
|
|