Skip to content

Commit 6429975

Browse files
corona10diegorusso
authored andcommitted
pythongh-112087: Store memory allocation information into _PyListArray (pythongh-116529)
1 parent 57f503d commit 6429975

File tree

2 files changed

+113
-14
lines changed

2 files changed

+113
-14
lines changed
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

+112-14
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,50 @@ get_list_freelist(void)
3131
}
3232
#endif
3333

34+
#ifdef Py_GIL_DISABLED
35+
typedef struct {
36+
Py_ssize_t allocated;
37+
PyObject *ob_item[];
38+
} _PyListArray;
39+
40+
static _PyListArray *
41+
list_allocate_array(size_t capacity)
42+
{
43+
if (capacity > PY_SSIZE_T_MAX/sizeof(PyObject*) - 1) {
44+
return NULL;
45+
}
46+
_PyListArray *array = PyMem_Malloc(sizeof(_PyListArray) + capacity * sizeof(PyObject *));
47+
if (array == NULL) {
48+
return NULL;
49+
}
50+
array->allocated = capacity;
51+
return array;
52+
}
53+
54+
static Py_ssize_t
55+
list_capacity(PyObject **items)
56+
{
57+
_PyListArray *array = _Py_CONTAINER_OF(items, _PyListArray, ob_item);
58+
return array->allocated;
59+
}
60+
#endif
61+
62+
static void
63+
free_list_items(PyObject** items, bool use_qsbr)
64+
{
65+
#ifdef Py_GIL_DISABLED
66+
_PyListArray *array = _Py_CONTAINER_OF(items, _PyListArray, ob_item);
67+
if (use_qsbr) {
68+
_PyMem_FreeDelayed(array);
69+
}
70+
else {
71+
PyMem_Free(array);
72+
}
73+
#else
74+
PyMem_Free(items);
75+
#endif
76+
}
77+
3478
/* Ensure ob_item has room for at least newsize elements, and set
3579
* ob_size to newsize. If newsize > ob_size on entry, the content
3680
* of the new slots at exit is undefined heap trash; it's the caller's
@@ -47,8 +91,7 @@ get_list_freelist(void)
4791
static int
4892
list_resize(PyListObject *self, Py_ssize_t newsize)
4993
{
50-
PyObject **items;
51-
size_t new_allocated, num_allocated_bytes;
94+
size_t new_allocated, target_bytes;
5295
Py_ssize_t allocated = self->allocated;
5396

5497
/* Bypass realloc() when a previous overallocation is large enough
@@ -80,9 +123,34 @@ list_resize(PyListObject *self, Py_ssize_t newsize)
80123

81124
if (newsize == 0)
82125
new_allocated = 0;
126+
127+
#ifdef Py_GIL_DISABLED
128+
_PyListArray *array = list_allocate_array(new_allocated);
129+
if (array == NULL) {
130+
PyErr_NoMemory();
131+
return -1;
132+
}
133+
PyObject **old_items = self->ob_item;
134+
if (self->ob_item) {
135+
if (new_allocated < (size_t)allocated) {
136+
target_bytes = new_allocated * sizeof(PyObject*);
137+
}
138+
else {
139+
target_bytes = allocated * sizeof(PyObject*);
140+
}
141+
memcpy(array->ob_item, self->ob_item, target_bytes);
142+
}
143+
_Py_atomic_store_ptr_release(&self->ob_item, &array->ob_item);
144+
self->allocated = new_allocated;
145+
Py_SET_SIZE(self, newsize);
146+
if (old_items != NULL) {
147+
free_list_items(old_items, _PyObject_GC_IS_SHARED(self));
148+
}
149+
#else
150+
PyObject **items;
83151
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);
152+
target_bytes = new_allocated * sizeof(PyObject *);
153+
items = (PyObject **)PyMem_Realloc(self->ob_item, target_bytes);
86154
}
87155
else {
88156
// integer overflow
@@ -95,12 +163,14 @@ list_resize(PyListObject *self, Py_ssize_t newsize)
95163
self->ob_item = items;
96164
Py_SET_SIZE(self, newsize);
97165
self->allocated = new_allocated;
166+
#endif
98167
return 0;
99168
}
100169

101170
static int
102171
list_preallocate_exact(PyListObject *self, Py_ssize_t size)
103172
{
173+
PyObject **items;
104174
assert(self->ob_item == NULL);
105175
assert(size > 0);
106176

@@ -110,11 +180,20 @@ list_preallocate_exact(PyListObject *self, Py_ssize_t size)
110180
* allocated size up to the nearest even number.
111181
*/
112182
size = (size + 1) & ~(size_t)1;
113-
PyObject **items = PyMem_New(PyObject*, size);
183+
#ifdef Py_GIL_DISABLED
184+
_PyListArray *array = list_allocate_array(size);
185+
if (array == NULL) {
186+
PyErr_NoMemory();
187+
return -1;
188+
}
189+
items = array->ob_item;
190+
#else
191+
items = PyMem_New(PyObject*, size);
114192
if (items == NULL) {
115193
PyErr_NoMemory();
116194
return -1;
117195
}
196+
#endif
118197
self->ob_item = items;
119198
self->allocated = size;
120199
return 0;
@@ -178,7 +257,17 @@ PyList_New(Py_ssize_t size)
178257
op->ob_item = NULL;
179258
}
180259
else {
260+
#ifdef Py_GIL_DISABLED
261+
_PyListArray *array = list_allocate_array(size);
262+
if (array == NULL) {
263+
Py_DECREF(op);
264+
return PyErr_NoMemory();
265+
}
266+
memset(&array->ob_item, 0, size * sizeof(PyObject *));
267+
op->ob_item = array->ob_item;
268+
#else
181269
op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));
270+
#endif
182271
if (op->ob_item == NULL) {
183272
Py_DECREF(op);
184273
return PyErr_NoMemory();
@@ -199,11 +288,20 @@ list_new_prealloc(Py_ssize_t size)
199288
return NULL;
200289
}
201290
assert(op->ob_item == NULL);
291+
#ifdef Py_GIL_DISABLED
292+
_PyListArray *array = list_allocate_array(size);
293+
if (array == NULL) {
294+
Py_DECREF(op);
295+
return PyErr_NoMemory();
296+
}
297+
op->ob_item = array->ob_item;
298+
#else
202299
op->ob_item = PyMem_New(PyObject *, size);
203300
if (op->ob_item == NULL) {
204301
Py_DECREF(op);
205302
return PyErr_NoMemory();
206303
}
304+
#endif
207305
op->allocated = size;
208306
return (PyObject *) op;
209307
}
@@ -268,7 +366,7 @@ list_get_item_ref(PyListObject *op, Py_ssize_t i)
268366
if (ob_item == NULL) {
269367
return NULL;
270368
}
271-
Py_ssize_t cap = _Py_atomic_load_ssize_relaxed(&op->allocated);
369+
Py_ssize_t cap = list_capacity(ob_item);
272370
assert(cap != -1 && cap >= size);
273371
if (!valid_index(i, cap)) {
274372
return NULL;
@@ -438,7 +536,7 @@ list_dealloc(PyObject *self)
438536
while (--i >= 0) {
439537
Py_XDECREF(op->ob_item[i]);
440538
}
441-
PyMem_Free(op->ob_item);
539+
free_list_items(op->ob_item, false);
442540
}
443541
#ifdef WITH_FREELISTS
444542
struct _Py_list_freelist *list_freelist = get_list_freelist();
@@ -737,12 +835,7 @@ list_clear_impl(PyListObject *a, bool is_resize)
737835
#else
738836
bool use_qsbr = false;
739837
#endif
740-
if (use_qsbr) {
741-
_PyMem_FreeDelayed(items);
742-
}
743-
else {
744-
PyMem_Free(items);
745-
}
838+
free_list_items(items, use_qsbr);
746839
// Note that there is no guarantee that the list is actually empty
747840
// at this point, because XDECREF may have populated it indirectly again!
748841
}
@@ -2758,7 +2851,12 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)
27582851
while (--i >= 0) {
27592852
Py_XDECREF(final_ob_item[i]);
27602853
}
2761-
PyMem_Free(final_ob_item);
2854+
#ifdef Py_GIL_DISABLED
2855+
bool use_qsbr = _PyObject_GC_IS_SHARED(self);
2856+
#else
2857+
bool use_qsbr = false;
2858+
#endif
2859+
free_list_items(final_ob_item, use_qsbr);
27622860
}
27632861
return Py_XNewRef(result);
27642862
}

0 commit comments

Comments
 (0)