Skip to content

Commit 799f591

Browse files
rwgkb-passpre-commit-ci[bot]
authored
Re-enable Move Subinterpreter test for free-threaded Python 3.14 (#5940)
* Remove skip for Move Subinterpreter test on free-threaded Python 3.14+ * Fix deadlock by detaching from the main interpreter before joining the thread. * style: pre-commit fixes --------- Co-authored-by: b-pass <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 7ae61bf commit 799f591

File tree

2 files changed

+13
-9
lines changed

2 files changed

+13
-9
lines changed

docs/advanced/embedding.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,4 +492,8 @@ Best Practices for sub-interpreter safety
492492
So you must still consider the thread safety of your C++ code. Remember, in Python 3.12
493493
sub-interpreters must be destroyed on the same thread that they were created on.
494494

495+
- When using sub-interpreters in free-threaded python builds, note that creating and destroying
496+
sub-interpreters may initiate a "stop-the-world". Be sure to detach long-running C++ threads
497+
from Python thread state (similar to releasing the GIL) to avoid deadlocks.
498+
495499
- Familiarize yourself with :ref:`misc_concurrency`.

tests/test_with_catch/test_subinterpreter.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,6 @@ TEST_CASE("Single Subinterpreter") {
9494

9595
# if PY_VERSION_HEX >= 0x030D0000
9696
TEST_CASE("Move Subinterpreter") {
97-
// Test is skipped on free-threaded Python 3.14+ due to a hang in Py_EndInterpreter()
98-
// when the subinterpreter is destroyed from a different thread than it was created on.
99-
// See: https://github.com/pybind/pybind11/pull/5940
100-
# if PY_VERSION_HEX >= 0x030E0000 && defined(Py_GIL_DISABLED)
101-
PYBIND11_CATCH2_SKIP_IF(true, "Skipped on free-threaded Python 3.14+ (see PR #5940)");
102-
# endif
103-
10497
std::unique_ptr<py::subinterpreter> sub(new py::subinterpreter(py::subinterpreter::create()));
10598

10699
// on this thread, use the subinterpreter and import some non-trivial junk
@@ -113,14 +106,21 @@ TEST_CASE("Move Subinterpreter") {
113106
py::module_::import("external_module");
114107
}
115108

116-
std::thread([&]() {
109+
auto t = std::thread([&]() {
117110
// Use it again
118111
{
119112
py::subinterpreter_scoped_activate activate(*sub);
120113
py::module_::import("external_module");
121114
}
122115
sub.reset();
123-
}).join();
116+
});
117+
118+
// on 3.14.1+ destructing a sub-interpreter does a stop-the-world. we need to detach our
119+
// thread state in order for that to be possible.
120+
{
121+
py::gil_scoped_release nogil;
122+
t.join();
123+
}
124124

125125
REQUIRE(!sub);
126126

0 commit comments

Comments
 (0)