Fix C++ invokes being responsible for destroying python callbacks

* If the last refcount on a python lambda/temp function is released when a
  wrapping std::function is destroyed in a C++ invoke, we can't destroy it
  safely. Instead we queue up that decref and process it the next chance we're
  able (which is either when the current execution finishes for a python shell
  execution, or on the next function call which handles extensions).
This commit is contained in:
baldurk
2020-12-03 14:55:02 +00:00
parent 0d1f6e3940
commit 6bc4e007a0
3 changed files with 50 additions and 11 deletions
@@ -137,6 +137,10 @@ QMap<rdcstr, PyObject *> PythonContext::extensions;
static PyObject *current_global_handle = NULL;
static QMutex decrefQueueMutex;
static QList<PyObject *> decrefQueue;
extern "C" void ProcessDecRefQueue();
void FetchException(QString &typeStr, QString &valueStr, int &finalLine, QList<QString> &frames)
{
PyObject *exObj = NULL, *valueObj = NULL, *tracebackObj = NULL;
@@ -845,6 +849,8 @@ void PythonContext::executeString(const QString &filename, const QString &source
PyEval_SetTrace(NULL, NULL);
ProcessDecRefQueue();
Py_XDECREF(thisobj);
Py_XDECREF(traceContext);
}
@@ -1327,6 +1333,26 @@ extern "C" void SetThreadBlocking(PyObject *global_handle, bool block)
redirector->block = block;
}
extern "C" void QueueDecRef(PyObject *obj)
{
QMutexLocker lock(&decrefQueueMutex);
decrefQueue.push_back(obj);
}
extern "C" void ProcessDecRefQueue()
{
QMutexLocker lock(&decrefQueueMutex);
if(decrefQueue.isEmpty())
return;
for(PyObject *obj : decrefQueue)
Py_XDECREF(obj);
decrefQueue.clear();
}
extern "C" QWidget *QWidgetFromPy(PyObject *widget)
{
return PythonContext::QWidgetFromPy(widget);