@@ -3490,6 +3490,9 @@ static int update_slot(PyTypeObject *, PyObject *);
34903490static void fixup_slot_dispatchers (PyTypeObject * );
34913491static int type_new_set_names (PyTypeObject * );
34923492static int type_new_init_subclass (PyTypeObject * , PyObject * );
3493+ #ifdef Py_GIL_DISABLED
3494+ static bool has_slotdef (PyObject * );
3495+ #endif
34933496
34943497/*
34953498 * Helpers for __dict__ descriptor. We don't want to expose the dicts
@@ -5943,6 +5946,23 @@ _Py_type_getattro(PyObject *tp, PyObject *name)
59435946 return _Py_type_getattro_impl (type , name , NULL );
59445947}
59455948
5949+ #ifdef Py_GIL_DISABLED
5950+ static int
5951+ update_slot_world_stopped (PyTypeObject * type , PyObject * name )
5952+ {
5953+ // Modification of type slots is protected by the global type
5954+ // lock. However, type slots are read non-atomically without holding the
5955+ // type lock. So, we need to stop-the-world while modifying slots, in
5956+ // order to avoid data races. This is unfortunately quite expensive.
5957+ int ret ;
5958+ PyInterpreterState * interp = _PyInterpreterState_GET ();
5959+ _PyEval_StopTheWorld (interp );
5960+ ret = update_slot (type , name );
5961+ _PyEval_StartTheWorld (interp );
5962+ return ret ;
5963+ }
5964+ #endif
5965+
59465966static int
59475967type_update_dict (PyTypeObject * type , PyDictObject * dict , PyObject * name ,
59485968 PyObject * value , PyObject * * old_value )
@@ -5972,9 +5992,15 @@ type_update_dict(PyTypeObject *type, PyDictObject *dict, PyObject *name,
59725992 return -1 ;
59735993 }
59745994
5995+ #if Py_GIL_DISABLED
5996+ if (is_dunder_name (name ) && has_slotdef (name )) {
5997+ return update_slot_world_stopped (type , name );
5998+ }
5999+ #else
59756000 if (is_dunder_name (name )) {
59766001 return update_slot (type , name );
59776002 }
6003+ #endif
59786004
59796005 return 0 ;
59806006}
@@ -11002,6 +11028,21 @@ resolve_slotdups(PyTypeObject *type, PyObject *name)
1100211028#undef ptrs
1100311029}
1100411030
11031+ #ifdef Py_GIL_DISABLED
11032+ // Return true if "name" corresponds to at least one slot definition. This is
11033+ // used to avoid calling update_slot() if is_dunder_name() is true but it's
11034+ // not actually a slot.
11035+ static bool
11036+ has_slotdef (PyObject * name )
11037+ {
11038+ for (pytype_slotdef * p = slotdefs ; p -> name_strobj ; p ++ ) {
11039+ if (p -> name_strobj == name ) {
11040+ return true;
11041+ }
11042+ }
11043+ return false;
11044+ }
11045+ #endif
1100511046
1100611047/* Common code for update_slots_callback() and fixup_slot_dispatchers().
1100711048 *
@@ -11241,20 +11282,32 @@ fixup_slot_dispatchers(PyTypeObject *type)
1124111282 END_TYPE_LOCK ();
1124211283}
1124311284
11285+ // Called when __bases__ is re-assigned.
1124411286static void
1124511287update_all_slots (PyTypeObject * type )
1124611288{
1124711289 pytype_slotdef * p ;
1124811290
1124911291 ASSERT_TYPE_LOCK_HELD ();
1125011292
11293+ // Similar to update_slot_world_stopped(), this is required to
11294+ // avoid races. We do it once here rather than once per-slot.
11295+ #ifdef Py_GIL_DISABLED
11296+ PyInterpreterState * interp = _PyInterpreterState_GET ();
11297+ _PyEval_StopTheWorld (interp );
11298+ #endif
11299+
1125111300 /* Clear the VALID_VERSION flag of 'type' and all its subclasses. */
1125211301 type_modified_unlocked (type );
1125311302
1125411303 for (p = slotdefs ; p -> name ; p ++ ) {
1125511304 /* update_slot returns int but can't actually fail */
1125611305 update_slot (type , p -> name_strobj );
1125711306 }
11307+
11308+ #ifdef Py_GIL_DISABLED
11309+ _PyEval_StartTheWorld (interp );
11310+ #endif
1125811311}
1125911312
1126011313
0 commit comments