|
14 | 14 |
|
15 | 15 | #include "frameobject.h" // PyFrame_New() |
16 | 16 | #include "marshal.h" // PyMarshal_WriteLongToFile() |
| 17 | +#include "pylifecycle.h" |
| 18 | +#include "pystate.h" |
17 | 19 |
|
18 | 20 | #include <float.h> // FLT_MAX |
19 | 21 | #include <signal.h> |
@@ -2556,38 +2558,95 @@ get_strong_ref(void) |
2556 | 2558 | return ref; |
2557 | 2559 | } |
2558 | 2560 |
|
2559 | | -static PyObject * |
2560 | | -test_interp_ensure(PyObject *self, PyObject *unused) |
| 2561 | +static void |
| 2562 | +test_interp_ref_common(void) |
2561 | 2563 | { |
2562 | 2564 | PyInterpreterState *interp = PyInterpreterState_Get(); |
2563 | 2565 | 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 |
2564 | 2583 | 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(); |
2568 | 2614 |
|
2569 | 2615 | 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(); |
2574 | 2625 | } |
2575 | 2626 |
|
| 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. |
2576 | 2632 | 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); |
2580 | 2641 | } |
2581 | 2642 |
|
2582 | | - for (int i = 0; i < 20; ++i) { |
| 2643 | + for (int i = 0; i < 10; ++i) { |
| 2644 | + assert(PyThreadState_Get() == save_tstate); |
2583 | 2645 | PyThreadState_Release(); |
2584 | 2646 | } |
2585 | 2647 |
|
| 2648 | + assert(PyThreadState_GetUnchecked() == NULL); |
2586 | 2649 | PyInterpreterRef_Close(ref); |
2587 | | - PyInterpreterRef_Close(sub_ref); |
2588 | | - |
2589 | | - PyThreadState_Swap(save_tstate); |
2590 | | - Py_RETURN_NONE; |
2591 | 2650 | } |
2592 | 2651 |
|
2593 | 2652 | static PyMethodDef TestMethods[] = { |
@@ -2684,7 +2743,8 @@ static PyMethodDef TestMethods[] = { |
2684 | 2743 | {"test_atexit", test_atexit, METH_NOARGS}, |
2685 | 2744 | {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL}, |
2686 | 2745 | {"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}, |
2688 | 2748 | {NULL, NULL} /* sentinel */ |
2689 | 2749 | }; |
2690 | 2750 |
|
|
0 commit comments