From 226d0981cdf1da8ca8ee53d88b663eea14de41e5 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Mon, 28 Jun 2021 15:27:43 +0200 Subject: QProperty: more micro optimization - Provide an inline version of evaluateRecursive which does not fetch the status. - Provide an unsafe variant of setBindingToNotify which does not set the tag. This can be used in allocateDependencyObserver, as newly allocated observers already have the correct tag (this is checked via an assert). Change-Id: I31aec6af4aef244efc6d0777e5bfaaa8f82f2046 Reviewed-by: Lars Knoll (cherry picked from commit f7eed15588fc93af417b8969fe4e498936d81e04) --- src/corelib/kernel/qproperty.cpp | 54 +++++++++++----------------------------- src/corelib/kernel/qproperty_p.h | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 40 deletions(-) diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index 97854f8ac3..0055e6a5ff 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -285,44 +285,7 @@ void QPropertyBindingPrivate::unlinkAndDeref() void QPropertyBindingPrivate::evaluateRecursive(QBindingStatus *status) { - if (updating) { - error = QPropertyBindingError(QPropertyBindingError::BindingLoop); - if (isQQmlPropertyBinding) - errorCallBack(this); - return; - } - - QScopedValueRollback updateGuard(updating, true); - - /* - * Evaluating the binding might lead to the binding being broken. This can - * cause ref to reach zero at the end of the function. However, the - * updateGuard's destructor will then still trigger, trying to set the - * updating bool to its old value - * To prevent this, we create a QPropertyBindingPrivatePtr which ensures - * that the object is still alive when updateGuard's dtor runs. - */ - QPropertyBindingPrivatePtr keepAlive {this}; - - BindingEvaluationState evaluationFrame(this, status); - - auto bindingFunctor = reinterpret_cast(this) + - QPropertyBindingPrivate::getSizeEnsuringAlignment(); - bool changed = false; - if (hasBindingWrapper) { - changed = staticBindingWrapper(metaType, propertyDataPtr, - {vtable, bindingFunctor}); - } else { - changed = vtable->call(metaType, propertyDataPtr, bindingFunctor); - } - // If there was a change, we must set pendingNotify. - // If there was not, we must not clear it, as that only should happen in notifyRecursive - pendingNotify = pendingNotify || changed; - if (!changed || !firstObserver) - return; - - firstObserver.noSelfDependencies(this); - firstObserver.evaluateBindings(status); + return evaluateRecursive_inline(status); } void QPropertyBindingPrivate::notifyRecursive() @@ -539,7 +502,8 @@ void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(Binding QPropertyBindingDataPointer d{this}; QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver(); - dependencyObserver.setBindingToNotify(currentState->binding); + Q_ASSERT(QPropertyObserver::ObserverNotifiesBinding == 0); + dependencyObserver.setBindingToNotify_unsafe(currentState->binding); d.addObserver(dependencyObserver.ptr); } @@ -663,6 +627,16 @@ void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *bindi ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding); } +/*! + \internal + The same as as setBindingToNotify, but assumes that the tag is already correct. + */ +void QPropertyObserverPointer::setBindingToNotify_unsafe(QPropertyBindingPrivate *binding) +{ + Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding); + ptr->binding = binding; +} + /*! \internal QPropertyObserverNodeProtector is a RAII wrapper which takes care of the internal switching logic @@ -775,7 +749,7 @@ void QPropertyObserverPointer::evaluateBindings(QBindingStatus *status) if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) { auto bindingToEvaluate = observer->binding; QPropertyObserverNodeProtector protector(observer); - bindingToEvaluate->evaluateRecursive(status); + bindingToEvaluate->evaluateRecursive_inline(status); next = protector.next(); } diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index 96e89926db..f1b6ef6aa2 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -55,6 +55,7 @@ #include #include +#include #include QT_BEGIN_NAMESPACE @@ -104,6 +105,7 @@ struct QPropertyObserverPointer void unlink(); void setBindingToNotify(QPropertyBindingPrivate *binding); + void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding); void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler); void notify(QUntypedPropertyData *propertyDataPtr); @@ -325,6 +327,8 @@ public: void unlinkAndDeref(); void evaluateRecursive(QBindingStatus *status = nullptr); + void Q_ALWAYS_INLINE evaluateRecursive_inline(QBindingStatus *status = nullptr); + void notifyRecursive(); static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding) @@ -693,6 +697,48 @@ struct QUntypedBindablePrivate } }; +inline void QPropertyBindingPrivate::evaluateRecursive_inline(QBindingStatus *status) +{ + if (updating) { + error = QPropertyBindingError(QPropertyBindingError::BindingLoop); + if (isQQmlPropertyBinding) + errorCallBack(this); + return; + } + + QScopedValueRollback updateGuard(updating, true); + + /* + * Evaluating the binding might lead to the binding being broken. This can + * cause ref to reach zero at the end of the function. However, the + * updateGuard's destructor will then still trigger, trying to set the + * updating bool to its old value + * To prevent this, we create a QPropertyBindingPrivatePtr which ensures + * that the object is still alive when updateGuard's dtor runs. + */ + QPropertyBindingPrivatePtr keepAlive {this}; + + QtPrivate::BindingEvaluationState evaluationFrame(this, status); + + auto bindingFunctor = reinterpret_cast(this) + + QPropertyBindingPrivate::getSizeEnsuringAlignment(); + bool changed = false; + if (hasBindingWrapper) { + changed = staticBindingWrapper(metaType, propertyDataPtr, + {vtable, bindingFunctor}); + } else { + changed = vtable->call(metaType, propertyDataPtr, bindingFunctor); + } + // If there was a change, we must set pendingNotify. + // If there was not, we must not clear it, as that only should happen in notifyRecursive + pendingNotify = pendingNotify || changed; + if (!changed || !firstObserver) + return; + + firstObserver.noSelfDependencies(this); + firstObserver.evaluateBindings(status); +} + QT_END_NAMESPACE #endif // QPROPERTY_P_H -- cgit v1.2.3