8000 Lock shared keys in `Py_dict_lookup` and use thread-safe lookup in `i… · python/cpython@f3e3db3 · GitHub
[go: up one dir, main page]

Skip to content

Commit f3e3db3

Browse files
committed
Lock shared keys in Py_dict_lookup and use thread-safe lookup in insertdict
1 parent b4fe02f commit f3e3db3

File tree

1 file changed

+58
-2
lines changed

1 file changed

+58
-2
lines changed

Objects/dictobject.c

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,16 @@ ASSERT_DICT_LOCKED(PyObject *op)
162162
assert(_Py_IsOwnedByCurrentThread((PyObject *)mp) || IS_DICT_SHARED(mp));
163163
#define LOAD_KEYS_NENTRIES(d)
164164

165+
#define LOCK_KEYS_IF_SPLIT(keys, kind) \
166+
if (kind == DICT_KEYS_SPLIT) { \
167+
LOCK_KEYS(dk); \
168+
}
169+
170+
#define UNLOCK_KEYS_IF_SPLIT(keys, kind) \
171+
if (kind == DICT_KEYS_SPLIT) { \
172+
UNLOCK_KEYS(dk); \
173+
}
174+
165175
static inline Py_ssize_t
166176
load_keys_nentries(PyDictObject *mp)
167177
{
@@ -195,6 +205,9 @@ set_values(PyDictObject *mp, PyDictValues *values)
195205
#define DECREF_KEYS(dk) _Py_atomic_add_ssize(&dk->dk_refcnt, -1)
196206
#define LOAD_KEYS_NENTIRES(keys) _Py_atomic_load_ssize_relaxed(&keys->dk_nentries)
197207

208+
#define INCREF_KEYS_FT(dk) dictkeys_incref(dk)
209+
#define DECREF_KEYS_FT(dk, shared) dictkeys_decref(_PyInterpreterState_GET(), dk, shared)
210+
198211
static inline void split_keys_entry_added(PyDictKeysObject *keys)
199212
{
200213
ASSERT_KEYS_LOCKED(keys);
@@ -216,6 +229,10 @@ static inline void split_keys_entry_added(PyDictKeysObject *keys)
216229
#define INCREF_KEYS(dk) dk->dk_refcnt++
217230
#define DECREF_KEYS(dk) dk->dk_refcnt--
218231
#define LOAD_KEYS_NENTIRES(keys) keys->dk_nentries
232+
#define INCREF_KEYS_FT(dk)
233+
#define DECREF_KEYS_FT(dk, shared)
234+
#define LOCK_KEYS_IF_SPLIT(keys, kind)
235+
#define UNLOCK_KEYS_IF_SPLIT(keys, kind)
219236
#define IS_DICT_SHARED(mp) (false)
220237
#define SET_DICT_SHARED(mp)
221238
#define LOAD_INDEX(keys, size, idx) ((const int##size##_t*)(keys->dk_indices))[idx]
@@ -1192,17 +1209,24 @@ _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **valu
11921209
PyDictKeysObject *dk;
11931210
DictKeysKind kind;
11941211
Py_ssize_t ix;
1195-
// TODO: Thread safety
11961212
start:
11971213
dk = mp->ma_keys;
11981214
kind = dk->dk_kind;
11991215

12001216
if (kind != DICT_KEYS_GENERAL) {
12011217
if (PyUnicode_CheckExact(key)) {
1218+
LOCK_KEYS_IF_SPLIT(dk, kind);
12021219
ix = unicodekeys_lookup_unicode(dk, key, hash);
1220+
UNLOCK_KEYS_IF_SPLIT(dk, kind);
12031221
}
12041222
else {
1223+
INCREF_KEYS_FT(dk);
1224+
LOCK_KEYS_IF_SPLIT(dk, kind);
1225+
12051226
ix = unicodekeys_lookup_generic(mp, dk, key, hash);
1227+
1228+
UNLOCK_KEYS_IF_SPLIT(dk, kind);
1229+
DECREF_KEYS_FT(dk, IS_DICT_SHARED(mp));
12061230
if (ix == DKIX_KEY_CHANGED) {
12071231
goto start;
12081232
}
@@ -1443,6 +1467,7 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
14431467
Py_DECREF(value);
14441468
goto read_failed;
14451469
}
1470+
14461471
}
14471472
}
14481473
else {
@@ -1709,7 +1734,38 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
17091734
assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL);
17101735
}
17111736

1712-
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &old_value);
1737+
Py_ssize_t ix;
1738+
#ifdef Py_GIL_DISABLED
1739+
PyDictKeysObject *dk = mp->ma_keys;
1740+
DictKeysKind kind = dk->dk_kind;
1741+
1742+
if (kind == DICT_KEYS_SPLIT) {
1743+
// Split keys can be mutated by other dictionaries, so we need
1744+
// to do a threadsafe lookup here. This is basically the split-key
1745+
// half of _Py_dict_lookup_threadsafe and avoids locking the
1746+
// shared keys if we can
1747+
if (PyUnicode_CheckExact(key)) {
1748+
ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
1749+
}
1750+
else {
1751+
ix = unicodekeys_lookup_generic_threadsafe(mp, dk, key, hash);
1752+
}
1753+
1754+
if (ix >= 0) {
1755+
old_value = mp->ma_values->values[ix];
1756+
}
1757+
else if (ix == DKIX_KEY_CHANGED) {
1758+
ix = _Py_dict_lookup(mp, key, hash, &old_value);
1759+
}
1760+
else {
1761+
old_value = NULL;
1762+
}
1763+
} else {
1764+
ix = _Py_dict_lookup(mp, key, hash, &old_value);
1765+
}
1766+
#else
1767+
ix = _Py_dict_lookup(mp, key, hash, &old_value);
1768+
#endif
17131769
if (ix == DKIX_ERROR)
17141770
goto Fail;
17151771

0 commit comments

Comments
 (0)
0