@@ -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,58 @@ 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 (((PyDictObject * )dict )-> ma_values == values );
7473+ assert (PyUnicode_CheckExact (name ));
74737474
7474- ix = insert_split_key (keys , name , hash );
7475+ Py_hash_t hash = unicode_get_hash (name );
7476+ if (hash == -1 ) {
7477+ hash = PyUnicode_Type .tp_hash (name );
7478+ assert (hash != -1 );
7479+ }
74757480
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- }
7481+ #ifdef Py_GIL_DISABLED
7482+ ix = unicodekeys_lookup_unicode_threadsafe ( keys , name , hash );
7483+ if (ix < 0 ) {
7484+ // insert split key locks which can release our object lock opening
7485+ // up an opportunity to create the dict.
7486+ ix = insert_split_key ( keys , name , hash );
7487+ PyDictObject * dict = _PyObject_GetManagedDict ( obj );
7488+ if ( dict != NULL && (( PyDictObject * ) dict ) -> ma_values != values ) {
7489+ int res ;
7490+ Py_BEGIN_CRITICAL_SECTION ( dict );
7491+ res = _PyDict_SetItem_LockHeld ( dict , name , value );
7492+ Py_END_CRITICAL_SECTION ( );
7493+ return res ;
74897494 }
7490- #endif
74917495 }
7496+ #else
7497+ ix = insert_split_key (keys , name , hash );
7498+ #endif
74927499
74937500 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- }
7501+ #ifdef Py_STATS
7502+ if (shared_keys_usable_size (keys ) == SHARED_KEYS_MAX_SIZE ) {
7503+ OBJECT_STAT_INC (dict_materialized_too_big );
7504+ }
7505+ else {
7506+ OBJECT_STAT_INC (dict_materialized_new_key );
7507+ }
7508+ #endif
75047509
7505- FT_ATOMIC_STORE_PTR_RELEASE (_PyObject_ManagedDictPointer (obj )-> dict ,
7506- (PyDictObject * )dict );
7507- return 0 ;
7510+ // Make the dict but don't publish it in the object
7511+ // so that no one else will see it.
7512+ PyDictObject * dict = make_dict_from_instance_attributes (keys , values );
7513+ if (dict == NULL ||
7514+ _PyDict_SetItem_LockHeld (dict , name , value ) < 0 ) {
7515+ Py_XDECREF (dict );
7516+ return -1 ;
75087517 }
75097518
7510- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (dict );
7519+ FT_ATOMIC_STORE_PTR_RELEASE (_PyObject_ManagedDictPointer (obj )-> dict ,
7520+ (PyDictObject * )dict );
7521+ return 0 ;
75117522
7512- res = _PyDict_SetItem_LockHeld (dict , name , value );
7513- return res ;
75147523 }
75157524
75167525 PyObject * old_value = values -> values [ix ];
@@ -7522,107 +7531,64 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values,
75227531 return -1 ;
75237532 }
75247533
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-
75327534 FT_ATOMIC_STORE_PTR_RELEASE (values -> values [ix ], Py_XNewRef (value ));
75337535
75347536 if (old_value == NULL ) {
75357537 _PyDictValues_AddToInsertionOrder (values , ix );
7536- if (dict ) {
7537- assert (dict -> ma_values == values );
7538- STORE_USED (dict , dict -> ma_used + 1 );
7539- }
75407538 }
75417539 else {
75427540 if (value == NULL ) {
75437541 delete_index_from_values (values , ix );
7544- if (dict ) {
7545- assert (dict -> ma_values == values );
7546- STORE_USED (dict , dict -> ma_used - 1 );
7547- }
75487542 }
75497543 Py_DECREF (old_value );
75507544 }
75517545 return 0 ;
75527546}
75537547
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-
75707548int
75717549_PyObject_StoreInstanceAttribute (PyObject * obj , PyObject * name , PyObject * value )
75727550{
75737551 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
7552+
7553+ int res ;
7554+ PyDictObject * dict ;
7555+ Py_BEGIN_CRITICAL_SECTION (obj );
7556+ dict = _PyObject_GetManagedDict (obj );
7557+
7558+ if (!FT_ATOMIC_LOAD_UINT8 (values -> valid ) || !PyUnicode_CheckExact (name )) {
75857559 if (dict == NULL ) {
7560+ #ifdef Py_STATS
7561+ if (!PyUnicode_CheckExact (name )) {
7562+ OBJECT_STAT_INC (dict_materialized_str_subclass );
7563+ }
7564+ #endif
75867565 dict = (PyDictObject * )PyObject_GenericGetDict (obj , NULL );
75877566 if (dict == NULL ) {
7588- return -1 ;
7567+ res = -1 ;
75897568 }
75907569 }
7591- res = store_instance_attr_dict (obj , dict , name , value );
7592- Py_DECREF (dict );
7593- return res ;
7570+ else {
7571+ Py_INCREF (dict );
7572+ }
7573+ }
7574+ else if (dict == NULL ) {
7575+ res = store_instance_attr_lock_held (obj , values , name , value );
7576+ }
7577+ else {
7578+ Py_INCREF (dict );
75947579 }
75957580
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 ();
7581+ Py_END_CRITICAL_SECTION ();
76177582
7618- if (dict == NULL ) {
7619- return res ;
7620- }
7583+ if (dict == NULL ) {
7584+ return res ;
76217585 }
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
7586+
7587+ Py_BEGIN_CRITICAL_SECTION (dict );
7588+ res = _PyDict_SetItem_LockHeld (dict , name , value );
7589+ Py_END_CRITICAL_SECTION ();
7590+ Py_DECREF (dict );
7591+ return res ;
76267592}
76277593
76287594/* Sanity check for managed dicts */
@@ -7868,8 +7834,9 @@ replace_dict_probably_inline_materialized(PyObject *obj, PyDictObject *inline_di
78687834 _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (obj );
78697835
78707836 if (cur_dict == inline_dict ) {
7871- assert (FT_ATOMIC_LOAD_PTR_RELAXED (inline_dict -> ma_values ) == _PyObject_InlineValues (obj ));
7872-
7837+ // The dict may no longer point at the inline values if it was
7838+ // resized while we didn't hold its lock, in which case detaching
7839+ // is a no-op.
78737840 int err = _PyDict_DetachFromObject (inline_dict , obj );
78747841 if (err != 0 ) {
78757842 assert (new_dict == NULL );
@@ -7895,14 +7862,46 @@ decref_maybe_delay(PyObject *obj, bool delay)
78957862 }
78967863}
78977864
7865+ #if !defined(NDEBUG ) && defined(Py_GIL_DISABLED )
7866+ // A materialized dict can be resized while only its own lock is held, so
7867+ // the inline values are only guaranteed to be consistent once both the
7868+ // object and the dict are locked.
7869+ static int
7870+ inline_values_consistency_check_locked (PyObject * obj )
7871+ {
7872+ PyDictObject * dict ;
7873+ int res = 1 ;
7874+
7875+ Py_BEGIN_CRITICAL_SECTION (obj );
7876+ dict = _PyObject_GetManagedDict (obj );
7877+ Py_XINCREF (dict );
7878+ Py_END_CRITICAL_SECTION ();
7879+
7880+ if (dict == NULL ) {
7881+ return 1 ;
7882+ }
7883+
7884+ Py_BEGIN_CRITICAL_SECTION2 (obj , dict );
7885+ if (_PyObject_GetManagedDict (obj ) == dict ) {
7886+ res = _PyObject_InlineValuesConsistencyCheck (obj );
7887+ }
7888+ Py_END_CRITICAL_SECTION2 ();
7889+
7890+ decref_maybe_delay ((PyObject * )dict , true);
7891+ return res ;
7892+ }
7893+ #endif
7894+
78987895int
78997896_PyObject_SetManagedDict (PyObject * obj , PyObject * new_dict )
79007897{
79017898 assert (Py_TYPE (obj )-> tp_flags & Py_TPFLAGS_MANAGED_DICT );
79027899#ifndef NDEBUG
7903- Py_BEGIN_CRITICAL_SECTION (obj );
7900+ #ifdef Py_GIL_DISABLED
7901+ assert (inline_values_consistency_check_locked (obj ));
7902+ #else
79047903 assert (_PyObject_InlineValuesConsistencyCheck (obj ));
7905- Py_END_CRITICAL_SECTION ();
7904+ #endif
79067905#endif
79077906 int err = 0 ;
79087907 PyTypeObject * tp = Py_TYPE (obj );
0 commit comments