mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
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().
This commit is contained in:
parent
ac4bf42411
commit
b4b53868d7
3 changed files with 168 additions and 86 deletions
|
@ -250,16 +250,26 @@ static uint64_t pydict_global_version = 0;
|
||||||
#ifndef PyDict_MAXFREELIST
|
#ifndef PyDict_MAXFREELIST
|
||||||
#define PyDict_MAXFREELIST 80
|
#define PyDict_MAXFREELIST 80
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* bpo-40521: dict free lists are shared by all interpreters. */
|
||||||
|
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
|
||||||
|
# undef PyDict_MAXFREELIST
|
||||||
|
# define PyDict_MAXFREELIST 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PyDict_MAXFREELIST > 0
|
||||||
static PyDictObject *free_list[PyDict_MAXFREELIST];
|
static PyDictObject *free_list[PyDict_MAXFREELIST];
|
||||||
static int numfree = 0;
|
static int numfree = 0;
|
||||||
static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
|
static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
|
||||||
static int numfreekeys = 0;
|
static int numfreekeys = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "clinic/dictobject.c.h"
|
#include "clinic/dictobject.c.h"
|
||||||
|
|
||||||
void
|
void
|
||||||
_PyDict_ClearFreeList(void)
|
_PyDict_ClearFreeList(void)
|
||||||
{
|
{
|
||||||
|
#if PyDict_MAXFREELIST > 0
|
||||||
while (numfree) {
|
while (numfree) {
|
||||||
PyDictObject *op = free_list[--numfree];
|
PyDictObject *op = free_list[--numfree];
|
||||||
assert(PyDict_CheckExact(op));
|
assert(PyDict_CheckExact(op));
|
||||||
|
@ -268,14 +278,17 @@ _PyDict_ClearFreeList(void)
|
||||||
while (numfreekeys) {
|
while (numfreekeys) {
|
||||||
PyObject_FREE(keys_free_list[--numfreekeys]);
|
PyObject_FREE(keys_free_list[--numfreekeys]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print summary info about the state of the optimized allocator */
|
/* Print summary info about the state of the optimized allocator */
|
||||||
void
|
void
|
||||||
_PyDict_DebugMallocStats(FILE *out)
|
_PyDict_DebugMallocStats(FILE *out)
|
||||||
{
|
{
|
||||||
|
#if PyDict_MAXFREELIST > 0
|
||||||
_PyDebugAllocatorStats(out,
|
_PyDebugAllocatorStats(out,
|
||||||
"free PyDictObject", numfree, sizeof(PyDictObject));
|
"free PyDictObject", numfree, sizeof(PyDictObject));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -553,10 +566,13 @@ static PyDictKeysObject *new_keys_object(Py_ssize_t size)
|
||||||
es = sizeof(Py_ssize_t);
|
es = sizeof(Py_ssize_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PyDict_MAXFREELIST > 0
|
||||||
if (size == PyDict_MINSIZE && numfreekeys > 0) {
|
if (size == PyDict_MINSIZE && numfreekeys > 0) {
|
||||||
dk = keys_free_list[--numfreekeys];
|
dk = keys_free_list[--numfreekeys];
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
dk = PyObject_MALLOC(sizeof(PyDictKeysObject)
|
dk = PyObject_MALLOC(sizeof(PyDictKeysObject)
|
||||||
+ es * size
|
+ es * size
|
||||||
+ sizeof(PyDictKeyEntry) * usable);
|
+ sizeof(PyDictKeyEntry) * usable);
|
||||||
|
@ -587,10 +603,12 @@ free_keys_object(PyDictKeysObject *keys)
|
||||||
Py_XDECREF(entries[i].me_key);
|
Py_XDECREF(entries[i].me_key);
|
||||||
Py_XDECREF(entries[i].me_value);
|
Py_XDECREF(entries[i].me_value);
|
||||||
}
|
}
|
||||||
|
#if PyDict_MAXFREELIST > 0
|
||||||
if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) {
|
if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) {
|
||||||
keys_free_list[numfreekeys++] = keys;
|
keys_free_list[numfreekeys++] = keys;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
PyObject_FREE(keys);
|
PyObject_FREE(keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,13 +621,16 @@ new_dict(PyDictKeysObject *keys, PyObject **values)
|
||||||
{
|
{
|
||||||
PyDictObject *mp;
|
PyDictObject *mp;
|
||||||
assert(keys != NULL);
|
assert(keys != NULL);
|
||||||
|
#if PyDict_MAXFREELIST > 0
|
||||||
if (numfree) {
|
if (numfree) {
|
||||||
mp = free_list[--numfree];
|
mp = free_list[--numfree];
|
||||||
assert (mp != NULL);
|
assert (mp != NULL);
|
||||||
assert (Py_IS_TYPE(mp, &PyDict_Type));
|
assert (Py_IS_TYPE(mp, &PyDict_Type));
|
||||||
_Py_NewReference((PyObject *)mp);
|
_Py_NewReference((PyObject *)mp);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
|
mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
|
||||||
if (mp == NULL) {
|
if (mp == NULL) {
|
||||||
dictkeys_decref(keys);
|
dictkeys_decref(keys);
|
||||||
|
@ -1258,12 +1279,15 @@ dictresize(PyDictObject *mp, Py_ssize_t minsize)
|
||||||
#ifdef Py_REF_DEBUG
|
#ifdef Py_REF_DEBUG
|
||||||
_Py_RefTotal--;
|
_Py_RefTotal--;
|
||||||
#endif
|
#endif
|
||||||
|
#if PyDict_MAXFREELIST > 0
|
||||||
if (oldkeys->dk_size == PyDict_MINSIZE &&
|
if (oldkeys->dk_size == PyDict_MINSIZE &&
|
||||||
numfreekeys < PyDict_MAXFREELIST)
|
numfreekeys < PyDict_MAXFREELIST)
|
||||||
{
|
{
|
||||||
keys_free_list[numfreekeys++] = oldkeys;
|
keys_free_list[numfreekeys++] = oldkeys;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
PyObject_FREE(oldkeys);
|
PyObject_FREE(oldkeys);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2005,10 +2029,15 @@ dict_dealloc(PyDictObject *mp)
|
||||||
assert(keys->dk_refcnt == 1);
|
assert(keys->dk_refcnt == 1);
|
||||||
dictkeys_decref(keys);
|
dictkeys_decref(keys);
|
||||||
}
|
}
|
||||||
if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type))
|
#if PyDict_MAXFREELIST > 0
|
||||||
|
if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) {
|
||||||
free_list[numfree++] = mp;
|
free_list[numfree++] = mp;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
Py_TYPE(mp)->tp_free((PyObject *)mp);
|
Py_TYPE(mp)->tp_free((PyObject *)mp);
|
||||||
|
}
|
||||||
Py_TRASHCAN_END
|
Py_TRASHCAN_END
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -556,12 +556,20 @@ static PyGetSetDef frame_getsetlist[] = {
|
||||||
free_list. Else programs creating lots of cyclic trash involving
|
free_list. Else programs creating lots of cyclic trash involving
|
||||||
frames could provoke free_list into growing without bound.
|
frames could provoke free_list into growing without bound.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static PyFrameObject *free_list = NULL;
|
|
||||||
static int numfree = 0; /* number of frames currently in free_list */
|
|
||||||
/* max value for numfree */
|
/* max value for numfree */
|
||||||
#define PyFrame_MAXFREELIST 200
|
#define PyFrame_MAXFREELIST 200
|
||||||
|
|
||||||
|
/* bpo-40521: frame free lists are shared by all interpreters. */
|
||||||
|
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
|
||||||
|
# undef PyFrame_MAXFREELIST
|
||||||
|
# define PyFrame_MAXFREELIST 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PyFrame_MAXFREELIST > 0
|
||||||
|
static PyFrameObject *free_list = NULL;
|
||||||
|
static int numfree = 0; /* number of frames currently in free_list */
|
||||||
|
#endif
|
||||||
|
|
||||||
static void _Py_HOT_FUNCTION
|
static void _Py_HOT_FUNCTION
|
||||||
frame_dealloc(PyFrameObject *f)
|
frame_dealloc(PyFrameObject *f)
|
||||||
{
|
{
|
||||||
|
@ -590,15 +598,19 @@ frame_dealloc(PyFrameObject *f)
|
||||||
Py_CLEAR(f->f_trace);
|
Py_CLEAR(f->f_trace);
|
||||||
|
|
||||||
co = f->f_code;
|
co = f->f_code;
|
||||||
if (co->co_zombieframe == NULL)
|
if (co->co_zombieframe == NULL) {
|
||||||
co->co_zombieframe = f;
|
co->co_zombieframe = f;
|
||||||
|
}
|
||||||
|
#if PyFrame_MAXFREELIST > 0
|
||||||
else if (numfree < PyFrame_MAXFREELIST) {
|
else if (numfree < PyFrame_MAXFREELIST) {
|
||||||
++numfree;
|
++numfree;
|
||||||
f->f_back = free_list;
|
f->f_back = free_list;
|
||||||
free_list = f;
|
free_list = f;
|
||||||
}
|
}
|
||||||
else
|
#endif
|
||||||
|
else {
|
||||||
PyObject_GC_Del(f);
|
PyObject_GC_Del(f);
|
||||||
|
}
|
||||||
|
|
||||||
Py_DECREF(co);
|
Py_DECREF(co);
|
||||||
Py_TRASHCAN_SAFE_END(f)
|
Py_TRASHCAN_SAFE_END(f)
|
||||||
|
@ -759,15 +771,107 @@ PyTypeObject PyFrame_Type = {
|
||||||
|
|
||||||
_Py_IDENTIFIER(__builtins__);
|
_Py_IDENTIFIER(__builtins__);
|
||||||
|
|
||||||
|
static inline PyFrameObject*
|
||||||
|
frame_alloc(PyCodeObject *code)
|
||||||
|
{
|
||||||
|
PyFrameObject *f;
|
||||||
|
|
||||||
|
f = code->co_zombieframe;
|
||||||
|
if (f != NULL) {
|
||||||
|
code->co_zombieframe = NULL;
|
||||||
|
_Py_NewReference((PyObject *)f);
|
||||||
|
assert(f->f_code == code);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_ssize_t ncells = PyTuple_GET_SIZE(code->co_cellvars);
|
||||||
|
Py_ssize_t nfrees = PyTuple_GET_SIZE(code->co_freevars);
|
||||||
|
Py_ssize_t extras = code->co_stacksize + code->co_nlocals + ncells + nfrees;
|
||||||
|
#if PyFrame_MAXFREELIST > 0
|
||||||
|
if (free_list == NULL)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras);
|
||||||
|
if (f == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if PyFrame_MAXFREELIST > 0
|
||||||
|
else {
|
||||||
|
assert(numfree > 0);
|
||||||
|
--numfree;
|
||||||
|
f = free_list;
|
||||||
|
free_list = free_list->f_back;
|
||||||
|
if (Py_SIZE(f) < extras) {
|
||||||
|
PyFrameObject *new_f = PyObject_GC_Resize(PyFrameObject, f, extras);
|
||||||
|
if (new_f == NULL) {
|
||||||
|
PyObject_GC_Del(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
f = new_f;
|
||||||
|
}
|
||||||
|
_Py_NewReference((PyObject *)f);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
f->f_code = code;
|
||||||
|
extras = code->co_nlocals + ncells + nfrees;
|
||||||
|
f->f_valuestack = f->f_localsplus + extras;
|
||||||
|
for (Py_ssize_t i=0; i<extras; i++) {
|
||||||
|
f->f_localsplus[i] = NULL;
|
||||||
|
}
|
||||||
|
f->f_locals = NULL;
|
||||||
|
f->f_trace = NULL;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline PyObject *
|
||||||
|
frame_get_builtins(PyFrameObject *back, PyObject *globals)
|
||||||
|
{
|
||||||
|
PyObject *builtins;
|
||||||
|
|
||||||
|
if (back != NULL && back->f_globals == globals) {
|
||||||
|
/* If we share the globals, we share the builtins.
|
||||||
|
Save a lookup and a call. */
|
||||||
|
builtins = back->f_builtins;
|
||||||
|
assert(builtins != NULL);
|
||||||
|
Py_INCREF(builtins);
|
||||||
|
return builtins;
|
||||||
|
}
|
||||||
|
|
||||||
|
builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__);
|
||||||
|
if (builtins != NULL && PyModule_Check(builtins)) {
|
||||||
|
builtins = PyModule_GetDict(builtins);
|
||||||
|
assert(builtins != NULL);
|
||||||
|
}
|
||||||
|
if (builtins != NULL) {
|
||||||
|
Py_INCREF(builtins);
|
||||||
|
return builtins;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No builtins! Make up a minimal one.
|
||||||
|
Give them 'None', at least. */
|
||||||
|
builtins = PyDict_New();
|
||||||
|
if (builtins == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (PyDict_SetItemString(builtins, "None", Py_None) < 0) {
|
||||||
|
Py_DECREF(builtins);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return builtins;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PyFrameObject* _Py_HOT_FUNCTION
|
PyFrameObject* _Py_HOT_FUNCTION
|
||||||
_PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
|
_PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
|
||||||
PyObject *globals, PyObject *locals)
|
PyObject *globals, PyObject *locals)
|
||||||
{
|
{
|
||||||
PyFrameObject *back = tstate->frame;
|
|
||||||
PyFrameObject *f;
|
|
||||||
PyObject *builtins;
|
|
||||||
Py_ssize_t i;
|
|
||||||
|
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
if (code == NULL || globals == NULL || !PyDict_Check(globals) ||
|
if (code == NULL || globals == NULL || !PyDict_Check(globals) ||
|
||||||
(locals != NULL && !PyMapping_Check(locals))) {
|
(locals != NULL && !PyMapping_Check(locals))) {
|
||||||
|
@ -775,82 +879,19 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (back == NULL || back->f_globals != globals) {
|
|
||||||
builtins = _PyDict_GetItemIdWithError(globals, &PyId___builtins__);
|
|
||||||
if (builtins) {
|
|
||||||
if (PyModule_Check(builtins)) {
|
|
||||||
builtins = PyModule_GetDict(builtins);
|
|
||||||
assert(builtins != NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (builtins == NULL) {
|
|
||||||
if (PyErr_Occurred()) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* No builtins! Make up a minimal one
|
|
||||||
Give them 'None', at least. */
|
|
||||||
builtins = PyDict_New();
|
|
||||||
if (builtins == NULL ||
|
|
||||||
PyDict_SetItemString(
|
|
||||||
builtins, "None", Py_None) < 0)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Py_INCREF(builtins);
|
|
||||||
|
|
||||||
|
PyFrameObject *back = tstate->frame;
|
||||||
|
PyObject *builtins = frame_get_builtins(back, globals);
|
||||||
|
if (builtins == NULL) {
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
/* If we share the globals, we share the builtins.
|
|
||||||
Save a lookup and a call. */
|
|
||||||
builtins = back->f_builtins;
|
|
||||||
assert(builtins != NULL);
|
|
||||||
Py_INCREF(builtins);
|
|
||||||
}
|
|
||||||
if (code->co_zombieframe != NULL) {
|
|
||||||
f = code->co_zombieframe;
|
|
||||||
code->co_zombieframe = NULL;
|
|
||||||
_Py_NewReference((PyObject *)f);
|
|
||||||
assert(f->f_code == code);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Py_ssize_t extras, ncells, nfrees;
|
|
||||||
ncells = PyTuple_GET_SIZE(code->co_cellvars);
|
|
||||||
nfrees = PyTuple_GET_SIZE(code->co_freevars);
|
|
||||||
extras = code->co_stacksize + code->co_nlocals + ncells +
|
|
||||||
nfrees;
|
|
||||||
if (free_list == NULL) {
|
|
||||||
f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type,
|
|
||||||
extras);
|
|
||||||
if (f == NULL) {
|
|
||||||
Py_DECREF(builtins);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert(numfree > 0);
|
|
||||||
--numfree;
|
|
||||||
f = free_list;
|
|
||||||
free_list = free_list->f_back;
|
|
||||||
if (Py_SIZE(f) < extras) {
|
|
||||||
PyFrameObject *new_f = PyObject_GC_Resize(PyFrameObject, f, extras);
|
|
||||||
if (new_f == NULL) {
|
|
||||||
PyObject_GC_Del(f);
|
|
||||||
Py_DECREF(builtins);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
f = new_f;
|
|
||||||
}
|
|
||||||
_Py_NewReference((PyObject *)f);
|
|
||||||
}
|
|
||||||
|
|
||||||
f->f_code = code;
|
PyFrameObject *f = frame_alloc(code);
|
||||||
extras = code->co_nlocals + ncells + nfrees;
|
if (f == NULL) {
|
||||||
f->f_valuestack = f->f_localsplus + extras;
|
Py_DECREF(builtins);
|
||||||
for (i=0; i<extras; i++)
|
return NULL;
|
||||||
f->f_localsplus[i] = NULL;
|
|
||||||
f->f_locals = NULL;
|
|
||||||
f->f_trace = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f->f_stacktop = f->f_valuestack;
|
f->f_stacktop = f->f_valuestack;
|
||||||
f->f_builtins = builtins;
|
f->f_builtins = builtins;
|
||||||
Py_XINCREF(back);
|
Py_XINCREF(back);
|
||||||
|
@ -1142,6 +1183,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
|
||||||
void
|
void
|
||||||
_PyFrame_ClearFreeList(void)
|
_PyFrame_ClearFreeList(void)
|
||||||
{
|
{
|
||||||
|
#if PyFrame_MAXFREELIST > 0
|
||||||
while (free_list != NULL) {
|
while (free_list != NULL) {
|
||||||
PyFrameObject *f = free_list;
|
PyFrameObject *f = free_list;
|
||||||
free_list = free_list->f_back;
|
free_list = free_list->f_back;
|
||||||
|
@ -1149,6 +1191,7 @@ _PyFrame_ClearFreeList(void)
|
||||||
--numfree;
|
--numfree;
|
||||||
}
|
}
|
||||||
assert(numfree == 0);
|
assert(numfree == 0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1161,9 +1204,11 @@ _PyFrame_Fini(void)
|
||||||
void
|
void
|
||||||
_PyFrame_DebugMallocStats(FILE *out)
|
_PyFrame_DebugMallocStats(FILE *out)
|
||||||
{
|
{
|
||||||
|
#if PyFrame_MAXFREELIST > 0
|
||||||
_PyDebugAllocatorStats(out,
|
_PyDebugAllocatorStats(out,
|
||||||
"free PyFrameObject",
|
"free PyFrameObject",
|
||||||
numfree, sizeof(PyFrameObject));
|
numfree, sizeof(PyFrameObject));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,12 @@ class tuple "PyTupleObject *" "&PyTuple_Type"
|
||||||
#define PyTuple_MAXFREELIST 2000 /* Maximum number of tuples of each size to save */
|
#define PyTuple_MAXFREELIST 2000 /* Maximum number of tuples of each size to save */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* bpo-40521: tuple free lists are shared by all interpreters. */
|
||||||
|
#ifdef EXPERIMENTAL_ISOLATED_SUBINTERPRETERS
|
||||||
|
# undef PyTuple_MAXSAVESIZE
|
||||||
|
# define PyTuple_MAXSAVESIZE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#if PyTuple_MAXSAVESIZE > 0
|
#if PyTuple_MAXSAVESIZE > 0
|
||||||
/* Entries 1 up to PyTuple_MAXSAVESIZE are free lists, entry 0 is the empty
|
/* Entries 1 up to PyTuple_MAXSAVESIZE are free lists, entry 0 is the empty
|
||||||
tuple () of which at most one instance will be allocated.
|
tuple () of which at most one instance will be allocated.
|
||||||
|
@ -248,7 +254,9 @@ tupledealloc(PyTupleObject *op)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
Py_TYPE(op)->tp_free((PyObject *)op);
|
Py_TYPE(op)->tp_free((PyObject *)op);
|
||||||
|
#if PyTuple_MAXSAVESIZE > 0
|
||||||
done:
|
done:
|
||||||
|
#endif
|
||||||
Py_TRASHCAN_END
|
Py_TRASHCAN_END
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue