Skip to content

Commit 531928e

Browse files
committed
Add some more tests.
1 parent 0c3c1c7 commit 531928e

1 file changed

Lines changed: 78 additions & 18 deletions

File tree

Modules/_testcapimodule.c

Lines changed: 78 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
#include "frameobject.h" // PyFrame_New()
1616
#include "marshal.h" // PyMarshal_WriteLongToFile()
17+
#include "pylifecycle.h"
18+
#include "pystate.h"
1719

1820
#include <float.h> // FLT_MAX
1921
#include <signal.h>
@@ -2556,38 +2558,95 @@ get_strong_ref(void)
25562558
return ref;
25572559
}
25582560

2559-
static PyObject *
2560-
test_interp_ensure(PyObject *self, PyObject *unused)
2561+
static void
2562+
test_interp_ref_common(void)
25612563
{
25622564
PyInterpreterState *interp = PyInterpreterState_Get();
25632565
PyInterpreterRef ref = get_strong_ref();
2566+
assert(PyInterpreterRef_AsInterpreter(ref) == interp);
2567+
2568+
PyInterpreterRef ref_2 = PyInterpreterRef_Dup(ref);
2569+
assert(PyInterpreterRef_AsInterpreter(ref_2) == interp);
2570+
2571+
// We can close the references in any order
2572+
PyInterpreterRef_Close(ref);
2573+
PyInterpreterRef_Close(ref_2);
2574+
}
2575+
2576+
static PyObject *
2577+
test_interpreter_refs(PyObject *self, PyObject *unused)
2578+
{
2579+
// Test the main interpreter
2580+
test_interp_ref_common();
2581+
2582+
// Test a (legacy) subinterpreter
25642583
PyThreadState *save_tstate = PyThreadState_Swap(NULL);
2565-
PyThreadState *tstate = Py_NewInterpreter();
2566-
PyInterpreterRef sub_ref = get_strong_ref();
2567-
PyInterpreterState *subinterp = PyThreadState_GetInterpreter(tstate);
2584+
PyThreadState *interp_tstate = Py_NewInterpreter();
2585+
test_interp_ref_common();
2586+
Py_EndInterpreter(interp_tstate);
2587+
2588+
// Test an isolated subinterpreter
2589+
PyInterpreterConfig config = {
2590+
.gil = PyInterpreterConfig_OWN_GIL,
2591+
.check_multi_interp_extensions = 1
2592+
};
2593+
2594+
PyThreadState *isolated_interp_tstate;
2595+
PyStatus status = Py_NewInterpreterFromConfig(&isolated_interp_tstate, &config);
2596+
if (PyStatus_Exception(status)) {
2597+
PyErr_SetString(PyExc_RuntimeError, "interpreter creation failed");
2598+
return NULL;
2599+
}
2600+
2601+
test_interp_ref_common();
2602+
Py_EndInterpreter(isolated_interp_tstate);
2603+
PyThreadState_Swap(save_tstate);
2604+
Py_RETURN_NONE;
2605+
}
2606+
2607+
static PyObject *
2608+
test_thread_state_ensure_nested(PyObject *self, PyObject *unused)
2609+
{
2610+
PyInterpreterState *interp = PyInterpreterState_Get();
2611+
PyThreadState *save_tstate = PyThreadState_Swap(NULL);
2612+
assert(PyGILState_GetThisThreadState() == save_tstate);
2613+
PyInterpreterRef ref = get_strong_ref();
25682614

25692615
for (int i = 0; i < 10; ++i) {
2570-
int res = PyThreadState_Ensure(ref);
2571-
assert(res == 0);
2572-
assert(PyThreadState_GetUnchecked() != NULL);
2573-
assert(PyInterpreterState_Get() == interp);
2616+
// Test reactivation of the detached tstate.
2617+
if (PyThreadState_Ensure(ref) < 0) {
2618+
PyInterpreterRef_Close(ref);
2619+
return PyErr_NoMemory();
2620+
}
2621+
2622+
// No new thread state should've been created.
2623+
assert(PyThreadState_Get() == save_tstate);
2624+
PyThreadState_Release();
25742625
}
25752626

2627+
assert(PyThreadState_GetUnchecked() == NULL);
2628+
2629+
// Similarly, test ensuring with deep nesting and *then* releasing.
2630+
// If the (detached) gilstate matches the interpreter, then it shouldn't
2631+
// create a new thread state.
25762632
for (int i = 0; i < 10; ++i) {
2577-
int res = PyThreadState_Ensure(sub_ref);
2578-
assert(res == 0);
2579-
assert(PyInterpreterState_Get() == subinterp);
2633+
if (PyThreadState_Ensure(ref) < 0) {
2634+
// This will technically leak other thread states, but it doesn't
2635+
// matter because this is a test.
2636+
PyInterpreterRef_Close(ref);
2637+
return PyErr_NoMemory();
2638+
}
2639+
2640+
assert(PyThreadState_Get() == save_tstate);
25802641
}
25812642

2582-
for (int i = 0; i < 20; ++i) {
2643+
for (int i = 0; i < 10; ++i) {
2644+
assert(PyThreadState_Get() == save_tstate);
25832645
PyThreadState_Release();
25842646
}
25852647

2648+
assert(PyThreadState_GetUnchecked() == NULL);
25862649
PyInterpreterRef_Close(ref);
2587-
PyInterpreterRef_Close(sub_ref);
2588-
2589-
PyThreadState_Swap(save_tstate);
2590-
Py_RETURN_NONE;
25912650
}
25922651

25932652
static PyMethodDef TestMethods[] = {
@@ -2684,7 +2743,8 @@ static PyMethodDef TestMethods[] = {
26842743
{"test_atexit", test_atexit, METH_NOARGS},
26852744
{"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL},
26862745
{"toggle_reftrace_printer", toggle_reftrace_printer, METH_O},
2687-
{"test_interp_ensure", test_interp_ensure, METH_NOARGS},
2746+
{"test_interpreter_refs", test_interpreter_refs, METH_NOARGS},
2747+
{"test_thread_state_ensure_nested", test_thread_state_ensure_nested, METH_NOARGS},
26882748
{NULL, NULL} /* sentinel */
26892749
};
26902750

0 commit comments

Comments
 (0)