Optimize Directory size counting

Two changes:
 * Prioritise size counting for visible path
 * stop the worker when switching dirs
This commit is contained in:
Méven Car 2023-02-12 11:21:53 +00:00
parent a15def4e64
commit ba930ddb36
6 changed files with 79 additions and 39 deletions

View file

@ -422,6 +422,10 @@ void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList &itemRang
m_hoverSequenceLoadedItems.clear(); m_hoverSequenceLoadedItems.clear();
killPreviewJob(); killPreviewJob();
if (m_scanDirectories) {
m_directoryContentsCounter->stopWorker();
}
} else { } else {
// Only remove the items from m_finishedItems. They will be removed // Only remove the items from m_finishedItems. They will be removed
// from the other sets later on. // from the other sets later on.
@ -537,7 +541,7 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem &item, const QPi
QPixmap scaledPixmap = transformPreviewPixmap(pixmap); QPixmap scaledPixmap = transformPreviewPixmap(pixmap);
QHash<QByteArray, QVariant> data = rolesData(item); QHash<QByteArray, QVariant> data = rolesData(item, index);
const QStringList overlays = data["iconOverlays"].toStringList(); const QStringList overlays = data["iconOverlays"].toStringList();
// Strangely KFileItem::overlays() returns empty string-values, so // Strangely KFileItem::overlays() returns empty string-values, so
@ -1210,13 +1214,10 @@ void KFileItemModelRolesUpdater::applySortRole(int index)
data.insert("type", item.mimeComment()); data.insert("type", item.mimeComment());
} else if (m_model->sortRole() == "size" && item.isLocalFile() && !item.isSlow() && item.isDir()) { } else if (m_model->sortRole() == "size" && item.isLocalFile() && !item.isSlow() && item.isDir()) {
const QString path = item.localPath(); startDirectorySizeCounting(item, index);
if (m_scanDirectories) {
m_directoryContentsCounter->scanDirectory(path);
}
} else { } else {
// Probably the sort role is a baloo role - just determine all roles. // Probably the sort role is a baloo role - just determine all roles.
data = rolesData(item); data = rolesData(item, index);
} }
disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged); disconnect(m_model, &KFileItemModel::itemsChanged, this, &KFileItemModelRolesUpdater::slotItemsChanged);
@ -1252,7 +1253,7 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint)
QHash<QByteArray, QVariant> data; QHash<QByteArray, QVariant> data;
if (resolveAll) { if (resolveAll) {
data = rolesData(item); data = rolesData(item, index);
} }
if (!item.iconName().isEmpty()) { if (!item.iconName().isEmpty()) {
@ -1273,7 +1274,19 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint)
return false; return false;
} }
QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem &item) void KFileItemModelRolesUpdater::startDirectorySizeCounting(const KFileItem &item, int index)
{
// Tell m_directoryContentsCounter that we want to count the items
// inside the directory. The result will be received in slotDirectoryContentsCountReceived.
if (m_scanDirectories && item.isLocalFile()) {
const QString path = item.localPath();
const auto priority = index >= m_firstVisibleIndex && index <= m_lastVisibleIndex ? KDirectoryContentsCounter::PathCountPriority::High
: KDirectoryContentsCounter::PathCountPriority::Normal;
m_directoryContentsCounter->scanDirectory(path, priority);
}
}
QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem &item, int index)
{ {
QHash<QByteArray, QVariant> data; QHash<QByteArray, QVariant> data;
@ -1281,16 +1294,7 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
const bool getIsExpandableRole = m_roles.contains("isExpandable"); const bool getIsExpandableRole = m_roles.contains("isExpandable");
if ((getSizeRole || getIsExpandableRole) && !item.isSlow() && item.isDir()) { if ((getSizeRole || getIsExpandableRole) && !item.isSlow() && item.isDir()) {
if (item.isLocalFile()) { startDirectorySizeCounting(item, index);
// Tell m_directoryContentsCounter that we want to count the items
// inside the directory. The result will be received in slotDirectoryContentsCountReceived.
if (m_scanDirectories) {
const QString path = item.localPath();
m_directoryContentsCounter->scanDirectory(path);
}
} else if (getSizeRole) {
data.insert("size", -1); // -1 indicates an unknown number of items
}
} }
if (m_roles.contains("extension")) { if (m_roles.contains("extension")) {

View file

@ -319,7 +319,7 @@ private:
enum ResolveHint { ResolveFast, ResolveAll }; enum ResolveHint { ResolveFast, ResolveAll };
bool applyResolvedRoles(int index, ResolveHint hint); bool applyResolvedRoles(int index, ResolveHint hint);
QHash<QByteArray, QVariant> rolesData(const KFileItem &item); QHash<QByteArray, QVariant> rolesData(const KFileItem &item, int index);
/** /**
* Must be invoked if a property has been changed that affects * Must be invoked if a property has been changed that affects
@ -334,6 +334,8 @@ private:
void trimHoverSequenceLoadedItems(); void trimHoverSequenceLoadedItems();
private: private:
void startDirectorySizeCounting(const KFileItem &item, int index);
enum State { Idle, Paused, ResolvingSortRole, ResolvingAllRoles, PreviewJobRunning }; enum State { Idle, Paused, ResolvingSortRole, ResolvingAllRoles, PreviewJobRunning };
State m_state; State m_state;

View file

@ -44,6 +44,7 @@ KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel *model, QObj
m_worker->moveToThread(m_workerThread); m_worker->moveToThread(m_workerThread);
connect(this, &KDirectoryContentsCounter::requestDirectoryContentsCount, m_worker, &KDirectoryContentsCounterWorker::countDirectoryContents); connect(this, &KDirectoryContentsCounter::requestDirectoryContentsCount, m_worker, &KDirectoryContentsCounterWorker::countDirectoryContents);
connect(this, &KDirectoryContentsCounter::stop, m_worker, &KDirectoryContentsCounterWorker::stop);
connect(m_worker, &KDirectoryContentsCounterWorker::result, this, &KDirectoryContentsCounter::slotResult); connect(m_worker, &KDirectoryContentsCounterWorker::result, this, &KDirectoryContentsCounter::slotResult);
m_dirWatcher = new KDirWatch(this); m_dirWatcher = new KDirWatch(this);
@ -71,11 +72,6 @@ KDirectoryContentsCounter::~KDirectoryContentsCounter()
} }
} }
void KDirectoryContentsCounter::scanDirectory(const QString &path)
{
startWorker(path);
}
void KDirectoryContentsCounter::slotResult(const QString &path, int count, long size) void KDirectoryContentsCounter::slotResult(const QString &path, int count, long size)
{ {
m_workerIsBusy = false; m_workerIsBusy = false;
@ -91,11 +87,11 @@ void KDirectoryContentsCounter::slotResult(const QString &path, int count, long
if (!m_priorityQueue.empty()) { if (!m_priorityQueue.empty()) {
const QString firstPath = m_priorityQueue.front(); const QString firstPath = m_priorityQueue.front();
m_priorityQueue.pop_front(); m_priorityQueue.pop_front();
startWorker(firstPath); scanDirectory(firstPath, PathCountPriority::High);
} else if (!m_queue.empty()) { } else if (!m_queue.empty()) {
const QString firstPath = m_queue.front(); const QString firstPath = m_queue.front();
m_queue.pop_front(); m_queue.pop_front();
startWorker(firstPath); scanDirectory(firstPath, PathCountPriority::Normal);
} }
if (s_cache->contains(resolvedPath)) { if (s_cache->contains(resolvedPath)) {
@ -127,7 +123,7 @@ void KDirectoryContentsCounter::slotDirWatchDirty(const QString &path)
return; return;
} }
startWorker(path); scanDirectory(path, PathCountPriority::High);
} }
} }
@ -156,7 +152,7 @@ void KDirectoryContentsCounter::slotItemsRemoved()
} }
} }
void KDirectoryContentsCounter::startWorker(const QString &path) void KDirectoryContentsCounter::scanDirectory(const QString &path, PathCountPriority priority)
{ {
const QString resolvedPath = QFileInfo(path).canonicalFilePath(); const QString resolvedPath = QFileInfo(path).canonicalFilePath();
const bool alreadyInCache = s_cache->contains(resolvedPath); const bool alreadyInCache = s_cache->contains(resolvedPath);
@ -168,10 +164,16 @@ void KDirectoryContentsCounter::startWorker(const QString &path)
} }
if (m_workerIsBusy) { if (m_workerIsBusy) {
// only enqueue path not yet in queue
if (std::find(m_queue.begin(), m_queue.end(), path) == m_queue.end() if (std::find(m_queue.begin(), m_queue.end(), path) == m_queue.end()
&& std::find(m_priorityQueue.begin(), m_priorityQueue.end(), path) == m_priorityQueue.end()) { && std::find(m_priorityQueue.begin(), m_priorityQueue.end(), path) == m_priorityQueue.end()) {
if (alreadyInCache) { if (priority == PathCountPriority::Normal) {
m_queue.push_back(path); if (alreadyInCache) {
// if we already knew the dir size, it gets lower priority
m_queue.push_back(path);
} else {
m_queue.push_front(path);
}
} else { } else {
// append to priority queue // append to priority queue
m_priorityQueue.push_back(path); m_priorityQueue.push_back(path);
@ -193,4 +195,11 @@ void KDirectoryContentsCounter::startWorker(const QString &path)
} }
} }
void KDirectoryContentsCounter::stopWorker()
{
m_queue.clear();
m_priorityQueue.clear();
Q_EMIT stop();
}
QThread *KDirectoryContentsCounter::m_workerThread = nullptr; QThread *KDirectoryContentsCounter::m_workerThread = nullptr;

View file

@ -21,6 +21,8 @@ class KDirectoryContentsCounter : public QObject
Q_OBJECT Q_OBJECT
public: public:
enum PathCountPriority { Normal, High };
explicit KDirectoryContentsCounter(KFileItemModel *model, QObject *parent = nullptr); explicit KDirectoryContentsCounter(KFileItemModel *model, QObject *parent = nullptr);
~KDirectoryContentsCounter() override; ~KDirectoryContentsCounter() override;
@ -35,7 +37,12 @@ public:
* Uses a cache internally to speed up first result, * Uses a cache internally to speed up first result,
* but emit again result when the cache was updated * but emit again result when the cache was updated
*/ */
void scanDirectory(const QString &path); void scanDirectory(const QString &path, PathCountPriority priority);
/**
* Stops the work until new input is passed
*/
void stopWorker();
Q_SIGNALS: Q_SIGNALS:
/** /**
@ -46,14 +53,13 @@ Q_SIGNALS:
void requestDirectoryContentsCount(const QString &path, KDirectoryContentsCounterWorker::Options options); void requestDirectoryContentsCount(const QString &path, KDirectoryContentsCounterWorker::Options options);
void stop();
private Q_SLOTS: private Q_SLOTS:
void slotResult(const QString &path, int count, long size); void slotResult(const QString &path, int count, long size);
void slotDirWatchDirty(const QString &path); void slotDirWatchDirty(const QString &path);
void slotItemsRemoved(); void slotItemsRemoved();
private:
void startWorker(const QString &path);
private: private:
KFileItemModel *m_model; KFileItemModel *m_model;

View file

@ -25,7 +25,7 @@ KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject *parent
#ifndef Q_OS_WIN #ifndef Q_OS_WIN
KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounterWorker::CountResult
walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDirectoriesOnly, const uint allowedRecursiveLevel) KDirectoryContentsCounterWorker::walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDirectoriesOnly, const uint allowedRecursiveLevel)
{ {
int count = -1; int count = -1;
long size = -1; long size = -1;
@ -36,7 +36,7 @@ walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDir
QT_DIRENT *dirEntry; QT_DIRENT *dirEntry;
QT_STATBUF buf; QT_STATBUF buf;
while ((dirEntry = QT_READDIR(dir))) { while (!m_stopping && (dirEntry = QT_READDIR(dir))) {
if (dirEntry->d_name[0] == '.') { if (dirEntry->d_name[0] == '.') {
if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) { if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) {
// Skip "." or hidden files // Skip "." or hidden files
@ -64,7 +64,7 @@ walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDir
size += buf.st_size; size += buf.st_size;
} }
} }
if (dirEntry->d_type == DT_DIR) { if (!m_stopping && dirEntry->d_type == DT_DIR) {
// recursion for dirs // recursion for dirs
auto subdirResult = walkDir(nameBuf, countHiddenFiles, countDirectoriesOnly, allowedRecursiveLevel - 1); auto subdirResult = walkDir(nameBuf, countHiddenFiles, countDirectoriesOnly, allowedRecursiveLevel - 1);
if (subdirResult.size > 0) { if (subdirResult.size > 0) {
@ -106,8 +106,18 @@ KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounterWorker::su
#endif #endif
} }
void KDirectoryContentsCounterWorker::stop()
{
m_stopping = true;
}
void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options) void KDirectoryContentsCounterWorker::countDirectoryContents(const QString &path, Options options)
{ {
m_stopping = false;
auto res = subItemsCount(path, options); auto res = subItemsCount(path, options);
Q_EMIT result(path, res.count, res.size);
if (!m_stopping) {
Q_EMIT result(path, res.count, res.size);
}
} }

View file

@ -36,7 +36,7 @@ public:
* *
* @return The number of items. * @return The number of items.
*/ */
static CountResult subItemsCount(const QString &path, Options options); CountResult subItemsCount(const QString &path, Options options);
Q_SIGNALS: Q_SIGNALS:
/** /**
@ -53,6 +53,15 @@ public Q_SLOTS:
// is needed here. Just using 'Options' is OK for the compiler, but // is needed here. Just using 'Options' is OK for the compiler, but
// confuses moc. // confuses moc.
void countDirectoryContents(const QString &path, KDirectoryContentsCounterWorker::Options options); void countDirectoryContents(const QString &path, KDirectoryContentsCounterWorker::Options options);
void stop();
private:
#ifndef Q_OS_WIN
KDirectoryContentsCounterWorker::CountResult
walkDir(const QString &dirPath, const bool countHiddenFiles, const bool countDirectoriesOnly, const uint allowedRecursiveLevel);
#endif
bool m_stopping = false;
}; };
Q_DECLARE_METATYPE(KDirectoryContentsCounterWorker::Options) Q_DECLARE_METATYPE(KDirectoryContentsCounterWorker::Options)