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.
This commit is contained in:
Wenzel Jakob 2016-11-24 18:52:31 +01:00
parent e72d958a5d
commit 099d6e9c48

View File

@ -482,7 +482,16 @@ public:
: std::runtime_error(e.what()), type(e.type), value(e.value), : std::runtime_error(e.what()), type(e.type), value(e.value),
trace(e.trace) { e.type = e.value = e.trace = nullptr; } trace(e.trace) { e.type = e.value = e.trace = nullptr; }
~error_already_set() { Py_XDECREF(type); Py_XDECREF(value); Py_XDECREF(trace); } ~error_already_set() {
if (value) {
PyGILState_STATE state = PyGILState_Ensure();
PyErr_Restore(type, value, trace);
PyErr_Clear();
PyGILState_Release(state);
}
}
error_already_set& operator=(const error_already_set &) = delete;
/// Give the error back to Python /// Give the error back to Python
void restore() { PyErr_Restore(type, value, trace); type = value = trace = nullptr; } void restore() { PyErr_Restore(type, value, trace); type = value = trace = nullptr; }