2012-11-15 16:59:37 -05:00
|
|
|
require File.expand_path('../helper', __FILE__)
|
|
|
|
require 'rake/thread_pool'
|
|
|
|
require 'test/unit/assertions'
|
|
|
|
|
|
|
|
class TestRakeTestThreadPool < Rake::TestCase
|
|
|
|
include Rake
|
|
|
|
|
|
|
|
def test_pool_executes_in_current_thread_for_zero_threads
|
|
|
|
pool = ThreadPool.new(0)
|
2013-10-11 17:35:01 -04:00
|
|
|
f = pool.future { Thread.current }
|
2012-11-15 16:59:37 -05:00
|
|
|
pool.join
|
|
|
|
assert_equal Thread.current, f.value
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_pool_executes_in_other_thread_for_pool_of_size_one
|
|
|
|
pool = ThreadPool.new(1)
|
2013-10-11 17:35:01 -04:00
|
|
|
f = pool.future { Thread.current }
|
2012-11-15 16:59:37 -05:00
|
|
|
pool.join
|
|
|
|
refute_equal Thread.current, f.value
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_pool_executes_in_two_other_threads_for_pool_of_size_two
|
|
|
|
pool = ThreadPool.new(2)
|
2013-10-11 17:35:01 -04:00
|
|
|
threads = 2.times.map {
|
|
|
|
pool.future {
|
|
|
|
sleep 0.1
|
|
|
|
Thread.current
|
|
|
|
}
|
|
|
|
}.each { |f|
|
|
|
|
f.value
|
|
|
|
}
|
2012-11-15 16:59:37 -05:00
|
|
|
|
|
|
|
refute_equal threads[0], threads[1]
|
|
|
|
refute_equal Thread.current, threads[0]
|
|
|
|
refute_equal Thread.current, threads[1]
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_pool_creates_the_correct_number_of_threads
|
|
|
|
pool = ThreadPool.new(2)
|
|
|
|
threads = Set.new
|
|
|
|
t_mutex = Mutex.new
|
|
|
|
10.times.each do
|
|
|
|
pool.future do
|
|
|
|
sleep 0.02
|
2013-10-11 17:35:01 -04:00
|
|
|
t_mutex.synchronize { threads << Thread.current }
|
2012-11-15 16:59:37 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
pool.join
|
|
|
|
assert_equal 2, threads.count
|
|
|
|
end
|
|
|
|
|
2013-10-11 17:35:01 -04:00
|
|
|
def test_pool_future_does_not_duplicate_arguments
|
2012-11-15 16:59:37 -05:00
|
|
|
pool = ThreadPool.new(2)
|
2013-10-11 17:35:01 -04:00
|
|
|
obj = Object.new
|
|
|
|
captured = nil
|
|
|
|
pool.future(obj) { |var| captured = var }
|
2012-11-15 16:59:37 -05:00
|
|
|
pool.join
|
2013-10-11 17:35:01 -04:00
|
|
|
assert_equal obj, captured
|
2012-11-15 16:59:37 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_pool_join_empties_queue
|
|
|
|
pool = ThreadPool.new(2)
|
|
|
|
repeat = 25
|
|
|
|
repeat.times {
|
|
|
|
pool.future do
|
|
|
|
repeat.times {
|
|
|
|
pool.future do
|
|
|
|
repeat.times {
|
|
|
|
pool.future do end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
}
|
|
|
|
|
|
|
|
pool.join
|
2013-10-11 17:35:01 -04:00
|
|
|
assert_equal(
|
|
|
|
true,
|
|
|
|
pool.__send__(:__queue__).empty?,
|
|
|
|
"queue should be empty")
|
2012-11-15 16:59:37 -05:00
|
|
|
end
|
|
|
|
|
2013-10-11 17:35:01 -04:00
|
|
|
CustomError = Class.new(StandardError)
|
|
|
|
|
2012-11-15 16:59:37 -05:00
|
|
|
# test that throwing an exception way down in the blocks propagates
|
|
|
|
# to the top
|
|
|
|
def test_exceptions
|
|
|
|
pool = ThreadPool.new(10)
|
|
|
|
|
|
|
|
deep_exception_block = lambda do |count|
|
2013-10-11 17:35:01 -04:00
|
|
|
raise CustomError if count < 1
|
|
|
|
pool.future(count - 1, &deep_exception_block).value
|
2012-11-15 16:59:37 -05:00
|
|
|
end
|
|
|
|
|
2013-10-11 17:35:01 -04:00
|
|
|
assert_raises(CustomError) do
|
2012-11-15 16:59:37 -05:00
|
|
|
pool.future(2, &deep_exception_block).value
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_pool_prevents_deadlock
|
|
|
|
pool = ThreadPool.new(5)
|
|
|
|
|
|
|
|
common_dependency_a = pool.future { sleep 0.2 }
|
2013-10-11 17:35:01 -04:00
|
|
|
futures_a = 10.times.map {
|
|
|
|
pool.future {
|
|
|
|
common_dependency_a.value
|
|
|
|
sleep(rand() * 0.01)
|
|
|
|
}
|
|
|
|
}
|
2012-11-15 16:59:37 -05:00
|
|
|
|
|
|
|
common_dependency_b = pool.future { futures_a.each { |f| f.value } }
|
2013-10-11 17:35:01 -04:00
|
|
|
futures_b = 10.times.map {
|
|
|
|
pool.future {
|
|
|
|
common_dependency_b.value
|
|
|
|
sleep(rand() * 0.01)
|
|
|
|
}
|
|
|
|
}
|
2012-11-15 16:59:37 -05:00
|
|
|
|
2013-10-11 17:35:01 -04:00
|
|
|
futures_b.each { |f| f.value }
|
2012-11-15 16:59:37 -05:00
|
|
|
pool.join
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_pool_reports_correct_results
|
|
|
|
pool = ThreadPool.new(7)
|
|
|
|
|
|
|
|
a = 18
|
|
|
|
b = 5
|
|
|
|
c = 3
|
|
|
|
|
2013-10-11 17:35:01 -04:00
|
|
|
result = a.times.map do
|
2012-11-15 16:59:37 -05:00
|
|
|
pool.future do
|
2013-10-11 17:35:01 -04:00
|
|
|
b.times.map do
|
2012-11-15 16:59:37 -05:00
|
|
|
pool.future { sleep rand * 0.001; c }
|
2013-10-11 17:35:01 -04:00
|
|
|
end.reduce(0) { |m, f| m + f.value }
|
2012-11-15 16:59:37 -05:00
|
|
|
end
|
2013-10-11 17:35:01 -04:00
|
|
|
end.reduce(0) { |m, f| m + f.value }
|
2012-11-15 16:59:37 -05:00
|
|
|
|
2013-10-11 17:35:01 -04:00
|
|
|
assert_equal a * b * c, result
|
2012-11-15 16:59:37 -05:00
|
|
|
pool.join
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|