diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 96f3ff805..104f32206 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -2113,12 +2113,22 @@ public: pybind11_fail("scoped_acquire::dec_ref(): internal error!"); #endif PyThreadState_Clear(tstate); - PyThreadState_DeleteCurrent(); + if (active) + PyThreadState_DeleteCurrent(); PYBIND11_TLS_DELETE_VALUE(detail::get_internals().tstate); release = false; } } + /// This method will disable the PyThreadState_DeleteCurrent call and the + /// GIL won't be acquired. This method should be used if the interpreter + /// could be shutting down when this is called, as thread deletion is not + /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and + /// protect subsequent code. + PYBIND11_NOINLINE void disarm() { + active = false; + } + PYBIND11_NOINLINE ~gil_scoped_acquire() { dec_ref(); if (release) @@ -2127,6 +2137,7 @@ public: private: PyThreadState *tstate = nullptr; bool release = true; + bool active = true; }; class gil_scoped_release { @@ -2142,10 +2153,22 @@ public: PYBIND11_TLS_DELETE_VALUE(key); } } + + /// This method will disable the PyThreadState_DeleteCurrent call and the + /// GIL won't be acquired. This method should be used if the interpreter + /// could be shutting down when this is called, as thread deletion is not + /// allowed during shutdown. Check _Py_IsFinalizing() on Python 3.7+, and + /// protect subsequent code. + PYBIND11_NOINLINE void disarm() { + active = false; + } + ~gil_scoped_release() { if (!tstate) return; - PyEval_RestoreThread(tstate); + // `PyEval_RestoreThread()` should not be called if runtime is finalizing + if (active) + PyEval_RestoreThread(tstate); if (disassoc) { auto key = detail::get_internals().tstate; PYBIND11_TLS_REPLACE_VALUE(key, tstate); @@ -2154,6 +2177,7 @@ public: private: PyThreadState *tstate; bool disassoc; + bool active = true; }; #elif defined(PYPY_VERSION) class gil_scoped_acquire { @@ -2161,6 +2185,7 @@ class gil_scoped_acquire { public: gil_scoped_acquire() { state = PyGILState_Ensure(); } ~gil_scoped_acquire() { PyGILState_Release(state); } + void disarm() {} }; class gil_scoped_release { @@ -2168,10 +2193,15 @@ class gil_scoped_release { public: gil_scoped_release() { state = PyEval_SaveThread(); } ~gil_scoped_release() { PyEval_RestoreThread(state); } + void disarm() {} }; #else -class gil_scoped_acquire { }; -class gil_scoped_release { }; +class gil_scoped_acquire { + void disarm() {} +}; +class gil_scoped_release { + void disarm() {} +}; #endif error_already_set::~error_already_set() {