summaryrefslogtreecommitdiffstats
path: root/Objects/object.c
diff options
context:
space:
mode:
authorSam Gross <colesbury@gmail.com>2024-07-22 16:08:27 (GMT)
committerGitHub <noreply@github.com>2024-07-22 16:08:27 (GMT)
commit5716cc352940a5f8557a8191e873837aa619498a (patch)
treea9b1526a46acfe002950b9ad0d046f03c7cab5e9 /Objects/object.c
parent2408a8a22bd13d8f15172a2ecf8bbbc4355dcb3b (diff)
downloadcpython-5716cc352940a5f8557a8191e873837aa619498a.zip
cpython-5716cc352940a5f8557a8191e873837aa619498a.tar.gz
cpython-5716cc352940a5f8557a8191e873837aa619498a.tar.bz2
gh-100240: Use a consistent implementation for freelists (#121934)
This combines and updates our freelist handling to use a consistent implementation. Objects in the freelist are linked together using the first word of memory block. If configured with freelists disabled, these operations are essentially no-ops.
Diffstat (limited to 'Objects/object.c')
-rw-r--r--Objects/object.c55
1 files changed, 45 insertions, 10 deletions
diff --git a/Objects/object.c b/Objects/object.c
index e2f96af..6d6bb87 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -10,6 +10,7 @@
#include "pycore_descrobject.h" // _PyMethodWrapper_Type
#include "pycore_dict.h" // _PyObject_MakeDictFromInstanceAttributes()
#include "pycore_floatobject.h" // _PyFloat_DebugMallocStats()
+#include "pycore_freelist.h" // _PyObject_ClearFreeLists()
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
#include "pycore_instruction_sequence.h" // _PyInstructionSequence_Type
#include "pycore_hashtable.h" // _Py_hashtable_new()
@@ -808,20 +809,54 @@ PyObject_Bytes(PyObject *v)
return PyBytes_FromObject(v);
}
+#ifdef WITH_FREELISTS
+static void
+clear_freelist(struct _Py_freelist *freelist, int is_finalization,
+ freefunc dofree)
+{
+ void *ptr;
+ while ((ptr = _PyFreeList_PopNoStats(freelist)) != NULL) {
+ dofree(ptr);
+ }
+ assert(freelist->size == 0 || freelist->size == -1);
+ assert(freelist->freelist == NULL);
+ if (is_finalization) {
+ freelist->size = -1;
+ }
+}
+
+static void
+free_object(void *obj)
+{
+ PyObject *op = (PyObject *)obj;
+ Py_TYPE(op)->tp_free(op);
+}
+
+#endif
+
void
-_PyObject_ClearFreeLists(struct _Py_object_freelists *freelists, int is_finalization)
+_PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization)
{
+#ifdef WITH_FREELISTS
// In the free-threaded build, freelists are per-PyThreadState and cleared in PyThreadState_Clear()
// In the default build, freelists are per-interpreter and cleared in finalize_interp_types()
- _PyFloat_ClearFreeList(freelists, is_finalization);
- _PyTuple_ClearFreeList(freelists, is_finalization);
- _PyList_ClearFreeList(freelists, is_finalization);
- _PyDict_ClearFreeList(freelists, is_finalization);
- _PyContext_ClearFreeList(freelists, is_finalization);
- _PyAsyncGen_ClearFreeLists(freelists, is_finalization);
- // Only be cleared if is_finalization is true.
- _PyObjectStackChunk_ClearFreeList(freelists, is_finalization);
- _PySlice_ClearFreeList(freelists, is_finalization);
+ clear_freelist(&freelists->floats, is_finalization, free_object);
+ for (Py_ssize_t i = 0; i < PyTuple_MAXSAVESIZE; i++) {
+ clear_freelist(&freelists->tuples[i], is_finalization, free_object);
+ }
+ clear_freelist(&freelists->lists, is_finalization, free_object);
+ clear_freelist(&freelists->dicts, is_finalization, free_object);
+ clear_freelist(&freelists->dictkeys, is_finalization, PyMem_Free);
+ clear_freelist(&freelists->slices, is_finalization, free_object);
+ clear_freelist(&freelists->contexts, is_finalization, free_object);
+ clear_freelist(&freelists->async_gens, is_finalization, free_object);
+ clear_freelist(&freelists->async_gen_asends, is_finalization, free_object);
+ if (is_finalization) {
+ // Only clear object stack chunks during finalization. We use object
+ // stacks during GC, so emptying the free-list is counterproductive.
+ clear_freelist(&freelists->object_stack_chunks, 1, PyMem_RawFree);
+ }
+#endif
}
/*