diff --git a/CMakeLists.txt b/CMakeLists.txt index e2dc6a42a..e8655b24c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,7 +115,7 @@ macro_log_feature(TAGLIB_FOUND "TagLib" "Audio Meta-Data Library" "http://develo include( CheckTagLibFileName ) check_taglib_filename( COMPLEX_TAGLIB_FILENAME ) -macro_optional_find_package(Boost) +macro_optional_find_package( Boost COMPONENTS thread ) macro_log_feature(Boost_FOUND "Boost" "Provides free peer-reviewed portable C++ source libraries" "http://www.boost.org" TRUE "" "") #FIXME: give useful explaination macro_optional_find_package(QCA2) diff --git a/src/libtomahawk/Album.cpp b/src/libtomahawk/Album.cpp index a639042c7..1b53146ca 100644 --- a/src/libtomahawk/Album.cpp +++ b/src/libtomahawk/Album.cpp @@ -23,6 +23,7 @@ #include "AlbumPlaylistInterface.h" #include "database/Database.h" #include "database/DatabaseImpl.h" +#include "database/IdThreadWorker.h" #include "Query.h" #include "Source.h" @@ -30,6 +31,12 @@ using namespace Tomahawk; +QHash< QString, album_ptr > Album::s_albumsByName = QHash< QString, album_ptr >(); +QHash< unsigned int, album_ptr > Album::s_albumsById = QHash< unsigned int, album_ptr >(); + +static QMutex s_nameCacheMutex; +static QMutex s_idCacheMutex; +static QMutex s_idMutex; Album::~Album() { @@ -47,11 +54,21 @@ Album::get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCr if ( !Database::instance() || !Database::instance()->impl() ) return album_ptr(); - int albid = Database::instance()->impl()->albumId( artist->id(), name, autoCreate ); - if ( albid < 1 && autoCreate ) - return album_ptr(); + QMutexLocker l( &s_nameCacheMutex ); - return Album::get( albid, name, artist ); + if ( s_albumsByName.contains( artist->name() + name ) ) + { + return s_albumsByName[ artist->name() + name ]; + } + +// qDebug() << "LOOKING UP ALBUM:" << artist->name() << name; + album_ptr album = album_ptr( new Album( name, artist ) ); + album->setWeakRef( album.toWeakRef() ); + album->loadId( autoCreate ); + + s_albumsByName[ artist->name() + name ] = album; + + return album; } @@ -61,17 +78,17 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar static QHash< unsigned int, album_ptr > s_albums; static QMutex s_mutex; - QMutexLocker lock( &s_mutex ); - if ( s_albums.contains( id ) ) + QMutexLocker lock( &s_idCacheMutex ); + if ( s_albumsById.contains( id ) ) { - return s_albums.value( id ); + return s_albumsById.value( id ); } album_ptr a = album_ptr( new Album( id, name, artist ), &QObject::deleteLater ); a->setWeakRef( a.toWeakRef() ); if ( id > 0 ) - s_albums.insert( id, a ); + s_albumsById.insert( id, a ); return a; } @@ -79,6 +96,7 @@ Album::get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& ar Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist ) : QObject() + , m_waitingForId( false ) , m_id( id ) , m_name( name ) , m_artist( artist ) @@ -92,6 +110,20 @@ Album::Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& } +Album::Album( const QString& name, const Tomahawk::artist_ptr& artist ) + : QObject() + , m_waitingForId( true ) + , m_name( name ) + , m_artist( artist ) + , m_coverLoaded( false ) + , m_coverLoading( false ) +#ifndef ENABLE_HEADLESS + , m_cover( 0 ) +#endif +{ + m_sortname = DatabaseImpl::sortname( name ); +} + void Album::onTracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ) { @@ -106,6 +138,31 @@ Album::artist() const } +void +Album::loadId( bool autoCreate ) +{ + Q_ASSERT( m_waitingForId ); + m_idFuture = IdThreadWorker::getAlbumId( m_ownRef.toStrongRef(), autoCreate ); +} + +unsigned int +Album::id() const +{ + QMutexLocker l( &s_idMutex ); + + if ( m_waitingForId ) + { + m_id = m_idFuture.get(); + m_waitingForId = false; + + if ( m_id > 0 ) + s_albumsById[ m_id ] = m_ownRef.toStrongRef(); + } + + return m_id; +} + + #ifndef ENABLE_HEADLESS QPixmap Album::cover( const QSize& size, bool forceLoad ) const @@ -239,4 +296,4 @@ Album::infoid() const m_uuid = uuid(); return m_uuid; -} \ No newline at end of file +} diff --git a/src/libtomahawk/Album.h b/src/libtomahawk/Album.h index 89c6ad09f..0290d72a1 100644 --- a/src/libtomahawk/Album.h +++ b/src/libtomahawk/Album.h @@ -28,6 +28,8 @@ #include #endif +#include "boost/thread/future.hpp" + #include "Typedefs.h" #include "PlaylistInterface.h" #include "DllMacro.h" @@ -45,10 +47,11 @@ public: static album_ptr get( const Tomahawk::artist_ptr& artist, const QString& name, bool autoCreate = false ); static album_ptr get( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist ); - explicit Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist ); + Album( unsigned int id, const QString& name, const Tomahawk::artist_ptr& artist ); + Album( const QString& name, const Tomahawk::artist_ptr& artist ); virtual ~Album(); - unsigned int id() const { return m_id; } + unsigned int id() const; QString name() const { return m_name; } QString sortname() const { return m_sortname; } @@ -64,6 +67,7 @@ public: QWeakPointer< Tomahawk::Album > weakRef() { return m_ownRef; } void setWeakRef( QWeakPointer< Tomahawk::Album > weakRef ) { m_ownRef = weakRef; } + void loadId( bool autoCreate ); signals: void tracksAdded( const QList& tracks, Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); void updated(); @@ -79,7 +83,9 @@ private: Q_DISABLE_COPY( Album ) QString infoid() const; - unsigned int m_id; + mutable bool m_waitingForId; + mutable boost::unique_future< unsigned int > m_idFuture; + mutable unsigned int m_id; QString m_name; QString m_sortname; @@ -98,6 +104,9 @@ private: QHash< Tomahawk::ModelMode, QHash< Tomahawk::collection_ptr, Tomahawk::playlistinterface_ptr > > m_playlistInterface; QWeakPointer< Tomahawk::Album > m_ownRef; + + static QHash< QString, album_ptr > s_albumsByName; + static QHash< unsigned int, album_ptr > s_albumsById; }; } // ns diff --git a/src/libtomahawk/Artist.cpp b/src/libtomahawk/Artist.cpp index fbd30f477..5c08ee9c6 100644 --- a/src/libtomahawk/Artist.cpp +++ b/src/libtomahawk/Artist.cpp @@ -25,12 +25,19 @@ #include "database/DatabaseImpl.h" #include "database/DatabaseCommand_AllAlbums.h" #include "database/DatabaseCommand_TrackStats.h" +#include "database/IdThreadWorker.h" #include "Source.h" #include "utils/Logger.h" using namespace Tomahawk; +QHash< QString, artist_ptr > Artist::s_artistsByName = QHash< QString, artist_ptr >(); +QHash< unsigned int, artist_ptr > Artist::s_artistsById = QHash< unsigned int, artist_ptr >(); + +static QMutex s_nameCacheMutex; +static QMutex s_idCacheMutex; +static QMutex s_idMutex; Artist::~Artist() { @@ -45,34 +52,40 @@ Artist::~Artist() artist_ptr Artist::get( const QString& name, bool autoCreate ) { + if ( name.isEmpty() ) + return artist_ptr(); + + QMutexLocker lock( &s_nameCacheMutex ); + if ( s_artistsByName.contains( name ) ) + return s_artistsByName.value( name ); + if ( !Database::instance() || !Database::instance()->impl() ) return artist_ptr(); - int artid = Database::instance()->impl()->artistId( name, autoCreate ); - if ( artid < 1 && autoCreate ) - return artist_ptr(); + artist_ptr artist = artist_ptr( new Artist( name ), &QObject::deleteLater ); + artist->setWeakRef( artist.toWeakRef() ); + artist->loadId( autoCreate ); - return Artist::get( artid, name ); + s_artistsByName[ name ] = artist; + + return artist; } artist_ptr Artist::get( unsigned int id, const QString& name ) { - static QHash< unsigned int, artist_ptr > s_artists; - static QMutex s_mutex; - - QMutexLocker lock( &s_mutex ); - if ( s_artists.contains( id ) ) + QMutexLocker lock( &s_idCacheMutex ); + if ( s_artistsById.contains( id ) ) { - return s_artists.value( id ); + return s_artistsById.value( id ); } artist_ptr a = artist_ptr( new Artist( id, name ), &QObject::deleteLater ); a->setWeakRef( a.toWeakRef() ); if ( id > 0 ) - s_artists.insert( id, a ); + s_artistsById.insert( id, a ); return a; } @@ -80,6 +93,7 @@ Artist::get( unsigned int id, const QString& name ) Artist::Artist( unsigned int id, const QString& name ) : QObject() + , m_waitingForFuture( false ) , m_id( id ) , m_name( name ) , m_coverLoaded( false ) @@ -95,6 +109,24 @@ Artist::Artist( unsigned int id, const QString& name ) } +Artist::Artist( const QString& name ) + : QObject() + , m_waitingForFuture( true ) + , m_id( 0 ) + , m_name( name ) + , m_coverLoaded( false ) + , m_coverLoading( false ) + , m_simArtistsLoaded( false ) + , m_biographyLoaded( false ) + , m_infoJobs( 0 ) +#ifndef ENABLE_HEADLESS + , m_cover( 0 ) +#endif +{ + m_sortname = DatabaseImpl::sortname( name, true ); +} + + void Artist::onTracksLoaded( Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ) { @@ -192,6 +224,31 @@ Artist::similarArtists() const } +void +Artist::loadId( bool autoCreate ) +{ + Q_ASSERT( m_waitingForFuture ); + m_idFuture = IdThreadWorker::getArtistId( m_ownRef.toStrongRef(), autoCreate ); +} + + +unsigned int +Artist::id() const +{ + QMutexLocker l( &s_idMutex ); + if ( m_waitingForFuture ) + { + m_id = m_idFuture.get(); + m_waitingForFuture = false; + + if ( m_id > 0 ) + s_artistsById[ m_id ] = m_ownRef.toStrongRef(); + } + + return m_id; +} + + QString Artist::biography() const { diff --git a/src/libtomahawk/Artist.h b/src/libtomahawk/Artist.h index 3eef077a1..e80854d63 100644 --- a/src/libtomahawk/Artist.h +++ b/src/libtomahawk/Artist.h @@ -31,6 +31,8 @@ #include "DllMacro.h" #include "Query.h" +#include + namespace Tomahawk { @@ -42,10 +44,11 @@ public: static artist_ptr get( const QString& name, bool autoCreate = false ); static artist_ptr get( unsigned int id, const QString& name ); - explicit Artist( unsigned int id, const QString& name ); + Artist( unsigned int id, const QString& name ); + explicit Artist( const QString& name ); virtual ~Artist(); - unsigned int id() const { return m_id; } + unsigned int id() const; QString name() const { return m_name; } QString sortname() const { return m_sortname; } @@ -72,6 +75,7 @@ public: QWeakPointer< Tomahawk::Artist > weakRef() { return m_ownRef; } void setWeakRef( QWeakPointer< Tomahawk::Artist > weakRef ) { m_ownRef = weakRef; } + void loadId( bool autoCreate ); signals: void tracksAdded( const QList& tracks, Tomahawk::ModelMode mode, const Tomahawk::collection_ptr& collection ); void albumsAdded( const QList& albums, Tomahawk::ModelMode mode ); @@ -93,7 +97,10 @@ private: Artist(); QString infoid() const; - unsigned int m_id; + mutable bool m_waitingForFuture; + mutable boost::unique_future< unsigned int > m_idFuture; + mutable unsigned int m_id; + QString m_name; QString m_sortname; @@ -123,6 +130,9 @@ private: QHash< Tomahawk::ModelMode, QHash< Tomahawk::collection_ptr, Tomahawk::playlistinterface_ptr > > m_playlistInterface; QWeakPointer< Tomahawk::Artist > m_ownRef; + + static QHash< QString, artist_ptr > s_artistsByName; + static QHash< unsigned int, artist_ptr > s_artistsById; }; } // ns diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index 2c0e5433c..ab3a9b4d3 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -256,6 +256,7 @@ set( libSources database/DatabaseCommand_SetTrackAttributes.cpp database/Database.cpp database/TomahawkSqlQuery.cpp + database/IdThreadWorker.cpp infosystem/InfoSystem.cpp infosystem/InfoSystemCache.cpp @@ -450,6 +451,7 @@ TARGET_LINK_LIBRARIES( tomahawklib ${OS_SPECIFIC_LINK_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${LINK_LIBRARIES} + ${Boost_LIBRARIES} ) INSTALL( TARGETS tomahawklib diff --git a/src/libtomahawk/database/Database.cpp b/src/libtomahawk/database/Database.cpp index eea51aca0..9f835a839 100644 --- a/src/libtomahawk/database/Database.cpp +++ b/src/libtomahawk/database/Database.cpp @@ -22,6 +22,7 @@ #include "DatabaseCommand.h" #include "DatabaseImpl.h" #include "DatabaseWorker.h" +#include "IdThreadWorker.h" #include "utils/Logger.h" #include "Source.h" @@ -43,6 +44,7 @@ Database::Database( const QString& dbname, QObject* parent ) , m_ready( false ) , m_impl( new DatabaseImpl( dbname, this ) ) , m_workerRW( new DatabaseWorker( this, true ) ) + , m_idWorker( new IdThreadWorker( this ) ) { s_instance = this; @@ -58,6 +60,7 @@ Database::Database( const QString& dbname, QObject* parent ) connect( m_impl, SIGNAL( indexReady() ), SLOT( setIsReadyTrue() ) ); m_workerRW->start(); + m_idWorker->start(); } @@ -67,6 +70,8 @@ Database::~Database() qDeleteAll( m_workers ); delete m_workerRW; + m_idWorker->stop(); + delete m_idWorker; delete m_impl; } diff --git a/src/libtomahawk/database/Database.h b/src/libtomahawk/database/Database.h index 7bf05a09e..307a3ddd8 100644 --- a/src/libtomahawk/database/Database.h +++ b/src/libtomahawk/database/Database.h @@ -31,6 +31,7 @@ class DatabaseImpl; class DatabaseWorker; +class IdThreadWorker; /* This class is really a firewall/pimpl - the public functions of LibraryImpl @@ -79,6 +80,7 @@ private: DatabaseImpl* m_impl; DatabaseWorker* m_workerRW; QList m_workers; + IdThreadWorker* m_idWorker; bool m_indexReady; int m_maxConcurrentThreads; diff --git a/src/libtomahawk/database/DatabaseImpl.cpp b/src/libtomahawk/database/DatabaseImpl.cpp index 4648a988a..661f99c2c 100644 --- a/src/libtomahawk/database/DatabaseImpl.cpp +++ b/src/libtomahawk/database/DatabaseImpl.cpp @@ -44,7 +44,7 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) - : QObject( (QObject*) parent ) + : QObject( 0 ) , m_parent( parent ) { QTime t; diff --git a/src/libtomahawk/database/IdThreadWorker.cpp b/src/libtomahawk/database/IdThreadWorker.cpp new file mode 100644 index 000000000..05db86aaf --- /dev/null +++ b/src/libtomahawk/database/IdThreadWorker.cpp @@ -0,0 +1,165 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ + +#include "IdThreadWorker.h" + +#include "Artist.h" +#include "Album.h" +#include "Database.h" +#include "DatabaseImpl.h" + +using namespace Tomahawk; + +namespace { + enum QueryType { + ArtistType, + AlbumType + }; +} + + +static QWaitCondition s_waitCond; +static QMutex s_mutex; +//static QSet< boost::promise< unsigned int >* > s_cachedPromises; + +struct QueueItem +{ + boost::promise< unsigned int >* promise; + artist_ptr artist; + album_ptr album; + QueryType type; + bool create; +}; + +// TODO Q_GLOBAL_STATIC +QQueue< QueueItem* > IdThreadWorker::s_workQueue = QQueue< QueueItem* >(); + +IdThreadWorker::IdThreadWorker( Database* db ) + : QThread() + , m_db( db ) + , m_stop( false ) +{ + m_impl = m_db->impl()->clone(); +} + + +IdThreadWorker::~IdThreadWorker() +{ + wait(); +} + + +void +IdThreadWorker::stop() +{ + { + QMutexLocker l( &s_mutex ); + m_stop = true; + } + + s_waitCond.wakeOne(); +} + + +QueueItem* +internalGet( const artist_ptr& artist, const album_ptr& album, bool autoCreate, QueryType type ) +{ + QueueItem* item = new QueueItem; + item->promise = new boost::promise< unsigned int >(); + item->artist = artist; + item->album = album; + item->type = type; + item->create = autoCreate; + + return item; +} + + +boost::unique_future< unsigned int > +IdThreadWorker::getArtistId( const artist_ptr& artist, bool autoCreate ) +{ + QueueItem* item = internalGet( artist, album_ptr(), autoCreate, ArtistType ); + +// qDebug() << "QUEUEING ARTIST:" << artist->name(); + s_mutex.lock(); + s_workQueue.enqueue( item ); + s_mutex.unlock(); + s_waitCond.wakeOne(); +// qDebug() << "DONE WOKE UP THREAD:" << artist->name(); + + return item->promise->get_future(); +} + + +boost::unique_future< unsigned int > +IdThreadWorker::getAlbumId( const album_ptr& album, bool autoCreate ) +{ + QueueItem* item = internalGet( artist_ptr(), album, autoCreate, AlbumType ); + +// qDebug() << "QUEUEING ALUBM:" << album->artist()->name() << album->name(); + s_mutex.lock(); + s_workQueue.enqueue( item ); + s_mutex.unlock(); + s_waitCond.wakeOne(); +// qDebug() << "DONE WOKE UP THREAD:" << album->artist()->name() << album->name(); + + return item->promise->get_future(); +} + + +void +IdThreadWorker::run() +{ + Q_ASSERT( !m_impl ); + + while ( !m_stop ) + { + s_mutex.lock(); +// qDebug() << "IdWorkerThread waiting on condition..."; + s_waitCond.wait( &s_mutex ); +// qDebug() << "IdWorkerThread WOKEN UP"; + if ( !s_workQueue.isEmpty() ) + { + QueueItem* item = s_workQueue.dequeue(); + s_mutex.unlock(); + +// qDebug() << "WITH CONTENTS:" << (item->type == ArtistType ? item->artist->name() : item->album->artist()->name() + " _ " + item->album->name()); + if ( item->type == ArtistType ) + { + unsigned int id = m_impl->artistId( item->artist->name(), item->create ); + item->promise->set_value( id ); + + item->artist->id(); + delete item; + } + else if ( item->type == AlbumType ) + { + unsigned int artistId = m_impl->artistId( item->album->artist()->name(), item->create ); + unsigned int albumId = m_impl->albumId( artistId, item->album->name(), item->create ); + item->promise->set_value( albumId ); + + item->album->id(); + delete item; + } + } + else + { + s_mutex.unlock(); + } + } +} diff --git a/src/libtomahawk/database/IdThreadWorker.h b/src/libtomahawk/database/IdThreadWorker.h new file mode 100644 index 000000000..58c1a5785 --- /dev/null +++ b/src/libtomahawk/database/IdThreadWorker.h @@ -0,0 +1,56 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2012 Leo Franchi + * + * Tomahawk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Tomahawk 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Tomahawk. If not, see . + */ +#ifndef IDTHREADWORKER_H +#define IDTHREADWORKER_H + +#include "DllMacro.h" +#include "Typedefs.h" + +#include +#include +#include +#include + +#include + +class QueueItem; +class Database; +class DatabaseImpl; + +class DLLEXPORT IdThreadWorker : public QThread +{ + Q_OBJECT +public: + explicit IdThreadWorker( Database* db ); + virtual ~IdThreadWorker(); + + void run(); + void stop(); + + static boost::unique_future< unsigned int > getArtistId( const Tomahawk::artist_ptr& artist, bool autoCreate = false ); + static boost::unique_future< unsigned int > getAlbumId( const Tomahawk::album_ptr& album, bool autoCreate = false ); + +private: + Database* m_db; + DatabaseImpl* m_impl; + bool m_stop; + + static QQueue< QueueItem* > s_workQueue; +}; + +#endif // IDTHREADWORKER_H diff --git a/src/libtomahawk/utils/TomahawkUtils.cpp b/src/libtomahawk/utils/TomahawkUtils.cpp index 83cfbf821..40cc0e6be 100644 --- a/src/libtomahawk/utils/TomahawkUtils.cpp +++ b/src/libtomahawk/utils/TomahawkUtils.cpp @@ -832,6 +832,20 @@ extractBinaryResolver( const QString& zipFilename, QObject* receiver ) } +boost::unique_future< unsigned int > +getArtistId( const QString &artistName ) +{ + +} + + +boost::unique_future< unsigned int > +getAlbumId( const QString &artistName, const QString& albumId ) +{ + +} + + } // ns #include "TomahawkUtils.moc"