Skip to content

gh-161065: Fix sqlite3 deadlock on connections shared between threads - #161066

Open
gpshead wants to merge 3 commits into
python:mainfrom
gpshead:sqlite3-gil-db-mutex-deadlock
Open

gpshead wants to merge 3 commits into
python:mainfrom
gpshead:sqlite3-gil-db-mutex-deadlock

Conversation

@gpshead

@gpshead gpshead commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

SQLite runs Python callbacks (functions, aggregates, collations, authorizer, progress and trace handlers) from inside sqlite3_step() with the connection mutex held, and the callback then takes the GIL. The module also made other SQLite calls that take the same mutex while holding the GIL, so two threads sharing a connection could take the two locks in opposite orders and deadlock.

The fix is one rule: a thread never blocks on the connection mutex while holding the GIL. New helpers in util.h try the mutex first and release the GIL to wait for it only when the try fails, so the uncontended case is unchanged. They wrap the SQLite calls made with the GIL held that may take the mutex; which calls do varies by SQLite version, and 3.53 takes it in more of them. The mutex is recursive, so SQLite calls made under it re-enter it, it is NULL (a no-op) unless SQLite is in serialized mode, and it is never held while calling into Python.

Sharing one cached statement between threads (gh-118172, gh-146471) is a separate problem, not addressed here. The sqlite3_mutex_*() functions are missing from an SQLite built with SQLITE_THREADSAFE=0, such as the Emscripten port, so configure checks for them and the helpers do nothing without them; such a build cannot be in serialized mode.

Fixes #161065

ANT-2026-R7FMVV3W

…hreads

SQLite calls Python callbacks with the connection mutex held, so never
block on that mutex while holding the GIL: enter it first, releasing
the GIL only if it is contended.
An SQLite built with SQLITE_THREADSAFE=0, such as the Emscripten port,
does not export sqlite3_mutex_enter(), sqlite3_mutex_try() or
sqlite3_mutex_leave().
@gpshead

This comment was marked as resolved.

SQLite 3.53, which the Windows build bundles, also takes the connection
mutex in sqlite3_limit(), sqlite3_get_autocommit(), sqlite3_changes(),
sqlite3_total_changes() and sqlite3_errcode().
@gpshead gpshead moved this to In Progress in Threading issues 🧵 Nov 1, 2026
@gpshead
gpshead marked this pull request as ready for review November 1, 2026 21:22
@gpshead
gpshead requested review from a team and erlend-aasland as code owners November 1, 2026 21:22
@gpshead

gpshead commented Nov 1, 2026

Copy link
Copy Markdown
Member Author

This could use review from anyone who knows anything about sqlite3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

sqlite3: deadlock when threads share a connection that has Python callbacks

1 participant