Skip to content

Commit e005ead

Browse files
authored
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().
1 parent 78a02c2 commit e005ead

File tree

7 files changed

+34
-28
lines changed

7 files changed

+34
-28
lines changed

Include/internal/pycore_context.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@ struct _pycontexttokenobject {
3737

3838

3939
int _PyContext_Init(void);
40-
void _PyContext_Fini(void);
40+
void _PyContext_Fini(PyThreadState *tstate);
4141

4242
#endif /* !Py_INTERNAL_CONTEXT_H */

Include/internal/pycore_gc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ extern void _PyFloat_ClearFreeList(PyThreadState *tstate);
171171
extern void _PyList_ClearFreeList(PyThreadState *tstate);
172172
extern void _PyDict_ClearFreeList(void);
173173
extern void _PyAsyncGen_ClearFreeLists(PyThreadState *tstate);
174-
extern void _PyContext_ClearFreeList(void);
174+
extern void _PyContext_ClearFreeList(PyThreadState *tstate);
175175

176176
#ifdef __cplusplus
177177
}

Include/internal/pycore_interp.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ struct _Py_async_gen_state {
124124
int asend_numfree;
125125
};
126126

127+
struct _Py_context_state {
128+
// List of free PyContext objects
129+
PyContext *freelist;
130+
int numfree;
131+
};
132+
127133

128134

129135
/* interpreter state */
@@ -223,6 +229,7 @@ struct _is {
223229
struct _Py_float_state float_state;
224230
struct _Py_frame_state frame;
225231
struct _Py_async_gen_state async_gen;
232+
struct _Py_context_state context;
226233

227234
/* Using a cache is very effective since typically only a single slice is
228235
created and then deleted again. */
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
The tuple free lists, the empty tuple singleton, the list free list, the float
22
free list, the slice cache, the frame free list, the asynchronous generator
3-
free lists are no longer shared by all interpreters: each interpreter now its
4-
has own free lists and caches.
3+
free lists, and the context free list are no longer shared by all interpreters:
4+
each interpreter now its has own free lists and caches.

Modules/gcmodule.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,16 +1023,15 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate,
10231023
* Clearing the free lists may give back memory to the OS earlier.
10241024
*/
10251025
static void
1026-
clear_freelists(void)
1026+
clear_freelists(PyThreadState *tstate)
10271027
{
1028-
PyThreadState *tstate = _PyThreadState_GET();
10291028
_PyFrame_ClearFreeList(tstate);
10301029
_PyTuple_ClearFreeList(tstate);
10311030
_PyFloat_ClearFreeList(tstate);
10321031
_PyList_ClearFreeList(tstate);
10331032
_PyDict_ClearFreeList();
10341033
_PyAsyncGen_ClearFreeLists(tstate);
1035-
_PyContext_ClearFreeList();
1034+
_PyContext_ClearFreeList(tstate);
10361035
}
10371036

10381037
// Show stats for objects in each generations
@@ -1306,7 +1305,7 @@ collect(PyThreadState *tstate, int generation,
13061305
/* Clear free list only during the collection of the highest
13071306
* generation */
13081307
if (generation == NUM_GENERATIONS-1) {
1309-
clear_freelists();
1308+
clear_freelists(tstate);
13101309
}
13111310

13121311
if (_PyErr_Occurred(tstate)) {

Python/context.c

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111

1212
#define CONTEXT_FREELIST_MAXLEN 255
13-
static PyContext *ctx_freelist = NULL;
14-
static int ctx_freelist_len = 0;
1513

1614

1715
#include "clinic/context.c.h"
@@ -334,11 +332,13 @@ class _contextvars.Context "PyContext *" "&PyContext_Type"
334332
static inline PyContext *
335333
_context_alloc(void)
336334
{
335+
PyInterpreterState *interp = _PyInterpreterState_GET();
336+
struct _Py_context_state *state = &interp->context;
337337
PyContext *ctx;
338-
if (ctx_freelist_len) {
339-
ctx_freelist_len--;
340-
ctx = ctx_freelist;
341-
ctx_freelist = (PyContext *)ctx->ctx_weakreflist;
338+
if (state->numfree) {
339+
state->numfree--;
340+
ctx = state->freelist;
341+
state->freelist = (PyContext *)ctx->ctx_weakreflist;
342342
ctx->ctx_weakreflist = NULL;
343343
_Py_NewReference((PyObject *)ctx);
344344
}
@@ -458,10 +458,12 @@ context_tp_dealloc(PyContext *self)
458458
}
459459
(void)context_tp_clear(self);
460460

461-
if (ctx_freelist_len < CONTEXT_FREELIST_MAXLEN) {
462-
ctx_freelist_len++;
463-
self->ctx_weakreflist = (PyObject *)ctx_freelist;
464-
ctx_freelist = self;
461+
PyInterpreterState *interp = _PyInterpreterState_GET();
462+
struct _Py_context_state *state = &interp->context;
463+
if (state->numfree < CONTEXT_FREELIST_MAXLEN) {
464+
state->numfree++;
465+
self->ctx_weakreflist = (PyObject *)state->freelist;
466+
state->freelist = self;
465467
}
466468
else {
467469
Py_TYPE(self)->tp_free(self);
@@ -1271,22 +1273,23 @@ get_token_missing(void)
12711273

12721274

12731275
void
1274-
_PyContext_ClearFreeList(void)
1276+
_PyContext_ClearFreeList(PyThreadState *tstate)
12751277
{
1276-
for (; ctx_freelist_len; ctx_freelist_len--) {
1277-
PyContext *ctx = ctx_freelist;
1278-
ctx_freelist = (PyContext *)ctx->ctx_weakreflist;
1278+
struct _Py_context_state *state = &tstate->interp->context;
1279+
for (; state->numfree; state->numfree--) {
1280+
PyContext *ctx = state->freelist;
1281+
state->freelist = (PyContext *)ctx->ctx_weakreflist;
12791282
ctx->ctx_weakreflist = NULL;
12801283
PyObject_GC_Del(ctx);
12811284
}
12821285
}
12831286

12841287

12851288
void
1286-
_PyContext_Fini(void)
1289+
_PyContext_Fini(PyThreadState *tstate)
12871290
{
12881291
Py_CLEAR(_token_missing);
1289-
_PyContext_ClearFreeList();
1292+
_PyContext_ClearFreeList(tstate);
12901293
_PyHamt_Fini();
12911294
}
12921295

Python/pylifecycle.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,10 +1273,7 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp)
12731273
}
12741274

12751275
_PyAsyncGen_Fini(tstate);
1276-
1277-
if (is_main_interp) {
1278-
_PyContext_Fini();
1279-
}
1276+
_PyContext_Fini(tstate);
12801277

12811278
/* Cleanup Unicode implementation */
12821279
_PyUnicode_Fini(tstate);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy