Skip to content

Commit 32da182

Browse files
committed
gh-115605: Update list_new to follow nogil-3.12 build
1 parent 200271c commit 32da182

File tree

2 files changed

+96
-54
lines changed

2 files changed

+96
-54
lines changed

Lib/test/test_list.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sys
22
from test import list_tests
3-
from test.support import cpython_only
3+
from test.support import cpython_only, Py_GIL_DISABLED
44
import pickle
55
import unittest
66

@@ -230,6 +230,7 @@ def __eq__(self, other):
230230
self.assertFalse(list3 == list4)
231231

232232
@cpython_only
233+
@unittest.skipIf(Py_GIL_DISABLED, 'Only for the default build')
233234
def test_preallocation(self):
234235
iterable = [0] * 10
235236
iter_size = sys.getsizeof(iterable)

Objects/listobject.c

Lines changed: 94 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,90 @@ get_list_freelist(void)
3131
}
3232
#endif
3333

34+
static size_t
35+
list_good_size(Py_ssize_t size)
36+
{
37+
// 4, 8, 16, 24, 32, 40, 48, 64, 80, ...
38+
// NOTE: we add one here so that the rounding accounts for the "allocated"
39+
// field in _PyListArray.
40+
size_t reqsize = (size_t)size + 1;
41+
if (reqsize <= 4) {
42+
reqsize = 4;
43+
}
44+
else if (reqsize <= 48) {
45+
reqsize = (reqsize + 7) & ~7;
46+
}
47+
else {
48+
reqsize = (reqsize + 15) & ~15;
49+
if (reqsize <= MI_MEDIUM_OBJ_WSIZE_MAX) {
50+
reqsize = mi_good_size(reqsize * sizeof(PyObject *))/sizeof(PyObject*);
51+
}
52+
else {
53+
// ensure geometric spacing for large arrays
54+
size_t shift = mi_bsr(reqsize) - 2;
55+
reqsize = ((reqsize >> shift) + 1) << shift;
56+
}
57+
}
58+
return reqsize - 1;
59+
}
60+
61+
static PyObject**
62+
list_allocate_items(size_t capacity)
63+
{
64+
if (capacity > PY_SSIZE_T_MAX / sizeof(PyObject *) - 1) {
65+
return NULL;
66+
}
67+
PyObject **items = PyMem_Malloc(capacity * sizeof(PyObject *));
68+
return items;
69+
}
70+
71+
static PyListObject *
72+
list_new(Py_ssize_t size)
73+
{
74+
PyListObject *op;
75+
assert(size >= 0);
76+
#ifdef WITH_FREELISTS
77+
struct _Py_list_freelist *list_freelist = get_list_freelist();
78+
if (PyList_MAXFREELIST && list_freelist->numfree > 0) {
79+
list_freelist->numfree--;
80+
op = list_freelist->items[list_freelist->numfree];
81+
OBJECT_STAT_INC(from_freelist);
82+
_Py_NewReference((PyObject *)op);
83+
}
84+
else
85+
#endif
86+
{
87+
op = PyObject_GC_New(PyListObject, &PyList_Type);
88+
if (op == NULL) {
89+
return NULL;
90+
}
91+
}
92+
if (size <= 0) {
93+
op->ob_item = NULL;
94+
op->allocated = 0;
95+
}
96+
else {
97+
#ifdef Py_GIL_DISABLED
98+
size_t capacity = list_good_size(size);
99+
PyObject **items = list_allocate_items(capacity);
100+
#else
101+
size_t capacity = size;
102+
PyObject **items = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));
103+
#endif
104+
if (items == NULL) {
105+
op->ob_item = NULL;
106+
Py_DECREF(op);
107+
PyErr_NoMemory();
108+
return NULL;
109+
}
110+
op->ob_item = items;
111+
op->allocated = capacity;
112+
}
113+
Py_SET_SIZE(op, size);
114+
_PyObject_GC_TRACK(op);
115+
return op;
116+
}
117+
34118
/* Ensure ob_item has room for at least newsize elements, and set
35119
* ob_size to newsize. If newsize > ob_size on entry, the content
36120
* of the new slots at exit is undefined heap trash; it's the caller's
@@ -151,61 +235,18 @@ _PyList_DebugMallocStats(FILE *out)
151235
PyObject *
152236
PyList_New(Py_ssize_t size)
153237
{
154-
PyListObject *op;
155-
156238
if (size < 0) {
157239
PyErr_BadInternalCall();
158240
return NULL;
159241
}
160-
161-
#ifdef WITH_FREELISTS
162-
struct _Py_list_freelist *list_freelist = get_list_freelist();
163-
if (PyList_MAXFREELIST && list_freelist->numfree > 0) {
164-
list_freelist->numfree--;
165-
op = list_freelist->items[list_freelist->numfree];
166-
OBJECT_STAT_INC(from_freelist);
167-
_Py_NewReference((PyObject *)op);
168-
}
169-
else
170-
#endif
171-
{
172-
op = PyObject_GC_New(PyListObject, &PyList_Type);
173-
if (op == NULL) {
174-
return NULL;
175-
}
176-
}
177-
if (size <= 0) {
178-
op->ob_item = NULL;
179-
}
180-
else {
181-
op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));
182-
if (op->ob_item == NULL) {
183-
Py_DECREF(op);
184-
return PyErr_NoMemory();
242+
PyListObject *op = list_new(size);
243+
if (op && op->ob_item) {
244+
PyObject **items = op->ob_item;
245+
for (Py_ssize_t i = 0, n = op->allocated; i < n; i++) {
246+
FT_ATOMIC_STORE_PTR_RELEASE(items[i], NULL);
185247
}
186248
}
187-
Py_SET_SIZE(op, size);
188-
op->allocated = size;
189-
_PyObject_GC_TRACK(op);
190-
return (PyObject *) op;
191-
}
192-
193-
static PyObject *
194-
list_new_prealloc(Py_ssize_t size)
195-
{
196-
assert(size > 0);
197-
PyListObject *op = (PyListObject *) PyList_New(0);
198-
if (op == NULL) {
199-
return NULL;
200-
}
201-
assert(op->ob_item == NULL);
202-
op->ob_item = PyMem_New(PyObject *, size);
203-
if (op->ob_item == NULL) {
204-
Py_DECREF(op);
205-
return PyErr_NoMemory();
206-
}
207-
op->allocated = size;
208-
return (PyObject *) op;
249+
return (PyObject *)op;
209250
}
210251

211252
Py_ssize_t
@@ -515,7 +556,7 @@ list_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
515556
if (len <= 0) {
516557
return PyList_New(0);
517558
}
518-
np = (PyListObject *) list_new_prealloc(len);
559+
np = (PyListObject *) list_new(len);
519560
if (np == NULL)
520561
return NULL;
521562

@@ -567,7 +608,7 @@ list_concat_lock_held(PyListObject *a, PyListObject *b)
567608
if (size == 0) {
568609
return PyList_New(0);
569610
}
570-
np = (PyListObject *) list_new_prealloc(size);
611+
np = (PyListObject *) list_new(size);
571612
if (np == NULL) {
572613
return NULL;
573614
}
@@ -617,7 +658,7 @@ list_repeat_lock_held(PyListObject *a, Py_ssize_t n)
617658
return PyErr_NoMemory();
618659
Py_ssize_t output_size = input_size * n;
619660

620-
PyListObject *np = (PyListObject *) list_new_prealloc(output_size);
661+
PyListObject *np = (PyListObject *) list_new(output_size);
621662
if (np == NULL)
622663
return NULL;
623664

@@ -3095,7 +3136,7 @@ list_subscript(PyObject* _self, PyObject* item)
30953136
return list_slice(self, start, stop);
30963137
}
30973138
else {
3098-
result = list_new_prealloc(slicelength);
3139+
result = (PyObject *)list_new(slicelength);
30993140
if (!result) return NULL;
31003141

31013142
src = self->ob_item;

0 commit comments

Comments
 (0)