@@ -46,6 +46,13 @@ class object "PyObject *" "&PyBaseObject_Type"
4646#define NEXT_VERSION_TAG (interp ) \
4747 (interp)->types.next_version_tag
4848
49+ // Storage for the mutexes saved by type_lock_prevent_release(). Defined for
50+ // both builds so the call sites don't need to be conditionally compiled.
51+ typedef struct {
52+ PyMutex * mutex1 ;
53+ PyMutex * mutex2 ;
54+ } pinned_mutexes_t ;
55+
4956#ifdef Py_GIL_DISABLED
5057
5158// There's a global lock for types that ensures that tp_version_tag and
@@ -134,44 +141,54 @@ types_start_world(void)
134141 assert (!types_world_is_stopped ());
135142}
136143
137- // This is used to temporarily prevent the TYPE_LOCK from being suspended
138- // when held by the topmost critical section.
144+ // Temporarily prevent the mutexes held by the topmost critical section from
145+ // being released when the current thread blocks (blocking detaches the thread,
146+ // which suspends its critical sections and releases the mutexes they hold).
147+ //
148+ // All of the mutexes held by the critical section are pinned, not just
149+ // TYPE_LOCK. If only TYPE_LOCK was pinned then _PyCriticalSection_Resume()
150+ // would have to re-acquire the other mutex while TYPE_LOCK is held. That
151+ // deadlocks against a thread that holds that mutex and is waiting for
152+ // TYPE_LOCK, which is exactly what BEGIN_TYPE_DICT_LOCK() does: the type dict
153+ // mutex is on the heap and TYPE_LOCK is in _PyRuntime, so the address ordering
154+ // used by two-mutex critical sections usually acquires the dict mutex first.
155+ // By pinning both mutexes there is nothing to re-acquire on resume.
156+ //
157+ // Holding the mutexes while blocked does not prevent the world from being
158+ // stopped: a thread waiting on either of them parks with _PY_LOCK_DETACH and
159+ // so is detached while it waits.
139160static void
140- type_lock_prevent_release (void )
161+ type_lock_prevent_release (pinned_mutexes_t * pinned )
141162{
142163 PyThreadState * tstate = _PyThreadState_GET ();
143- uintptr_t * tagptr = & tstate -> critical_section ;
144- PyCriticalSection * c = (PyCriticalSection * )(* tagptr & ~_Py_CRITICAL_SECTION_MASK );
145- if (!(* tagptr & _Py_CRITICAL_SECTION_TWO_MUTEXES )) {
146- assert (c -> _cs_mutex == TYPE_LOCK );
147- c -> _cs_mutex = NULL ;
148- }
149- else {
164+ uintptr_t tag = tstate -> critical_section ;
165+ PyCriticalSection * c = (PyCriticalSection * )(tag & ~_Py_CRITICAL_SECTION_MASK );
166+ pinned -> mutex1 = c -> _cs_mutex ;
167+ pinned -> mutex2 = NULL ;
168+ c -> _cs_mutex = NULL ;
169+ if ((tag & _Py_CRITICAL_SECTION_TWO_MUTEXES ) != 0 ) {
150170 PyCriticalSection2 * c2 = (PyCriticalSection2 * )c ;
151- if (c -> _cs_mutex == TYPE_LOCK ) {
152- c -> _cs_mutex = c2 -> _cs_mutex2 ;
153- c2 -> _cs_mutex2 = NULL ;
154- } else {
155- assert (c2 -> _cs_mutex2 == TYPE_LOCK );
156- c2 -> _cs_mutex2 = NULL ;
157- }
171+ pinned -> mutex2 = c2 -> _cs_mutex2 ;
172+ c2 -> _cs_mutex2 = NULL ;
158173 }
174+ assert (pinned -> mutex1 == TYPE_LOCK || pinned -> mutex2 == TYPE_LOCK );
159175}
160176
161177static void
162- type_lock_allow_release (void )
178+ type_lock_allow_release (pinned_mutexes_t * pinned )
163179{
164180 PyThreadState * tstate = _PyThreadState_GET ();
165- uintptr_t * tagptr = & tstate -> critical_section ;
166- PyCriticalSection * c = (PyCriticalSection * )(* tagptr & ~_Py_CRITICAL_SECTION_MASK );
167- if (!(* tagptr & _Py_CRITICAL_SECTION_TWO_MUTEXES )) {
168- assert (c -> _cs_mutex == NULL );
169- c -> _cs_mutex = TYPE_LOCK ;
170- }
171- else {
181+ uintptr_t tag = tstate -> critical_section ;
182+ PyCriticalSection * c = (PyCriticalSection * )(tag & ~_Py_CRITICAL_SECTION_MASK );
183+ assert (c -> _cs_mutex == NULL );
184+ c -> _cs_mutex = pinned -> mutex1 ;
185+ if ((tag & _Py_CRITICAL_SECTION_TWO_MUTEXES ) != 0 ) {
172186 PyCriticalSection2 * c2 = (PyCriticalSection2 * )c ;
173187 assert (c2 -> _cs_mutex2 == NULL );
174- c2 -> _cs_mutex2 = TYPE_LOCK ;
188+ c2 -> _cs_mutex2 = pinned -> mutex2 ;
189+ }
190+ else {
191+ assert (pinned -> mutex2 == NULL );
175192 }
176193}
177194
@@ -189,8 +206,8 @@ type_lock_allow_release(void)
189206#define types_world_is_stopped () 1
190207#define types_stop_world ()
191208#define types_start_world ()
192- #define type_lock_prevent_release ()
193- #define type_lock_allow_release ()
209+ #define type_lock_prevent_release (pinned ) ((void)(pinned) )
210+ #define type_lock_allow_release (pinned ) ((void)(pinned) )
194211
195212#endif
196213
@@ -661,14 +678,15 @@ set_tp_mro(PyTypeObject *self, PyObject *mro, int initial)
661678 PyUnstable_Object_EnableDeferredRefcount (mro );
662679 }
663680 }
681+ pinned_mutexes_t pinned ;
664682 if (!initial ) {
665- type_lock_prevent_release ();
683+ type_lock_prevent_release (& pinned );
666684 types_stop_world ();
667685 }
668686 self -> tp_mro = mro ;
669687 if (!initial ) {
670688 types_start_world ();
671- type_lock_allow_release ();
689+ type_lock_allow_release (& pinned );
672690 }
673691}
674692
@@ -1894,13 +1912,14 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, PyTypeObject *b
18941912 PyObject * old_bases = lookup_tp_bases (type );
18951913 assert (old_bases != NULL );
18961914 PyTypeObject * old_base = type -> tp_base ;
1915+ pinned_mutexes_t pinned ;
18971916
1898- type_lock_prevent_release ();
1917+ type_lock_prevent_release (& pinned );
18991918 types_stop_world ();
19001919 set_tp_bases (type , Py_NewRef (new_bases ), 0 );
19011920 type -> tp_base = (PyTypeObject * )Py_NewRef (best_base );
19021921 types_start_world ();
1903- type_lock_allow_release ();
1922+ type_lock_allow_release (& pinned );
19041923
19051924 PyObject * temp = PyList_New (0 );
19061925 if (temp == NULL ) {
@@ -1961,12 +1980,12 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, PyTypeObject *b
19611980 if (lookup_tp_bases (type ) == new_bases ) {
19621981 assert (type -> tp_base == best_base );
19631982
1964- type_lock_prevent_release ();
1983+ type_lock_prevent_release (& pinned );
19651984 types_stop_world ();
19661985 set_tp_bases (type , old_bases , 0 );
19671986 type -> tp_base = old_base ;
19681987 types_start_world ();
1969- type_lock_allow_release ();
1988+ type_lock_allow_release (& pinned );
19701989
19711990 Py_DECREF (new_bases );
19721991 Py_DECREF (best_base );
@@ -3874,16 +3893,22 @@ apply_type_slot_updates(slot_update_t *updates)
38743893 // to update the dict. That's because TYPE_LOCK was acquired using a
38753894 // critical section.
38763895 //
3877- // The type_lock_prevent_release() call prevents the TYPE_LOCK mutex from
3878- // being released even if we block on the STM mutex. We need to take care
3879- // that we do not deadlock because of that. It is safe because we always
3880- // acquire locks in the same order: first the TYPE_LOCK mutex and then the
3881- // STM mutex.
3882- type_lock_prevent_release ();
3896+ // The type_lock_prevent_release() call prevents the mutexes held by the
3897+ // critical section (TYPE_LOCK and the type dict mutex) from being released
3898+ // even if we block on the STW mutex. We need to take care that we do not
3899+ // deadlock because of that. It is safe because a thread waiting for either
3900+ // of those mutexes detaches while it waits and so does not hold up the
3901+ // stop-the-world. Pinning both mutexes rather than only TYPE_LOCK is what
3902+ // makes this safe: otherwise the dict mutex would be released when we
3903+ // block and _PyCriticalSection_Resume() would have to re-acquire it while
3904+ // holding TYPE_LOCK, deadlocking with a thread that holds the dict mutex
3905+ // and is waiting for TYPE_LOCK.
3906+ pinned_mutexes_t pinned ;
3907+ type_lock_prevent_release (& pinned );
38833908 types_stop_world ();
38843909 apply_slot_updates (updates );
38853910 types_start_world ();
3886- type_lock_allow_release ();
3911+ type_lock_allow_release (& pinned );
38873912}
38883913
38893914#else
@@ -6404,11 +6429,12 @@ _PyType_SetFlagsRecursive(PyTypeObject *self, unsigned long mask, unsigned long
64046429 }
64056430 /* Keep TYPE_LOCK held while waiting for stop-the-world so no thread
64066431 can reassign a version tag before the flag update. */
6407- type_lock_prevent_release ();
6432+ pinned_mutexes_t pinned ;
6433+ type_lock_prevent_release (& pinned );
64086434 types_stop_world ();
64096435 set_flags_recursive (self , mask , flags );
64106436 types_start_world ();
6411- type_lock_allow_release ();
6437+ type_lock_allow_release (& pinned );
64126438 END_TYPE_LOCK ();
64136439}
64146440
0 commit comments