From 0430dfac629b4eb0e899a09b899a494aa92145f6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 24 Jun 2020 15:21:54 +0200 Subject: [PATCH] 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(). --- Include/internal/pycore_pylifecycle.h | 1 + Objects/tupleobject.c | 144 +++++++++++++++++--------- Python/pylifecycle.c | 9 +- 3 files changed, 105 insertions(+), 49 deletions(-) diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index f29c7cb9f39..3b217378711 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -34,6 +34,7 @@ PyAPI_FUNC(int) _Py_IsLocaleCoercionTarget(const char *ctype_loc); extern PyStatus _PyUnicode_Init(PyThreadState *tstate); extern int _PyStructSequence_Init(void); extern int _PyLong_Init(PyThreadState *tstate); +extern PyStatus _PyTuple_Init(PyThreadState *tstate); extern PyStatus _PyFaulthandler_Init(int enable); extern int _PyTraceMalloc_Init(int enable); extern PyObject * _PyBuiltin_Init(PyThreadState *tstate); diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index f4f9aa259e8..41677d7e710 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -2,10 +2,10 @@ /* Tuple object implementation */ #include "Python.h" -#include "pycore_abstract.h" // _PyIndex_Check() -#include "pycore_accu.h" -#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() -#include "pycore_object.h" +#include "pycore_abstract.h" // _PyIndex_Check() +#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() +#include "pycore_initconfig.h" // _PyStatus_OK() +#include "pycore_object.h" // _PyObject_GC_TRACK() /*[clinic input] class tuple "PyTupleObject *" "&PyTuple_Type" @@ -15,12 +15,14 @@ class tuple "PyTupleObject *" "&PyTuple_Type" #include "clinic/tupleobject.c.h" +#if PyTuple_MAXSAVESIZE > 0 static struct _Py_tuple_state * get_tuple_state(void) { PyInterpreterState *interp = _PyInterpreterState_GET(); return &interp->tuple; } +#endif static inline void @@ -55,14 +57,21 @@ _PyTuple_DebugMallocStats(FILE *out) which wraps this function). */ static PyTupleObject * -tuple_alloc(struct _Py_tuple_state *state, Py_ssize_t size) +tuple_alloc(Py_ssize_t size) { 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) { PyErr_BadInternalCall(); return NULL; } + #if PyTuple_MAXSAVESIZE > 0 + struct _Py_tuple_state *state = get_tuple_state(); #ifdef Py_DEBUG // tuple_alloc() must not be called after _PyTuple_Fini() assert(state->numfree[0] != -1); @@ -93,36 +102,65 @@ tuple_alloc(struct _Py_tuple_state *state, Py_ssize_t size) 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 * PyTuple_New(Py_ssize_t size) { PyTupleObject *op; #if PyTuple_MAXSAVESIZE > 0 - struct _Py_tuple_state *state = get_tuple_state(); - if (size == 0 && state->free_list[0]) { - op = state->free_list[0]; - Py_INCREF(op); - return (PyObject *) op; + if (size == 0) { + return tuple_get_empty(); } #endif - op = tuple_alloc(state, size); + op = tuple_alloc(size); if (op == NULL) { return NULL; } for (Py_ssize_t i = 0; i < size; i++) { 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); return (PyObject *) op; } @@ -203,13 +241,11 @@ PyTuple_Pack(Py_ssize_t n, ...) va_list vargs; if (n == 0) { - return PyTuple_New(0); + return tuple_get_empty(); } - struct _Py_tuple_state *state = get_tuple_state(); - va_start(vargs, n); - PyTupleObject *result = tuple_alloc(state, n); + PyTupleObject *result = tuple_alloc(n); if (result == NULL) { va_end(vargs); return NULL; @@ -245,9 +281,9 @@ tupledealloc(PyTupleObject *op) // tupledealloc() must not be called after _PyTuple_Fini() assert(state->numfree[0] != -1); #endif - if (len < PyTuple_MAXSAVESIZE && - state->numfree[len] < PyTuple_MAXFREELIST && - Py_IS_TYPE(op, &PyTuple_Type)) + if (len < PyTuple_MAXSAVESIZE + && state->numfree[len] < PyTuple_MAXFREELIST + && Py_IS_TYPE(op, &PyTuple_Type)) { op->ob_item[0] = (PyObject *) state->free_list[len]; state->numfree[len]++; @@ -257,6 +293,7 @@ tupledealloc(PyTupleObject *op) #endif } Py_TYPE(op)->tp_free((PyObject *)op); + #if PyTuple_MAXSAVESIZE > 0 done: #endif @@ -423,11 +460,10 @@ PyObject * _PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) { if (n == 0) { - return PyTuple_New(0); + return tuple_get_empty(); } - struct _Py_tuple_state *state = get_tuple_state(); - PyTupleObject *tuple = tuple_alloc(state, n); + PyTupleObject *tuple = tuple_alloc(n); if (tuple == 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); size = Py_SIZE(a) + Py_SIZE(b); if (size == 0) { - return PyTuple_New(0); + return tuple_get_empty(); } - struct _Py_tuple_state *state = get_tuple_state(); - np = tuple_alloc(state, size); + np = tuple_alloc(size); if (np == NULL) { return NULL; } @@ -536,13 +571,12 @@ tuplerepeat(PyTupleObject *a, Py_ssize_t n) } } if (Py_SIZE(a) == 0 || n <= 0) { - return PyTuple_New(0); + return tuple_get_empty(); } if (n > PY_SSIZE_T_MAX / Py_SIZE(a)) return PyErr_NoMemory(); size = Py_SIZE(a) * n; - struct _Py_tuple_state *state = get_tuple_state(); - np = tuple_alloc(state, size); + np = tuple_alloc(size); if (np == NULL) return NULL; p = np->ob_item; @@ -713,10 +747,12 @@ tuple_new_impl(PyTypeObject *type, PyObject *iterable) if (type != &PyTuple_Type) return tuple_subtype_new(type, iterable); - if (iterable == NULL) - return PyTuple_New(0); - else + if (iterable == NULL) { + return tuple_get_empty(); + } + else { return PySequence_Tuple(iterable); + } } static PyObject * @@ -735,7 +771,9 @@ tuple_vectorcall(PyObject *type, PyObject * const*args, if (nargs) { return tuple_new_impl((PyTypeObject *)type, args[0]); } - return PyTuple_New(0); + else { + return tuple_get_empty(); + } } static PyObject * @@ -798,7 +836,7 @@ tuplesubscript(PyTupleObject* self, PyObject* item) &stop, step); if (slicelength <= 0) { - return PyTuple_New(0); + return tuple_get_empty(); } else if (start == 0 && step == 1 && slicelength == PyTuple_GET_SIZE(self) && @@ -807,8 +845,7 @@ tuplesubscript(PyTupleObject* self, PyObject* item) return (PyObject *)self; } else { - struct _Py_tuple_state *state = get_tuple_state(); - PyTupleObject* result = tuple_alloc(state, slicelength); + PyTupleObject* result = tuple_alloc(slicelength); if (!result) return NULL; src = self->ob_item; @@ -988,15 +1025,26 @@ _PyTuple_ClearFreeList(PyThreadState *tstate) #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 _PyTuple_Fini(PyThreadState *tstate) { #if PyTuple_MAXSAVESIZE > 0 struct _Py_tuple_state *state = &tstate->interp->tuple; - /* empty tuples are used all over the place and applications may - * rely on the fact that an empty tuple is a singleton. */ + // The empty tuple singleton must not be tracked by the GC + assert(!_PyObject_GC_IS_TRACKED(state->free_list[0])); Py_CLEAR(state->free_list[0]); - _PyTuple_ClearFreeList(tstate); #ifdef Py_DEBUG state->numfree[0] = -1; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index eda4c6ad7e4..4b658f847bc 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -583,6 +583,14 @@ pycore_init_types(PyThreadState *tstate) 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) { status = _PyTypes_Init(); if (_PyStatus_EXCEPTION(status)) { @@ -590,7 +598,6 @@ pycore_init_types(PyThreadState *tstate) } } - if (!_PyLong_Init(tstate)) { return _PyStatus_ERR("can't init longs"); }