One of the reasons for collection scanner as a separate executable was to prevent a crash in collectionscanner from bringing down Amarok as well. As both processes only communicated with each other via files or stdout/stdin, this was relatively safe. Is it ensured that a corrpution of the shared memory data does not crash both processes?
On Sat, Nov 6, 2010 at 4:05 AM, Ralf Engels <ralf-eng...@gmx.de> wrote: > commit b18a028c66b4aff2df1df9cf63fe1eb712a9b8bd > branch master > Author: Ralf Engels <ralf-eng...@gmx.de> > Date: Fri Nov 5 19:14:32 2010 +0100 > > Use shared memory for collection scanner to remember last scanning > position increasing the scanning speed. > Fix problem with track and stream resume > > diff --git a/src/core-impl/collections/db/ScanManager.cpp > b/src/core-impl/collections/db/ScanManager.cpp > index b02a80d..c145e08 100644 > --- a/src/core-impl/collections/db/ScanManager.cpp > +++ b/src/core-impl/collections/db/ScanManager.cpp > @@ -41,6 +41,7 @@ > #include <QListIterator> > #include <QStringList> > #include <QTextCodec> > +#include <QSharedMemory> > > #include <KMessageBox> > #include <KStandardDirs> > @@ -53,12 +54,14 @@ static const int MAX_RESTARTS = 80; > static const int MAX_FAILURE_PERCENTAGE = 5; > static const int WATCH_INTERVAL = 60 * 1000; // = 60 seconds > > +static const int SHARED_MEMORY_SIZE = 1024 * 1024; // 1 MB shared memory > > ScanManager::ScanManager( Collections::DatabaseCollection *collection, > QObject *parent ) > : QObject( parent ) > , m_collection( static_cast<Collections::SqlCollection*>( collection ) ) > , m_scanner( 0 ) > , m_parser( 0 ) > + , m_scannerStateMemory( 0 ) > , m_restartCount( 0 ) > , m_blockCount( 0 ) > , m_fullScanRequested( false ) > @@ -218,6 +221,20 @@ ScanManager::startScannerProcess( bool restart ) > Q_ASSERT( m_parser ); > Q_ASSERT( !m_scanner ); > > + // -- create the shared memory > + if( !m_scannerStateMemory && !restart ) > + { > + m_sharedMemoryKey = > "AmarokScannerMemory"+QDateTime::current().toString(); > + m_scannerStateMemory = new QSharedMemory( m_sharedMemoryKey, this ); > + if( !m_scannerStateMemory.create( SHARED_MEMORY_SIZE ) ) > + { > + warning() << "Unable to create shared memory for collection > scanner"; > + delete m_scannerStateMemory; > + m_scannerStateMemory = 0; > + } > + } > + > + // -- create the scanner process > m_scanner = new AmarokProcess( this ); > *m_scanner << scannerPath() << "--idlepriority"; > if( !m_fullScanRequested ) > @@ -232,6 +249,9 @@ ScanManager::startScannerProcess( bool restart ) > if( restart ) > *m_scanner << "-s"; > > + if( m_scannerStateMemory ) > + *m_scanner << "--sharedmemory" << m_sharedMemoryKey; > + > *m_scanner << "--batch" << m_batchfilePath; > > m_scanner->setOutputChannelMode( KProcess::OnlyStdoutChannel ); > @@ -300,15 +320,14 @@ ScanManager::slotFinished(int exitCode, > QProcess::ExitStatus exitStatus) > > { > QMutexLocker locker( &m_mutex ); > - m_scanner->terminate(); > - m_scanner->deleteLater(); > - m_scanner = 0; > - m_restartCount = 0; > - > - m_fullScan = false; > - m_scanDirs.clear(); > + if( m_scanner ) > + { > + stopScanner(); > > - QFile::remove( m_batchfilePath ); > + m_restartCount = 0; > + m_fullScan = false; > + m_scanDirs.clear(); > + } > } > } > > @@ -337,15 +356,10 @@ ScanManager::abort( const QString &reason ) > QMutexLocker locker( &m_mutex ); > if( m_scanner ) > { > - disconnect( m_scanner, 0, this, 0 ); > - m_scanner->terminate(); > - m_scanner->deleteLater(); > - m_scanner = 0; > + stopScanner(); > > m_fullScan = false; > m_scanDirs.clear(); > - > - QFile::remove( m_batchfilePath ); > } > } > > @@ -426,6 +440,23 @@ ScanManager::handleRestart() > void > ScanManager::stopParser() > { > + // stop the scanner > + disconnect( m_scanner, 0, this, 0 ); > + m_scanner->terminate(); > + m_scanner->deleteLater(); > + m_scanner = 0; > + > + // free it's shared memory. > + delete m_scannerStateMemory; > + m_scannerStateMemory = 0; > + > + // remove the batch file > + QFile::remove( m_batchfilePath ); > +} > + > +void > +ScanManager::stopParser() > +{ > QMutexLocker locker( &m_mutex ); > if( m_parser ) > { > diff --git a/src/core-impl/collections/db/ScanManager.h > b/src/core-impl/collections/db/ScanManager.h > index 9fcdafd..4e20cbb 100644 > --- a/src/core-impl/collections/db/ScanManager.h > +++ b/src/core-impl/collections/db/ScanManager.h > @@ -35,6 +35,7 @@ > #include <threadweaver/Job.h> > > class XmlParseJob; > +class QSharedMemory; > > class AMAROK_DATABASECOLLECTION_EXPORT_TESTS ScanManager : public QObject > { > @@ -110,12 +111,15 @@ class AMAROK_DATABASECOLLECTION_EXPORT_TESTS > ScanManager : public QObject > > void handleRestart(); > > + void stopScanner(); > void stopParser(); > > private: > Collections::DatabaseCollection *m_collection; > > AmarokProcess *m_scanner; > + QSharedMemory *m_scannerStateMemory; // a persistent storage of the > current scanner state in case it needs to be restarted. > + QString m_sharedMemoryKey; > XmlParseJob *m_parser; > > int m_restartCount; > diff --git a/utilities/collectionscanner/CollectionScanner.cpp > b/utilities/collectionscanner/CollectionScanner.cpp > index 7a4b588..e188272 100644 > --- a/utilities/collectionscanner/CollectionScanner.cpp > +++ b/utilities/collectionscanner/CollectionScanner.cpp > @@ -36,10 +36,13 @@ > #include <QStringList> > #include <QDir> > #include <QFile> > -#include <QSettings> > #include <QDateTime> > #include <QXmlStreamReader> > #include <QXmlStreamWriter> > +#include <QSharedMemory> > +#include <QByteArray> > +#include <QTextStream> > +#include <QDebug> > > #include <audiblefiletyperesolver.h> > #include <realmediafiletyperesolver.h> > @@ -54,6 +57,157 @@ main( int argc, char *argv[] ) > return scanner.exec(); > } > > + > +// ------------ the scanning state ----------- > + > +CollectionScanner::ScanningState::ScanningState() > + : m_sharedMemory(0) > +{ > +} > + > +CollectionScanner::ScanningState::~ScanningState() > +{ > + delete m_sharedMemory; > +} > + > +void > +CollectionScanner::ScanningState::setKey( const QString &key ) > +{ > + delete m_sharedMemory; > + m_sharedMemory = new QSharedMemory( key ); > + if( m_sharedMemory->attach() ) > + readFull(); > +} > + > +bool > +CollectionScanner::ScanningState::isValid() const > +{ > + return m_sharedMemory && m_sharedMemory->isAttached(); > +} > + > +QString > +CollectionScanner::ScanningState::lastDirectory() const > +{ return m_lastDirectory; } > + > +void > +CollectionScanner::ScanningState::setLastDirectory( const QString &dir ) > +{ > + if( dir == m_lastDirectory ) > + return; > + > + m_lastDirectory = dir; > + writeFull(); > +} > + > +QStringList > +CollectionScanner::ScanningState::directories() const > +{ return m_directories; } > + > +void > +CollectionScanner::ScanningState::setDirectories( const QStringList > &directories ) > +{ > + if( directories == m_directories ) > + return; > + > + m_directories = directories; > + writeFull(); > +} > + > +QStringList > +CollectionScanner::ScanningState::badFiles() const > +{ return m_badFiles; } > + > +void > +CollectionScanner::ScanningState::setBadFiles( const QStringList &badFiles ) > +{ > + if( badFiles == m_badFiles ) > + return; > + > + m_badFiles = badFiles; > + writeFull(); > +} > + > +QString > +CollectionScanner::ScanningState::lastFile() const > +{ return m_lastFile; } > + > +void > +CollectionScanner::ScanningState::setLastFile( const QString &file ) > +{ > + if( file == m_lastFile ) > + return; > + > + m_sharedMemory->lock(); > + QByteArray data = QByteArray::fromRawData((char*)m_sharedMemory->data(), > m_sharedMemory->size()); > + QTextStream out( &data ); > + out.seek( m_lastFilePos ); > + out << m_lastFile; > + m_sharedMemory->unlock(); > +} > + > +void > +CollectionScanner::ScanningState::readFull() > +{ > + if( !isValid() ) > + return; > + > + m_sharedMemory->lock(); > + QByteArray data = QByteArray::fromRawData((char*)m_sharedMemory->data(), > m_sharedMemory->size()); > + QTextStream in(&data, QIODevice::ReadOnly); > + > + int count; > + > + in >> m_lastDirectory; > + > + m_directories.clear(); > + in >> count; > + for( int i=0; i<count; i++ ) > + { > + QString s; > + in >> s; > + m_directories.append( s ); > + } > + > + m_badFiles.clear(); > + in >> count; > + for( int i=0; i<count; i++ ) > + { > + QString s; > + in >> s; > + m_badFiles.append( s ); > + } > + > + m_lastFilePos = in.pos(); > + in >> m_lastFile; > + > + m_sharedMemory->unlock(); > +} > + > +void > +CollectionScanner::ScanningState::writeFull() > +{ > + if( !isValid() ) > + return; > + > + m_sharedMemory->lock(); > + QByteArray data = QByteArray::fromRawData((char*)m_sharedMemory->data(), > m_sharedMemory->size()); > + QTextStream out( &data ); > + out << m_lastDirectory; > + out << m_directories.count(); > + foreach( const QString &s, m_directories ) > + out << s; > + out << m_badFiles.count(); > + foreach( const QString &s, m_badFiles ) > + out << s; > + m_lastFilePos = out.pos(); > + out << m_lastFile; > + qWarning("shared memory at %d of %d", out.pos(), m_sharedMemory->size()); > + m_sharedMemory->unlock(); > +} > + > + > +// ------------ the scanner ----------- > + > CollectionScanner::Scanner::Scanner( int &argc, char **argv ) > : QCoreApplication( argc, argv ) > , m_charset( false ) > @@ -78,19 +232,6 @@ CollectionScanner::Scanner::Scanner( int &argc, char > **argv ) > TagLib::FileRef::addFileTypeResolver(new ASFFileTypeResolver); > TagLib::FileRef::addFileTypeResolver(new MP4FileTypeResolver); > TagLib::FileRef::addFileTypeResolver(new WAVFileTypeResolver); > - > - QString env = qgetenv( "KDEHOME" ); > - if( env.isEmpty() || !QDir(env).isReadable() ) > - env = QString( "%1/.kde" ).arg( QDir::homePath() ); > - QString path = env + QLatin1String("/share/apps"); > - > - // give two different settings. > - // prevent the possibility that an incremental and a non-incremental > scan clash > - QSettings::setPath( QSettings::NativeFormat, QSettings::UserScope, path > ); > - if( m_incremental ) > - m_settings = new QSettings( "amarok", > "CollectionScannerIncremental", this ); > - else > - m_settings = new QSettings( "amarok", "CollectionScanner", this ); > } > > > @@ -144,11 +285,11 @@ CollectionScanner::Scanner::doJob() //SLOT > // --- determine the directories we have to scan > QStringList entries; > > - // -- when restarting read them from the settings > - if( m_restart ) > + // -- when restarting read them from the shared memory > + if( m_restart && m_scanningState.isValid() ) > { > - QString lastEntry = m_settings->value( "lastDirectory" ).toString(); > - entries = m_settings->value( "directories" ).toStringList(); > + QString lastEntry = m_scanningState.lastDirectory(); > + entries = m_scanningState.directories(); > > // remove the entries we already scanned > while( entries.front() != lastEntry && !entries.empty() ) > @@ -180,8 +321,8 @@ CollectionScanner::Scanner::doJob() //SLOT > } > > entries = entriesSet.toList(); > - m_settings->setValue( "lastDirectory", QString() ); > - m_settings->setValue( "directories", entries ); > + m_scanningState.setLastDirectory( QString() ); > + m_scanningState.setDirectories( entries ); > } > > if( !m_restart ) > @@ -194,7 +335,7 @@ CollectionScanner::Scanner::doJob() //SLOT > // --- now do the scanning > foreach( QString path, entries ) > { > - CollectionScanner::Directory dir( path, m_settings, > + CollectionScanner::Directory dir( path, &m_scanningState, > m_incremental && !isModified( path > ) ); > > xmlWriter.writeStartElement( "directory" ); > @@ -295,6 +436,14 @@ CollectionScanner::Scanner::readArgs() > missingArg = true; > argnum++; > } > + else if( myarg == "sharedmemory" ) > + { > + if( argslist.count() > argnum + 1 ) > + m_scanningState.setKey( argslist.at( argnum + 1 ) ); > + else > + missingArg = true; > + argnum++; > + } > else if( myarg == "version" ) > displayVersion(); > else if( myarg == "incremental" ) > @@ -392,6 +541,7 @@ CollectionScanner::Scanner::displayHelp( const QString > &error ) > "-i, --incremental : Incremental scan (modified folders only)\n" > "-s, --restart : After a crash, restart the scanner in its > last position\n" > " --idlepriority : Run at idle priority\n" > + " --sharedmemory <key> : A shared memory segment to be used for > restarting a scan\n" > " --newer <path> : Only scan directories if modification time > is new than <path>\n" > " Only useful in incremental scan mode\n" > " --batch <path> : Add the directories from the batch xml > file\n" > diff --git a/utilities/collectionscanner/CollectionScanner.h > b/utilities/collectionscanner/CollectionScanner.h > index 4a21248..4968c98 100644 > --- a/utilities/collectionscanner/CollectionScanner.h > +++ b/utilities/collectionscanner/CollectionScanner.h > @@ -33,11 +33,49 @@ > #include <QSet> > #include <QStringList> > > -class QSettings; > +class QSharedMemory; > > namespace CollectionScanner > { > > +/** A class used to store the current scanning state in a shared memory > segment. > + Storing the state of the scanner shouldn't cause a file access. > + We are using a shared memory that the amarok process holds open until > the scanning > + is finished to store the state. > + */ > +class ScanningState > +{ > + public: > + ScanningState(); > + ~ScanningState(); > + > + void setKey( const QString &key ); > + bool isValid() const; > + > + QString lastDirectory() const; > + void setLastDirectory( const QString &dir ); > + QStringList directories() const; > + void setDirectories( const QStringList &directories ); > + > + QStringList badFiles() const; > + void setBadFiles( const QStringList &badFiles ); > + QString lastFile() const; > + void setLastFile( const QString &file ); > + > + private: > + void readFull(); > + void writeFull(); > + > + QSharedMemory *m_sharedMemory; > + > + QString m_lastDirectory; > + QStringList m_directories; > + QStringList m_badFiles; > + QString m_lastFile; > + qint64 m_lastFilePos; > +}; > + > + > /** > * @class Scanner > * @short Scans directories and builds the Collection > @@ -92,8 +130,7 @@ private: > bool m_idlePriority; > > QString m_mtimeFile; > - > - QSettings* m_settings; > + ScanningState m_scanningState; > > > // Disable copy constructor and assignment > diff --git a/utilities/collectionscanner/Directory.cpp > b/utilities/collectionscanner/Directory.cpp > index 76ceb99..7acd8ed 100644 > --- a/utilities/collectionscanner/Directory.cpp > +++ b/utilities/collectionscanner/Directory.cpp > @@ -37,7 +37,9 @@ > > #ifdef UTILITIES_BUILD > > -CollectionScanner::Directory::Directory( const QString &path, QSettings > *settings, bool skip ) > +CollectionScanner::Directory::Directory( const QString &path, > + CollectionScanner::ScanningState > *state, > + bool skip ) > : m_ignored( false ) > { > m_path = path; > @@ -55,26 +57,26 @@ CollectionScanner::Directory::Directory( const QString > &path, QSettings *setting > return; > } > > - > - QStringList validImages; validImages << "jpg" << "png" << "gif" << > "jpeg" << "bmp"; > - QStringList validPlaylists; validPlaylists << "m3u" << "pls" << "xspf"; > + QStringList validImages; > + validImages << "jpg" << "png" << "gif" << "jpeg" << "bmp" << "svg" << > "xpm"; > + QStringList validPlaylists; > + validPlaylists << "m3u" << "pls" << "xspf"; > > // --- check if we were restarted and failed at a file > QStringList badFiles; > > - QString lastEntry = settings->value( "lastDirectory" ).toString(); > - if( lastEntry == path ) > + if( state->lastDirectory() == path ) > { > - badFiles << settings->value( "lastFile" ).toString(); > - badFiles << settings->value( "badFiles" ).toStringList(); > + badFiles << state->badFiles(); > + badFiles << state->lastFile(); > > - settings->setValue( "badFiles", badFiles ); > + state->setBadFiles( badFiles ); > } > else > { > - settings->setValue( "lastDirectory", path ); > - settings->setValue( "lastFile", QString() ); > - settings->setValue( "badFiles", QStringList() ); > + state->setLastDirectory( path ); > + state->setLastFile( QString() ); > + state->setBadFiles( badFiles ); > } > > QStringList covers; > @@ -92,10 +94,6 @@ CollectionScanner::Directory::Directory( const QString > &path, QSettings *setting > if( badFiles.contains( f.absoluteFilePath() ) ) > continue; > > - // remember the last file before it get's dangerous. Before starting > taglib > - settings->setValue( "lastFile", f.absoluteFilePath() ); > - settings->sync(); > - > const QString suffix = fi.suffix().toLower(); > const QString filePath = f.absoluteFilePath(); > > @@ -103,8 +101,8 @@ CollectionScanner::Directory::Directory( const QString > &path, QSettings *setting > if( validImages.contains( suffix ) ) > { > covers += filePath; > - > } > + > // -- playlist ? > else if( validPlaylists.contains( suffix ) ) > { > @@ -114,6 +112,9 @@ CollectionScanner::Directory::Directory( const QString > &path, QSettings *setting > // -- audio track ? > else > { > + // remember the last file before it get's dangerous. Before > starting taglib > + state->setLastFile( f.absoluteFilePath() ); > + > CollectionScanner::Track newTrack( filePath ); > if( newTrack.isValid() ) > { > diff --git a/utilities/collectionscanner/Directory.h > b/utilities/collectionscanner/Directory.h > index 8ba28f7..091390f 100644 > --- a/utilities/collectionscanner/Directory.h > +++ b/utilities/collectionscanner/Directory.h > @@ -34,16 +34,17 @@ class QXmlStreamWriter; > namespace CollectionScanner > { > > +class ScanningState; > + > /** > * @class Directory > * @short Represents a scanned directory and it's contents > */ > - > class Directory > { > public: > #ifdef UTILITIES_BUILD > - Directory( const QString &path, QSettings *settings, bool skip = false ); > + Directory( const QString &path, ScanningState *state, bool skip ); > #endif // UTILITIES_BUILD > > /** Reads a directory from an xml stream. > diff --git a/utilities/collectionscanner/Track.cpp > b/utilities/collectionscanner/Track.cpp > index 9291662..d0458d7 100644 > --- a/utilities/collectionscanner/Track.cpp > +++ b/utilities/collectionscanner/Track.cpp > @@ -129,7 +129,7 @@ CollectionScanner::Track::Track( const QString &path) > if( !fileref.isNull() ) > { > tag = fileref.tag(); > - if ( tag ) > + if( tag ) > { > m_title = strip( tag->title() ); > m_artist = strip( tag->artist() ); > @@ -404,7 +404,7 @@ CollectionScanner::Track::Track( const QString &path) > > m_filesize = QFile( path ).size(); > > - if( m_uniqueid.isEmpty() ) > + if( m_valid && m_uniqueid.isEmpty() ) > { > AFTUtility aftutil; > m_uniqueid = "amarok-sqltrackuid://" + aftutil.readUniqueId( path ); > _______________________________________________ Amarok-devel mailing list Amarok-devel@kde.org https://mail.kde.org/mailman/listinfo/amarok-devel