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

Diff for: Include/internal/pycore_context.h

+1-1
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 */

Diff for: Include/internal/pycore_gc.h

+1-1
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
}

Diff for: Include/internal/pycore_interp.h

+7
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. */
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.

Diff for: Modules/gcmodule.c

+3-4
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)) {

Diff for: Python/context.c

+19-16
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

Diff for: Python/pylifecycle.c

+1-4
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)