Skip to content

Commit 8031359

Browse files
committed
Add a note about exceptions.
They contain Python object references and acquire the GIL, that means they are a danger with subinterpreters!
1 parent 7fbca52 commit 8031359

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

docs/advanced/embedding.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,12 @@ Best Practices for sub-interpreter safety
436436

437437
- Never share Python objects across different interpreters.
438438

439+
- :class:`error_already_set` objects contain a reference to the Python exception type,
440+
and :func:`error_already_set::what()` acquires the GIL. So Python exceptions must
441+
**never** be allowed to propoagate past the enclosing
442+
:class:`subinterpreter_scoped_activate` instance!
443+
(So your try/catch should be *just inside* the scope covered by the :class:`subinterpreter_scoped_activate`.)
444+
439445
- Avoid global/static state whenever possible. Instead, keep state within each interpreter,
440446
such as within the interpreter state dict, which can be accessed via
441447
``subinterpreter::current().state_dict()``, or within instance members and tied to

include/pybind11/subinterpreter.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,10 @@ class subinterpreter {
7878
/// interpreter and its GIL are not required to be held prior to calling this function.
7979
static inline subinterpreter create(PyInterpreterConfig const &cfg) {
8080
error_scope err_scope;
81-
subinterpreter_scoped_activate main_guard(main());
8281
subinterpreter result;
8382
{
8483
// we must hold the main GIL in order to create a subinterpreter
85-
gil_scoped_acquire gil;
84+
subinterpreter_scoped_activate main_guard(main());
8685

8786
auto prev_tstate = PyThreadState_Get();
8887

@@ -264,6 +263,28 @@ inline subinterpreter_scoped_activate::~subinterpreter_scoped_activate() {
264263
// We were on this interpreter already, so just make sure the GIL goes back as it was
265264
PyGILState_Release(gil_state_);
266265
} else {
266+
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
267+
bool has_active_exception;
268+
# if defined(__cpp_lib_uncaught_exceptions)
269+
has_active_exception = std::uncaught_exceptions() > 0;
270+
# else
271+
// removed in C++20, replaced with uncaught_exceptions
272+
has_active_exception = std::uncaught_exception();
273+
# endif
274+
if (has_active_exception) {
275+
try {
276+
std::rethrow_exception(std::current_exception());
277+
} catch (error_already_set &e) {
278+
// Because error_already_set holds python objects and what() acquires the GIL, it
279+
// is basically never OK to let these exceptions propagate outside the current
280+
// active interpreter.
281+
pybind11_fail("~subinterpreter_scoped_activate: cannot propagate Python "
282+
"exceptions outside of their owning interpreter");
283+
} catch (...) {
284+
}
285+
}
286+
#endif
287+
267288
if (tstate_) {
268289
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
269290
if (detail::get_thread_state_unchecked() != tstate_) {

0 commit comments

Comments
 (0)