From e005ead49b1ee2b1507ceea94e6f89c28ecf1f81 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Jun 2020 02:56:37 +0200 Subject: [PATCH] bpo-40521: Make context free list per-interpreter (GH-20644) Each interpreter now has its own context free list: * Move context free list into PyInterpreterState. * Add _Py_context_state structure. * Add tstate parameter to _PyContext_ClearFreeList() and _PyContext_Fini(). * Pass tstate to clear_freelists(). --- Include/internal/pycore_context.h | 2 +- Include/internal/pycore_gc.h | 2 +- Include/internal/pycore_interp.h | 7 ++++ .../2020-05-20-01-17-34.bpo-40521.wvAehI.rst | 4 +-- Modules/gcmodule.c | 7 ++-- Python/context.c | 35 ++++++++++--------- Python/pylifecycle.c | 5 +-- 7 files changed, 34 insertions(+), 28 deletions(-) diff --git a/Include/internal/pycore_context.h b/Include/internal/pycore_context.h index f665ad5c115..ea4b3c8ea73 100644 --- a/Include/internal/pycore_context.h +++ b/Include/internal/pycore_context.h @@ -37,6 +37,6 @@ struct _pycontexttokenobject { int _PyContext_Init(void); -void _PyContext_Fini(void); +void _PyContext_Fini(PyThreadState *tstate); #endif /* !Py_INTERNAL_CONTEXT_H */ diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index ad2e552df55..fd3fb7f94ca 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -171,7 +171,7 @@ extern void _PyFloat_ClearFreeList(PyThreadState *tstate); extern void _PyList_ClearFreeList(PyThreadState *tstate); extern void _PyDict_ClearFreeList(void); extern void _PyAsyncGen_ClearFreeLists(PyThreadState *tstate); -extern void _PyContext_ClearFreeList(void); +extern void _PyContext_ClearFreeList(PyThreadState *tstate); #ifdef __cplusplus } diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index d624218201b..4f811023f7a 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -124,6 +124,12 @@ struct _Py_async_gen_state { int asend_numfree; }; +struct _Py_context_state { + // List of free PyContext objects + PyContext *freelist; + int numfree; +}; + /* interpreter state */ @@ -223,6 +229,7 @@ struct _is { struct _Py_float_state float_state; struct _Py_frame_state frame; struct _Py_async_gen_state async_gen; + struct _Py_context_state context; /* Using a cache is very effective since typically only a single slice is created and then deleted again. */ diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst b/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst index f0fd5a1e13b..39cb80447f6 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2020-05-20-01-17-34.bpo-40521.wvAehI.rst @@ -1,4 +1,4 @@ The tuple free lists, the empty tuple singleton, the list free list, the float free list, the slice cache, the frame free list, the asynchronous generator -free lists are no longer shared by all interpreters: each interpreter now its -has own free lists and caches. +free lists, and the context free list are no longer shared by all interpreters: +each interpreter now its has own free lists and caches. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 89e2db7b194..f68258d7a32 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -1023,16 +1023,15 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate, * Clearing the free lists may give back memory to the OS earlier. */ static void -clear_freelists(void) +clear_freelists(PyThreadState *tstate) { - PyThreadState *tstate = _PyThreadState_GET(); _PyFrame_ClearFreeList(tstate); _PyTuple_ClearFreeList(tstate); _PyFloat_ClearFreeList(tstate); _PyList_ClearFreeList(tstate); _PyDict_ClearFreeList(); _PyAsyncGen_ClearFreeLists(tstate); - _PyContext_ClearFreeList(); + _PyContext_ClearFreeList(tstate); } // Show stats for objects in each generations @@ -1306,7 +1305,7 @@ collect(PyThreadState *tstate, int generation, /* Clear free list only during the collection of the highest * generation */ if (generation == NUM_GENERATIONS-1) { - clear_freelists(); + clear_freelists(tstate); } if (_PyErr_Occurred(tstate)) { diff --git a/Python/context.c b/Python/context.c index bacc7010c45..3cf8db4c90c 100644 --- a/Python/context.c +++ b/Python/context.c @@ -10,8 +10,6 @@ #define CONTEXT_FREELIST_MAXLEN 255 -static PyContext *ctx_freelist = NULL; -static int ctx_freelist_len = 0; #include "clinic/context.c.h" @@ -334,11 +332,13 @@ class _contextvars.Context "PyContext *" "&PyContext_Type" static inline PyContext * _context_alloc(void) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_context_state *state = &interp->context; PyContext *ctx; - if (ctx_freelist_len) { - ctx_freelist_len--; - ctx = ctx_freelist; - ctx_freelist = (PyContext *)ctx->ctx_weakreflist; + if (state->numfree) { + state->numfree--; + ctx = state->freelist; + state->freelist = (PyContext *)ctx->ctx_weakreflist; ctx->ctx_weakreflist = NULL; _Py_NewReference((PyObject *)ctx); } @@ -458,10 +458,12 @@ context_tp_dealloc(PyContext *self) } (void)context_tp_clear(self); - if (ctx_freelist_len < CONTEXT_FREELIST_MAXLEN) { - ctx_freelist_len++; - self->ctx_weakreflist = (PyObject *)ctx_freelist; - ctx_freelist = self; + PyInterpreterState *interp = _PyInterpreterState_GET(); + struct _Py_context_state *state = &interp->context; + if (state->numfree < CONTEXT_FREELIST_MAXLEN) { + state->numfree++; + self->ctx_weakreflist = (PyObject *)state->freelist; + state->freelist = self; } else { Py_TYPE(self)->tp_free(self); @@ -1271,11 +1273,12 @@ get_token_missing(void) void -_PyContext_ClearFreeList(void) +_PyContext_ClearFreeList(PyThreadState *tstate) { - for (; ctx_freelist_len; ctx_freelist_len--) { - PyContext *ctx = ctx_freelist; - ctx_freelist = (PyContext *)ctx->ctx_weakreflist; + struct _Py_context_state *state = &tstate->interp->context; + for (; state->numfree; state->numfree--) { + PyContext *ctx = state->freelist; + state->freelist = (PyContext *)ctx->ctx_weakreflist; ctx->ctx_weakreflist = NULL; PyObject_GC_Del(ctx); } @@ -1283,10 +1286,10 @@ _PyContext_ClearFreeList(void) void -_PyContext_Fini(void) +_PyContext_Fini(PyThreadState *tstate) { Py_CLEAR(_token_missing); - _PyContext_ClearFreeList(); + _PyContext_ClearFreeList(tstate); _PyHamt_Fini(); } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 073973e1328..6d2eb1defc8 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1273,10 +1273,7 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp) } _PyAsyncGen_Fini(tstate); - - if (is_main_interp) { - _PyContext_Fini(); - } + _PyContext_Fini(tstate); /* Cleanup Unicode implementation */ _PyUnicode_Fini(tstate);