Commit eb4a4d15 authored by Michael Pyne's avatar Michael Pyne

Use a thread pool for the threaded music loader.

Large music libraries can cause dozens/hundreds or even more of threads
to be created at once. But all we really need is to do the loading off
of the GUI thread, and the I/O will be the bottleneck no matter how many
threads we use. So use Qt Concurrent to manage a threadpool instead,
which also simplifies the code somewhat.

I also fixed the broken global status updating when using threaded
loader while refactoring to support this.
parent 3cf74c35
......@@ -25,7 +25,7 @@ include(ECMInstallIcons)
include(ECMAddAppIcon)
include(ECMQtDeclareLoggingCategory)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Gui Svg Network Test Widgets)
find_package(Qt5 ${QT_MIN_VERSION} REQUIRED COMPONENTS Concurrent Gui Svg Network Test Widgets)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS CoreAddons Completion Config Crash GlobalAccel
I18n IconThemes DocTools KIO JobWidgets Notifications TextWidgets XmlGui Wallet
WidgetsAddons WindowSystem)
......
......@@ -54,7 +54,8 @@
#include <QStackedWidget>
#include <QScrollBar>
#include <QPainter>
#include <QThread>
#include <QtConcurrent>
#include <id3v1genres.h>
......@@ -1138,24 +1139,28 @@ void Playlist::addFiles(const QStringList &files, PlaylistItem *after)
if(!after)
after = static_cast<PlaylistItem *>(topLevelItem(topLevelItemCount() - 1));
QApplication::setOverrideCursor(Qt::WaitCursor);
m_blockDataChanged = true;
m_itemsLoading++;
setEnabled(false);
FileHandleList queue;
for(const auto &file : files) {
// some files added here will launch threads that will do cleanup (fix
// the cursor, allow data updates etc) when the last thread is done.
// Managed by m_itemsLoading going to 0 which is why we ++ above.
addUntypedFile(file, queue, true, &after);
}
addFileHelper(queue, &after, true);
m_blockDataChanged = false;
slotWeightDirty();
playlistItemsChanged();
QApplication::restoreOverrideCursor();
// If no items are being loaded by now then we must have loaded all M3U
// playlists or something, so cleanup immediately since no threads will
// have been launched.
if(--m_itemsLoading == 0) {
cleanupAfterAllFileLoadsCompleted();
}
}
void Playlist::refreshAlbums(const PlaylistItemList &items, coverKey id)
......@@ -1611,6 +1616,37 @@ void Playlist::addPlaylistFile(const QString &m3uFile)
}
}
void Playlist::addFilesFromDirectory(const QString &dirPath)
{
++m_itemsLoading;
DirectoryLoader *loader = new DirectoryLoader(dirPath);
connect(loader, &DirectoryLoader::doneLoading, this,
[this, loader]() {
loader->deleteLater();
if(--m_itemsLoading == 0) {
cleanupAfterAllFileLoadsCompleted();
}
}
);
connect(loader, &DirectoryLoader::loadedPlaylist, this,
[this](const QString &m3uFile) {
addPlaylistFile(m3uFile);
}
);
connect(loader, &DirectoryLoader::loadedFiles, this,
[this](const FileHandleList &newFiles) {
for(const auto newFile : newFiles) {
createItem(newFile);
}
}
);
(void) QtConcurrent::run(loader, &DirectoryLoader::startLoading);
}
/**
* Super spaghetti function that adds music files, m3u playlist files, or directories
* into the playlist as appropriate, but only if the playlist doesn't already contain
......@@ -1653,34 +1689,7 @@ void Playlist::addUntypedFile(const QString &file, FileHandleList &files, bool i
return; // Exclude it
}
DirectoryLoader *loader = new DirectoryLoader(canonicalPath);
QThread *loaderThread = new QThread;
loader->moveToThread(loaderThread);
connect(loaderThread, &QThread::started, loader, &DirectoryLoader::startLoading);
connect(loader, &DirectoryLoader::doneLoading, loaderThread, &QThread::quit);
connect(loader, &DirectoryLoader::doneLoading, loader, &QObject::deleteLater);
connect(loaderThread, &QThread::finished, loaderThread, &QObject::deleteLater);
connect(loader, &DirectoryLoader::loadedPlaylist, this,
[this](const QString &m3uFile) {
addPlaylistFile(m3uFile);
}
);
connect(loader, &DirectoryLoader::loadedFiles, this,
[this](const FileHandleList &newFiles) {
// NOTE: after and files are both invalid by this point since
// this can be called long after our own caller has returned.
PlaylistItem *after = nullptr;
for(const auto newFile : newFiles) {
after = createItem(newFile, after);
}
}
);
loaderThread->start();
addFilesFromDirectory(canonicalPath);
}
}
......@@ -1717,6 +1726,17 @@ void Playlist::addFileHelper(FileHandleList &files, PlaylistItem **after, bool i
}
}
// Called directly or after a threaded directory load has completed, managed by
// m_itemsLoading
void Playlist::cleanupAfterAllFileLoadsCompleted()
{
m_blockDataChanged = false;
setEnabled(true);
slotWeightDirty();
playlistItemsChanged();
}
////////////////////////////////////////////////////////////////////////////////
// private slots
////////////////////////////////////////////////////////////////////////////////
......
......@@ -539,10 +539,12 @@ private:
void calculateColumnWeights();
void addPlaylistFile(const QString &m3uFile);
void addFilesFromDirectory(const QString &dirPath);
void addUntypedFile(const QString &file, FileHandleList &files, bool importPlaylists,
PlaylistItem **after);
void addFileHelper(FileHandleList &files, PlaylistItem **after,
bool ignoreTimer = false);
void cleanupAfterAllFileLoadsCompleted();
void redisplaySearch() { setSearch(m_search); }
......@@ -699,6 +701,9 @@ private:
KActionMenu *m_columnVisibleAction;
PlaylistToolTip *m_toolTip;
int m_itemsLoading = 0; /// Count of pending file loads outstanding
bool m_blockDataChanged;
/**
* This is used to indicate if the list of visible items has changed (via a
* call to setVisibleItems()) while random play is playing.
......@@ -707,8 +712,6 @@ private:
static bool m_shuttingDown;
static int m_leftColumn;
static QVector<PlaylistItem *> m_backMenuItems;
bool m_blockDataChanged;
};
typedef QList<Playlist *> PlaylistList;
......
......@@ -220,13 +220,7 @@ void PlaylistBox::duplicate()
void PlaylistBox::scanFolders()
{
qCDebug(JUK_LOG) << "Starting folder scan";
QTime stopwatch; stopwatch.start();
PlaylistCollection::scanFolders();
qCDebug(JUK_LOG) << "Folder scan complete, took" << stopwatch.elapsed() << "ms";
qCDebug(JUK_LOG) << "Startup complete!";
emit startupComplete();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment