Skip to content

Commit 48daae1

Browse files
committed
Fix bug with busy_handler_timeout and write better tests
1 parent 9444b9b commit 48daae1

File tree

2 files changed

+36
-22
lines changed

2 files changed

+36
-22
lines changed

lib/sqlite3/database.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def initialize file, options = {}, zvfs = nil
131131
@type_translator = make_type_translator @type_translation
132132
@readonly = mode & Constants::Open::READONLY != 0
133133
@default_transaction_mode = options[:default_transaction_mode] || :deferred
134+
@timeout_deadline = nil
134135

135136
if block_given?
136137
begin
@@ -697,12 +698,16 @@ def readonly?
697698
# while SQLite sleeps and retries.
698699
def busy_handler_timeout=( milliseconds )
699700
timeout_seconds = milliseconds.fdiv(1000)
700-
timeout_deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout_seconds
701701

702702
busy_handler do |count|
703-
next false if Process.clock_gettime(Process::CLOCK_MONOTONIC) > timeout_deadline
704-
705-
sleep(0.001)
703+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
704+
if count.zero?
705+
@timeout_deadline = now + timeout_seconds
706+
elsif now > @timeout_deadline
707+
next false
708+
else
709+
sleep(0.001)
710+
end
706711
end
707712
end
708713

test/test_integration_pending.rb

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -113,30 +113,39 @@ def test_busy_timeout
113113
assert time.real*1000 >= 1000
114114
end
115115

116-
def test_busy_timeout_blocks_gvl_while_busy_handler_timeout_releases_gvl
117-
busy_timeout_db = SQLite3::Database.open( "test.db" )
118-
busy_timeout_db.busy_timeout 1000
119-
busy_timeout_time = Benchmark.measure do
120-
@db.transaction( :exclusive ) do
121-
assert_raises( SQLite3::BusyException ) do
122-
busy_timeout_db.transaction( :exclusive ) {}
116+
def test_busy_timeout_blocks_gvl
117+
threads = [1, 2].map do
118+
Thread.new do
119+
db = SQLite3::Database.new("test.db")
120+
db.busy_timeout = 3000
121+
db.transaction(:immediate) do
122+
db.execute "insert into foo ( b ) values ( ? )", rand(1000).to_s
123+
sleep 1
124+
db.execute "insert into foo ( b ) values ( ? )", rand(1000).to_s
123125
end
124126
end
125127
end
126-
busy_timeout_db.close
127-
128-
busy_handler_timeout_db = SQLite3::Database.open( "test.db" )
129-
busy_handler_timeout_db.busy_handler_timeout = 1000
130-
busy_handler_timeout_time = Benchmark.measure do
131-
@db.transaction( :exclusive ) do
132-
assert_raises( SQLite3::BusyException ) do
133-
busy_handler_timeout_db.transaction( :exclusive ) {}
128+
129+
assert_raise( SQLite3::BusyException ) do
130+
threads.each(&:join)
131+
end
132+
end
133+
134+
def test_busy_handler_timeout_releases_gvl
135+
threads = [1, 2].map do
136+
Thread.new do
137+
db = SQLite3::Database.new("test.db")
138+
db.busy_handler_timeout = 3000
139+
db.transaction(:immediate) do
140+
db.execute "insert into foo ( b ) values ( ? )", rand(1000).to_s
141+
sleep 1
142+
db.execute "insert into foo ( b ) values ( ? )", rand(1000).to_s
134143
end
135144
end
136145
end
137-
busy_handler_timeout_db.close
138146

139-
assert_operator busy_timeout_time.real, :>, busy_handler_timeout_time.real
140-
assert_operator busy_timeout_time.real, :>, busy_handler_timeout_time.real+1
147+
assert_nothing_raised do
148+
threads.each(&:join)
149+
end
141150
end
142151
end

0 commit comments

Comments
 (0)