Skip to content

Commit b4b5386

Browse files
authored
bpo-40521: Disable free lists in subinterpreters (GH-19937)
When Python is built with experimental isolated interpreters, disable tuple, dict and free free lists. Temporary workaround until these caches are made per-interpreter. Add frame_alloc() and frame_get_builtins() subfunctions to simplify _PyFrame_New_NoTrack().
1 parent ac4bf42 commit b4b5386

File tree

3 files changed

+162
-80
lines changed

3 files changed

+162
-80
lines changed

Objects/dictobject.c

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,16 +250,26 @@ static uint64_t pydict_global_version = 0;
250250
#ifndef PyDict_MAXFREELIST
251251
#define PyDict_MAXFREELIST 80
252252
#endif
253+
254+
/* bpo-40521: dict free lists are shared by all interpreters. */
255+
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
256+
# undef PyDict_MAXFREELIST
257+
# define PyDict_MAXFREELIST 0
258+
#endif
259+
260+
#if PyDict_MAXFREELIST > 0
253261
static PyDictObject *free_list[PyDict_MAXFREELIST];
254262
static int numfree = 0;
255263
static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
256264
static int numfreekeys = 0;
265+
#endif
257266

258267
#include "clinic/dictobject.c.h"
259268

260269
void
261270
_PyDict_ClearFreeList(void)
262271
{
272+
#if PyDict_MAXFREELIST > 0
263273
while (numfree) {
264274
PyDictObject *op = free_list[--numfree];
265275
assert(PyDict_CheckExact(op));
@@ -268,14 +278,17 @@ _PyDict_ClearFreeList(void)
268278
while (numfreekeys) {
269279
PyObject_FREE(keys_free_list[--numfreekeys]);
270280
}
281+
#endif
271282
}
272283

273284
/* Print summary info about the state of the optimized allocator */
274285
void
275286
_PyDict_DebugMallocStats(FILE *out)
276287
{
288+
#if PyDict_MAXFREELIST > 0
277289
_PyDebugAllocatorStats(out,
278290
"free PyDictObject", numfree, sizeof(PyDictObject));
291+
#endif
279292
}
280293

281294

@@ -553,10 +566,13 @@ static PyDictKeysObject *new_keys_object(Py_ssize_t size)
553566
es = sizeof(Py_ssize_t);
554567
}
555568

569+
#if PyDict_MAXFREELIST > 0
556570
if (size == PyDict_MINSIZE && numfreekeys > 0) {
557571
dk = keys_free_list[--numfreekeys];
558572
}
559-
else {
573+
else
574+
#endif
575+
{
560576
dk = PyObject_MALLOC(sizeof(PyDictKeysObject)
561577
+ es * size
562578
+ sizeof(PyDictKeyEntry) * usable);
@@ -587,10 +603,12 @@ free_keys_object(PyDictKeysObject *keys)
587603
Py_XDECREF(entries[i].me_key);
588604
Py_XDECREF(entries[i].me_value);
589605
}
606+
#if PyDict_MAXFREELIST > 0
590607
if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) {
591608
keys_free_list[numfreekeys++] = keys;
592609
return;
593610
}
611+
#endif
594612
PyObject_FREE(keys);
595613
}
596614

@@ -603,13 +621,16 @@ new_dict(PyDictKeysObject *keys, PyObject **values)
603621
{
604622
PyDictObject *mp;
605623
assert(keys != NULL);
624+
#if PyDict_MAXFREELIST > 0
606625
if (numfree) {
607626
mp = free_list[--numfree];
608627
assert (mp != NULL);
609628
assert (Py_IS_TYPE(mp, &PyDict_Type));
610629
_Py_NewReference((PyObject *)mp);
611630
}
612-
else {
631+
else
632+
#endif
633+
{
613634
mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
614635
if (mp == NULL) {
615636
dictkeys_decref(keys);
@@ -1258,12 +1279,15 @@ dictresize(PyDictObject *mp, Py_ssize_t minsize)
12581279
#ifdef Py_REF_DEBUG
12591280
_Py_RefTotal--;
12601281
#endif
1282+
#if PyDict_MAXFREELIST > 0
12611283
if (oldkeys->dk_size == PyDict_MINSIZE &&
12621284
numfreekeys < PyDict_MAXFREELIST)
12631285
{
12641286
keys_free_list[numfreekeys++] = oldkeys;
12651287
}
1266-
else {
1288+
else
1289+
#endif
1290+
{
12671291
PyObject_FREE(oldkeys);
12681292
}
12691293
}
@@ -2005,10 +2029,15 @@ dict_dealloc(PyDictObject *mp)
20052029
assert(keys->dk_refcnt == 1);
20062030
dictkeys_decref(keys);
20072031
}
2008-
if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type))
2032+
#if PyDict_MAXFREELIST > 0
2033+
if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) {
20092034
free_list[numfree++] = mp;
2035+
}
20102036
else
2037+
#endif
2038+
{
20112039
Py_TYPE(mp)->tp_free((PyObject *)mp);
2040+
}
20122041
Py_TRASHCAN_END
20132042
}
20142043

Objects/frameobject.c

Lines changed: 121 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -556,11 +556,19 @@ static PyGetSetDef frame_getsetlist[] = {
556556
free_list. Else programs creating lots of cyclic trash involving
557557
frames could provoke free_list into growing without bound.
558558
*/
559+
/* max value for numfree */
560+
#define PyFrame_MAXFREELIST 200
561+
562+
/* bpo-40521: frame free lists are shared by all interpreters. */
563+
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
564+
# undef PyFrame_MAXFREELIST
565+
# define PyFrame_MAXFREELIST 0
566+
#endif
559567

568+
#if PyFrame_MAXFREELIST > 0
560569
static PyFrameObject *free_list = NULL;
561570
static int numfree = 0; /* number of frames currently in free_list */
562-
/* max value for numfree */
563-
#define PyFrame_MAXFREELIST 200
571+
#endif
564572

565573
static void _Py_HOT_FUNCTION
566574
frame_dealloc(PyFrameObject *f)
@@ -590,15 +598,19 @@ frame_dealloc(PyFrameObject *f)
590598
Py_CLEAR(f->f_trace);
591599

592600
co = f->f_code;
593-
if (co->co_zombieframe == NULL)
601+
if (co->co_zombieframe == NULL) {
594602
co->co_zombieframe = f;
603+
}
604+
#if PyFrame_MAXFREELIST > 0
595605
else if (numfree < PyFrame_MAXFREELIST) {
596606
++numfree;
597607
f->f_back = free_list;
598608
free_list = f;
599609
}
600-
else
610+
#endif
611+
else {
601612
PyObject_GC_Del(f);
613+
}
602614

603615
Py_DECREF(co);
604616
Py_TRASHCAN_SAFE_END(f)
@@ -759,98 +771,127 @@ PyTypeObject PyFrame_Type = {
759771

760772
_Py_IDENTIFIER(__builtins__);
761773

762-
PyFrameObject* _Py_HOT_FUNCTION
763-
_PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
764-
PyObject *globals, PyObject *locals)
774+
static inline PyFrameObject*
775+
frame_alloc(PyCodeObject *code)
765776
{
766-
PyFrameObject *back = tstate->frame;
767777
PyFrameObject *f;
768-
PyObject *builtins;
769-
Py_ssize_t i;
770778

771-
#ifdef Py_DEBUG
772-
if (code == NULL || globals == NULL || !PyDict_Check(globals) ||
773-
(locals != NULL && !PyMapping_Check(locals))) {
774-
PyErr_BadInternalCall();
775-
return NULL;
779+
f = code->co_zombieframe;
780+
if (f != NULL) {
781+
code->co_zombieframe = NULL;
782+
_Py_NewReference((PyObject *)f);
783+
assert(f->f_code == code);
784+
return f;
776785
}
786+
787+
Py_ssize_t ncells = PyTuple_GET_SIZE(code->co_cellvars);
788+
Py_ssize_t nfrees = PyTuple_GET_SIZE(code->co_freevars);
789+
Py_ssize_t extras = code->co_stacksize + code->co_nlocals + ncells + nfrees;
790+
#if PyFrame_MAXFREELIST > 0
791+
if (free_list == NULL)
777792
#endif
778-
if (back == NULL || back->f_globals != globals) {
779-
builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__);
780-
if (builtins) {
781-
if (PyModule_Check(builtins)) {
782-
builtins = PyModule_GetDict(builtins);
783-
assert(builtins != NULL);
784-
}
793+
{
794+
f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras);
795+
if (f == NULL) {
796+
return NULL;
785797
}
786-
if (builtins == NULL) {
787-
if (PyErr_Occurred()) {
798+
}
799+
#if PyFrame_MAXFREELIST > 0
800+
else {
801+
assert(numfree > 0);
802+
--numfree;
803+
f = free_list;
804+
free_list = free_list->f_back;
805+
if (Py_SIZE(f) < extras) {
806+
PyFrameObject *new_f = PyObject_GC_Resize(PyFrameObject, f, extras);
807+
if (new_f == NULL) {
808+
PyObject_GC_Del(f);
788809
return NULL;
789810
}
790-
/* No builtins! Make up a minimal one
791-
Give them 'None', at least. */
792-
builtins = PyDict_New();
793-
if (builtins == NULL ||
794-
PyDict_SetItemString(
795-
builtins, "None", Py_None) < 0)
796-
return NULL;
811+
f = new_f;
797812
}
798-
else
799-
Py_INCREF(builtins);
813+
_Py_NewReference((PyObject *)f);
814+
}
815+
#endif
800816

817+
f->f_code = code;
818+
extras = code->co_nlocals + ncells + nfrees;
819+
f->f_valuestack = f->f_localsplus + extras;
820+
for (Py_ssize_t i=0; i<extras; i++) {
821+
f->f_localsplus[i] = NULL;
801822
}
802-
else {
823+
f->f_locals = NULL;
824+
f->f_trace = NULL;
825+
return f;
826+
}
827+
828+
829+
static inline PyObject *
830+
frame_get_builtins(PyFrameObject *back, PyObject *globals)
831+
{
832+
PyObject *builtins;
833+
834+
if (back != NULL && back->f_globals == globals) {
803835
/* If we share the globals, we share the builtins.
804836
Save a lookup and a call. */
805837
builtins = back->f_builtins;
806838
assert(builtins != NULL);
807839
Py_INCREF(builtins);
840+
return builtins;
808841
}
809-
if (code->co_zombieframe != NULL) {
810-
f = code->co_zombieframe;
811-
code->co_zombieframe = NULL;
812-
_Py_NewReference((PyObject *)f);
813-
assert(f->f_code == code);
842+
843+
builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__);
844+
if (builtins != NULL && PyModule_Check(builtins)) {
845+
builtins = PyModule_GetDict(builtins);
846+
assert(builtins != NULL);
814847
}
815-
else {
816-
Py_ssize_t extras, ncells, nfrees;
817-
ncells = PyTuple_GET_SIZE(code->co_cellvars);
818-
nfrees = PyTuple_GET_SIZE(code->co_freevars);
819-
extras = code->co_stacksize + code->co_nlocals + ncells +
820-
nfrees;
821-
if (free_list == NULL) {
822-
f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type,
823-
extras);
824-
if (f == NULL) {
825-
Py_DECREF(builtins);
826-
return NULL;
827-
}
828-
}
829-
else {
830-
assert(numfree > 0);
831-
--numfree;
832-
f = free_list;
833-
free_list = free_list->f_back;
834-
if (Py_SIZE(f) < extras) {
835-
PyFrameObject *new_f = PyObject_GC_Resize(PyFrameObject, f, extras);
836-
if (new_f == NULL) {
837-
PyObject_GC_Del(f);
838-
Py_DECREF(builtins);
839-
return NULL;
840-
}
841-
f = new_f;
842-
}
843-
_Py_NewReference((PyObject *)f);
844-
}
848+
if (builtins != NULL) {
849+
Py_INCREF(builtins);
850+
return builtins;
851+
}
852+
853+
if (PyErr_Occurred()) {
854+
return NULL;
855+
}
856+
857+
/* No builtins! Make up a minimal one.
858+
Give them 'None', at least. */
859+
builtins = PyDict_New();
860+
if (builtins == NULL) {
861+
return NULL;
862+
}
863+
if (PyDict_SetItemString(builtins, "None", Py_None) < 0) {
864+
Py_DECREF(builtins);
865+
return NULL;
866+
}
867+
return builtins;
868+
}
845869

846-
f->f_code = code;
847-
extras = code->co_nlocals + ncells + nfrees;
848-
f->f_valuestack = f->f_localsplus + extras;
849-
for (i=0; i<extras; i++)
850-
f->f_localsplus[i] = NULL;
851-
f->f_locals = NULL;
852-
f->f_trace = NULL;
870+
871+
PyFrameObject* _Py_HOT_FUNCTION
872+
_PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
873+
PyObject *globals, PyObject *locals)
874+
{
875+
#ifdef Py_DEBUG
876+
if (code == NULL || globals == NULL || !PyDict_Check(globals) ||
877+
(locals != NULL && !PyMapping_Check(locals))) {
878+
PyErr_BadInternalCall();
879+
return NULL;
880+
}
881+
#endif
882+
883+
PyFrameObject *back = tstate->frame;
884+
PyObject *builtins = frame_get_builtins(back, globals);
885+
if (builtins == NULL) {
886+
return NULL;
853887
}
888+
889+
PyFrameObject *f = frame_alloc(code);
890+
if (f == NULL) {
891+
Py_DECREF(builtins);
892+
return NULL;
893+
}
894+
854895
f->f_stacktop = f->f_valuestack;
855896
f->f_builtins = builtins;
856897
Py_XINCREF(back);
@@ -1142,13 +1183,15 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
11421183
void
11431184
_PyFrame_ClearFreeList(void)
11441185
{
1186+
#if PyFrame_MAXFREELIST > 0
11451187
while (free_list != NULL) {
11461188
PyFrameObject *f = free_list;
11471189
free_list = free_list->f_back;
11481190
PyObject_GC_Del(f);
11491191
--numfree;
11501192
}
11511193
assert(numfree == 0);
1194+
#endif
11521195
}
11531196

11541197
void
@@ -1161,9 +1204,11 @@ _PyFrame_Fini(void)
11611204
void
11621205
_PyFrame_DebugMallocStats(FILE *out)
11631206
{
1207+
#if PyFrame_MAXFREELIST > 0
11641208
_PyDebugAllocatorStats(out,
11651209
"free PyFrameObject",
11661210
numfree, sizeof(PyFrameObject));
1211+
#endif
11671212
}
11681213

11691214

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