diff options
author | Simon Hausmann <simon.hausmann@theqtcompany.com> | 2014-11-20 09:07:52 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@digia.com> | 2014-12-29 14:39:22 +0100 |
commit | efc3cc717ac1a62965465e909cc99acef627cdb1 (patch) | |
tree | 9d181ca8e9e299fa99b9b7598bfb83bc53056dde /src/qml/jsapi | |
parent | 65953304a2775e69c7edd46b780aa39f769d32ac (diff) |
Make QQmlPropertyCache available in QJSEngine
The QQmlEngine has two containers for property caches, one for QML types and
one for all-purpose meta-objects. The latter is rather useful and now being
moved to QJSEngine to be available there.
Change-Id: Ieab65c400b8a2e410e5f9eee6d603162dbb864d9
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml/jsapi')
-rw-r--r-- | src/qml/jsapi/qjsengine.cpp | 21 | ||||
-rw-r--r-- | src/qml/jsapi/qjsengine_p.h | 125 |
2 files changed, 145 insertions, 1 deletions
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index e4daa64674..04ed341a50 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -539,6 +539,27 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) \sa toScriptValue() */ + +QJSEnginePrivate::~QJSEnginePrivate() +{ + for (QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator iter = propertyCache.begin(); iter != propertyCache.end(); ++iter) + (*iter)->release(); +} + +QQmlPropertyCache *QJSEnginePrivate::createCache(const QMetaObject *mo) +{ + if (!mo->superClass()) { + QQmlPropertyCache *rv = new QQmlPropertyCache(q_func(), mo); + propertyCache.insert(mo, rv); + return rv; + } else { + QQmlPropertyCache *super = cache(mo->superClass()); + QQmlPropertyCache *rv = super->copyAndAppend(mo); + propertyCache.insert(mo, rv); + return rv; + } +} + QT_END_NAMESPACE #include "moc_qjsengine.cpp" diff --git a/src/qml/jsapi/qjsengine_p.h b/src/qml/jsapi/qjsengine_p.h index 654d0dee56..eba8ffb4be 100644 --- a/src/qml/jsapi/qjsengine_p.h +++ b/src/qml/jsapi/qjsengine_p.h @@ -46,11 +46,13 @@ // #include <QtCore/private/qobject_p.h> +#include <QtCore/qmutex.h> #include "qjsengine.h" QT_BEGIN_NAMESPACE +class QQmlPropertyCache; class QJSEnginePrivate : public QObjectPrivate { @@ -58,10 +60,131 @@ class QJSEnginePrivate : public QObjectPrivate public: static QJSEnginePrivate* get(QJSEngine*e) { return e->d_func(); } + static const QJSEnginePrivate* get(const QJSEngine*e) { return e->d_func(); } - QJSEnginePrivate() {} + QJSEnginePrivate() : mutex(QMutex::Recursive) {} + ~QJSEnginePrivate(); + + // Locker locks the QQmlEnginePrivate data structures for read and write, if necessary. + // Currently, locking is only necessary if the threaded loader is running concurrently. If it is + // either idle, or is running with the main thread blocked, no locking is necessary. This way + // we only pay for locking when we have to. + // Consequently, this class should only be used to protect simple accesses or modifications of the + // QQmlEnginePrivate structures or operations that can be guaranteed not to start activity + // on the loader thread. + // The Locker API is identical to QMutexLocker. Locker reuses the QQmlEnginePrivate::mutex + // QMutex instance and multiple Lockers are recursive in the same thread. + class Locker + { + public: + inline Locker(const QJSEngine *); + inline Locker(const QJSEnginePrivate *); + inline ~Locker(); + + inline void unlock(); + inline void relock(); + + private: + const QJSEnginePrivate *m_ep; + quint32 m_locked:1; + }; + + // Shared by QQmlEngine + mutable QMutex mutex; + + + // These methods may be called from the QML loader thread + inline QQmlPropertyCache *cache(QObject *obj); + inline QQmlPropertyCache *cache(const QMetaObject *); + +private: + // Must be called locked + QQmlPropertyCache *createCache(const QMetaObject *); + + // These members must be protected by a QJSEnginePrivate::Locker as they are required by + // the threaded loader. Only access them through their respective accessor methods. + QHash<const QMetaObject *, QQmlPropertyCache *> propertyCache; }; +QJSEnginePrivate::Locker::Locker(const QJSEngine *e) +: m_ep(QJSEnginePrivate::get(e)) +{ + relock(); +} + +QJSEnginePrivate::Locker::Locker(const QJSEnginePrivate *e) +: m_ep(e), m_locked(false) +{ + relock(); +} + +QJSEnginePrivate::Locker::~Locker() +{ + unlock(); +} + +void QJSEnginePrivate::Locker::unlock() +{ + if (m_locked) { + m_ep->mutex.unlock(); + m_locked = false; + } +} + +void QJSEnginePrivate::Locker::relock() +{ + Q_ASSERT(!m_locked); + m_ep->mutex.lock(); + m_locked = true; +} + +/*! +Returns a QQmlPropertyCache for \a obj if one is available. + +If \a obj is null, being deleted or contains a dynamic meta object 0 +is returned. + +The returned cache is not referenced, so if it is to be stored, call addref(). + +XXX thread There is a potential future race condition in this and all the cache() +functions. As the QQmlPropertyCache is returned unreferenced, when called +from the loader thread, it is possible that the cache will have been dereferenced +and deleted before the loader thread has a chance to use or reference it. This +can't currently happen as the cache holds a reference to the +QQmlPropertyCache until the QQmlEngine is destroyed. +*/ +QQmlPropertyCache *QJSEnginePrivate::cache(QObject *obj) +{ + if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted) + return 0; + + Locker locker(this); + const QMetaObject *mo = obj->metaObject(); + QQmlPropertyCache *rv = propertyCache.value(mo); + if (!rv) rv = createCache(mo); + return rv; +} + +/*! +Returns a QQmlPropertyCache for \a metaObject. + +As the cache is persisted for the life of the engine, \a metaObject must be +a static "compile time" meta-object, or a meta-object that is otherwise known to +exist for the lifetime of the QQmlEngine. + +The returned cache is not referenced, so if it is to be stored, call addref(). +*/ +QQmlPropertyCache *QJSEnginePrivate::cache(const QMetaObject *metaObject) +{ + Q_ASSERT(metaObject); + + Locker locker(this); + QQmlPropertyCache *rv = propertyCache.value(metaObject); + if (!rv) rv = createCache(metaObject); + return rv; +} + + QT_END_NAMESPACE #endif // QJSENGINE_P_H |