QProperty: Destroy binding when refcount is 0

This has to be done from all places where we deref it. Otherwise we leak
memory.

Pick-to: 6.6 6.5
Fixes: QTBUG-116086
Change-Id: I57307ac746205578a920d2bb1e159b66ebd9b2cc
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 717dc9450ffc13ef8209a10073552ac4574a4160)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Ulf Hermann 2024-03-01 15:16:54 +01:00 committed by Qt Cherry-pick Bot
parent fb5ff98d32
commit 933d695ecb
3 changed files with 60 additions and 9 deletions

View File

@ -753,13 +753,6 @@ void QPropertyObserverPointer::setChangeHandler(QPropertyObserver::ChangeHandler
ptr->next.setTag(QPropertyObserver::ObserverNotifiesChangeHandler);
}
void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *binding)
{
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
ptr->binding = binding;
ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
}
/*!
\internal
The same as setBindingToNotify, but assumes that the tag is already correct.

View File

@ -143,7 +143,13 @@ struct QPropertyObserverPointer
unlink_common();
}
void setBindingToNotify(QPropertyBindingPrivate *binding);
void setBindingToNotify(QPropertyBindingPrivate *binding)
{
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
ptr->binding = binding;
ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
}
void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding);
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
@ -941,7 +947,15 @@ QBindingObserverPtr::QBindingObserverPtr(QPropertyObserver *observer) noexcept :
QPropertyObserverPointer{d}.binding()->addRef();
}
QBindingObserverPtr::~QBindingObserverPtr() { if (d) QPropertyObserverPointer{d}.binding()->deref(); }
QBindingObserverPtr::~QBindingObserverPtr()
{
if (!d)
return;
QPropertyBindingPrivate *bindingPrivate = binding();
if (!bindingPrivate->deref())
QPropertyBindingPrivate::destroyAndFreeMemory(bindingPrivate);
}
QPropertyBindingPrivate *QBindingObserverPtr::binding() const noexcept { return QPropertyObserverPointer{d}.binding(); }

View File

@ -108,6 +108,8 @@ private slots:
void propertyAdaptorBinding();
void propertyUpdateViaSignaledProperty();
void derefFromObserver();
};
namespace {
@ -315,6 +317,7 @@ void tst_QProperty::bindingAfterUse()
void tst_QProperty::bindingFunctionDtorCalled()
{
DtorCounter::counter = 0;
DtorCounter dc;
{
QProperty<int> prop;
@ -2531,6 +2534,47 @@ void tst_QProperty::propertyUpdateViaSignaledProperty()
QCOMPARE(o.bindable2(), 36);
}
void tst_QProperty::derefFromObserver()
{
int triggered = 0;
QProperty<int> source(11);
DtorCounter::counter = 0;
DtorCounter dc;
QProperty<int> target([&triggered, &source, dc]() mutable {
dc.shouldIncrement = true;
return ++triggered + source.value();
});
QCOMPARE(triggered, 1);
{
auto propObserver = std::make_unique<QPropertyObserver>();
QPropertyObserverPointer propObserverPtr { propObserver.get() };
propObserverPtr.setBindingToNotify(QPropertyBindingPrivate::get(target.binding()));
QBindingObserverPtr bindingPtr(propObserver.get());
QCOMPARE(triggered, 1);
source = 25;
QCOMPARE(triggered, 2);
QCOMPARE(target, 27);
target.setBinding([]() { return 8; });
QCOMPARE(target, 8);
// The QBindingObserverPtr still holds on to the binding.
QCOMPARE(dc.counter, 0);
}
// The binding is actually gone now.
QCOMPARE(dc.counter, 1);
source = 26;
QCOMPARE(triggered, 2);
QCOMPARE(target, 8);
}
QTEST_MAIN(tst_QProperty);
#undef QT_SOURCE_LOCATION_NAMESPACE