@@ -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
}
@@ -296,8 +297,6 @@ static int dictresize(PyInterpreterState *interp, PyDictObject *mp,
296
297
297
298
static PyObject * dict_iter (PyObject * dict );
298
299
299
- static int
300
-contains_known_hash (PyDictObject * mp , PyObject * key , Py_ssize_t hash );
301
300
static int
302
301
setitem_lock_held (PyDictObject * mp , PyObject * key , PyObject * value );
303
302
static int
@@ -764,9 +763,9 @@ new_values(size_t size)
764
763
}
765
764
766
765
static inline void
767
- free_values (PyDictValues * values , int use_qsbr )
766
+ free_values (PyDictValues * values , bool use_qsbr )
768
767
{
769
- int prefix_size = (( uint8_t * ) values )[ -1 ] ;
768
+ int prefix_size = DICT_VALUES_SIZE ( values );
770
769
#ifdef Py_GIL_DISABLED
771
770
if (use_qsbr ) {
772
771
_PyMem_FreeQsbr (((char * )values )- prefix_size );
@@ -1154,12 +1153,10 @@ ensure_shared_on_read(PyDictObject *mp)
1154
1153
{
1155
1154
#ifdef Py_GIL_DISABLED
1156
1155
if (!_Py_IsOwnedByCurrentThread ((PyObject * )mp ) && !IS_DICT_SHARED (mp )) {
1157
- // We are accessing the dict from another thread then owns
1158
- // it and we haven't marked it as shared which will ensure
1159
- // that when we re-size ma_keys or ma_values that we will
1160
- // free using QSBR. We need to lock the dictionary to
1161
- // contend with writes from the owning thread, mark it as
1162
- // shared, and then we can continue with lock-free reads.
1156
+ // The first time we access a dict from a non-owning thread we mark it
1157
+ // as shared. This ensures that a concurrent resize operation will
1158
+ // delay freeing the old keys or values using QSBR, which is necessary
1159
+ // to safely allow concurrent reads without locking...
1163
1160
Py_BEGIN_CRITICAL_SECTION (mp );
1164
1161
if (!IS_DICT_SHARED (mp )) {
1165
1162
SET_DICT_SHARED (mp );
@@ -1176,7 +1173,7 @@ ensure_shared_on_resize(PyDictObject *mp)
1176
1173
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (mp );
1177
1174
1178
1175
if (!_Py_IsOwnedByCurrentThread ((PyObject * )mp ) && !IS_DICT_SHARED (mp )) {
1179
- // We are writing to the dict from another thread then owns
1176
+ // We are writing to the dict from another thread that owns
1180
1177
// it and we haven't marked it as shared which will ensure
1181
1178
// that when we re-size ma_keys or ma_values that we will
1182
1179
// free using QSBR. We need to lock the dictionary to
@@ -1234,8 +1231,8 @@ unicodekeys_lookup_generic_threadsafe(PyDictObject *mp, PyDictKeysObject* dk, Py
1234
1231
return do_lookup (mp , dk , key , hash , compare_unicode_generic_threadsafe );
1235
1232
}
1236
1233
1237
- static inline Py_ALWAYS_INLINE
1238
- Py_ssize_t compare_unicode_unicode_threadsafe (PyDictObject * mp , PyDictKeysObject * dk ,
1234
+ static inline Py_ALWAYS_INLINE Py_ssize_t
1235
+ compare_unicode_unicode_threadsafe (PyDictObject * mp , PyDictKeysObject * dk ,
1239
1236
void * ep0 , Py_ssize_t ix , PyObject * key , Py_hash_t hash )
1240
1237
{
1241
1238
PyDictUnicodeEntry * ep = & ((PyDictUnicodeEntry * )ep0 )[ix ];
@@ -1646,7 +1643,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
1646
1643
return -1 ;
1647
1644
}
1648
1645
1649
- // Same to insertdict but specialized for ma_keys == Py_EMPTY_KEYS.
1646
+ // Same as insertdict but specialized for ma_keys == Py_EMPTY_KEYS.
1650
1647
// Consumes key and value references.
1651
1648
static int
1652
1649
insert_to_emptydict (PyInterpreterState * interp , PyDictObject * mp ,
@@ -1691,7 +1688,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
1691
1688
// We store the keys last so no one can see them in a partially inconsistent
1692
1689
// state so that we don't need to switch the keys to being shared yet for
1693
1690
// the case where we're inserting from the non-owner thread. We don't use
1694
- // store_keys here because the transition from empty to non-empty is safe
1691
+ // set_keys here because the transition from empty to non-empty is safe
1695
1692
// as the empty keys will never be freed.
1696
1693
#ifdef Py_GIL_DISABLED
1697
1694
_Py_atomic_store_ptr_release (& mp -> ma_keys , newkeys );
@@ -1753,7 +1750,7 @@ static int
1753
1750
dictresize (PyInterpreterState * interp , PyDictObject * mp ,
1754
1751
uint8_t log2_newsize , int unicode )
1755
1752
{
1756
- PyDictKeysObject * oldkeys ;
1753
+ PyDictKeysObject * oldkeys , * newkeys ;
1757
1754
PyDictValues * oldvalues ;
1758
1755
1759
1756
ASSERT_DICT_LOCKED (mp );
@@ -1778,13 +1775,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1778
1775
*/
1779
1776
1780
1777
/* Allocate a new table. */
1781
- set_keys (mp , new_keys_object (interp , log2_newsize , unicode ));
1782
- if (mp -> ma_keys == NULL ) {
1783
- set_keys (mp , oldkeys );
1778
+ newkeys = new_keys_object (interp , log2_newsize , unicode );
1779
+ if (newkeys == NULL ) {
1784
1780
return -1 ;
1785
1781
}
1786
1782
// New table must be large enough.
1787
- assert (mp -> ma_keys -> dk_usable >= mp -> ma_used );
1783
+ assert (newkeys -> dk_usable >= mp -> ma_used );
1788
1784
1789
1785
Py_ssize_t numentries = mp -> ma_used ;
1790
1786
@@ -1793,9 +1789,9 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1793
1789
/* Convert split table into new combined table.
1794
1790
* We must incref keys; we can transfer values.
1795
1791
*/
1796
- if (mp -> ma_keys -> dk_kind == DICT_KEYS_GENERAL ) {
1792
+ if (newkeys -> dk_kind == DICT_KEYS_GENERAL ) {
1797
1793
// split -> generic
1798
- PyDictKeyEntry * newentries = DK_ENTRIES (mp -> ma_keys );
1794
+ PyDictKeyEntry * newentries = DK_ENTRIES (newkeys );
1799
1795
1800
1796
for (Py_ssize_t i = 0 ; i < numentries ; i ++ ) {
1801
1797
int index = get_index_from_order (mp , i );
@@ -1805,10 +1801,10 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1805
1801
newentries [i ].me_hash = unicode_get_hash (ep -> me_key );
1806
1802
newentries [i ].me_value = oldvalues -> values [index ];
1807
1803
}
1808
- build_indices_generic (mp -> ma_keys , newentries , numentries );
1804
+ build_indices_generic (newkeys , newentries , numentries );
1809
1805
}
1810
1806
else { // split -> combined unicode
1811
- PyDictUnicodeEntry * newentries = DK_UNICODE_ENTRIES (mp -> ma_keys );
1807
+ PyDictUnicodeEntry * newentries = DK_UNICODE_ENTRIES (newkeys );
1812
1808
1813
1809
for (Py_ssize_t i = 0 ; i < numentries ; i ++ ) {
1814
1810
int index = get_index_from_order (mp , i );
@@ -1817,18 +1813,19 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1817
1813
newentries [i ].me_key = Py_NewRef (ep -> me_key );
1818
1814
newentries [i ].me_value = oldvalues -> values [index ];
1819
1815
}
1820
- build_indices_unicode (mp -> ma_keys , newentries , numentries );
1816
+ build_indices_unicode (newkeys , newentries , numentries );
1821
1817
}
1818
+ set_keys (mp , newkeys );
1822
1819
dictkeys_decref (interp , oldkeys , IS_DICT_SHARED (mp ));
1823
1820
set_values (mp , NULL );
1824
1821
free_values (oldvalues , IS_DICT_SHARED (mp ));
1825
1822
}
1826
1823
else { // oldkeys is combined.
1827
1824
if (oldkeys -> dk_kind == DICT_KEYS_GENERAL ) {
1828
1825
// generic -> generic
1829
- assert (mp -> ma_keys -> dk_kind == DICT_KEYS_GENERAL );
1826
+ assert (newkeys -> dk_kind == DICT_KEYS_GENERAL );
1830
1827
PyDictKeyEntry * oldentries = DK_ENTRIES (oldkeys );
1831
- PyDictKeyEntry * newentries = DK_ENTRIES (mp -> ma_keys );
1828
+ PyDictKeyEntry * newentries = DK_ENTRIES (newkeys );
1832
1829
if (oldkeys -> dk_nentries == numentries ) {
1833
1830
memcpy (newentries , oldentries , numentries * sizeof (PyDictKeyEntry ));
1834
1831
}
@@ -1840,12 +1837,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1840
1837
newentries [i ] = * ep ++ ;
1841
1838
}
1842
1839
}
1843
- build_indices_generic (mp -> ma_keys , newentries , numentries );
1840
+ build_indices_generic (newkeys , newentries , numentries );
1844
1841
}
1845
1842
else { // oldkeys is combined unicode
1846
1843
PyDictUnicodeEntry * oldentries = DK_UNICODE_ENTRIES (oldkeys );
1847
1844
if (unicode ) { // combined unicode -> combined unicode
1848
- PyDictUnicodeEntry * newentries = DK_UNICODE_ENTRIES (mp -> ma_keys );
1845
+ PyDictUnicodeEntry * newentries = DK_UNICODE_ENTRIES (newkeys );
1849
1846
if (oldkeys -> dk_nentries == numentries && mp -> ma_keys -> dk_kind == DICT_KEYS_UNICODE ) {
1850
1847
memcpy (newentries , oldentries , numentries * sizeof (PyDictUnicodeEntry ));
1851
1848
}
@@ -1857,10 +1854,10 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1857
1854
newentries [i ] = * ep ++ ;
1858
1855
}
1859
1856
}
1860
- build_indices_unicode (mp -> ma_keys , newentries , numentries );
1857
+ build_indices_unicode (newkeys , newentries , numentries );
1861
1858
}
1862
1859
else { // combined unicode -> generic
1863
- PyDictKeyEntry * newentries = DK_ENTRIES (mp -> ma_keys );
1860
+ PyDictKeyEntry * newentries = DK_ENTRIES (newkeys );
1864
1861
PyDictUnicodeEntry * ep = oldentries ;
1865
1862
for (Py_ssize_t i = 0 ; i < numentries ; i ++ ) {
1866
1863
while (ep -> me_value == NULL )
@@ -1870,10 +1867,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
1870
1867
newentries [i ].me_value = ep -> me_value ;
1871
1868
ep ++ ;
1872
1869
}
1873
- build_indices_generic (mp -> ma_keys , newentries , numentries );
1870
+ build_indices_generic (newkeys , newentries , numentries );
1874
1871
}
1875
1872
}
1876
1873
1874
+ set_keys (mp , newkeys );
1875
+
1877
1876
if (oldkeys != Py_EMPTY_KEYS ) {
1878
1877
#ifdef Py_REF_DEBUG
1879
1878
_Py_DecRefTotal (_PyInterpreterState_GET ());
@@ -3528,7 +3527,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
3528
3527
Py_NewRef (key ), hash , Py_NewRef (value ));
3529
3528
}
3530
3529
else {
3531
- err = contains_known_hash ( mp , key , hash );
3530
+ err = _PyDict_Contains_KnownHash (( PyObject * ) mp , key , hash );
3532
3531
if (err == 0 ) {
3533
3532
err = insertdict (interp , mp ,
3534
3533
Py_NewRef (key ), hash , Py_NewRef (value ));
@@ -3921,29 +3920,14 @@ static PyObject *
3921
3920
dict___contains__ (PyDictObject * self , PyObject * key )
3922
3921
/*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/
3923
3922
{
3924
- register PyDictObject * mp = self ;
3925
- Py_hash_t hash ;
3926
- Py_ssize_t ix ;
3927
- PyObject * value ;
3928
-
3929
- if (!PyUnicode_CheckExact (key ) || (hash = unicode_get_hash (key )) == -1 ) {
3930
- hash = PyObject_Hash (key );
3931
- if (hash == -1 )
3932
- return NULL ;
3933
- }
3934
- #ifdef Py_GIL_DISABLED
3935
- ix = _Py_dict_lookup_threadsafe (mp , key , hash , & value );
3936
- #else
3937
- ix = _Py_dict_lookup (mp , key , hash , & value );
3938
- #endif
3939
- if (ix == DKIX_ERROR )
3923
+ int contains = PyDict_Contains ((PyObject * )self , key );
3924
+ if (contains < 0 ) {
3940
3925
return NULL ;
3941
- if (ix == DKIX_EMPTY || value == NULL )
3942
- Py_RETURN_FALSE ;
3943
- #ifdef Py_GIL_DISABLED
3944
- Py_DECREF (value );
3945
- #endif
3946
- Py_RETURN_TRUE ;
3926
+ }
3927
+ if (contains ) {
3928
+ Py_RETURN_TRUE ;
3929
+ }
3930
+ Py_RETURN_FALSE ;
3947
3931
}
3948
3932
3949
3933
/*[clinic input]
@@ -4450,57 +4434,19 @@ static PyMethodDef mapp_methods[] = {
4450
4434
{NULL , NULL } /* sentinel */
4451
4435
};
4452
4436
4453
- static int
4454
- contains_known_hash (PyDictObject * mp , PyObject * key , Py_ssize_t hash )
4455
- {
4456
- Py_ssize_t ix ;
4457
- PyObject * value ;
4458
-
4459
- #ifdef Py_GIL_DISABLED
4460
- ix = _Py_dict_lookup_threadsafe (mp , key , hash , & value );
4461
- #else
4462
- ix = _Py_dict_lookup (mp , key , hash , & value );
4463
- #endif
4464
- if (ix == DKIX_ERROR )
4465
- return -1 ;
4466
-
4467
- if (ix != DKIX_EMPTY && value != NULL ) {
4468
- #ifdef Py_GIL_DISABLED
4469
- Py_DECREF (value );
4470
- #endif
4471
- return 1 ;
4472
- }
4473
- return 0 ;
4474
- }
4475
-
4476
4437
/* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */
4477
4438
int
4478
4439
PyDict_Contains (PyObject * op , PyObject * key )
4479
4440
{
4480
4441
Py_hash_t hash ;
4481
- Py_ssize_t ix ;
4482
- PyObject * value ;
4483
- PyDictObject * mp = (PyDictObject * )op ;
4484
4442
4485
4443
if (!PyUnicode_CheckExact (key ) || (hash = unicode_get_hash (key )) == -1 ) {
4486
4444
hash = PyObject_Hash (key );
4487
4445
if (hash == -1 )
4488
4446
return -1 ;
4489
4447
}
4490
- #ifdef Py_GIL_DISABLED
4491
- ix = _Py_dict_lookup_threadsafe (mp , key , hash , & value );
4492
- #else
4493
- ix = _Py_dict_lookup (mp , key , hash , & value );
4494
- #endif
4495
- if (ix == DKIX_ERROR )
4496
- return -1 ;
4497
- if (ix != DKIX_EMPTY && value != NULL ) {
4498
- #ifdef Py_GIL_DISABLED
4499
- Py_DECREF (value );
4500
- #endif
4501
- return 1 ;
4502
- }
4503
- return 0 ;
4448
+
4449
+ return _PyDict_Contains_KnownHash (op , key , hash );
4504
4450
}
4505
4451
4506
4452
int
0 commit comments