diff options
Diffstat (limited to 'sources/pyside6/libpyside/globalreceiverv2.cpp')
-rw-r--r-- | sources/pyside6/libpyside/globalreceiverv2.cpp | 199 |
1 files changed, 95 insertions, 104 deletions
diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp index c843a40ac..51070b4ad 100644 --- a/sources/pyside6/libpyside/globalreceiverv2.cpp +++ b/sources/pyside6/libpyside/globalreceiverv2.cpp @@ -9,35 +9,23 @@ #include <autodecref.h> #include <gilstate.h> +#include <pep384ext.h> -#include <QtCore/qhashfunctions.h> #include <QtCore/QMetaMethod> #include <QtCore/QSet> +#include <QtCore/QDebug> #include <cstring> #define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)" -namespace -{ - static int DESTROY_SIGNAL_ID = 0; - static int DESTROY_SLOT_ID = 0; -} namespace PySide { -size_t qHash(const GlobalReceiverKey &k, size_t seed) -{ - QtPrivate::QHashCombine hash; - seed = hash(seed, k.object); - seed = hash(seed, k.method); - return seed; -} - class DynamicSlotDataV2 { - Q_DISABLE_COPY(DynamicSlotDataV2) + Q_DISABLE_COPY_MOVE(DynamicSlotDataV2) public: DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *parent); ~DynamicSlotDataV2(); @@ -51,6 +39,8 @@ class DynamicSlotDataV2 static void onCallbackDestroyed(void *data); static GlobalReceiverKey key(PyObject *callback); + void formatDebug(QDebug &debug) const; + private: bool m_isMethod; PyObject *m_callback; @@ -61,6 +51,32 @@ class DynamicSlotDataV2 GlobalReceiverV2 *m_parent; }; +void DynamicSlotDataV2::formatDebug(QDebug &debug) const +{ + debug << "method=" << m_isMethod << ", m_callback=" << m_callback; + if (m_callback != nullptr) + debug << '/' << Py_TYPE(m_callback)->tp_name; + debug << ", self=" << m_pythonSelf; + if (m_pythonSelf != nullptr) + debug << '/' << Py_TYPE(m_pythonSelf)->tp_name; + debug << ", m_pyClass=" << m_pyClass; + if (m_pyClass != nullptr) + debug << '/' << Py_TYPE(m_pyClass)->tp_name; + debug << ", signatures=" << m_signatures.keys(); +} + +QDebug operator<<(QDebug debug, const DynamicSlotDataV2 *d) +{ + QDebugStateSaver saver(debug); + debug.noquote(); + debug.nospace(); + debug << "DynamicSlotDataV2("; + if (d) + d->formatDebug(debug); + else + debug << '0'; + debug << ')'; + return debug; } using namespace PySide; @@ -83,10 +99,10 @@ DynamicSlotDataV2::DynamicSlotDataV2(PyObject *callback, GlobalReceiverV2 *paren // PYSIDE-1523: PyMethod_Check is not accepting compiled form, we just go by attributes. m_isMethod = true; - m_callback = PyObject_GetAttr(callback, PySide::PyName::im_func()); + m_callback = PyObject_GetAttr(callback, PySide::PySideName::im_func()); Py_DECREF(m_callback); - m_pythonSelf = PyObject_GetAttr(callback, PySide::PyName::im_self()); + m_pythonSelf = PyObject_GetAttr(callback, PySide::PySideName::im_self()); Py_DECREF(m_pythonSelf); //monitor class from method lifetime @@ -107,8 +123,8 @@ GlobalReceiverKey DynamicSlotDataV2::key(PyObject *callback) return {PyMethod_GET_SELF(callback), PyMethod_GET_FUNCTION(callback)}; } else if (PySide::isCompiledMethod(callback)) { // PYSIDE-1589: Fix for slots in compiled functions - Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PyName::im_self())); - Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PyName::im_func())); + Shiboken::AutoDecRef self(PyObject_GetAttr(callback, PySide::PySideName::im_self())); + Shiboken::AutoDecRef func(PyObject_GetAttr(callback, PySide::PySideName::im_func())); return {self, func}; } return {nullptr, callback}; @@ -120,7 +136,7 @@ PyObject *DynamicSlotDataV2::callback() //create a callback based on method data if (m_isMethod) - callback = Py_TYPE(m_callback)->tp_descr_get(m_callback, m_pythonSelf, nullptr); + callback = PepExt_Type_CallDescrGet(m_callback, m_pythonSelf, nullptr); else Py_INCREF(callback); @@ -146,7 +162,7 @@ void DynamicSlotDataV2::onCallbackDestroyed(void *data) auto self = reinterpret_cast<DynamicSlotDataV2 *>(data); self->m_weakRef = nullptr; Py_BEGIN_ALLOW_THREADS - delete self->m_parent; + SignalManager::instance().deleteGlobalReceiver(self->m_parent); Py_END_ALLOW_THREADS } @@ -160,31 +176,20 @@ DynamicSlotDataV2::~DynamicSlotDataV2() Py_DECREF(m_callback); } -GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, GlobalReceiverV2MapPtr map) : +const char *GlobalReceiverV2::senderDynamicProperty = "_q_pyside_sender"; + +GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, QObject *receiver) : QObject(nullptr), m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject), - m_sharedMap(std::move(map)) + m_receiver(receiver) { m_data = new DynamicSlotDataV2(callback, this); - m_metaObject.addSlot(RECEIVER_DESTROYED_SLOT_NAME); - m_metaObject.update(); - m_refs.append(nullptr); - - - if (DESTROY_SIGNAL_ID == 0) - DESTROY_SIGNAL_ID = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); - - if (DESTROY_SLOT_ID == 0) - DESTROY_SLOT_ID = m_metaObject.indexOfMethod(QMetaMethod::Slot, RECEIVER_DESTROYED_SLOT_NAME); - - } GlobalReceiverV2::~GlobalReceiverV2() { m_refs.clear(); // Remove itself from map. - m_sharedMap->remove(m_data->key()); // 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. @@ -193,7 +198,7 @@ GlobalReceiverV2::~GlobalReceiverV2() // 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. DynamicSlotDataV2 *data = m_data; - m_data = Q_NULLPTR; + m_data = nullptr; delete data; } @@ -204,69 +209,34 @@ int GlobalReceiverV2::addSlot(const char *signature) void GlobalReceiverV2::incRef(const QObject *link) { - if (link) { - if (!m_refs.contains(link)) { - bool connected{}; - Py_BEGIN_ALLOW_THREADS - connected = QMetaObject::connect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); - Py_END_ALLOW_THREADS - if (connected) - m_refs.append(link); - else - Q_ASSERT(false); - } else { - m_refs.append(link); - } - } else { - m_refs.append(nullptr); - } + Q_ASSERT(link); + m_refs.append(link); } void GlobalReceiverV2::decRef(const QObject *link) { - if (m_refs.isEmpty()) - return; - - + Q_ASSERT(link); m_refs.removeOne(link); - if (link) { - if (!m_refs.contains(link)) { - bool result{}; - Py_BEGIN_ALLOW_THREADS - result = QMetaObject::disconnect(link, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); - Py_END_ALLOW_THREADS - Q_ASSERT(result); - if (!result) - return; - } - } - - if (m_refs.isEmpty()) - Py_BEGIN_ALLOW_THREADS - delete this; - Py_END_ALLOW_THREADS +} +void GlobalReceiverV2::notify() +{ + purgeDeletedSenders(); } -int GlobalReceiverV2::refCount(const QObject *link) const +static bool isNull(const QPointer<const QObject> &p) { - if (link) - return m_refs.count(link); + return p.isNull(); +} - return m_refs.size(); +void GlobalReceiverV2::purgeDeletedSenders() +{ + m_refs.erase(std::remove_if(m_refs.begin(), m_refs.end(), isNull), m_refs.end()); } -void GlobalReceiverV2::notify() +bool GlobalReceiverV2::isEmpty() const { - const QSet<const QObject *> objSet(m_refs.cbegin(), m_refs.cend()); - Py_BEGIN_ALLOW_THREADS - for (const QObject *o : objSet) { - if (o) { - QMetaObject::disconnect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); - QMetaObject::connect(o, DESTROY_SIGNAL_ID, this, DESTROY_SLOT_ID); - } - } - Py_END_ALLOW_THREADS + return std::all_of(m_refs.cbegin(), m_refs.cend(), isNull); } GlobalReceiverKey GlobalReceiverV2::key() const @@ -294,26 +264,22 @@ int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args) Q_ASSERT(slot.methodType() == QMetaMethod::Slot); if (!m_data) { - if (id != DESTROY_SLOT_ID) { - const QByteArray message = "PySide6 Warning: Skipping callback call " - + slot.methodSignature() + " because the callback object is being destructed."; - PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0); - } + 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; } - if (id == DESTROY_SLOT_ID) { - if (m_refs.isEmpty()) - return -1; - auto obj = *reinterpret_cast<QObject **>(args[1]); - incRef(); //keep the object live (safe ref) - m_refs.removeAll(obj); // remove all refs to this object - decRef(); //remove the safe ref - } else { - const bool isShortCuit = std::strchr(slot.methodSignature(), '(') == nullptr; - Shiboken::AutoDecRef callback(m_data->callback()); - SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit); - } + const bool setSenderDynamicProperty = !m_receiver.isNull(); + if (setSenderDynamicProperty) + m_receiver->setProperty(senderDynamicProperty, QVariant::fromValue(sender())); + + const bool isShortCuit = std::strchr(slot.methodSignature(), '(') == nullptr; + Shiboken::AutoDecRef callback(m_data->callback()); + SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit); + + 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". @@ -330,3 +296,28 @@ int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void **args) return -1; } + +void GlobalReceiverV2::formatDebug(QDebug &debug) const +{ + debug << "receiver=" << m_receiver << ", 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 |