Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2024-07-09 13:08:05 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2024-08-21 22:41:13 +0200
commit33bd61d13d8d9e3794b6049891be62f3351313d9 (patch)
tree73204bed688aa7c3268d5173dbe567cf78be8b3f /sources/pyside6/libpyside/globalreceiverv2.cpp
parent05c4e6372e9515f81534ff1d0816695d4e6a9b27 (diff)
libpyside: Reimplement signal connections for Python callables not targeting a QMetaMethod
The code previously used a instances of class GlobalReceiverV2 inheriting QObject in a hash in SignalManager per slot tracking the list of senders to be able to use standard signal/slot connections in Qt. This was a complicated data structure and had issues with cleanups. This has been replaced by using an invoker object based on QtPrivate::QSlotObjectBase which can be passed to QObjectPrivate::connect(const QObject *, int signal, QtPrivate::QSlotObjectBase *, ...). The connections (identified by ConnectionKey) are now stored in a hash with QMetaObject::Connection as value, which can be used to disconnect using QObject::disconnect(QMetaObject::Connection). Deletion tracking is done by using signal QObject::destroyed(QObject*) which requires adapting some tests checking on the connection count and weak ref notification on receivers as was the case before. [ChangeLog][PySide6] Signal connections for Python callables not targeting a QMetaMethod has be reimplemented to simplify code and prepare for removal of the GIL. Task-number: PYSIDE-2810 Task-number: PYSIDE-2221 Change-Id: Ib55e73d4d7bfe6d7a8b7adc3ce3734eac5789bea Reviewed-by: Shyamnath Premnadh <Shyamnath.Premnadh@qt.io> Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Diffstat (limited to 'sources/pyside6/libpyside/globalreceiverv2.cpp')
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.cpp180
1 files changed, 0 insertions, 180 deletions
diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp
deleted file mode 100644
index 41ed35263..000000000
--- a/sources/pyside6/libpyside/globalreceiverv2.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "globalreceiverv2.h"
-#include "dynamicslot_p.h"
-#include "pysideweakref.h"
-#include "pysidestaticstrings.h"
-#include "pysideutils.h"
-#include "signalmanager.h"
-
-#include <autodecref.h>
-#include <gilstate.h>
-#include <pep384ext.h>
-
-#include <QtCore/QMetaMethod>
-#include <QtCore/QSet>
-#include <QtCore/QDebug>
-
-#include <cstring>
-#include <utility>
-
-namespace PySide
-{
-
-GlobalReceiverKey GlobalReceiverV2::key(PyObject *callback)
-{
- Shiboken::GilState gil;
- switch (DynamicSlot::slotType(callback)) {
- case DynamicSlot::SlotType::Method:
- // PYSIDE-1422: Avoid hash on self which might be unhashable.
- return {PyMethod_GET_SELF(callback), PyMethod_GET_FUNCTION(callback)};
- case DynamicSlot::SlotType::CompiledMethod: {
- // PYSIDE-1589: Fix for slots in compiled functions
- Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self()));
- Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func()));
- return {self, func};
- }
- case DynamicSlot::SlotType::Callable:
- break;
- }
- return {nullptr, callback};
-}
-
-const char *GlobalReceiverV2::senderDynamicProperty = "_q_pyside_sender";
-
-GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, QObject *receiver) :
- QObject(nullptr),
- m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject),
- m_data(DynamicSlot::create(callback, this)),
- m_receiver(receiver)
-{
-}
-
-GlobalReceiverV2::~GlobalReceiverV2()
-{
- m_refs.clear();
- // Remove itself from map.
- // Suppress handling of destroyed() for objects whose last reference is contained inside
- // the callback object that will now be deleted. The reference could be a default argument,
- // a callback local variable, etc.
- // The signal has to be suppressed because it would lead to the following situation:
- // Callback is deleted, hence the last reference is decremented,
- // leading to the object being deleted, which emits destroyed(), which would try to invoke
- // the already deleted callback, and also try to delete the object again.
- delete std::exchange(m_data, nullptr);
-}
-
-int GlobalReceiverV2::addSlot(const QByteArray &signature)
-{
- auto it = m_signatures.find(signature);
- if (it == m_signatures.end()) {
- const int index = metaObjectBuilder().addSlot(signature);
- it = m_signatures.insert(signature, index);
- }
- return it.value();
-}
-
-void GlobalReceiverV2::incRef(const QObject *link)
-{
- Q_ASSERT(link);
- m_refs.append(link);
-}
-
-void GlobalReceiverV2::decRef(const QObject *link)
-{
- Q_ASSERT(link);
- m_refs.removeOne(link);
-}
-
-void GlobalReceiverV2::notify()
-{
- purgeDeletedSenders();
-}
-
-static bool isNull(const QPointer<const QObject> &p)
-{
- return p.isNull();
-}
-
-void GlobalReceiverV2::purgeDeletedSenders()
-{
- m_refs.erase(std::remove_if(m_refs.begin(), m_refs.end(), isNull), m_refs.end());
-}
-
-bool GlobalReceiverV2::isEmpty() const
-{
- return std::all_of(m_refs.cbegin(), m_refs.cend(), isNull);
-}
-
-const QMetaObject *GlobalReceiverV2::metaObject() const
-{
- return const_cast<GlobalReceiverV2 *>(this)->m_metaObject.update();
-}
-
-int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args)
-{
- Shiboken::GilState gil;
- Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
- Q_ASSERT(id >= QObject::staticMetaObject.methodCount());
-
- QMetaMethod slot = metaObject()->method(id);
- Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
-
- if (!m_data) {
- const QByteArray message = "PySide6 Warning: Skipping callback call "
- + slot.methodSignature() + " because the callback object is being destructed.";
- PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0);
- return -1;
- }
-
- const bool setSenderDynamicProperty = !m_receiver.isNull();
- if (setSenderDynamicProperty)
- m_receiver->setProperty(senderDynamicProperty, QVariant::fromValue(sender()));
-
- m_data->call(slot.parameterTypes(), slot.typeName(), args);
-
- if (setSenderDynamicProperty)
- m_receiver->setProperty(senderDynamicProperty, QVariant{});
-
- // SignalManager::callPythonMetaMethod might have failed, in that case we have to print the
- // error so it considered "handled".
- if (PyErr_Occurred()) {
- int reclimit = Py_GetRecursionLimit();
- // Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function.
- // Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion
- // error again. Don't do it when the limit is already insanely high, to avoid overflow.
- if (reclimit < (1 << 30))
- Py_SetRecursionLimit(reclimit + 5);
- PyErr_Print();
- Py_SetRecursionLimit(reclimit);
- }
-
- return -1;
-}
-
-void GlobalReceiverV2::formatDebug(QDebug &debug) const
-{
- debug << "receiver=" << m_receiver
- << ", signatures=" << m_signatures.keys() << ", slot=" << m_data;
- if (isEmpty())
- debug << ", empty";
- else
- debug << ", refs=" << m_refs;
-};
-
-QDebug operator<<(QDebug debug, const GlobalReceiverV2 *g)
-{
- QDebugStateSaver saver(debug);
- debug.noquote();
- debug.nospace();
- debug << "GlobalReceiverV2(";
- if (g)
- g->formatDebug(debug);
- else
- debug << '0';
- debug << ')';
- return debug;
-}
-
-} // namespace PySide