Skip to content

Commit bb4bf90

Browse files
committed
iterator safe from multi-crit deadlocks
1 parent 6e7b65a commit bb4bf90

File tree

2 files changed

+52
-72
lines changed

2 files changed

+52
-72
lines changed

Modules/arraymodule.c

Lines changed: 49 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3199,53 +3199,46 @@ array_iter(arrayobject *ao)
31993199
return NULL;
32003200

32013201
it->ao = (arrayobject*)Py_NewRef(ao);
3202-
it->index = 0;
3202+
it->index = 0; // -1 indicates exhausted
32033203
it->getitem = ao->ob_descr->getitem;
32043204
PyObject_GC_Track(it);
32053205
return (PyObject *)it;
32063206
}
32073207

32083208
static PyObject *
3209-
arrayiter_next_lock_held(arrayiterobject *it)
3209+
arrayiter_next(arrayiterobject *it)
32103210
{
3211-
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(it);
3212-
arrayobject *ao;
3213-
3211+
Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(it->index);
3212+
if (index < 0) {
3213+
return NULL;
3214+
}
3215+
PyObject *ret;
32143216
#ifndef NDEBUG
32153217
array_state *state = find_array_state_by_type(Py_TYPE(it));
32163218
assert(PyObject_TypeCheck(it, state->ArrayIterType));
3219+
assert(array_Check(it->ao, state));
32173220
#endif
3218-
ao = it->ao;
3219-
if (ao == NULL) {
3220-
return NULL;
3221+
3222+
Py_BEGIN_CRITICAL_SECTION(it->ao);
3223+
if (index < Py_SIZE(it->ao)) {
3224+
ret = (*it->getitem)(it->ao, index);
32213225
}
3222-
PyObject *ret = NULL;
3223-
Py_BEGIN_CRITICAL_SECTION(ao);
3224-
#ifndef NDEBUG
3225-
assert(array_Check(ao, state));
3226-
#endif
3227-
if (it->index < Py_SIZE(ao)) {
3228-
ret = (*it->getitem)(ao, it->index++);
3226+
else {
3227+
ret = NULL;
32293228
}
32303229
Py_END_CRITICAL_SECTION();
3230+
32313231
if (ret != NULL) {
3232-
return ret;
3232+
FT_ATOMIC_STORE_SSIZE_RELAXED(it->index, index + 1);
3233+
}
3234+
else {
3235+
FT_ATOMIC_STORE_SSIZE_RELAXED(it->index, -1);
3236+
#ifndef Py_GIL_DISABLED
3237+
arrayobject *ao = it->ao;
3238+
it->ao = NULL;
3239+
Py_DECREF(ao);
3240+
#endif
32333241
}
3234-
it->ao = NULL;
3235-
Py_DECREF(ao);
3236-
return NULL;
3237-
}
3238-
3239-
static PyObject *
3240-
arrayiter_next(arrayiterobject *it)
3241-
{
3242-
PyObject *ret;
3243-
assert(it != NULL);
3244-
3245-
Py_BEGIN_CRITICAL_SECTION(it);
3246-
ret = arrayiter_next_lock_held(it);
3247-
Py_END_CRITICAL_SECTION();
3248-
32493242
return ret;
32503243
}
32513244

@@ -3269,7 +3262,6 @@ arrayiter_traverse(arrayiterobject *it, visitproc visit, void *arg)
32693262
}
32703263

32713264
/*[clinic input]
3272-
@critical_section
32733265
array.arrayiterator.__reduce__
32743266
32753267
cls: defining_class
@@ -3280,19 +3272,27 @@ Return state information for pickling.
32803272

32813273
static PyObject *
32823274
array_arrayiterator___reduce___impl(arrayiterobject *self, PyTypeObject *cls)
3283-
/*[clinic end generated code: output=4b032417a2c8f5e6 input=61ad213fe49ae0f7]*/
3275+
/*[clinic end generated code: output=4b032417a2c8f5e6 input=ac64e65a87ad452e]*/
32843276
{
32853277
array_state *state = get_array_state_by_class(cls);
32863278
assert(state != NULL);
32873279
PyObject *func = _PyEval_GetBuiltin(state->str_iter);
3288-
if (self->ao == NULL) {
3289-
return Py_BuildValue("N(())", func);
3280+
PyObject *ret = NULL;
3281+
Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->index);
3282+
if (index >= 0) {
3283+
Py_BEGIN_CRITICAL_SECTION(self->ao);
3284+
if (index <= Py_SIZE(self->ao)) {
3285+
ret = Py_BuildValue("N(O)n", func, self->ao, index);
3286+
}
3287+
Py_END_CRITICAL_SECTION();
3288+
}
3289+
if (ret == NULL) {
3290+
ret = Py_BuildValue("N(())", func);
32903291
}
3291-
return Py_BuildValue("N(O)n", func, self->ao, self->index);
3292+
return ret;
32923293
}
32933294

32943295
/*[clinic input]
3295-
@critical_section
32963296
array.arrayiterator.__setstate__
32973297
32983298
state: object
@@ -3302,24 +3302,26 @@ Set state information for unpickling.
33023302
[clinic start generated code]*/
33033303

33043304
static PyObject *
3305-
array_arrayiterator___setstate___impl(arrayiterobject *self, PyObject *state)
3306-
/*[clinic end generated code: output=d7837ae4ac1fd8b9 input=8d8dc7ce40b9c1f7]*/
3305+
array_arrayiterator___setstate__(arrayiterobject *self, PyObject *state)
3306+
/*[clinic end generated code: output=397da9904e443cbe input=f47d5ceda19e787b]*/
33073307
{
33083308
Py_ssize_t index = PyLong_AsSsize_t(state);
33093309
if (index == -1 && PyErr_Occurred()) {
33103310
return NULL;
33113311
}
3312-
arrayobject *ao = self->ao;
3313-
if (ao != NULL) {
3314-
Py_BEGIN_CRITICAL_SECTION(ao);
3315-
if (index < 0) {
3316-
index = 0;
3312+
if (FT_ATOMIC_LOAD_SSIZE_RELAXED(self->index) >= 0) {
3313+
Py_BEGIN_CRITICAL_SECTION(self->ao);
3314+
if (index < -1) {
3315+
index = -1;
33173316
}
3318-
else if (index > Py_SIZE(ao)) {
3319-
index = Py_SIZE(ao); /* iterator exhausted */
3317+
else {
3318+
Py_ssize_t size = Py_SIZE(self->ao);
3319+
if (index > size) {
3320+
index = size; /* iterator at end */
3321+
}
33203322
}
3323+
FT_ATOMIC_STORE_SSIZE_RELAXED(self->index, index);
33213324
Py_END_CRITICAL_SECTION();
3322-
self->index = index;
33233325
}
33243326
Py_RETURN_NONE;
33253327
}

Modules/clinic/arraymodule.c.h

Lines changed: 3 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)