@@ -60,23 +60,32 @@ class object "PyObject *" "&PyBaseObject_Type"
6060
6161#ifdef Py_GIL_DISABLED
6262
63- // There's a global lock for mutation of types. This avoids having to take
63+ // There's a global lock for types that ensures that the tp_version_tag is
64+ // correctly updated if the type is modified. This avoids having to take
6465// additional locks while doing various subclass processing which may result
6566// in odd behaviors w.r.t. running with the GIL as the outer type lock could
6667// be released and reacquired during a subclass update if there's contention
6768// on the subclass lock.
6869//
69- // While modification of type slots and type flags is protected by the
70- // global types lock. However, the slots and flags are read non-atomically
71- // without holding the type lock. So, we need to stop-the-world while
72- // modifying these, in order to avoid data races.
70+ // Note that this lock does not protect when updating type slots or the
71+ // tp_flags member. Instead, we either ensure those updates are done before
72+ // the type has been revealed to other threads or we only do those updates
73+ // while the stop-the-world mechanism is active. The slots and flags are read
74+ // in many places without holding a lock and without atomics.
75+ //
76+ // Since TYPE_LOCK is used as regular mutex, we must take special care about
77+ // potential re-entrant code paths. We use TYPE_LOCK_TID in debug builds to
78+ // ensure that we are not trying to re-acquire the mutex when it is already
79+ // held by the current thread. There are a couple cases when we release the
80+ // mutex, when we call functions that might re-enter.
7381#define TYPE_LOCK &PyInterpreterState_Get()->types.mutex
7482
75- // Used to check for correct use of the TYPE_LOCK mutex. It is a simple
83+ // Used to check for correct use of the TYPE_LOCK mutex. It is a regular
7684// mutex and does not support re-entrancy. If we already hold the lock and
7785// try to acquire it again with the same thread, it is a bug on the code.
7886#define TYPE_LOCK_TID &PyInterpreterState_Get()->types.mutex_tid
7987
88+ // Return true if the world is currently stopped.
8089static bool
8190types_world_is_stopped (void )
8291{
@@ -109,6 +118,7 @@ types_start_world(void)
109118}
110119
111120#ifdef Py_DEBUG
121+ // Return true if the TYPE_LOCK mutex is held by the current thread.
112122static bool
113123types_mutex_is_owned (void )
114124{
@@ -117,6 +127,7 @@ types_mutex_is_owned(void)
117127}
118128#endif
119129
130+ // Set the TID of the thread currently holding TYPE_LOCK.
120131static void
121132types_mutex_set_owned (PyThread_ident_t tid )
122133{
@@ -3401,6 +3412,7 @@ mro_invoke(PyTypeObject *type)
34013412 if (mro_result == NULL )
34023413 return NULL ;
34033414
3415+ // FIXME: possible re-entrancy, drop lock?
34043416 new_mro = PySequence_Tuple (mro_result );
34053417 Py_DECREF (mro_result );
34063418 if (new_mro == NULL ) {
@@ -4576,6 +4588,7 @@ type_new_impl(type_new_ctx *ctx)
45764588 }
45774589
45784590 assert (_PyType_CheckConsistency (type ));
4591+ // After this point, other threads can potentally use this type.
45794592 type -> tp_flags |= Py_TPFLAGS_EXPOSED ;
45804593
45814594 return (PyObject * )type ;
@@ -5290,6 +5303,7 @@ PyType_FromMetaclass(
52905303 }
52915304
52925305 assert (_PyType_CheckConsistency (type ));
5306+ // After this point, other threads can potentally use this type.
52935307 type -> tp_flags |= Py_TPFLAGS_EXPOSED ;
52945308
52955309 finally :
0 commit comments