Skip to content

Commit ed4a167

Browse files
committed
Use cancel in disconnect and NIF for set_busy_timeout
- Replace Sqlite3.interrupt(db) with Sqlite3.cancel(db) in disconnect/2 (cancel is a superset that also wakes the busy handler condvar) - Replace PRAGMA busy_timeout with Sqlite3.set_busy_timeout NIF call (PRAGMA internally calls sqlite3_busy_timeout which destroys custom handlers) Together these changes complete the fix for #192 - disconnect now properly cancels all three blocking planes (VDBE execution, busy handler sleep, and mutex contention as a side effect of the first two).
1 parent 0e90b8e commit ed4a167

2 files changed

Lines changed: 10 additions & 4 deletions

File tree

c_src/sqlite3_nif.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,7 +1790,10 @@ exqlite_set_busy_timeout(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
17901790
return enif_make_badarg(env);
17911791
}
17921792

1793+
// Protect write to busy_timeout_ms since busy handler reads it
1794+
tw_lock(&conn->cancel_tw);
17931795
conn->busy_timeout_ms = timeout_ms;
1796+
tw_unlock(&conn->cancel_tw);
17941797

17951798
return am_ok;
17961799
}

lib/exqlite/connection.ex

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,11 @@ defmodule Exqlite.Connection do
212212
apply(state.before_disconnect, [err, state])
213213
end
214214

215-
# Interrupt any in-flight query so close() doesn't block on conn->mutex.
216-
# Without this, a long-running dirty NIF holds the mutex and close() deadlocks.
215+
# Cancel any in-flight query: wake the busy handler condvar AND interrupt
216+
# VDBE execution so close() doesn't block on conn->mutex.
217+
# This is a superset of the old Sqlite3.interrupt(db) call.
217218
# See: https://github.com/elixir-sqlite/exqlite/issues/192
218-
Sqlite3.interrupt(db)
219+
Sqlite3.cancel(db)
219220

220221
case Sqlite3.close(db) do
221222
:ok -> :ok
@@ -513,7 +514,9 @@ defmodule Exqlite.Connection do
513514
end
514515

515516
defp set_busy_timeout(db, options) do
516-
set_pragma(db, "busy_timeout", Pragma.busy_timeout(options))
517+
# Use our NIF instead of PRAGMA busy_timeout, because PRAGMA internally
518+
# calls sqlite3_busy_timeout() which destroys our custom busy handler.
519+
Sqlite3.set_busy_timeout(db, Pragma.busy_timeout(options))
517520
end
518521

519522
defp deserialize(db, options) do

0 commit comments

Comments
 (0)