]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-40521: Make list free list per-interpreter (GH-20642)
authorVictor Stinner <vstinner@python.org>
Fri, 5 Jun 2020 00:05:41 +0000 (02:05 +0200)
committerGitHub <noreply@github.com>
Fri, 5 Jun 2020 00:05:41 +0000 (02:05 +0200)
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.

Include/internal/pycore_gc.h
Include/internal/pycore_interp.h
Include/internal/pycore_pylifecycle.h
Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst
Modules/gcmodule.c
Objects/listobject.c
Python/pylifecycle.c

index 01265d3f985b93470f307bc7384cd1cdae92cac8..3388b4d69e2644591a78ac99a785cf0998bd8954 100644 (file)
@@ -168,7 +168,7 @@ PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *);
 extern void _PyFrame_ClearFreeList(PyThreadState *tstate);
 extern void _PyTuple_ClearFreeList(PyThreadState *tstate);
 extern void _PyFloat_ClearFreeList(PyThreadState *tstate);
-extern void _PyList_ClearFreeList(void);
+extern void _PyList_ClearFreeList(PyThreadState *tstate);
 extern void _PyDict_ClearFreeList(void);
 extern void _PyAsyncGen_ClearFreeLists(void);
 extern void _PyContext_ClearFreeList(void);
index 9b805f004eaa6ea8a0f18af5f3f24415206b2781..0eab246562051f0691e1909015a79c7c18ec78c0 100644 (file)
@@ -84,6 +84,16 @@ struct _Py_tuple_state {
 #endif
 };
 
+/* Empty list reuse scheme to save calls to malloc and free */
+#ifndef PyList_MAXFREELIST
+#  define PyList_MAXFREELIST 80
+#endif
+
+struct _Py_list_state {
+    PyListObject *free_list[PyList_MAXFREELIST];
+    int numfree;
+};
+
 struct _Py_float_state {
     /* Special free list
        free_list is a singly-linked list of available PyFloatObjects,
@@ -192,6 +202,7 @@ struct _is {
     PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
 #endif
     struct _Py_tuple_state tuple;
+    struct _Py_list_state list;
     struct _Py_float_state float_state;
     struct _Py_frame_state frame;
 
index 06d2ac167d6196ffac0c2b7bba9d0af066b7f367..3c35ca23eab1a8268563e0a41dff3c9c1efa78b0 100644 (file)
@@ -61,7 +61,7 @@ extern PyStatus _PyGC_Init(PyThreadState *tstate);
 extern void _PyFrame_Fini(PyThreadState *tstate);
 extern void _PyDict_Fini(void);
 extern void _PyTuple_Fini(PyThreadState *tstate);
-extern void _PyList_Fini(void);
+extern void _PyList_Fini(PyThreadState *tstate);
 extern void _PySet_Fini(void);
 extern void _PyBytes_Fini(void);
 extern void _PyFloat_Fini(PyThreadState *tstate);
index 71a1064ba7d143765bdd8138504888041996d9e6..54cc60036164ef6667678f7bd61bb55fcc186abe 100644 (file)
@@ -1,3 +1,3 @@
-The tuple free lists, the empty tuple singleton, the float free list, the slice
-cache, and the frame free list are no longer shared by all interpreters: each
-interpreter now its has own free lists and caches.
+The tuple free lists, the empty tuple singleton, the list free list, the float
+free list, the slice cache, and the frame free list are no longer shared by all
+interpreters: each interpreter now its has own free lists and caches.
index 45dc89d08c1fbbf9d8b74b6f78fecf57ddcc05e8..2f062d0022589df0aead027c0d836edcae174390 100644 (file)
@@ -1029,7 +1029,7 @@ clear_freelists(void)
     _PyFrame_ClearFreeList(tstate);
     _PyTuple_ClearFreeList(tstate);
     _PyFloat_ClearFreeList(tstate);
-    _PyList_ClearFreeList();
+    _PyList_ClearFreeList(tstate);
     _PyDict_ClearFreeList();
     _PyAsyncGen_ClearFreeLists();
     _PyContext_ClearFreeList();
index 30d262075374430dee519b1c8019d3867d9525d4..043256d8adbf5c0faf95f3a839f045aa3c49a7ad 100644 (file)
@@ -96,65 +96,59 @@ list_preallocate_exact(PyListObject *self, Py_ssize_t size)
     return 0;
 }
 
-/* Empty list reuse scheme to save calls to malloc and free */
-#ifndef PyList_MAXFREELIST
-#  define PyList_MAXFREELIST 80
-#endif
-
-/* bpo-40521: list free lists are shared by all interpreters. */
-#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
-#  undef PyList_MAXFREELIST
-#  define PyList_MAXFREELIST 0
-#endif
-
-static PyListObject *free_list[PyList_MAXFREELIST];
-static int numfree = 0;
-
 void
-_PyList_ClearFreeList(void)
+_PyList_ClearFreeList(PyThreadState *tstate)
 {
-    while (numfree) {
-        PyListObject *op = free_list[--numfree];
+    struct _Py_list_state *state = &tstate->interp->list;
+    while (state->numfree) {
+        PyListObject *op = state->free_list[--state->numfree];
         assert(PyList_CheckExact(op));
         PyObject_GC_Del(op);
     }
 }
 
 void
-_PyList_Fini(void)
+_PyList_Fini(PyThreadState *tstate)
 {
-    _PyList_ClearFreeList();
+    _PyList_ClearFreeList(tstate);
 }
 
 /* Print summary info about the state of the optimized allocator */
 void
 _PyList_DebugMallocStats(FILE *out)
 {
+    PyInterpreterState *interp = _PyInterpreterState_GET();
+    struct _Py_list_state *state = &interp->list;
     _PyDebugAllocatorStats(out,
                            "free PyListObject",
-                           numfree, sizeof(PyListObject));
+                           state->numfree, sizeof(PyListObject));
 }
 
 PyObject *
 PyList_New(Py_ssize_t size)
 {
-    PyListObject *op;
-
     if (size < 0) {
         PyErr_BadInternalCall();
         return NULL;
     }
-    if (numfree) {
-        numfree--;
-        op = free_list[numfree];
+
+    PyInterpreterState *interp = _PyInterpreterState_GET();
+    struct _Py_list_state *state = &interp->list;
+    PyListObject *op;
+    if (state->numfree) {
+        state->numfree--;
+        op = state->free_list[state->numfree];
         _Py_NewReference((PyObject *)op);
-    } else {
+    }
+    else {
         op = PyObject_GC_New(PyListObject, &PyList_Type);
-        if (op == NULL)
+        if (op == NULL) {
             return NULL;
+        }
     }
-    if (size <= 0)
+    if (size <= 0) {
         op->ob_item = NULL;
+    }
     else {
         op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));
         if (op->ob_item == NULL) {
@@ -334,10 +328,14 @@ list_dealloc(PyListObject *op)
         }
         PyMem_FREE(op->ob_item);
     }
-    if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op))
-        free_list[numfree++] = op;
-    else
+    PyInterpreterState *interp = _PyInterpreterState_GET();
+    struct _Py_list_state *state = &interp->list;
+    if (state->numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) {
+        state->free_list[state->numfree++] = op;
+    }
+    else {
         Py_TYPE(op)->tp_free((PyObject *)op);
+    }
     Py_TRASHCAN_END
 }
 
index 1dbdbfdf5a3180b440906c93d17a941577de4e60..09d4d884041447a9c6b11fe61183d6d6e57d2b7d 100644 (file)
@@ -1251,8 +1251,8 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp)
 {
     _PyFrame_Fini(tstate);
     _PyTuple_Fini(tstate);
+    _PyList_Fini(tstate);
     if (is_main_interp) {
-        _PyList_Fini();
         _PySet_Fini();
         _PyBytes_Fini();
     }
@@ -1296,6 +1296,8 @@ finalize_interp_clear(PyThreadState *tstate)
         _PyGC_CollectNoFail();
     }
 
+    _PyGC_Fini(tstate);
+
     finalize_interp_types(tstate, is_main_interp);
 
     if (is_main_interp) {
@@ -1309,8 +1311,6 @@ finalize_interp_clear(PyThreadState *tstate)
 
         _PyExc_Fini();
     }
-
-    _PyGC_Fini(tstate);
 }