126 lines
2.5 KiB
Ruby
126 lines
2.5 KiB
Ruby
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
|