Skip to content

Commit 099d6e9

Browse files
committed
Improved implementation of error_already_set::~error_already_set()
C++ exceptions are destructed in the context of the code that catches them. At this point, the Python GIL may not be held, which could lead to crashes with the previous implementation. PyErr_Fetch and PyErr_Restore should always occur in pairs, which was not the case for the previous implementation. To clear the exception, the new approach uses PyErr_Restore && PyErr_Clear instead of simply decreasing the reference counts of the exception objects.
1 parent e72d958 commit 099d6e9

File tree

1 file changed

+10
-1
lines changed

1 file changed

+10
-1
lines changed

include/pybind11/common.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,16 @@ class error_already_set : public std::runtime_error {
482482
: std::runtime_error(e.what()), type(e.type), value(e.value),
483483
trace(e.trace) { e.type = e.value = e.trace = nullptr; }
484484

485-
~error_already_set() { Py_XDECREF(type); Py_XDECREF(value); Py_XDECREF(trace); }
485+
~error_already_set() {
486+
if (value) {
487+
PyGILState_STATE state = PyGILState_Ensure();
488+
PyErr_Restore(type, value, trace);
489+
PyErr_Clear();
490+
PyGILState_Release(state);
491+
}
492+
}
493+
494+
error_already_set& operator=(const error_already_set &) = delete;
486495

487496
/// Give the error back to Python
488497
void restore() { PyErr_Restore(type, value, trace); type = value = trace = nullptr; }

0 commit comments

Comments
 (0)