Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,15 @@ set_values(PyDictObject *mp, PyDictValues *values)
_Py_atomic_store_ptr_release(&mp->ma_values, values);
}

// gh-151593: The _Py_LOCK_DONT_DETACH flag ensures that the outer critical
// section is not dropped if there is some contention on the keys lock.
// It also means that it will be important that LOCK_KEYS() is essentially the
// "inner-most" code and that we don't call Py_DECREF() or similar while
// holding the keys lock.
//
// We are not allowed to acquire other locks within LOCK_KEYS(). For example,
// PyType_Modified() must not be called within LOCK_KEYS() since it acquires
// the type lock.
#define LOCK_KEYS(keys) PyMutex_LockFlags(&keys->dk_mutex, _Py_LOCK_DONT_DETACH)
#define UNLOCK_KEYS(keys) PyMutex_Unlock(&keys->dk_mutex)

Expand Down Expand Up @@ -1923,12 +1932,13 @@ insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash)
}
#endif

bool inserted = false;
LOCK_KEYS(keys);
ix = unicodekeys_lookup_unicode(keys, key, hash);
if (ix == DKIX_EMPTY && keys->dk_usable > 0) {
// Insert into new slot
inserted = true;
FT_ATOMIC_STORE_UINT32_RELAXED(keys->dk_version, 0);
_PyDict_SplitKeysInvalidated(keys);
Py_ssize_t hashpos = find_empty_slot(keys, hash);
ix = keys->dk_nentries;
dictkeys_set_index(keys, hashpos, ix);
Expand All @@ -1938,6 +1948,13 @@ insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash)
}
assert (ix < SHARED_KEYS_MAX_SIZE);
UNLOCK_KEYS(keys);

if (inserted) {
// gh-151593: Calling PyType_Modified() with LOCK_KEYS() creates a
// deadlock. So only call the function after UNLOCK_KEYS().
_PyDict_SplitKeysInvalidated(keys);
}

return ix;
}

Expand Down
Loading