@@ -1691,15 +1691,26 @@ insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
1691
1691
return 0 ;
1692
1692
}
1693
1693
1694
+ static int
1695
+ split_dict_resize_and_insert (PyInterpreterState * interp , PyDictObject * mp ,
1696
+ Py_hash_t hash , PyObject * key , PyObject * value )
1697
+ {
1698
+ /* Need to resize. */
1699
+ int ins = insertion_resize (interp , mp , 1 );
1700
+ if (ins < 0 ) {
1701
+ return -1 ;
1702
+ }
1703
+ assert (!_PyDict_HasSplitTable (mp ));
1704
+ return insert_combined_dict (interp , mp , hash , key , value );
1705
+ }
1706
+
1694
1707
static int
1695
1708
insert_split_dict (PyInterpreterState * interp , PyDictObject * mp ,
1696
1709
Py_hash_t hash , PyObject * key , PyObject * value )
1697
1710
{
1698
1711
PyDictKeysObject * keys = mp -> ma_keys ;
1699
- LOCK_KEYS (keys );
1700
1712
if (keys -> dk_usable <= 0 ) {
1701
1713
/* Need to resize. */
1702
- UNLOCK_KEYS (keys );
1703
1714
int ins = insertion_resize (interp , mp , 1 );
1704
1715
if (ins < 0 ) {
1705
1716
return -1 ;
@@ -1722,10 +1733,34 @@ insert_split_dict(PyInterpreterState *interp, PyDictObject *mp,
1722
1733
1723
1734
split_keys_entry_added (keys );
1724
1735
assert (keys -> dk_usable >= 0 );
1725
- UNLOCK_KEYS (keys );
1726
1736
return 0 ;
1727
1737
}
1728
1738
1739
+ #ifdef Py_GIL_DISABLED
1740
+
1741
+ static inline Py_ssize_t
1742
+ splitdict_lookup_threadsafe (PyDictObject * mp , PyDictKeysObject * dk ,
1743
+ PyObject * key , Py_ssize_t hash ,
1744
+ PyObject * * value )
1745
+ {
1746
+ ASSERT_DICT_LOCKED (mp );
1747
+
1748
+ Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe (dk , key , hash );
1749
+
1750
+ if (ix >= 0 ) {
1751
+ * value = mp -> ma_values -> values [ix ];
1752
+ }
1753
+ else if (ix == DKIX_KEY_CHANGED ) {
1754
+ ix = _Py_dict_lookup (mp , key , hash , value );
1755
+ }
1756
+ else {
1757
+ * value = 0 ;
1758
+ }
1759
+ return ix ;
1760
+ }
1761
+
1762
+ #endif
1763
+
1729
1764
/*
1730
1765
Internal routine to insert a new item into the table.
1731
1766
Used both by the internal resize routine and by the public insert routine.
@@ -1757,17 +1792,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
1757
1792
// half of _Py_dict_lookup_threadsafe and avoids locking the
1758
1793
// shared keys if we can
1759
1794
assert (PyUnicode_CheckExact (key ));
1760
- ix = unicodekeys_lookup_unicode_threadsafe (dk , key , hash );
1761
-
1762
- if (ix >= 0 ) {
1763
- old_value = mp -> ma_values -> values [ix ];
1764
- }
1765
- else if (ix == DKIX_KEY_CHANGED ) {
1766
- ix = _Py_dict_lookup (mp , key , hash , & old_value );
1767
- }
1768
- else {
1769
- old_value = NULL ;
1770
- }
1795
+ ix = splitdict_lookup_threadsafe (mp , dk , key , hash , & old_value );
1771
1796
} else {
1772
1797
ix = _Py_dict_lookup (mp , key , hash , & old_value );
1773
1798
}
@@ -1780,20 +1805,47 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
1780
1805
MAINTAIN_TRACKING (mp , key , value );
1781
1806
1782
1807
if (ix == DKIX_EMPTY ) {
1783
- uint64_t new_version = _PyDict_NotifyEvent (
1784
- interp , PyDict_EVENT_ADDED , mp , key , value );
1785
- /* Insert into new slot. */
1786
- mp -> ma_keys -> dk_version = 0 ;
1787
- assert (old_value == NULL );
1788
-
1808
+ uint64_t new_version ;
1789
1809
if (!_PyDict_HasSplitTable (mp )) {
1810
+ new_version = _PyDict_NotifyEvent (
1811
+ interp , PyDict_EVENT_ADDED , mp , key , value );
1812
+ /* Insert into new slot. */
1813
+ mp -> ma_keys -> dk_version = 0 ;
1790
1814
if (insert_combined_dict (interp , mp , hash , key , value ) < 0 ) {
1791
1815
goto Fail ;
1792
1816
}
1793
1817
}
1794
1818
else {
1795
- if (insert_split_dict (interp , mp , hash , key , value ) < 0 )
1796
- goto Fail ;
1819
+ LOCK_KEYS (mp -> ma_keys );
1820
+
1821
+ #ifdef Py_GIL_DISABLED
1822
+ // We could have raced between our lookup before and the insert,
1823
+ // so we need to lookup again with the keys locked
1824
+ ix = splitdict_lookup_threadsafe (mp , dk , key , hash , & old_value );
1825
+ if (ix >= 0 ) {
1826
+ UNLOCK_KEYS (mp -> ma_keys );
1827
+ goto insert_on_split_race ;
1828
+ }
1829
+ #endif
1830
+ new_version = _PyDict_NotifyEvent (
1831
+ interp , PyDict_EVENT_ADDED , mp , key , value );
1832
+ /* Insert into new slot. */
1833
+ mp -> ma_keys -> dk_version = 0 ;
1834
+ if (mp -> ma_keys -> dk_usable <= 0 ) {
1835
+ UNLOCK_KEYS (mp -> ma_keys );
1836
+
1837
+ if (split_dict_resize_and_insert (interp , mp , hash , key , value ) < 0 ) {
1838
+ goto Fail ;
1839
+ }
1840
+ } else {
1841
+ int insert = insert_split_dict (interp , mp , hash , key , value );
1842
+ UNLOCK_KEYS (mp -> ma_keys );
1843
+
1844
+ if (insert < 0 ) {
1845
+ goto Fail ;
1846
+ }
1847
+ }
1848
+ mp -> ma_keys -> dk_version = new_version ;
1797
1849
}
1798
1850
1799
1851
mp -> ma_used ++ ;
@@ -1802,6 +1854,9 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
1802
1854
return 0 ;
1803
1855
}
1804
1856
1857
+ #ifdef Py_GIL_DISABLED
1858
+ insert_on_split_race :
1859
+ #endif
1805
1860
if (old_value != value ) {
1806
1861
uint64_t new_version = _PyDict_NotifyEvent (
1807
1862
interp , PyDict_EVENT_MODIFIED , mp , key , value );
@@ -4255,13 +4310,40 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
4255
4310
}
4256
4311
}
4257
4312
else {
4258
- if (insert_split_dict (interp , mp , hash , Py_NewRef (key ), Py_NewRef (value )) < 0 ) {
4259
- Py_DECREF (key );
4260
- Py_DECREF (value );
4261
- if (result ) {
4262
- * result = NULL ;
4313
+ LOCK_KEYS (mp -> ma_keys );
4314
+ #ifdef Py_GIL_DISABLED
4315
+ // We could have raced between our lookup before and the insert,
4316
+ // so we need to lookup again with the keys locked
4317
+ ix = _Py_dict_lookup (mp , key , hash , & value );
4318
+ if (ix >= 0 ) {
4319
+ UNLOCK_KEYS (mp -> ma_keys );
4320
+ if (value != NULL ) {
4321
+ if (result ) {
4322
+ * result = incref_result ? Py_NewRef (value ) : value ;
4323
+ }
4324
+ return 0 ;
4325
+ }
4326
+ goto insert_on_split_race ;
4327
+ }
4328
+ #endif
4329
+ if (mp -> ma_keys -> dk_usable <= 0 ) {
4330
+ UNLOCK_KEYS (mp -> ma_keys );
4331
+
4332
+ if (split_dict_resize_and_insert (interp , mp , hash , key , value ) < 0 ) {
4333
+ return -1 ;
4334
+ }
4335
+ } else {
4336
+ int insert = insert_split_dict (interp , mp , hash , Py_NewRef (key ), Py_NewRef (value ));
4337
+ UNLOCK_KEYS (mp -> ma_keys );
4338
+
4339
+ if (insert < 0 ) {
4340
+ Py_DECREF (key );
4341
+ Py_DECREF (value );
4342
+ if (result ) {
4343
+ * result = NULL ;
4344
+ }
4345
+ return -1 ;
4263
4346
}
4264
- return -1 ;
4265
4347
}
4266
4348
}
4267
4349
@@ -4276,6 +4358,9 @@ dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_valu
4276
4358
return 0 ;
4277
4359
}
4278
4360
else if (value == NULL ) {
4361
+ #ifdef Py_GIL_DISABLED
4362
+ insert_on_split_race :
4363
+ #endif
4279
4364
uint64_t new_version = _PyDict_NotifyEvent (
4280
4365
interp , PyDict_EVENT_ADDED , mp , key , default_value );
4281
4366
value = default_value ;
0 commit comments