Skip to content

Commit 88ec919

Browse files
authored
bpo-40521: Make list free list per-interpreter (GH-20642)
Each interpreter now has its own list free list: * Move list numfree and free_list into PyInterpreterState. * Add _Py_list_state structure. * Add tstate parameter to _PyList_ClearFreeList() and _PyList_Fini(). * Remove "#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS". * _PyGC_Fini() clears gcstate->garbage list which can be stored in the list free list. Call _PyGC_Fini() before _PyList_Fini() to prevent leaking this list.
1 parent 052d3fc commit 88ec919

File tree

7 files changed

+49
-40
lines changed

7 files changed

+49
-40
lines changed

Include/internal/pycore_gc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *);
168168
extern void _PyFrame_ClearFreeList(PyThreadState *tstate);
169169
extern void _PyTuple_ClearFreeList(PyThreadState *tstate);
170170
extern void _PyFloat_ClearFreeList(PyThreadState *tstate);
171-
extern void _PyList_ClearFreeList(void);
171+
extern void _PyList_ClearFreeList(PyThreadState *tstate);
172172
extern void _PyDict_ClearFreeList(void);
173173
extern void _PyAsyncGen_ClearFreeLists(void);
174174
extern void _PyContext_ClearFreeList(void);

Include/internal/pycore_interp.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ struct _Py_tuple_state {
8484
#endif
8585
};
8686

87+
/* Empty list reuse scheme to save calls to malloc and free */
88+
#ifndef PyList_MAXFREELIST
89+
# define PyList_MAXFREELIST 80
90+
#endif
91+
92+
struct _Py_list_state {
93+
PyListObject *free_list[PyList_MAXFREELIST];
94+
int numfree;
95+
};
96+
8797
struct _Py_float_state {
8898
/* Special free list
8999
free_list is a singly-linked list of available PyFloatObjects,
@@ -192,6 +202,7 @@ struct _is {
192202
PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
193203
#endif
194204
struct _Py_tuple_state tuple;
205+
struct _Py_list_state list;
195206
struct _Py_float_state float_state;
196207
struct _Py_frame_state frame;
197208

Include/internal/pycore_pylifecycle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ extern PyStatus _PyGC_Init(PyThreadState *tstate);
6161
extern void _PyFrame_Fini(PyThreadState *tstate);
6262
extern void _PyDict_Fini(void);
6363
extern void _PyTuple_Fini(PyThreadState *tstate);
64-
extern void _PyList_Fini(void);
64+
extern void _PyList_Fini(PyThreadState *tstate);
6565
extern void _PySet_Fini(void);
6666
extern void _PyBytes_Fini(void);
6767
extern void _PyFloat_Fini(PyThreadState *tstate);
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
The tuple free lists, the empty tuple singleton, the float free list, the slice
2-
cache, and the frame free list are no longer shared by all interpreters: each
3-
interpreter now its has own free lists and caches.
1+
The tuple free lists, the empty tuple singleton, the list free list, the float
2+
free list, the slice cache, and the frame free list are no longer shared by all
3+
interpreters: each interpreter now its has own free lists and caches.

Modules/gcmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,7 @@ clear_freelists(void)
10291029
_PyFrame_ClearFreeList(tstate);
10301030
_PyTuple_ClearFreeList(tstate);
10311031
_PyFloat_ClearFreeList(tstate);
1032-
_PyList_ClearFreeList();
1032+
_PyList_ClearFreeList(tstate);
10331033
_PyDict_ClearFreeList();
10341034
_PyAsyncGen_ClearFreeLists();
10351035
_PyContext_ClearFreeList();

Objects/listobject.c

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -96,65 +96,59 @@ list_preallocate_exact(PyListObject *self, Py_ssize_t size)
9696
return 0;
9797
}
9898

99-
/* Empty list reuse scheme to save calls to malloc and free */
100-
#ifndef PyList_MAXFREELIST
101-
# define PyList_MAXFREELIST 80
102-
#endif
103-
104-
/* bpo-40521: list free lists are shared by all interpreters. */
105-
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
106-
# undef PyList_MAXFREELIST
107-
# define PyList_MAXFREELIST 0
108-
#endif
109-
110-
static PyListObject *free_list[PyList_MAXFREELIST];
111-
static int numfree = 0;
112-
11399
void
114-
_PyList_ClearFreeList(void)
100+
_PyList_ClearFreeList(PyThreadState *tstate)
115101
{
116-
while (numfree) {
117-
PyListObject *op = free_list[--numfree];
102+
struct _Py_list_state *state = &tstate->interp->list;
103+
while (state->numfree) {
104+
PyListObject *op = state->free_list[--state->numfree];
118105
assert(PyList_CheckExact(op));
119106
PyObject_GC_Del(op);
120107
}
121108
}
122109

123110
void
124-
_PyList_Fini(void)
111+
_PyList_Fini(PyThreadState *tstate)
125112
{
126-
_PyList_ClearFreeList();
113+
_PyList_ClearFreeList(tstate);
127114
}
128115

129116
/* Print summary info about the state of the optimized allocator */
130117
void
131118
_PyList_DebugMallocStats(FILE *out)
132119
{
120+
PyInterpreterState *interp = _PyInterpreterState_GET();
121+
struct _Py_list_state *state = &interp->list;
133122
_PyDebugAllocatorStats(out,
134123
"free PyListObject",
135-
numfree, sizeof(PyListObject));
124+
state->numfree, sizeof(PyListObject));
136125
}
137126

138127
PyObject *
139128
PyList_New(Py_ssize_t size)
140129
{
141-
PyListObject *op;
142-
143130
if (size < 0) {
144131
PyErr_BadInternalCall();
145132
return NULL;
146133
}
147-
if (numfree) {
148-
numfree--;
149-
op = free_list[numfree];
134+
135+
PyInterpreterState *interp = _PyInterpreterState_GET();
136+
struct _Py_list_state *state = &interp->list;
137+
PyListObject *op;
138+
if (state->numfree) {
139+
state->numfree--;
140+
op = state->free_list[state->numfree];
150141
_Py_NewReference((PyObject *)op);
151-
} else {
142+
}
143+
else {
152144
op = PyObject_GC_New(PyListObject, &PyList_Type);
153-
if (op == NULL)
145+
if (op == NULL) {
154146
return NULL;
147+
}
155148
}
156-
if (size <= 0)
149+
if (size <= 0) {
157150
op->ob_item = NULL;
151+
}
158152
else {
159153
op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));
160154
if (op->ob_item == NULL) {
@@ -334,10 +328,14 @@ list_dealloc(PyListObject *op)
334328
}
335329
PyMem_FREE(op->ob_item);
336330
}
337-
if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op))
338-
free_list[numfree++] = op;
339-
else
331+
PyInterpreterState *interp = _PyInterpreterState_GET();
332+
struct _Py_list_state *state = &interp->list;
333+
if (state->numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) {
334+
state->free_list[state->numfree++] = op;
335+
}
336+
else {
340337
Py_TYPE(op)->tp_free((PyObject *)op);
338+
}
341339
Py_TRASHCAN_END
342340
}
343341

Python/pylifecycle.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,8 +1251,8 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp)
12511251
{
12521252
_PyFrame_Fini(tstate);
12531253
_PyTuple_Fini(tstate);
1254+
_PyList_Fini(tstate);
12541255
if (is_main_interp) {
1255-
_PyList_Fini();
12561256
_PySet_Fini();
12571257
_PyBytes_Fini();
12581258
}
@@ -1296,6 +1296,8 @@ finalize_interp_clear(PyThreadState *tstate)
12961296
_PyGC_CollectNoFail();
12971297
}
12981298

1299+
_PyGC_Fini(tstate);
1300+
12991301
finalize_interp_types(tstate, is_main_interp);
13001302

13011303
if (is_main_interp) {
@@ -1309,8 +1311,6 @@ finalize_interp_clear(PyThreadState *tstate)
13091311

13101312
_PyExc_Fini();
13111313
}
1312-
1313-
_PyGC_Fini(tstate);
13141314
}
13151315

13161316

0 commit comments

Comments
 (0)