@@ -2059,6 +2059,12 @@ insertdict(PyDictObject *mp,
20592059
20602060 if (_PyDict_HasSplitTable (mp ) && PyUnicode_CheckExact (key )) {
20612061 ix = insert_split_key (mp -> ma_keys , key , hash );
2062+ #ifdef Py_GIL_DISABLED
2063+ if (!_PyDict_HasSplitTable (mp )) {
2064+ ix = _Py_dict_lookup (mp , key , hash , & old_value );
2065+ }
2066+ else
2067+ #endif
20622068 if (ix != DKIX_EMPTY ) {
20632069 _PyDict_InsertSplitValue (mp , key , value , ix );
20642070 Py_DECREF (key );
@@ -7462,55 +7468,57 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values,
74627468 assert (values != NULL );
74637469 assert (Py_TYPE (obj )-> tp_flags & Py_TPFLAGS_INLINE_VALUES );
74647470 Py_ssize_t ix = DKIX_EMPTY ;
7465- PyDictObject * dict = _PyObject_GetManagedDict (obj );
7466- assert (dict == NULL || ((PyDictObject * )dict )-> ma_values == values );
7467- if (PyUnicode_CheckExact (name )) {
7468- Py_hash_t hash = unicode_get_hash (name );
7469- if (hash == -1 ) {
7470- hash = PyUnicode_Type .tp_hash (name );
7471- assert (hash != -1 );
7472- }
7471+ assert (!_PyObject_GetManagedDict (obj ));
7472+ assert (PyUnicode_CheckExact (name ));
74737473
7474- ix = insert_split_key (keys , name , hash );
7474+ Py_hash_t hash = unicode_get_hash (name );
7475+ if (hash == -1 ) {
7476+ hash = PyUnicode_Type .tp_hash (name );
7477+ assert (hash != -1 );
7478+ }
74757479
7476- #ifdef Py_STATS
7477- if ( ix == DKIX_EMPTY ) {
7478- if (PyUnicode_CheckExact ( name ) ) {
7479- if ( shared_keys_usable_size ( keys ) == SHARED_KEYS_MAX_SIZE ) {
7480- OBJECT_STAT_INC ( dict_materialized_too_big );
7481- }
7482- else {
7483- OBJECT_STAT_INC ( dict_materialized_new_key );
7484- }
7485- }
7486- else {
7487- OBJECT_STAT_INC ( dict_materialized_str_subclass );
7488- }
7480+ #ifdef Py_GIL_DISABLED
7481+ ix = unicodekeys_lookup_unicode_threadsafe ( keys , name , hash );
7482+ if (ix < 0 ) {
7483+ // insert split key locks which can release our object lock opening
7484+ // up an opportunity to create the dict.
7485+ ix = insert_split_key ( keys , name , hash );
7486+ PyDictObject * dict = _PyObject_GetManagedDict ( obj );
7487+ if ( dict != NULL && (( PyDictObject * ) dict ) -> ma_values != values ) {
7488+ int res ;
7489+ Py_BEGIN_CRITICAL_SECTION ( dict );
7490+ res = _PyDict_SetItem_LockHeld ( dict , name , value );
7491+ Py_END_CRITICAL_SECTION ( );
7492+ return res ;
74897493 }
7490- #endif
74917494 }
7495+ #else
7496+ ix = insert_split_key (keys , name , hash );
7497+ #endif
74927498
74937499 if (ix == DKIX_EMPTY ) {
7494- int res ;
7495- if (dict == NULL ) {
7496- // Make the dict but don't publish it in the object
7497- // so that no one else will see it.
7498- dict = make_dict_from_instance_attributes (keys , values );
7499- if (dict == NULL ||
7500- _PyDict_SetItem_LockHeld (dict , name , value ) < 0 ) {
7501- Py_XDECREF (dict );
7502- return -1 ;
7503- }
7500+ #ifdef Py_STATS
7501+ if (shared_keys_usable_size (keys ) == SHARED_KEYS_MAX_SIZE ) {
7502+ OBJECT_STAT_INC (dict_materialized_too_big );
7503+ }
7504+ else {
7505+ OBJECT_STAT_INC (dict_materialized_new_key );
7506+ }
7507+ #endif
75047508
7505- FT_ATOMIC_STORE_PTR_RELEASE (_PyObject_ManagedDictPointer (obj )-> dict ,
7506- (PyDictObject * )dict );
7507- return 0 ;
7509+ // Make the dict but don't publish it in the object
7510+ // so that no one else will see it.
7511+ PyDictObject * dict = make_dict_from_instance_attributes (keys , values );
7512+ if (dict == NULL ||
7513+ _PyDict_SetItem_LockHeld (dict , name , value ) < 0 ) {
7514+ Py_XDECREF (dict );
7515+ return -1 ;
75087516 }
75097517
7510- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (dict );
7518+ FT_ATOMIC_STORE_PTR_RELEASE (_PyObject_ManagedDictPointer (obj )-> dict ,
7519+ (PyDictObject * )dict );
7520+ return 0 ;
75117521
7512- res = _PyDict_SetItem_LockHeld (dict , name , value );
7513- return res ;
75147522 }
75157523
75167524 PyObject * old_value = values -> values [ix ];
@@ -7522,107 +7530,64 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values,
75227530 return -1 ;
75237531 }
75247532
7525- if (dict ) {
7526- PyDict_WatchEvent event = (old_value == NULL ? PyDict_EVENT_ADDED :
7527- value == NULL ? PyDict_EVENT_DELETED :
7528- PyDict_EVENT_MODIFIED );
7529- _PyDict_NotifyEvent (event , dict , name , value );
7530- }
7531-
75327533 FT_ATOMIC_STORE_PTR_RELEASE (values -> values [ix ], Py_XNewRef (value ));
75337534
75347535 if (old_value == NULL ) {
75357536 _PyDictValues_AddToInsertionOrder (values , ix );
7536- if (dict ) {
7537- assert (dict -> ma_values == values );
7538- STORE_USED (dict , dict -> ma_used + 1 );
7539- }
75407537 }
75417538 else {
75427539 if (value == NULL ) {
75437540 delete_index_from_values (values , ix );
7544- if (dict ) {
7545- assert (dict -> ma_values == values );
7546- STORE_USED (dict , dict -> ma_used - 1 );
7547- }
75487541 }
75497542 Py_DECREF (old_value );
75507543 }
75517544 return 0 ;
75527545}
75537546
7554- static inline int
7555- store_instance_attr_dict (PyObject * obj , PyDictObject * dict , PyObject * name , PyObject * value )
7556- {
7557- PyDictValues * values = _PyObject_InlineValues (obj );
7558- int res ;
7559- Py_BEGIN_CRITICAL_SECTION (dict );
7560- if (dict -> ma_values == values ) {
7561- res = store_instance_attr_lock_held (obj , values , name , value );
7562- }
7563- else {
7564- res = _PyDict_SetItem_LockHeld (dict , name , value );
7565- }
7566- Py_END_CRITICAL_SECTION ();
7567- return res ;
7568- }
7569-
75707547int
75717548_PyObject_StoreInstanceAttribute (PyObject * obj , PyObject * name , PyObject * value )
75727549{
75737550 PyDictValues * values = _PyObject_InlineValues (obj );
7574- if (!FT_ATOMIC_LOAD_UINT8 (values -> valid )) {
7575- PyDictObject * dict ;
7576- int res ;
7577- #ifdef Py_GIL_DISABLED
7578- Py_BEGIN_CRITICAL_SECTION (obj );
7579- #endif
7580- dict = _PyObject_GetManagedDict (obj );
7581- Py_XINCREF (dict );
7582- #ifdef Py_GIL_DISABLED
7583- Py_END_CRITICAL_SECTION ();
7584- #endif
7551+
7552+ int res ;
7553+ PyDictObject * dict ;
7554+ Py_BEGIN_CRITICAL_SECTION (obj );
7555+ dict = _PyObject_GetManagedDict (obj );
7556+
7557+ if (!FT_ATOMIC_LOAD_UINT8 (values -> valid ) || !PyUnicode_CheckExact (name )) {
75857558 if (dict == NULL ) {
7559+ #ifdef Py_STATS
7560+ if (!PyUnicode_CheckExact (name )) {
7561+ OBJECT_STAT_INC (dict_materialized_str_subclass );
7562+ }
7563+ #endif
75867564 dict = (PyDictObject * )PyObject_GenericGetDict (obj , NULL );
75877565 if (dict == NULL ) {
7588- return -1 ;
7566+ res = -1 ;
75897567 }
75907568 }
7591- res = store_instance_attr_dict (obj , dict , name , value );
7592- Py_DECREF (dict );
7593- return res ;
7569+ else {
7570+ Py_INCREF (dict );
7571+ }
7572+ }
7573+ else if (dict == NULL ) {
7574+ res = store_instance_attr_lock_held (obj , values , name , value );
7575+ }
7576+ else {
7577+ Py_INCREF (dict );
75947578 }
75957579
7596- #ifdef Py_GIL_DISABLED
7597- // We have a valid inline values, at least for now... There are two potential
7598- // races with having the values become invalid. One is the dictionary
7599- // being detached from the object. The other is if someone is inserting
7600- // into the dictionary directly and therefore causing it to resize.
7601- //
7602- // If we haven't materialized the dictionary yet we lock on the object, which
7603- // will also be used to prevent the dictionary from being materialized while
7604- // we're doing the insertion. If we race and the dictionary gets created
7605- // then we'll need to release the object lock and lock the dictionary to
7606- // prevent resizing.
7607- PyDictObject * dict = _PyObject_GetManagedDict (obj );
7608- if (dict == NULL ) {
7609- int res ;
7610- Py_BEGIN_CRITICAL_SECTION (obj );
7611- dict = _PyObject_GetManagedDict (obj );
7612-
7613- if (dict == NULL ) {
7614- res = store_instance_attr_lock_held (obj , values , name , value );
7615- }
7616- Py_END_CRITICAL_SECTION ();
7580+ Py_END_CRITICAL_SECTION ();
76177581
7618- if (dict == NULL ) {
7619- return res ;
7620- }
7582+ if (dict == NULL ) {
7583+ return res ;
76217584 }
7622- return store_instance_attr_dict (obj , dict , name , value );
7623- #else
7624- return store_instance_attr_lock_held (obj , values , name , value );
7625- #endif
7585+
7586+ Py_BEGIN_CRITICAL_SECTION (dict );
7587+ res = _PyDict_SetItem_LockHeld (dict , name , value );
7588+ Py_END_CRITICAL_SECTION ();
7589+ Py_DECREF (dict );
7590+ return res ;
76267591}
76277592
76287593/* Sanity check for managed dicts */
@@ -7868,8 +7833,9 @@ replace_dict_probably_inline_materialized(PyObject *obj, PyDictObject *inline_di
78687833 _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (obj );
78697834
78707835 if (cur_dict == inline_dict ) {
7871- assert (FT_ATOMIC_LOAD_PTR_RELAXED (inline_dict -> ma_values ) == _PyObject_InlineValues (obj ));
7872-
7836+ // The dict may no longer point at the inline values if it was
7837+ // resized while we didn't hold its lock, in which case detaching
7838+ // is a no-op.
78737839 int err = _PyDict_DetachFromObject (inline_dict , obj );
78747840 if (err != 0 ) {
78757841 assert (new_dict == NULL );
@@ -7895,14 +7861,46 @@ decref_maybe_delay(PyObject *obj, bool delay)
78957861 }
78967862}
78977863
7864+ #if !defined(NDEBUG ) && defined(Py_GIL_DISABLED )
7865+ // A materialized dict can be resized while only its own lock is held, so
7866+ // the inline values are only guaranteed to be consistent once both the
7867+ // object and the dict are locked.
7868+ static int
7869+ inline_values_consistency_check_locked (PyObject * obj )
7870+ {
7871+ PyDictObject * dict ;
7872+ int res = 1 ;
7873+
7874+ Py_BEGIN_CRITICAL_SECTION (obj );
7875+ dict = _PyObject_GetManagedDict (obj );
7876+ Py_XINCREF (dict );
7877+ Py_END_CRITICAL_SECTION ();
7878+
7879+ if (dict == NULL ) {
7880+ return 1 ;
7881+ }
7882+
7883+ Py_BEGIN_CRITICAL_SECTION2 (obj , dict );
7884+ if (_PyObject_GetManagedDict (obj ) == dict ) {
7885+ res = _PyObject_InlineValuesConsistencyCheck (obj );
7886+ }
7887+ Py_END_CRITICAL_SECTION2 ();
7888+
7889+ decref_maybe_delay ((PyObject * )dict , true);
7890+ return res ;
7891+ }
7892+ #endif
7893+
78987894int
78997895_PyObject_SetManagedDict (PyObject * obj , PyObject * new_dict )
79007896{
79017897 assert (Py_TYPE (obj )-> tp_flags & Py_TPFLAGS_MANAGED_DICT );
79027898#ifndef NDEBUG
7903- Py_BEGIN_CRITICAL_SECTION (obj );
7899+ #ifdef Py_GIL_DISABLED
7900+ assert (inline_values_consistency_check_locked (obj ));
7901+ #else
79047902 assert (_PyObject_InlineValuesConsistencyCheck (obj ));
7905- Py_END_CRITICAL_SECTION ();
7903+ #endif
79067904#endif
79077905 int err = 0 ;
79087906 PyTypeObject * tp = Py_TYPE (obj );
0 commit comments