Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a2d4281

Browse filesBrowse files
authored
gh-112087: Make __sizeof__ and listiter_{len, next} to be threadsafe (gh-114843)
1 parent 4b2d178 commit a2d4281
Copy full SHA for a2d4281

File tree

7 files changed

+80
-69
lines changed
Filter options

7 files changed

+80
-69
lines changed

‎Lib/test/support/__init__.py

Copy file name to clipboardExpand all lines: Lib/test/support/__init__.py
+15-12Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,19 +1727,22 @@ def _check_tracemalloc():
17271727

17281728

17291729
def check_free_after_iterating(test, iter, cls, args=()):
1730-
class A(cls):
1731-
def __del__(self):
1732-
nonlocal done
1733-
done = True
1734-
try:
1735-
next(it)
1736-
except StopIteration:
1737-
pass
1738-
17391730
done = False
1740-
it = iter(A(*args))
1741-
# Issue 26494: Shouldn't crash
1742-
test.assertRaises(StopIteration, next, it)
1731+
def wrapper():
1732+
class A(cls):
1733+
def __del__(self):
1734+
nonlocal done
1735+
done = True
1736+
try:
1737+
next(it)
1738+
except StopIteration:
1739+
pass
1740+
1741+
it = iter(A(*args))
1742+
# Issue 26494: Shouldn't crash
1743+
test.assertRaises(StopIteration, next, it)
1744+
1745+
wrapper()
17431746
# The sequence should be deallocated just after the end of iterating
17441747
gc_collect()
17451748
test.assertTrue(done)

‎Lib/test/test_iter.py

Copy file name to clipboardExpand all lines: Lib/test/test_iter.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ def __eq__(self, other):
302302
# listiter_reduce_general
303303
self.assertEqual(
304304
run("reversed", orig["reversed"](list(range(8)))),
305-
(iter, ([],))
305+
(reversed, ([],))
306306
)
307307

308308
for case in types:
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
For an empty reverse iterator for list will be reduced to :func:`reversed`.
2+
Patch by Donghee Na

‎Objects/listobject.c

Copy file name to clipboardExpand all lines: Objects/listobject.c
+52-50Lines changed: 52 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ class list "PyListObject *" "&PyList_Type"
2020

2121
_Py_DECLARE_STR(list_err, "list index out of range");
2222

23+
#ifdef Py_GIL_DISABLED
24+
# define LOAD_SSIZE(value) _Py_atomic_load_ssize_relaxed(&value)
25+
# define STORE_SSIZE(value, new_value) _Py_atomic_store_ssize_relaxed(&value, new_value)
26+
#else
27+
# define LOAD_SSIZE(value) value
28+
# define STORE_SSIZE(value, new_value) value = new_value
29+
#endif
30+
2331
#ifdef WITH_FREELISTS
2432
static struct _Py_list_freelist *
2533
get_list_freelist(void)
@@ -2971,7 +2979,8 @@ list___sizeof___impl(PyListObject *self)
29712979
/*[clinic end generated code: output=3417541f95f9a53e input=b8030a5d5ce8a187]*/
29722980
{
29732981
size_t res = _PyObject_SIZE(Py_TYPE(self));
2974-
res += (size_t)self->allocated * sizeof(void*);
2982+
Py_ssize_t allocated = LOAD_SSIZE(self->allocated);
2983+
res += (size_t)allocated * sizeof(void*);
29752984
return PyLong_FromSize_t(res);
29762985
}
29772986

@@ -3373,33 +3382,34 @@ static PyObject *
33733382
listiter_next(PyObject *self)
33743383
{
33753384
_PyListIterObject *it = (_PyListIterObject *)self;
3376-
PyListObject *seq;
3377-
PyObject *item;
3378-
3379-
assert(it != NULL);
3380-
seq = it->it_seq;
3381-
if (seq == NULL)
3385+
Py_ssize_t index = LOAD_SSIZE(it->it_index);
3386+
if (index < 0) {
33823387
return NULL;
3383-
assert(PyList_Check(seq));
3384-
3385-
if (it->it_index < PyList_GET_SIZE(seq)) {
3386-
item = PyList_GET_ITEM(seq, it->it_index);
3387-
++it->it_index;
3388-
return Py_NewRef(item);
33893388
}
33903389

3391-
it->it_seq = NULL;
3392-
Py_DECREF(seq);
3393-
return NULL;
3390+
PyObject *item = list_get_item_ref(it->it_seq, index);
3391+
if (item == NULL) {
3392+
// out-of-bounds
3393+
STORE_SSIZE(it->it_index, -1);
3394+
#ifndef Py_GIL_DISABLED
3395+
PyListObject *seq = it->it_seq;
3396+
it->it_seq = NULL;
3397+
Py_DECREF(seq);
3398+
#endif
3399+
return NULL;
3400+
}
3401+
STORE_SSIZE(it->it_index, index + 1);
3402+
return item;
33943403
}
33953404

33963405
static PyObject *
33973406
listiter_len(PyObject *self, PyObject *Py_UNUSED(ignored))
33983407
{
3408+
assert(self != NULL);
33993409
_PyListIterObject *it = (_PyListIterObject *)self;
3400-
Py_ssize_t len;
3401-
if (it->it_seq) {
3402-
len = PyList_GET_SIZE(it->it_seq) - it->it_index;
3410+
Py_ssize_t index = LOAD_SSIZE(it->it_index);
3411+
if (index >= 0) {
3412+
Py_ssize_t len = PyList_GET_SIZE(it->it_seq) - index;
34033413
if (len >= 0)
34043414
return PyLong_FromSsize_t(len);
34053415
}
@@ -3420,8 +3430,8 @@ listiter_setstate(PyObject *self, PyObject *state)
34203430
if (index == -1 && PyErr_Occurred())
34213431
return NULL;
34223432
if (it->it_seq != NULL) {
3423-
if (index < 0)
3424-
index = 0;
3433+
if (index < -1)
3434+
index = -1;
34253435
else if (index > PyList_GET_SIZE(it->it_seq))
34263436
index = PyList_GET_SIZE(it->it_seq); /* iterator exhausted */
34273437
it->it_index = index;
@@ -3526,34 +3536,33 @@ static PyObject *
35263536
listreviter_next(PyObject *self)
35273537
{
35283538
listreviterobject *it = (listreviterobject *)self;
3529-
PyObject *item;
3530-
Py_ssize_t index;
3531-
PyListObject *seq;
3532-
35333539
assert(it != NULL);
3534-
seq = it->it_seq;
3535-
if (seq == NULL) {
3536-
return NULL;
3537-
}
3540+
PyListObject *seq = it->it_seq;
35383541
assert(PyList_Check(seq));
35393542

3540-
index = it->it_index;
3541-
if (index>=0 && index < PyList_GET_SIZE(seq)) {
3542-
item = PyList_GET_ITEM(seq, index);
3543-
it->it_index--;
3544-
return Py_NewRef(item);
3543+
Py_ssize_t index = LOAD_SSIZE(it->it_index);
3544+
if (index < 0) {
3545+
return NULL;
3546+
}
3547+
PyObject *item = list_get_item_ref(seq, index);
3548+
if (item != NULL) {
3549+
STORE_SSIZE(it->it_index, index - 1);
3550+
return item;
35453551
}
3546-
it->it_index = -1;
3552+
STORE_SSIZE(it->it_index, -1);
3553+
#ifndef Py_GIL_DISABLED
35473554
it->it_seq = NULL;
35483555
Py_DECREF(seq);
3556+
#endif
35493557
return NULL;
35503558
}
35513559

35523560
static PyObject *
35533561
listreviter_len(PyObject *self, PyObject *Py_UNUSED(ignored))
35543562
{
35553563
listreviterobject *it = (listreviterobject *)self;
3556-
Py_ssize_t len = it->it_index + 1;
3564+
Py_ssize_t index = LOAD_SSIZE(it->it_index);
3565+
Py_ssize_t len = index + 1;
35573566
if (it->it_seq == NULL || PyList_GET_SIZE(it->it_seq) < len)
35583567
len = 0;
35593568
return PyLong_FromSsize_t(len);
@@ -3588,36 +3597,29 @@ static PyObject *
35883597
listiter_reduce_general(void *_it, int forward)
35893598
{
35903599
PyObject *list;
3600+
PyObject *iter;
35913601

35923602
/* _PyEval_GetBuiltin can invoke arbitrary code,
35933603
* call must be before access of iterator pointers.
35943604
* see issue #101765 */
35953605

35963606
/* the objects are not the same, index is of different types! */
35973607
if (forward) {
3598-
PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter));
3599-
if (!iter) {
3600-
return NULL;
3601-
}
3608+
iter = _PyEval_GetBuiltin(&_Py_ID(iter));
36023609
_PyListIterObject *it = (_PyListIterObject *)_it;
3603-
if (it->it_seq) {
3610+
if (it->it_index >= 0) {
36043611
return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index);
36053612
}
3606-
Py_DECREF(iter);
36073613
} else {
3608-
PyObject *reversed = _PyEval_GetBuiltin(&_Py_ID(reversed));
3609-
if (!reversed) {
3610-
return NULL;
3611-
}
3614+
iter = _PyEval_GetBuiltin(&_Py_ID(reversed));
36123615
listreviterobject *it = (listreviterobject *)_it;
3613-
if (it->it_seq) {
3614-
return Py_BuildValue("N(O)n", reversed, it->it_seq, it->it_index);
3616+
if (it->it_index >= 0) {
3617+
return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index);
36153618
}
3616-
Py_DECREF(reversed);
36173619
}
36183620
/* empty iterator, create an empty list */
36193621
list = PyList_New(0);
36203622
if (list == NULL)
36213623
return NULL;
3622-
return Py_BuildValue("N(N)", _PyEval_GetBuiltin(&_Py_ID(iter)), list);
3624+
return Py_BuildValue("N(N)", iter, list);
36233625
}

‎Python/bytecodes.c

Copy file name to clipboardExpand all lines: Python/bytecodes.c
+5-3Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2606,11 +2606,14 @@ dummy_func(
26062606
assert(Py_TYPE(iter) == &PyListIter_Type);
26072607
STAT_INC(FOR_ITER, hit);
26082608
PyListObject *seq = it->it_seq;
2609-
if (seq == NULL || it->it_index >= PyList_GET_SIZE(seq)) {
2609+
if ((size_t)it->it_index >= (size_t)PyList_GET_SIZE(seq)) {
2610+
it->it_index = -1;
2611+
#ifndef Py_GIL_DISABLED
26102612
if (seq != NULL) {
26112613
it->it_seq = NULL;
26122614
Py_DECREF(seq);
26132615
}
2616+
#endif
26142617
Py_DECREF(iter);
26152618
STACK_SHRINK(1);
26162619
/* Jump forward oparg, then skip following END_FOR and POP_TOP instructions */
@@ -2624,8 +2627,7 @@ dummy_func(
26242627
_PyListIterObject *it = (_PyListIterObject *)iter;
26252628
assert(Py_TYPE(iter) == &PyListIter_Type);
26262629
PyListObject *seq = it->it_seq;
2627-
DEOPT_IF(seq == NULL);
2628-
DEOPT_IF(it->it_index >= PyList_GET_SIZE(seq));
2630+
DEOPT_IF((size_t)it->it_index >= (size_t)PyList_GET_SIZE(seq));
26292631
}
26302632

26312633
op(_ITER_NEXT_LIST, (iter -- iter, next)) {

‎Python/executor_cases.c.h

Copy file name to clipboardExpand all lines: Python/executor_cases.c.h
+1-2Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Python/generated_cases.c.h

Copy file name to clipboardExpand all lines: Python/generated_cases.c.h
+4-1Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.