aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sources/pyside6/PySide6/QtCore/typesystem_core_common.xml4
-rw-r--r--sources/pyside6/PySide6/glue/qtcore.cpp9
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.cpp113
-rw-r--r--sources/pyside6/libpyside/globalreceiverv2.h29
-rw-r--r--sources/pyside6/libpyside/signalmanager.cpp83
-rw-r--r--sources/pyside6/libpyside/signalmanager.h5
6 files changed, 74 insertions, 169 deletions
diff --git a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
index d9a6c8435..21be326df 100644
--- a/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
+++ b/sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
@@ -1773,10 +1773,6 @@
</modify-argument>
</add-function>
- <modify-function signature="receivers(const char*)const">
- <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-receivers"/>
- </modify-function>
-
<modify-function signature="destroyed(QObject*)" allow-thread="yes">
<modify-argument index="1">
<rename to="object"/>
diff --git a/sources/pyside6/PySide6/glue/qtcore.cpp b/sources/pyside6/PySide6/glue/qtcore.cpp
index 0f4816bbb..ba34a0259 100644
--- a/sources/pyside6/PySide6/glue/qtcore.cpp
+++ b/sources/pyside6/PySide6/glue/qtcore.cpp
@@ -536,15 +536,6 @@ const QString result = qObjectTr(reinterpret_cast<PyTypeObject *>(%PYSELF), %1,
%PYARG_0 = %CONVERTTOPYTHON[QString](result);
// @snippet qobject-tr
-// @snippet qobject-receivers
-// Avoid return +1 because SignalManager connect to "destroyed()" signal to control object timelife
-int ret = %CPPSELF.%FUNCTION_NAME(%1);
-if (ret > 0 && ((strcmp(%1, SIGNAL(destroyed())) == 0) || (strcmp(%1, SIGNAL(destroyed(QObject*))) == 0)))
- ret -= PySide::SignalManager::instance().countConnectionsWith(%CPPSELF);
-
-%PYARG_0 = %CONVERTTOPYTHON[int](ret);
-// @snippet qobject-receivers
-
// @snippet qbytearray-mgetitem
if (PepIndex_Check(_key)) {
const Py_ssize_t _i = PyNumber_AsSsize_t(_key, PyExc_IndexError);
diff --git a/sources/pyside6/libpyside/globalreceiverv2.cpp b/sources/pyside6/libpyside/globalreceiverv2.cpp
index 4f72d14ce..1342af24e 100644
--- a/sources/pyside6/libpyside/globalreceiverv2.cpp
+++ b/sources/pyside6/libpyside/globalreceiverv2.cpp
@@ -18,11 +18,6 @@
#define RECEIVER_DESTROYED_SLOT_NAME "__receiverDestroyed__(QObject*)"
-namespace
-{
- static int DESTROY_SIGNAL_ID = 0;
- static int DESTROY_SLOT_ID = 0;
-}
namespace PySide
{
@@ -146,7 +141,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().deleteGobalReceiver(self->m_parent);
Py_END_ALLOW_THREADS
}
@@ -160,31 +155,17 @@ DynamicSlotDataV2::~DynamicSlotDataV2()
Py_DECREF(m_callback);
}
-GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, GlobalReceiverV2MapPtr map) :
+GlobalReceiverV2::GlobalReceiverV2(PyObject *callback) :
QObject(nullptr),
- m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject),
- m_sharedMap(std::move(map))
+ m_metaObject("__GlobalReceiver__", &QObject::staticMetaObject)
{
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.
@@ -204,69 +185,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 +240,15 @@ 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 isShortCuit = std::strchr(slot.methodSignature(), '(') == nullptr;
+ Shiboken::AutoDecRef callback(m_data->callback());
+ SignalManager::callPythonMetaMethod(slot, args, callback, isShortCuit);
// SignalManager::callPythonMetaMethod might have failed, in that case we have to print the
// error so it considered "handled".
diff --git a/sources/pyside6/libpyside/globalreceiverv2.h b/sources/pyside6/libpyside/globalreceiverv2.h
index 472c3e94a..7ac966eb5 100644
--- a/sources/pyside6/libpyside/globalreceiverv2.h
+++ b/sources/pyside6/libpyside/globalreceiverv2.h
@@ -10,6 +10,7 @@
#include <QtCore/QByteArray>
#include <QtCore/QObject>
+#include <QtCore/QPointer>
#include <QtCore/QMap>
#include <QtCore/QSharedPointer>
@@ -37,9 +38,6 @@ inline bool operator!=(const GlobalReceiverKey &k1, const GlobalReceiverKey &k2)
size_t qHash(const GlobalReceiverKey &k, size_t seed = 0);
-using GlobalReceiverV2Map = QHash<GlobalReceiverKey, GlobalReceiverV2 *>;
-using GlobalReceiverV2MapPtr = QSharedPointer<GlobalReceiverV2Map>;
-
/// A class used to link C++ Signals to non C++ slots (Python callbacks) by
/// providing fake slots for QObject::connect().
/// It keeps a Python callback and the list of QObject senders. It is stored
@@ -49,9 +47,7 @@ class GlobalReceiverV2 : public QObject
public:
/// Create a GlobalReceiver object that will call 'callback'
/// @param callback A Python callable object (can be a method or not)
- /// @param map A SharedPointer used on Signal manager that contains
- /// all instaces of GlobalReceiver
- GlobalReceiverV2(PyObject *callback, GlobalReceiverV2MapPtr map);
+ explicit GlobalReceiverV2(PyObject *callback);
~GlobalReceiverV2() override;
@@ -68,20 +64,17 @@ public:
void notify();
/// Used to increment the reference of the GlobalReceiver object
- /// @param link This is a optional parameter used to link the ref to
+ /// @param link This is a parameter used to link the ref to
/// some QObject life.
- void incRef(const QObject *link = nullptr);
+ void incRef(const QObject *link);
/// Used to decrement the reference of the GlobalReceiver object.
- /// @param link This is a optional parameter used to dismiss the link
+ /// @param link This is a parameter used to dismiss the link
/// ref to some QObject.
- void decRef(const QObject *link = nullptr);
+ void decRef(const QObject *link);
- /// Return the count of refs which the GlobalReceiver has
- /// @param link If any QObject was passed, the function returns the
- /// number of references relative to this 'link' object.
- /// @return The number of references
- int refCount(const QObject *link) const;
+ /// Returns whether any senders are registered.
+ bool isEmpty() const;
/// Use to retrieve the unique hash of this GlobalReceiver object
/// @return hash key
@@ -96,10 +89,12 @@ public:
MetaObjectBuilder &metaObjectBuilder() { return m_metaObject; }
private:
+ void purgeDeletedSenders();
+
MetaObjectBuilder m_metaObject;
DynamicSlotDataV2 *m_data;
- QList<const QObject *> m_refs;
- GlobalReceiverV2MapPtr m_sharedMap;
+ using QObjectPointer = QPointer<const QObject>;
+ QList<QObjectPointer> m_refs;
};
}
diff --git a/sources/pyside6/libpyside/signalmanager.cpp b/sources/pyside6/libpyside/signalmanager.cpp
index 9a9d1c19b..1f2fcac56 100644
--- a/sources/pyside6/libpyside/signalmanager.cpp
+++ b/sources/pyside6/libpyside/signalmanager.cpp
@@ -187,28 +187,19 @@ QDataStream &operator>>(QDataStream &in, PyObjectWrapper &myObj)
};
+namespace PySide {
+using GlobalReceiverV2Ptr = QSharedPointer<GlobalReceiverV2>;
+using GlobalReceiverV2Map = QHash<PySide::GlobalReceiverKey, GlobalReceiverV2Ptr>;
+}
+
using namespace PySide;
struct SignalManager::SignalManagerPrivate
{
- GlobalReceiverV2MapPtr m_globalReceivers;
- static SignalManager::QmlMetaCallErrorHandler m_qmlMetaCallErrorHandler;
-
- SignalManagerPrivate() : m_globalReceivers(new GlobalReceiverV2Map{})
- {
- }
+ void deleteGobalReceiver(const QObject *gr);
- ~SignalManagerPrivate()
- {
- if (!m_globalReceivers.isNull()) {
- // Delete receivers by always retrieving the current first element, because deleting a
- // receiver can indirectly delete another one, and if we use qDeleteAll, that could
- // cause either a double delete, or iterator invalidation, and thus undefined behavior.
- while (!m_globalReceivers->isEmpty())
- delete *m_globalReceivers->cbegin();
- Q_ASSERT(m_globalReceivers->isEmpty());
- }
- }
+ GlobalReceiverV2Map m_globalReceivers;
+ static SignalManager::QmlMetaCallErrorHandler m_qmlMetaCallErrorHandler;
static void handleMetaCallError(QObject *object, int *result);
static int qtPropertyMetacall(QObject *object, QMetaObject::Call call,
@@ -264,8 +255,7 @@ SignalManager::SignalManager() : m_d(new SignalManagerPrivate)
void SignalManager::clear()
{
- delete m_d;
- m_d = new SignalManagerPrivate();
+ m_d->m_globalReceivers.clear();
}
SignalManager::~SignalManager()
@@ -286,34 +276,16 @@ void SignalManager::setQmlMetaCallErrorHandler(QmlMetaCallErrorHandler handler)
QObject *SignalManager::globalReceiver(QObject *sender, PyObject *callback)
{
- GlobalReceiverV2MapPtr globalReceivers = m_d->m_globalReceivers;
- GlobalReceiverKey key = GlobalReceiverV2::key(callback);
- GlobalReceiverV2 *gr = nullptr;
- auto it = globalReceivers->find(key);
- if (it == globalReceivers->end()) {
- gr = new GlobalReceiverV2(callback, globalReceivers);
- globalReceivers->insert(key, gr);
- if (sender) {
- gr->incRef(sender); // create a link reference
- gr->decRef(); // remove extra reference
- }
- } else {
- gr = it.value();
- if (sender)
- gr->incRef(sender);
+ auto &globalReceivers = m_d->m_globalReceivers;
+ const GlobalReceiverKey key = GlobalReceiverV2::key(callback);
+ auto it = globalReceivers.find(key);
+ if (it == globalReceivers.end()) {
+ it = globalReceivers.insert(key, GlobalReceiverV2Ptr(new GlobalReceiverV2(callback)));
}
+ if (sender)
+ it.value()->incRef(sender); // create a link reference
- return reinterpret_cast<QObject *>(gr);
-}
-
-int SignalManager::countConnectionsWith(const QObject *object)
-{
- int count = 0;
- for (GlobalReceiverV2Map::const_iterator it = m_d->m_globalReceivers->cbegin(), end = m_d->m_globalReceivers->cend(); it != end; ++it) {
- if (it.value()->refCount(object))
- count++;
- }
- return count;
+ return it.value().data();
}
void SignalManager::notifyGlobalReceiver(QObject *receiver)
@@ -323,13 +295,30 @@ void SignalManager::notifyGlobalReceiver(QObject *receiver)
void SignalManager::releaseGlobalReceiver(const QObject *source, QObject *receiver)
{
- auto gr = reinterpret_cast<GlobalReceiverV2 *>(receiver);
+ auto gr = static_cast<GlobalReceiverV2 *>(receiver);
gr->decRef(source);
+ if (gr->isEmpty())
+ m_d->deleteGobalReceiver(gr);
+}
+
+void SignalManager::deleteGobalReceiver(const QObject *gr)
+{
+ SignalManager::instance().m_d->deleteGobalReceiver(gr);
+}
+
+void SignalManager::SignalManagerPrivate::deleteGobalReceiver(const QObject *gr)
+{
+ for (auto it = m_globalReceivers.begin(), end = m_globalReceivers.end(); it != end; ++it) {
+ if (it.value().data() == gr) {
+ m_globalReceivers.erase(it);
+ break;
+ }
+ }
}
int SignalManager::globalReceiverSlotIndex(QObject *receiver, const char *signature) const
{
- return reinterpret_cast<GlobalReceiverV2 *>(receiver)->addSlot(signature);
+ return static_cast<GlobalReceiverV2 *>(receiver)->addSlot(signature);
}
bool SignalManager::emitSignal(QObject *source, const char *signal, PyObject *args)
diff --git a/sources/pyside6/libpyside/signalmanager.h b/sources/pyside6/libpyside/signalmanager.h
index 60cf4e75e..abcff24d3 100644
--- a/sources/pyside6/libpyside/signalmanager.h
+++ b/sources/pyside6/libpyside/signalmanager.h
@@ -67,15 +67,14 @@ public:
// used to discovery metaobject
static const QMetaObject* retrieveMetaObject(PyObject* self);
- // Used to discovery if SignalManager was connected with object "destroyed()" signal.
- int countConnectionsWith(const QObject *object);
-
// Disconnect all signals managed by Globalreceiver
void clear();
// Utility function to call a python method usign args received in qt_metacall
static int callPythonMetaMethod(const QMetaMethod& method, void** args, PyObject* obj, bool isShortCuit);
+ static void deleteGobalReceiver(const QObject *globalReceiver);
+
private:
struct SignalManagerPrivate;
SignalManagerPrivate* m_d;