QThread: Clean up bindingStatusOrList if object gets deleted
Deal with the case that the object gets deleted between a call to moveToThread and the start of the thread by removing the object from the list in that case. Fixes: QTBUG-104014 Pick-to: 6.4 Change-Id: Ib249b6e8e8dfbc4d1332bb99a57fa9d3cff16465 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
6db91c0df1
commit
2c81ba2df9
@ -50,6 +50,7 @@ public:
|
|||||||
~QBindingStorage();
|
~QBindingStorage();
|
||||||
|
|
||||||
bool isEmpty() { return !d; }
|
bool isEmpty() { return !d; }
|
||||||
|
bool isValid() const noexcept { return bindingStatus; }
|
||||||
|
|
||||||
const QBindingStatus *status(QtPrivate::QBindingStatusAccessToken) const;
|
const QBindingStatus *status(QtPrivate::QBindingStatusAccessToken) const;
|
||||||
|
|
||||||
|
@ -970,6 +970,16 @@ QObject::~QObject()
|
|||||||
d->wasDeleted = true;
|
d->wasDeleted = true;
|
||||||
d->blockSig = 0; // unblock signals so we always emit destroyed()
|
d->blockSig = 0; // unblock signals so we always emit destroyed()
|
||||||
|
|
||||||
|
if (!d->bindingStorage.isValid()) {
|
||||||
|
// this might be the case after an incomplete thread-move
|
||||||
|
// remove this object from the pending list in that case
|
||||||
|
if (QThread *ownThread = thread()) {
|
||||||
|
auto *privThread = static_cast<QThreadPrivate *>(
|
||||||
|
QObjectPrivate::get(ownThread));
|
||||||
|
privThread->removeObjectWithPendingBindingStatusChange(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we reached this point, we need to clear the binding data
|
// If we reached this point, we need to clear the binding data
|
||||||
// as the corresponding properties are no longer useful
|
// as the corresponding properties are no longer useful
|
||||||
d->clearBindingStorage();
|
d->clearBindingStorage();
|
||||||
|
@ -605,6 +605,19 @@ QBindingStatus *QtPrivate::BindingStatusOrList::addObjectUnlessAlreadyStatus(QOb
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
If BindingStatusOrList is a list, remove \a object from it
|
||||||
|
*/
|
||||||
|
void QtPrivate::BindingStatusOrList::removeObject(QObject *object)
|
||||||
|
{
|
||||||
|
List *objectList = list();
|
||||||
|
if (!objectList)
|
||||||
|
return;
|
||||||
|
auto it = std::remove(objectList->begin(), objectList->end(), object);
|
||||||
|
objectList->erase(it, objectList->end());
|
||||||
|
}
|
||||||
|
|
||||||
QBindingStatus *QThreadPrivate::addObjectWithPendingBindingStatusChange(QObject *obj)
|
QBindingStatus *QThreadPrivate::addObjectWithPendingBindingStatusChange(QObject *obj)
|
||||||
{
|
{
|
||||||
if (auto status = m_statusOrPendingObjects.bindingStatus())
|
if (auto status = m_statusOrPendingObjects.bindingStatus())
|
||||||
@ -613,6 +626,15 @@ QBindingStatus *QThreadPrivate::addObjectWithPendingBindingStatusChange(QObject
|
|||||||
return m_statusOrPendingObjects.addObjectUnlessAlreadyStatus(obj);
|
return m_statusOrPendingObjects.addObjectUnlessAlreadyStatus(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QThreadPrivate::removeObjectWithPendingBindingStatusChange(QObject *obj)
|
||||||
|
{
|
||||||
|
if (m_statusOrPendingObjects.bindingStatus())
|
||||||
|
return;
|
||||||
|
QMutexLocker lock(&mutex);
|
||||||
|
m_statusOrPendingObjects.removeObject(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\threadsafe
|
\threadsafe
|
||||||
Tells the thread's event loop to exit with a return code.
|
Tells the thread's event loop to exit with a return code.
|
||||||
|
@ -108,6 +108,7 @@ public:
|
|||||||
|
|
||||||
// requires external synchronization:
|
// requires external synchronization:
|
||||||
QBindingStatus *addObjectUnlessAlreadyStatus(QObject *object);
|
QBindingStatus *addObjectUnlessAlreadyStatus(QObject *object);
|
||||||
|
void removeObject(QObject *object);
|
||||||
void setStatusAndClearList(QBindingStatus *status) noexcept;
|
void setStatusAndClearList(QBindingStatus *status) noexcept;
|
||||||
|
|
||||||
|
|
||||||
@ -237,6 +238,7 @@ public:
|
|||||||
if that one has been set in the meantime
|
if that one has been set in the meantime
|
||||||
*/
|
*/
|
||||||
QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *obj);
|
QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *obj);
|
||||||
|
void removeObjectWithPendingBindingStatusChange(QObject *obj);
|
||||||
|
|
||||||
// manipulating m_statusOrPendingObjects requires mutex to be locked
|
// manipulating m_statusOrPendingObjects requires mutex to be locked
|
||||||
QtPrivate::BindingStatusOrList m_statusOrPendingObjects = {};
|
QtPrivate::BindingStatusOrList m_statusOrPendingObjects = {};
|
||||||
@ -263,6 +265,7 @@ public:
|
|||||||
|
|
||||||
QBindingStatus* bindingStatus() { return m_bindingStatus; }
|
QBindingStatus* bindingStatus() { return m_bindingStatus; }
|
||||||
QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *) { return nullptr; }
|
QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *) { return nullptr; }
|
||||||
|
void removeObjectWithPendingBindingStatusChange(QObject *) {}
|
||||||
|
|
||||||
static void setCurrentThread(QThread *) { }
|
static void setCurrentThread(QThread *) { }
|
||||||
static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
|
static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
|
||||||
|
@ -97,6 +97,8 @@ private slots:
|
|||||||
|
|
||||||
void terminateAndPrematureDestruction();
|
void terminateAndPrematureDestruction();
|
||||||
void terminateAndDoubleDestruction();
|
void terminateAndDoubleDestruction();
|
||||||
|
|
||||||
|
void bindingListCleanupAfterDelete();
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute };
|
enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute };
|
||||||
@ -1830,5 +1832,17 @@ void tst_QThread::terminateAndDoubleDestruction()
|
|||||||
TestObject obj;
|
TestObject obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QThread::bindingListCleanupAfterDelete()
|
||||||
|
{
|
||||||
|
QThread t;
|
||||||
|
auto optr = std::make_unique<QObject>();
|
||||||
|
optr->moveToThread(&t);
|
||||||
|
auto threadPriv = static_cast<QThreadPrivate *>(QObjectPrivate::get(&t));
|
||||||
|
auto list = threadPriv->m_statusOrPendingObjects.list();
|
||||||
|
QVERIFY(list);
|
||||||
|
optr.reset();
|
||||||
|
QVERIFY(list->empty());
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QThread)
|
QTEST_MAIN(tst_QThread)
|
||||||
#include "tst_qthread.moc"
|
#include "tst_qthread.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user