Skip to content

Commit 9298103

Browse files
committed
More list
1 parent 9ac81d7 commit 9298103

File tree

3 files changed

+57
-7
lines changed

3 files changed

+57
-7
lines changed

Include/internal/pycore_list.h

+4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ _PyList_AppendTakeRef(PyListObject *self, PyObject *newitem)
2828
Py_ssize_t allocated = self->allocated;
2929
assert((size_t)len + 1 < PY_SSIZE_T_MAX);
3030
if (allocated > len) {
31+
#ifdef Py_GIL_DISABLED
32+
_Py_atomic_store_ptr_release(&self->ob_item[len], newitem);
33+
#else
3134
PyList_SET_ITEM(self, len, newitem);
35+
#endif
3236
Py_SET_SIZE(self, len + 1);
3337
return 0;
3438
}

Lib/test/test_free_threading/test_list.py

+46-4
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,30 @@
33
from threading import Thread
44
from unittest import TestCase
55

6+
from test.support import is_wasi
7+
8+
9+
class C:
10+
def __init__(self, v):
11+
self.v = v
12+
13+
14+
@unittest.skipIf(is_wasi, "WASI has no threads.")
615
class TestList(TestCase):
716
def test_racing_iter_append(self):
817

918
l = []
1019
OBJECT_COUNT = 10000
20+
1121
def writer_func():
1222
for i in range(OBJECT_COUNT):
13-
l.append(i + OBJECT_COUNT)
23+
l.append(C(i + OBJECT_COUNT))
1424

1525
def reader_func():
1626
while True:
1727
count = len(l)
1828
for i, x in enumerate(l):
19-
self.assertEqual(x, i + OBJECT_COUNT)
29+
self.assertEqual(x.v, i + OBJECT_COUNT)
2030
if count == OBJECT_COUNT:
2131
break
2232

@@ -29,10 +39,42 @@ def reader_func():
2939

3040
writer.start()
3141
writer.join()
32-
print('writer done')
3342
for reader in readers:
3443
reader.join()
35-
print('reader done')
44+
45+
def test_racing_iter_extend(self):
46+
iters = [
47+
lambda x: [x],
48+
]
49+
for iter_case in iters:
50+
with self.subTest(iter=iter_case):
51+
l = []
52+
OBJECT_COUNT = 10000
53+
54+
def writer_func():
55+
for i in range(OBJECT_COUNT):
56+
l.extend(iter_case(C(i + OBJECT_COUNT)))
57+
58+
def reader_func():
59+
while True:
60+
count = len(l)
61+
for i, x in enumerate(l):
62+
self.assertEqual(x.v, i + OBJECT_COUNT)
63+
if count == OBJECT_COUNT:
64+
break
65+
66+
writer = Thread(target=writer_func)
67+
readers = []
68+
for x in range(30):
69+
reader = Thread(target=reader_func)
70+
readers.append(reader)
71+
reader.start()
72+
73+
writer.start()
74+
writer.join()
75+
for reader in readers:
76+
reader.join()
77+
3678

3779
if __name__ == "__main__":
3880
unittest.main()

Objects/listobject.c

+7-3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ list_allocate_array(size_t capacity)
4545
if (capacity > PY_SSIZE_T_MAX/sizeof(PyObject*) - 1) {
4646
return NULL;
4747
}
48+
4849
_PyListArray *array = PyMem_Malloc(sizeof(_PyListArray) + capacity * sizeof(PyObject *));
4950
if (array == NULL) {
5051
return NULL;
@@ -141,6 +142,9 @@ list_resize(PyListObject *self, Py_ssize_t newsize)
141142
target_bytes = allocated * sizeof(PyObject*);
142143
}
143144
memcpy(array->ob_item, self->ob_item, target_bytes);
145+
}
146+
if (new_allocated > (size_t)allocated) {
147+
memset(array->ob_item + allocated, 0, sizeof(PyObject *) * (new_allocated - allocated));
144148
}
145149
_Py_atomic_store_ptr_release(&self->ob_item, &array->ob_item);
146150
self->allocated = new_allocated;
@@ -502,7 +506,7 @@ _PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem)
502506
Py_DECREF(newitem);
503507
return -1;
504508
}
505-
PyList_SET_ITEM(self, len, newitem);
509+
FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item[len], newitem);
506510
return 0;
507511
}
508512

@@ -1181,7 +1185,7 @@ list_extend_fast(PyListObject *self, PyObject *iterable)
11811185
PyObject **dest = self->ob_item + m;
11821186
for (Py_ssize_t i = 0; i < n; i++) {
11831187
PyObject *o = src[i];
1184-
dest[i] = Py_NewRef(o);
1188+
FT_ATOMIC_STORE_PTR_RELEASE(dest[i], Py_NewRef(o));
11851189
}
11861190
return 0;
11871191
}
@@ -1238,7 +1242,7 @@ list_extend_iter_lock_held(PyListObject *self, PyObject *iterable)
12381242

12391243
if (Py_SIZE(self) < self->allocated) {
12401244
Py_ssize_t len = Py_SIZE(self);
1241-
PyList_SET_ITEM(self, len, item); // steals item ref
1245+
FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item[len], item);
12421246
Py_SET_SIZE(self, len + 1);
12431247
}
12441248
else {

0 commit comments

Comments
 (0)