aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Tismer <tismer@stackless.com>2023-01-25 19:06:12 +0100
committerChristian Tismer <tismer@stackless.com>2023-02-02 09:29:00 +0100
commitdb40e3e07932576bc54cd922eecd423c0f675613 (patch)
tree28e9dec9e104dcefc4052fb67970b5ce7ca925d7
parentf799f1dd678cd17b59436a0c3a734135841eaa10 (diff)
signals: Introduce Weak Reference to protect lost SignalInstance
This first solution detects the vanishing object and raises an exception. Trying to revive the object was unsuccessful. Since this happens only when no signals are connected, it is ok to leave it this way. Change-Id: Ib47f85657363b675cec3616b5550ce41d93fb4d3 Fixes: PYSIDE-2201 Pick-to: 6.4 Task-number: PYSIDE-79 Task-number: PYSIDE-68 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
-rw-r--r--sources/pyside6/libpyside/pysidesignal.cpp21
-rw-r--r--sources/pyside6/libpyside/pysidesignal.h1
-rw-r--r--sources/pyside6/tests/QtCore/destroysignal_test.py27
3 files changed, 48 insertions, 1 deletions
diff --git a/sources/pyside6/libpyside/pysidesignal.cpp b/sources/pyside6/libpyside/pysidesignal.cpp
index 5df42261c..cdc3042c0 100644
--- a/sources/pyside6/libpyside/pysidesignal.cpp
+++ b/sources/pyside6/libpyside/pysidesignal.cpp
@@ -6,6 +6,7 @@
#include "pysidesignal_p.h"
#include "pysideutils.h"
#include "pysidestaticstrings.h"
+#include "pysideweakref.h"
#include "signalmanager.h"
#include <shiboken.h>
@@ -342,6 +343,7 @@ static void signalInstanceFree(void *vself)
}
delete dataPvt;
self->d = nullptr;
+ self->deleted = true;
Py_TYPE(pySelf)->tp_base->tp_free(self);
}
@@ -550,6 +552,13 @@ static PyObject *signalInstanceEmit(PyObject *self, PyObject *args)
{
PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self);
+ // PYSIDE-2201: Check if the object has vanished meanwhile.
+ // Tried to revive it without exception, but this gives problems.
+ if (source->deleted) {
+ PyErr_Format(PyExc_RuntimeError, "The SignalInstance object was already deleted");
+ return nullptr;
+ }
+
Shiboken::AutoDecRef pyArgs(PyList_New(0));
int numArgsGiven = PySequence_Fast_GET_SIZE(args);
int numArgsInSignature = argCountInSignature(source->d->signature);
@@ -932,9 +941,16 @@ static void appendSignature(PySideSignal *self, const SignalSignature &signature
self->data->signatures.append({signature.m_parameterTypes, signature.m_attributes});
}
+static void sourceGone(void *data)
+{
+ auto *self = reinterpret_cast<PySideSignalInstance *>(data);
+ self->deleted = true;
+}
+
static void instanceInitialize(PySideSignalInstance *self, PyObject *name, PySideSignal *signal, PyObject *source, int index)
{
self->d = new PySideSignalInstancePrivate;
+ self->deleted = false;
PySideSignalInstancePrivate *selfPvt = self->d;
selfPvt->next = nullptr;
if (signal->data->signalName.isEmpty())
@@ -950,6 +966,10 @@ static void instanceInitialize(PySideSignalInstance *self, PyObject *name, PySid
selfPvt->homonymousMethod = signal->homonymousMethod;
Py_INCREF(selfPvt->homonymousMethod);
}
+ // PYSIDE-2201: We have no reference to source. Let's take a weakref to get
+ // notified when source gets deleted.
+ PySide::WeakRef::create(source, sourceGone, self);
+
index++;
if (index < signal->data->signatures.size()) {
@@ -1009,6 +1029,7 @@ PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMet
previous->d->next = item;
item->d = new PySideSignalInstancePrivate;
+ item->deleted = false;
PySideSignalInstancePrivate *selfPvt = item->d;
selfPvt->source = source;
Py_INCREF(selfPvt->source); // PYSIDE-79: an INCREF is missing.
diff --git a/sources/pyside6/libpyside/pysidesignal.h b/sources/pyside6/libpyside/pysidesignal.h
index c38371d81..3b82e6c58 100644
--- a/sources/pyside6/libpyside/pysidesignal.h
+++ b/sources/pyside6/libpyside/pysidesignal.h
@@ -30,6 +30,7 @@ extern "C"
{
PyObject_HEAD
PySideSignalInstancePrivate *d;
+ bool deleted;
};
}; // extern "C"
diff --git a/sources/pyside6/tests/QtCore/destroysignal_test.py b/sources/pyside6/tests/QtCore/destroysignal_test.py
index 34aaded9e..f1d7cfec1 100644
--- a/sources/pyside6/tests/QtCore/destroysignal_test.py
+++ b/sources/pyside6/tests/QtCore/destroysignal_test.py
@@ -11,7 +11,7 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
from init_paths import init_test_paths
init_test_paths(False)
-from PySide6.QtCore import QTimer, QObject
+from PySide6.QtCore import QTimer, QObject, Signal
class TestDestroySignal(unittest.TestCase):
@@ -43,6 +43,31 @@ class TestDestroySignal(unittest.TestCase):
self.assertTrue(self._destroyed)
+class Foo(QObject):
+ s = Signal(int)
+
+ def __init__(self):
+ QObject.__init__(self)
+ sys.stderr.write(f"__init__ {id(self):x}\n")
+
+ def __del__(self):
+ sys.stderr.write(f"__del__ {id(self):x}\n")
+
+ def send(self, i):
+ self.s.emit(i)
+
+
+# PYSIDE-2201: This crashed until we introduced a weak reference.
+class TestDestroyNoConnect(unittest.TestCase):
+
+ def testSignalDestroyedMissingReference(self):
+ # This works since it has one reference more to Foo
+ Foo().send(43)
+ # This crashed because we have no reference in the signal.
+ with self.assertRaises(RuntimeError):
+ Foo().s.emit(44)
+
+
if __name__ == '__main__':
unittest.main()