From 9e6a5e692ee4fbbeb548d0fa6b85e67778a950bf Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 19 Oct 2018 15:53:36 +0100 Subject: [PATCH] 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. --- .../Code/pyrenderdoc/function_conversion.h | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/qrenderdoc/Code/pyrenderdoc/function_conversion.h b/qrenderdoc/Code/pyrenderdoc/function_conversion.h index 474ec4ef9..0c91a1a0f 100644 --- a/qrenderdoc/Code/pyrenderdoc/function_conversion.h +++ b/qrenderdoc/Code/pyrenderdoc/function_conversion.h @@ -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 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 f(funcname, param...); - return f.call(funcname, func, global_internal_handle, exHandle); + return f.call(funcname, funcptr.obj, global_internal_handle, exHandle); }; }