@@ -167,7 +167,8 @@ set_keys(PyDictObject *mp, PyDictKeysObject *keys)
167
167
}
168
168
169
169
static inline void
170
- set_values (PyDictObject * mp , PyDictValues * values ) {
170
+ set_values (PyDictObject * mp , PyDictValues * values )
171
+ {
171
172
ASSERT_OWNED_OR_SHARED (mp );
172
173
_Py_atomic_store_ptr_release (& mp -> ma_values , values );
173
174
}
@@ -332,8 +333,6 @@ static int dictresize(PyInterpreterState *interp, PyDictObject *mp,
332
333
333
334
static PyObject * dict_iter (PyObject * dict );
334
335
335
- static int
336
- contains_known_hash (PyDictObject * mp , PyObject * key , Py_ssize_t hash );
337
336
static int
338
337
setitem_lock_held (PyDictObject * mp , PyObject * key , PyObject * value );
339
338
static int
@@ -817,9 +816,9 @@ new_values(size_t size)
817
816
}
818
817
819
818
static inline void
820
- free_values (PyDictValues * values , int use_qsbr )
819
+ free_values (PyDictValues * values , bool use_qsbr )
821
820
{
822
- int prefix_size = (( uint8_t * ) values )[ -1 ] ;
821
+ int prefix_size = DICT_VALUES_SIZE ( values );
823
822
#ifdef Py_GIL_DISABLED
824
823
if (use_qsbr ) {
825
824
_PyMem_FreeQsbr (((char * )values )- prefix_size );
@@ -1219,12 +1218,10 @@ ensure_shared_on_read(PyDictObject *mp)
1219
1218
{
1220
1219
#ifdef Py_GIL_DISABLED
1221
1220
if (!_Py_IsOwnedByCurrentThread ((PyObject * )mp ) && !IS_DICT_SHARED (mp )) {
1222
- // We are accessing the dict from another thread then owns
1223
- // it and we haven't marked it as shared which will ensure
1224
- // that when we re-size ma_keys or ma_values that we will
1225
- // free using QSBR. We need to lock the dictionary to
1226
- // contend with writes from the owning thread, mark it as
1227
- // shared, and then we can continue with lock-free reads.
1221
+ // The first time we access a dict from a non-owning thread we mark it
1222
+ // as shared. This ensures that a concurrent resize operation will
1223
+ // delay freeing the old keys or values using QSBR, which is necessary
1224
+ // to safely allow concurrent reads without locking...
1228
1225
Py_BEGIN_CRITICAL_SECTION (mp );
1229
1226
if (!IS_DICT_SHARED (mp )) {
1230
1227
SET_DICT_SHARED (mp );
@@ -1241,7 +1238,7 @@ ensure_shared_on_resize(PyDictObject *mp)
1241
1238
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (mp );
1242
1239
1243
1240
if (!_Py_IsOwnedByCurrentThread ((PyObject * )mp ) && !IS_DICT_SHARED (mp )) {
1244
- // We are writing to the dict from another thread then owns
1241
+ // We are writing to the dict from another thread that owns
1245
1242
// it and we haven't marked it as shared which will ensure
1246
1243
// that when we re-size ma_keys or ma_values that we will
1247
1244
// free using QSBR. We need to lock the dictionary to
@@ -1299,8 +1296,8 @@ unicodekeys_lookup_generic_threadsafe(PyDictObject *mp, PyDictKeysObject* dk, Py
1299
1296
return do_lookup (mp , dk , key , hash , compare_unicode_generic_threadsafe );
1300
1297
}
1301
1298
1302
- static inline Py_ALWAYS_INLINE
1303
- Py_ssize_t compare_unicode_unicode_threadsafe (PyDictObject * mp , PyDictKeysObject * dk ,
1299
+ static inline Py_ALWAYS_INLINE Py_ssize_t
1300
+ compare_unicode_unicode_threadsafe (PyDictObject * mp , PyDictKeysObject * dk ,
1304
1301
void * ep0 , Py_ssize_t ix , PyObject * key , Py_hash_t hash )
1305
1302
{
1306
1303
PyDictUnicodeEntry * ep = & ((PyDictUnicodeEntry * )ep0 )[ix ];
@@ -1758,7 +1755,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
1758
1755
return -1 ;
1759
1756
}
1760
1757
1761
- // Same to insertdict but specialized for ma_keys == Py_EMPTY_KEYS.
1758
+ // Same as insertdict but specialized for ma_keys == Py_EMPTY_KEYS.
1762
1759
// Consumes key and value references.
1763
1760
static int
1764
1761
insert_to_emptydict (PyInterpreterState * interp , PyDictObject * mp ,
@@ -1803,7 +1800,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
1803
1800
// We store the keys last so no one can see them in a partially inconsistent
1804
1801
// state so that we don't need to switch the keys to being shared yet for
1805
1802
// the case where we're inserting from the non-owner thread. We don't use
1806
- // store_keys here because the transition from empty to non-empty is safe
1803
+ // set_keys here because the transition from empty to non-empty is safe
1807
1804
// as the empty keys will never be freed.
1808
1805
#ifdef Py_GIL_DISABLED
1809
1806
_Py_atomic_store_ptr_release (& mp -> ma_keys , newkeys );
@@ -1865,7 +1862,7 @@ static int
1865
1862
dictresize (PyInterpreterState * interp , PyDictObject * mp ,
1866
1863
uint8_t log2_newsize , int unicode )
1867
1864
{
1868
- PyDictKeysObject * oldkeys ;
1865
+ PyDictKeysObject * oldkeys , * newkeys ;
1869
1866
PyDictValues * oldvalues ;
1870
1867
1871
1868
ASSERT_DICT_LOCKED (mp );
@@ -1890,13 +1887,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1890
1887
*/
1891
1888
1892
1889
/* Allocate a new table. */
1893
- set_keys (mp , new_keys_object (interp , log2_newsize , unicode ));
1894
- if (mp -> ma_keys == NULL ) {
1895
- set_keys (mp , oldkeys );
1890
+ newkeys = new_keys_object (interp , log2_newsize , unicode );
1891
+ if (newkeys == NULL ) {
1896
1892
return -1 ;
1897
1893
}
1898
1894
// New table must be large enough.
1899
- assert (mp -> ma_keys -> dk_usable >= mp -> ma_used );
1895
+ assert (newkeys -> dk_usable >= mp -> ma_used );
1900
1896
1901
1897
Py_ssize_t numentries = mp -> ma_used ;
1902
1898
@@ -1906,9 +1902,9 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1906
1902
/* Convert split table into new combined table.
1907
1903
* We must incref keys; we can transfer values.
1908
1904
*/
1909
- if (mp -> ma_keys -> dk_kind == DICT_KEYS_GENERAL ) {
1905
+ if (newkeys -> dk_kind == DICT_KEYS_GENERAL ) {
1910
1906
// split -> generic
1911
- PyDictKeyEntry * newentries = DK_ENTRIES (mp -> ma_keys );
1907
+ PyDictKeyEntry * newentries = DK_ENTRIES (newkeys );
1912
1908
1913
1909
for (Py_ssize_t i = 0 ; i < numentries ; i ++ ) {
1914
1910
int index = get_index_from_order (mp , i );
@@ -1918,10 +1914,10 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1918
1914
newentries [i ].me_hash = unicode_get_hash (ep -> me_key );
1919
1915
newentries [i ].me_value = oldvalues -> values [index ];
1920
1916
}
1921
- build_indices_generic (mp -> ma_keys , newentries , numentries );
1917
+ build_indices_generic (newkeys , newentries , numentries );
1922
1918
}
1923
1919
else { // split -> combined unicode
1924
- PyDictUnicodeEntry * newentries = DK_UNICODE_ENTRIES (mp -> ma_keys );
1920
+ PyDictUnicodeEntry * newentries = DK_UNICODE_ENTRIES (newkeys );
1925
1921
1926
1922
for (Py_ssize_t i = 0 ; i < numentries ; i ++ ) {
1927
1923
int index = get_index_from_order (mp , i );
@@ -1930,19 +1926,20 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1930
1926
newentries [i ].me_key = Py_NewRef (ep -> me_key );
1931
1927
newentries [i ].me_value = oldvalues -> values [index ];
1932
1928
}
1933
- build_indices_unicode (mp -> ma_keys , newentries , numentries );
1929
+ build_indices_unicode (newkeys , newentries , numentries );
1934
1930
}
1935
1931
UNLOCK_KEYS (oldkeys );
1932
+ set_keys (mp , newkeys );
1936
1933
dictkeys_decref (interp , oldkeys , IS_DICT_SHARED (mp ));
1937
1934
set_values (mp , NULL );
1938
1935
free_values (oldvalues , IS_DICT_SHARED (mp ));
1939
1936
}
1940
1937
else { // oldkeys is combined.
1941
1938
if (oldkeys -> dk_kind == DICT_KEYS_GENERAL ) {
1942
1939
// generic -> generic
1943
- assert (mp -> ma_keys -> dk_kind == DICT_KEYS_GENERAL );
1940
+ assert (newkeys -> dk_kind == DICT_KEYS_GENERAL );
1944
1941
PyDictKeyEntry * oldentries = DK_ENTRIES (oldkeys );
1945
- PyDictKeyEntry * newentries = DK_ENTRIES (mp -> ma_keys );
1942
+ PyDictKeyEntry * newentries = DK_ENTRIES (newkeys );
1946
1943
if (oldkeys -> dk_nentries == numentries ) {
1947
1944
memcpy (newentries , oldentries , numentries * sizeof (PyDictKeyEntry ));
1948
1945
}
@@ -1954,12 +1951,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1954
1951
newentries [i ] = * ep ++ ;
1955
1952
}
1956
1953
}
1957
- build_indices_generic (mp -> ma_keys , newentries , numentries );
1954
+ build_indices_generic (newkeys , newentries , numentries );
1958
1955
}
1959
1956
else { // oldkeys is combined unicode
1960
1957
PyDictUnicodeEntry * oldentries = DK_UNICODE_ENTRIES (oldkeys );
1961
1958
if (unicode ) { // combined unicode -> combined unicode
1962
- PyDictUnicodeEntry * newentries = DK_UNICODE_ENTRIES (mp -> ma_keys );
1959
+ PyDictUnicodeEntry * newentries = DK_UNICODE_ENTRIES (newkeys );
1963
1960
if (oldkeys -> dk_nentries == numentries && mp -> ma_keys -> dk_kind == DICT_KEYS_UNICODE ) {
1964
1961
memcpy (newentries , oldentries , numentries * sizeof (PyDictUnicodeEntry ));
1965
1962
}
@@ -1971,10 +1968,10 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1971
1968
newentries [i ] = * ep ++ ;
1972
1969
}
1973
1970
}
1974
- build_indices_unicode (mp -> ma_keys , newentries , numentries );
1971
+ build_indices_unicode (newkeys , newentries , numentries );
1975
1972
}
1976
1973
else { // combined unicode -> generic
1977
- PyDictKeyEntry * newentries = DK_ENTRIES (mp -> ma_keys );
1974
+ PyDictKeyEntry * newentries = DK_ENTRIES (newkeys );
1978
1975
PyDictUnicodeEntry * ep = oldentries ;
1979
1976
for (Py_ssize_t i = 0 ; i < numentries ; i ++ ) {
1980
1977
while (ep -> me_value == NULL )
@@ -1984,10 +1981,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1984
1981
newentries [i ].me_value = ep -> me_value ;
1985
1982
ep ++ ;
1986
1983
}
1987
- build_indices_generic (mp -> ma_keys , newentries , numentries );
1984
+ build_indices_generic (newkeys , newentries , numentries );
1988
1985
}
1989
1986
}
1990
1987
1988
+ set_keys (mp , newkeys );
1989
+
1991
1990
if (oldkeys != Py_EMPTY_KEYS ) {
1992
1991
#ifdef Py_REF_DEBUG
1993
1992
_Py_DecRefTotal (_PyInterpreterState_GET ());
@@ -3640,7 +3639,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
3640
3639
Py_NewRef (key ), hash , Py_NewRef (value ));
3641
3640
}
3642
3641
else {
3643
- err = contains_known_hash ( mp , key , hash );
3642
+ err = _PyDict_Contains_KnownHash (( PyObject * ) mp , key , hash );
3644
3643
if (err == 0 ) {
3645
3644
err = insertdict (interp , mp ,
3646
3645
Py_NewRef (key ), hash , Py_NewRef (value ));
@@ -4033,29 +4032,14 @@ static PyObject *
4033
4032
dict___contains__ (PyDictObject * self , PyObject * key )
4034
4033
/*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/
4035
4034
{
4036
- register PyDictObject * mp = self ;
4037
- Py_hash_t hash ;
4038
- Py_ssize_t ix ;
4039
- PyObject * value ;
4040
-
4041
- if (!PyUnicode_CheckExact (key ) || (hash = unicode_get_hash (key )) == -1 ) {
4042
- hash = PyObject_Hash (key );
4043
- if (hash == -1 )
4044
- return NULL ;
4045
- }
4046
- #ifdef Py_GIL_DISABLED
4047
- ix = _Py_dict_lookup_threadsafe (mp , key , hash , & value );
4048
- #else
4049
- ix = _Py_dict_lookup (mp , key , hash , & value );
4050
- #endif
4051
- if (ix == DKIX_ERROR )
4035
+ int contains = PyDict_Contains ((PyObject * )self , key );
4036
+ if (contains < 0 ) {
4052
4037
return NULL ;
4053
- if (ix == DKIX_EMPTY || value == NULL )
4054
- Py_RETURN_FALSE ;
4055
- #ifdef Py_GIL_DISABLED
4056
- Py_DECREF (value );
4057
- #endif
4058
- Py_RETURN_TRUE ;
4038
+ }
4039
+ if (contains ) {
4040
+ Py_RETURN_TRUE ;
4041
+ }
4042
+ Py_RETURN_FALSE ;
4059
4043
}
4060
4044
4061
4045
/*[clinic input]
@@ -4551,57 +4535,19 @@ static PyMethodDef mapp_methods[] = {
4551
4535
{NULL , NULL } /* sentinel */
4552
4536
};
4553
4537
4554
- static int
4555
- contains_known_hash (PyDictObject * mp , PyObject * key , Py_ssize_t hash )
4556
- {
4557
- Py_ssize_t ix ;
4558
- PyObject * value ;
4559
-
4560
- #ifdef Py_GIL_DISABLED
4561
- ix = _Py_dict_lookup_threadsafe (mp , key , hash , & value );
4562
- #else
4563
- ix = _Py_dict_lookup (mp , key , hash , & value );
4564
- #endif
4565
- if (ix == DKIX_ERROR )
4566
- return -1 ;
4567
-
4568
- if (ix != DKIX_EMPTY && value != NULL ) {
4569
- #ifdef Py_GIL_DISABLED
4570
- Py_DECREF (value );
4571
- #endif
4572
- return 1 ;
4573
- }
4574
- return 0 ;
4575
- }
4576
-
4577
4538
/* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */
4578
4539
int
4579
4540
PyDict_Contains (PyObject * op , PyObject * key )
4580
4541
{
4581
4542
Py_hash_t hash ;
4582
- Py_ssize_t ix ;
4583
- PyObject * value ;
4584
- PyDictObject * mp = (PyDictObject * )op ;
4585
4543
4586
4544
if (!PyUnicode_CheckExact (key ) || (hash = unicode_get_hash (key )) == -1 ) {
4587
4545
hash = PyObject_Hash (key );
4588
4546
if (hash == -1 )
4589
4547
return -1 ;
4590
4548
}
4591
- #ifdef Py_GIL_DISABLED
4592
- ix = _Py_dict_lookup_threadsafe (mp , key , hash , & value );
4593
- #else
4594
- ix = _Py_dict_lookup (mp , key , hash , & value );
4595
- #endif
4596
- if (ix == DKIX_ERROR )
4597
- return -1 ;
4598
- if (ix != DKIX_EMPTY && value != NULL ) {
4599
- #ifdef Py_GIL_DISABLED
4600
- Py_DECREF (value );
4601
- #endif
4602
- return 1 ;
4603
- }
4604
- return 0 ;
4549
+
4550
+ return _PyDict_Contains_KnownHash (op , key , hash );
4605
4551
}
4606
4552
4607
4553
int
0 commit comments