From d99b6f7c45d7058b79742ac854a654ad99613ded Mon Sep 17 00:00:00 2001 From: Justin Balthrop Date: Tue, 24 Nov 2009 13:29:07 -0800 Subject: [PATCH] add deadlock test --- test/deadlock_test.rb | 125 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 test/deadlock_test.rb diff --git a/test/deadlock_test.rb b/test/deadlock_test.rb new file mode 100644 index 0000000..268a859 --- /dev/null +++ b/test/deadlock_test.rb @@ -0,0 +1,125 @@ +require File.dirname(__FILE__) + '/test_helper' + +DIR = '/tmp/bdb_deadlock_test' +Bdb::Environment.config :path => DIR, :cache_size => 1 * 1024 * 1024, :page_size => 512 + +class DeadlockTest < Test::Unit::TestCase + def setup + FileUtils.rmtree DIR + FileUtils.mkdir DIR + Bdb::Environment[DIR].close + @db = Bdb::Database.new('foo') + end + + attr_reader :db + + N = 5000 # total number of records + R = 10 # number of readers + W = 10 # number of writers + T = 20 # reads per transaction + L = 100 # logging frequency + + def test_detect_deadlock + pids = [] + + W.times do |n| + pids << fork(&writer) + end + + sleep(1) + + R.times do + pids << fork(&reader) + end + + # Make sure that all processes finish with no errors. + pids.each do |pid| + Process.wait(pid) + assert_equal status, $?.exitstatus + end + end + + C = 10 + def test_detect_unclosed_resources + threads = [] + + threads << Thread.new do + C.times do + sleep(10) + + pid = fork do + cursor = db.db.cursor(nil, 0) + cursor.get(nil, nil, Bdb::DB_FIRST) + exit!(1) + end + puts "\n====simulating exit with unclosed resources ====" + Process.wait(pid) + assert_equal 1, $?.exitstatus + end + end + + threads << Thread.new do + C.times do + pid = fork(&writer(1000)) + Process.wait(pid) + assert [0,9].include?($?.exitstatus) + end + end + + sleep(3) + + threads << Thread.new do + C.times do + pid = fork(&reader(1000)) + Process.wait(pid) + assert [0,9].include?($?.exitstatus) + end + end + + threads.each {|t| t.join} + end + + def reader(n = N) + lambda do + T.times do + (1...n).to_a.shuffle.each_slice(T) do |ids| + db.transaction do + ids.each {|id| db.get(id)} + end + log('r') + end + end + db.close_environment + end + end + + def writer(n = N) + lambda do + (1...n).to_a.shuffle.each do |id| + db.transaction do + begin + db.set(id, {:bar => "bar" * 1000 + " ayn #{rand}"}) + rescue Bdb::DbError => e + if e.code == Bdb::DB_KEYEXIST + db.delete(id) + retry + else + raise(e) + end + end + end + log('w') + end + db.close_environment + end + end + + def log(action) + @count ||= Hash.new(0) + if @count[action] % L == 0 + print action.to_s + $stdout.flush + end + @count[action] += 1 + end +end