[Python-checkins] bpo-40521: Always create the empty tuple singleton (GH-21116)

Victor Stinner webhook-mailer at python.org
Wed Jun 24 09:22:06 EDT 2020


https://github.com/python/cpython/commit/0430dfac629b4eb0e899a09b899a494aa92145f6
commit: 0430dfac629b4eb0e899a09b899a494aa92145f6
branch: master
author: Victor Stinner <vstinner at python.org>
committer: GitHub <noreply at github.com>
date: 2020-06-24T15:21:54+02:00
summary:

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().

files:
M Include/internal/pycore_pylifecycle.h
M Objects/tupleobject.c
M Python/pylifecycle.c

diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h
index f29c7cb9f392c..3b2173787118f 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 f4f9aa259e8b2..41677d7e710aa 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 eda4c6ad7e474..4b658f847bc12 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");
     }



More information about the Python-checkins mailing list