Skip to content

Commit

Permalink
all IO operations should be done in background thread
Browse files Browse the repository at this point in the history
  • Loading branch information
vigsterkr committed Feb 15, 2021
1 parent 6eadc04 commit c602db4
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 61 deletions.
5 changes: 2 additions & 3 deletions src/library/browse/browsefeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ BrowseFeature::BrowseFeature(
#elif defined(__APPLE__)
// Apple hides the base Linux file structure But all devices are mounted at
// /Volumes
pRootItem->appendChild(tr("Devices"), "/Volumes/");
pRootItem->appendChild(tr("Devices"), "/Volumes");
#else // LINUX
// DEVICE_NODE contents will be rendered lazily in onLazyChildExpandation.
pRootItem->appendChild(tr("Removable Devices"), DEVICE_NODE);
Expand Down Expand Up @@ -401,8 +401,7 @@ void BrowseFeature::onLazyChildExpandation(const QModelIndex& index) {
if (path == DEVICE_NODE) {
folders += getRemovableDevices();
} else {
MDir dir(path);
m_childModel.processFolder(index, dir);
m_childModel.processFolder(index, path);
// FIXME: create QTimer to update label
/*
auto title = m_childModel.data(index, Qt::DisplayRole);
Expand Down
93 changes: 50 additions & 43 deletions src/library/browse/foldertreemodel.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#if defined (__WINDOWS__)
#include <windows.h>
#if defined(__WINDOWS__)
#include <Shellapi.h>
#include <Shlobj.h>
#include <windows.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif

#include <QFileInfo>
Expand All @@ -18,25 +18,23 @@
#include "library/treeitem.h"
#include "moc_foldertreemodel.cpp"
#include "util/file.h"
#include "util/logger.h"

namespace {

mixxx::Logger kLogger("FolderTreeModel");

} // namespace

FolderTreeModel::FolderTreeModel(QObject *parent)
FolderTreeModel::FolderTreeModel(QObject* parent)
: TreeItemModel(parent), m_isRunning(true) {
QObject::connect(&m_fsWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(dirModified(QString)));
connect(this, &FolderTreeModel::newChildren, this, &FolderTreeModel::addChildren, Qt::QueuedConnection);
connect(this, &FolderTreeModel::hasSubDirectory, this, &FolderTreeModel::onHasSubDirectory, Qt::QueuedConnection);

m_pool.setMaxThreadCount(4);
QtConcurrent::run(&m_pool, [&]() {
while (m_isRunning.load(std::memory_order_consume)) {
if (!m_folderQueue.isEmpty()) {
const auto& [parent, dir] = m_folderQueue.dequeue();
kLogger.debug() << "processing " << dir.dir().dirName();
m_queueLock.lock();
const auto& [parent, path] = m_folderQueue.dequeue();
m_queueLock.unlock();

m_cacheLock.lockForWrite();
MDir dir(path);
QFileInfoList all = dir.dir().entryInfoList(
QDir::Dirs | QDir::NoDotAndDotDot);

Expand All @@ -51,18 +49,24 @@ FolderTreeModel::FolderTreeModel(QObject *parent)
// We here create new items for the sidebar models
// Once the items are added to the TreeItemModel,
// the models takes ownership of them and ensures their deletion
const auto& absPath = one.absoluteFilePath();
auto* folder = new TreeItem(
one.fileName(),
QVariant(one.absoluteFilePath() + QStringLiteral("/")));
QVariant(absPath));
// init cache
sync.addFuture(QtConcurrent::run(&m_pool, [&]() {
this->directoryHasChildren(one.absoluteFilePath());
}));
if (m_directoryCache.find(absPath) == m_directoryCache.end()) {
sync.addFuture(QtConcurrent::run(&m_pool, [this, absPath]() {
this->directoryHasChildren(absPath);
}));
}
folders->push_back(folder);
}
if (!folders->isEmpty()) {
sync.waitForFinished();
m_cacheLock.unlock();
emit newChildren(parent, folders);
} else {
m_cacheLock.unlock();
}
}
QThread::sleep(1);
Expand All @@ -83,7 +87,7 @@ FolderTreeModel::~FolderTreeModel() {
* is only called if necessary.
*/
bool FolderTreeModel::hasChildren(const QModelIndex& parent) const {
TreeItem *item = static_cast<TreeItem*>(parent.internalPointer());
TreeItem* item = static_cast<TreeItem*>(parent.internalPointer());
/* Usually the child count is 0 because we do lazy initialization
* However, for, buid-in items such as 'Quick Links' there exist
* child items at init time
Expand All @@ -98,18 +102,18 @@ bool FolderTreeModel::hasChildren(const QModelIndex& parent) const {

// In all other cases the getData() points to a folder
QString folder = item->getData().toString();
return directoryHasChildren(folder);
}

bool FolderTreeModel::directoryHasChildren(const QString& path) const {
MReadLocker readLock(&m_cacheLock);
const auto it = m_directoryCache.find(path);
if (it != m_directoryCache.end()) {
return it->second;
if (m_cacheLock.tryLockForRead()) {
const auto it = m_directoryCache.find(folder);
m_cacheLock.unlock();
if (it != m_directoryCache.end()) {
return it->second;
}
emit hasSubDirectory(folder);
}
readLock.unlock();
return true;
}

MWriteLocker writeLock(&m_cacheLock);
void FolderTreeModel::directoryHasChildren(const QString& path) {
// Acquire a security token for the path.
MDir dir(path);

Expand All @@ -128,25 +132,25 @@ bool FolderTreeModel::directoryHasChildren(const QString& path) const {

bool has_children = false;

#if defined (__WINDOWS__)
#if defined(__WINDOWS__)
QString folder = path;
folder.replace("/","\\");
folder.replace("/", "\\");

//quick subfolder test
SHFILEINFOW sfi;
SHGetFileInfo((LPCWSTR) folder.constData(), NULL, &sfi, sizeof(sfi), SHGFI_ATTRIBUTES);
SHGetFileInfo((LPCWSTR)folder.constData(), NULL, &sfi, sizeof(sfi), SHGFI_ATTRIBUTES);
has_children = (sfi.dwAttributes & SFGAO_HASSUBFOLDER);
#else
// For OS X and Linux
// http://stackoverflow.com/questions/2579948/checking-if-subfolders-exist-linux

std::string dot("."), dotdot("..");
QByteArray ba = QFile::encodeName(path);
DIR *directory = opendir(ba);
DIR* directory = opendir(ba);
int unknown_count = 0;
int total_count = 0;
if (directory != nullptr) {
struct dirent *entry;
struct dirent* entry;
while (!has_children && ((entry = readdir(directory)) != nullptr)) {
if (entry->d_name != dot && entry->d_name != dotdot) {
total_count++;
Expand All @@ -169,26 +173,29 @@ bool FolderTreeModel::directoryHasChildren(const QString& path) const {
}
#endif

// Cache and return the result
return m_directoryCache.emplace(path, has_children).first->second;
// Cache the result
m_directoryCache.emplace(path, has_children);
}

void FolderTreeModel::dirModified(const QString& str) {
if (m_directoryCache.count(str.toUtf8().data())) {
//m_directoryCache.erase(str);


}
}

void FolderTreeModel::processFolder(const QModelIndex& parent, const MDir& path) {
void FolderTreeModel::processFolder(const QModelIndex& parent, const QString& path) const {
std::lock_guard<std::mutex> lock(m_queueLock);
m_folderQueue.enqueue(std::make_pair(parent, path));
}

void FolderTreeModel::addChildren(const QModelIndex& parent, TreeItemList children) {
kLogger.debug() << "adding " << children->size() << " to the list";
TreeItemModel::insertTreeItemRows(*children, 0, parent);
TreeItemModel::insertTreeItemRows(*children, 0, parent);
delete children;
}


void FolderTreeModel::onHasSubDirectory(const QString& path) {
QtConcurrent::run(&m_pool, [this, path]() {
MWriteLocker lock(&m_cacheLock);
directoryHasChildren(path);
});
}
36 changes: 21 additions & 15 deletions src/library/browse/foldertreemodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@

#include <QAbstractItemModel>
#include <QFileSystemWatcher>
#include <QModelIndex>
#include <QVariant>
#include <QList>
#include <QHash>
#include <QThreadPool>
#include <QList>
#include <QModelIndex>
#include <QQueue>

#include <QThreadPool>
#include <QVariant>
#include <functional>
#include <unordered_map>

#include "library/treeitemmodel.h"
#include "library/treeitem.h"
#include "library/treeitemmodel.h"
#include "util/mutex.h"

namespace std {
Expand All @@ -32,30 +31,37 @@ class TreeItem;
class FolderTreeModel : public TreeItemModel {
Q_OBJECT

using FolderQueue = QQueue<std::pair<QModelIndex, MDir>>;
using FolderQueue = QQueue<std::pair<QModelIndex, QString>>;
using Cache = std::unordered_map<QString, bool>;

public:
using TreeItemList = QList<TreeItem*>*;

FolderTreeModel(QObject *parent = 0);
FolderTreeModel(QObject* parent = 0);
virtual ~FolderTreeModel();
virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const;
bool directoryHasChildren(const QString& path) const;
void processFolder(const QModelIndex& parent, const MDir& path);
void processFolder(const QModelIndex& parent, const QString& path) const;

private:
void directoryHasChildren(const QString& path);

private slots:
void dirModified(const QString& str);
void addChildren(const QModelIndex& parent, TreeItemList children);
void addChildren(const QModelIndex& parent, TreeItemList children);
void onHasSubDirectory(const QString& path);

signals:
void newChildren(const QModelIndex& parent, TreeItemList hildren);
void newChildren(const QModelIndex& parent, TreeItemList hildren);
void hasSubDirectory(const QString& path) const;

private:
// Used for memoizing the results of directoryHasChildren
mutable std::unordered_map<QString, bool> m_directoryCache;
Cache m_directoryCache;
mutable MReadWriteLock m_cacheLock;
mutable QFileSystemWatcher m_fsWatcher;
QFileSystemWatcher m_fsWatcher;
QThreadPool m_pool;
FolderQueue m_folderQueue;
mutable FolderQueue m_folderQueue;
mutable std::mutex m_queueLock;
std::atomic<bool> m_isRunning;
};
Q_DECLARE_METATYPE(FolderTreeModel::TreeItemList);
Expand Down

0 comments on commit c602db4

Please sign in to comment.