Skip to content

Raise ProgrammingError immediately on concurrent connection use instead of blocking#781

Merged
methane merged 10 commits into
mainfrom
copilot/modify-lightweight-lock-for-connection
Jun 10, 2026
Merged

Raise ProgrammingError immediately on concurrent connection use instead of blocking#781
methane merged 10 commits into
mainfrom
copilot/modify-lightweight-lock-for-connection

Conversation

Copilot AI commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Using the same Connection object from multiple threads simultaneously is unsupported and leads to undefined behavior. Previously, the C-level lock used WAIT_LOCK, which silently serialized concurrent calls rather than surfacing the bug to callers. This change switches to NOWAIT_LOCK so that any second thread that attempts to use a connection already locked by another thread receives a ProgrammingError immediately.

C extension (_mysql.c)

  • Unified lock implementation: free-thread and GIL builds both now use PyThread_type_lock (done in prior commit); the PyCriticalSection branch is gone.
  • _mysql_ConnectionObject_Lockint: uses NOWAIT_LOCK; sets ProgrammingError and returns -1 on contention.
  • _mysql_ConnectionObject_LockWait (new): blocking WAIT_LOCK variant used exclusively in _mysql_ResultObject_dealloc, where mysql_free_result() must be called regardless.
  • BEGIN_CONNECTION_OPERATION: propagates lock failure via return NULL before checking open.
  • Four direct BEGIN_CONNECTION_LOCK call sites updated to check the return value and propagate errors correctly (handles both PyObject * and int return contexts).

Tests (test_connection.py)

Replaced the two "operations are serialized" tests with tests that assert concurrent access raises ProgrammingError immediately:

# Thread A: conn.query("SELECT SLEEP(0.5)")  ← holds lock ~0.5 s
# Thread B (100 ms later):
with pytest.raises(ProgrammingError, match="already in use"):
    conn.thread_id()   # must fail instantly, not block

Both Connection method contention and Result.fetch_row contention are covered.

Copilot AI changed the title [WIP] Add lightweight lock on connection for thread safety Guard _mysql.connection with a per-connection lock on non-free-threaded CPython Jun 8, 2026
Copilot AI requested a review from methane June 8, 2026 16:20
Copilot AI changed the title Guard _mysql.connection with a per-connection lock on non-free-threaded CPython Unify connection locking on PyThread_type_lock across GIL and free-thread builds Jun 9, 2026
Copilot AI changed the title Unify connection locking on PyThread_type_lock across GIL and free-thread builds Raise ProgrammingError immediately on concurrent connection use instead of blocking Jun 9, 2026
Comment thread src/MySQLdb/_mysql.c Outdated
@methane methane marked this pull request as ready for review June 10, 2026 06:11
@methane methane merged commit 29a574b into main Jun 10, 2026
15 of 27 checks passed
@methane methane deleted the copilot/modify-lightweight-lock-for-connection branch June 10, 2026 06:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C extension bug / Uncaught in Python causing kernel to die

2 participants