From 50e3f9dba978709c35c869ccaa8345719f23deb1 Mon Sep 17 00:00:00 2001 From: Bea Lam Date: Mon, 22 Mar 2010 16:00:27 +1000 Subject: Properly use one thread for all instances of XmlListModel. Task-number: QT-2831 --- src/declarative/util/qdeclarativexmllistmodel.cpp | 190 ++++++++++++--------- src/declarative/util/qdeclarativexmllistmodel_p.h | 15 +- .../tst_qdeclarativexmllistmodel.cpp | 66 +++++++ 3 files changed, 188 insertions(+), 83 deletions(-) diff --git a/src/declarative/util/qdeclarativexmllistmodel.cpp b/src/declarative/util/qdeclarativexmllistmodel.cpp index 01ae2ed..03ddddf 100644 --- a/src/declarative/util/qdeclarativexmllistmodel.cpp +++ b/src/declarative/util/qdeclarativexmllistmodel.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #include #include @@ -61,8 +62,7 @@ QT_BEGIN_NAMESPACE - - +Q_DECLARE_METATYPE(QDeclarativeXmlQueryResult) typedef QPair QDeclarativeXmlListRange; @@ -109,13 +109,25 @@ typedef QPair QDeclarativeXmlListRange; \sa XmlListModel */ +struct XmlQueryJob +{ + int queryId; + QByteArray data; + QString query; + QString namespaces; + QStringList roleQueries; + QStringList keyRoleQueries; + QStringList keyRoleResultsCache; +}; + class QDeclarativeXmlQuery : public QThread { Q_OBJECT public: QDeclarativeXmlQuery(QObject *parent=0) - : QThread(parent), m_quit(false), m_restart(false), m_abort(false), m_queryId(0) { + : QThread(parent), m_quit(false), m_abortQueryId(-1), m_queryIds(0) { + qRegisterMetaType("QDeclarativeXmlQueryResult"); } ~QDeclarativeXmlQuery() { m_mutex.lock(); @@ -126,27 +138,38 @@ public: wait(); } - void abort() { + void abort(int id) { QMutexLocker locker(&m_mutex); - m_abort = true; + m_abortQueryId = id; } - int doQuery(QString query, QString namespaces, QByteArray data, QList *roleObjects) { + int doQuery(QString query, QString namespaces, QByteArray data, QList *roleObjects, QStringList keyRoleResultsCache) { QMutexLocker locker(&m_mutex); - m_size = 0; - m_data = data; - m_query = QLatin1String("doc($src)") + query; - m_namespaces = namespaces; - m_roleObjects = roleObjects; - if (!isRunning()) { - m_abort = false; + + XmlQueryJob job; + job.queryId = m_queryIds; + job.data = data; + job.query = QLatin1String("doc($src)") + query; + job.namespaces = namespaces; + job.keyRoleResultsCache = keyRoleResultsCache; + + for (int i=0; icount(); i++) { + if (!roleObjects->at(i)->isValid()) { + job.roleQueries << ""; + continue; + } + job.roleQueries << roleObjects->at(i)->query(); + if (roleObjects->at(i)->isKey()) + job.keyRoleQueries << job.roleQueries.last(); + } + m_jobs.enqueue(job); + m_queryIds++; + + if (!isRunning()) start(); - } else { - m_restart = true; + else m_condition.wakeOne(); - } - m_queryId++; - return m_queryId; + return job.queryId; } QList > modelData() { @@ -164,26 +187,33 @@ public: return m_removedItemRanges; } + Q_SIGNALS: - void queryCompleted(int queryId, int size); + void queryCompleted(const QDeclarativeXmlQueryResult &); protected: void run() { while (!m_quit) { m_mutex.lock(); - int queryId = m_queryId; doQueryJob(); doSubQueryJob(); - m_data.clear(); // no longer needed m_mutex.unlock(); m_mutex.lock(); - if (!m_abort) - emit queryCompleted(queryId, m_size); - if (!m_restart) + const XmlQueryJob &job = m_jobs.dequeue(); + if (m_abortQueryId != job.queryId) { + QDeclarativeXmlQueryResult r; + r.queryId = job.queryId; + r.size = m_size; + r.data = m_modelData; + r.inserted = m_insertedItemRanges; + r.removed = m_removedItemRanges; + r.keyRoleResultsCache = job.keyRoleResultsCache; + emit queryCompleted(r); + } + if (m_jobs.isEmpty()) m_condition.wait(&m_mutex); - m_abort = false; - m_restart = false; + m_abortQueryId = -1; m_mutex.unlock(); } } @@ -197,30 +227,30 @@ private: private: QMutex m_mutex; QWaitCondition m_condition; + QQueue m_jobs; bool m_quit; - bool m_restart; - bool m_abort; - QByteArray m_data; - QString m_query; - QString m_namespaces; + int m_abortQueryId; QString m_prefix; int m_size; - int m_queryId; - const QList *m_roleObjects; + int m_queryIds; QList > m_modelData; - QStringList m_keysValues; QList m_insertedItemRanges; QList m_removedItemRanges; }; +Q_GLOBAL_STATIC(QDeclarativeXmlQuery, globalXmlQuery) + void QDeclarativeXmlQuery::doQueryJob() { + Q_ASSERT(!m_jobs.isEmpty()); + XmlQueryJob &job = m_jobs.head(); + QString r; QXmlQuery query; - QBuffer buffer(&m_data); + QBuffer buffer(&job.data); buffer.open(QIODevice::ReadOnly); query.bindVariable(QLatin1String("src"), &buffer); - query.setQuery(m_namespaces + m_query); + query.setQuery(job.namespaces + job.query); query.evaluateTo(&r); //qDebug() << r; @@ -231,9 +261,9 @@ void QDeclarativeXmlQuery::doQueryJob() b.open(QIODevice::ReadOnly); //qDebug() << xml; - QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + m_namespaces; + QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + job.namespaces; QString prefix = QLatin1String("doc($inputDocument)/dummy:items") + - m_query.mid(m_query.lastIndexOf(QLatin1Char('/'))); + job.query.mid(job.query.lastIndexOf(QLatin1Char('/'))); //figure out how many items we are dealing with int count = -1; @@ -249,19 +279,18 @@ void QDeclarativeXmlQuery::doQueryJob() } //qDebug() << count; + job.data = xml; m_prefix = namespaces + prefix + QLatin1Char('/'); - m_data = xml; + m_size = 0; if (count > 0) m_size = count; } void QDeclarativeXmlQuery::getValuesOfKeyRoles(QStringList *values, QXmlQuery *query) const { - QStringList keysQueries; - for (int i=0; icount(); i++) { - if (m_roleObjects->at(i)->isKey()) - keysQueries << m_roleObjects->at(i)->query(); - } + Q_ASSERT(!m_jobs.isEmpty()); + + const QStringList &keysQueries = m_jobs.head().keyRoleQueries; QString keysQuery; if (keysQueries.count() == 1) keysQuery = m_prefix + keysQueries[0]; @@ -291,54 +320,58 @@ void QDeclarativeXmlQuery::addIndexToRangeList(QList * void QDeclarativeXmlQuery::doSubQueryJob() { + Q_ASSERT(!m_jobs.isEmpty()); + XmlQueryJob &job = m_jobs.head(); m_modelData.clear(); - QBuffer b(&m_data); + QBuffer b(&job.data); b.open(QIODevice::ReadOnly); QXmlQuery subquery; subquery.bindVariable(QLatin1String("inputDocument"), &b); - QStringList keysValues; - getValuesOfKeyRoles(&keysValues, &subquery); + QStringList keyRoleResults; + getValuesOfKeyRoles(&keyRoleResults, &subquery); // See if any values of key roles have been inserted or removed. + m_insertedItemRanges.clear(); m_removedItemRanges.clear(); - if (m_keysValues.isEmpty()) { + if (job.keyRoleResultsCache.isEmpty()) { m_insertedItemRanges << qMakePair(0, m_size); } else { - if (keysValues != m_keysValues) { + if (keyRoleResults != job.keyRoleResultsCache) { QStringList temp; - for (int i=0; isize(); ++i) { - QDeclarativeXmlListModelRole *role = m_roleObjects->at(i); - if (!role->isValid()) { + const QStringList &queries = job.roleQueries; + for (int i = 0; i < queries.size(); ++i) { + if (queries[i].isEmpty()) { QList resultList; for (int j = 0; j < m_size; ++j) resultList << QVariant(); m_modelData << resultList; continue; } - subquery.setQuery(m_prefix + QLatin1String("(let $v := ") + role->query() + QLatin1String(" return if ($v) then ") + role->query() + QLatin1String(" else \"\")")); + subquery.setQuery(m_prefix + QLatin1String("(let $v := ") + queries[i] + QLatin1String(" return if ($v) then ") + queries[i] + QLatin1String(" else \"\")")); QXmlResultItems resultItems; subquery.evaluateTo(&resultItems); QXmlItem item(resultItems.next()); @@ -404,8 +437,8 @@ public: QNetworkReply *reply; QDeclarativeXmlListModel::Status status; qreal progress; - QDeclarativeXmlQuery qmlXmlQuery; int queryId; + QStringList keyRoleResultsCache; QList roleObjects; static void append_role(QDeclarativeListProperty *list, QDeclarativeXmlListModelRole *role); static void clear_role(QDeclarativeListProperty *list); @@ -489,9 +522,8 @@ void QDeclarativeXmlListModelPrivate::clear_role(QDeclarativeListPropertyqmlXmlQuery, SIGNAL(queryCompleted(int,int)), - this, SLOT(queryCompleted(int,int))); + connect(globalXmlQuery(), SIGNAL(queryCompleted(QDeclarativeXmlQueryResult)), + this, SLOT(queryCompleted(QDeclarativeXmlQueryResult))); } QDeclarativeXmlListModel::~QDeclarativeXmlListModel() @@ -723,7 +755,7 @@ void QDeclarativeXmlListModel::reload() if (!d->isComponentComplete) return; - d->qmlXmlQuery.abort(); + globalXmlQuery()->abort(d->queryId); d->queryId = -1; int count = d->size; @@ -755,7 +787,7 @@ void QDeclarativeXmlListModel::reload() } if (!d->xml.isEmpty()) { - d->queryId = d->qmlXmlQuery.doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects); + d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache); d->progress = 1.0; d->status = Ready; emit progressChanged(d->progress); @@ -802,7 +834,7 @@ void QDeclarativeXmlListModel::requestFinished() } else { d->status = Ready; QByteArray data = d->reply->readAll(); - d->queryId = d->qmlXmlQuery.doQuery(d->query, d->namespaces, data, &d->roleObjects); + d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache); disconnect(d->reply, 0, this, 0); d->reply->deleteLater(); d->reply = 0; @@ -821,22 +853,20 @@ void QDeclarativeXmlListModel::requestProgress(qint64 received, qint64 total) } } -void QDeclarativeXmlListModel::queryCompleted(int id, int size) +void QDeclarativeXmlListModel::queryCompleted(const QDeclarativeXmlQueryResult &result) { Q_D(QDeclarativeXmlListModel); - if (id != d->queryId) + if (result.queryId != d->queryId) return; - bool sizeChanged = size != d->size; - d->size = size; - d->data = d->qmlXmlQuery.modelData(); - - QList removed = d->qmlXmlQuery.removedItemRanges(); - QList inserted = d->qmlXmlQuery.insertedItemRanges(); - - for (int i=0; isize; + d->size = result.size; + d->data = result.data; + d->keyRoleResultsCache = result.keyRoleResultsCache; + + for (int i=0; i #include +#include #include @@ -56,10 +57,18 @@ QT_BEGIN_NAMESPACE QT_MODULE(Declarative) class QDeclarativeContext; - class QDeclarativeXmlListModelRole; - class QDeclarativeXmlListModelPrivate; + +struct QDeclarativeXmlQueryResult { + int queryId; + int size; + QList > data; + QList > inserted; + QList > removed; + QStringList keyRoleResultsCache; +}; + class Q_DECLARATIVE_EXPORT QDeclarativeXmlListModel : public QListModelInterface, public QDeclarativeParserStatus { Q_OBJECT @@ -126,7 +135,7 @@ public Q_SLOTS: private Q_SLOTS: void requestFinished(); void requestProgress(qint64,qint64); - void queryCompleted(int,int); + void queryCompleted(const QDeclarativeXmlQueryResult &); private: Q_DECLARE_PRIVATE(QDeclarativeXmlListModel) diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp b/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp index 0e5e1b0..81cc922 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp +++ b/tests/auto/declarative/qdeclarativexmllistmodel/tst_qdeclarativexmllistmodel.cpp @@ -74,6 +74,8 @@ private slots: void useKeys_data(); void noKeysValueChanges(); void keysChanged(); + void threading(); + void threading_data(); void propertyChanges(); private: @@ -86,6 +88,8 @@ private: if (!data.isEmpty()) { QStringList items = data.split(";"); foreach(const QString &item, items) { + if (item.isEmpty()) + continue; QVariantList variants; xml += QLatin1String(""); QStringList fields = item.split(","); @@ -505,6 +509,63 @@ void tst_qdeclarativexmllistmodel::keysChanged() delete model; } +void tst_qdeclarativexmllistmodel::threading() +{ + QFETCH(int, xmlDataCount); + + QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/roleKeys.qml")); + + QDeclarativeXmlListModel *m1 = qobject_cast(component.create()); + QVERIFY(m1 != 0); + QDeclarativeXmlListModel *m2 = qobject_cast(component.create()); + QVERIFY(m2 != 0); + QDeclarativeXmlListModel *m3 = qobject_cast(component.create()); + QVERIFY(m3 != 0); + + for (int dataCount=0; dataCountsetXml(makeItemXmlAndData(data1)); + m2->setXml(makeItemXmlAndData(data2)); + m3->setXml(makeItemXmlAndData(data3)); + + QTRY_VERIFY(m1->count() == dataCount && m2->count() == dataCount && m3->count() == dataCount); + + for (int i=0; idata(i, m1->roles()[0]).toString(), QString("A" + QString::number(i))); + QCOMPARE(m1->data(i, m1->roles()[1]).toString(), QString("1" + QString::number(i))); + QCOMPARE(m1->data(i, m1->roles()[2]).toString(), QString("Football")); + + QCOMPARE(m2->data(i, m2->roles()[0]).toString(), QString("B" + QString::number(i))); + QCOMPARE(m2->data(i, m2->roles()[1]).toString(), QString("2" + QString::number(i))); + QCOMPARE(m2->data(i, m2->roles()[2]).toString(), QString("Athletics")); + + QCOMPARE(m3->data(i, m3->roles()[0]).toString(), QString("C" + QString::number(i))); + QCOMPARE(m3->data(i, m3->roles()[1]).toString(), QString("3" + QString::number(i))); + QCOMPARE(m3->data(i, m3->roles()[2]).toString(), QString("Curling")); + } + } + + delete m1; + delete m2; + delete m3; +} + +void tst_qdeclarativexmllistmodel::threading_data() +{ + QTest::addColumn("xmlDataCount"); + + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; + QTest::newRow("10") << 10; +} + void tst_qdeclarativexmllistmodel::propertyChanges() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/propertychanges.qml")); @@ -554,6 +615,8 @@ void tst_qdeclarativexmllistmodel::propertyChanges() QCOMPARE(model->query(), QString("/Pets")); QCOMPARE(model->namespaceDeclarations(), QString("declare namespace media=\"http://search.yahoo.com/mrss/\";")); + QTRY_VERIFY(model->count() == 1); + QCOMPARE(sourceSpy.count(),1); QCOMPARE(xmlSpy.count(),1); QCOMPARE(modelQuerySpy.count(),1); @@ -568,6 +631,9 @@ void tst_qdeclarativexmllistmodel::propertyChanges() QCOMPARE(xmlSpy.count(),1); QCOMPARE(modelQuerySpy.count(),1); QCOMPARE(namespaceDeclarationsSpy.count(),1); + + QTRY_VERIFY(model->count() == 1); + delete model; } QTEST_MAIN(tst_qdeclarativexmllistmodel) -- cgit v0.12