gh-112069: Make setiter_iternext to be thread-safe (gh-117935) · python/cpython@a734fd5

@@ -834,7 +834,7 @@ static PyMethodDef setiter_methods[] = {

834834835835

static PyObject *setiter_iternext(setiterobject *si)

836836

{

837-

PyObject *key;

837+

PyObject *key = NULL;

838838

Py_ssize_t i, mask;

839839

setentry *entry;

840840

PySetObject *so = si->si_set;

@@ -843,30 +843,35 @@ static PyObject *setiter_iternext(setiterobject *si)

843843

return NULL;

844844

assert (PyAnySet_Check(so));

845845846-

if (si->si_used != so->used) {

846+

Py_ssize_t so_used = FT_ATOMIC_LOAD_SSIZE(so->used);

847+

Py_ssize_t si_used = FT_ATOMIC_LOAD_SSIZE(si->si_used);

848+

if (si_used != so_used) {

847849

PyErr_SetString(PyExc_RuntimeError,

848850

"Set changed size during iteration");

849851

si->si_used = -1; /* Make this state sticky */

850852

return NULL;

851853

}

852854855+

Py_BEGIN_CRITICAL_SECTION(so);

853856

i = si->si_pos;

854857

assert(i>=0);

855858

entry = so->table;

856859

mask = so->mask;

857-

while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy))

860+

while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy)) {

858861

i++;

862+

}

863+

if (i <= mask) {

864+

key = Py_NewRef(entry[i].key);

865+

}

866+

Py_END_CRITICAL_SECTION();

859867

si->si_pos = i+1;

860-

if (i > mask)

861-

goto fail;

868+

if (key == NULL) {

869+

si->si_set = NULL;

870+

Py_DECREF(so);

871+

return NULL;

872+

}

862873

si->len--;

863-

key = entry[i].key;

864-

return Py_NewRef(key);

865-866-

fail:

867-

si->si_set = NULL;

868-

Py_DECREF(so);

869-

return NULL;

874+

return key;

870875

}

871876872877

PyTypeObject PySetIter_Type = {