8000 Less duplication, improved comments, better handling of new keys duri… · python/cpython@af7ddb9 · GitHub
[go: up one dir, main page]

Skip to content

Commit af7ddb9

Browse files
committed
Less duplication, improved comments, better handling of new keys during resize
1 parent 39c1a11 commit af7ddb9

File tree

1 file changed

+42
-96
lines changed

1 file changed

+42
-96
lines changed

Objects/dictobject.c

Lines changed: 42 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ set_keys(PyDictObject *mp, PyDictKeysObject *keys)
167167
}
168168

169169
static inline void
170-
set_values(PyDictObject *mp, PyDictValues *values) {
170+
set_values(PyDictObject *mp, PyDictValues *values)
171+
{
171172
ASSERT_OWNED_OR_SHARED(mp);
172173
_Py_atomic_store_ptr_release(&mp->ma_values, values);
173174
}
@@ -296,8 +297,6 @@ static int dictresize(PyInterpreterState *interp, PyDictObject *mp,
296297

297298
static PyObject* dict_iter(PyObject *dict);
298299

299-
static int
300-
contains_known_hash(PyDictObject *mp, PyObject *key, Py_ssize_t hash);
301300
static int
302301
setitem_lock_held(PyDictObject *mp, PyObject *key, PyObject *value);
303302
static int
@@ -764,9 +763,9 @@ new_values(size_t size)
764763
}
765764

766765
static inline void
767-
free_values(PyDictValues *values, int use_qsbr)
766+
free_values(PyDictValues *values, bool use_qsbr)
768767
{
769-
int prefix_size = ((uint8_t *)values)[-1];
768+
int prefix_size = DICT_VALUES_SIZE(values);
770769
#ifdef Py_GIL_DISABLED
771770
if (use_qsbr) {
772771
_PyMem_FreeQsbr(((char *)values)-prefix_size);
@@ -1154,12 +1153,10 @@ ensure_shared_on_read(PyDictObject *mp)
11541153
{
11551154
#ifdef Py_GIL_DISABLED
11561155
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...
11631160
Py_BEGIN_CRITICAL_SECTION(mp);
11641161
if (!IS_DICT_SHARED(mp)) {
11651162
SET_DICT_SHARED(mp);
@@ -1176,7 +1173,7 @@ ensure_shared_on_resize(PyDictObject *mp)
11761173
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp);
11771174

11781175
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
11801177
// it and we haven't marked it as shared which will ensure
11811178
// that when we re-size ma_keys or ma_values that we will
11821179
// free using QSBR. We need to lock the dictionary to
@@ -1234,8 +1231,8 @@ unicodekeys_lookup_generic_threadsafe(PyDictObject *mp, PyDictKeysObject* dk, Py
12341231
return do_lookup(mp, dk, key, hash, compare_unicode_generic_threadsafe);
12351232
}
12361233

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,
12391236
void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash)
12401237
{
12411238
PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix];
@@ -1646,7 +1643,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
16461643
return -1;
16471644
}
16481645

1649-
// Same to insertdict but specialized for ma_keys == Py_EMPTY_KEYS.
1646+
// Same as insertdict but specialized for ma_keys == Py_EMPTY_KEYS.
16501647
// Consumes key and value references.
16511648
static int
16521649
insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
@@ -1691,7 +1688,7 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
16911688
// We store the keys last so no one can see them in a partially inconsistent
16921689
// state so that we don't need to switch the keys to being shared yet for
16931690
// 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
16951692
// as the empty keys will never be freed.
16961693
#ifdef Py_GIL_DISABLED
16971694
_Py_atomic_store_ptr_release(&mp->ma_keys, newkeys);
@@ -1753,7 +1750,7 @@ static int
17531750
dictresize(PyInterpreterState *interp, PyDictObject *mp,
17541751
uint8_t log2_newsize, int unicode)
17551752
{
1756-
PyDictKeysObject *oldkeys;
1753+
PyDictKeysObject *oldkeys, *newkeys;
17571754
PyDictValues *oldvalues;
17581755

17591756
ASSERT_DICT_LOCKED(mp);
@@ -1778,13 +1775,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
17781775
*/
17791776

17801777
/* 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) {
17841780
return -1;
17851781
}
17861782
// New table must be large enough.
1787-
assert(mp->ma_keys->dk_usable >= mp->ma_used);
1783+
assert(newkeys->dk_usable >= mp->ma_used);
17881784

17891785
Py_ssize_t numentries = mp->ma_used;
17901786

@@ -1793,9 +1789,9 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
17931789
/* Convert split table into new combined table.
17941790
* We must incref keys; we can transfer values.
17951791
*/
1796-
if (mp->ma_keys->dk_kind == DICT_KEYS_GENERAL) {
1792+
if (newkeys->dk_kind == DICT_KEYS_GENERAL) {
17971793
// split -> generic
1798-
PyDictKeyEntry *newentries = DK_ENTRIES(mp->ma_keys);
1794+
PyDictKeyEntry *newentries = DK_ENTRIES(newkeys);
17991795

18001796
for (Py_ssize_t i = 0; i < numentries; i++) {
18011797
int index = get_index_from_order(mp, i);
@@ -1805,10 +1801,10 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
18051801
newentries[i].me_hash = unicode_get_hash(ep->me_key);
18061802
newentries[i].me_value = oldvalues->values[index];
18071803
}
1808-
build_indices_generic(mp->ma_keys, newentries, numentries);
1804+
build_indices_generic(newkeys, newentries, numentries);
18091805
}
18101806
else { // split -> combined unicode
1811-
PyDictUnicodeEntry *newentries = DK_UNICODE_ENTRIES(mp->ma_keys);
1807+
PyDictUnicodeEntry *newentries = DK_UNICODE_ENTRIES(newkeys);
18121808

18131809
for (Py_ssize_t i = 0; i < numentries; i++) {
18141810
int index = get_index_from_order(mp, i);
@@ -1817,18 +1813,19 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
18171813
newentries[i].me_key = Py_NewRef(ep->me_key);
18181814
newentries[i].me_value = oldvalues->values[index];
18191815
}
1820-
build_indices_unicode(mp->ma_keys, newentries, numentries);
1816+
build_indices_unicode(newkeys, newentries, numentries);
18211817
}
1818+
set_keys(mp, newkeys);
18221819
dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp));
18231820
set_values(mp, NULL);
18241821
free_values(oldvalues, IS_DICT_SHARED(mp));
18251822
}
18261823
else { // oldkeys is combined.
18271824
if (oldkeys->dk_kind == DICT_KEYS_GENERAL) {
18281825
// generic -> generic
1829-
assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL);
1826+
assert(newkeys->dk_kind == DICT_KEYS_GENERAL);
18301827
PyDictKeyEntry *oldentries = DK_ENTRIES(oldkeys);
1831-
PyDictKeyEntry *newentries = DK_ENTRIES(mp->ma_keys);
1828+
PyDictKeyEntry *newentries = DK_ENTRIES(newkeys);
18321829
if (oldkeys->dk_nentries == numentries) {
18331830
memcpy(newentries, oldentries, numentries * sizeof(PyDictKeyEntry));
18341831
}
@@ -1840,12 +1837,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
18401837
newentries[i] = *ep++;
18411838
}
18421839
}
1843-
build_indices_generic(mp->ma_keys, newentries, numentries);
1840+
build_indices_generic(newkeys, newentries, numentries);
18441841
}
18451842
else { // oldkeys is combined unicode
18461843
PyDictUnicodeEntry *oldentries = DK_UNICODE_ENTRIES(oldkeys);
18471844
if (unicode) { // combined unicode -> combined unicode
1848-
PyDictUnicodeEntry *newentries = DK_UNICODE_ENTRIES(mp->ma_keys);
1845+
PyDictUnicodeEntry *newentries = DK_UNICODE_ENTRIES(newkeys);
18491846
if (oldkeys->dk_nentries == numentries && mp->ma_keys->dk_kind == DICT_KEYS_UNICODE) {
18501847
memcpy(newentries, oldentries, numentries * sizeof(PyDictUnicodeEntry));
18511848
}
@@ -1857,10 +1854,10 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
18571854
newentries[i] = *ep++;
18581855
}
18591856
}
1860-
build_indices_unicode(mp->ma_keys, newentries, numentries);
1857+
build_indices_unicode(newkeys, newentries, numentries);
18611858
}
18621859
else { // combined unicode -> generic
1863-
PyDictKeyEntry *newentries = DK_ENTRIES(mp->ma_keys);
1860+
PyDictKeyEntry *newentries = DK_ENTRIES(newkeys);
18641861
PyDictUnicodeEntry *ep = oldentries;
18651862
for (Py_ssize_t i = 0; i < numentries; i++) {
18661863
while (ep->me_value == NULL)
@@ -1870,10 +1867,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
18701867
newentries[i].me_value = ep->me_value;
18711868
ep++;
18721869
}
1873-
build_indices_generic(mp->ma_keys, newentries, numentries);
1870+
build_indices_generic(newkeys, newentries, numentries);
18741871
}
18751872
}
18761873

1874+
set_keys(mp, newkeys);
1875+
18771876
if (oldkeys != Py_EMPTY_KEYS) {
18781877
#ifdef Py_REF_DEBUG
18791878
_Py_DecRefTotal(_PyInterpreterState_GET());
@@ -3528,7 +3527,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
35283527
Py_NewRef(key), hash, Py_NewRef(value));
35293528
}
35303529
else {
3531-
err = contains_known_hash(mp, key, hash);
3530+
err = _PyDict_Contains_KnownHash((PyObject *)mp, key, hash);
35323531
if (err == 0) {
35333532
err = insertdict(interp, mp,
35343533
Py_NewRef(key), hash, Py_NewRef(value));
@@ -3921,29 +3920,14 @@ static PyObject *
39213920
dict___contains__(PyDictObject *self, PyObject *key)
39223921
/*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/
39233922
{
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) {
39403925
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;
39473931
}
39483932

39493933
/*[clinic input]
@@ -4450,57 +4434,19 @@ static PyMethodDef mapp_methods[] = {
44504434
{NULL, NULL} /* sentinel */
44514435
};
44524436

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-
44764437
/* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */
44774438
int
44784439
PyDict_Contains(PyObject *op, PyObject *key)
44794440
{
44804441
Py_hash_t hash;
4481-
Py_ssize_t ix;
4482-
PyObject *value;
4483-
PyDictObject *mp = (PyDictObject *)op;
44844442

44854443
if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
44864444
hash = PyObject_Hash(key);
44874445
if (hash == -1)
44884446
return -1;
44894447
}
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);
45044450
}
45054451

45064452
int

0 commit comments

Comments
 (0)
0