Keep a reference to python function object in wrapped lambda

* This will rarely be relevant, but it ensures if the function is decref'd and
  the lambda is still alive, that we keep the python object alive until we are
  done with it. The primary case for this is persistent callbacks where the
  module is then reloaded.
This commit is contained in:
baldurk
2018-10-19 15:53:36 +01:00
parent 52c0a806c3
commit 9e6a5e692e
@@ -117,6 +117,31 @@ inline void get_return(const char *funcname, PyObject *result, PyObject *global_
Py_XDECREF(result);
}
struct PyObjectRefCounter
{
PyObjectRefCounter(PyObject *o) : obj(o) { Py_INCREF(obj); }
PyObjectRefCounter(const PyObjectRefCounter &o)
{
obj = o.obj;
Py_INCREF(obj);
}
~PyObjectRefCounter()
{
// in non-release, check that we're currently executing if we're about to delete the object.
#if !defined(RELEASE)
if(obj->ob_refcnt == 1 && PyGILState_Check() == 0)
{
RENDERDOC_LogMessage(LogType::Error, "QTRD", __FILE__, __LINE__,
"Deleting PyObjectRefCounter without python executing on this thread");
// return and leak the object rather than crashing
return;
}
#endif
Py_DECREF(obj);
}
PyObject *obj;
};
template <typename rettype, typename... paramTypes>
struct varfunc
{
@@ -224,10 +249,13 @@ funcType ConvertFunc(const char *funcname, PyObject *func, ExceptionHandling &ex
}
}
return [global_internal_handle, funcname, func, &exHandle](auto... param) {
// create a copy that will keep the function object alive as long as the lambda is
PyObjectRefCounter funcptr(func);
return [global_internal_handle, funcname, funcptr, &exHandle](auto... param) {
ScopedFuncCall gil(global_internal_handle);
varfunc<typename funcType::result_type, decltype(param)...> f(funcname, param...);
return f.call(funcname, func, global_internal_handle, exHandle);
return f.call(funcname, funcptr.obj, global_internal_handle, exHandle);
};
}