From b38230bcf1aaa831950455fbb81f90d902446d00 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 16 Jul 2026 11:44:35 -0700 Subject: [PATCH] Add comments about _Py_LOCK_DONT_DETACH usage. The need for _Py_LOCK_DONT_DETACH in intern_common() is not so obvious. Add some code comments explaining why we use that flag. Also, add a comment to the flag noting that you need extra care when writing code that holds a mutex with this flag set. --- Include/internal/pycore_lock.h | 5 +++++ Objects/unicodeobject.c | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/Include/internal/pycore_lock.h b/Include/internal/pycore_lock.h index e31d8b4e5c68c9..13224a0572934b 100644 --- a/Include/internal/pycore_lock.h +++ b/Include/internal/pycore_lock.h @@ -34,6 +34,11 @@ _PyMutex_at_fork_reinit(PyMutex *m) typedef enum _PyLockFlags { // Do not detach/release the GIL when waiting on the lock. + // + // Note that code executed while holding a mutex with this flag must + // not detach, reach a safepoint or initiate a stop-the-world pause. + // Otherwise, a non-detaching waiter may remain waiting for this mutex and + // prevent the pause from completing. _Py_LOCK_DONT_DETACH = 0, // Detach/release the GIL while waiting on the lock. diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 6562546ae2f9cc..6bb12601473825 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -14722,6 +14722,15 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */, } #endif + // Why _Py_LOCK_DONT_DETACH is used here: waiting for the interned mutex + // must not detach the thread state. Extension code is expected to + // detach before blocking on opaque external synchronization. However, + // the lock used for C++ static initialization is hidden, making + // that difficult, and it is common for C++ extensions to call + // PyUnicode_InternFromString() from static initializers. Detaching here + // can therefore deadlock: a stop-the-world pause may prevent the lock + // owner from reattaching while the pause waits for another attached + // thread blocked on the hidden lock. FT_MUTEX_LOCK_FLAGS(INTERN_MUTEX, _Py_LOCK_DONT_DETACH); PyObject *t; {