File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -695,6 +695,25 @@ def readonly?
695695 @readonly
696696 end
697697
698+ # Sets a #busy_handler that releases the GVL between retries,
699+ # but only retries up to the indicated number of +milliseconds+.
700+ # This is an alternative to #busy_timeout, which holds the GVL
701+ # while SQLite sleeps and retries.
702+ def busy_handler_timeout = ( milliseconds )
703+ timeout_seconds = milliseconds . fdiv ( 1000 )
704+
705+ busy_handler do |count |
706+ now = Process . clock_gettime ( Process ::CLOCK_MONOTONIC )
707+ if count . zero?
708+ @timeout_deadline = now + timeout_seconds
709+ elsif now > @timeout_deadline
710+ next false
711+ else
712+ sleep ( 0.001 )
713+ end
714+ end
715+ end
716+
698717 # A helper class for dealing with custom functions (see #create_function,
699718 # #create_aggregate, and #create_aggregate_handler). It encapsulates the
700719 # opaque function object that represents the current invocation. It also
Original file line number Diff line number Diff line change @@ -74,4 +74,42 @@ def test_busy_timeout
7474
7575 assert_operator time . real * 1000 , :>= , 1000
7676 end
77+
78+ def test_busy_handler_timeout_releases_gvl
79+ work = [ ]
80+
81+ Thread . new do
82+ while true
83+ sleep 0.1
84+ work << '.'
85+ end
86+ end
87+ sleep 1
88+
89+ @db . busy_handler_timeout = 1000
90+ busy = Mutex . new
91+ busy . lock
92+
93+ t = Thread . new do
94+ begin
95+ db2 = SQLite3 ::Database . open ( "test.db" )
96+ db2 . transaction ( :exclusive ) do
97+ busy . lock
98+ end
99+ ensure
100+ db2 . close if db2
101+ end
102+ end
103+ sleep 1
104+
105+ assert_raises ( SQLite3 ::BusyException ) do
106+ work << '|'
107+ @db . execute "insert into foo (b) values ( 'from 2' )"
108+ end
109+
110+ busy . unlock
111+ t . join
112+
113+ assert work . size - work . find_index ( '|' ) > 3
114+ end
77115end
You can’t perform that action at this time.
0 commit comments