mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-29 08:32:02 +00:00
fix: improve the error reporting for inc_ref GIL failures (#4427)
* First * Fixs * Improve * Additional assertions comment * Improve docs
This commit is contained in:
parent
2de6e398e1
commit
e414c4bdd6
@ -118,6 +118,34 @@ The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy
|
|||||||
m.def("call_go", &call_go, py::call_guard<py::gil_scoped_release>());
|
m.def("call_go", &call_go, py::call_guard<py::gil_scoped_release>());
|
||||||
|
|
||||||
|
|
||||||
|
Common Sources Of Global Interpreter Lock Errors
|
||||||
|
==================================================================
|
||||||
|
|
||||||
|
Failing to properly hold the Global Interpreter Lock (GIL) is one of the
|
||||||
|
more common sources of bugs within code that uses pybind11. If you are
|
||||||
|
running into GIL related errors, we highly recommend you consult the
|
||||||
|
following checklist.
|
||||||
|
|
||||||
|
- Do you have any global variables that are pybind11 objects or invoke
|
||||||
|
pybind11 functions in either their constructor or destructor? You are generally
|
||||||
|
not allowed to invoke any Python function in a global static context. We recommend
|
||||||
|
using lazy initialization and then intentionally leaking at the end of the program.
|
||||||
|
|
||||||
|
- Do you have any pybind11 objects that are members of other C++ structures? One
|
||||||
|
commonly overlooked requirement is that pybind11 objects have to increase their reference count
|
||||||
|
whenever their copy constructor is called. Thus, you need to be holding the GIL to invoke
|
||||||
|
the copy constructor of any C++ class that has a pybind11 member. This can sometimes be very
|
||||||
|
tricky to track for complicated programs Think carefully when you make a pybind11 object
|
||||||
|
a member in another struct.
|
||||||
|
|
||||||
|
- C++ destructors that invoke Python functions can be particularly troublesome as
|
||||||
|
destructors can sometimes get invoked in weird and unexpected circumstances as a result
|
||||||
|
of exceptions.
|
||||||
|
|
||||||
|
- You should try running your code in a debug build. That will enable additional assertions
|
||||||
|
within pybind11 that will throw exceptions on certain GIL handling errors
|
||||||
|
(reference counting operations).
|
||||||
|
|
||||||
Binding sequence data types, iterators, the slicing protocol, etc.
|
Binding sequence data types, iterators, the slicing protocol, etc.
|
||||||
==================================================================
|
==================================================================
|
||||||
|
|
||||||
|
@ -250,9 +250,9 @@ public:
|
|||||||
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
||||||
inc_ref_counter(1);
|
inc_ref_counter(1);
|
||||||
#endif
|
#endif
|
||||||
#if defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
|
#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
|
||||||
if (m_ptr != nullptr && !PyGILState_Check()) {
|
if (m_ptr != nullptr && !PyGILState_Check()) {
|
||||||
throw std::runtime_error("pybind11::handle::inc_ref() PyGILState_Check() failure.");
|
throw_gilstate_error("pybind11::handle::inc_ref()");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Py_XINCREF(m_ptr);
|
Py_XINCREF(m_ptr);
|
||||||
@ -265,9 +265,9 @@ public:
|
|||||||
this function automatically. Returns a reference to itself.
|
this function automatically. Returns a reference to itself.
|
||||||
\endrst */
|
\endrst */
|
||||||
const handle &dec_ref() const & {
|
const handle &dec_ref() const & {
|
||||||
#if defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
|
#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
|
||||||
if (m_ptr != nullptr && !PyGILState_Check()) {
|
if (m_ptr != nullptr && !PyGILState_Check()) {
|
||||||
throw std::runtime_error("pybind11::handle::dec_ref() PyGILState_Check() failure.");
|
throw_gilstate_error("pybind11::handle::dec_ref()");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Py_XDECREF(m_ptr);
|
Py_XDECREF(m_ptr);
|
||||||
@ -296,8 +296,28 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
PyObject *m_ptr = nullptr;
|
PyObject *m_ptr = nullptr;
|
||||||
|
|
||||||
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
|
||||||
private:
|
private:
|
||||||
|
#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
|
||||||
|
void throw_gilstate_error(const std::string &function_name) const {
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"%s is being called while the GIL is either not held or invalid. Please see "
|
||||||
|
"https://pybind11.readthedocs.io/en/stable/advanced/"
|
||||||
|
"misc.html#common-sources-of-global-interpreter-lock-errors for debugging advice.\n",
|
||||||
|
function_name.c_str());
|
||||||
|
fflush(stderr);
|
||||||
|
if (Py_TYPE(m_ptr)->tp_name != nullptr) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"The failing %s call was triggered on a %s object.\n",
|
||||||
|
function_name.c_str(),
|
||||||
|
Py_TYPE(m_ptr)->tp_name);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
throw std::runtime_error(function_name + " PyGILState_Check() failure.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PYBIND11_HANDLE_REF_DEBUG
|
||||||
static std::size_t inc_ref_counter(std::size_t add) {
|
static std::size_t inc_ref_counter(std::size_t add) {
|
||||||
thread_local std::size_t counter = 0;
|
thread_local std::size_t counter = 0;
|
||||||
counter += add;
|
counter += add;
|
||||||
|
Loading…
Reference in New Issue
Block a user