Index: test/asyncresultwaiter.cpp =================================================================== --- test/asyncresultwaiter.cpp (.../tags/soprano/2.1.1) (revision 0) +++ test/asyncresultwaiter.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -0,0 +1,65 @@ +/* + * This file is part of Soprano Project. + * + * Copyright (C) 2008 Sebastian Trueg <strueg@mandriva.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "asyncresultwaiter.h" +#include "asyncmodel.h" + +#include <QtCore/QEventLoop> +#include <QtCore/QVariant> + + +class Soprano::Util::AsyncResultWaiter::Private +{ +public: + QEventLoop loop; + QVariant value; +}; + + +Soprano::Util::AsyncResultWaiter::AsyncResultWaiter() + : d( new Private() ) +{ +} + + +Soprano::Util::AsyncResultWaiter::~AsyncResultWaiter() +{ + delete d; +} + + +void Soprano::Util::AsyncResultWaiter::slotResultReady( AsyncResult* result ) +{ + d->value = result->value(); + d->loop.exit(); +} + + +QVariant Soprano::Util::AsyncResultWaiter::waitForResult( AsyncResult* result ) +{ + AsyncResultWaiter waiter; + connect( result, SIGNAL( resultReady( Soprano::Util::AsyncResult* ) ), + &waiter, SLOT( slotResultReady( Soprano::Util::AsyncResult* ) ) ); + waiter.d->loop.exec(); + return waiter.d->value; +} + +#include "asyncresultwaiter.moc" Index: test/asyncmodeltest.cpp =================================================================== --- test/asyncmodeltest.cpp (.../tags/soprano/2.1.1) (revision 0) +++ test/asyncmodeltest.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -0,0 +1,226 @@ +/* + * This file is part of Soprano Project. + * + * Copyright (C) 2008 Sebastian Trueg <strueg@mandriva.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "asyncmodeltest.h" +#include "asyncresultwaiter.h" +#include "soprano.h" + +#include <QtCore/QPointer> +#include <QtCore/QUuid> + +#include <QtTest/QTest> + +using namespace Soprano; +using namespace Soprano::Util; + +Q_DECLARE_METATYPE( Soprano::Error::ErrorCode ) +Q_DECLARE_METATYPE( Soprano::StatementIterator ) +Q_DECLARE_METATYPE( Soprano::QueryResultIterator ) + + +static QUrl createRandomUri() +{ + // FIXME: check if the uri already exists + QString uid = QUuid::createUuid().toString(); + uid = uid.mid( 1, uid.length()-2 ); + return QUrl( "http://soprano.org/test#" + uid ); +} + + +static QList<Statement> createTestData( const Statement& s, int num ) +{ + QList<Statement> sl; + for( int i = 0; i < num; ++i ) { + sl.append( Statement( s.subject().isEmpty() ? Node( createRandomUri() ) : s.subject(), + s.predicate().isEmpty() ? Node( createRandomUri() ) : s.predicate(), + s.object().isEmpty() ? Node( createRandomUri() ) : s.object(), + s.context() ) ); + } + return sl; +} + + +void AsyncModelTest::initTestCase() +{ + Soprano::Model* model = Soprano::createModel(); + QVERIFY( model ); + m_asyncModel = new Soprano::Util::AsyncModel( model ); + m_asyncModel->setMode( Soprano::Util::AsyncModel::MultiThreaded ); + + + Node subject1( QUrl("http://soprano.sf.net#init:test1") ); + Node subject2( QUrl("http://soprano.sf.net#init:test2") ); + + Node predicate1( QUrl( "http://soprano.sf.net#predicate1" ) ); + Node predicate2( QUrl( "http://soprano.sf.net#predicate2" ) ); + + Node object1( LiteralValue( "Literal value1" ) ); + Node object2( LiteralValue( "Literal value2" ) ); + + m_s1 = Soprano::Statement( subject1, predicate1, object1 ); + m_s2 = Soprano::Statement( subject2, predicate2, object2 ); +} + + +void AsyncModelTest::testAddStatement() +{ + QVariant result = AsyncResultWaiter::waitForResult( m_asyncModel->addStatementAsync( m_s1 ) ); + QVERIFY( result.value<Soprano::Error::ErrorCode>() == Soprano::Error::ErrorNone ); + QVERIFY( m_asyncModel->containsStatement( m_s1 ) ); + + QVERIFY( AsyncResultWaiter::waitForResult( m_asyncModel->containsStatementAsync( m_s1 ) ).toBool() ); +} + + +void AsyncModelTest::testCountStatements() +{ + QVariant result = AsyncResultWaiter::waitForResult( m_asyncModel->statementCountAsync() ); + int c = result.toInt(); + if ( c != -1 ) { + QCOMPARE( c, 1 ); + } +} + + +void AsyncModelTest::testListStatements() +{ + m_asyncModel->removeAllStatements(); + m_asyncModel->addStatement( m_s1 ); + m_asyncModel->addStatement( m_s2 ); + + QVariant result = AsyncResultWaiter::waitForResult( m_asyncModel->listStatementsAsync() ); + QVERIFY( result.userType() == qMetaTypeId<Soprano::StatementIterator>() ); + Soprano::StatementIterator it = result.value<Soprano::StatementIterator>(); + QVERIFY( it.next() ); + QVERIFY( *it == m_s1 || *it == m_s2 ); + QVERIFY( it.next() ); + QVERIFY( *it == m_s1 || *it == m_s2 ); + QVERIFY( !it.next() ); +} + + +void AsyncModelTest::testSelectQuery() +{ + m_asyncModel->removeAllStatements(); + m_asyncModel->addStatement( m_s1 ); + m_asyncModel->addStatement( m_s2 ); + + QVariant result = AsyncResultWaiter::waitForResult( m_asyncModel->executeQueryAsync( "select ?r ?p ?o where { ?r ?p ?o . }", Query::QueryLanguageSparql ) ); + QVERIFY( result.userType() == qMetaTypeId<Soprano::QueryResultIterator>() ); + Soprano::QueryResultIterator it = result.value<Soprano::QueryResultIterator>(); + QVERIFY( !it.isGraph() ); + QVERIFY( !it.isBool() ); + QVERIFY( it.isBinding() ); + QVERIFY( it.next() ); + QCOMPARE( it.bindingCount(), 3 ); + QCOMPARE( it.bindingNames()[0], QString( "r" ) ); + QCOMPARE( it.bindingNames()[1], QString( "p" ) ); + QCOMPARE( it.bindingNames()[2], QString( "o" ) ); + QVERIFY( it.binding( 0 ) == m_s1.subject() || it.binding(0) == m_s2.subject() ); + QVERIFY( it.next() ); + QCOMPARE( it.bindingCount(), 3 ); + QVERIFY( it.binding(0) == m_s1.subject() || it.binding(0) == m_s2.subject() ); + QVERIFY( !it.next() ); +} + + +void AsyncModelTest::testConstructQuery() +{ + m_asyncModel->removeAllStatements(); + m_asyncModel->addStatement( m_s1 ); + m_asyncModel->addStatement( m_s2 ); + + QVariant result = AsyncResultWaiter::waitForResult( m_asyncModel->executeQueryAsync( "construct { ?r ?p ?o } where { ?r ?p ?o . }", Query::QueryLanguageSparql ) ); + QVERIFY( result.userType() == qMetaTypeId<Soprano::QueryResultIterator>() ); + Soprano::QueryResultIterator it = result.value<Soprano::QueryResultIterator>(); + QVERIFY( it.isGraph() ); + QVERIFY( !it.isBool() ); + QVERIFY( !it.isBinding() ); + QVERIFY( it.next() ); + QVERIFY( it.currentStatement() == m_s1 || it.currentStatement() == m_s2 ); + QVERIFY( it.next() ); + QVERIFY( it.currentStatement() == m_s1 || it.currentStatement() == m_s2 ); + QVERIFY( !it.next() ); +} + + +void AsyncModelTest::testAskQuery() +{ + m_asyncModel->removeAllStatements(); + m_asyncModel->addStatement( m_s1 ); + + QVariant result = AsyncResultWaiter::waitForResult( m_asyncModel->executeQueryAsync( "ask where { ?r ?p ?o . }", Query::QueryLanguageSparql ) ); + QVERIFY( result.userType() == qMetaTypeId<Soprano::QueryResultIterator>() ); + Soprano::QueryResultIterator it = result.value<Soprano::QueryResultIterator>(); + QVERIFY( it.isBool() ); + QVERIFY( !it.isGraph() ); + QVERIFY( !it.isBinding() ); + QVERIFY( it.boolValue() ); +} + + +void AsyncModelTest::testListAndAdd() +{ + // perform a list and then an add. The add should be postponed until after the list + Soprano::StatementIterator it = AsyncResultWaiter::waitForResult( m_asyncModel->listStatementsAsync() ).value<Soprano::StatementIterator>(); + QPointer<AsyncResult> addResult = m_asyncModel->addStatementAsync( m_s2 ); + QVERIFY( it.next() ); + it.close(); + QVERIFY( addResult ); + QVERIFY( AsyncResultWaiter::waitForResult( addResult ).value<Soprano::Error::ErrorCode>() == Soprano::Error::ErrorNone ); +} + + +void AsyncModelTest::testMultiAdd() +{ + m_asyncModel->removeAllStatements(); + + // start a bunch of commands... + QList<QPointer<AsyncResult> > results; + QList<Statement> data = createTestData( Statement(), 20 ); + foreach( const Statement& s, data ) { + results.append( m_asyncModel->addStatementAsync( s ) ); + } + + // ...wait for them... + foreach( QPointer<AsyncResult> r, results ) { + qApp->processEvents(); + if ( r ) + AsyncResultWaiter::waitForResult( r ); + } + + // ...and then check for all the statements + foreach( const Statement& s, data ) { + QVERIFY( m_asyncModel->containsStatement( s ) ); + } + + + Soprano::StatementIterator it = AsyncResultWaiter::waitForResult( m_asyncModel->listStatementsAsync() ).value<Soprano::StatementIterator>(); + int cnt = 0; + while ( it.next() ) + ++cnt; + QCOMPARE( cnt, data.count() ); +} + + +QTEST_MAIN( AsyncModelTest ) + +#include "asyncmodeltest.moc" Index: test/asyncresultwaiter.h =================================================================== --- test/asyncresultwaiter.h (.../tags/soprano/2.1.1) (revision 0) +++ test/asyncresultwaiter.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -0,0 +1,54 @@ +/* + * This file is part of Soprano Project. + * + * Copyright (C) 2008 Sebastian Trueg <strueg@mandriva.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _SOPRANO_ASYNC_RESULT_WAITER_H_ +#define _SOPRANO_ASYNC_RESULT_WAITER_H_ + +#include <QtCore/QObject> + +class QVariant; + +namespace Soprano { + namespace Util { + + class AsyncResult; + + class AsyncResultWaiter : public QObject + { + Q_OBJECT + + public: + ~AsyncResultWaiter(); + + static QVariant waitForResult( AsyncResult* result ); + + private Q_SLOTS: + void slotResultReady( Soprano::Util::AsyncResult* ); + + private: + AsyncResultWaiter(); + + class Private; + Private* const d; + }; + } +} +#endif Index: test/asyncmodeltest.h =================================================================== --- test/asyncmodeltest.h (.../tags/soprano/2.1.1) (revision 0) +++ test/asyncmodeltest.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -0,0 +1,52 @@ +/* + * This file is part of Soprano Project. + * + * Copyright (C) 2008 Sebastian Trueg <strueg@mandriva.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SOPRANO_ASYNC_MODEL_TEST_H +#define SOPRANO_ASYNC_MODEL_TEST_H + +#include <QtCore/QObject> + +#include "asyncmodel.h" +#include "statement.h" + + +class AsyncModelTest: public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testAddStatement(); + void testCountStatements(); + void testListStatements(); + void testSelectQuery(); + void testConstructQuery(); + void testAskQuery(); + void testListAndAdd(); + void testMultiAdd(); + +private: + Soprano::Util::AsyncModel* m_asyncModel; + Soprano::Statement m_s1; + Soprano::Statement m_s2; +}; + +#endif Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt (.../tags/soprano/2.1.1) (revision 846898) +++ test/CMakeLists.txt (.../trunk/kdesupport/soprano) (revision 846898) @@ -101,7 +101,7 @@ #target_link_libraries(queryapitest soprano ${QT_QTCORE_LIBRARY} ${QT_QTTEST_LIBRARY}) #add_test(queryapitest ${EXECUTABLE_OUTPUT_PATH}/queryapitest) -if(JNI_1_6_FOUND) +if(JNI_1_4_FOUND) # Sesame2 model tests qt4_automoc(sesame2backendtest sesame2backendtest.cpp) add_executable(sesame2backendtest sesame2backendtest.cpp) @@ -112,7 +112,7 @@ add_executable(sesame2multithreadtest sesame2multithreadtest.cpp) target_link_libraries(sesame2multithreadtest soprano multithreadingtest ${QT_QTCORE_LIBRARY} ${QT_QTTEST_LIBRARY}) add_test(sesame2multithreadtest ${EXECUTABLE_OUTPUT_PATH}/sesame2multithreadtest) -endif(JNI_1_6_FOUND) +endif(JNI_1_4_FOUND) # parser test qt4_automoc(parsertest parsertest.cpp) @@ -191,3 +191,9 @@ qt4_automoc(serveroperatortest.cpp) add_executable(serveroperatortest serveroperatortest.cpp ../server/datastream.cpp) target_link_libraries(serveroperatortest soprano ${QT_QTCORE_LIBRARY} ${QT_QTTEST_LIBRARY}) + +# async model test +set(asyncmodeltest_SRC asyncresultwaiter.cpp asyncmodeltest.cpp) +qt4_automoc(${asyncmodeltest_SRC}) +add_executable(asyncmodeltest ${asyncmodeltest_SRC}) +target_link_libraries(asyncmodeltest soprano ${QT_QTCORE_LIBRARY} ${QT_QTTEST_LIBRARY}) Index: test/parsertest.cpp =================================================================== --- test/parsertest.cpp (.../tags/soprano/2.1.1) (revision 846898) +++ test/parsertest.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -84,6 +84,14 @@ QVERIFY( all.contains( s ) ); } + QFile file( filename ); + file.open( QIODevice::ReadOnly ); + QByteArray data = file.readAll(); + QTextStream bs( data ); + it = parser->parseStream( bs, QUrl("http://soprano.sf.net/testdata/"), serialization ); + all = it.allStatements(); + QCOMPARE( all.count(), testStatements.count() ); + // test serialization // it = SimpleStatementIterator( testStatements ); // QString serializedData; Index: soprano/iterator.h =================================================================== --- soprano/iterator.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/iterator.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -65,8 +65,10 @@ * This can either be achieved by deleting the iterator, finishing it (next() does return \p false), * or calling close(). Before that other operations on the Model may block. * - * Iterators are not thread-safe. Two threads using the same iterator at the same time may result - * in undefined behaviour and even crashes. + * Iterators are not thread-safe. Two threads using the same iterator may result + * in undefined behaviour and even crashes. An iterator needs to be closed by the + * same thread that opened it (except if the iterator contains special code to handle such + * a situation.) * * \warning Be aware that iterators in Soprano are shared objects which means * that copies of one iterator object work on the same data. Index: soprano/global.cpp =================================================================== --- soprano/global.cpp (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/global.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -58,7 +58,7 @@ } -Soprano::Model* Soprano::createModel( const QList<BackendSetting>& settings ) +Soprano::Model* Soprano::createModel( const BackendSettings& settings ) { initSoprano(); Index: soprano/filtermodel.h =================================================================== --- soprano/filtermodel.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/filtermodel.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -178,6 +178,13 @@ virtual Node createBlankNode(); //@} + using Model::addStatement; + using Model::removeStatement; + using Model::removeAllStatements; + using Model::listStatements; + using Model::containsStatement; + using Model::containsAnyStatement; + protected: /** * Create an empty filter model. Index: soprano/queryresultiterator.h =================================================================== --- soprano/queryresultiterator.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/queryresultiterator.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -93,8 +93,10 @@ * This can either be achieved by deleting the iterator, finishing it (next() does return \p false), * or calling close(). Before that other operations on the Model may block. * - * Iterators are not thread-safe. Two threads using the same iterator at the same time may result - * in undefined behaviour and even crashes. + * Iterators are not thread-safe. Two threads using the same iterator may result + * in undefined behaviour and even crashes. An iterator needs to be closed by the + * same thread that opened it (except if the iterator contains special code to handle such + * a situation.) * * \warning Be aware that iterators in Soprano are shared objects which means * that copies of one iterator object work on the same data. Index: soprano/nodeiterator.h =================================================================== --- soprano/nodeiterator.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/nodeiterator.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -57,8 +57,10 @@ * This can either be achieved by deleting the iterator, finishing it (next() does return \p false), * or calling close(). Before that other operations on the Model may block. * - * Iterators are not thread-safe. Two threads using the same iterator at the same time may result - * in undefined behaviour and even crashes. + * Iterators are not thread-safe. Two threads using the same iterator may result + * in undefined behaviour and even crashes. An iterator needs to be closed by the + * same thread that opened it (except if the iterator contains special code to handle such + * a situation.) * * \warning Be aware that iterators in Soprano are shared objects which means * that copies of one iterator object work on the same data. Index: soprano/global.h =================================================================== --- soprano/global.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/global.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -70,9 +70,9 @@ * should never ignore settings but rather return 0 if an option is not supported. Backends can, * however, define their own default settings. * - * \sa Model, Backend::createModel + * \sa Model, Backend::createModel, BackendSetting */ - SOPRANO_EXPORT Model* createModel( const QList<BackendSetting>& settings = QList<BackendSetting>() ); + SOPRANO_EXPORT Model* createModel( const BackendSettings& settings = BackendSettings() ); } #endif // SOPRANO_H Index: soprano/statementiterator.h =================================================================== --- soprano/statementiterator.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/statementiterator.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -60,8 +60,10 @@ * This can either be achieved by deleting the iterator, finishing it (next() does return \p false), * or calling close(). Before that other operations on the Model may block. * - * Iterators are not thread-safe. Two threads using the same iterator at the same time may result - * in undefined behaviour and even crashes. + * Iterators are not thread-safe. Two threads using the same iterator may result + * in undefined behaviour and even crashes. An iterator needs to be closed by the + * same thread that opened it (except if the iterator contains special code to handle such + * a situation.) * * \warning Be aware that iterators in Soprano are shared objects which means * that copies of one iterator object work on the same data. Index: soprano/util/asyncmodel.cpp =================================================================== --- soprano/util/asyncmodel.cpp (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/util/asyncmodel.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -32,15 +32,36 @@ #include "queryresultiterator.h" #include <QtCore/QTimer> +#include <QtCore/QThreadPool> -void Soprano::Util::AsyncModelPrivate::addIterator( AsyncIteratorBase* it ) +Q_DECLARE_METATYPE( Soprano::Statement ) + + +Soprano::Util::AsyncModelPrivate::AsyncModelPrivate( AsyncModel* parent ) + : mode( AsyncModel::SingleThreaded ), + m_model( parent ) { +} + + +Soprano::Util::AsyncModelPrivate::~AsyncModelPrivate() +{ + foreach( AsyncIteratorHandle* it, openIterators ) { + it->setModelGone(); + } +} + + +// only called in SingleThreaded mode +void Soprano::Util::AsyncModelPrivate::addIterator( AsyncIteratorHandle* it ) +{ openIterators.append( it ); } -void Soprano::Util::AsyncModelPrivate::removeIterator( AsyncIteratorBase* it ) +// only called in SingleThreaded mode +void Soprano::Util::AsyncModelPrivate::removeIterator( AsyncIteratorHandle* it ) { Q_ASSERT( openIterators.contains( it ) ); openIterators.removeAll( it ); @@ -50,14 +71,21 @@ void Soprano::Util::AsyncModelPrivate::enqueueCommand( Command* command ) { - commandQueue.append( command ); + if ( mode == AsyncModel::SingleThreaded ) { + commandQueue.append( command ); - // it is important to not call _s_executeNextCommand directly to let - // the client the time to connect to the resultReady signal - QTimer::singleShot( 0, m_model, SLOT( _s_executeNextCommand() ) ); + // it is important to not call _s_executeNextCommand directly to let + // the client the time to connect to the resultReady signal + QTimer::singleShot( 0, m_model, SLOT( _s_executeNextCommand() ) ); + } + else { + // the underlying model is thread-safe + QThreadPool::globalInstance()->start( command ); + } } +// only called in SingleThreaded mode void Soprano::Util::AsyncModelPrivate::_s_executeNextCommand() { for ( QLinkedList<Command*>::iterator it = commandQueue.begin(); @@ -65,7 +93,7 @@ Command* c = *it; if ( openIterators.isEmpty() || c->type() == Command::ReadCommand ) { - c->execute( m_model ); + c->execute(); commandQueue.erase( it ); delete c; @@ -83,6 +111,8 @@ : FilterModel( parent ), d( new AsyncModelPrivate( this ) ) { + // Model signals are delivered queued in MultiThreaded mode + qRegisterMetaType<Soprano::Statement>(); } @@ -93,18 +123,54 @@ } +void Soprano::Util::AsyncModel::setMode( AsyncModelMode mode ) +{ + d->mode = mode; +} + + +Soprano::Util::AsyncModel::AsyncModelMode Soprano::Util::AsyncModel::mode() const +{ + return d->mode; +} + + Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::addStatementAsync( const Statement& statement ) { + return addStatementsAsync( QList<Statement>() << statement ); +} + + +Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::addStatementAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context ) +{ + return addStatementAsync( Statement( subject, predicate, object, context ) ); +} + + +Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::addStatementsAsync( const QList<Statement>& statements ) +{ AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new AddStatementCommand( result, statement ) ); + d->enqueueCommand( new AddStatementCommand( result, this, statements ) ); return result; } Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::removeStatementAsync( const Statement& statement ) { + return removeStatementsAsync( QList<Statement>() << statement ); +} + + +Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::removeStatementAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context ) +{ + return removeStatementAsync( Statement( subject, predicate, object, context ) ); +} + + +Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::removeStatementsAsync( const QList<Statement>& statements ) +{ AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new RemoveStatementCommand( result, statement ) ); + d->enqueueCommand( new RemoveStatementCommand( result, this, statements ) ); return result; } @@ -112,15 +178,21 @@ Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::removeAllStatementsAsync( const Statement& statement ) { AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new RemoveAllStatementsCommand( result, statement ) ); + d->enqueueCommand( new RemoveAllStatementsCommand( result, this, statement ) ); return result; } +Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::removeAllStatementsAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context ) +{ + return removeAllStatementsAsync( Statement( subject, predicate, object, context ) ); +} + + Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::isEmptyAsync() const { AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new IsEmptyCommand( result ) ); + d->enqueueCommand( new IsEmptyCommand( result, const_cast<AsyncModel*>(this) ) ); return result; } @@ -128,7 +200,7 @@ Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::statementCountAsync() const { AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new StatementCountCommand( result ) ); + d->enqueueCommand( new StatementCountCommand( result, const_cast<AsyncModel*>(this) ) ); return result; } @@ -136,15 +208,27 @@ Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::listStatementsAsync( const Statement& statement ) const { AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new ListStatementsCommand( d, result, statement ) ); + d->enqueueCommand( new ListStatementsCommand( d, result, const_cast<AsyncModel*>(this), statement ) ); return result; } +Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::listStatementsAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context ) const +{ + return listStatementsAsync( Statement( subject, predicate, object, context ) ); +} + + +Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::listStatementsAsync() const +{ + return listStatementsAsync( Statement() ); +} + + Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::listContextsAsync() const { AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new ListContextsCommand( d, result ) ); + d->enqueueCommand( new ListContextsCommand( d, result, const_cast<AsyncModel*>(this) ) ); return result; } @@ -154,7 +238,7 @@ const QString& userQueryLanguage ) const { AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new ExecuteQueryCommand( d, result, query, language, userQueryLanguage ) ); + d->enqueueCommand( new ExecuteQueryCommand( d, result, const_cast<AsyncModel*>(this), query, language, userQueryLanguage ) ); return result; } @@ -162,23 +246,35 @@ Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::containsStatementAsync( const Statement& statement ) const { AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new ContainsStatementCommand( result, statement ) ); + d->enqueueCommand( new ContainsStatementCommand( result, const_cast<AsyncModel*>(this), statement ) ); return result; } +Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::containsStatementAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context ) const +{ + return containsStatementAsync( Statement( subject, predicate, object, context ) ); +} + + Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::containsAnyStatementAsync( const Statement& statement ) const { AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new ContainsAnyStatementCommand( result, statement ) ); + d->enqueueCommand( new ContainsAnyStatementCommand( result, const_cast<AsyncModel*>(this), statement ) ); return result; } +Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::containsAnyStatementAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context ) const +{ + return containsAnyStatementAsync( Statement( subject, predicate, object, context ) ); +} + + Soprano::Util::AsyncResult* Soprano::Util::AsyncModel::createBlankNodeAsync() { AsyncResult* result = new AsyncResult(); - d->enqueueCommand( new CreateBlankNodeCommand( result ) ); + d->enqueueCommand( new CreateBlankNodeCommand( result, this ) ); return result; } Index: soprano/util/asynciteratorbackend.cpp =================================================================== --- soprano/util/asynciteratorbackend.cpp (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/util/asynciteratorbackend.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -22,29 +22,33 @@ #include "asynciteratorbackend.h" #include "asyncmodel_p.h" -Soprano::Util::AsyncIteratorBase::AsyncIteratorBase( AsyncModelPrivate* d ) + +Soprano::Util::AsyncIteratorHandle::AsyncIteratorHandle( AsyncModelPrivate* d ) : m_asyncModelPrivate( d ) { - m_asyncModelPrivate->addIterator( this ); + if ( d->mode == AsyncModel::SingleThreaded ) { + m_asyncModelPrivate->addIterator( this ); + } } -Soprano::Util::AsyncIteratorBase::~AsyncIteratorBase() +Soprano::Util::AsyncIteratorHandle::~AsyncIteratorHandle() { remove(); } -void Soprano::Util::AsyncIteratorBase::setModelGone() +void Soprano::Util::AsyncIteratorHandle::setModelGone() { m_asyncModelPrivate = 0; } -void Soprano::Util::AsyncIteratorBase::remove() +void Soprano::Util::AsyncIteratorHandle::remove() { - if( m_asyncModelPrivate ) { - m_asyncModelPrivate->removeIterator( this ); + if ( m_asyncModelPrivate ) { + if ( m_asyncModelPrivate->mode == AsyncModel::SingleThreaded ) + m_asyncModelPrivate->removeIterator( this ); m_asyncModelPrivate = 0; } } Index: soprano/util/asyncmodel.h =================================================================== --- soprano/util/asyncmodel.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/util/asyncmodel.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -23,6 +23,8 @@ #define _SOPRANO_ASYNC_MODEL_H_ #include "filtermodel.h" +#include "asyncresult.h" // backwards comp when AsyncResult was defined in this header + #include "soprano_export.h" @@ -36,67 +38,6 @@ namespace Util { - class AsyncModel; - - /** - * \class AsyncResult asyncmodel.h Soprano/Util/AsyncResult - * - * \brief A delayed result as returned by AsyncModel. - * - * \author Sebastian Trueg <trueg@kde.org> - * - * \since 2.1 - */ - class SOPRANO_EXPORT AsyncResult : public QObject, public Error::ErrorCache - { - Q_OBJECT - - Q_SIGNALS: - /** - * Emitted once the async operation is completed - * and the result can be read. - * - * The result will delete itself. - */ - void resultReady( Soprano::Util::AsyncResult* ); - - public: - /** - * The result of the async operation. Its type is dependent - * on the operation (for example Error::ErrorCode for - * AsyncModel::addStatementAsync or StatementIterator for - * AsyncModel::listStatementsAsync). Types may need to be - * registered with Q_DECLARE_METATYPE. - * - * Use Error::ErrorCache::lastError() to check - * for error details. - * - * This value is not ready before resultReady() - * as been emitted. - */ - QVariant value() const { return m_result; } - - /** \cond internal_asyncresult_members */ - /** - * Internal method. Do not call. - */ - void setResult( const QVariant& result, const Error::Error& error ) { - m_result = result; - setError( error ); - emit resultReady( this ); - deleteLater(); - } - /** \endcond */ - - private: - AsyncResult() - : QObject( 0 ) {} - - QVariant m_result; - - friend class AsyncModel; - }; - class AsyncModelPrivate; /** @@ -105,9 +46,13 @@ * \brief Filter model that allows to perform operations * asyncroneously. * - * The main purpose is to protect a Model against deadlocks - * in a single threaded situation. + * AsyncModel has two modes: AsyncModel::SingleThreaded and AsyncModel::MultiThreaded. + * The main purpose of the AsyncModel::SingleThreaded mode is to protect a + * Model against deadlocks in a single threaded situation. * + * AsyncModel::MultiThreaded mode provides real asyncroneous execution of + * Model commands. + * * Usage: * \code * AsyncResult* result = model->listStatementsAsync( s ); @@ -137,6 +82,46 @@ ~AsyncModel(); /** + * The mode of the model, single vs. multi threaded execution. + * + * \since 2.2 + */ + enum AsyncModelMode { + /** + * The model uses a single thread. Thus, commands are executed in the + * same thread but no two commands will ever block each other. + * This is the default mode for historical reasons. + */ + SingleThreaded, + + /** + * The model uses multiple threads through QThreadPool. + * Commands are executed in parallel. + * Be aware that the parent model needs to be thread-safe. + */ + MultiThreaded + }; + + /** + * Set the mode to be used. For historical reasons the default mode is + * SingleThreaded. + * + * \sa mode + * + * \since 2.2 + */ + void setMode( AsyncModelMode mode ); + + /** + * The mode used by this model. + * + * \sa setMode + * + * \since 2.2 + */ + AsyncModelMode mode() const; + + /** * Asyncroneously add the Statement to the Model. * * \param statement The Statement to add. @@ -149,6 +134,20 @@ AsyncResult* addStatementAsync( const Statement& statement ); /** + * \overload + * + * \since 2.2 + */ + AsyncResult* addStatementAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context = Node() ); + + /** + * \overload + * + * \since 2.2 + */ + AsyncResult* addStatementsAsync( const QList<Statement>& statements ); + + /** * Asyncroneously remove one statement. For removing statements * with wildward matching see removeAllStatementsAsync(). * @@ -163,6 +162,20 @@ AsyncResult* removeStatementAsync( const Statement& statement ); /** + * \overload + * + * \since 2.2 + */ + AsyncResult* removeStatementAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context = Node() ); + + /** + * \overload + * + * \since 2.2 + */ + AsyncResult* removeStatementsAsync( const QList<Statement>& statements ); + + /** * Asyncroneously remove all statements that match the partial statement. * For removing one specific statement see removeStatement(). * @@ -177,6 +190,13 @@ AsyncResult* removeAllStatementsAsync( const Statement& statement ); /** + * \overload + * + * \since 2.2 + */ + AsyncResult* removeAllStatementsAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context = Node() ); + + /** * Asyncroneously check if the Model does contain any Statement. * * \sa isEmpty @@ -210,6 +230,22 @@ AsyncResult* listStatementsAsync( const Statement& statement ) const; /** + * \overload + * + * \since 2.2 + */ + AsyncResult* listStatementsAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context = Node() ) const; + + /** + * \overload + * + * Lists all statements in the Model asyncroneously. + * + * \since 2.2 + */ + AsyncResult* listStatementsAsync() const; + + /** * Asyncroneously list all contexts in the model, i.e. all named graphs. * * \sa listContexts @@ -256,6 +292,13 @@ AsyncResult* containsStatementAsync( const Statement& statement ) const; /** + * \overload + * + * \since 2.2 + */ + AsyncResult* containsStatementAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context = Node() ) const; + + /** * Asyncroneously check if the model contains certain statements. * * \param statement A partially defined statement that serves as @@ -269,6 +312,13 @@ AsyncResult* containsAnyStatementAsync( const Statement& statement ) const; /** + * \overload + * + * \since 2.2 + */ + AsyncResult* containsAnyStatementAsync( const Node& subject, const Node& predicate, const Node& object, const Node& context = Node() ) const; + + /** * Asyncroneously create a new blank node with a unique identifier. * * \sa createBlankNode @@ -278,6 +328,13 @@ */ AsyncResult* createBlankNodeAsync(); + using FilterModel::addStatement; + using FilterModel::removeStatement; + using FilterModel::removeAllStatements; + using FilterModel::listStatements; + using FilterModel::containsStatement; + using FilterModel::containsAnyStatement; + private: AsyncModelPrivate* const d; Index: soprano/util/asynciteratorbackend.h =================================================================== --- soprano/util/asynciteratorbackend.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/util/asynciteratorbackend.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -24,55 +24,243 @@ #include "iteratorbackend.h" #include "iterator.h" +#include "asyncmodel_p.h" +#include <QtCore/QQueue> +#include <QtCore/QMutex> +#include <QtCore/QMutexLocker> +#include <QtCore/QWaitCondition> +#include <QtCore/QDebug> + +namespace { + const int s_cacheSize = 10; +} + namespace Soprano { namespace Util { - - class AsyncModelPrivate; - - class AsyncIteratorBase + /** + * The sole purpose of this class is to provide a single + * interface to all interators for the AsyncModel + */ + class AsyncIteratorHandle { public: - AsyncIteratorBase( AsyncModelPrivate* d ); - virtual ~AsyncIteratorBase(); + AsyncIteratorHandle( AsyncModelPrivate* d ); + virtual ~AsyncIteratorHandle(); + + /** + * Simply set m_asyncModelPrivate to 0. used + * by the model in single thread mode. + */ void setModelGone(); protected: + /** + * remove this iterator, ie. close it and remove it from the + * model in single threaded mode + */ void remove(); + AsyncModelPrivate* modelPrivate() const { return m_asyncModelPrivate; } + private: AsyncModelPrivate* m_asyncModelPrivate; }; - template<typename T> class AsyncIteratorBackend : public IteratorBackend<T>, public AsyncIteratorBase + /** + * Base class for the iterators which does the communications between the + * main thread and the work thread to make sure iterators are only used + * in the thread that created them. + * + * For that results are cached in a queue and communicated via the m_current + * variable. + */ + template<typename T> class AsyncIteratorBase : public AsyncIteratorHandle { public: + AsyncIteratorBase( AsyncModelPrivate* d, const Iterator<T>& it ) + : AsyncIteratorHandle( d ), + m_iterator( it ) { + } + + virtual ~AsyncIteratorBase() {} + + virtual void initWorkThread() {} + + // called in work thread by the AsyncCommand + void iterate() { + //qDebug() << "iterate"; + m_atEnd = false; + while( !m_atEnd ) { + // wait for cache space to become avilable + m_mutex.lock(); + if ( cacheFillState() >= s_cacheSize ) { + //qDebug() << "waiting for cache space"; + m_iterateWc.wait( &m_mutex ); + } + m_mutex.unlock(); + + // check if we have more data and if so, cache it + bool haveNext = false; + while ( m_iterator.next() ) { + QMutexLocker lock( &m_mutex ); + //qDebug() << "iterate: filling cache"; + enqueueCurrent(); + if( m_iterator.lastError() ) { + //qDebug() << "iterate: error"; + m_error = m_iterator.lastError(); + haveNext = false; + break; + } + if ( cacheFillState() >= s_cacheSize ) { + haveNext = true; + break; + } + } + + QMutexLocker lock( &m_mutex ); + m_atEnd = !haveNext; + + // tell the waiting thread that data is available + //qDebug() << "iterate: waking consumer"; + m_nextWc.wakeAll(); + } + + // close the iterator + m_iterator.close(); + } + + protected: + // called in main thread by IteratorBackend::next + bool getNext() { + //qDebug() << "getNext"; + if( modelPrivate() ) { + if( modelPrivate()->mode == AsyncModel::MultiThreaded ) { + m_mutex.lock(); + + // wait for data to become available + if ( !cacheFillState() && !m_atEnd ) { + //qDebug() << "getNext: Waiting for data"; + m_iterateWc.wakeAll(); + m_nextWc.wait( &m_mutex ); + } + + if( m_error ) { + //qDebug() << "getNext: error"; + m_mutex.unlock(); + return false; + } + + // get the new data + else if ( cacheFillState() != 0 ) { + //qDebug() << "getNext: got data"; + dequeueFirst(); + m_mutex.unlock(); + return true; + } + else { + //qDebug() << "getNext: got no data"; + m_mutex.unlock(); + return false; + } + } + else { + return m_iterator.next(); + } + } + else { + return false; + } + } + + void closeIterator() { + //qDebug() << "closeIterator"; + if( modelPrivate() ) { + if( modelPrivate()->mode == AsyncModel::MultiThreaded ) { + stopIterating(); + } + else { + m_iterator.close(); + remove(); + } + } + } + + // called in the main thread by IteratorBackend::close + void stopIterating() { + //qDebug() << "stopIterating"; + QMutexLocker lock( &m_mutex ); + m_atEnd = true; + m_iterateWc.wakeAll(); + } + + // called in the main thread by IteratorBackend::current + T getCurrent() const { + if( modelPrivate() ) { + if( modelPrivate()->mode == AsyncModel::MultiThreaded ) + return m_current; + else + return m_iterator.current(); + } + else { + return T(); + } + } + + virtual int cacheFillState() const { + return m_cache.size(); + } + + virtual void enqueueCurrent() { + m_cache.enqueue( m_iterator.current() ); + } + + virtual void dequeueFirst() { + m_current = m_cache.dequeue(); + } + + Iterator<T> m_iterator; + Error::Error m_error; + + private: + bool m_atEnd; + QQueue<T> m_cache; + T m_current; + + QMutex m_mutex; + QWaitCondition m_nextWc; + QWaitCondition m_iterateWc; + }; + + + template<typename T> class AsyncIteratorBackend : public AsyncIteratorBase<T>, public IteratorBackend<T> + { + public: AsyncIteratorBackend( AsyncModelPrivate* d, const Iterator<T>& it ) - : AsyncIteratorBase( d ), - m_iterator( it ) { + : AsyncIteratorBase<T>( d, it ) { } bool next() { - return m_iterator.next(); + return AsyncIteratorBase<T>::getNext(); } T current() const { - return m_iterator.current(); + return AsyncIteratorBase<T>::getCurrent(); } void close() { - m_iterator.close(); - remove(); + AsyncIteratorBase<T>::closeIterator(); } Error::Error lastError() const { - return m_iterator.lastError(); + if( AsyncIteratorHandle::modelPrivate() && + AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return AsyncIteratorBase<T>::m_iterator.lastError(); + else + return AsyncIteratorBase<T>::m_error; } - - private: - Iterator<T> m_iterator; }; } } Index: soprano/util/asyncresult.cpp =================================================================== --- soprano/util/asyncresult.cpp (.../tags/soprano/2.1.1) (revision 0) +++ soprano/util/asyncresult.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -0,0 +1,79 @@ +/* + * This file is part of Soprano Project. + * + * Copyright (C) 2008 Sebastian Trueg <trueg@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "asyncresult.h" +#include "node.h" +#include "nodeiterator.h" +#include "statementiterator.h" +#include "queryresultiterator.h" + + +Q_DECLARE_METATYPE( Soprano::Error::ErrorCode ) +Q_DECLARE_METATYPE( Soprano::Node ) +Q_DECLARE_METATYPE( Soprano::StatementIterator ) +Q_DECLARE_METATYPE( Soprano::NodeIterator ) +Q_DECLARE_METATYPE( Soprano::QueryResultIterator ) + + +Soprano::Util::AsyncResult::AsyncResult() + : QObject( 0 ) { + // a little hack to ensure that results are delivered in the calling thread + connect( this,SIGNAL(emitReady()), + SLOT(slotEmitReady()), + Qt::QueuedConnection ); +} + + +Soprano::Util::AsyncResult::~AsyncResult() +{ +} + + +Soprano::Error::ErrorCode Soprano::Util::AsyncResult::errorCode() const +{ + return value().value<Error::ErrorCode>(); +} + + +Soprano::StatementIterator Soprano::Util::AsyncResult::statementIterator() const +{ + return value().value<StatementIterator>(); +} + + +Soprano::NodeIterator Soprano::Util::AsyncResult::nodeIterator() const +{ + return value().value<NodeIterator>(); +} + + +Soprano::QueryResultIterator Soprano::Util::AsyncResult::queryResultIterator() const +{ + return value().value<QueryResultIterator>(); +} + + +Soprano::Node Soprano::Util::AsyncResult::node() const +{ + return value().value<Node>(); +} + +#include "asyncresult.moc" Index: soprano/util/asynccommand.cpp =================================================================== --- soprano/util/asynccommand.cpp (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/util/asynccommand.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -36,8 +36,10 @@ Q_DECLARE_METATYPE(Soprano::NodeIterator) Q_DECLARE_METATYPE(Soprano::QueryResultIterator) -Soprano::Util::Command::Command( Type type ) - : m_type( type ) +Soprano::Util::Command::Command( AsyncResult* result, Model* model, Type type ) + : m_result( result ), + m_model( model ), + m_type( type ) { } @@ -48,159 +50,162 @@ -Soprano::Util::StatementCountCommand::StatementCountCommand( AsyncResult* result ) - : Command( ReadCommand ), - m_result( result ) +Soprano::Util::StatementCountCommand::StatementCountCommand( AsyncResult* result, Model* model ) + : Command( result, model, ReadCommand ) { } -void Soprano::Util::StatementCountCommand::execute( Model* model ) +void Soprano::Util::StatementCountCommand::execute() { - int cnt = model->statementCount(); - m_result->setResult( cnt, model->lastError() ); + int cnt = model()->statementCount(); + result()->setResult( cnt, model()->lastError() ); } -Soprano::Util::IsEmptyCommand::IsEmptyCommand( AsyncResult* result ) - : Command( ReadCommand ), - m_result( result ) +Soprano::Util::IsEmptyCommand::IsEmptyCommand( AsyncResult* result, Model* model ) + : Command( result, model, ReadCommand ) { } -void Soprano::Util::IsEmptyCommand::execute( Model* model ) +void Soprano::Util::IsEmptyCommand::execute() { - bool r = model->isEmpty(); - m_result->setResult( r, model->lastError() ); + bool r = model()->isEmpty(); + result()->setResult( r, model()->lastError() ); } -Soprano::Util::StatementCommand::StatementCommand( const Statement& s, Type type ) - : Command( type ), - m_statement( s ) +Soprano::Util::StatementCommand::StatementCommand( AsyncResult* result, Model* model, const Statement& s, Type type ) + : Command( result, model, type ) { + m_statements << s; } -Soprano::Util::AddStatementCommand::AddStatementCommand( AsyncResult* result, const Statement& statement ) - : StatementCommand( statement, WriteCommand ), - m_result( result ) +Soprano::Util::StatementCommand::StatementCommand( AsyncResult* result, Model* model, const QList<Statement>& s, Type type ) + : Command( result, model, type ), + m_statements( s ) { } -void Soprano::Util::AddStatementCommand::execute( Model* model ) +Soprano::Util::AddStatementCommand::AddStatementCommand( AsyncResult* result, Model* model, const QList<Statement>& statements ) + : StatementCommand( result, model, statements, WriteCommand ) { - Error::ErrorCode r = model->addStatement( statement() ); - m_result->setResult( QVariant::fromValue( r ), model->lastError() ); } -Soprano::Util::RemoveStatementCommand::RemoveStatementCommand( AsyncResult* result, const Statement& statement ) - : StatementCommand( statement, WriteCommand ), - m_result( result ) +void Soprano::Util::AddStatementCommand::execute() { + Error::ErrorCode r = model()->addStatements( statements() ); + result()->setResult( QVariant::fromValue( r ), model()->lastError() ); } -void Soprano::Util::RemoveStatementCommand::execute( Model* model ) +Soprano::Util::RemoveStatementCommand::RemoveStatementCommand( AsyncResult* result, Model* model, const QList<Statement>& statements ) + : StatementCommand( result, model, statements, WriteCommand ) { - Error::ErrorCode r = model->removeStatement( statement() ); - m_result->setResult( QVariant::fromValue( r ), model->lastError() ); } -Soprano::Util::RemoveAllStatementsCommand::RemoveAllStatementsCommand( AsyncResult* result, const Statement& statement ) - : StatementCommand( statement, WriteCommand ), - m_result( result ) +void Soprano::Util::RemoveStatementCommand::execute() { + Error::ErrorCode r = model()->removeStatements( statements() ); + result()->setResult( QVariant::fromValue( r ), model()->lastError() ); } -void Soprano::Util::RemoveAllStatementsCommand::execute( Model* model ) +Soprano::Util::RemoveAllStatementsCommand::RemoveAllStatementsCommand( AsyncResult* result, Model* model, const Statement& statement ) + : StatementCommand( result, model, statement, WriteCommand ) { - Error::ErrorCode r = model->removeAllStatements( statement() ); - m_result->setResult( QVariant::fromValue( r ), model->lastError() ); } -Soprano::Util::ContainsStatementCommand::ContainsStatementCommand( AsyncResult* result, const Statement& statement ) - : StatementCommand( statement, ReadCommand ), - m_result( result ) +void Soprano::Util::RemoveAllStatementsCommand::execute() { + Error::ErrorCode r = model()->removeAllStatements( statement() ); + result()->setResult( QVariant::fromValue( r ), model()->lastError() ); } -void Soprano::Util::ContainsStatementCommand::execute( Model* model ) +Soprano::Util::ContainsStatementCommand::ContainsStatementCommand( AsyncResult* result, Model* model, const Statement& statement ) + : StatementCommand( result, model, statement, ReadCommand ) { - bool r = model->containsStatement( statement() ); - m_result->setResult( r, model->lastError() ); } -Soprano::Util::ContainsAnyStatementCommand::ContainsAnyStatementCommand( AsyncResult* result, const Statement& statement ) - : StatementCommand( statement, ReadCommand ), - m_result( result ) +void Soprano::Util::ContainsStatementCommand::execute() { + bool r = model()->containsStatement( statement() ); + result()->setResult( r, model()->lastError() ); } -void Soprano::Util::ContainsAnyStatementCommand::execute( Model* model ) +Soprano::Util::ContainsAnyStatementCommand::ContainsAnyStatementCommand( AsyncResult* result, Model* model, const Statement& statement ) + : StatementCommand( result, model, statement, ReadCommand ) { - bool r = model->containsAnyStatement( statement() ); - m_result->setResult( r, model->lastError() ); } -Soprano::Util::ListStatementsCommand::ListStatementsCommand( AsyncModelPrivate* d, AsyncResult* result, const Statement& statement ) - : StatementCommand( statement, ReadCommand ), - m_result( result ), +void Soprano::Util::ContainsAnyStatementCommand::execute() +{ + bool r = model()->containsAnyStatement( statement() ); + result()->setResult( r, model()->lastError() ); +} + + +Soprano::Util::ListStatementsCommand::ListStatementsCommand( AsyncModelPrivate* d, AsyncResult* result, Model* model, const Statement& statement ) + : StatementCommand( result, model, statement, ReadCommand ), m_asyncModelPrivate( d ) { } -void Soprano::Util::ListStatementsCommand::execute( Model* model ) +void Soprano::Util::ListStatementsCommand::execute() { - StatementIterator r = model->listStatements( statement() ); + StatementIterator r = model()->listStatements( statement() ); if ( r.isValid() ) { AsyncIteratorBackend<Statement>* b = new AsyncIteratorBackend<Statement>( m_asyncModelPrivate, r ); - m_result->setResult( QVariant::fromValue( StatementIterator( b ) ), model->lastError() ); + result()->setResult( QVariant::fromValue( StatementIterator( b ) ), model()->lastError() ); + if ( m_asyncModelPrivate->mode == AsyncModel::MultiThreaded ) + b->iterate(); } else { - m_result->setResult( QVariant::fromValue( r ), model->lastError() ); + result()->setResult( QVariant::fromValue( r ), model()->lastError() ); } } -Soprano::Util::ListContextsCommand::ListContextsCommand( AsyncModelPrivate* d, AsyncResult* result ) - : Command( ReadCommand ), - m_result( result ), +Soprano::Util::ListContextsCommand::ListContextsCommand( AsyncModelPrivate* d, AsyncResult* result, Model* model ) + : Command( result, model, ReadCommand ), m_asyncModelPrivate( d ) { } -void Soprano::Util::ListContextsCommand::execute( Model* model ) + +void Soprano::Util::ListContextsCommand::execute() { - NodeIterator r = model->listContexts(); + NodeIterator r = model()->listContexts(); if ( r.isValid() ) { AsyncIteratorBackend<Node>* b = new AsyncIteratorBackend<Node>( m_asyncModelPrivate, r ); - m_result->setResult( QVariant::fromValue( NodeIterator( b ) ), model->lastError() ); + result()->setResult( QVariant::fromValue( NodeIterator( b ) ), model()->lastError() ); + if ( m_asyncModelPrivate->mode == AsyncModel::MultiThreaded ) + b->iterate(); } else { - m_result->setResult( QVariant::fromValue( r ), model->lastError() ); + result()->setResult( QVariant::fromValue( r ), model()->lastError() ); } } Soprano::Util::ExecuteQueryCommand::ExecuteQueryCommand( AsyncModelPrivate* d, AsyncResult* result, + Model* model, const QString& query, Query::QueryLanguage lang, const QString& userQueryLang ) - : Command( ReadCommand ), - m_result( result ), + : Command( result, model, ReadCommand ), m_query( query ), m_queryLanguage( lang ), m_userQueryLanguage( userQueryLang ), @@ -208,27 +213,30 @@ { } -void Soprano::Util::ExecuteQueryCommand::execute( Model* model ) +void Soprano::Util::ExecuteQueryCommand::execute() { - QueryResultIterator r = model->executeQuery( m_query, m_queryLanguage, m_userQueryLanguage ); + QueryResultIterator r = model()->executeQuery( m_query, m_queryLanguage, m_userQueryLanguage ); if ( r.isValid() ) { AsyncQueryResultIteratorBackend* b = new AsyncQueryResultIteratorBackend( m_asyncModelPrivate, r ); - m_result->setResult( QVariant::fromValue( QueryResultIterator( b ) ), model->lastError() ); + if ( m_asyncModelPrivate->mode == AsyncModel::MultiThreaded ) + b->initWorkThread(); + result()->setResult( QVariant::fromValue( QueryResultIterator( b ) ), model()->lastError() ); + if ( m_asyncModelPrivate->mode == AsyncModel::MultiThreaded ) + b->iterate(); } else { - m_result->setResult( QVariant::fromValue( r ), model->lastError() ); + result()->setResult( QVariant::fromValue( r ), model()->lastError() ); } } -Soprano::Util::CreateBlankNodeCommand::CreateBlankNodeCommand( AsyncResult* result ) - : Command( WriteCommand ), - m_result( result ) +Soprano::Util::CreateBlankNodeCommand::CreateBlankNodeCommand( AsyncResult* result, Model* model ) + : Command( result, model, WriteCommand ) { } -void Soprano::Util::CreateBlankNodeCommand::execute( Model* model ) +void Soprano::Util::CreateBlankNodeCommand::execute() { - Node r = model->createBlankNode(); - m_result->setResult( QVariant::fromValue( r ), model->lastError() ); + Node r = model()->createBlankNode(); + result()->setResult( QVariant::fromValue( r ), model()->lastError() ); } Index: soprano/util/asyncresult.h =================================================================== --- soprano/util/asyncresult.h (.../tags/soprano/2.1.1) (revision 0) +++ soprano/util/asyncresult.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -0,0 +1,175 @@ +/* + * This file is part of Soprano Project. + * + * Copyright (C) 2008 Sebastian Trueg <trueg@kde.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _SOPRANO_ASYNC_RESULT_H_ +#define _SOPRANO_ASYNC_RESULT_H_ + +#include <QtCore/QObject> +#include <QtCore/QVariant> + +#include "error.h" +#include "soprano_export.h" + + +namespace Soprano { + + class StatementIterator; + class Node; + class NodeIterator; + class QueryResultIterator; + + namespace Util { + + class AsyncModel; + + /** + * \class AsyncResult asyncmodel.h Soprano/Util/AsyncResult + * + * \brief A delayed result as returned by AsyncModel. + * + * \author Sebastian Trueg <trueg@kde.org> + * + * \since 2.1 + */ + class SOPRANO_EXPORT AsyncResult : public QObject, public Error::ErrorCache + { + Q_OBJECT + + Q_SIGNALS: + /** + * Emitted once the async operation is completed + * and the result can be read. + * + * The result will delete itself. + */ + void resultReady( Soprano::Util::AsyncResult* ); + + public: + ~AsyncResult(); + + /** + * The result of the async operation. Its type is dependent + * on the operation (for example Error::ErrorCode for + * AsyncModel::addStatementAsync or StatementIterator for + * AsyncModel::listStatementsAsync). Types may need to be + * registered with Q_DECLARE_METATYPE. + * + * Use Error::ErrorCache::lastError() to check + * for error details. + * + * This value is not ready before resultReady() + * has been emitted. <b>Do only use this in a slot connected to + * resultReady.</b> + * + * \sa errorCode, statementIterator, nodeIterator, queryResultIterator, node + */ + QVariant value() const { return m_result; } + + /** + * Convinience method which converts value() into + * a Error::ErrorCode as returned for the following AsyncModel methods: + * \li AsyncModel::addStatementAsync + * \li AsyncModel::removeStatementAsync + * \li AsyncModel::removeAllStatementsAsync + * + * \sa value() + * + * \since 2.2 + */ + Error::ErrorCode errorCode() const; + + /** + * Convinience method which converts value() into + * a StatementIterator as returned AsyncModel::listStatementsAsync. + * + * \sa value() + * + * \since 2.2 + */ + StatementIterator statementIterator() const; + + /** + * Convinience method which converts value() into + * a StatementIterator as returned AsyncModel::listContextsAsync. + * + * \sa value() + * + * \since 2.2 + */ + NodeIterator nodeIterator() const; + + /** + * Convinience method which converts value() into + * a StatementIterator as returned AsyncModel::executeQueryAsync. + * + * \sa value() + * + * \since 2.2 + */ + QueryResultIterator queryResultIterator() const; + + /** + * Convinience method which converts value() into + * a StatementIterator as returned AsyncModel::createBlankNodeAsync. + * + * \sa value() + * + * \since 2.2 + */ + Node node() const; + + /** \cond internal_asyncresult_members */ + /** + * Internal method. Do not call. + */ + void setResult( const QVariant& result, const Error::Error& error ) { + m_result = result; + setError( error ); + emit emitReady(); + } + + Q_SIGNALS: +#ifndef Q_MOC_RUN + private: // don't tell moc, but this signal is in fact private +#endif + /** + * Private signal used internally. + */ + void emitReady(); + /** \endcond */ + + private Q_SLOTS: + void slotEmitReady() { + emit resultReady( this ); + delete this; + } + + private: + AsyncResult(); + + QVariant m_result; + + friend class AsyncModel; + }; + } +} + +#endif Index: soprano/util/asynccommand.h =================================================================== --- soprano/util/asynccommand.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/util/asynccommand.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -22,6 +22,8 @@ #ifndef _SOPRANO_UTIL_ASYNC_COMMAND_H_ #define _SOPRANO_UTIL_ASYNC_COMMAND_H_ +#include <QtCore/QRunnable> + #include "asyncmodel.h" #include "statement.h" #include "node.h" @@ -37,7 +39,7 @@ class AsyncModelPrivate; - class Command + class Command : public QRunnable { public: enum Type { @@ -45,14 +47,21 @@ WriteCommand }; - Command( Type type ); + Command( AsyncResult* result, Model* model, Type type ); virtual ~Command(); Type type() const { return m_type; } + AsyncResult* result() const { return m_result; } + Model* model() const { return m_model; } - virtual void execute( Model* ) = 0; + virtual void execute() = 0; + // reimplemented from QRunnable + void run() { execute(); } + private: + AsyncResult* m_result; + Model* m_model; Type m_type; }; @@ -60,24 +69,18 @@ class StatementCountCommand : public Command { public: - StatementCountCommand( AsyncResult* result ); + StatementCountCommand( AsyncResult* result, Model* model ); - void execute( Model* ); - - private: - AsyncResult* m_result; + void execute(); }; class IsEmptyCommand : public Command { public: - IsEmptyCommand( AsyncResult* result ); + IsEmptyCommand( AsyncResult* result, Model* model ); - void execute( Model* ); - - private: - AsyncResult* m_result; + void execute(); }; @@ -86,86 +89,71 @@ public: virtual ~StatementCommand() {} - Statement statement() const { return m_statement; } + Statement statement() const { return m_statements.first(); } + QList<Statement> statements() const { return m_statements; } protected: - StatementCommand( const Statement&, Type type ); + StatementCommand( AsyncResult* result, Model* model, const Statement&, Type type ); + StatementCommand( AsyncResult* result, Model* model, const QList<Statement>&, Type type ); private: - Statement m_statement; + QList<Statement> m_statements; }; class AddStatementCommand : public StatementCommand { public: - AddStatementCommand( AsyncResult* result, const Statement& ); + AddStatementCommand( AsyncResult* result, Model* model, const QList<Statement>& ); - void execute( Model* ); - - private: - AsyncResult* m_result; + void execute(); }; class RemoveStatementCommand : public StatementCommand { public: - RemoveStatementCommand( AsyncResult* result, const Statement& ); + RemoveStatementCommand( AsyncResult* result, Model* model, const QList<Statement>& ); - void execute( Model* ); - - private: - AsyncResult* m_result; + void execute(); }; class RemoveAllStatementsCommand : public StatementCommand { public: - RemoveAllStatementsCommand( AsyncResult* result, const Statement& ); + RemoveAllStatementsCommand( AsyncResult* result, Model* model, const Statement& ); - void execute( Model* ); - - private: - AsyncResult* m_result; + void execute(); }; class ContainsStatementCommand : public StatementCommand { public: - ContainsStatementCommand( AsyncResult* result, const Statement& ); + ContainsStatementCommand( AsyncResult* result, Model* model, const Statement& ); - void execute( Model* ); - - private: - AsyncResult* m_result; + void execute(); }; class ContainsAnyStatementCommand : public StatementCommand { public: - ContainsAnyStatementCommand( AsyncResult* result, const Statement& ); + ContainsAnyStatementCommand( AsyncResult* result, Model* model, const Statement& ); - void execute( Model* ); - - private: - AsyncResult* m_result; + void execute(); }; class ListStatementsCommand : public StatementCommand { public: - ListStatementsCommand( AsyncModelPrivate*, AsyncResult* result, const Statement& ); + ListStatementsCommand( AsyncModelPrivate*, AsyncResult* result, Model* model, const Statement& ); - void execute( Model* ); + void execute(); private: - AsyncResult* m_result; - AsyncModelPrivate* m_asyncModelPrivate; }; @@ -173,13 +161,11 @@ class ListContextsCommand : public Command { public: - ListContextsCommand( AsyncModelPrivate*, AsyncResult* result ); + ListContextsCommand( AsyncModelPrivate*, AsyncResult* result, Model* model ); - void execute( Model* ); + void execute(); private: - AsyncResult* m_result; - AsyncModelPrivate* m_asyncModelPrivate; }; @@ -187,13 +173,11 @@ class ExecuteQueryCommand : public Command { public: - ExecuteQueryCommand( AsyncModelPrivate*, AsyncResult* result, const QString&, Query::QueryLanguage lang, const QString& userQueryLang ); + ExecuteQueryCommand( AsyncModelPrivate*, AsyncResult* result, Model* model, const QString&, Query::QueryLanguage lang, const QString& userQueryLang ); - void execute( Model* ); + void execute(); private: - AsyncResult* m_result; - QString m_query; Query::QueryLanguage m_queryLanguage; QString m_userQueryLanguage; @@ -205,12 +189,9 @@ class CreateBlankNodeCommand : public Command { public: - CreateBlankNodeCommand( AsyncResult* result ); + CreateBlankNodeCommand( AsyncResult* result, Model* model ); - void execute( Model* ); - - private: - AsyncResult* m_result; + void execute(); }; } } Index: soprano/util/asyncmodel_p.h =================================================================== --- soprano/util/asyncmodel_p.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/util/asyncmodel_p.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -24,7 +24,6 @@ #include "asyncmodel.h" #include "asynccommand.h" -#include "asynciteratorbackend.h" #include "iteratorbackend.h" #include <QtCore/QLinkedList> @@ -32,25 +31,24 @@ namespace Soprano { namespace Util { + + class AsyncIteratorHandle; + class AsyncModelPrivate { public: - AsyncModelPrivate( AsyncModel* parent ) - : m_model( parent ) { - } + AsyncModelPrivate( AsyncModel* parent ); + ~AsyncModelPrivate(); - ~AsyncModelPrivate() { - foreach( AsyncIteratorBase* it, openIterators ) { - it->setModelGone(); - } - } + AsyncModel::AsyncModelMode mode; QLinkedList<Command*> commandQueue; - QList<AsyncIteratorBase*> openIterators; + // only used for single threaded mode + QList<AsyncIteratorHandle*> openIterators; - void addIterator( AsyncIteratorBase* ); - void removeIterator( AsyncIteratorBase* ); + void addIterator( AsyncIteratorHandle* ); + void removeIterator( AsyncIteratorHandle* ); void enqueueCommand( Command* ); void _s_executeNextCommand(); Index: soprano/util/asyncqueryresultiteratorbackend.h =================================================================== --- soprano/util/asyncqueryresultiteratorbackend.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/util/asyncqueryresultiteratorbackend.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -29,69 +29,139 @@ namespace Soprano { namespace Util { - class AsyncQueryResultIteratorBackend : public QueryResultIteratorBackend, public AsyncIteratorBase + class AsyncQueryResultIteratorBackend : public QueryResultIteratorBackend, public AsyncIteratorBase<Soprano::BindingSet> { public: AsyncQueryResultIteratorBackend( AsyncModelPrivate* d, const QueryResultIterator& it ) - : AsyncIteratorBase( d ), - m_iterator( it ) { + : AsyncIteratorBase<BindingSet>( d, it ), + m_iterator( it ) { } + // called in work thread + void initWorkThread() { + m_isGraph = m_iterator.isGraph(); + m_isBinding = m_iterator.isBinding(); + m_isBool = m_iterator.isBool(); + if( m_isBool ) { + m_boolVal = m_iterator.boolValue(); + m_iterator.close(); + } + } + bool next() { - return m_iterator.next(); + return AsyncIteratorBase<BindingSet>::getNext(); } BindingSet current() const { - return m_iterator.current(); + return AsyncIteratorBase<BindingSet>::getCurrent(); } Statement currentStatement() const { - return m_iterator.currentStatement(); + if( AsyncIteratorHandle::modelPrivate() && AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return m_iterator.currentStatement(); + else + return m_currentStatement; } Node binding( const QString& name ) const { - return m_iterator.binding( name ); + if( AsyncIteratorHandle::modelPrivate() && AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return m_iterator.binding( name ); + else + return current()[ name ]; } Node binding( int offset ) const { - return m_iterator.binding( offset ); + if( AsyncIteratorHandle::modelPrivate() && AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return m_iterator.binding( offset ); + else + return current()[ offset ]; } int bindingCount() const { - return m_iterator.bindingCount(); + if( AsyncIteratorHandle::modelPrivate() && AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return m_iterator.bindingCount(); + else + return current().count(); } QStringList bindingNames() const { - return m_iterator.bindingNames(); + if( AsyncIteratorHandle::modelPrivate() && AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return m_iterator.bindingNames(); + else + return current().bindingNames(); } bool isGraph() const { - return m_iterator.isGraph(); + if( AsyncIteratorHandle::modelPrivate() && AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return m_iterator.isGraph(); + else + return m_isGraph; } bool isBinding() const { - return m_iterator.isBinding(); + if( AsyncIteratorHandle::modelPrivate() && AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return m_iterator.isBinding(); + else + return m_isBinding; } bool isBool() const { - return m_iterator.isBool(); + if( AsyncIteratorHandle::modelPrivate() && AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return m_iterator.isBool(); + else + return m_isBool; } bool boolValue() const { - return m_iterator.boolValue(); + if( AsyncIteratorHandle::modelPrivate() && AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return m_iterator.boolValue(); + else + return m_boolVal; } void close() { - m_iterator.close(); - remove(); + AsyncIteratorBase<BindingSet>::closeIterator(); } Error::Error lastError() const { - return m_iterator.lastError(); + if( AsyncIteratorHandle::modelPrivate() && AsyncIteratorHandle::modelPrivate()->mode == AsyncModel::SingleThreaded ) + return m_iterator.lastError(); + else + return AsyncIteratorBase<BindingSet>::m_error; } private: + void enqueueCurrent() { + if( isGraph() ) + m_statementCache.enqueue( m_iterator.currentStatement() ); + else if( isBinding() ) + AsyncIteratorBase<BindingSet>::enqueueCurrent(); + } + + void dequeueFirst() { + if( isGraph() ) + m_currentStatement = m_statementCache.dequeue(); + else if( isBinding() ) + AsyncIteratorBase<BindingSet>::dequeueFirst(); + } + + int cacheFillState() const { + if( isGraph() ) + return m_statementCache.size(); + else if( isBinding() ) + return AsyncIteratorBase<BindingSet>::cacheFillState(); + else + return 0; + } + QueryResultIterator m_iterator; + bool m_isGraph; + bool m_isBinding; + bool m_isBool; + bool m_boolVal; + + QQueue<Statement> m_statementCache; + Statement m_currentStatement; }; } } Index: soprano/CMakeLists.txt =================================================================== --- soprano/CMakeLists.txt (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/CMakeLists.txt (.../trunk/kdesupport/soprano) (revision 846898) @@ -91,6 +91,7 @@ util/signalcachemodel.cpp util/readonlymodel.cpp util/asyncmodel.cpp + util/asyncresult.cpp util/asynccommand.cpp util/asynciteratorbackend.cpp ) @@ -166,4 +167,5 @@ util/signalcachemodel.h util/readonlymodel.h util/asyncmodel.h + util/asyncresult.h DESTINATION include/soprano) Index: soprano/sopranotypes.h =================================================================== --- soprano/sopranotypes.h (.../tags/soprano/2.1.1) (revision 846898) +++ soprano/sopranotypes.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -31,6 +31,8 @@ { /** * Different types of RDF serialization. + * + * \sa serializationMimeType, mimeTypeToSerialization */ // FIXME: what about the used charsets? Should we and if so, how should we include them? // FIXME: SerializationUnknown and SerializationUser should become the same, i.e. one should become deprecated Index: ChangeLog =================================================================== --- ChangeLog (.../tags/soprano/2.1.1) (revision 846898) +++ ChangeLog (.../trunk/kdesupport/soprano) (revision 846898) @@ -1,3 +1,7 @@ +2.2 + * The AsyncModel now has a "real" asyncroneous mode which uses multiple threads to execute the + commands. + 2.1 * SignalCacheModel to restrict the number of emitted statementsAdded and statementsRemoved signals in a certain timeframe. Index: includes/Util/AsyncResult =================================================================== --- includes/Util/AsyncResult (.../tags/soprano/2.1.1) (revision 846898) +++ includes/Util/AsyncResult (.../trunk/kdesupport/soprano) (revision 846898) @@ -1 +1 @@ -#include "../../soprano/asyncmodel.h" +#include "../../soprano/asyncresult.h" Index: index/indexfiltermodel.cpp =================================================================== --- index/indexfiltermodel.cpp (.../tags/soprano/2.1.1) (revision 846898) +++ index/indexfiltermodel.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -262,3 +262,9 @@ { return d->indexOnlyPredicates.toList(); } + + +void Soprano::Index::IndexFilterModel::optimizeIndex() +{ + d->index->optimize(); +} Index: index/indexfiltermodel.h =================================================================== --- index/indexfiltermodel.h (.../tags/soprano/2.1.1) (revision 846898) +++ index/indexfiltermodel.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -179,6 +179,16 @@ void rebuildIndex(); /** + * Optimize the index for search. This makes sense after adding or + * removing a large number of statements. + * + * \sa CLuceneIndex::optimize + * + * \since 2.2 + */ + void optimizeIndex(); + + /** * Add a predicate which should only be indexed. This might be useful * for very large literals whose value is of no interest but which * should be searchable. Index: index/cluceneindex.cpp =================================================================== --- index/cluceneindex.cpp (.../tags/soprano/2.1.1) (revision 846898) +++ index/cluceneindex.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -694,3 +694,9 @@ } d->closeReader(); } + + +void Soprano::Index::CLuceneIndex::optimize() +{ + d->getIndexWriter()->optimize(); +} Index: index/cluceneindex.h =================================================================== --- index/cluceneindex.h (.../tags/soprano/2.1.1) (revision 846898) +++ index/cluceneindex.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -244,6 +244,14 @@ * \since 2.1 */ void clear(); + + /** + * Optimize the index for search. This makes sense after adding or + * removing a large number of statements. + * + * \since 2.2 + */ + void optimize(); //@} private: Index: backends/sesame2/sesame2backend.cpp =================================================================== --- backends/sesame2/sesame2backend.cpp (.../tags/soprano/2.1.1) (revision 846898) +++ backends/sesame2/sesame2backend.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -63,7 +63,7 @@ } -Soprano::StorageModel* Soprano::Sesame2::BackendPlugin::createModel( const QList<BackendSetting>& settings ) const +Soprano::StorageModel* Soprano::Sesame2::BackendPlugin::createModel( const BackendSettings& settings ) const { m_mutex.lock(); if ( !m_jniWrapper ) { Index: backends/sesame2/sesame2backend.h =================================================================== --- backends/sesame2/sesame2backend.h (.../tags/soprano/2.1.1) (revision 846898) +++ backends/sesame2/sesame2backend.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -41,7 +41,7 @@ BackendPlugin(); ~BackendPlugin(); - StorageModel* createModel( const QList<BackendSetting>& settings = QList<BackendSetting>() ) const; + StorageModel* createModel( const BackendSettings& settings = BackendSettings() ) const; bool deleteModelData( const BackendSettings& settings ) const; Index: backends/sesame2/jniwrapper.cpp =================================================================== --- backends/sesame2/jniwrapper.cpp (.../tags/soprano/2.1.1) (revision 846898) +++ backends/sesame2/jniwrapper.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -66,12 +66,14 @@ if ( !s_instance ) { // prepare the VM options JavaVMInitArgs vmArgs; - JavaVMOption vmOptions[2]; + JavaVMOption vmOptions[4]; vmOptions[0].optionString = ( char* )"-Djava.class.path="SESAME2_CLASSPATH; vmOptions[1].optionString = ( char* )"-verbose:jni,gc,class"; + vmOptions[2].optionString = ( char* )"-Xms256m"; + vmOptions[3].optionString = ( char* )"-Xmx256m"; vmArgs.version = JNI_VERSION_1_4; vmArgs.options = vmOptions; - vmArgs.nOptions = 2; + vmArgs.nOptions = 4; // create the VM JavaVM* jvm = 0; Index: backends/redland/redlandbackend.cpp =================================================================== --- backends/redland/redlandbackend.cpp (.../tags/soprano/2.1.1) (revision 846898) +++ backends/redland/redlandbackend.cpp (.../trunk/kdesupport/soprano) (revision 846898) @@ -56,7 +56,7 @@ } -Soprano::StorageModel* Soprano::Redland::BackendPlugin::createModel( const QList<BackendSetting>& settings ) const +Soprano::StorageModel* Soprano::Redland::BackendPlugin::createModel( const BackendSettings& settings ) const { QMutexLocker lock( &m_mutex ); Index: backends/redland/redlandbackend.h =================================================================== --- backends/redland/redlandbackend.h (.../tags/soprano/2.1.1) (revision 846898) +++ backends/redland/redlandbackend.h (.../trunk/kdesupport/soprano) (revision 846898) @@ -47,7 +47,7 @@ * contexts=yes, new=no. Soprano::BackendOptions Soprano::BackendOptionStorageDir and * Soprano::BackendOptionStorageMemory change the values of redland options "dir" and "has-type". */ - StorageModel* createModel( const QList<BackendSetting>& settings = QList<BackendSetting>() ) const; + StorageModel* createModel( const BackendSettings& settings = BackendSettings() ) const; bool deleteModelData( const BackendSettings& settings ) const; Index: backends/CMakeLists.txt =================================================================== --- backends/CMakeLists.txt (.../tags/soprano/2.1.1) (revision 846898) +++ backends/CMakeLists.txt (.../trunk/kdesupport/soprano) (revision 846898) @@ -4,6 +4,6 @@ add_subdirectory(redland) endif(REDLAND_FOUND) -if(JNI_1_6_FOUND) +if(JNI_1_4_FOUND) add_subdirectory(sesame2) -endif(JNI_1_6_FOUND) +endif(JNI_1_4_FOUND) Index: CMakeLists.txt =================================================================== --- CMakeLists.txt (.../tags/soprano/2.1.1) (revision 846898) +++ CMakeLists.txt (.../trunk/kdesupport/soprano) (revision 846898) @@ -2,7 +2,7 @@ set(CMAKE_SOPRANO_VERSION_MAJOR 2 CACHE INT "Major Soprano version number" FORCE) set(CMAKE_SOPRANO_VERSION_MINOR 1 CACHE INT "Minor Soprano version number" FORCE) -set(CMAKE_SOPRANO_VERSION_RELEASE 1 CACHE INT "Release Soprano version number" FORCE) +set(CMAKE_SOPRANO_VERSION_RELEASE 61 CACHE INT "Release Soprano version number" FORCE) set(CMAKE_SOPRANO_VERSION_STRING "${CMAKE_SOPRANO_VERSION_MAJOR}.${CMAKE_SOPRANO_VERSION_MINOR}.${CMAKE_SOPRANO_VERSION_RELEASE}" CACHE STRING "Soprano version string" FORCE) enable_testing() @@ -21,14 +21,17 @@ find_package(JNI) if(JAVA_INCLUDE_PATH AND JAVA_JVM_LIBRARY) file(READ ${JAVA_INCLUDE_PATH}/jni.h jni_header_data) - string(REGEX MATCH "JNI_VERSION_1_6" JNI_1_6_FOUND "${jni_header_data}") - if(JNI_1_6_FOUND) - message(STATUS "Found Java JNI >= 1.6: ${JAVA_INCLUDE_PATH}, ${JAVA_JVM_LIBRARY}") - else(JNI_1_6_FOUND) - message( "Need JRE version 1.6 or higher for the Sesame2 backend.") - endif(JNI_1_6_FOUND) + string(REGEX MATCH "JNI_VERSION_1_4" JNI_1_4_FOUND "${jni_header_data}") + if(JNI_1_4_FOUND) + message(STATUS "Found Java JNI >= 1.4: ${JAVA_INCLUDE_PATH}, ${JAVA_JVM_LIBRARY}") + else(JNI_1_4_FOUND) + message( "Need JNI version 1.4 or higher for the Sesame2 backend.") + endif(JNI_1_4_FOUND) else(JAVA_INCLUDE_PATH AND JAVA_JVM_LIBRARY) message(STATUS "Could not find Java JNI") + if("$ENV{JAVA_HOME}" STREQUAL "") + message("Make sure JAVA_HOME is set") + endif("$ENV{JAVA_HOME}" STREQUAL "") endif(JAVA_INCLUDE_PATH AND JAVA_JVM_LIBRARY) set (LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) @@ -99,16 +102,16 @@ message("---------------------------------------------------------------------------------------") message("-- Soprano Components that will be built:") -if(NOT REDLAND_FOUND AND NOT JNI_1_6_FOUND) +if(NOT REDLAND_FOUND AND NOT JNI_1_4_FOUND) message(" WARNING: No Soprano backends will be compiled due to missing dependencies!") message(" WARNING: Soprano will not be very useful without storage backends!") -endif(NOT REDLAND_FOUND AND NOT JNI_1_6_FOUND) +endif(NOT REDLAND_FOUND AND NOT JNI_1_4_FOUND) if(REDLAND_FOUND) message(" * Redland storage backend") endif(REDLAND_FOUND) -if(JNI_1_6_FOUND) +if(JNI_1_4_FOUND) message(" * Sesame2 storage backend (java-based)") -endif(JNI_1_6_FOUND) +endif(JNI_1_4_FOUND) if(REDLAND_FOUND) message(" * Raptor RDF parser") @@ -131,9 +134,9 @@ if(NOT REDLAND_FOUND) message(" * Redland storage backend") endif(NOT REDLAND_FOUND) -if(NOT JNI_1_6_FOUND) +if(NOT JNI_1_4_FOUND) message(" * Sesame2 storage backend (java-based)") -endif(NOT JNI_1_6_FOUND) +endif(NOT JNI_1_4_FOUND) if(NOT REDLAND_FOUND) message(" * Raptor RDF parser")