Skip to content

Commit 4f7b3ec

Browse files
committed
Stackless issue python#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
1 parent acfc710 commit 4f7b3ec

3 files changed

Lines changed: 55 additions & 7 deletions

File tree

Stackless/core/stacklesseval.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ static PyMemberDef cstack_members[] = {
172172
{"task", T_OBJECT, offsetof(PyCStackObject, task), READONLY},
173173
{"startaddr", T_ADDR, offsetof(PyCStackObject, startaddr), READONLY},
174174
{"nesting_level", T_INT, offsetof(PyCStackObject, nesting_level), READONLY},
175+
{"serial", T_LONGLONG, offsetof(PyCStackObject, serial), READONLY},
175176
{0}
176177
};
177178

Stackless/module/stacklessmodule.c

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -784,12 +784,15 @@ get_thread_info(PyObject *self, PyObject *args)
784784
long id_is_valid;
785785
/* The additional optional argument flags is currently intentionally
786786
* undocumented. The lower order flag bits are reserved for future public
787-
* applications. If the flag bit 31 is set, the returned tuple contains
788-
* the watchdog list as an additional item. The Stackless test suite uses
789-
* this feature to ensure that the list is empty at start and end of each
790-
* test.
787+
* applications.
788+
* If the flag bit 30 is set, the values of serial, serial_last_jump and
789+
* initial_stub are appended to the result. The Stackless test suite uses them.
790+
* If the flag bit 31 is set, the watchdog list is appended to the result.
791+
* The Stackless test suite uses this feature to ensure that the list is empty at
792+
* start and end of each test.
791793
*/
792794
unsigned long flags=0;
795+
PyObject *retval;
793796

794797
if (!PyArg_ParseTuple(args, "|O!k:get_thread_info", &PyLong_Type, &thread_id, &flags))
795798
return NULL;
@@ -807,11 +810,44 @@ get_thread_info(PyObject *self, PyObject *args)
807810
RUNTIME_ERROR("Thread id not found", NULL);
808811
}
809812

810-
return Py_BuildValue((flags & (1ul<<31)) ? "(OOiO)": "(OOi)",
813+
retval = Py_BuildValue("(OOi)",
811814
ts->st.main ? (PyObject *) ts->st.main : Py_None,
812815
ts->st.runcount ? (PyObject *) ts->st.current : Py_None,
813-
ts->st.runcount,
814-
ts->st.watchdogs ? ts->st.watchdogs : Py_None);
816+
ts->st.runcount
817+
);
818+
819+
if (retval && (flags & (1ul<<30))) {
820+
Py_ssize_t retsize = PyTuple_GET_SIZE(retval);
821+
/* Append the serial numbers */
822+
PyObject *serial, *serial_last_jump;
823+
serial = PyLong_FromLongLong(ts->st.serial);
824+
if (serial == NULL) {
825+
Py_DECREF(retval);
826+
return NULL;
827+
}
828+
serial_last_jump = PyLong_FromLongLong(ts->st.serial_last_jump);
829+
if (serial == NULL) {
830+
Py_DECREF(retval);
831+
return NULL;
832+
}
833+
if (!_PyTuple_Resize(&retval, retsize + 3)) {
834+
PyTuple_SET_ITEM(retval, retsize, serial); /* steals a ref to serial */
835+
PyTuple_SET_ITEM(retval, retsize + 1, serial_last_jump); /* steals a ref to serial_last_jump */
836+
serial = ts->st.initial_stub ? (PyObject *) ts->st.initial_stub : Py_None;
837+
Py_INCREF(serial);
838+
PyTuple_SET_ITEM(retval, retsize + 2, serial); /* steals a ref to serial */
839+
}
840+
}
841+
if (retval && (flags & (1ul<<31))) {
842+
Py_ssize_t retsize = PyTuple_GET_SIZE(retval);
843+
/* Append the watchdog list */
844+
if (!_PyTuple_Resize(&retval, retsize + 1)) {
845+
PyObject *o = ts->st.watchdogs ? ts->st.watchdogs : Py_None;
846+
Py_INCREF(o);
847+
PyTuple_SET_ITEM(retval, retsize, o); /* steals a ref to o */
848+
}
849+
}
850+
return retval;
815851
}
816852

817853
static PyObject *

Stackless/unittests/support.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ def wrapper(self):
131131
# raise RuntimeError("There is no ref leak for obj. Missing referrers %d" % (delta,))
132132

133133

134+
def get_serial_last_jump(threadid=-1):
135+
"""Get the current serial_last_jump of the given thread.
136+
"""
137+
# The second argument of get_thread_info() is intentionally undocumented.
138+
# See C source.
139+
return stackless.get_thread_info(threadid, 1 << 30)[4]
140+
141+
134142
def get_watchdog_list(threadid):
135143
"""Get the watchdog list of a thread.
136144
@@ -561,6 +569,7 @@ def setUpStacklessTestCase(self):
561569

562570
self.__active_test_cases[id(self)] = self
563571
self.__uncollectable_tasklets = []
572+
self.__initial_cstack_serial = get_serial_last_jump()
564573
self.assertListEqual([t for t in gc.garbage if isinstance(t, stackless.tasklet)], [],
565574
"Leakage from other tests, with tasklets in gc.garbage")
566575

@@ -590,6 +599,8 @@ def setUp(self):
590599
(active_count, expected_thread_count))
591600

592601
def tearDown(self):
602+
# Test that the C-stack didn't change
603+
self.assertEqual(self.__initial_cstack_serial, get_serial_last_jump())
593604
# Test, that stackless errorhandler is None and reset it
594605
self.assertIsNone(stackless.set_error_handler(None))
595606

0 commit comments

Comments
 (0)