diff options
Diffstat (limited to 'src/qml/qml/qqmldata_p.h')
-rw-r--r-- | src/qml/qml/qqmldata_p.h | 196 |
1 files changed, 103 insertions, 93 deletions
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index ed77c1851b..7055ca94e6 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QQMLDATA_P_H #define QQMLDATA_P_H @@ -57,6 +21,7 @@ #include <private/qv4value_p.h> #include <private/qv4persistent_p.h> #include <private/qqmlrefcount_p.h> +#include <private/qqmlpropertycache_p.h> #include <qqmlprivate.h> #include <qjsengine.h> #include <qvector.h> @@ -87,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() { @@ -98,7 +65,6 @@ public: if (!initialized) { initialized = true; QAbstractDeclarativeData::destroyed = destroyed; - QAbstractDeclarativeData::parentChanged = nullptr; QAbstractDeclarativeData::signalEmitted = signalEmitted; QAbstractDeclarativeData::receivers = receivers; QAbstractDeclarativeData::isSignalConnected = isSignalConnected; @@ -133,13 +99,15 @@ public: quint32 isQueuedForDeletion:1; /* * rootObjectInCreation should be true only when creating top level CPP and QML objects, - * v8 GC will check this flag, only deletes the objects when rootObjectInCreation is false. + * v4 GC will check this flag, only deletes the objects when rootObjectInCreation is false. */ quint32 rootObjectInCreation:1; // set when at least one of the object's properties is intercepted quint32 hasInterceptorMetaObject:1; quint32 hasVMEMetaObject:1; - quint32 dummy:8; + // If we have another wrapper for a const QObject * in the multiply wrapped QObjects. + quint32 hasConstWrapper: 1; + quint32 dummy:7; // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated @@ -156,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; @@ -181,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); @@ -197,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(); @@ -224,12 +192,11 @@ public: QV4::WeakValue jsWrapper; - QQmlPropertyCache *propertyCache; + QQmlPropertyCache::ConstPtr propertyCache; - QQmlGuardImpl *guards; + QQmlGuardImpl *guards = nullptr; - static QQmlData *get(const QObject *object, bool create = false) { - QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); + static QQmlData *get(QObjectPrivate *priv, bool create) { // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use. if (priv->isDeletingChildren || priv->wasDeleted) { @@ -244,6 +211,25 @@ public: } } + static QQmlData *get(const QObjectPrivate *priv) { + // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has + // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use. + if (priv->isDeletingChildren || priv->wasDeleted) + return nullptr; + if (priv->declarativeData) + return static_cast<QQmlData *>(priv->declarativeData); + return nullptr; + } + + static QQmlData *get(QObject *object, bool create) { + return QQmlData::get(QObjectPrivate::get(object), create); + } + + static QQmlData *get(const QObject *object) { + return QQmlData::get(QObjectPrivate::get(object)); + + } + static bool keepAliveDuringGarbageCollection(const QObject *object) { QQmlData *ddata = get(object); if (!ddata || ddata->indestructible || ddata->rootObjectInCreation) @@ -255,19 +241,20 @@ public: QHash<QQmlAttachedPropertiesFunc, QObject *> *attachedProperties() const; static inline bool wasDeleted(const QObject *); + static inline bool wasDeleted(const QObjectPrivate *); static void markAsDeleted(QObject *); static void setQueuedForDeletion(QObject *); - static inline void flushPendingBinding(QObject *, QQmlPropertyIndex propertyIndex); + static inline void flushPendingBinding(QObject *object, int coreIndex); + void flushPendingBinding(int coreIndex); - static QQmlPropertyCache *ensurePropertyCache(QJSEngine *engine, QObject *object) + static QQmlPropertyCache::ConstPtr ensurePropertyCache(QObject *object) { - Q_ASSERT(engine); QQmlData *ddata = QQmlData::get(object, /*create*/true); if (Q_LIKELY(ddata->propertyCache)) return ddata->propertyCache; - return createPropertyCache(engine, object); + return createPropertyCache(object); } Q_ALWAYS_INLINE static uint offsetForBit(int bit) { return static_cast<uint>(bit) / BitsPerType; } @@ -275,12 +262,10 @@ public: private: // For attachedProperties - mutable QQmlDataExtended *extendedData; + mutable QQmlDataExtended *extendedData = nullptr; Q_NEVER_INLINE static QQmlData *createQQmlData(QObjectPrivate *priv); - Q_NEVER_INLINE static QQmlPropertyCache *createPropertyCache(QJSEngine *engine, QObject *object); - - void flushPendingBindingImpl(QQmlPropertyIndex index); + Q_NEVER_INLINE static QQmlPropertyCache::ConstPtr createPropertyCache(QObject *object); Q_ALWAYS_INLINE bool hasBitSet(int bit) const { @@ -312,39 +297,52 @@ 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) +{ + if (!priv || priv->wasDeleted || priv->isDeletingChildren) + return true; + + const QQmlData *ddata = QQmlData::get(priv); + return ddata && ddata->isQueuedForDeletion; +} + bool QQmlData::wasDeleted(const QObject *object) { if (!object) return true; const QObjectPrivate *priv = QObjectPrivate::get(object); - if (!priv || priv->wasDeleted || priv->isDeletingChildren) - return true; + return QQmlData::wasDeleted(priv); +} - const QQmlData *ddata = QQmlData::get(object); - return ddata && ddata->isQueuedForDeletion; +inline bool isIndexInConnectionMask(quint64 connectionMask, int index) +{ + return connectionMask & (1ULL << quint64(index % 64)); } -QQmlNotifierEndpoint *QQmlData::notify(int index) +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; } /* @@ -353,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 @@ -400,11 +410,11 @@ void QQmlData::clearPendingBindingBit(int coreIndex) clearBit(coreIndex * 2 + 1); } -void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex) +void QQmlData::flushPendingBinding(QObject *object, int coreIndex) { - QQmlData *data = QQmlData::get(o, false); - if (data && data->hasPendingBindingBit(propertyIndex.coreIndex())) - data->flushPendingBindingImpl(propertyIndex); + QQmlData *data = QQmlData::get(object, false); + if (data && data->hasPendingBindingBit(coreIndex)) + data->flushPendingBinding(coreIndex); } QT_END_NAMESPACE |