diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index b8b5c0bdc79..9ae78669b9e 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -118,12 +118,11 @@ struct QPropertyDelayedNotifications Change notifications are sent later with notify (following the logic of separating binding updates and notifications used in non-deferred updates). */ - [[nodiscard]] PendingBindingObserverList evaluateBindings(qsizetype index, QBindingStatus *status) { - PendingBindingObserverList bindingObservers; + void evaluateBindings(PendingBindingObserverList &bindingObservers, qsizetype index, QBindingStatus *status) { auto *delayed = delayedProperties + index; auto *bindingData = delayed->originalBindingData; if (!bindingData) - return bindingObservers; + return; bindingData->d_ptr = delayed->d_ptr; Q_ASSERT(!(bindingData->d_ptr & QPropertyBindingData::DelayedNotificationBit)); @@ -136,7 +135,6 @@ struct QPropertyDelayedNotifications QPropertyObserverPointer observer = bindingDataPointer.firstObserver(); if (observer) observer.evaluateBindings(bindingObservers, status); - return bindingObservers; } /*! @@ -150,17 +148,17 @@ struct QPropertyDelayedNotifications */ void notify(qsizetype index) { auto *delayed = delayedProperties + index; - auto *bindingData = delayed->originalBindingData; - if (!bindingData) + if (delayed->d_ptr & QPropertyBindingData::BindingBit) + return; // already handled + if (!delayed->originalBindingData) return; - delayed->originalBindingData = nullptr; + + QPropertyObserverPointer observer { reinterpret_cast(delayed->d_ptr & ~QPropertyBindingData::DelayedNotificationBit) }; delayed->d_ptr = 0; - QPropertyBindingDataPointer bindingDataPointer{bindingData}; - QPropertyObserverPointer observer = bindingDataPointer.firstObserver(); if (observer) - observer.notify(delayed->propertyData); + observer.notify(delayed->propertyData); } }; @@ -215,17 +213,21 @@ void Qt::endPropertyUpdateGroup() if (--data->ref) return; groupUpdateData = nullptr; + // ensures that bindings are kept alive until endPropertyUpdateGroup concludes + PendingBindingObserverList bindingObservers; // update all delayed properties auto start = data; while (data) { - for (qsizetype i = 0; i < data->used; ++i) { - PendingBindingObserverList bindingObserves = data->evaluateBindings(i, status); - Q_UNUSED(bindingObserves); - // ### TODO: Use bindingObservers for notify - } + for (qsizetype i = 0; i < data->used; ++i) + data->evaluateBindings(bindingObservers, i, status); data = data->next; } - // notify all delayed properties + // notify all delayed notifications from binding evaluation + for (const QBindingObserverPtr &observer: bindingObservers) { + QPropertyBindingPrivate *binding = observer.binding(); + binding->notifyNonRecursive(); + } + // do the same for properties which only have observers data = start; while (data) { for (qsizetype i = 0; i < data->used; ++i) diff --git a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp index 97dda5f437c..18bdcb9dc75 100644 --- a/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp +++ b/tests/auto/corelib/kernel/qproperty/tst_qproperty.cpp @@ -87,6 +87,7 @@ private slots: void groupedNotifications(); void groupedNotificationConsistency(); void bindingGroupMovingBindingData(); + void bindingGroupBindingDeleted(); void uninstalledBindingDoesNotEvaluate(); void notify(); @@ -1976,6 +1977,31 @@ void tst_QProperty::bindingGroupMovingBindingData() QVERIFY(proxyData); } +void tst_QProperty::bindingGroupBindingDeleted() +{ + auto deleter = std::make_unique(); + auto toBeDeleted = std::make_unique(); + + bool calledHandler = false; + deleter->property.setBinding([&](){ + int newValue = toBeDeleted->property; + if (newValue == 42) + toBeDeleted.reset(); + return newValue; + }); + auto handler = toBeDeleted->property.onValueChanged([&]() { calledHandler = true; } ); + { + Qt::beginPropertyUpdateGroup(); + auto cleanup = qScopeGuard([](){ Qt::endPropertyUpdateGroup(); }); + QVERIFY(toBeDeleted); + toBeDeleted->property = 42; + // ASAN should not complain here + } + QVERIFY(!toBeDeleted); + // the change notification is sent, even if the binding is deleted during evaluation + QVERIFY(calledHandler); +} + void tst_QProperty::uninstalledBindingDoesNotEvaluate() { QProperty i;