diff options
Diffstat (limited to 'src/qml/qml/qqmldata_p.h')
-rw-r--r-- | src/qml/qml/qqmldata_p.h | 90 |
1 files changed, 56 insertions, 34 deletions
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 5b7c05c61e..7055ca94e6 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -52,10 +52,12 @@ struct Binding; // default state for elemental object allocations. This is crucial in the // workings of the QQmlInstruction::CreateSimpleObject instruction. // Don't change anything here without first considering that case! -class Q_QML_PRIVATE_EXPORT QQmlData : public QAbstractDeclarativeData +class Q_QML_EXPORT QQmlData : public QAbstractDeclarativeData { public: - QQmlData(); + enum Ownership { DoesNotOwnMemory, OwnsMemory }; + + QQmlData(Ownership ownership); ~QQmlData(); static inline void init() { @@ -122,24 +124,24 @@ public: }; struct NotifyList { - quint64 connectionMask; - - quint16 maximumTodoIndex; - quint16 notifiesSize; - - QQmlNotifierEndpoint *todo; - QQmlNotifierEndpoint**notifies; + QAtomicInteger<quint64> connectionMask; + QQmlNotifierEndpoint *todo = nullptr; + QQmlNotifierEndpoint**notifies = nullptr; + quint16 maximumTodoIndex = 0; + quint16 notifiesSize = 0; void layout(); private: void layout(QQmlNotifierEndpoint*); }; - NotifyList *notifyList; + QAtomicPointer<NotifyList> notifyList; - inline QQmlNotifierEndpoint *notify(int index); + inline QQmlNotifierEndpoint *notify(int index) const; void addNotify(int index, QQmlNotifierEndpoint *); int endpointCount(int index); bool signalHasEndpoint(int index) const; - void disconnectNotifiers(); + + enum class DeleteNotifyList { Yes, No }; + void disconnectNotifiers(DeleteNotifyList doDelete); // The context that created the C++ object; not refcounted to prevent cycles QQmlContextData *context = nullptr; @@ -147,13 +149,13 @@ public: QQmlContextData *outerContext = nullptr; QQmlRefPointer<QQmlContextData> ownContext; - QQmlAbstractBinding *bindings; - QQmlBoundSignal *signalHandlers; + QQmlAbstractBinding *bindings = nullptr; + QQmlBoundSignal *signalHandlers = nullptr; std::vector<QQmlPropertyObserver> propertyObservers; // Linked list for QQmlContext::contextObjects - QQmlData *nextContextObject; - QQmlData**prevContextObject; + QQmlData *nextContextObject = nullptr; + QQmlData**prevContextObject = nullptr; inline bool hasBindingBit(int) const; inline void setBindingBit(QObject *obj, int); @@ -163,10 +165,10 @@ public: inline void setPendingBindingBit(QObject *obj, int); inline void clearPendingBindingBit(int); - quint16 lineNumber; - quint16 columnNumber; + quint16 lineNumber = 0; + quint16 columnNumber = 0; - quint32 jsEngineId; // id of the engine that created the jsWrapper + quint32 jsEngineId = 0; // id of the engine that created the jsWrapper struct DeferredData { DeferredData(); @@ -192,7 +194,7 @@ public: QQmlPropertyCache::ConstPtr propertyCache; - QQmlGuardImpl *guards; + QQmlGuardImpl *guards = nullptr; static QQmlData *get(QObjectPrivate *priv, bool create) { // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has @@ -260,7 +262,7 @@ public: private: // For attachedProperties - mutable QQmlDataExtended *extendedData; + mutable QQmlDataExtended *extendedData = nullptr; Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv); Q_NEVER_INLINE static QQmlPropertyCache::ConstPtr createPropertyCache(QObject *object); @@ -295,7 +297,7 @@ private: Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit); - Q_DISABLE_COPY(QQmlData); + Q_DISABLE_COPY_MOVE(QQmlData); }; bool QQmlData::wasDeleted(const QObjectPrivate *priv) @@ -316,23 +318,31 @@ bool QQmlData::wasDeleted(const QObject *object) return QQmlData::wasDeleted(priv); } -QQmlNotifierEndpoint *QQmlData::notify(int index) +inline bool isIndexInConnectionMask(quint64 connectionMask, int index) +{ + return connectionMask & (1ULL << quint64(index % 64)); +} + +QQmlNotifierEndpoint *QQmlData::notify(int index) const { + // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics. + Q_ASSERT(index <= 0xFFFF); - if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) { + NotifyList *list = notifyList.loadRelaxed(); + if (!list || !isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index)) return nullptr; - } else if (index < notifyList->notifiesSize) { - return notifyList->notifies[index]; - } else if (index <= notifyList->maximumTodoIndex) { - notifyList->layout(); - } - if (index < notifyList->notifiesSize) { - return notifyList->notifies[index]; - } else { - return nullptr; + if (index < list->notifiesSize) + return list->notifies[index]; + + if (index <= list->maximumTodoIndex) { + list->layout(); + if (index < list->notifiesSize) + return list->notifies[index]; } + + return nullptr; } /* @@ -341,7 +351,19 @@ QQmlNotifierEndpoint *QQmlData::notify(int index) */ inline bool QQmlData::signalHasEndpoint(int index) const { - return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64))); + // This can be called from any thread. + // We still use relaxed semantics. If we're on a thread different from the "home" thread + // of the QQmlData, two interesting things might happen: + // + // 1. The list might go away while we hold it. In that case we are dealing with an object whose + // QObject dtor is being executed concurrently. This is UB already without the notify lists. + // Therefore, we don't need to consider it. + // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case + // we "misreport" the endpoint. Since ordering of events across threads is inherently + // nondeterministic, either result is correct in that case. We can accept it. + + NotifyList *list = notifyList.loadRelaxed(); + return list && isIndexInConnectionMask(list->connectionMask.loadRelaxed(), index); } bool QQmlData::hasBindingBit(int coreIndex) const |