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 b98f853

Browse filesBrowse files
committed
gh-112087: Store memory allocation information into _PyListArray
1 parent c012c8a commit b98f853
Copy full SHA for b98f853

File tree

3 files changed

+122
-13
lines changed
Filter options

3 files changed

+122
-13
lines changed

‎Include/cpython/listobject.h

Copy file name to clipboardExpand all lines: Include/cpython/listobject.h
+7Lines changed: 7 additions & 0 deletions
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)))
+1Lines changed: 1 addition & 0 deletions
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

Copy file name to clipboardExpand all lines: Objects/listobject.c
+114-13Lines changed: 114 additions & 13 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.