@@ -60,6 +60,13 @@ class object "PyObject *" "&PyBaseObject_Type"
6060#define NEXT_VERSION_TAG (interp ) \
6161 (interp)->types.next_version_tag
6262
63+ // Storage for the mutexes saved by type_lock_prevent_release(). Defined for
64+ // both builds so the call sites don't need to be conditionally compiled.
65+ typedef struct {
66+ PyMutex * mutex1 ;
67+ PyMutex * mutex2 ;
68+ } pinned_mutexes_t ;
69+
6370#ifdef Py_GIL_DISABLED
6471
6572// There's a global lock for types that ensures that tp_version_tag and
@@ -138,44 +145,54 @@ types_start_world(void)
138145 assert (!types_world_is_stopped ());
139146}
140147
141- // This is used to temporarily prevent the TYPE_LOCK from being suspended
142- // when held by the topmost critical section.
148+ // Temporarily prevent the mutexes held by the topmost critical section from
149+ // being released when the current thread blocks (blocking detaches the thread,
150+ // which suspends its critical sections and releases the mutexes they hold).
151+ //
152+ // All of the mutexes held by the critical section are pinned, not just
153+ // TYPE_LOCK. If only TYPE_LOCK was pinned then _PyCriticalSection_Resume()
154+ // would have to re-acquire the other mutex while TYPE_LOCK is held. That
155+ // deadlocks against a thread that holds that mutex and is waiting for
156+ // TYPE_LOCK, which is exactly what BEGIN_TYPE_DICT_LOCK() does: the type dict
157+ // mutex is on the heap and TYPE_LOCK is in _PyRuntime, so the address ordering
158+ // used by two-mutex critical sections usually acquires the dict mutex first.
159+ // By pinning both mutexes there is nothing to re-acquire on resume.
160+ //
161+ // Holding the mutexes while blocked does not prevent the world from being
162+ // stopped: a thread waiting on either of them parks with _PY_LOCK_DETACH and
163+ // so is detached while it waits.
143164static void
144- type_lock_prevent_release (void )
165+ type_lock_prevent_release (pinned_mutexes_t * pinned )
145166{
146167 PyThreadState * tstate = _PyThreadState_GET ();
147- uintptr_t * tagptr = & tstate -> critical_section ;
148- PyCriticalSection * c = (PyCriticalSection * )(* tagptr & ~_Py_CRITICAL_SECTION_MASK );
149- if (!(* tagptr & _Py_CRITICAL_SECTION_TWO_MUTEXES )) {
150- assert (c -> _cs_mutex == TYPE_LOCK );
151- c -> _cs_mutex = NULL ;
152- }
153- else {
168+ uintptr_t tag = tstate -> critical_section ;
169+ PyCriticalSection * c = (PyCriticalSection * )(tag & ~_Py_CRITICAL_SECTION_MASK );
170+ pinned -> mutex1 = c -> _cs_mutex ;
171+ pinned -> mutex2 = NULL ;
172+ c -> _cs_mutex = NULL ;
173+ if ((tag & _Py_CRITICAL_SECTION_TWO_MUTEXES ) != 0 ) {
154174 PyCriticalSection2 * c2 = (PyCriticalSection2 * )c ;
155- if (c -> _cs_mutex == TYPE_LOCK ) {
156- c -> _cs_mutex = c2 -> _cs_mutex2 ;
157- c2 -> _cs_mutex2 = NULL ;
158- } else {
159- assert (c2 -> _cs_mutex2 == TYPE_LOCK );
160- c2 -> _cs_mutex2 = NULL ;
161- }
175+ pinned -> mutex2 = c2 -> _cs_mutex2 ;
176+ c2 -> _cs_mutex2 = NULL ;
162177 }
178+ assert (pinned -> mutex1 == TYPE_LOCK || pinned -> mutex2 == TYPE_LOCK );
163179}
164180
165181static void
166- type_lock_allow_release (void )
182+ type_lock_allow_release (pinned_mutexes_t * pinned )
167183{
168184 PyThreadState * tstate = _PyThreadState_GET ();
169- uintptr_t * tagptr = & tstate -> critical_section ;
170- PyCriticalSection * c = (PyCriticalSection * )(* tagptr & ~_Py_CRITICAL_SECTION_MASK );
171- if (!(* tagptr & _Py_CRITICAL_SECTION_TWO_MUTEXES )) {
172- assert (c -> _cs_mutex == NULL );
173- c -> _cs_mutex = TYPE_LOCK ;
174- }
175- else {
185+ uintptr_t tag = tstate -> critical_section ;
186+ PyCriticalSection * c = (PyCriticalSection * )(tag & ~_Py_CRITICAL_SECTION_MASK );
187+ assert (c -> _cs_mutex == NULL );
188+ c -> _cs_mutex = pinned -> mutex1 ;
189+ if ((tag & _Py_CRITICAL_SECTION_TWO_MUTEXES ) != 0 ) {
176190 PyCriticalSection2 * c2 = (PyCriticalSection2 * )c ;
177191 assert (c2 -> _cs_mutex2 == NULL );
178- c2 -> _cs_mutex2 = TYPE_LOCK ;
192+ c2 -> _cs_mutex2 = pinned -> mutex2 ;
193+ }
194+ else {
195+ assert (pinned -> mutex2 == NULL );
179196 }
180197}
181198
@@ -192,8 +209,8 @@ type_lock_allow_release(void)
192209#define types_world_is_stopped () 1
193210#define types_stop_world ()
194211#define types_start_world ()
195- #define type_lock_prevent_release ()
196- #define type_lock_allow_release ()
212+ #define type_lock_prevent_release (pinned ) ((void)(pinned) )
213+ #define type_lock_allow_release (pinned ) ((void)(pinned) )
197214
198215#endif
199216
@@ -664,14 +681,15 @@ set_tp_mro(PyTypeObject *self, PyObject *mro, int initial)
664681 PyUnstable_Object_EnableDeferredRefcount (mro );
665682 }
666683 }
684+ pinned_mutexes_t pinned ;
667685 if (!initial ) {
668- type_lock_prevent_release ();
686+ type_lock_prevent_release (& pinned );
669687 types_stop_world ();
670688 }
671689 self -> tp_mro = mro ;
672690 if (!initial ) {
673691 types_start_world ();
674- type_lock_allow_release ();
692+ type_lock_allow_release (& pinned );
675693 }
676694}
677695
@@ -1952,13 +1970,14 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, PyTypeObject *b
19521970 PyObject * old_bases = lookup_tp_bases (type );
19531971 assert (old_bases != NULL );
19541972 PyTypeObject * old_base = type -> tp_base ;
1973+ pinned_mutexes_t pinned ;
19551974
1956- type_lock_prevent_release ();
1975+ type_lock_prevent_release (& pinned );
19571976 types_stop_world ();
19581977 set_tp_bases (type , Py_NewRef (new_bases ), 0 );
19591978 type -> tp_base = (PyTypeObject * )Py_NewRef (best_base );
19601979 types_start_world ();
1961- type_lock_allow_release ();
1980+ type_lock_allow_release (& pinned );
19621981
19631982 PyObject * temp = PyList_New (0 );
19641983 if (temp == NULL ) {
@@ -2019,12 +2038,12 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, PyTypeObject *b
20192038 if (lookup_tp_bases (type ) == new_bases ) {
20202039 assert (type -> tp_base == best_base );
20212040
2022- type_lock_prevent_release ();
2041+ type_lock_prevent_release (& pinned );
20232042 types_stop_world ();
20242043 set_tp_bases (type , old_bases , 0 );
20252044 type -> tp_base = old_base ;
20262045 types_start_world ();
2027- type_lock_allow_release ();
2046+ type_lock_allow_release (& pinned );
20282047
20292048 Py_DECREF (new_bases );
20302049 Py_DECREF (best_base );
@@ -3932,16 +3951,22 @@ apply_type_slot_updates(slot_update_t *updates)
39323951 // to update the dict. That's because TYPE_LOCK was acquired using a
39333952 // critical section.
39343953 //
3935- // The type_lock_prevent_release() call prevents the TYPE_LOCK mutex from
3936- // being released even if we block on the STM mutex. We need to take care
3937- // that we do not deadlock because of that. It is safe because we always
3938- // acquire locks in the same order: first the TYPE_LOCK mutex and then the
3939- // STM mutex.
3940- type_lock_prevent_release ();
3954+ // The type_lock_prevent_release() call prevents the mutexes held by the
3955+ // critical section (TYPE_LOCK and the type dict mutex) from being released
3956+ // even if we block on the STW mutex. We need to take care that we do not
3957+ // deadlock because of that. It is safe because a thread waiting for either
3958+ // of those mutexes detaches while it waits and so does not hold up the
3959+ // stop-the-world. Pinning both mutexes rather than only TYPE_LOCK is what
3960+ // makes this safe: otherwise the dict mutex would be released when we
3961+ // block and _PyCriticalSection_Resume() would have to re-acquire it while
3962+ // holding TYPE_LOCK, deadlocking with a thread that holds the dict mutex
3963+ // and is waiting for TYPE_LOCK.
3964+ pinned_mutexes_t pinned ;
3965+ type_lock_prevent_release (& pinned );
39413966 types_stop_world ();
39423967 apply_slot_updates (updates );
39433968 types_start_world ();
3944- type_lock_allow_release ();
3969+ type_lock_allow_release (& pinned );
39453970}
39463971
39473972#else
@@ -6526,11 +6551,12 @@ _PyType_SetFlagsRecursive(PyTypeObject *self, unsigned long mask, unsigned long
65266551 }
65276552 /* Keep TYPE_LOCK held while waiting for stop-the-world so no thread
65286553 can reassign a version tag before the flag update. */
6529- type_lock_prevent_release ();
6554+ pinned_mutexes_t pinned ;
6555+ type_lock_prevent_release (& pinned );
65306556 types_stop_world ();
65316557 set_flags_recursive (self , mask , flags );
65326558 types_start_world ();
6533- type_lock_allow_release ();
6559+ type_lock_allow_release (& pinned );
65346560 END_TYPE_LOCK ();
65356561}
65366562
0 commit comments