From bffe80b80ba1afd884cea4bbc79e52b8992941a5 Mon Sep 17 00:00:00 2001 From: Anselm Kruis Date: Sat, 22 Jun 2019 01:30:21 +0200 Subject: [PATCH] Stackless issue #222: Support code for testing C-stack serial numbers Allow the Stackless test suite to access - PyThreadState fields st.serial, st.serial_last_jump, st.initial_stub - PyCStackObject attribute serial Intentionally undocumented (cherry picked from commit 4f7b3ec25139316a226ae6d4eedbc768c6684dc5) --- Stackless/core/stacklesseval.c | 1 + Stackless/module/stacklessmodule.c | 50 +++++++++++++++++++++++++----- Stackless/unittests/support.py | 11 +++++++ 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c index f91a39a8cb7164..3becc754b10e78 100644 --- a/Stackless/core/stacklesseval.c +++ b/Stackless/core/stacklesseval.c @@ -172,6 +172,7 @@ static PyMemberDef cstack_members[] = { {"task", T_OBJECT, offsetof(PyCStackObject, task), READONLY}, {"startaddr", T_ADDR, offsetof(PyCStackObject, startaddr), READONLY}, {"nesting_level", T_INT, offsetof(PyCStackObject, nesting_level), READONLY}, + {"serial", T_LONGLONG, offsetof(PyCStackObject, serial), READONLY}, {0} }; diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c index cc3baf7f55a91f..dd44c30e084b6f 100644 --- a/Stackless/module/stacklessmodule.c +++ b/Stackless/module/stacklessmodule.c @@ -784,12 +784,15 @@ get_thread_info(PyObject *self, PyObject *args) long id_is_valid; /* The additional optional argument flags is currently intentionally * undocumented. The lower order flag bits are reserved for future public - * applications. If the flag bit 31 is set, the returned tuple contains - * the watchdog list as an additional item. The Stackless test suite uses - * this feature to ensure that the list is empty at start and end of each - * test. + * applications. + * If the flag bit 30 is set, the values of serial, serial_last_jump and + * initial_stub are appended to the result. The Stackless test suite uses them. + * If the flag bit 31 is set, the watchdog list is appended to the result. + * The Stackless test suite uses this feature to ensure that the list is empty at + * start and end of each test. */ unsigned long flags=0; + PyObject *retval; if (!PyArg_ParseTuple(args, "|O!k:get_thread_info", &PyLong_Type, &thread_id, &flags)) return NULL; @@ -807,11 +810,44 @@ get_thread_info(PyObject *self, PyObject *args) RUNTIME_ERROR("Thread id not found", NULL); } - return Py_BuildValue((flags & (1ul<<31)) ? "(OOiO)": "(OOi)", + retval = Py_BuildValue("(OOi)", ts->st.main ? (PyObject *) ts->st.main : Py_None, ts->st.runcount ? (PyObject *) ts->st.current : Py_None, - ts->st.runcount, - ts->st.watchdogs ? ts->st.watchdogs : Py_None); + ts->st.runcount + ); + + if (retval && (flags & (1ul<<30))) { + Py_ssize_t retsize = PyTuple_GET_SIZE(retval); + /* Append the serial numbers */ + PyObject *serial, *serial_last_jump; + serial = PyLong_FromLongLong(ts->st.serial); + if (serial == NULL) { + Py_DECREF(retval); + return NULL; + } + serial_last_jump = PyLong_FromLongLong(ts->st.serial_last_jump); + if (serial == NULL) { + Py_DECREF(retval); + return NULL; + } + if (!_PyTuple_Resize(&retval, retsize + 3)) { + PyTuple_SET_ITEM(retval, retsize, serial); /* steals a ref to serial */ + PyTuple_SET_ITEM(retval, retsize + 1, serial_last_jump); /* steals a ref to serial_last_jump */ + serial = ts->st.initial_stub ? (PyObject *) ts->st.initial_stub : Py_None; + Py_INCREF(serial); + PyTuple_SET_ITEM(retval, retsize + 2, serial); /* steals a ref to serial */ + } + } + if (retval && (flags & (1ul<<31))) { + Py_ssize_t retsize = PyTuple_GET_SIZE(retval); + /* Append the watchdog list */ + if (!_PyTuple_Resize(&retval, retsize + 1)) { + PyObject *o = ts->st.watchdogs ? ts->st.watchdogs : Py_None; + Py_INCREF(o); + PyTuple_SET_ITEM(retval, retsize, o); /* steals a ref to o */ + } + } + return retval; } static PyObject * diff --git a/Stackless/unittests/support.py b/Stackless/unittests/support.py index 2bd6e7f64e2f3e..3bd97662ffa336 100644 --- a/Stackless/unittests/support.py +++ b/Stackless/unittests/support.py @@ -131,6 +131,14 @@ def wrapper(self): # raise RuntimeError("There is no ref leak for obj. Missing referrers %d" % (delta,)) +def get_serial_last_jump(threadid=-1): + """Get the current serial_last_jump of the given thread. + """ + # The second argument of get_thread_info() is intentionally undocumented. + # See C source. + return stackless.get_thread_info(threadid, 1 << 30)[4] + + def get_watchdog_list(threadid): """Get the watchdog list of a thread. @@ -561,6 +569,7 @@ def setUpStacklessTestCase(self): self.__active_test_cases[id(self)] = self self.__uncollectable_tasklets = [] + self.__initial_cstack_serial = get_serial_last_jump() self.assertListEqual([t for t in gc.garbage if isinstance(t, stackless.tasklet)], [], "Leakage from other tests, with tasklets in gc.garbage") @@ -590,6 +599,8 @@ def setUp(self): (active_count, expected_thread_count)) def tearDown(self): + # Test that the C-stack didn't change + self.assertEqual(self.__initial_cstack_serial, get_serial_last_jump()) # Test, that stackless errorhandler is None and reset it self.assertIsNone(stackless.set_error_handler(None))