Skip to content

Commit b98f853

Browse files
committed
gh-112087: Store memory allocation information into _PyListArray
1 parent c012c8a commit b98f853

File tree

3 files changed

+122
-13
lines changed

3 files changed

+122
-13
lines changed

Include/cpython/listobject.h

+7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ typedef struct {
2121
Py_ssize_t allocated;
2222
} PyListObject;
2323

24+
#ifdef Py_GIL_DISABLED
25+
typedef struct {
26+
Py_ssize_t allocated;
27+
PyObject *ob_item[1];
28+
} _PyListArray;
29+
#endif
30+
2431
/* Cast argument to PyListObject* type. */
2532
#define _PyList_CAST(op) \
2633
(assert(PyList_Check(op)), _Py_CAST(PyListObject*, (op)))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:class:`list` is now compatible with the implementation of :pep:`703`.

Objects/listobject.c

+114-13
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,49 @@ get_list_freelist(void)
3131
}
3232
#endif
3333

34+
#ifdef Py_GIL_DISABLED
35+
static _PyListArray *
36+
list_allocate_array(size_t capacity)
37+
{
38+
if (capacity > PY_SSIZE_T_MAX/sizeof(PyObject*) - 1) {
39+
return NULL;
40+
}
41+
_PyListArray *array = PyMem_Malloc(sizeof(_PyListArray) + (capacity - 1) * sizeof(PyObject *));
42+
if (array == NULL) {
43+
return NULL;
44+
}
45+
array->allocated = capacity;
46+
return array;
47+
}
48+
49+
static Py_ssize_t
50+
list_capacity(PyObject **items)
51+
{
52+
char *mem = (char *)items;
53+
mem -= offsetof(_PyListArray, ob_item);
54+
_PyListArray *array = (_PyListArray *)mem;
55+
return _Py_atomic_load_ssize_relaxed(&array->allocated);
56+
}
57+
#endif
58+
59+
static void
60+
free_list_items(PyObject** items, bool use_qsbr)
61+
{
62+
#ifdef Py_GIL_DISABLED
63+
char *mem = (char *)items;
64+
mem -= offsetof(_PyListArray, ob_item);
65+
_PyListArray *array = (_PyListArray *)mem;
66+
if (use_qsbr) {
67+
_PyMem_FreeDelayed(array);
68+
}
69+
else {
70+
PyMem_Free(array);
71+
}
72+
#else
73+
PyMem_Free(items);
74+
#endif
75+
}
76+
3477
/* Ensure ob_item has room for at least newsize elements, and set
3578
* ob_size to newsize. If newsize > ob_size on entry, the content
3679
* of the new slots at exit is undefined heap trash; it's the caller's
@@ -47,8 +90,7 @@ get_list_freelist(void)
4790
static int
4891
list_resize(PyListObject *self, Py_ssize_t newsize)
4992
{
50-
PyObject **items;
51-
size_t new_allocated, num_allocated_bytes;
93+
size_t new_allocated;
5294
Py_ssize_t allocated = self->allocated;
5395

5496
/* Bypass realloc() when a previous overallocation is large enough
@@ -80,9 +122,39 @@ list_resize(PyListObject *self, Py_ssize_t newsize)
80122

81123
if (newsize == 0)
82124
new_allocated = 0;
125+
126+
#ifdef Py_GIL_DISABLED
127+
_PyListArray *array = NULL;
128+
if (new_allocated <= (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)) {
129+
array = list_allocate_array(new_allocated);
130+
}
131+
else {
132+
// integer overflow
133+
array = NULL;
134+
}
135+
if (array == NULL) {
136+
PyErr_NoMemory();
137+
return -1;
138+
}
139+
PyObject **old_items = self->ob_item;
140+
if (self->ob_item) {
141+
if (allocated < new_allocated) {
142+
memcpy(&array->ob_item, self->ob_item, allocated * sizeof(PyObject*));
143+
}
144+
else {
145+
memcpy(&array->ob_item, self->ob_item, new_allocated * sizeof(PyObject*));
146+
}
147+
}
148+
_Py_atomic_store_ptr_release(&self->ob_item, &array->ob_item);
149+
self->allocated = new_allocated;
150+
Py_SET_SIZE(self, newsize);
151+
if (old_items != NULL) {
152+
free_list_items(old_items, _PyObject_GC_IS_SHARED(self));
153+
}
154+
#else
155+
PyObject **items;
83156
if (new_allocated <= (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)) {
84-
num_allocated_bytes = new_allocated * sizeof(PyObject *);
85-
items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes);
157+
items = PyMem_Realloc(self->ob_item, new_allocated * sizeof(PyObject *));
86158
}
87159
else {
88160
// integer overflow
@@ -95,6 +167,7 @@ list_resize(PyListObject *self, Py_ssize_t newsize)
95167
self->ob_item = items;
96168
Py_SET_SIZE(self, newsize);
97169
self->allocated = new_allocated;
170+
#endif
98171
return 0;
99172
}
100173

@@ -110,12 +183,21 @@ list_preallocate_exact(PyListObject *self, Py_ssize_t size)
110183
* allocated size up to the nearest even number.
111184
*/
112185
size = (size + 1) & ~(size_t)1;
186+
#ifdef Py_GIL_DISABLED
187+
_PyListArray *array = list_allocate_array(size);
188+
if (array == NULL) {
189+
PyErr_NoMemory();
190+
return -1;
191+
}
192+
self->ob_item = array->ob_item;
193+
#else
113194
PyObject **items = PyMem_New(PyObject*, size);
114195
if (items == NULL) {
115196
PyErr_NoMemory();
116197
return -1;
117198
}
118199
self->ob_item = items;
200+
#endif
119201
self->allocated = size;
120202
return 0;
121203
}
@@ -178,7 +260,17 @@ PyList_New(Py_ssize_t size)
178260
op->ob_item = NULL;
179261
}
180262
else {
263+
#ifdef Py_GIL_DISABLED
264+
_PyListArray *array = list_allocate_array(size);
265+
if (array == NULL) {
266+
Py_DECREF(op);
267+
return PyErr_NoMemory();
268+
}
269+
op->ob_item = array->ob_item;
270+
memset(op->ob_item, 0, size * sizeof(PyObject *));
271+
#else
181272
op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));
273+
#endif
182274
if (op->ob_item == NULL) {
183275
Py_DECREF(op);
184276
return PyErr_NoMemory();
@@ -199,11 +291,20 @@ list_new_prealloc(Py_ssize_t size)
199291
return NULL;
200292
}
201293
assert(op->ob_item == NULL);
294+
#ifdef Py_GIL_DISABLED
295+
_PyListArray *array = list_allocate_array(size);
296+
if (array == NULL) {
297+
Py_DECREF(op);
298+
return PyErr_NoMemory();
299+
}
300+
op->ob_item = array->ob_item;
301+
#else
202302
op->ob_item = PyMem_New(PyObject *, size);
203303
if (op->ob_item == NULL) {
204304
Py_DECREF(op);
205305
return PyErr_NoMemory();
206306
}
307+
#endif
207308
op->allocated = size;
208309
return (PyObject *) op;
209310
}
@@ -268,7 +369,7 @@ list_get_item_ref(PyListObject *op, Py_ssize_t i)
268369
if (ob_item == NULL) {
269370
return NULL;
270371
}
271-
Py_ssize_t cap = _Py_atomic_load_ssize_relaxed(&op->allocated);
372+
Py_ssize_t cap = list_capacity(ob_item);
272373
assert(cap != -1 && cap >= size);
273374
if (!valid_index(i, cap)) {
274375
return NULL;
@@ -438,7 +539,7 @@ list_dealloc(PyObject *self)
438539
while (--i >= 0) {
439540
Py_XDECREF(op->ob_item[i]);
440541
}
441-
PyMem_Free(op->ob_item);
542+
free_list_items(op->ob_item, false);
442543
}
443544
#ifdef WITH_FREELISTS
444545
struct _Py_list_freelist *list_freelist = get_list_freelist();
@@ -737,12 +838,7 @@ list_clear_impl(PyListObject *a, bool is_resize)
737838
#else
738839
bool use_qsbr = false;
739840
#endif
740-
if (use_qsbr) {
741-
_PyMem_FreeDelayed(items);
742-
}
743-
else {
744-
PyMem_Free(items);
745-
}
841+
free_list_items(items, use_qsbr);
746842
// Note that there is no guarantee that the list is actually empty
747843
// at this point, because XDECREF may have populated it indirectly again!
748844
}
@@ -2758,7 +2854,12 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)
27582854
while (--i >= 0) {
27592855
Py_XDECREF(final_ob_item[i]);
27602856
}
2761-
PyMem_Free(final_ob_item);
2857+
#ifdef Py_GIL_DISABLED
2858+
bool use_qsbr = _PyObject_GC_IS_SHARED(self);
2859+
#else
2860+
bool use_qsbr = false;
2861+
#endif
2862+
free_list_items(final_ob_item, use_qsbr);
27622863
}
27632864
return Py_XNewRef(result);
27642865
}

0 commit comments

Comments
 (0)