Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-112075: Iterating a dict shouldn't require locks #115108

Merged
merged 7 commits into from
Feb 22, 2024
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Don't use atomics for iteratio and allow multiple threads to race on …
…values seen
  • Loading branch information
DinoV committed Feb 22, 2024
commit 39bc495d4dd3ba2e085decd20c6f72e1b65689a1
33 changes: 10 additions & 23 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -5342,7 +5342,7 @@ dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self,

ensure_shared_on_read(d);

Py_ssize_t start_pos = i = _Py_atomic_load_ssize_relaxed(&di->di_pos);
i = _Py_atomic_load_ssize_relaxed(&di->di_pos);
k = _Py_atomic_load_ptr_relaxed(&d->ma_keys);
assert(i >= 0);
if (_PyDict_HasSplitTable(d)) {
Expand Down Expand Up @@ -5402,20 +5402,13 @@ dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self,
}
}
// We found an element (key), but did not expect it
if (_Py_atomic_load_ssize_relaxed(&di->len) == 0) {
Py_ssize_t len;
if ((len = _Py_atomic_load_ssize_relaxed(&di->len)) == 0) {
goto concurrent_modification;
}
if (!_Py_atomic_compare_exchange_ssize(&di->di_pos, &start_pos, i+1)) {
// We lost a race with someone else iterating...
if (out_key != NULL) {
Py_DECREF(*out_key);
}
if (out_value != NULL) {
Py_DECREF(*out_value);
}
goto try_locked;
}
_Py_atomic_add_ssize(&di->len, -1);

_Py_atomic_store_ssize_relaxed(&di->di_pos, i + 1);
_Py_atomic_store_ssize_relaxed(&di->len, len - 1);
return 1;

concurrent_modification:
Expand All @@ -5441,22 +5434,16 @@ static bool
acquire_iter_result(PyObject *result)
{
#ifdef Py_GIL_DISABLED
if (Py_REFCNT(result) == 1) {
Py_INCREF(result);
if (Py_REFCNT(result) == 2) {
return true;
}
Py_DECREF(result);
return false;
}
return false;
if (_Py_IsOwnedByCurrentThread(result) &&
result->ob_ref_local == 1 &&
_Py_atomic_load_ssize_relaxed(&result->ob_ref_shared) == 0) {
#else
if (Py_REFCNT(result) == 1) {
#endif
Py_INCREF(result);
return true;
}
return false;
#endif
}

static PyObject *
Expand Down