Skip to content

Commit

Permalink
test: restore the "busy handler does not release GVL test"
Browse files Browse the repository at this point in the history
  • Loading branch information
flavorjones committed Apr 25, 2024
1 parent 3100997 commit c71f8da
Showing 1 changed file with 59 additions and 47 deletions.
106 changes: 59 additions & 47 deletions test/test_integration_pending.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,62 +115,74 @@ def test_busy_timeout
t.join
end

def test_busy_handler_timeout_releases_gvl
@db.busy_handler_timeout = 100

t1sync = ThreadSynchronizer.new
t2sync = ThreadSynchronizer.new

busy = Mutex.new
busy.lock

count = 0
active_thread = Thread.new(t1sync) do |sync|
sync.send_to_main :ready
sync.wait_for_main :start

loop do
sleep 0.01
count += 1
begin
sync.wait_for_main :end, true
break
rescue ThreadError
end
[
# 10 is the theoretical max if timeout is 100ms and active thread sleeps 10ms
# in practice this number is almost always 9, but let's leave a bit of room for randomness
{name: "test_busy_handler_timeout_releases_gvl", busy_handler_timeout: 100, expected_count: [:>=, 8]},

# in practive this number is almost always 0, but it might squeak in 1 if the timing is juuuust right
{name: "test_busy_timeout_does_not_release_gvl", busy_timeout: 100, expected_count: [:<=, 1]}
].each do |test_case|
define_method test_case[:name] do
if test_case[:busy_handler_timeout]
@db.busy_handler_timeout = 100
end
if test_case[:busy_timeout]
@db.busy_timeout = test_case[:busy_timeout]
end
sync.send_to_main :done
end

blocking_thread = Thread.new(t2sync) do |sync|
db2 = SQLite3::Database.open("test.db")
db2.transaction(:exclusive) do
t1sync = ThreadSynchronizer.new
t2sync = ThreadSynchronizer.new

busy = Mutex.new
busy.lock

count = 0
active_thread = Thread.new(t1sync) do |sync|
sync.send_to_main :ready
busy.lock
sync.wait_for_main :start

loop do
sleep 0.01
count += 1
begin
sync.wait_for_main :end, true
break
rescue ThreadError
end
end
sync.send_to_main :done
end
ensure
db2&.close
end

t1sync.wait_for_thread :ready
t2sync.wait_for_thread :ready
blocking_thread = Thread.new(t2sync) do |sync|
db2 = SQLite3::Database.open("test.db")
db2.transaction(:exclusive) do
sync.send_to_main :ready
busy.lock
end
ensure
db2&.close
end

t1sync.send_to_thread :start
assert_raises(SQLite3::BusyException) do
@db.execute "insert into foo (b) values ( 'from 2' )"
end
t1sync.send_to_thread :end
t1sync.wait_for_thread :ready
t2sync.wait_for_thread :ready

# 10 is the theoretical max if timeout is 100ms and active thread sleeps 10ms
# in practice this number is almost always 9, but let's leave a bit of room for randomness
assert_operator(count, :>=, 8)
t1sync.send_to_thread :start
assert_raises(SQLite3::BusyException) do
@db.execute "insert into foo (b) values ( 'from 2' )"
end
t1sync.send_to_thread :end

busy.unlock
ensure
active_thread&.kill
blocking_thread&.kill
assert_operator(count, *test_case[:expected_count])

t1sync&.close
t2sync&.close
busy.unlock
ensure
active_thread&.join
blocking_thread&.join

t1sync&.close
t2sync&.close
end
end

def test_busy_handler_outwait
Expand Down

0 comments on commit c71f8da

Please sign in to comment.