actionpool 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 +13 -0
- data/LICENSE +165 -0
- data/README.rdoc +192 -0
- data/Rakefile +74 -0
- data/actionpool.gemspec +15 -0
- data/lib/actionpool/LogHelper.rb +25 -0
- data/lib/actionpool/Pool.rb +282 -0
- data/lib/actionpool/Pool.rb~ +260 -0
- data/lib/actionpool/Queue.rb +53 -0
- data/lib/actionpool/Queue.rb~ +46 -0
- data/lib/actionpool/Thread.rb +155 -0
- data/lib/actionpool/Thread.rb~ +146 -0
- data/lib/actionpool.rb +7 -0
- data/test.rb +7 -0
- data/tests/cases/general.rb +52 -0
- data/tests/cases/general.rb~ +45 -0
- data/tests/cases/grow.rb +24 -0
- data/tests/cases/nogrow.rb +15 -0
- data/tests/cases/queue.rb +34 -0
- data/tests/cases/resize.rb +35 -0
- data/tests/cases/shutdown.rb +36 -0
- data/tests/cases/thread.rb +27 -0
- data/tests/cases/timeouts.rb +37 -0
- data/tests/run_tests.rb +8 -0
- metadata +83 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module ActionPool
|
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
|
10
|
+
class Thread
|
11
|
+
# :pool:: pool thread is associated with
|
12
|
+
# :t_timeout:: max time a thread is allowed to wait for action
|
13
|
+
# :a_timeout:: max time thread is allowed to work
|
14
|
+
# :respond_thread:: thread to send execptions to
|
15
|
+
# :logger:: LogHelper for logging messages
|
16
|
+
# Create a new thread
|
17
|
+
def initialize(args)
|
18
|
+
raise ArgumentError.new('Hash required for initialization') unless args.is_a?(Hash)
|
19
|
+
raise ArgumentError.new('ActionPool::Thread requires a pool') unless args[:pool]
|
20
|
+
raise ArgumentError.new('ActionPool::Thread requries thread to respond') unless args[:respond_thread]
|
21
|
+
@pool = args[:pool]
|
22
|
+
@respond_to = args[:respond_thread]
|
23
|
+
@thread_timeout = args[:t_timeout] ? args[:t_timeout].to_f : 0
|
24
|
+
@action_timeout = args[:a_timeout] ? args[:a_timeout].to_f : 0
|
25
|
+
@kill = false
|
26
|
+
@logger = args[:logger].is_a?(LogHelper) ? args[:logger] : LogHelper.new(args[:logger])
|
27
|
+
@lock = Mutex.new
|
28
|
+
@thread = ::Thread.new{ start_thread }
|
29
|
+
end
|
30
|
+
|
31
|
+
# :force:: force the thread to stop
|
32
|
+
# :wait:: wait for the thread to stop
|
33
|
+
# Stop the thread
|
34
|
+
def stop(*args)
|
35
|
+
@kill = true
|
36
|
+
@thread.raise Wakeup.new if args.include?(:force) || waiting?
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# Currently waiting
|
41
|
+
def waiting?
|
42
|
+
@lock.synchronize{@status} == :wait
|
43
|
+
end
|
44
|
+
|
45
|
+
# Is the thread still alive
|
46
|
+
def alive?
|
47
|
+
@thread.alive?
|
48
|
+
end
|
49
|
+
|
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 }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Seconds thread will wait for input
|
63
|
+
def thread_timeout
|
64
|
+
@thread_timeout
|
65
|
+
end
|
66
|
+
|
67
|
+
# Seconds thread will spend working on a given task
|
68
|
+
def action_timeout
|
69
|
+
@action_timeout
|
70
|
+
end
|
71
|
+
|
72
|
+
# t:: seconds to wait for input (floats allow for values 0 < t < 1)
|
73
|
+
# Set the maximum amount of time to wait for a task
|
74
|
+
def thread_timeout=(t)
|
75
|
+
t = t.to_f
|
76
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
77
|
+
@thread_timeout = t
|
78
|
+
@thread.raise Retimeout.new if waiting?
|
79
|
+
t
|
80
|
+
end
|
81
|
+
|
82
|
+
# t:: seconds to work on a task (floats allow for values 0 < t < 1)
|
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
|
85
|
+
def action_timeout=(t)
|
86
|
+
t = t.to_f
|
87
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
88
|
+
@action_timeout = t
|
89
|
+
t
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# Start our thread
|
95
|
+
def start_thread
|
96
|
+
begin
|
97
|
+
@logger.info("New pool thread is starting (#{self})")
|
98
|
+
until(@kill) do
|
99
|
+
status(:wait)
|
100
|
+
begin
|
101
|
+
action = nil
|
102
|
+
if(@pool.size > @pool.min && !@thread_timeout.zero?)
|
103
|
+
Timeout::timeout(@thread_timeout) do
|
104
|
+
action = @pool.action
|
105
|
+
end
|
106
|
+
else
|
107
|
+
action = @pool.action
|
108
|
+
end
|
109
|
+
status(:run)
|
110
|
+
run(action[0], action[1]) unless action.nil?
|
111
|
+
status(:wait)
|
112
|
+
rescue Timeout::Error => boom
|
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')
|
118
|
+
rescue Exception => boom
|
119
|
+
@logger.error("Pool thread caught an exception: #{boom}\n#{boom.backtrace.join("\n")}")
|
120
|
+
@respond_to.raise boom
|
121
|
+
end
|
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.")
|
128
|
+
rescue Exception => boom
|
129
|
+
@logger.error("Pool thread caught an exception: #{boom}\n#{boom.backtrace.join("\n")}")
|
130
|
+
@respond_to.raise boom
|
131
|
+
ensure
|
132
|
+
@logger.info("Pool thread is shutting down (#{self})")
|
133
|
+
@pool.remove(self)
|
134
|
+
@pool.create_thread
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# action:: task to be run
|
139
|
+
# args:: arguments to be passed to task
|
140
|
+
# Run the task
|
141
|
+
def run(action, args)
|
142
|
+
begin
|
143
|
+
unless(@action_timeout.zero?)
|
144
|
+
Timeout::timeout(@action_timeout) do
|
145
|
+
action.call(*args[0])
|
146
|
+
end
|
147
|
+
else
|
148
|
+
action.call(*args[0])
|
149
|
+
end
|
150
|
+
rescue Timeout::Error => boom
|
151
|
+
@logger.warn("Pool thread reached max execution time for action: #{boom}")
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module ActionPool
|
4
|
+
# Exception class used for waking up a thread
|
5
|
+
class Wakeup < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
class Thread
|
9
|
+
# :pool:: pool thread is associated with
|
10
|
+
# :t_timeout:: max time a thread is allowed to wait for action
|
11
|
+
# :a_timeout:: max time thread is allowed to work
|
12
|
+
# :respond_thread:: thread to send execptions to
|
13
|
+
# :logger:: LogHelper for logging messages
|
14
|
+
# Create a new thread
|
15
|
+
def initialize(args)
|
16
|
+
raise ArgumentError.new('Hash required for initialization') unless args.is_a?(Hash)
|
17
|
+
raise ArgumentError.new('ActionPool::Thread requires a pool') unless args[:pool]
|
18
|
+
raise ArgumentError.new('ActionPool::Thread requries thread to respond') unless args[:respond_thread]
|
19
|
+
@pool = args[:pool]
|
20
|
+
@respond_to = args[:respond_thread]
|
21
|
+
@thread_timeout = args[:t_timeout] ? args[:t_timeout].to_f : 0
|
22
|
+
@action_timeout = args[:a_timeout] ? args[:a_timeout].to_f : 0
|
23
|
+
@kill = false
|
24
|
+
@logger = args[:logger].is_a?(LogHelper) ? args[:logger] : LogHelper.new(args[:logger])
|
25
|
+
@lock = Mutex.new
|
26
|
+
@thread = ::Thread.new{ start_thread }
|
27
|
+
end
|
28
|
+
|
29
|
+
# :force:: force the thread to stop
|
30
|
+
# :wait:: wait for the thread to stop
|
31
|
+
# Stop the thread
|
32
|
+
def stop(*args)
|
33
|
+
@kill = true
|
34
|
+
@thread.raise Wakeup.new if args.include?(:force) || waiting?
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# Currently waiting
|
39
|
+
def waiting?
|
40
|
+
@status == :wait
|
41
|
+
end
|
42
|
+
|
43
|
+
# Is the thread still alive
|
44
|
+
def alive?
|
45
|
+
@thread.alive?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Current thread status
|
49
|
+
def status
|
50
|
+
@lock.synchronize{ return @status }
|
51
|
+
end
|
52
|
+
|
53
|
+
# arg:: :wait or :run
|
54
|
+
# Set current status
|
55
|
+
def status(arg)
|
56
|
+
raise InvalidType.new('Status can only be set to :wait or :run') unless arg == :wait || arg == :run
|
57
|
+
@lock.synchronize{ @status = arg }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Seconds thread will wait for input
|
61
|
+
def thread_timeout
|
62
|
+
@thread_timeout
|
63
|
+
end
|
64
|
+
|
65
|
+
# Seconds thread will spend working on a given task
|
66
|
+
def action_timeout
|
67
|
+
@action_timeout
|
68
|
+
end
|
69
|
+
|
70
|
+
# t:: seconds to wait for input (floats allow for values 0 < t < 1)
|
71
|
+
# Set the maximum amount of time to wait for a task
|
72
|
+
def thread_timeout=(t)
|
73
|
+
t = t.to_f
|
74
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
75
|
+
@thread_timeout = t
|
76
|
+
t
|
77
|
+
end
|
78
|
+
|
79
|
+
# t:: seconds to work on a task (floats allow for values 0 < t < 1)
|
80
|
+
# Set the maximum amount of time to work on a given task
|
81
|
+
def action_timeout=(t)
|
82
|
+
t = t.to_f
|
83
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
84
|
+
@action_timeout = t
|
85
|
+
t
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Start our thread
|
91
|
+
def start_thread
|
92
|
+
begin
|
93
|
+
@logger.info("New pool thread is starting (#{self})")
|
94
|
+
until(@kill) do
|
95
|
+
status(:wait)
|
96
|
+
begin
|
97
|
+
action = nil
|
98
|
+
if(@pool.size > @pool.min)
|
99
|
+
Timeout::timeout(@thread_timeout) do
|
100
|
+
action = @pool.action
|
101
|
+
end
|
102
|
+
else
|
103
|
+
action = @pool.action
|
104
|
+
end
|
105
|
+
status(:run)
|
106
|
+
run(action[0], action[1]) unless action.nil?
|
107
|
+
status(:wait)
|
108
|
+
rescue Timeout::Error => boom
|
109
|
+
@kill = true
|
110
|
+
rescue Wakeup
|
111
|
+
@logger.info("Thread #{::Thread.current} was woken up.")
|
112
|
+
rescue Exception => boom
|
113
|
+
@logger.error("Pool thread caught an exception: #{boom}\n#{boom.backtrace.join("\n")}")
|
114
|
+
@respond_to.raise boom
|
115
|
+
end
|
116
|
+
end
|
117
|
+
rescue Wakeup
|
118
|
+
@logger.info("Thread #{::Thread.current} was woken up.")
|
119
|
+
rescue Exception => boom
|
120
|
+
@logger.error("Pool thread caught an exception: #{boom}\n#{boom.backtrace.join("\n")}")
|
121
|
+
@respond_to.raise boom
|
122
|
+
ensure
|
123
|
+
@logger.info("Pool thread is shutting down (#{self})")
|
124
|
+
@pool.remove(self)
|
125
|
+
@pool.create_threads
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# action:: task to be run
|
130
|
+
# args:: arguments to be passed to task
|
131
|
+
# Run the task
|
132
|
+
def run(action, args)
|
133
|
+
begin
|
134
|
+
if(@action_timeout > 0)
|
135
|
+
Timeout::timeout(@action_timeout) do
|
136
|
+
action.call(*args.flatten)
|
137
|
+
end
|
138
|
+
else
|
139
|
+
action.call(*args[0])
|
140
|
+
end
|
141
|
+
rescue Timeout::Error => boom
|
142
|
+
@logger.warn("Pool thread reached max execution time for action: #{boom}")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/lib/actionpool.rb
ADDED
data/test.rb
ADDED
@@ -0,0 +1,52 @@
|
|
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 teardown
|
9
|
+
@pool.shutdown(true)
|
10
|
+
end
|
11
|
+
def test_numbers
|
12
|
+
assert_equal(10, @pool.size)
|
13
|
+
assert_equal(10, @pool.min)
|
14
|
+
assert_equal(100, @pool.max)
|
15
|
+
assert_equal(0, @pool.action_timeout)
|
16
|
+
assert_equal(0, @pool.thread_timeout)
|
17
|
+
assert_equal(0, @pool.action_size)
|
18
|
+
end
|
19
|
+
def test_output
|
20
|
+
a = 0
|
21
|
+
lock = Mutex.new
|
22
|
+
run = lambda{ lock.synchronize{ a += 1 } }
|
23
|
+
100.times{ @pool << run }
|
24
|
+
@pool.shutdown
|
25
|
+
assert_equal(100, a)
|
26
|
+
@pool.status :open
|
27
|
+
a = 0
|
28
|
+
jobs = [].fill(run,0,100)
|
29
|
+
@pool.add_jobs(jobs)
|
30
|
+
@pool.shutdown
|
31
|
+
assert_equal(100, a)
|
32
|
+
@pool.shutdown(true)
|
33
|
+
end
|
34
|
+
def test_args
|
35
|
+
@pool.status :open
|
36
|
+
output = nil
|
37
|
+
@pool << [lambda{|x| output = x}, [2]]
|
38
|
+
assert(2, output)
|
39
|
+
@pool.add_jobs([[lambda{|x| output = x}, [3]]])
|
40
|
+
assert(3, output)
|
41
|
+
@pool << [lambda{|x,y| output = x+y}, [1,2]]
|
42
|
+
assert(3, output)
|
43
|
+
output = []
|
44
|
+
@pool.add_jobs([[lambda{|x,y| output << x + y}, [1,1]], [lambda{|x| output << x}, [3]]])
|
45
|
+
assert(output.include?(2))
|
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))
|
50
|
+
@pool.shutdown(true)
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,45 @@
|
|
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
|
+
@pool << [lambda{|x,y| output = x+y}, [1,2]]
|
37
|
+
# assert(3, output)
|
38
|
+
sleep(0.01)
|
39
|
+
output = []
|
40
|
+
@pool.add_jobs([[lambda{|x,y| output << x + y}, [1,1]], [lambda{|x| output << x}, [3]]])
|
41
|
+
# assert(output.include?(2))
|
42
|
+
# assert(output.include?(3))
|
43
|
+
@pool.shutdown(true)
|
44
|
+
end
|
45
|
+
end
|
data/tests/cases/grow.rb
ADDED
@@ -0,0 +1,24 @@
|
|
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 teardown
|
9
|
+
@pool.shutdown(true)
|
10
|
+
end
|
11
|
+
def test_grow
|
12
|
+
jobs = [].fill(lambda{sleep}, 0..20)
|
13
|
+
@pool.add_jobs(jobs)
|
14
|
+
Thread.pass
|
15
|
+
assert(@pool.size > 10)
|
16
|
+
@pool.shutdown(true)
|
17
|
+
end
|
18
|
+
def test_max
|
19
|
+
@pool.create_thread(:force) until @pool.size > @pool.max
|
20
|
+
assert(@pool.create_thread.nil?)
|
21
|
+
assert(!@pool.create_thread(:force).nil?)
|
22
|
+
@pool.shutdown(true)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'actionpool'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class NoGrowPoolTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@pool = ActionPool::Pool.new
|
7
|
+
end
|
8
|
+
def teardown
|
9
|
+
@pool.shutdown(true)
|
10
|
+
end
|
11
|
+
def test_nogrow
|
12
|
+
5.times{ @pool << lambda{} }
|
13
|
+
assert_equal(10, @pool.size)
|
14
|
+
end
|
15
|
+
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,35 @@
|
|
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 teardown
|
9
|
+
@pool.shutdown(true)
|
10
|
+
end
|
11
|
+
def test_resize
|
12
|
+
stop = false
|
13
|
+
20.times{ @pool << lambda{ sleep } }
|
14
|
+
Thread.pass
|
15
|
+
sleep(0.1)
|
16
|
+
assert(@pool.size > 10)
|
17
|
+
stop = true
|
18
|
+
@pool.shutdown(true)
|
19
|
+
@pool.status :open
|
20
|
+
@pool.max = 10
|
21
|
+
assert_equal(10, @pool.max)
|
22
|
+
stop = false
|
23
|
+
20.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
|
24
|
+
assert_equal(10, @pool.size)
|
25
|
+
stop = true
|
26
|
+
@pool.shutdown(true)
|
27
|
+
@pool.status :open
|
28
|
+
@pool.max = 20
|
29
|
+
stop = false
|
30
|
+
30.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
|
31
|
+
stop = true
|
32
|
+
assert(@pool.size > 10)
|
33
|
+
@pool.shutdown(true)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
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 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
|
25
|
+
def test_shutdown
|
26
|
+
assert_equal(10, @pool.size)
|
27
|
+
@pool.shutdown
|
28
|
+
sleep(0.5)
|
29
|
+
assert_equal(0, @pool.size)
|
30
|
+
@pool.status :open
|
31
|
+
@pool << lambda{ sleep }
|
32
|
+
sleep(0.01)
|
33
|
+
assert_equal(10, @pool.size)
|
34
|
+
@pool.shutdown(true)
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
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 teardown
|
10
|
+
@pool.shutdown(true)
|
11
|
+
end
|
12
|
+
def test_thread
|
13
|
+
sleep(0.01)
|
14
|
+
assert(@thread.waiting?)
|
15
|
+
assert_equal(60, @thread.thread_timeout)
|
16
|
+
assert_equal(0, @thread.action_timeout)
|
17
|
+
assert(@thread.alive?)
|
18
|
+
stop = false
|
19
|
+
10.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
|
20
|
+
assert(!@thread.waiting?)
|
21
|
+
@thread.stop(:force)
|
22
|
+
sleep(0.01)
|
23
|
+
assert(!@thread.alive?)
|
24
|
+
stop = true
|
25
|
+
@pool.shutdown(true)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
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 teardown
|
9
|
+
@pool.shutdown(true)
|
10
|
+
end
|
11
|
+
def test_actiontimeout
|
12
|
+
@pool.action_timeout = 0.01
|
13
|
+
assert_equal(10, @pool.size)
|
14
|
+
stop = false
|
15
|
+
@pool.add_jobs [].fill(lambda{loop{ 1+1 }}, 0, 20)
|
16
|
+
::Thread.pass
|
17
|
+
assert(@pool.working > 10)
|
18
|
+
stop = true
|
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)
|
35
|
+
@pool.shutdown(true)
|
36
|
+
end
|
37
|
+
end
|
data/tests/run_tests.rb
ADDED