Skip to content

Commit 60f02f5

Browse files
fix: improve the error reporting for inc_ref GIL failures (#4427)
* First * Fixs * Improve * Additional assertions comment * Improve docs
1 parent 70af987 commit 60f02f5

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

docs/advanced/misc.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,34 @@ The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy
118118
m.def("call_go", &call_go, py::call_guard<py::gil_scoped_release>());
119119
120120
121+
Common Sources Of Global Interpreter Lock Errors
122+
==================================================================
123+
124+
Failing to properly hold the Global Interpreter Lock (GIL) is one of the
125+
more common sources of bugs within code that uses pybind11. If you are
126+
running into GIL related errors, we highly recommend you consult the
127+
following checklist.
128+
129+
- Do you have any global variables that are pybind11 objects or invoke
130+
pybind11 functions in either their constructor or destructor? You are generally
131+
not allowed to invoke any Python function in a global static context. We recommend
132+
using lazy initialization and then intentionally leaking at the end of the program.
133+
134+
- Do you have any pybind11 objects that are members of other C++ structures? One
135+
commonly overlooked requirement is that pybind11 objects have to increase their reference count
136+
whenever their copy constructor is called. Thus, you need to be holding the GIL to invoke
137+
the copy constructor of any C++ class that has a pybind11 member. This can sometimes be very
138+
tricky to track for complicated programs Think carefully when you make a pybind11 object
139+
a member in another struct.
140+
141+
- C++ destructors that invoke Python functions can be particularly troublesome as
142+
destructors can sometimes get invoked in weird and unexpected circumstances as a result
143+
of exceptions.
144+
145+
- You should try running your code in a debug build. That will enable additional assertions
146+
within pybind11 that will throw exceptions on certain GIL handling errors
147+
(reference counting operations).
148+
121149
Binding sequence data types, iterators, the slicing protocol, etc.
122150
==================================================================
123151

include/pybind11/pytypes.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,9 @@ class handle : public detail::object_api<handle> {
250250
#ifdef PYBIND11_HANDLE_REF_DEBUG
251251
inc_ref_counter(1);
252252
#endif
253-
#if defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
253+
#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
254254
if (m_ptr != nullptr && !PyGILState_Check()) {
255-
throw std::runtime_error("pybind11::handle::inc_ref() PyGILState_Check() failure.");
255+
throw_gilstate_error("pybind11::handle::inc_ref()");
256256
}
257257
#endif
258258
Py_XINCREF(m_ptr);
@@ -265,9 +265,9 @@ class handle : public detail::object_api<handle> {
265265
this function automatically. Returns a reference to itself.
266266
\endrst */
267267
const handle &dec_ref() const & {
268-
#if defined(PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF)
268+
#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
269269
if (m_ptr != nullptr && !PyGILState_Check()) {
270-
throw std::runtime_error("pybind11::handle::dec_ref() PyGILState_Check() failure.");
270+
throw_gilstate_error("pybind11::handle::dec_ref()");
271271
}
272272
#endif
273273
Py_XDECREF(m_ptr);
@@ -296,8 +296,28 @@ class handle : public detail::object_api<handle> {
296296
protected:
297297
PyObject *m_ptr = nullptr;
298298

299-
#ifdef PYBIND11_HANDLE_REF_DEBUG
300299
private:
300+
#ifdef PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF
301+
void throw_gilstate_error(const std::string &function_name) const {
302+
fprintf(
303+
stderr,
304+
"%s is being called while the GIL is either not held or invalid. Please see "
305+
"https://pybind11.readthedocs.io/en/stable/advanced/"
306+
"misc.html#common-sources-of-global-interpreter-lock-errors for debugging advice.\n",
307+
function_name.c_str());
308+
fflush(stderr);
309+
if (Py_TYPE(m_ptr)->tp_name != nullptr) {
310+
fprintf(stderr,
311+
"The failing %s call was triggered on a %s object.\n",
312+
function_name.c_str(),
313+
Py_TYPE(m_ptr)->tp_name);
314+
fflush(stderr);
315+
}
316+
throw std::runtime_error(function_name + " PyGILState_Check() failure.");
317+
}
318+
#endif
319+
320+
#ifdef PYBIND11_HANDLE_REF_DEBUG
301321
static std::size_t inc_ref_counter(std::size_t add) {
302322
thread_local std::size_t counter = 0;
303323
counter += add;

0 commit comments

Comments
 (0)