diff options
| author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2022-12-06 17:56:14 +0100 |
|---|---|---|
| committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2022-12-08 10:30:35 +0100 |
| commit | 1270a9e82e5bc3bd53a1131698ece60403da1192 (patch) | |
| tree | 5015e154c123078bd5d4524b63f06db6f2f0cd69 /sources/pyside6/libpyside/signalmanager.cpp | |
| parent | f7e2771a8f3a187407822df4564deeb7df1712dc (diff) | |
Fix a crash when deleting QObject instances with connections in threads
GlobalReceiverV2 connected to the destroyed() signal of the senders to
keep track of its lifetime, which caused issues with delayed emission
of signals when using threads.
To fix this, change GlobalReceiverV2's sender list to use QPointer,
which automatically tracks the lifetime of the pointees. Move the
deletion of the GlobalReceiverV2 instances into SignalManager,
completely, removing the "delete this" pattern used.
This allows for removing some hacks for the QObject::receivers()
function.
Fixes: PYSIDE-2141
Change-Id: I361256f919dab13bfcf20800624b2454308bbc4b
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'sources/pyside6/libpyside/signalmanager.cpp')
| -rw-r--r-- | sources/pyside6/libpyside/signalmanager.cpp | 83 |
1 files changed, 36 insertions, 47 deletions
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) |
