@@ -81,23 +81,13 @@ types_world_is_stopped(void)
8181#endif
8282
8383// Checks that the type has not yet been revealed (exposed) to other
84- // threads. The _Py_TYPE_REVEALED_FLAG flag is set by type_ready_publish(),
85- // just before the type is added to the subclasses of its bases. That is the
86- // point where other threads can find it. The flag only exists on 64-bit
87- // platforms (we only have ob_flags there) and in debug builds.
88- //
89- // The flag is set with the type lock held and it is never cleared. A thread
90- // that can see the type has either found it through the subclasses of its
91- // bases, which requires the type lock, or the world is stopped. So, plain
92- // loads and stores are enough here.
93- #if defined(Py_DEBUG ) && SIZEOF_VOID_P > 4
94- #define TYPE_IS_REVEALED (tp ) \
95- ((((PyObject *)(tp))->ob_flags & _Py_TYPE_REVEALED_FLAG) != 0)
96- #define TYPE_SET_REVEALED (tp ) \
97- ((void)(((PyObject *)(tp))->ob_flags |= _Py_TYPE_REVEALED_FLAG))
84+ // threads. The _Py_TYPE_REVEALED_FLAG flag is set by type_new() and
85+ // PyType_FromMetaclass() to indicate that a newly initialized type might be
86+ // revealed. We only have ob_flags on 64-bit platforms.
87+ #if SIZEOF_VOID_P > 4
88+ #define TYPE_IS_REVEALED (tp ) ((((PyObject *)(tp))->ob_flags & _Py_TYPE_REVEALED_FLAG) != 0)
9889#else
9990#define TYPE_IS_REVEALED (tp ) 0
100- #define TYPE_SET_REVEALED (tp ) ((void)0)
10191#endif
10292
10393#ifdef Py_DEBUG
@@ -183,7 +173,6 @@ type_lock_allow_release(void)
183173#define END_TYPE_DICT_LOCK ()
184174#define ASSERT_TYPE_LOCK_HELD ()
185175#define TYPE_IS_REVEALED (tp ) 0
186- #define TYPE_SET_REVEALED (tp ) ((void)0)
187176#define ASSERT_WORLD_STOPPED_OR_NEW_TYPE (tp )
188177#define ASSERT_NEW_TYPE_OR_LOCKED (tp )
189178#define types_world_is_stopped () 1
@@ -3902,9 +3891,9 @@ static void object_dealloc(PyObject *);
39023891static PyObject * object_new (PyTypeObject * , PyObject * , PyObject * );
39033892static int object_init (PyObject * , PyObject * , PyObject * );
39043893static int update_slot (PyTypeObject * , PyObject * , slot_update_t * update );
3905- static int fixup_slot_dispatchers (PyTypeObject * );
3894+ static void fixup_slot_dispatchers (PyTypeObject * );
39063895static int type_ready (PyTypeObject * , int , int );
3907- static int type_ready_publish (PyTypeObject * );
3896+ static int type_ready_publish (PyTypeObject * , int );
39083897static int type_new_set_names (PyTypeObject * );
39093898static int type_new_init_subclass (PyTypeObject * , PyObject * );
39103899static bool has_slotdef (PyObject * );
@@ -4910,27 +4899,8 @@ type_new_impl(type_new_ctx *ctx)
49104899 goto error ;
49114900 }
49124901
4913- /* Initialize the rest, put the proper slots in place and only then
4914- publish the type as a subclass of its bases. Since the type is not
4915- reachable by other threads before it is published, the slots can be
4916- updated without stopping the world.
4917-
4918- All of it is done with the type lock held. Otherwise a thread that
4919- assigns to a special method of a base could look for the subclasses to
4920- update after we have looked up the special methods in the bases but
4921- before the type is published, and the type would be left with a stale
4922- slot. */
4923- int res ;
4924- BEGIN_TYPE_LOCK ();
4925- res = type_ready (type , 1 , 0 );
4926- if (res == 0 ) {
4927- res = fixup_slot_dispatchers (type );
4928- }
4929- if (res == 0 ) {
4930- res = type_ready_publish (type );
4931- }
4932- END_TYPE_LOCK ();
4933- if (res < 0 ) {
4902+ /* Initialize the rest */
4903+ if (type_ready_publish (type , 1 ) < 0 ) {
49344904 goto error ;
49354905 }
49364906
@@ -5694,16 +5664,7 @@ type_from_slots_or_spec(
56945664 * After this call we should generally only touch up what's
56955665 * accessible to Python code, like __dict__.
56965666 */
5697-
5698- BEGIN_TYPE_LOCK ();
5699- r = type_ready (type , 1 , 0 );
5700- if (r == 0 ) {
5701- /* The type is only revealed to other threads once it is fully
5702- initialized. */
5703- r = type_ready_publish (type );
5704- }
5705- END_TYPE_LOCK ();
5706- if (r < 0 ) {
5667+ if (type_ready_publish (type , 0 ) < 0 ) {
57075668 goto finally ;
57085669 }
57095670
@@ -9506,28 +9467,43 @@ type_ready(PyTypeObject *type, int initial, int add_subclasses)
95069467}
95079468
95089469static int
9509- type_ready_publish (PyTypeObject * type )
9470+ type_ready_publish (PyTypeObject * type , int fix_slots )
95109471{
9511- ASSERT_TYPE_LOCK_HELD ();
9512- assert (!(type -> tp_flags & Py_TPFLAGS_READY ));
9513- assert (!is_readying (type ));
9514-
9515- /* Set the ready flag before revealing the type since type_add_flags()
9516- may only be used on types that are not yet revealed. */
9517- type_add_flags (type , Py_TPFLAGS_READY );
9518-
9519- /* Mark the type as revealed while still holding the type lock. Threads
9520- can only find the type through the subclasses of its bases, which is
9521- done below with the lock held. So, they cannot see the type before
9522- the flag is set. */
9523- TYPE_SET_REVEALED (type );
9472+ int res ;
9473+ BEGIN_TYPE_LOCK ();
9474+ res = type_ready (type , 1 , 0 );
9475+ if (res == 0 ) {
9476+ assert (!(type -> tp_flags & Py_TPFLAGS_READY ));
9477+ assert (!is_readying (type ));
9478+
9479+ if (fix_slots ) {
9480+ // Put the proper slots in place and only then publish the type as
9481+ // a subclass of its bases. Since the type is not reachable by
9482+ // other threads before it is published, the slots can be updated
9483+ // without stopping the world. This step is skipped for
9484+ // type_from_slots_or_spec().
9485+ fixup_slot_dispatchers (type );
9486+ }
9487+
9488+ // Set the ready flag before revealing the type since type_add_flags()
9489+ // may only be used on types that are not yet revealed.
9490+ type_add_flags (type , Py_TPFLAGS_READY );
9491+
9492+ #if defined(Py_GIL_DISABLED ) && defined(Py_DEBUG ) && SIZEOF_VOID_P > 4
9493+ // Mark the type as revealed while still holding the type lock.
9494+ // Threads can only find the type through the subclasses of its bases,
9495+ // which is done below with the lock held. So, they cannot see the
9496+ // type before the flag is set.
9497+ ((PyObject * )type )-> ob_flags |= _Py_TYPE_REVEALED_FLAG ;
9498+ #endif
95249499
9525- if (type_ready_add_subclasses (type ) < 0 ) {
9526- return -1 ;
9500+ res = type_ready_add_subclasses (type );
9501+ if (res == 0 ) {
9502+ assert (_PyType_CheckConsistency (type ));
9503+ }
95279504 }
9528-
9529- assert (_PyType_CheckConsistency (type ));
9530- return 0 ;
9505+ END_TYPE_LOCK ();
9506+ return res ;
95319507}
95329508
95339509int
@@ -12052,18 +12028,17 @@ update_slot(PyTypeObject *type, PyObject *name, slot_update_t *queued_updates)
1205212028 definition time, based upon which operations the class overrides in its
1205312029 dict. The type must not be revealed to other threads yet, so that the
1205412030 slots can be updated directly rather than with the world stopped. */
12055- static int
12031+ static void
1205612032fixup_slot_dispatchers (PyTypeObject * type )
1205712033{
1205812034 ASSERT_TYPE_LOCK_HELD ();
1205912035 ASSERT_WORLD_STOPPED_OR_NEW_TYPE (type );
1206012036 assert (!PyErr_Occurred ());
1206112037 for (pytype_slotdef * p = slotdefs ; p -> name ; ) {
12062- if ( update_one_slot (type , p , & p , NULL ) < 0 ) {
12063- return -1 ;
12064- }
12038+ int rv = update_one_slot (type , p , & p , NULL );
12039+ // always returns 0 if queued_updates == NULL
12040+ assert ( rv == 0 );
1206512041 }
12066- return 0 ;
1206712042}
1206812043
1206912044#ifdef Py_GIL_DISABLED
0 commit comments