@@ -1687,7 +1687,7 @@ static int add_subclass(PyTypeObject*, PyTypeObject*);
16871687static int add_all_subclasses (PyTypeObject * type , PyObject * bases );
16881688static void remove_subclass (PyTypeObject * , PyTypeObject * );
16891689static void remove_all_subclasses (PyTypeObject * type , PyObject * bases );
1690- static void update_all_slots (PyTypeObject * );
1690+ static int update_all_slots (PyTypeObject * );
16911691
16921692typedef int (* update_callback )(PyTypeObject * , void * );
16931693static int update_subclasses (PyTypeObject * type , PyObject * attr_name ,
@@ -1862,10 +1862,9 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, PyTypeObject *b
18621862 add to all new_bases */
18631863 remove_all_subclasses (type , old_bases );
18641864 res = add_all_subclasses (type , new_bases );
1865- types_stop_world ();
1866- update_all_slots (type );
1867- types_start_world ();
1868- ASSERT_TYPE_LOCK_HELD ();
1865+ if (update_all_slots (type ) < 0 ) {
1866+ goto bail ;
1867+ }
18691868 }
18701869 else {
18711870 res = 0 ;
@@ -3690,10 +3689,127 @@ solid_base(PyTypeObject *type)
36903689 }
36913690}
36923691
3692+ #ifdef Py_GIL_DISABLED
3693+
3694+ // The structures and functions below are used in the free-threaded build
3695+ // to safely make updates to type slots, when __bases__ is re-assigned. Since
3696+ // the slots are read without atomic operations and without locking, we can
3697+ // only safely update them while the world is stopped. However, with the
3698+ // world stopped, we are very limited on which APIs can be safely used. For
3699+ // example, calling _PyObject_HashFast() or _PyDict_GetItemRef_KnownHash() are
3700+ // not safe and can potentially cause deadlocks. Hashing can be re-entrant
3701+ // and _PyDict_GetItemRef_KnownHash can acquire a lock if the dictionary is
3702+ // not owned by the current thread, to mark it shared on reading.
3703+ //
3704+ // We do the slot updates in two steps. First, with TYPE_LOCK held, we lookup
3705+ // the descriptor for each slot, for each subclass. We build a queue of
3706+ // updates to perform but don't actually update the type structures. After we
3707+ // are finished the lookups, we stop-the-world and apply all of the updates.
3708+ // The apply_slot_updates() code is simple and easy to confirm that it is
3709+ // safe.
3710+
3711+ typedef struct {
3712+ void * * slot_ptr ;
3713+ void * slot_value ;
3714+ } slot_update_item_t ;
3715+
3716+ // The number of slot updates performed is based on the number of changed
3717+ // slots and the number of subclasses. It's possible there are many updates
3718+ // required if there are many subclasses (potentially an unbounded amount).
3719+ // Usually the number of slot updates is small, most often zero or one. When
3720+ // running the unit tests, we don't exceed 20. The chunk size is set to
3721+ // handle the common case with a single chunk and to not require too many
3722+ // chunk allocations if there are many subclasses.
3723+ #define SLOT_UPDATE_CHUNK_SIZE 30
3724+
3725+ typedef struct _slot_update {
3726+ struct _slot_update * prev ;
3727+ Py_ssize_t n ;
3728+ slot_update_item_t updates [SLOT_UPDATE_CHUNK_SIZE ];
3729+ } slot_update_chunk_t ;
3730+
3731+ // a queue of updates to be performed
3732+ typedef struct {
3733+ slot_update_chunk_t * head ;
3734+ } slot_update_t ;
3735+
3736+ static slot_update_chunk_t *
3737+ slot_update_new_chunk (void )
3738+ {
3739+ slot_update_chunk_t * chunk = PyMem_Malloc (sizeof (slot_update_chunk_t ));
3740+ if (chunk == NULL ) {
3741+ PyErr_NoMemory ();
3742+ return NULL ;
3743+ }
3744+ chunk -> prev = NULL ;
3745+ chunk -> n = 0 ;
3746+ return chunk ;
3747+ }
3748+
3749+ static void
3750+ slot_update_free_chunks (slot_update_t * updates )
3751+ {
3752+ slot_update_chunk_t * chunk = updates -> head ;
3753+ while (chunk != NULL ) {
3754+ slot_update_chunk_t * prev = chunk -> prev ;
3755+ PyMem_Free (chunk );
3756+ chunk = prev ;
3757+ }
3758+ }
3759+
3760+ static int
3761+ queue_slot_update (slot_update_t * updates , void * * slot_ptr , void * slot_value )
3762+ {
3763+ if (* slot_ptr == slot_value ) {
3764+ return 0 ; // slot pointer not actually changed, don't queue update
3765+ }
3766+ if (updates -> head == NULL || updates -> head -> n == SLOT_UPDATE_CHUNK_SIZE ) {
3767+ slot_update_chunk_t * chunk = slot_update_new_chunk ();
3768+ if (chunk == NULL ) {
3769+ return -1 ; // out-of-memory
3770+ }
3771+ chunk -> prev = updates -> head ;
3772+ updates -> head = chunk ;
3773+ }
3774+ slot_update_item_t * item = & updates -> head -> updates [updates -> head -> n ];
3775+ item -> slot_ptr = slot_ptr ;
3776+ item -> slot_value = slot_value ;
3777+ updates -> head -> n ++ ;
3778+ assert (updates -> head -> n <= SLOT_UPDATE_CHUNK_SIZE );
3779+ return 0 ;
3780+ }
3781+
3782+ static void
3783+ apply_slot_updates (slot_update_t * updates )
3784+ {
3785+ assert (types_world_is_stopped ());
3786+ slot_update_chunk_t * chunk = updates -> head ;
3787+ while (chunk != NULL ) {
3788+ for (Py_ssize_t i = 0 ; i < chunk -> n ; i ++ ) {
3789+ slot_update_item_t * item = & chunk -> updates [i ];
3790+ * (item -> slot_ptr ) = item -> slot_value ;
3791+ }
3792+ chunk = chunk -> prev ;
3793+ }
3794+ }
3795+
3796+ #else
3797+
3798+ // not used, slot updates are applied immediately
3799+ typedef struct {} slot_update_t ;
3800+
3801+ #endif
3802+
3803+ /// data passed to update_slots_callback()
3804+ typedef struct {
3805+ slot_update_t * queued_updates ;
3806+ pytype_slotdef * * defs ;
3807+ } update_callback_data_t ;
3808+
36933809static void object_dealloc (PyObject * );
36943810static PyObject * object_new (PyTypeObject * , PyObject * , PyObject * );
36953811static int object_init (PyObject * , PyObject * , PyObject * );
3696- static int update_slot (PyTypeObject * , PyObject * );
3812+ static int update_slot (PyTypeObject * , PyObject * , slot_update_t * update );
36973813static void fixup_slot_dispatchers (PyTypeObject * );
36983814static int type_new_set_names (PyTypeObject * );
36993815static int type_new_init_subclass (PyTypeObject * , PyObject * );
@@ -6274,7 +6390,7 @@ type_setattro(PyObject *self, PyObject *name, PyObject *value)
62746390 if (is_dunder_name (name ) && has_slotdef (name )) {
62756391 // The name corresponds to a type slot.
62766392 types_stop_world ();
6277- res = update_slot (type , name );
6393+ res = update_slot (type , name , NULL );
62786394 types_start_world ();
62796395 ASSERT_TYPE_LOCK_HELD ();
62806396 }
@@ -11254,13 +11370,22 @@ has_slotdef(PyObject *name)
1125411370 * There are some further special cases for specific slots, like supporting
1125511371 * __hash__ = None for tp_hash and special code for tp_new.
1125611372 *
11257- * When done, return a pointer to the next slotdef with a different offset,
11258- * because that's convenient for fixup_slot_dispatchers(). This function never
11259- * sets an exception: if an internal error happens (unlikely), it's ignored. */
11260- static pytype_slotdef *
11261- update_one_slot (PyTypeObject * type , pytype_slotdef * p )
11373+ * When done, next_p is set to the next slotdef with a different offset,
11374+ * because that's convenient for fixup_slot_dispatchers().
11375+ *
11376+ * If the queued_updates pointer is provided, the actual updates to the slot
11377+ * pointers are queued, rather than being immediately performed. That argument
11378+ * is only used for the free-threaded build since those updates need to be
11379+ * done while the world is stopped.
11380+ *
11381+ * This function will only return an error if the queued_updates argument is
11382+ * provided and allocating memory for the queue fails. Other exceptions that
11383+ * occur internally are ignored, such as when looking up descriptors. */
11384+ static int
11385+ update_one_slot (PyTypeObject * type , pytype_slotdef * p , pytype_slotdef * * next_p ,
11386+ slot_update_t * queued_updates )
1126211387{
11263- ASSERT_WORLD_STOPPED_OR_NEW_TYPE (type );
11388+ ASSERT_NEW_TYPE_OR_LOCKED (type );
1126411389
1126511390 PyObject * descr ;
1126611391 PyWrapperDescrObject * d ;
@@ -11283,7 +11408,10 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
1128311408 do {
1128411409 ++ p ;
1128511410 } while (p -> offset == offset );
11286- return p ;
11411+ if (next_p != NULL ) {
11412+ * next_p = p ;
11413+ }
11414+ return 0 ;
1128711415 }
1128811416 /* We may end up clearing live exceptions below, so make sure it's ours. */
1128911417 assert (!PyErr_Occurred ());
@@ -11371,37 +11499,63 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
1137111499 }
1137211500 Py_DECREF (descr );
1137311501 } while ((++ p )-> offset == offset );
11374- if (specific && !use_generic )
11375- * ptr = specific ;
11376- else
11377- * ptr = generic ;
11378- return p ;
11502+
11503+ void * slot_value ;
11504+ if (specific && !use_generic ) {
11505+ slot_value = specific ;
11506+ } else {
11507+ slot_value = generic ;
11508+ }
11509+
11510+ #ifdef Py_GIL_DISABLED
11511+ if (queued_updates != NULL ) {
11512+ // queue the update to perform later, while world is stopped
11513+ if (queue_slot_update (queued_updates , ptr , slot_value ) < 0 ) {
11514+ return -1 ;
11515+ }
11516+ } else {
11517+ // do the update to the type structure now
11518+ * ptr = slot_value ;
11519+ }
11520+ #else
11521+ // always do the update immediately
11522+ assert (queued_updates == NULL );
11523+ * ptr = slot_value ;
11524+ #endif
11525+
11526+ if (next_p != NULL ) {
11527+ * next_p = p ;
11528+ }
11529+ return 0 ;
1137911530}
1138011531
1138111532/* In the type, update the slots whose slotdefs are gathered in the pp array.
1138211533 This is a callback for update_subclasses(). */
1138311534static int
1138411535update_slots_callback (PyTypeObject * type , void * data )
1138511536{
11386- ASSERT_WORLD_STOPPED_OR_NEW_TYPE (type );
11537+ ASSERT_NEW_TYPE_OR_LOCKED (type );
1138711538
11388- pytype_slotdef * * pp = (pytype_slotdef * * )data ;
11539+ update_callback_data_t * update_data = (update_callback_data_t * )data ;
11540+ pytype_slotdef * * pp = update_data -> defs ;
1138911541 for (; * pp ; pp ++ ) {
11390- update_one_slot (type , * pp );
11542+ if (update_one_slot (type , * pp , NULL , update_data -> queued_updates ) < 0 ) {
11543+ return -1 ;
11544+ }
1139111545 }
1139211546 return 0 ;
1139311547}
1139411548
1139511549/* Update the slots after assignment to a class (type) attribute. */
1139611550static int
11397- update_slot (PyTypeObject * type , PyObject * name )
11551+ update_slot (PyTypeObject * type , PyObject * name , slot_update_t * queued_updates )
1139811552{
1139911553 pytype_slotdef * ptrs [MAX_EQUIV ];
1140011554 pytype_slotdef * p ;
1140111555 pytype_slotdef * * pp ;
1140211556 int offset ;
1140311557
11404- assert ( types_world_is_stopped () );
11558+ ASSERT_TYPE_LOCK_HELD ( );
1140511559 assert (PyUnicode_CheckExact (name ));
1140611560 assert (PyUnicode_CHECK_INTERNED (name ));
1140711561
@@ -11425,8 +11579,12 @@ update_slot(PyTypeObject *type, PyObject *name)
1142511579 }
1142611580 if (ptrs [0 ] == NULL )
1142711581 return 0 ; /* Not an attribute that affects any slots */
11582+
11583+ update_callback_data_t callback_data ;
11584+ callback_data .defs = ptrs ;
11585+ callback_data .queued_updates = queued_updates ;
1142811586 return update_subclasses (type , name ,
11429- update_slots_callback , (void * )ptrs );
11587+ update_slots_callback , (void * )& callback_data );
1143011588}
1143111589
1143211590/* Store the proper functions in the slot dispatches at class (type)
@@ -11437,27 +11595,64 @@ fixup_slot_dispatchers(PyTypeObject *type)
1143711595{
1143811596 assert (!PyErr_Occurred ());
1143911597 for (pytype_slotdef * p = slotdefs ; p -> name ; ) {
11440- p = update_one_slot (type , p );
11598+ update_one_slot (type , p , & p , NULL );
1144111599 }
1144211600}
1144311601
11602+ #ifdef Py_GIL_DISABLED
11603+
1144411604// Called when __bases__ is re-assigned.
11445- static void
11605+ static int
1144611606update_all_slots (PyTypeObject * type )
1144711607{
11448- pytype_slotdef * p ;
11608+ // Note that update_slot() can fail due to out-of-memory when allocating
11609+ // the queue chunks to hold the updates. That's unlikely since the number
11610+ // of updates is normally small but we handle that case. update_slot()
11611+ // can fail internally for other reasons (a lookup fails) but those
11612+ // errors are suppressed.
11613+ slot_update_t queued_updates = {0 };
11614+ for (pytype_slotdef * p = slotdefs ; p -> name ; p ++ ) {
11615+ if (update_slot (type , p -> name_strobj , & queued_updates ) < 0 ) {
11616+ if (queued_updates .head ) {
11617+ slot_update_free_chunks (& queued_updates );
11618+ }
11619+ return -1 ;
11620+ }
11621+ }
11622+ if (queued_updates .head != NULL ) {
11623+ types_stop_world ();
11624+ apply_slot_updates (& queued_updates );
11625+ types_start_world ();
11626+ ASSERT_TYPE_LOCK_HELD ();
1144911627
11450- assert (types_world_is_stopped ());
11628+ slot_update_free_chunks (& queued_updates );
11629+
11630+ /* Clear the VALID_VERSION flag of 'type' and all its subclasses. */
11631+ type_modified_unlocked (type );
11632+ }
11633+ return 0 ;
11634+ }
11635+
11636+ #else
11637+
11638+ // Called when __bases__ is re-assigned.
11639+ static int
11640+ update_all_slots (PyTypeObject * type )
11641+ {
11642+ pytype_slotdef * p ;
1145111643
1145211644 for (p = slotdefs ; p -> name ; p ++ ) {
11453- /* update_slot returns int but can't actually fail */
11454- update_slot (type , p -> name_strobj );
11645+ /* update_slot returns int but can't actually fail in this case */
11646+ update_slot (type , p -> name_strobj , NULL );
1145511647 }
1145611648
1145711649 /* Clear the VALID_VERSION flag of 'type' and all its subclasses. */
1145811650 type_modified_unlocked (type );
11651+ return 0 ;
1145911652}
1146011653
11654+ #endif
11655+
1146111656
1146211657PyObject *
1146311658_PyType_GetSlotWrapperNames (void )
0 commit comments