Skip to content

Commit 9ea007d

Browse files
Add test for exit
1 parent d5c2e03 commit 9ea007d

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

Lib/test/test_capi/test_opt.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ def iter_opnames(ex):
6060
def get_opnames(ex):
6161
return list(iter_opnames(ex))
6262

63+
def iter_ops(ex):
64+
for item in ex:
65+
yield item
66+
67+
def get_ops(ex):
68+
return list(iter_ops(ex))
69+
6370

6471
@requires_specialization
6572
@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds")
@@ -3003,14 +3010,22 @@ def f():
30033010
# Outer loop warms up later, linking to the inner one.
30043011
# Therefore, we have at least two executors.
30053012
self.assertGreaterEqual(len(all_executors), 2)
3013+
executor_ids = [id(e) for e in all_executors]
30063014
for executor in all_executors:
3007-
opnames = list(get_opnames(executor))
3015+
ops = list(get_ops(executor))
30083016
# Assert all executors first terminator ends in
30093017
# _EXIT_TRACE or _JUMP_TO_TOP, not _DEOPT
3010-
for idx, op in enumerate(opnames):
3011-
if op == "_EXIT_TRACE" or op == "_JUMP_TO_TOP":
3018+
for idx, op in enumerate(ops):
3019+
opname = op[0]
3020+
if opname == "_EXIT_TRACE":
3021+
# All executors exits should point to another valid executor
3022+
exit = op[3]
3023+
link_to = _testinternalcapi.get_exit_executor(exit)
3024+
self.assertIn(id(link_to), executor_ids)
3025+
break
3026+
elif opname == "_JUMP_TO_TOP":
30123027
break
3013-
elif op == "_DEOPT":
3028+
elif opname == "_DEOPT":
30143029
self.fail(f"_DEOPT encountered first at executor"
30153030
f" {executor} at offset {idx} rather"
30163031
f" than expected _EXIT_TRACE")

Modules/_testinternalcapi.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,6 +1245,22 @@ invalidate_executors(PyObject *self, PyObject *obj)
12451245
Py_RETURN_NONE;
12461246
}
12471247

1248+
static PyObject *
1249+
get_exit_executor(PyObject *self, PyObject *arg)
1250+
{
1251+
if (!PyLong_CheckExact(arg)) {
1252+
PyErr_SetString(PyExc_TypeError, "argument must be an ID to an _PyExitData");
1253+
return NULL;
1254+
}
1255+
uint64_t ptr;
1256+
if (PyLong_AsUInt64(arg, &ptr) < 0) {
1257+
// Error set by PyLong API
1258+
return NULL;
1259+
}
1260+
_PyExitData *exit = (_PyExitData *)ptr;
1261+
return Py_NewRef(exit->executor);
1262+
}
1263+
12481264
#endif
12491265

12501266
static int _pending_callback(void *arg)
@@ -2546,6 +2562,7 @@ static PyMethodDef module_functions[] = {
25462562
#ifdef _Py_TIER2
25472563
{"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL},
25482564
{"invalidate_executors", invalidate_executors, METH_O, NULL},
2565+
{"get_exit_executor", get_exit_executor, METH_O, NULL},
25492566
#endif
25502567
{"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc),
25512568
METH_VARARGS | METH_KEYWORDS},

0 commit comments

Comments
 (0)