1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-06 14:16:32 +02:00

After discussion in #qt, I looked at our threading code and it's all wrong/unsafe. This properly threads scanning. Also makes it idle priority and splits it into a separate scan manager

This commit is contained in:
Jeff Mitchell
2011-02-14 20:28:03 -05:00
parent 451c7d6943
commit 10c59d6f9e
12 changed files with 171 additions and 115 deletions

View File

@@ -42,6 +42,7 @@ SET( tomahawkSources ${tomahawkSources}
scrobbler.cpp scrobbler.cpp
shortcuthandler.cpp shortcuthandler.cpp
scanmanager.cpp
tomahawkapp.cpp tomahawkapp.cpp
main.cpp main.cpp
) )
@@ -81,6 +82,7 @@ SET( tomahawkHeaders ${tomahawkHeaders}
musicscanner.h musicscanner.h
scriptresolver.h scriptresolver.h
scrobbler.h scrobbler.h
scanmanager.h
shortcuthandler.h shortcuthandler.h
) )

View File

@@ -8,14 +8,13 @@
using namespace Tomahawk; using namespace Tomahawk;
MusicScanner::MusicScanner( const QString& dir, quint32 bs ) MusicScanner::MusicScanner( const QString& dir, quint32 bs )
: QThread() : QObject()
, m_dir( dir ) , m_dir( dir )
, m_batchsize( bs ) , m_batchsize( bs )
, m_dirLister( 0 )
, m_dirListerThreadController( 0 )
{ {
moveToThread( this );
m_ext2mime.insert( "mp3", "audio/mpeg" ); m_ext2mime.insert( "mp3", "audio/mpeg" );
#ifndef NO_OGG #ifndef NO_OGG
@@ -31,15 +30,6 @@ MusicScanner::MusicScanner( const QString& dir, quint32 bs )
// m_ext2mime.insert( "mp4", "audio/mp4" ); // m_ext2mime.insert( "mp4", "audio/mp4" );
} }
void
MusicScanner::run()
{
QTimer::singleShot( 0, this, SLOT( startScan() ) );
exec();
}
void void
MusicScanner::startScan() MusicScanner::startScan()
{ {
@@ -74,18 +64,21 @@ MusicScanner::scan()
connect( this, SIGNAL( batchReady( QVariantList ) ), connect( this, SIGNAL( batchReady( QVariantList ) ),
SLOT( commitBatch( QVariantList ) ), Qt::DirectConnection ); SLOT( commitBatch( QVariantList ) ), Qt::DirectConnection );
DirLister* lister = new DirLister( QDir( m_dir, 0 ), m_dirmtimes ); m_dirListerThreadController = new QThread( this );
m_dirLister = new DirLister( QDir( m_dir, 0 ), m_dirmtimes );
m_dirLister->moveToThread( m_dirListerThreadController );
connect( lister, SIGNAL( fileToScan( QFileInfo ) ), connect( m_dirLister, SIGNAL( fileToScan( QFileInfo ) ),
SLOT( scanFile( QFileInfo ) ), Qt::QueuedConnection ); SLOT( scanFile( QFileInfo ) ), Qt::QueuedConnection );
// queued, so will only fire after all dirs have been scanned: // queued, so will only fire after all dirs have been scanned:
connect( lister, SIGNAL( finished( const QMap<QString, unsigned int>& ) ), connect( m_dirLister, SIGNAL( finished( const QMap<QString, unsigned int>& ) ),
SLOT( listerFinished( const QMap<QString, unsigned int>& ) ), Qt::QueuedConnection ); SLOT( listerFinished( const QMap<QString, unsigned int>& ) ), Qt::QueuedConnection );
connect( lister, SIGNAL( finished() ), lister, SLOT( deleteLater() ) ); connect( m_dirLister, SIGNAL( destroyed(QObject*) ), this, SLOT( listerDestroyed(QObject*) ) );
lister->start(); m_dirListerThreadController->start();
QMetaObject::invokeMethod( m_dirLister, "go" );
} }
@@ -111,9 +104,20 @@ MusicScanner::listerFinished( const QMap<QString, unsigned int>& newmtimes )
qDebug() << "Skipped the following files (no tags / no valid audio):"; qDebug() << "Skipped the following files (no tags / no valid audio):";
foreach( const QString& s, m_skippedFiles ) foreach( const QString& s, m_skippedFiles )
qDebug() << s; qDebug() << s;
m_dirLister->deleteLater();
} }
void
MusicScanner::listerDestroyed( QObject* dirLister )
{
qDebug() << Q_FUNC_INFO;
m_dirLister = 0;
m_dirListerThreadController->deleteLater();
m_dirListerThreadController = 0;
}
void void
MusicScanner::commitBatch( const QVariantList& tracks ) MusicScanner::commitBatch( const QVariantList& tracks )
{ {

View File

@@ -5,69 +5,26 @@
#include <taglib/tag.h> #include <taglib/tag.h>
#include <QVariantMap> #include <QVariantMap>
#include <QThread>
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QString> #include <QString>
#include <QDebug> #include <QDebug>
#include <QDateTime> #include <QDateTime>
class MusicScanner : public QThread
{
Q_OBJECT
public:
MusicScanner( const QString& dir, quint32 bs = 0 );
protected:
void run();
signals:
//void fileScanned( QVariantMap );
void finished( int, int );
void batchReady( const QVariantList& );
private:
QVariant readFile( const QFileInfo& fi );
private slots:
void listerFinished( const QMap<QString, unsigned int>& newmtimes );
void scanFile( const QFileInfo& fi );
void startScan();
void scan();
void setMtimes( const QMap<QString, unsigned int>& m );
void commitBatch( const QVariantList& );
private:
QString m_dir;
QMap<QString,QString> m_ext2mime; // eg: mp3 -> audio/mpeg
unsigned int m_scanned;
unsigned int m_skipped;
QList<QString> m_skippedFiles;
QMap<QString, unsigned int> m_dirmtimes;
QMap<QString, unsigned int> m_newdirmtimes;
QList<QVariant> m_scannedfiles;
quint32 m_batchsize;
};
#include <QTimer> #include <QTimer>
// descend dir tree comparing dir mtimes to last known mtime // descend dir tree comparing dir mtimes to last known mtime
// emit signal for any dir with new content, so we can scan it. // emit signal for any dir with new content, so we can scan it.
// finally, emit the list of new mtimes we observed. // finally, emit the list of new mtimes we observed.
class DirLister : public QThread class DirLister : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
DirLister( QDir d, QMap<QString, unsigned int>& mtimes ) DirLister( QDir d, QMap<QString, unsigned int>& mtimes )
: QThread(), m_dir( d ), m_dirmtimes( mtimes ) : QObject(), m_dir( d ), m_dirmtimes( mtimes )
{ {
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
moveToThread(this);
} }
~DirLister() ~DirLister()
@@ -75,13 +32,6 @@ public:
qDebug() << Q_FUNC_INFO; qDebug() << Q_FUNC_INFO;
} }
protected:
void run()
{
QTimer::singleShot(0,this,SLOT(go()));
exec();
}
signals: signals:
void fileToScan( QFileInfo ); void fileToScan( QFileInfo );
void finished( const QMap<QString, unsigned int>& ); void finished( const QMap<QString, unsigned int>& );
@@ -130,4 +80,46 @@ private:
QMap<QString, unsigned int> m_newdirmtimes; QMap<QString, unsigned int> m_newdirmtimes;
}; };
class MusicScanner : public QObject
{
Q_OBJECT
public:
MusicScanner( const QString& dir, quint32 bs = 0 );
signals:
//void fileScanned( QVariantMap );
void finished( int, int );
void batchReady( const QVariantList& );
private:
QVariant readFile( const QFileInfo& fi );
private slots:
void listerFinished( const QMap<QString, unsigned int>& newmtimes );
void listerDestroyed( QObject* dirLister );
void scanFile( const QFileInfo& fi );
void startScan();
void scan();
void setMtimes( const QMap<QString, unsigned int>& m );
void commitBatch( const QVariantList& );
private:
QString m_dir;
QMap<QString,QString> m_ext2mime; // eg: mp3 -> audio/mpeg
unsigned int m_scanned;
unsigned int m_skipped;
QList<QString> m_skippedFiles;
QMap<QString, unsigned int> m_dirmtimes;
QMap<QString, unsigned int> m_newdirmtimes;
QList<QVariant> m_scannedfiles;
quint32 m_batchsize;
DirLister* m_dirLister;
QThread* m_dirListerThreadController;
};
#endif #endif

58
src/scanmanager.cpp Normal file
View File

@@ -0,0 +1,58 @@
#include "scanmanager.h"
#include "musicscanner.h"
#include <QDebug>
#include <QThread>
ScanManager* ScanManager::s_instance = 0;
ScanManager*
ScanManager::instance()
{
return s_instance;
}
ScanManager::ScanManager( QObject* parent )
: QObject( parent )
, m_scanner( 0 )
, m_musicScannerThreadController( 0 )
{
s_instance = this;
}
ScanManager::~ScanManager()
{
s_instance = 0;
m_musicScannerThreadController->deleteLater();
m_musicScannerThreadController = 0;
m_scanner->deleteLater();
m_scanner = 0;
}
void
ScanManager::runManualScan( const QString &path )
{
qDebug() << Q_FUNC_INFO;
if ( !m_musicScannerThreadController && !m_scanner ) //still running if these are not zero
{
m_musicScannerThreadController = new QThread( this );
MusicScanner* m_scanner = new MusicScanner( path );
m_scanner->moveToThread( m_musicScannerThreadController );
connect( m_scanner, SIGNAL( finished() ), m_scanner, SLOT( deleteLater() ) );
connect( m_scanner, SIGNAL( destroyed(QObject*) ), this, SLOT( scanDestroyed(QObject*) ) );
m_musicScannerThreadController->start( QThread::IdlePriority );
QMetaObject::invokeMethod( m_scanner, "startScan" );
}
}
void
ScanManager::scannerDestroyed( QObject* scanner )
{
qDebug() << Q_FUNC_INFO;
m_scanner = 0;
m_musicScannerThreadController->deleteLater();
m_musicScannerThreadController = 0;
}

32
src/scanmanager.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef SCANMANAGER_H
#define SCANMANAGER_H
#include <QObject>
#include "dllmacro.h"
class MusicScanner;
class QThread;
class DLLEXPORT ScanManager : public QObject
{
Q_OBJECT
public:
static ScanManager* instance();
explicit ScanManager( QObject* parent = 0 );
virtual ~ScanManager();
void runManualScan( const QString &path );
private slots:
void scannerDestroyed( QObject *scanner );
private:
static ScanManager* s_instance;
MusicScanner* m_scanner;
QThread* m_musicScannerThreadController;
};
#endif

View File

@@ -22,6 +22,7 @@
#include "sip/SipHandler.h" #include "sip/SipHandler.h"
#include "sip/twitter/tomahawkoauthtwitter.h" #include "sip/twitter/tomahawkoauthtwitter.h"
#include <database/database.h> #include <database/database.h>
#include "scanmanager.h"
static QString static QString
md5( const QByteArray& src ) md5( const QByteArray& src )
@@ -145,11 +146,7 @@ SettingsDialog::~SettingsDialog()
s->setScriptResolvers( resolvers ); s->setScriptResolvers( resolvers );
if( rescan ) if( rescan )
{ ScanManager::instance()->runManualScan( s->scannerPath() );
MusicScanner* scanner = new MusicScanner(s->scannerPath() );
connect( scanner, SIGNAL( finished() ), scanner, SLOT( deleteLater() ) );
scanner->start();
}
if( rejabber ) if( rejabber )
{ {
@@ -180,21 +177,6 @@ SettingsDialog::showPathSelector()
} }
void
SettingsDialog::doScan()
{
// TODO this doesnt really belong here..
QString path = ui->lineEditMusicPath->text();
MusicScanner* scanner = new MusicScanner( path );
connect( scanner, SIGNAL( finished() ), scanner, SLOT( deleteLater() ) );
scanner->start();
QMessageBox::information( this, tr( "Scanning Started" ),
tr( "Scanning now, check console output. TODO." ),
QMessageBox::Ok );
}
void void
SettingsDialog::onRejected() SettingsDialog::onRejected()
{ {

View File

@@ -48,8 +48,7 @@ protected:
private slots: private slots:
void onRejected(); void onRejected();
void showPathSelector(); void showPathSelector();
void doScan();
void toggleUpnp( bool preferStaticEnabled ); void toggleUpnp( bool preferStaticEnabled );
void showProxySettings(); void showProxySettings();

View File

@@ -5,6 +5,7 @@
#include <QTimer> #include <QTimer>
#include <QString> #include <QString>
#include <QRegExp> #include <QRegExp>
#include <QThread>
#include <utils/tomahawkutils.h> #include <utils/tomahawkutils.h>
using namespace gloox; using namespace gloox;

View File

@@ -10,7 +10,6 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QMap> #include <QMap>
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QThread>
#include <string> #include <string>

View File

@@ -25,6 +25,7 @@
#include "scriptresolver.h" #include "scriptresolver.h"
#include "sourcelist.h" #include "sourcelist.h"
#include "shortcuthandler.h" #include "shortcuthandler.h"
#include "scanmanager.h"
#include "tomahawksettings.h" #include "tomahawksettings.h"
#include "audio/audioengine.h" #include "audio/audioengine.h"
@@ -149,10 +150,11 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] )
new TomahawkSettings( this ); new TomahawkSettings( this );
m_audioEngine = new AudioEngine; m_audioEngine = new AudioEngine;
new ScanManager( this );
new Pipeline( this ); new Pipeline( this );
new SourceList( this ); new SourceList( this );
m_servent = new Servent( this ); m_servent = new Servent( this );
connect( m_servent, SIGNAL( ready() ), SLOT( setupSIP() ) ); connect( m_servent, SIGNAL( ready() ), SLOT( setupSIP() ) );

View File

@@ -32,13 +32,13 @@
#include "widgets/welcomewidget.h" #include "widgets/welcomewidget.h"
#include "audiocontrols.h" #include "audiocontrols.h"
#include "musicscanner.h"
#include "settingsdialog.h" #include "settingsdialog.h"
#include "tomahawksettings.h" #include "tomahawksettings.h"
#include "sourcelist.h" #include "sourcelist.h"
#include "transferview.h" #include "transferview.h"
#include "tomahawktrayicon.h" #include "tomahawktrayicon.h"
#include "playlist/dynamic/GeneratorInterface.h" #include "playlist/dynamic/GeneratorInterface.h"
#include "scanmanager.h"
using namespace Tomahawk; using namespace Tomahawk;
@@ -249,23 +249,9 @@ TomahawkWindow::rescanCollectionManually()
s->scannerPath(), &ok ); s->scannerPath(), &ok );
s->setValue( "scannerpath", path ); s->setValue( "scannerpath", path );
if ( ok && !path.isEmpty() ) if ( ok && !path.isEmpty() )
{ ScanManager::instance()->runManualScan( path );
MusicScanner* scanner = new MusicScanner( path );
connect( scanner, SIGNAL( finished() ), this, SLOT( scanFinished() ) );
scanner->start();
}
} }
void
TomahawkWindow::scanFinished()
{
qDebug() << Q_FUNC_INFO;
MusicScanner* scanner = (MusicScanner*) sender();
scanner->deleteLater();
}
void void
TomahawkWindow::addPeerManually() TomahawkWindow::addPeerManually()
{ {

View File

@@ -12,6 +12,7 @@
class QAction; class QAction;
class MusicScanner;
class AudioControls; class AudioControls;
class TomahawkTrayIcon; class TomahawkTrayIcon;
@@ -46,11 +47,9 @@ public slots:
void createPlaylist(); void createPlaylist();
void loadSpiff(); void loadSpiff();
void showSettingsDialog(); void showSettingsDialog();
void rescanCollectionManually();
private slots: private slots:
void scanFinished();
void rescanCollectionManually();
void onSipConnected(); void onSipConnected();
void onSipDisconnected(); void onSipDisconnected();
void onSipError(); void onSipError();