bpo-40521: Always create the empty tuple singleton (GH-21116)

Py_InitializeFromConfig() now always creates the empty tuple
singleton as soon as possible.

Optimize PyTuple_New(0): it no longer has to check if the empty tuple
was created or not, it is always creatd.

* Add tuple_create_empty_tuple_singleton() function.
* Add tuple_get_empty() function.
* Remove state parameter of tuple_alloc().
This commit is contained in:
Victor Stinner 2020-06-24 15:21:54 +02:00 committed by GitHub
parent 80526f6841
commit 0430dfac62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 49 deletions

View File

@ -34,6 +34,7 @@ PyAPI_FUNC(int) _Py_IsLocaleCoercionTarget(const char *ctype_loc);
extern PyStatus _PyUnicode_Init(PyThreadState *tstate); extern PyStatus _PyUnicode_Init(PyThreadState *tstate);
extern int _PyStructSequence_Init(void); extern int _PyStructSequence_Init(void);
extern int _PyLong_Init(PyThreadState *tstate); extern int _PyLong_Init(PyThreadState *tstate);
extern PyStatus _PyTuple_Init(PyThreadState *tstate);
extern PyStatus _PyFaulthandler_Init(int enable); extern PyStatus _PyFaulthandler_Init(int enable);
extern int _PyTraceMalloc_Init(int enable); extern int _PyTraceMalloc_Init(int enable);
extern PyObject * _PyBuiltin_Init(PyThreadState *tstate); extern PyObject * _PyBuiltin_Init(PyThreadState *tstate);

View File

@ -2,10 +2,10 @@
/* Tuple object implementation */ /* Tuple object implementation */
#include "Python.h" #include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check() #include "pycore_abstract.h" // _PyIndex_Check()
#include "pycore_accu.h" #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() #include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_object.h" #include "pycore_object.h" // _PyObject_GC_TRACK()
/*[clinic input] /*[clinic input]
class tuple "PyTupleObject *" "&PyTuple_Type" class tuple "PyTupleObject *" "&PyTuple_Type"
@ -15,12 +15,14 @@ class tuple "PyTupleObject *" "&PyTuple_Type"
#include "clinic/tupleobject.c.h" #include "clinic/tupleobject.c.h"
#if PyTuple_MAXSAVESIZE > 0
static struct _Py_tuple_state * static struct _Py_tuple_state *
get_tuple_state(void) get_tuple_state(void)
{ {
PyInterpreterState *interp = _PyInterpreterState_GET(); PyInterpreterState *interp = _PyInterpreterState_GET();
return &interp->tuple; return &interp->tuple;
} }
#endif
static inline void static inline void
@ -55,14 +57,21 @@ _PyTuple_DebugMallocStats(FILE *out)
which wraps this function). which wraps this function).
*/ */
static PyTupleObject * static PyTupleObject *
tuple_alloc(struct _Py_tuple_state *state, Py_ssize_t size) tuple_alloc(Py_ssize_t size)
{ {
PyTupleObject *op; PyTupleObject *op;
#if PyTuple_MAXSAVESIZE > 0
// If Python is built with the empty tuple singleton,
// tuple_alloc(0) must not be called.
assert(size != 0);
#endif
if (size < 0) { if (size < 0) {
PyErr_BadInternalCall(); PyErr_BadInternalCall();
return NULL; return NULL;
} }
#if PyTuple_MAXSAVESIZE > 0 #if PyTuple_MAXSAVESIZE > 0
struct _Py_tuple_state *state = get_tuple_state();
#ifdef Py_DEBUG #ifdef Py_DEBUG
// tuple_alloc() must not be called after _PyTuple_Fini() // tuple_alloc() must not be called after _PyTuple_Fini()
assert(state->numfree[0] != -1); assert(state->numfree[0] != -1);
@ -93,36 +102,65 @@ tuple_alloc(struct _Py_tuple_state *state, Py_ssize_t size)
return op; return op;
} }
static int
tuple_create_empty_tuple_singleton(struct _Py_tuple_state *state)
{
#if PyTuple_MAXSAVESIZE > 0
assert(state->free_list[0] == NULL);
PyTupleObject *op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, 0);
if (op == NULL) {
return -1;
}
// The empty tuple singleton is not tracked by the GC.
// It does not contain any Python object.
state->free_list[0] = op;
state->numfree[0]++;
assert(state->numfree[0] == 1);
#endif
return 0;
}
static PyObject *
tuple_get_empty(void)
{
#if PyTuple_MAXSAVESIZE > 0
struct _Py_tuple_state *state = get_tuple_state();
PyTupleObject *op = state->free_list[0];
// tuple_get_empty() must not be called before _PyTuple_Init()
// or after _PyTuple_Fini()
assert(op != NULL);
#ifdef Py_DEBUG
assert(state->numfree[0] != -1);
#endif
Py_INCREF(op);
return (PyObject *) op;
#else
return PyTuple_New(0);
#endif
}
PyObject * PyObject *
PyTuple_New(Py_ssize_t size) PyTuple_New(Py_ssize_t size)
{ {
PyTupleObject *op; PyTupleObject *op;
#if PyTuple_MAXSAVESIZE > 0 #if PyTuple_MAXSAVESIZE > 0
struct _Py_tuple_state *state = get_tuple_state(); if (size == 0) {
if (size == 0 && state->free_list[0]) { return tuple_get_empty();
op = state->free_list[0];
Py_INCREF(op);
return (PyObject *) op;
} }
#endif #endif
op = tuple_alloc(state, size); op = tuple_alloc(size);
if (op == NULL) { if (op == NULL) {
return NULL; return NULL;
} }
for (Py_ssize_t i = 0; i < size; i++) { for (Py_ssize_t i = 0; i < size; i++) {
op->ob_item[i] = NULL; op->ob_item[i] = NULL;
} }
#if PyTuple_MAXSAVESIZE > 0
if (size == 0) {
#ifdef Py_DEBUG
// PyTuple_New() must not be called after _PyTuple_Fini()
assert(state->numfree[0] != -1);
#endif
state->free_list[0] = op;
++state->numfree[0];
Py_INCREF(op); /* extra INCREF so that this is never freed */
}
#endif
tuple_gc_track(op); tuple_gc_track(op);
return (PyObject *) op; return (PyObject *) op;
} }
@ -203,13 +241,11 @@ PyTuple_Pack(Py_ssize_t n, ...)
va_list vargs; va_list vargs;
if (n == 0) { if (n == 0) {
return PyTuple_New(0); return tuple_get_empty();
} }
struct _Py_tuple_state *state = get_tuple_state();
va_start(vargs, n); va_start(vargs, n);
PyTupleObject *result = tuple_alloc(state, n); PyTupleObject *result = tuple_alloc(n);
if (result == NULL) { if (result == NULL) {
va_end(vargs); va_end(vargs);
return NULL; return NULL;
@ -245,9 +281,9 @@ tupledealloc(PyTupleObject *op)
// tupledealloc() must not be called after _PyTuple_Fini() // tupledealloc() must not be called after _PyTuple_Fini()
assert(state->numfree[0] != -1); assert(state->numfree[0] != -1);
#endif #endif
if (len < PyTuple_MAXSAVESIZE && if (len < PyTuple_MAXSAVESIZE
state->numfree[len] < PyTuple_MAXFREELIST && && state->numfree[len] < PyTuple_MAXFREELIST
Py_IS_TYPE(op, &PyTuple_Type)) && Py_IS_TYPE(op, &PyTuple_Type))
{ {
op->ob_item[0] = (PyObject *) state->free_list[len]; op->ob_item[0] = (PyObject *) state->free_list[len];
state->numfree[len]++; state->numfree[len]++;
@ -257,6 +293,7 @@ tupledealloc(PyTupleObject *op)
#endif #endif
} }
Py_TYPE(op)->tp_free((PyObject *)op); Py_TYPE(op)->tp_free((PyObject *)op);
#if PyTuple_MAXSAVESIZE > 0 #if PyTuple_MAXSAVESIZE > 0
done: done:
#endif #endif
@ -423,11 +460,10 @@ PyObject *
_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
{ {
if (n == 0) { if (n == 0) {
return PyTuple_New(0); return tuple_get_empty();
} }
struct _Py_tuple_state *state = get_tuple_state(); PyTupleObject *tuple = tuple_alloc(n);
PyTupleObject *tuple = tuple_alloc(state, n);
if (tuple == NULL) { if (tuple == NULL) {
return NULL; return NULL;
} }
@ -494,11 +530,10 @@ tupleconcat(PyTupleObject *a, PyObject *bb)
assert((size_t)Py_SIZE(a) + (size_t)Py_SIZE(b) < PY_SSIZE_T_MAX); assert((size_t)Py_SIZE(a) + (size_t)Py_SIZE(b) < PY_SSIZE_T_MAX);
size = Py_SIZE(a) + Py_SIZE(b); size = Py_SIZE(a) + Py_SIZE(b);
if (size == 0) { if (size == 0) {
return PyTuple_New(0); return tuple_get_empty();
} }
struct _Py_tuple_state *state = get_tuple_state(); np = tuple_alloc(size);
np = tuple_alloc(state, size);
if (np == NULL) { if (np == NULL) {
return NULL; return NULL;
} }
@ -536,13 +571,12 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n)
} }
} }
if (Py_SIZE(a) == 0 || n <= 0) { if (Py_SIZE(a) == 0 || n <= 0) {
return PyTuple_New(0); return tuple_get_empty();
} }
if (n > PY_SSIZE_T_MAX / Py_SIZE(a)) if (n > PY_SSIZE_T_MAX / Py_SIZE(a))
return PyErr_NoMemory(); return PyErr_NoMemory();
size = Py_SIZE(a) * n; size = Py_SIZE(a) * n;
struct _Py_tuple_state *state = get_tuple_state(); np = tuple_alloc(size);
np = tuple_alloc(state, size);
if (np == NULL) if (np == NULL)
return NULL; return NULL;
p = np->ob_item; p = np->ob_item;
@ -713,10 +747,12 @@ tuple_new_impl(PyTypeObject *type, PyObject *iterable)
if (type != &PyTuple_Type) if (type != &PyTuple_Type)
return tuple_subtype_new(type, iterable); return tuple_subtype_new(type, iterable);
if (iterable == NULL) if (iterable == NULL) {
return PyTuple_New(0); return tuple_get_empty();
else }
else {
return PySequence_Tuple(iterable); return PySequence_Tuple(iterable);
}
} }
static PyObject * static PyObject *
@ -735,7 +771,9 @@ tuple_vectorcall(PyObject *type, PyObject * const*args,
if (nargs) { if (nargs) {
return tuple_new_impl((PyTypeObject *)type, args[0]); return tuple_new_impl((PyTypeObject *)type, args[0]);
} }
return PyTuple_New(0); else {
return tuple_get_empty();
}
} }
static PyObject * static PyObject *
@ -798,7 +836,7 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
&stop, step); &stop, step);
if (slicelength <= 0) { if (slicelength <= 0) {
return PyTuple_New(0); return tuple_get_empty();
} }
else if (start == 0 && step == 1 && else if (start == 0 && step == 1 &&
slicelength == PyTuple_GET_SIZE(self) && slicelength == PyTuple_GET_SIZE(self) &&
@ -807,8 +845,7 @@ tuplesubscript(PyTupleObject* self, PyObject* item)
return (PyObject *)self; return (PyObject *)self;
} }
else { else {
struct _Py_tuple_state *state = get_tuple_state(); PyTupleObject* result = tuple_alloc(slicelength);
PyTupleObject* result = tuple_alloc(state, slicelength);
if (!result) return NULL; if (!result) return NULL;
src = self->ob_item; src = self->ob_item;
@ -988,15 +1025,26 @@ _PyTuple_ClearFreeList(PyThreadState *tstate)
#endif #endif
} }
PyStatus
_PyTuple_Init(PyThreadState *tstate)
{
struct _Py_tuple_state *state = &tstate->interp->tuple;
if (tuple_create_empty_tuple_singleton(state) < 0) {
return _PyStatus_NO_MEMORY();
}
return _PyStatus_OK();
}
void void
_PyTuple_Fini(PyThreadState *tstate) _PyTuple_Fini(PyThreadState *tstate)
{ {
#if PyTuple_MAXSAVESIZE > 0 #if PyTuple_MAXSAVESIZE > 0
struct _Py_tuple_state *state = &tstate->interp->tuple; struct _Py_tuple_state *state = &tstate->interp->tuple;
/* empty tuples are used all over the place and applications may // The empty tuple singleton must not be tracked by the GC
* rely on the fact that an empty tuple is a singleton. */ assert(!_PyObject_GC_IS_TRACKED(state->free_list[0]));
Py_CLEAR(state->free_list[0]); Py_CLEAR(state->free_list[0]);
_PyTuple_ClearFreeList(tstate); _PyTuple_ClearFreeList(tstate);
#ifdef Py_DEBUG #ifdef Py_DEBUG
state->numfree[0] = -1; state->numfree[0] = -1;

View File

@ -583,6 +583,14 @@ pycore_init_types(PyThreadState *tstate)
return status; return status;
} }
// Create the empty tuple singleton. It must be created before the first
// PyType_Ready() call since PyType_Ready() creates tuples, for tp_bases
// for example.
status = _PyTuple_Init(tstate);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
if (is_main_interp) { if (is_main_interp) {
status = _PyTypes_Init(); status = _PyTypes_Init();
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
@ -590,7 +598,6 @@ pycore_init_types(PyThreadState *tstate)
} }
} }
if (!_PyLong_Init(tstate)) { if (!_PyLong_Init(tstate)) {
return _PyStatus_ERR("can't init longs"); return _PyStatus_ERR("can't init longs");
} }