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).

Pick-to: 6.2
Change-Id: I31aec6af4aef244efc6d0777e5bfaaa8f82f2046
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
bb10
Fabian Kosmale 2021-06-28 15:27:43 +02:00
parent 77d528f8d3
commit f7eed15588
2 changed files with 60 additions and 40 deletions

View File

@ -285,44 +285,7 @@ void QPropertyBindingPrivate::unlinkAndDeref()
void QPropertyBindingPrivate::evaluateRecursive(QBindingStatus *status)
{
if (updating) {
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
if (isQQmlPropertyBinding)
errorCallBack(this);
return;
}
QScopedValueRollback<bool> 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<std::byte *>(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();
}

View File

@ -55,6 +55,7 @@
#include <qproperty.h>
#include <qscopedpointer.h>
#include <qscopedvaluerollback.h>
#include <vector>
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<bool> 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<std::byte *>(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