From 3a65665d36022c50637fc53d5c9c1ee022c5b3b5 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Mon, 10 Feb 2020 10:51:23 -0500
Subject: wstring_view when possible first implementation of
NtQueryDirectoryFile
---
src/shared/directoryentry.cpp | 76 ++++++++++++++++++++++++++++++-------------
1 file changed, 54 insertions(+), 22 deletions(-)
(limited to 'src/shared/directoryentry.cpp')
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index 6e44cc91..28201a11 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -20,6 +20,8 @@ along with Mod Organizer. If not, see .
#include "directoryentry.h"
#include "windows_error.h"
#include "error_report.h"
+#include "envfs.h"
+#include
#include
#include
#include
@@ -172,15 +174,15 @@ FileEntry::FileEntry() :
{
}
-FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent) :
- m_Index(index), m_Name(name), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent),
+FileEntry::FileEntry(Index index, std::wstring_view name, DirectoryEntry *parent) :
+ m_Index(index), m_Name(name.begin(), name.end()), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent),
m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize),
m_LastAccessed(time(nullptr))
{
}
void FileEntry::addOrigin(
- int origin, FILETIME fileTime, const std::wstring &archive, int order)
+ int origin, FILETIME fileTime, std::wstring_view archive, int order)
{
m_LastAccessed = time(nullptr);
if (m_Parent != nullptr) {
@@ -192,7 +194,7 @@ void FileEntry::addOrigin(
// alternatives
m_Origin = origin;
m_FileTime = fileTime;
- m_Archive = std::pair(archive, order);
+ m_Archive = std::pair(std::wstring(archive.begin(), archive.end()), order);
}
else if (
(m_Parent != nullptr) && (
@@ -213,7 +215,7 @@ void FileEntry::addOrigin(
m_Origin = origin;
m_FileTime = fileTime;
- m_Archive = std::pair(archive, order);
+ m_Archive = std::pair(std::wstring(archive.begin(), archive.end()), order);
}
else {
// This mod is just an alternative
@@ -232,14 +234,14 @@ void FileEntry::addOrigin(
if ((m_Parent != nullptr) &&
(m_Parent->getOriginByID(iter->first).getPriority() < m_Parent->getOriginByID(origin).getPriority())) {
- m_Alternatives.insert(iter, {origin, {archive, order}});
+ m_Alternatives.insert(iter, {origin, {std::wstring(archive.begin(), archive.end()), order}});
found = true;
break;
}
}
if (!found) {
- m_Alternatives.push_back({origin, {archive, order}});
+ m_Alternatives.push_back({origin, {std::wstring(archive.begin(), archive.end()), order}});
}
}
}
@@ -509,12 +511,12 @@ bool FileRegister::indexValid(FileEntry::Index index) const
return (m_Files.find(index) != m_Files.end());
}
-FileEntry::Ptr FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent)
+FileEntry::Ptr FileRegister::createFile(std::wstring_view name, DirectoryEntry *parent)
{
FileEntry::Index index = generateIndex();
auto r = m_Files.insert_or_assign(
- index, FileEntry::Ptr(new FileEntry(index, name, parent)));
+ index, FileEntry::Ptr(new FileEntry(index, std::move(name), parent)));
return r.first->second;
}
@@ -637,20 +639,20 @@ void FileRegister::unregisterFile(FileEntry::Ptr file)
DirectoryEntry::DirectoryEntry(
- const std::wstring &name, DirectoryEntry *parent, int originID) :
+ std::wstring name, DirectoryEntry *parent, int originID) :
m_OriginConnection(new OriginConnection),
- m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(true)
+ m_Name(std::move(name)), m_Parent(parent), m_Populated(false), m_TopLevel(true)
{
m_FileRegister.reset(new FileRegister(m_OriginConnection));
m_Origins.insert(originID);
}
DirectoryEntry::DirectoryEntry(
- const std::wstring &name, DirectoryEntry *parent, int originID,
+ std::wstring name, DirectoryEntry *parent, int originID,
boost::shared_ptr fileRegister,
boost::shared_ptr originConnection) :
m_FileRegister(fileRegister), m_OriginConnection(originConnection),
- m_Name(name), m_Parent(parent), m_Populated(false), m_TopLevel(false)
+ m_Name(std::move(name)), m_Parent(parent), m_Populated(false), m_TopLevel(false)
{
m_Origins.insert(originID);
}
@@ -1025,8 +1027,8 @@ void DirectoryEntry::removeFiles(const std::set &indices)
}
FileEntry::Ptr DirectoryEntry::insert(
- const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime,
- const std::wstring &archive, int order)
+ std::wstring_view fileName, FilesOrigin &origin, FILETIME fileTime,
+ std::wstring_view archive, int order)
{
std::wstring fileNameLower = ToLowerCopy(fileName);
@@ -1050,6 +1052,35 @@ FileEntry::Ptr DirectoryEntry::insert(
void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset)
{
+ struct Context
+ {
+ FilesOrigin& origin;
+ std::stack current;
+ };
+
+ Context cx = {origin};
+ cx.current.push(this);
+
+ env::forEachEntry(buffer, &cx,
+ [](void* pcx, std::wstring_view path) {
+ Context* cx = (Context*)pcx;
+ cx->current.push(cx->current.top()->getSubDirectory(path, true, cx->origin.getID()));
+ },
+
+ [](void* pcx, std::wstring_view path) {
+ Context* cx = (Context*)pcx;
+ auto* current= cx->current.top();
+ std::sort(current->m_SubDirectories.begin(), current->m_SubDirectories.end(), &DirCompareByName);
+ cx->current.pop();
+ },
+
+ [](void* pcx, std::wstring_view path, FILETIME ft) {
+ Context* cx = (Context*)pcx;
+ cx->current.top()->insert(path, cx->origin, ft, L"", -1);
+ }
+ );
+
+ /*
WIN32_FIND_DATAW findData;
_snwprintf_s(buffer + bufferOffset, MAXPATH_UNICODE - bufferOffset, _TRUNCATE, L"\\*");
@@ -1087,7 +1118,7 @@ void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOf
}
std::sort(m_SubDirectories.begin(), m_SubDirectories.end(), &DirCompareByName);
- ::FindClose(searchHandle);
+ ::FindClose(searchHandle);*/
}
void DirectoryEntry::addFiles(
@@ -1119,17 +1150,18 @@ void DirectoryEntry::addFiles(
}
DirectoryEntry *DirectoryEntry::getSubDirectory(
- const std::wstring &name, bool create, int originID)
+ std::wstring_view name, bool create, int originID)
{
- for (DirectoryEntry *entry : m_SubDirectories) {
- if (CaseInsensitiveEqual(entry->getName(), name)) {
- return entry;
- }
+ auto itor = m_SubDirectoriesLookup.find(ToLowerCopy(name));
+
+ if (itor != m_SubDirectoriesLookup.end()) {
+ return itor->second;
}
if (create) {
auto* entry = new DirectoryEntry(
- name, this, originID, m_FileRegister, m_OriginConnection);
+ std::wstring(name.begin(), name.end()), this, originID,
+ m_FileRegister, m_OriginConnection);
addDirectoryToList(entry);
--
cgit v1.3.1
From c1cf76033782ff8dc4ccc465b60a2f9f1edb69b8 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Mon, 10 Feb 2020 11:05:21 -0500
Subject: don't lowercase twice
---
src/shared/directoryentry.cpp | 10 ++++++----
src/shared/directoryentry.h | 2 +-
2 files changed, 7 insertions(+), 5 deletions(-)
(limited to 'src/shared/directoryentry.cpp')
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index 28201a11..d171be38 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -1152,7 +1152,8 @@ void DirectoryEntry::addFiles(
DirectoryEntry *DirectoryEntry::getSubDirectory(
std::wstring_view name, bool create, int originID)
{
- auto itor = m_SubDirectoriesLookup.find(ToLowerCopy(name));
+ std::wstring nameLc = ToLowerCopy(name);
+ auto itor = m_SubDirectoriesLookup.find(nameLc);
if (itor != m_SubDirectoriesLookup.end()) {
return itor->second;
@@ -1163,7 +1164,8 @@ DirectoryEntry *DirectoryEntry::getSubDirectory(
std::wstring(name.begin(), name.end()), this, originID,
m_FileRegister, m_OriginConnection);
- addDirectoryToList(entry);
+ addDirectoryToList(entry, std::move(nameLc));
+ // nameLc is moved from this point
return entry;
} else {
@@ -1211,10 +1213,10 @@ void DirectoryEntry::removeDirRecursive()
m_SubDirectoriesLookup.clear();
}
-void DirectoryEntry::addDirectoryToList(DirectoryEntry* e)
+void DirectoryEntry::addDirectoryToList(DirectoryEntry* e, std::wstring nameLc)
{
m_SubDirectories.push_back(e);
- m_SubDirectoriesLookup.emplace(ToLowerCopy(e->getName()), e);
+ m_SubDirectoriesLookup.emplace(std::move(nameLc), e);
}
void DirectoryEntry::removeDirectoryFromList(SubDirectories::iterator itor)
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index 1a8a4247..58e70ffa 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -519,7 +519,7 @@ private:
void removeDirRecursive();
- void addDirectoryToList(DirectoryEntry* e);
+ void addDirectoryToList(DirectoryEntry* e, std::wstring nameLc);
void removeDirectoryFromList(SubDirectories::iterator itor);
void addFileToList(std::wstring fileNameLower, FileEntry::Index index);
--
cgit v1.3.1
From 3423b0a59337cf4cf99a24a1421ea33c4c641a22 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Mon, 10 Feb 2020 14:53:04 -0500
Subject: threaded refresher
---
src/directoryrefresher.cpp | 188 ++++++++++++++++++++++++++++++++----------
src/directoryrefresher.h | 42 ++++++----
src/envfs.cpp | 130 +++++++++++++++++------------
src/envfs.h | 88 ++++++++++++++++++++
src/organizercore.cpp | 23 +++---
src/settings.cpp | 10 +++
src/settings.h | 5 ++
src/shared/directoryentry.cpp | 27 ++++++
src/shared/directoryentry.h | 8 ++
9 files changed, 395 insertions(+), 126 deletions(-)
(limited to 'src/shared/directoryentry.cpp')
diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp
index b7dd51ba..5bff9d37 100644
--- a/src/directoryrefresher.cpp
+++ b/src/directoryrefresher.cpp
@@ -24,6 +24,7 @@ along with Mod Organizer. If not, see .
#include "report.h"
#include "modinfo.h"
#include "settings.h"
+#include "envfs.h"
#include
#include
@@ -36,8 +37,8 @@ using namespace MOBase;
using namespace MOShared;
-DirectoryRefresher::DirectoryRefresher()
- : m_DirectoryStructure(nullptr)
+DirectoryRefresher::DirectoryRefresher(std::size_t threadCount)
+ : m_DirectoryStructure(nullptr), m_threadCount(threadCount)
{
}
@@ -116,59 +117,160 @@ void DirectoryRefresher::addModBSAToStructure(DirectoryEntry *directoryStructure
}
}
-void DirectoryRefresher::addModFilesToStructure(DirectoryEntry *directoryStructure, const QString &modName,
- int priority, const QString &directory, const QStringList &stealFiles)
+void DirectoryRefresher::stealModFilesIntoStructure(
+ DirectoryEntry *directoryStructure, const QString &modName,
+ int priority, const QString &directory, const QStringList &stealFiles)
{
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
- if (stealFiles.length() > 0) {
- // instead of adding all the files of the target directory, we just change the root of the specified
- // files to this mod
- FilesOrigin &origin = directoryStructure->createOrigin(ToWString(modName), directoryW, priority);
- for (const QString &filename : stealFiles) {
- if (filename.isEmpty()) {
- log::warn("Trying to find file with no name");
- continue;
- }
- QFileInfo fileInfo(filename);
- FileEntry::Ptr file = directoryStructure->findFile(ToWString(fileInfo.fileName()));
- if (file.get() != nullptr) {
- if (file->getOrigin() == 0) {
- // replace data as the origin on this bsa
- file->removeOrigin(0);
- }
- origin.addFile(file->getIndex());
- file->addOrigin(origin.getID(), file->getFileTime(), L"", -1);
- } else {
- QString warnStr = fileInfo.absolutePath();
- if (warnStr.isEmpty())
- warnStr = filename;
- log::warn("file not found: {}", warnStr);
+ // instead of adding all the files of the target directory, we just change the root of the specified
+ // files to this mod
+ FilesOrigin &origin = directoryStructure->createOrigin(ToWString(modName), directoryW, priority);
+ for (const QString &filename : stealFiles) {
+ if (filename.isEmpty()) {
+ log::warn("Trying to find file with no name");
+ continue;
+ }
+ QFileInfo fileInfo(filename);
+ FileEntry::Ptr file = directoryStructure->findFile(ToWString(fileInfo.fileName()));
+ if (file.get() != nullptr) {
+ if (file->getOrigin() == 0) {
+ // replace data as the origin on this bsa
+ file->removeOrigin(0);
}
+ origin.addFile(file->getIndex());
+ file->addOrigin(origin.getID(), file->getFileTime(), L"", -1);
+ } else {
+ QString warnStr = fileInfo.absolutePath();
+ if (warnStr.isEmpty())
+ warnStr = filename;
+ log::warn("file not found: {}", warnStr);
}
+ }
+}
+
+void DirectoryRefresher::addModFilesToStructure(
+ DirectoryEntry *directoryStructure, const QString &modName,
+ int priority, const QString &directory, const QStringList &stealFiles)
+{
+ TimeThis tt("addModFilesToStructure()");
+
+ std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
+
+ if (stealFiles.length() > 0) {
+ stealModFilesIntoStructure(
+ directoryStructure, modName, priority, directory, stealFiles);
} else {
directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority);
}
}
void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure
- , const QString &modName
- , int priority
- , const QString &directory
- , const QStringList &stealFiles
- , const QStringList &archives)
+ , const QString &modName
+ , int priority
+ , const QString &directory
+ , const QStringList &stealFiles
+ , const QStringList &archives)
{
- addModFilesToStructure(directoryStructure, modName, priority, directory, stealFiles);
+ TimeThis tt("addModToStructure()");
+
+ if (stealFiles.length() > 0) {
+ stealModFilesIntoStructure(
+ directoryStructure, modName, priority, directory, stealFiles);
+ } else {
+ std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
+ directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority);
+ }
if (Settings::instance().archiveParsing()) {
addModBSAToStructure(directoryStructure, modName, priority, directory, archives);
}
}
+struct ModThread
+{
+ std::wstring path;
+ env::Directory* dir = nullptr;
+ std::condition_variable cv;
+ std::mutex mutex;
+ bool ready = false;
+
+ void wakeup()
+ {
+ ready = true;
+ cv.notify_one();
+ }
+
+ void run()
+ {
+ std::unique_lock lock(mutex);
+ cv.wait(lock, [&]{ return ready; });
+
+ *dir = env::getFilesAndDirs(path);
+
+ ready = false;
+ }
+};
+
+void DirectoryRefresher::addMultipleModsFilesToStructure(
+ MOShared::DirectoryEntry *directoryStructure,
+ const std::vector& entries, bool emitProgress)
+{
+ TimeThis tt(QString("add %1 mods").arg(entries.size()));
+
+ env::ThreadPool threads(m_threadCount);
+ std::vector dirs(entries.size());
+
+ for (std::size_t i=0; i(i + 1);
+
+ try {
+ if (e.stealFiles.length() > 0) {
+ stealModFilesIntoStructure(
+ directoryStructure, e.modName, prio, e.absolutePath, e.stealFiles);
+ } else {
+ auto& mt = threads.request();
+
+ mt.path = QDir::toNativeSeparators(e.absolutePath).toStdWString();
+ mt.dir = &dirs[i];
+
+ mt.wakeup();
+ }
+ } catch (const std::exception& ex) {
+ emit error(tr("failed to read mod (%1): %2").arg(e.modName, ex.what()));
+ }
+
+ if (emitProgress) {
+ emit progress((static_cast(i) * 100) / static_cast(entries.size()) + 1);
+ }
+ }
+
+ threads.join();
+
+ for (std::size_t i=0; i(i + 1);
+
+ directoryStructure->addFromList(
+ entries[i].modName.toStdWString(),
+ entries[i].absolutePath.toStdWString(),
+ dirs[i],
+ prio);
+
+ if (Settings::instance().archiveParsing()) {
+ addModBSAToStructure(
+ directoryStructure,
+ entries[i].modName,
+ prio,
+ entries[i].absolutePath,
+ entries[i].archives);
+ }
+ }
+}
+
void DirectoryRefresher::refresh()
{
SetThisThreadName("DirectoryRefresher");
- TimeThis tt("DirectoryRefresher::refresh()");
QMutexLocker locker(&m_RefreshLock);
@@ -178,20 +280,16 @@ void DirectoryRefresher::refresh()
IPluginGame *game = qApp->property("managed_game").value();
- std::wstring dataDirectory = QDir::toNativeSeparators(game->dataDirectory().absolutePath()).toStdWString();
+ std::wstring dataDirectory =
+ QDir::toNativeSeparators(game->dataDirectory().absolutePath()).toStdWString();
+
m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0);
- std::sort(m_Mods.begin(), m_Mods.end(), [](auto lhs, auto rhs){return lhs.priority < rhs.priority;});
- auto iter = m_Mods.begin();
+ std::sort(m_Mods.begin(), m_Mods.end(), [](auto lhs, auto rhs) {
+ return lhs.priority < rhs.priority;
+ });
- for (int i = 1; iter != m_Mods.end(); ++iter, ++i) {
- try {
- addModToStructure(m_DirectoryStructure, iter->modName, i, iter->absolutePath, iter->stealFiles, iter->archives);
- } catch (const std::exception &e) {
- emit error(tr("failed to read mod (%1): %2").arg(iter->modName, e.what()));
- }
- emit progress((i * 100) / static_cast(m_Mods.size()) + 1);
- }
+ addMultipleModsFilesToStructure(m_DirectoryStructure, m_Mods, true);
m_DirectoryStructure->getFileRegister()->sortOrigins();
diff --git a/src/directoryrefresher.h b/src/directoryrefresher.h
index a4fc5dbc..c531ba39 100644
--- a/src/directoryrefresher.h
+++ b/src/directoryrefresher.h
@@ -39,12 +39,23 @@ class DirectoryRefresher : public QObject
Q_OBJECT
public:
+ struct EntryInfo {
+ EntryInfo(const QString &modName, const QString &absolutePath,
+ const QStringList &stealFiles, const QStringList &archives, int priority)
+ : modName(modName), absolutePath(absolutePath), stealFiles(stealFiles)
+ , archives(archives), priority(priority) {}
+ QString modName;
+ QString absolutePath;
+ QStringList stealFiles;
+ QStringList archives;
+ int priority;
+ };
/**
* @brief constructor
*
**/
- DirectoryRefresher();
+ DirectoryRefresher(std::size_t threadCount);
~DirectoryRefresher();
@@ -53,7 +64,7 @@ public:
*
* returns a pointer to the updated directory structure. DirectoryRefresher
* deletes its own pointer and the caller takes custody of the pointer
- *
+ *
* @return updated directory structure
**/
MOShared::DirectoryEntry *getDirectoryStructure();
@@ -107,7 +118,13 @@ public:
* @param directory
* @param stealFiles
*/
- void addModFilesToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles);
+ void addModFilesToStructure(
+ MOShared::DirectoryEntry *directoryStructure, const QString &modName,
+ int priority, const QString &directory, const QStringList &stealFiles);
+
+ void addMultipleModsFilesToStructure(
+ MOShared::DirectoryEntry *directoryStructure,
+ const std::vector& entries, bool emitProgress=false);
public slots:
@@ -123,26 +140,15 @@ signals:
void refreshed();
private:
-
- struct EntryInfo {
- EntryInfo(const QString &modName, const QString &absolutePath,
- const QStringList &stealFiles, const QStringList &archives, int priority)
- : modName(modName), absolutePath(absolutePath), stealFiles(stealFiles)
- , archives(archives), priority(priority) {}
- QString modName;
- QString absolutePath;
- QStringList stealFiles;
- QStringList archives;
- int priority;
- };
-
-private:
-
std::vector m_Mods;
std::set m_EnabledArchives;
MOShared::DirectoryEntry *m_DirectoryStructure;
QMutex m_RefreshLock;
+ std::size_t m_threadCount;
+ void stealModFilesIntoStructure(
+ MOShared::DirectoryEntry *directoryStructure, const QString &modName,
+ int priority, const QString &directory, const QStringList &stealFiles);
};
#endif // DIRECTORYREFRESHER_H
diff --git a/src/envfs.cpp b/src/envfs.cpp
index 81a0d3ef..2716737f 100644
--- a/src/envfs.cpp
+++ b/src/envfs.cpp
@@ -150,44 +150,62 @@ QString toString(POBJECT_ATTRIBUTES poa)
}
-constexpr std::size_t AllocSize = 1024 * 1024;
-std::vector> g_buffers;
-
-
-struct HandleCloserThread
+class HandleCloserThread
{
- std::vector handles;
- std::thread thread;
- std::atomic busy;
-
+public:
HandleCloserThread()
- : busy(false)
+ : m_ready(false)
{
+ m_handles.reserve(50'000);
}
- ~HandleCloserThread()
+ void add(HANDLE h)
{
- if (thread.joinable()) {
- thread.join();
- }
+ m_handles.push_back(h);
+ }
+
+ void wakeup()
+ {
+ m_ready = true;
+ m_cv.notify_one();
+ }
+
+ void run()
+ {
+ std::unique_lock lock(m_mutex);
+ m_cv.wait(lock, [&]{ return m_ready; });
+
+ closeHandles();
}
+private:
+ std::vector m_handles;
+ std::condition_variable m_cv;
+ std::mutex m_mutex;
+ bool m_ready;
+
void closeHandles()
{
- for (auto& h : handles) {
+ for (auto& h : m_handles) {
NtClose(h);
}
- handles.clear();
- busy = false;
+ m_handles.clear();
+ m_ready = false;
}
};
-std::array g_handleCloserThreads;
+constexpr std::size_t AllocSize = 1024 * 1024;
+static ThreadPool g_handleClosers;
+void setHandleCloserThreadCount(std::size_t n)
+{
+ g_handleClosers.setMax(n);
+}
void forEachEntryImpl(
- void* cx, HandleCloserThread& hc, POBJECT_ATTRIBUTES poa, std::size_t depth,
+ void* cx, HandleCloserThread& hc, std::vector>& buffers,
+ POBJECT_ATTRIBUTES poa, std::size_t depth,
DirStartF* dirStartF, DirEndF* dirEndF, FileF* fileF)
{
IO_STATUS_BLOCK iosb;
@@ -207,14 +225,14 @@ void forEachEntryImpl(
return;
}
- hc.handles.push_back(oa.RootDirectory);
+ hc.add(oa.RootDirectory);
unsigned char* buffer;
- if (depth >= g_buffers.size()) {
- g_buffers.emplace_back(std::make_unique(AllocSize));
- buffer = g_buffers.back().get();
+ if (depth >= buffers.size()) {
+ buffers.emplace_back(std::make_unique(AllocSize));
+ buffer = buffers.back().get();
} else {
- buffer = g_buffers[depth].get();
+ buffer = buffers[depth].get();
}
union
@@ -268,7 +286,7 @@ void forEachEntryImpl(
if (DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
dirStartF(cx, toStringView(&oa));
- forEachEntryImpl(cx, hc, &oa, depth+1, dirStartF, dirEndF, fileF);
+ forEachEntryImpl(cx, hc, buffers, &oa, depth+1, dirStartF, dirEndF, fileF);
dirEndF(cx, toStringView(&oa));
} else {
//log::debug("{}{}", std::wstring((depth + 1) * 2, L' '), toString(&oa));
@@ -293,35 +311,13 @@ void forEachEntryImpl(
}
}
-std::size_t findHandleCloserThread()
-{
- for (;;) {
- for (std::size_t i=0; i> buffers;
if (!NtOpenFile) {
HMODULE m = ::LoadLibraryW(L"ntdll.dll");
@@ -342,9 +338,41 @@ void forEachEntry(
oa.Length = sizeof(oa);
oa.ObjectName = &ObjectName;
- forEachEntryImpl(cx, hc, &oa, 0, dirStartF, dirEndF, fileF);
+ forEachEntryImpl(cx, hc, buffers, &oa, 0, dirStartF, dirEndF, fileF);
+ hc.wakeup();
+}
+
+Directory getFilesAndDirs(const std::wstring& path)
+{
+ struct Context
+ {
+ std::stack current;
+ };
+
+ Directory root;
+
+ Context cx;
+ cx.current.push(&root);
+
+ env::forEachEntry(path, &cx,
+ [](void* pcx, std::wstring_view path) {
+ Context* cx = (Context*)pcx;
+ cx->current.top()->dirs.push_back({std::wstring(path.begin(), path.end())});
+ cx->current.push(&cx->current.top()->dirs.back());
+ },
+
+ [](void* pcx, std::wstring_view path) {
+ Context* cx = (Context*)pcx;
+ cx->current.pop();
+ },
+
+ [](void* pcx, std::wstring_view path, FILETIME ft) {
+ Context* cx = (Context*)pcx;
+ cx->current.top()->files.push_back({std::wstring(path.begin(), path.end()), ft});
+ }
+ );
- hc.thread = std::thread([hci]{ g_handleCloserThreads[hci].closeHandles(); });
+ return root;
}
} // namespace
diff --git a/src/envfs.h b/src/envfs.h
index aeaa6796..1fc53bcf 100644
--- a/src/envfs.h
+++ b/src/envfs.h
@@ -1,17 +1,105 @@
#ifndef ENV_ENVFS_H
#define ENV_ENVFS_H
+#include
+
namespace env
{
+struct File
+{
+ std::wstring name;
+ FILETIME ft;
+};
+
+struct Directory
+{
+ std::wstring name;
+ std::list dirs;
+ std::list files;
+};
+
+
+template
+class ThreadPool
+{
+public:
+ ThreadPool(std::size_t max=1)
+ : m_threads(max)
+ {
+ }
+
+ ~ThreadPool()
+ {
+ join();
+ }
+
+ void setMax(std::size_t n)
+ {
+ m_threads.resize(n);
+ }
+
+ void join()
+ {
+ for (auto& ti : m_threads) {
+ if (ti.thread.joinable()) {
+ ti.thread.join();
+ }
+ }
+ }
+
+ T& request()
+ {
+ if (m_threads.empty()) {
+ std::terminate();
+ }
+
+ for (;;) {
+ for (auto& ti : m_threads) {
+ bool expected = false;
+
+ if (ti.busy.compare_exchange_strong(expected, true)) {
+ if (ti.thread.joinable()) {
+ ti.thread.join();
+ }
+
+ ti.thread = std::thread([&]{
+ ti.o.run();
+ ti.busy = false;
+ });
+
+ return ti.o;
+ }
+ }
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ }
+
+private:
+ struct ThreadInfo
+ {
+ std::thread thread;
+ std::atomic busy;
+ T o;
+ };
+
+ std::list m_threads;
+};
+
+
using DirStartF = void (void*, std::wstring_view);
using DirEndF = void (void*, std::wstring_view);
using FileF = void (void*, std::wstring_view, FILETIME);
+void setHandleCloserThreadCount(std::size_t n);
+
void forEachEntry(
const std::wstring& path, void* cx,
DirStartF* dirStartF, DirEndF* dirEndF, FileF* fileF);
+Directory getFilesAndDirs(const std::wstring& path);
+
} // namespace
#endif // ENV_ENVFS_H
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 8124be1d..86abeb35 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -88,24 +88,19 @@ QStringList toStringList(InputIterator current, InputIterator end)
OrganizerCore::OrganizerCore(Settings &settings)
: m_UserInterface(nullptr)
, m_PluginContainer(nullptr)
- , m_GameName()
, m_CurrentProfile(nullptr)
, m_Settings(settings)
, m_Updater(NexusInterface::instance(m_PluginContainer))
- , m_AboutToRun()
- , m_FinishedRun()
- , m_ModInstalled()
, m_ModList(m_PluginContainer, this)
, m_PluginList(this)
- , m_DirectoryRefresher()
+ , m_DirectoryRefresher(settings.refreshThreadCount())
, m_DirectoryStructure(new DirectoryEntry(L"data", nullptr, 0))
, m_DownloadManager(NexusInterface::instance(m_PluginContainer), this)
- , m_InstallationManager()
- , m_RefresherThread()
, m_DirectoryUpdate(false)
, m_ArchivesInit(false)
, m_PluginListsWriter(std::bind(&OrganizerCore::savePluginList, this))
{
+ env::setHandleCloserThreadCount(settings.refreshThreadCount());
m_DownloadManager.setOutputDirectory(m_Settings.paths().downloads(), false);
NexusInterface::instance(m_PluginContainer)->setCacheDirectory(
@@ -1245,13 +1240,17 @@ void OrganizerCore::updateModInDirectoryStructure(unsigned int index,
void OrganizerCore::updateModsInDirectoryStructure(QMap modInfo)
{
+ std::vector entries;
+
for (auto idx : modInfo.keys()) {
- // add files of the bsa to the directory structure
- m_DirectoryRefresher.addModFilesToStructure(
- m_DirectoryStructure, modInfo[idx]->name(),
- m_CurrentProfile->getModPriority(idx), modInfo[idx]->absolutePath(),
- modInfo[idx]->stealFiles());
+ entries.push_back({
+ modInfo[idx]->name(), modInfo[idx]->absolutePath(),
+ modInfo[idx]->stealFiles(), {}, m_CurrentProfile->getModPriority(idx)});
}
+
+ m_DirectoryRefresher.addMultipleModsFilesToStructure(
+ m_DirectoryStructure, entries);
+
DirectoryRefresher::cleanStructure(m_DirectoryStructure);
// need to refresh plugin list now so we can activate esps
refreshESPList(true);
diff --git a/src/settings.cpp b/src/settings.cpp
index 761cd669..f0496fe8 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -209,6 +209,16 @@ void Settings::setUseSplash(bool b)
set(m_Settings, "Settings", "use_splash", b);
}
+std::size_t Settings::refreshThreadCount() const
+{
+ return get(m_Settings, "Settings", "refresh_thread_count", 10);
+}
+
+void Settings::setRefreshThreadCount(std::size_t n) const
+{
+ return set(m_Settings, "Settings", "refresh_thread_count", n);
+}
+
std::optional Settings::version() const
{
if (auto v=getOptional(m_Settings, "General", "version")) {
diff --git a/src/settings.h b/src/settings.h
index 3d37bdd8..b2cb6be5 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -731,6 +731,11 @@ public:
bool useSplash() const;
void setUseSplash(bool b);
+ // number of threads to use when refreshing
+ //
+ std::size_t refreshThreadCount() const;
+ void setRefreshThreadCount(std::size_t n) const;
+
GameSettings& game();
const GameSettings& game() const;
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index d171be38..b7329833 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -691,6 +691,33 @@ void DirectoryEntry::addFromOrigin(
m_Populated = true;
}
+void DirectoryEntry::addFromList(
+ const std::wstring &originName, const std::wstring &directory,
+ env::Directory& root, int priority)
+{
+ FilesOrigin &origin = createOrigin(originName, directory, priority);
+ addDir(origin, root);
+}
+
+void DirectoryEntry::addDir(FilesOrigin& origin, env::Directory& d)
+{
+ for (auto& sd : d.dirs) {
+ auto* sdirEntry = getSubDirectory(sd.name, true, origin.getID());
+ sdirEntry->addDir(origin, sd);
+ }
+
+ for (auto& f : d.files) {
+ insert(f.name, origin, f.ft, L"", -1);
+ }
+
+ std::sort(
+ m_SubDirectories.begin(),
+ m_SubDirectories.end(),
+ &DirCompareByName);
+
+ m_Populated = true;
+}
+
void DirectoryEntry::addFromBSA(
const std::wstring &originName, std::wstring &directory,
const std::wstring &fileName, int priority, int order)
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index 58e70ffa..772899d7 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -33,7 +33,9 @@ along with Mod Organizer. If not, see .
#include
#include
#endif
+
#include "util.h"
+#include "envfs.h"
namespace MOShared { struct DirectoryEntryFileKey; }
@@ -358,6 +360,10 @@ public:
const std::wstring &originName, std::wstring &directory,
const std::wstring &fileName, int priority, int order);
+ void addFromList(
+ const std::wstring &originName, const std::wstring &directory,
+ env::Directory& root, int priority);
+
void propagateOrigin(int origin);
const std::wstring &getName() const
@@ -511,6 +517,8 @@ private:
FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime,
const std::wstring &archiveName, int order);
+ void addDir(FilesOrigin& origin, env::Directory& d);
+
DirectoryEntry* getSubDirectory(
std::wstring_view name, bool create, int originID = -1);
--
cgit v1.3.1
From 7860e927a771d6a07d9b1905071bd389a0e64001 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Mon, 10 Feb 2020 15:10:27 -0500
Subject: removed dead insertFile() avoided a few string copies lowercase
filename in thread
---
src/envfs.cpp | 8 +++++++-
src/envfs.h | 3 ++-
src/shared/directoryentry.cpp | 45 ++++++++++++++++++++++++-------------------
src/shared/directoryentry.h | 10 ++++++----
4 files changed, 40 insertions(+), 26 deletions(-)
(limited to 'src/shared/directoryentry.cpp')
diff --git a/src/envfs.cpp b/src/envfs.cpp
index 2716737f..80749046 100644
--- a/src/envfs.cpp
+++ b/src/envfs.cpp
@@ -1,4 +1,5 @@
#include "envfs.h"
+#include "util.h"
#include
#include
@@ -368,7 +369,12 @@ Directory getFilesAndDirs(const std::wstring& path)
[](void* pcx, std::wstring_view path, FILETIME ft) {
Context* cx = (Context*)pcx;
- cx->current.top()->files.push_back({std::wstring(path.begin(), path.end()), ft});
+
+ cx->current.top()->files.push_back({
+ std::wstring(path.begin(), path.end()),
+ MOShared::ToLowerCopy(path),
+ ft
+ });
}
);
diff --git a/src/envfs.h b/src/envfs.h
index 1fc53bcf..6cc98775 100644
--- a/src/envfs.h
+++ b/src/envfs.h
@@ -9,7 +9,8 @@ namespace env
struct File
{
std::wstring name;
- FILETIME ft;
+ std::wstring lcname;
+ FILETIME lastModified;
};
struct Directory
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index b7329833..5df2c1ba 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -174,8 +174,8 @@ FileEntry::FileEntry() :
{
}
-FileEntry::FileEntry(Index index, std::wstring_view name, DirectoryEntry *parent) :
- m_Index(index), m_Name(name.begin(), name.end()), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent),
+FileEntry::FileEntry(Index index, std::wstring name, DirectoryEntry *parent) :
+ m_Index(index), m_Name(std::move(name)), m_Origin(-1), m_Archive(L"", -1), m_Parent(parent),
m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize),
m_LastAccessed(time(nullptr))
{
@@ -511,7 +511,7 @@ bool FileRegister::indexValid(FileEntry::Index index) const
return (m_Files.find(index) != m_Files.end());
}
-FileEntry::Ptr FileRegister::createFile(std::wstring_view name, DirectoryEntry *parent)
+FileEntry::Ptr FileRegister::createFile(std::wstring name, DirectoryEntry *parent)
{
FileEntry::Index index = generateIndex();
@@ -707,7 +707,7 @@ void DirectoryEntry::addDir(FilesOrigin& origin, env::Directory& d)
}
for (auto& f : d.files) {
- insert(f.name, origin, f.ft, L"", -1);
+ insert(f, origin, L"", -1);
}
std::sort(
@@ -944,20 +944,6 @@ const FileEntry::Ptr DirectoryEntry::searchFile(
return FileEntry::Ptr();
}
-void DirectoryEntry::insertFile(
- const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime)
-{
- size_t pos = filePath.find_first_of(L"\\/");
-
- if (pos == std::string::npos) {
- this->insert(filePath, origin, fileTime, std::wstring(), -1);
- } else {
- std::wstring dirName = filePath.substr(0, pos);
- std::wstring rest = filePath.substr(pos + 1);
- getSubDirectoryRecursive(dirName, true, origin.getID())->insertFile(rest, origin, fileTime);
- }
-}
-
void DirectoryEntry::removeFile(FileEntry::Index index)
{
removeFileFromList(index);
@@ -1065,9 +1051,8 @@ FileEntry::Ptr DirectoryEntry::insert(
if (iter != m_Files.end()) {
file = m_FileRegister->getFile(iter->second);
} else {
- file = m_FileRegister->createFile(fileName, this);
+ file = m_FileRegister->createFile(std::wstring(fileName.begin(), fileName.end()), this);
addFileToList(std::move(fileNameLower), file->getIndex());
-
// fileNameLower has moved from this point
}
@@ -1077,6 +1062,26 @@ FileEntry::Ptr DirectoryEntry::insert(
return file;
}
+FileEntry::Ptr DirectoryEntry::insert(
+ env::File& file, FilesOrigin &origin, std::wstring_view archive, int order)
+{
+ auto iter = m_Files.find(file.lcname);
+ FileEntry::Ptr fe;
+
+ if (iter != m_Files.end()) {
+ fe = m_FileRegister->getFile(iter->second);
+ } else {
+ fe = m_FileRegister->createFile(std::move(file.name), this);
+ addFileToList(std::move(file.lcname), fe->getIndex());
+ // both file.name and file.lcname have been moved from this point
+ }
+
+ fe->addOrigin(origin.getID(), file.lastModified, archive, order);
+ origin.addFile(fe->getIndex());
+
+ return fe;
+}
+
void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset)
{
struct Context
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index 772899d7..f8c72d77 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -80,7 +80,7 @@ public:
AlternativesVector;
FileEntry();
- FileEntry(Index index, std::wstring_view name, DirectoryEntry *parent);
+ FileEntry(Index index, std::wstring name, DirectoryEntry *parent);
Index getIndex() const
{
@@ -263,7 +263,7 @@ public:
bool indexValid(FileEntry::Index index) const;
- FileEntry::Ptr createFile(std::wstring_view name, DirectoryEntry *parent);
+ FileEntry::Ptr createFile(std::wstring name, DirectoryEntry *parent);
FileEntry::Ptr getFile(FileEntry::Index index) const;
size_t size() const
@@ -459,8 +459,6 @@ public:
const FileEntry::Ptr searchFile(
const std::wstring &path, const DirectoryEntry **directory=nullptr) const;
- void insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime);
-
void removeFile(FileEntry::Index index);
// remove the specified file from the tree. This can be a path leading to a
@@ -510,6 +508,10 @@ private:
std::wstring_view fileName, FilesOrigin &origin, FILETIME fileTime,
std::wstring_view archive, int order);
+ FileEntry::Ptr insert(
+ env::File& file, FilesOrigin &origin,
+ std::wstring_view archive, int order);
+
void addFiles(
FilesOrigin &origin, wchar_t *buffer, int bufferOffset);
--
cgit v1.3.1
From d082a91ad617808a6dd89ad9fdb9479524795520 Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Wed, 12 Feb 2020 16:14:18 -0500
Subject: faster getSubDirectory() for env::Directory, added lcname
instrumentation
---
src/envfs.cpp | 32 ++++-
src/envfs.h | 11 ++
src/shared/directoryentry.cpp | 313 +++++++++++++++++++++++++++++++++++++-----
src/shared/directoryentry.h | 59 +++++++-
4 files changed, 366 insertions(+), 49 deletions(-)
(limited to 'src/shared/directoryentry.cpp')
diff --git a/src/envfs.cpp b/src/envfs.cpp
index 80749046..d7245f66 100644
--- a/src/envfs.cpp
+++ b/src/envfs.cpp
@@ -1,4 +1,5 @@
#include "envfs.h"
+#include "env.h"
#include "util.h"
#include
#include
@@ -160,6 +161,11 @@ public:
m_handles.reserve(50'000);
}
+ void shrink()
+ {
+ m_handles.shrink_to_fit();
+ }
+
void add(HANDLE h)
{
m_handles.push_back(h);
@@ -204,6 +210,15 @@ void setHandleCloserThreadCount(std::size_t n)
g_handleClosers.setMax(n);
}
+void shrinkFs()
+{
+ g_handleClosers.join();
+
+ g_handleClosers.forEach([](auto&& t) {
+ t.shrink();
+ });
+}
+
void forEachEntryImpl(
void* cx, HandleCloserThread& hc, std::vector>& buffers,
POBJECT_ATTRIBUTES poa, std::size_t depth,
@@ -290,7 +305,6 @@ void forEachEntryImpl(
forEachEntryImpl(cx, hc, buffers, &oa, depth+1, dirStartF, dirEndF, fileF);
dirEndF(cx, toStringView(&oa));
} else {
- //log::debug("{}{}", std::wstring((depth + 1) * 2, L' '), toString(&oa));
FILETIME ft;
ft.dwLowDateTime = DirInfo->LastWriteTime.LowPart;
ft.dwHighDateTime = DirInfo->LastWriteTime.HighPart;
@@ -321,11 +335,10 @@ void forEachEntry(
std::vector> buffers;
if (!NtOpenFile) {
- HMODULE m = ::LoadLibraryW(L"ntdll.dll");
- NtOpenFile = (NtOpenFile_type)::GetProcAddress(m, "NtOpenFile");
- NtQueryDirectoryFile = (NtQueryDirectoryFile_type)::GetProcAddress(m, "NtQueryDirectoryFile");
- NtClose = (NtClose_type)::GetProcAddress(m, "NtClose");
- ::FreeLibrary(m);
+ LibraryPtr m(::LoadLibraryW(L"ntdll.dll"));
+ NtOpenFile = (NtOpenFile_type)::GetProcAddress(m.get(), "NtOpenFile");
+ NtQueryDirectoryFile = (NtQueryDirectoryFile_type)::GetProcAddress(m.get(), "NtQueryDirectoryFile");
+ NtClose = (NtClose_type)::GetProcAddress(m.get(), "NtClose");
}
const std::wstring ntpath = std::wstring(L"\\??\\") + path;
@@ -358,7 +371,12 @@ Directory getFilesAndDirs(const std::wstring& path)
env::forEachEntry(path, &cx,
[](void* pcx, std::wstring_view path) {
Context* cx = (Context*)pcx;
- cx->current.top()->dirs.push_back({std::wstring(path.begin(), path.end())});
+
+ cx->current.top()->dirs.push_back({
+ std::wstring(path.begin(), path.end()),
+ MOShared::ToLowerCopy(path)
+ });
+
cx->current.push(&cx->current.top()->dirs.back());
},
diff --git a/src/envfs.h b/src/envfs.h
index 6cc98775..6e0da6d0 100644
--- a/src/envfs.h
+++ b/src/envfs.h
@@ -16,6 +16,8 @@ struct File
struct Directory
{
std::wstring name;
+ std::wstring lcname;
+
std::list dirs;
std::list files;
};
@@ -77,6 +79,14 @@ public:
}
}
+ template
+ void forEach(F&& f)
+ {
+ for (auto& ti : m_threads) {
+ f(ti.o);
+ }
+ }
+
private:
struct ThreadInfo
{
@@ -94,6 +104,7 @@ using DirEndF = void (void*, std::wstring_view);
using FileF = void (void*, std::wstring_view, FILETIME);
void setHandleCloserThreadCount(std::size_t n);
+void shrinkFs();
void forEachEntry(
const std::wstring& path, void* cx,
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index 5df2c1ba..a90f6f08 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -40,6 +40,30 @@ namespace MOShared
using namespace MOBase;
static const int MAXPATH_UNICODE = 32767;
+
+static std::atomic FileEntryCount(0);
+static std::atomic FilesOriginCount(0);
+static std::atomic FileRegisterCount(0);
+static std::atomic DirectoryEntryCount(0);
+static std::atomic OriginConnectionCount(0);
+
+template
+std::chrono::nanoseconds elapsed(F&& f)
+{
+ const auto start = std::chrono::high_resolution_clock::now();
+ f();
+ const auto end = std::chrono::high_resolution_clock::now();
+ return (end - start);
+}
+
+void logcounts(std::string w)
+{
+ log::debug(
+ "{}: FileEntry={} FilesOrigin={} FileRegister={} DirectoryEntry={} OriginConnection={}",
+ w, FileEntryCount, FilesOriginCount, FileRegisterCount, DirectoryEntryCount, OriginConnectionCount);
+}
+
+
static std::wstring tail(const std::wstring &source, const size_t count)
{
if (count >= source.length()) {
@@ -71,6 +95,101 @@ static bool DirCompareByName(const DirectoryEntry *lhs, const DirectoryEntry *rh
}
+DirectoryStats::DirectoryStats()
+{
+ std::memset(this, 0, sizeof(DirectoryStats));
+}
+
+DirectoryStats& DirectoryStats::operator+=(const DirectoryStats& o)
+{
+ dirTimes += o.dirTimes;
+ fileTimes += o.fileTimes;
+ sortTimes += o.sortTimes;
+
+ subdirLookupTimes += o.subdirLookupTimes;
+ addDirectoryTimes += o.addDirectoryTimes;
+
+ filesLookupTimes += o.filesLookupTimes;
+ addFileTimes += o.addFileTimes;
+ addOriginToFileTimes += o.addOriginToFileTimes;
+ addFileToOriginTimes += o.addFileToOriginTimes;
+ addFileToRegisterTimes += o.addFileToRegisterTimes;
+
+ originExists += o.originExists;
+ originCreate += o.originCreate;
+ originsNeededEnabled += o.originsNeededEnabled;
+
+ subdirExists += o.subdirExists;
+ subdirCreate += o.subdirCreate;
+
+ fileExists += o.fileExists;
+ fileCreate += o.fileCreate;
+ filesInsertedInRegister += o.filesInsertedInRegister;
+ filesAssignedInRegister += o.filesAssignedInRegister;
+
+ return *this;
+}
+
+std::string DirectoryStats::csvHeader()
+{
+ QStringList sl = {
+ "dirTimes",
+ "fileTimes",
+ "sortTimes",
+ "subdirLookupTimes",
+ "addDirectoryTimes",
+ "filesLookupTimes",
+ "addFileTimes",
+ "addOriginToFileTimes",
+ "addFileToOriginTimes",
+ "addFileToRegisterTimes",
+ "originExists",
+ "originCreate",
+ "originsNeededEnabled",
+ "subdirExists",
+ "subdirCreate",
+ "fileExists",
+ "fileCreate",
+ "filesInsertedInRegister",
+ "filesAssignedInRegister"};
+
+ return sl.join(",").toStdString();
+}
+
+std::string DirectoryStats::toCsv() const
+{
+ QStringList oss;
+
+ oss
+ << QString::number(dirTimes.count())
+ << QString::number(fileTimes.count())
+ << QString::number(sortTimes.count())
+
+ << QString::number(subdirLookupTimes.count())
+ << QString::number(addDirectoryTimes.count())
+
+ << QString::number(filesLookupTimes.count())
+ << QString::number(addFileTimes.count())
+ << QString::number(addOriginToFileTimes.count())
+ << QString::number(addFileToOriginTimes.count())
+ << QString::number(addFileToRegisterTimes.count())
+
+ << QString::number(originExists)
+ << QString::number(originCreate)
+ << QString::number(originsNeededEnabled)
+
+ << QString::number(subdirExists)
+ << QString::number(subdirCreate)
+
+ << QString::number(fileExists)
+ << QString::number(fileCreate)
+ << QString::number(filesInsertedInRegister)
+ << QString::number(filesAssignedInRegister);
+
+ return oss.join(",").toStdString();
+}
+
+
class OriginConnection
{
public:
@@ -80,6 +199,12 @@ public:
OriginConnection()
: m_NextID(0)
{
+ ++OriginConnectionCount;
+ }
+
+ ~OriginConnection()
+ {
+ --OriginConnectionCount;
}
FilesOrigin& createOrigin(
@@ -172,6 +297,7 @@ FileEntry::FileEntry() :
m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize),
m_LastAccessed(time(nullptr))
{
+ ++FileEntryCount;
}
FileEntry::FileEntry(Index index, std::wstring name, DirectoryEntry *parent) :
@@ -179,6 +305,16 @@ FileEntry::FileEntry(Index index, std::wstring name, DirectoryEntry *parent) :
m_FileSize(NoFileSize), m_CompressedFileSize(NoFileSize),
m_LastAccessed(time(nullptr))
{
+ ++FileEntryCount;
+}
+
+FileEntry::~FileEntry()
+{
+ while (!m_Alternatives.empty()) {
+ m_Alternatives.pop_back();
+ }
+
+ --FileEntryCount;
}
void FileEntry::addOrigin(
@@ -407,6 +543,7 @@ bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent)
FilesOrigin::FilesOrigin()
: m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0)
{
+ ++FilesOriginCount;
}
FilesOrigin::FilesOrigin(const FilesOrigin &reference)
@@ -418,6 +555,7 @@ FilesOrigin::FilesOrigin(const FilesOrigin &reference)
, m_FileRegister(reference.m_FileRegister)
, m_OriginConnection(reference.m_OriginConnection)
{
+ ++FilesOriginCount;
}
FilesOrigin::FilesOrigin(
@@ -428,6 +566,12 @@ FilesOrigin::FilesOrigin(
m_Priority(priority), m_FileRegister(fileRegister),
m_OriginConnection(originConnection)
{
+ ++FilesOriginCount;
+}
+
+FilesOrigin::~FilesOrigin()
+{
+ --FilesOriginCount;
}
void FilesOrigin::setPriority(int priority)
@@ -468,8 +612,15 @@ FileEntry::Ptr FilesOrigin::findFile(FileEntry::Index index) const
}
void FilesOrigin::enable(bool enabled, time_t notAfter)
+{
+ DirectoryStats dummy;
+ enable(enabled, dummy, notAfter);
+}
+
+void FilesOrigin::enable(bool enabled, DirectoryStats& stats, time_t notAfter)
{
if (!enabled) {
+ ++stats.originsNeededEnabled;
std::set copy = m_Files;
m_FileRegister.lock()->removeOriginMulti(copy, m_ID, notAfter);
m_Files.clear();
@@ -504,6 +655,12 @@ bool FilesOrigin::containsArchive(std::wstring archiveName)
FileRegister::FileRegister(boost::shared_ptr originConnection)
: m_OriginConnection(originConnection)
{
+ ++FileRegisterCount;
+}
+
+FileRegister::~FileRegister()
+{
+ --FileRegisterCount;
}
bool FileRegister::indexValid(FileEntry::Index index) const
@@ -511,14 +668,26 @@ bool FileRegister::indexValid(FileEntry::Index index) const
return (m_Files.find(index) != m_Files.end());
}
-FileEntry::Ptr FileRegister::createFile(std::wstring name, DirectoryEntry *parent)
+FileEntry::Ptr FileRegister::createFile(
+ std::wstring name, DirectoryEntry *parent, DirectoryStats& stats)
{
FileEntry::Index index = generateIndex();
+ FileEntry::Ptr p;
- auto r = m_Files.insert_or_assign(
- index, FileEntry::Ptr(new FileEntry(index, std::move(name), parent)));
+ stats.addFileToRegisterTimes += elapsed([&]{
+ auto r = m_Files.insert_or_assign(
+ index, FileEntry::Ptr(new FileEntry(index, std::move(name), parent)));
- return r.first->second;
+ if (r.second) {
+ ++stats.filesInsertedInRegister;
+ } else {
+ ++stats.filesAssignedInRegister;
+ }
+
+ p = r.first->second;
+ });
+
+ return p;
}
FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const
@@ -643,6 +812,7 @@ DirectoryEntry::DirectoryEntry(
m_OriginConnection(new OriginConnection),
m_Name(std::move(name)), m_Parent(parent), m_Populated(false), m_TopLevel(true)
{
+ ++DirectoryEntryCount;
m_FileRegister.reset(new FileRegister(m_OriginConnection));
m_Origins.insert(originID);
}
@@ -654,23 +824,24 @@ DirectoryEntry::DirectoryEntry(
m_FileRegister(fileRegister), m_OriginConnection(originConnection),
m_Name(std::move(name)), m_Parent(parent), m_Populated(false), m_TopLevel(false)
{
+ ++DirectoryEntryCount;
m_Origins.insert(originID);
}
DirectoryEntry::~DirectoryEntry()
{
+ --DirectoryEntryCount;
clear();
}
void DirectoryEntry::clear()
{
- m_Files.clear();
- m_FilesLookup.clear();
-
- for (DirectoryEntry *entry : m_SubDirectories) {
- delete entry;
+ for (auto itor=m_SubDirectories.rbegin(); itor!=m_SubDirectories.rend(); ++itor) {
+ delete *itor;
}
+ m_Files.clear();
+ m_FilesLookup.clear();
m_SubDirectories.clear();
m_SubDirectoriesLookup.clear();
}
@@ -678,7 +849,8 @@ void DirectoryEntry::clear()
void DirectoryEntry::addFromOrigin(
const std::wstring &originName, const std::wstring &directory, int priority)
{
- FilesOrigin &origin = createOrigin(originName, directory, priority);
+ DirectoryStats dummy;
+ FilesOrigin &origin = createOrigin(originName, directory, priority, dummy);
if (directory.length() != 0) {
boost::scoped_array buffer(new wchar_t[MAXPATH_UNICODE + 1]);
@@ -693,27 +865,36 @@ void DirectoryEntry::addFromOrigin(
void DirectoryEntry::addFromList(
const std::wstring &originName, const std::wstring &directory,
- env::Directory& root, int priority)
+ env::Directory& root, int priority, DirectoryStats& stats)
{
- FilesOrigin &origin = createOrigin(originName, directory, priority);
- addDir(origin, root);
+ stats = {};
+
+ FilesOrigin &origin = createOrigin(originName, directory, priority, stats);
+ addDir(origin, root, stats);
}
-void DirectoryEntry::addDir(FilesOrigin& origin, env::Directory& d)
+void DirectoryEntry::addDir(
+ FilesOrigin& origin, env::Directory& d, DirectoryStats& stats)
{
- for (auto& sd : d.dirs) {
- auto* sdirEntry = getSubDirectory(sd.name, true, origin.getID());
- sdirEntry->addDir(origin, sd);
- }
+ stats.dirTimes += elapsed([&]{
+ for (auto& sd : d.dirs) {
+ auto* sdirEntry = getSubDirectory(sd, true, stats, origin.getID());
+ sdirEntry->addDir(origin, sd, stats);
+ }
+ });
- for (auto& f : d.files) {
- insert(f, origin, L"", -1);
- }
+ stats.fileTimes += elapsed([&]{
+ for (auto& f : d.files) {
+ insert(f, origin, L"", -1, stats);
+ }
+ });
- std::sort(
- m_SubDirectories.begin(),
- m_SubDirectories.end(),
- &DirCompareByName);
+ stats.sortTimes += elapsed([&]{
+ std::sort(
+ m_SubDirectories.begin(),
+ m_SubDirectories.end(),
+ &DirCompareByName);
+ });
m_Populated = true;
}
@@ -722,7 +903,8 @@ void DirectoryEntry::addFromBSA(
const std::wstring &originName, std::wstring &directory,
const std::wstring &fileName, int priority, int order)
{
- FilesOrigin &origin = createOrigin(originName, directory, priority);
+ DirectoryStats dummy;
+ FilesOrigin &origin = createOrigin(originName, directory, priority, dummy);
WIN32_FILE_ATTRIBUTE_DATA fileData;
if (::GetFileAttributesExW(fileName.c_str(), GetFileExInfoStandard, &fileData) == 0) {
@@ -1022,13 +1204,16 @@ bool DirectoryEntry::hasContentsFromOrigin(int originID) const
}
FilesOrigin &DirectoryEntry::createOrigin(
- const std::wstring &originName, const std::wstring &directory, int priority)
+ const std::wstring &originName, const std::wstring &directory, int priority,
+ DirectoryStats& stats)
{
if (m_OriginConnection->exists(originName)) {
+ ++stats.originExists;
FilesOrigin &origin = m_OriginConnection->getByName(originName);
- origin.enable(true);
+ origin.enable(true, stats);
return origin;
} else {
+ ++stats.originCreate;
return m_OriginConnection->createOrigin(
originName, directory, priority, m_FileRegister, m_OriginConnection);
}
@@ -1051,7 +1236,11 @@ FileEntry::Ptr DirectoryEntry::insert(
if (iter != m_Files.end()) {
file = m_FileRegister->getFile(iter->second);
} else {
- file = m_FileRegister->createFile(std::wstring(fileName.begin(), fileName.end()), this);
+ DirectoryStats dummy;
+
+ file = m_FileRegister->createFile(
+ std::wstring(fileName.begin(), fileName.end()), this, dummy);
+
addFileToList(std::move(fileNameLower), file->getIndex());
// fileNameLower has moved from this point
}
@@ -1063,21 +1252,37 @@ FileEntry::Ptr DirectoryEntry::insert(
}
FileEntry::Ptr DirectoryEntry::insert(
- env::File& file, FilesOrigin &origin, std::wstring_view archive, int order)
+ env::File& file, FilesOrigin &origin, std::wstring_view archive, int order,
+ DirectoryStats& stats)
{
- auto iter = m_Files.find(file.lcname);
+ FilesMap::iterator itor;
+
+ stats.filesLookupTimes += elapsed([&]{
+ itor = m_Files.find(file.lcname);
+ });
+
FileEntry::Ptr fe;
- if (iter != m_Files.end()) {
- fe = m_FileRegister->getFile(iter->second);
+ if (itor != m_Files.end()) {
+ ++stats.fileExists;
+ fe = m_FileRegister->getFile(itor->second);
} else {
- fe = m_FileRegister->createFile(std::move(file.name), this);
- addFileToList(std::move(file.lcname), fe->getIndex());
+ fe = m_FileRegister->createFile(std::move(file.name), this, stats);
+
+ stats.addFileTimes += elapsed([&]{
+ addFileToList(std::move(file.lcname), fe->getIndex());
+ });
+
// both file.name and file.lcname have been moved from this point
}
- fe->addOrigin(origin.getID(), file.lastModified, archive, order);
- origin.addFile(fe->getIndex());
+ stats.addOriginToFileTimes += elapsed([&]{
+ fe->addOrigin(origin.getID(), file.lastModified, archive, order);
+ });
+
+ stats.addFileToOriginTimes += elapsed([&]{
+ origin.addFile(fe->getIndex());
+ });
return fe;
}
@@ -1205,6 +1410,40 @@ DirectoryEntry *DirectoryEntry::getSubDirectory(
}
}
+DirectoryEntry *DirectoryEntry::getSubDirectory(
+ env::Directory& dir, bool create, DirectoryStats& stats, int originID)
+{
+ SubDirectoriesLookup::iterator itor;
+
+ stats.subdirLookupTimes += elapsed([&] {
+ itor = m_SubDirectoriesLookup.find(dir.lcname);
+ });
+
+ if (itor != m_SubDirectoriesLookup.end()) {
+ ++stats.subdirExists;
+ return itor->second;
+ }
+
+ if (create) {
+ ++stats.subdirCreate;
+
+ auto* entry = new DirectoryEntry(
+ std::move(dir.name), this, originID,
+ m_FileRegister, m_OriginConnection);
+ // dir.name is moved from this point
+
+ stats.addDirectoryTimes += elapsed([&]{
+ addDirectoryToList(entry, std::move(dir.lcname));
+ });
+
+ // dir.lcname is moved from this point
+
+ return entry;
+ } else {
+ return nullptr;
+ }
+}
+
DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive(
const std::wstring &path, bool create, int originID)
{
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index f8c72d77..c8ec8a4e 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -60,6 +60,44 @@ class OriginConnection;
class FileRegister;
+struct DirectoryStats
+{
+ std::string mod;
+
+ std::chrono::nanoseconds dirTimes;
+ std::chrono::nanoseconds fileTimes;
+ std::chrono::nanoseconds sortTimes;
+
+ std::chrono::nanoseconds subdirLookupTimes;
+ std::chrono::nanoseconds addDirectoryTimes;
+
+ std::chrono::nanoseconds filesLookupTimes;
+ std::chrono::nanoseconds addFileTimes;
+ std::chrono::nanoseconds addOriginToFileTimes;
+ std::chrono::nanoseconds addFileToOriginTimes;
+ std::chrono::nanoseconds addFileToRegisterTimes;
+
+ int64_t originExists;
+ int64_t originCreate;
+ int64_t originsNeededEnabled;
+
+ int64_t subdirExists;
+ int64_t subdirCreate;
+
+ int64_t fileExists;
+ int64_t fileCreate;
+ int64_t filesInsertedInRegister;
+ int64_t filesAssignedInRegister;
+
+ DirectoryStats();
+
+ DirectoryStats& operator+=(const DirectoryStats& o);
+
+ static std::string csvHeader();
+ std::string toCsv() const;
+};
+
+
class FileEntry
{
public:
@@ -81,6 +119,7 @@ public:
FileEntry();
FileEntry(Index index, std::wstring name, DirectoryEntry *parent);
+ ~FileEntry();
Index getIndex() const
{
@@ -195,6 +234,7 @@ class FilesOrigin
public:
FilesOrigin();
FilesOrigin(const FilesOrigin &reference);
+ ~FilesOrigin();
// sets priority for this origin, but it will overwrite the existing mapping
// for this priority, the previous origin will no longer be referenced
@@ -224,7 +264,9 @@ public:
std::vector getFiles() const;
FileEntry::Ptr findFile(FileEntry::Index index) const;
+ void enable(bool enabled, DirectoryStats& stats, time_t notAfter = LONG_MAX);
void enable(bool enabled, time_t notAfter = LONG_MAX);
+
bool isDisabled() const
{
return m_Disabled;
@@ -260,10 +302,13 @@ class FileRegister
{
public:
FileRegister(boost::shared_ptr originConnection);
+ ~FileRegister();
bool indexValid(FileEntry::Index index) const;
- FileEntry::Ptr createFile(std::wstring name, DirectoryEntry *parent);
+ FileEntry::Ptr createFile(
+ std::wstring name, DirectoryEntry *parent, DirectoryStats& stats);
+
FileEntry::Ptr getFile(FileEntry::Index index) const;
size_t size() const
@@ -362,7 +407,7 @@ public:
void addFromList(
const std::wstring &originName, const std::wstring &directory,
- env::Directory& root, int priority);
+ env::Directory& root, int priority, DirectoryStats& stats);
void propagateOrigin(int origin);
@@ -477,7 +522,7 @@ public:
FilesOrigin &createOrigin(
const std::wstring &originName,
- const std::wstring &directory, int priority);
+ const std::wstring &directory, int priority, DirectoryStats& stats);
void removeFiles(const std::set &indices);
@@ -510,7 +555,7 @@ private:
FileEntry::Ptr insert(
env::File& file, FilesOrigin &origin,
- std::wstring_view archive, int order);
+ std::wstring_view archive, int order, DirectoryStats& stats);
void addFiles(
FilesOrigin &origin, wchar_t *buffer, int bufferOffset);
@@ -519,11 +564,15 @@ private:
FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime,
const std::wstring &archiveName, int order);
- void addDir(FilesOrigin& origin, env::Directory& d);
+ void addDir(FilesOrigin& origin, env::Directory& d, DirectoryStats& stats);
DirectoryEntry* getSubDirectory(
std::wstring_view name, bool create, int originID = -1);
+ DirectoryEntry* getSubDirectory(
+ env::Directory& dir, bool create, DirectoryStats& stats,
+ int originID = -1);
+
DirectoryEntry* getSubDirectoryRecursive(
const std::wstring &path, bool create, int originID = -1);
--
cgit v1.3.1
From 29244f3328cdf878ac557acbbabb19a5b7190b2d Mon Sep 17 00:00:00 2001
From: isanae <14251494+isanae@users.noreply.github.com>
Date: Thu, 13 Feb 2020 20:13:37 -0500
Subject: thread-safe directory entry
---
src/directoryrefresher.cpp | 113 ++++++++++++++++++++++++++----------------
src/envfs.cpp | 70 ++++++++++++++++++++++----
src/envfs.h | 10 +++-
src/shared/directoryentry.cpp | 87 ++++++++++++++++++++++++--------
src/shared/directoryentry.h | 8 +++
5 files changed, 212 insertions(+), 76 deletions(-)
(limited to 'src/shared/directoryentry.cpp')
diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp
index 5bff9d37..bce3f65a 100644
--- a/src/directoryrefresher.cpp
+++ b/src/directoryrefresher.cpp
@@ -25,6 +25,7 @@ along with Mod Organizer. If not, see .
#include "modinfo.h"
#include "settings.h"
#include "envfs.h"
+#include "modinfodialogfwd.h"
#include
#include
@@ -125,7 +126,10 @@ void DirectoryRefresher::stealModFilesIntoStructure(
// instead of adding all the files of the target directory, we just change the root of the specified
// files to this mod
- FilesOrigin &origin = directoryStructure->createOrigin(ToWString(modName), directoryW, priority);
+ DirectoryStats dummy;
+ FilesOrigin &origin = directoryStructure->createOrigin(
+ ToWString(modName), directoryW, priority, dummy);
+
for (const QString &filename : stealFiles) {
if (filename.isEmpty()) {
log::warn("Trying to find file with no name");
@@ -189,8 +193,12 @@ void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure
struct ModThread
{
+ DirectoryEntry* ds = nullptr;
+ std::wstring modName;
std::wstring path;
+ int prio = -1;
env::Directory* dir = nullptr;
+
std::condition_variable cv;
std::mutex mutex;
bool ready = false;
@@ -206,7 +214,16 @@ struct ModThread
std::unique_lock lock(mutex);
cv.wait(lock, [&]{ return ready; });
- *dir = env::getFilesAndDirs(path);
+ ds->addFromOrigin(modName, path, prio);
+
+ /*if (Settings::instance().archiveParsing()) {
+ addModBSAToStructure(
+ directoryStructure,
+ entries[i].modName,
+ prio,
+ entries[i].absolutePath,
+ entries[i].archives);
+ }*/
ready = false;
}
@@ -216,65 +233,75 @@ void DirectoryRefresher::addMultipleModsFilesToStructure(
MOShared::DirectoryEntry *directoryStructure,
const std::vector& entries, bool emitProgress)
{
- TimeThis tt(QString("add %1 mods").arg(entries.size()));
-
- env::ThreadPool threads(m_threadCount);
std::vector dirs(entries.size());
- for (std::size_t i=0; i(i + 1);
+ {
+ TimeThis tt("walk dirs");
- try {
- if (e.stealFiles.length() > 0) {
- stealModFilesIntoStructure(
- directoryStructure, e.modName, prio, e.absolutePath, e.stealFiles);
- } else {
- auto& mt = threads.request();
+ env::ThreadPool threads(m_threadCount);
- mt.path = QDir::toNativeSeparators(e.absolutePath).toStdWString();
- mt.dir = &dirs[i];
+ for (std::size_t i=0; i(i + 1);
- mt.wakeup();
+ try
+ {
+ if (e.stealFiles.length() > 0) {
+ stealModFilesIntoStructure(
+ directoryStructure, e.modName, prio, e.absolutePath, e.stealFiles);
+ } else {
+ auto& mt = threads.request();
+
+ mt.ds = directoryStructure;
+ mt.modName = entries[i].modName.toStdWString();
+ mt.path = QDir::toNativeSeparators(e.absolutePath).toStdWString();
+ mt.prio = prio;
+ mt.dir = &dirs[i];
+
+ mt.wakeup();
+ }
+ } catch (const std::exception& ex) {
+ emit error(tr("failed to read mod (%1): %2").arg(e.modName, ex.what()));
}
- } catch (const std::exception& ex) {
- emit error(tr("failed to read mod (%1): %2").arg(e.modName, ex.what()));
- }
- if (emitProgress) {
- emit progress((static_cast(i) * 100) / static_cast(entries.size()) + 1);
+ if (emitProgress) {
+ emit progress((static_cast(i) * 100) / static_cast(entries.size()) + 1);
+ }
}
- }
-
- threads.join();
-
- for (std::size_t i=0; i(i + 1);
-
- directoryStructure->addFromList(
- entries[i].modName.toStdWString(),
- entries[i].absolutePath.toStdWString(),
- dirs[i],
- prio);
- if (Settings::instance().archiveParsing()) {
- addModBSAToStructure(
- directoryStructure,
- entries[i].modName,
- prio,
- entries[i].absolutePath,
- entries[i].archives);
- }
+ threads.join();
}
+
+ //std::sort(stats.begin(), stats.end(), [](auto&& a, auto&& b){
+ // return (naturalCompare(QString::fromStdString(a.mod), QString::fromStdString(b.mod)) < 0);
+ //});
+
+ //static int run = 1;
+ //
+ //std::ofstream out("c:\\tmp\\data.csv", std::ios::app);
+
+ //out << fmt::format("what,run,{}", DirectoryStats::csvHeader());
+ //
+ //for (std::size_t i=0; icurrent.top()->dirs.push_back({
- std::wstring(path.begin(), path.end()),
- MOShared::ToLowerCopy(path)
- });
-
+ cx->current.top()->dirs.push_back(Directory(path));
cx->current.push(&cx->current.top()->dirs.back());
},
@@ -385,18 +381,70 @@ Directory getFilesAndDirs(const std::wstring& path)
cx->current.pop();
},
- [](void* pcx, std::wstring_view path, FILETIME ft) {
+ [](void* pcx, std::wstring_view path, FILETIME ft) {
Context* cx = (Context*)pcx;
- cx->current.top()->files.push_back({
- std::wstring(path.begin(), path.end()),
- MOShared::ToLowerCopy(path),
- ft
- });
+ cx->current.top()->files.push_back(File(path, ft));
}
);
return root;
}
+File::File(std::wstring_view n, FILETIME ft) :
+ name(n.begin(), n.end()),
+ lcname(MOShared::ToLowerCopy(name)),
+ lastModified(ft)
+{
+}
+
+Directory::Directory()
+{
+}
+
+Directory::Directory(std::wstring_view n)
+ : name(n.begin(), n.end()), lcname(MOShared::ToLowerCopy(name))
+{
+}
+
+
+void getFilesAndDirsWithFindImpl(const std::wstring& path, Directory& d)
+{
+ const std::wstring searchString = path + L"\\*";
+
+ WIN32_FIND_DATAW findData;
+
+ HANDLE searchHandle = ::FindFirstFileExW(
+ searchString.c_str(), FindExInfoBasic, &findData, FindExSearchNameMatch,
+ nullptr, FIND_FIRST_EX_LARGE_FETCH);
+
+ if (searchHandle != INVALID_HANDLE_VALUE) {
+ BOOL result = true;
+
+ while (result) {
+ if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if ((wcscmp(findData.cFileName, L".") != 0) &&
+ (wcscmp(findData.cFileName, L"..") != 0)) {
+ const std::wstring newPath = path + L"\\" + findData.cFileName;
+ d.dirs.push_back(Directory(findData.cFileName));
+ getFilesAndDirsWithFindImpl(newPath, d.dirs.back());
+ }
+ } else {
+ d.files.push_back(File(findData.cFileName, findData.ftLastWriteTime));
+ }
+
+ result = ::FindNextFileW(searchHandle, &findData);
+ }
+ }
+
+ ::FindClose(searchHandle);
+}
+
+Directory getFilesAndDirsWithFind(const std::wstring& path)
+{
+ Directory d;
+ getFilesAndDirsWithFindImpl(path, d);
+ return d;
+}
+
} // namespace
diff --git a/src/envfs.h b/src/envfs.h
index 6e0da6d0..001f2d09 100644
--- a/src/envfs.h
+++ b/src/envfs.h
@@ -11,6 +11,8 @@ struct File
std::wstring name;
std::wstring lcname;
FILETIME lastModified;
+
+ File(std::wstring_view name, FILETIME ft);
};
struct Directory
@@ -18,8 +20,11 @@ struct Directory
std::wstring name;
std::wstring lcname;
- std::list dirs;
- std::list files;
+ std::vector dirs;
+ std::vector files;
+
+ Directory();
+ Directory(std::wstring_view name);
};
@@ -111,6 +116,7 @@ void forEachEntry(
DirStartF* dirStartF, DirEndF* dirEndF, FileF* fileF);
Directory getFilesAndDirs(const std::wstring& path);
+Directory getFilesAndDirsWithFind(const std::wstring& path);
} // namespace
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
index a90f6f08..e1464595 100644
--- a/src/shared/directoryentry.cpp
+++ b/src/shared/directoryentry.cpp
@@ -214,11 +214,14 @@ public:
{
int newID = createID();
- m_Origins[newID] = FilesOrigin(newID, originName, directory, priority, fileRegister, originConnection);
- m_OriginsNameMap[originName] = newID;
- m_OriginsPriorityMap[priority] = newID;
+ auto itor = m_Origins.insert({newID, FilesOrigin(
+ newID, originName, directory, priority,
+ fileRegister, originConnection)}).first;
- return m_Origins[newID];
+ m_OriginsNameMap.insert({originName, newID});
+ m_OriginsPriorityMap.insert({priority, newID});
+
+ return itor->second;
}
bool exists(const std::wstring &name)
@@ -597,9 +600,13 @@ std::vector FilesOrigin::getFiles() const
{
std::vector result;
- for (FileEntry::Index fileIdx : m_Files) {
- if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) {
- result.push_back(p);
+ {
+ std::scoped_lock lock(m_Mutex);
+
+ for (FileEntry::Index fileIdx : m_Files) {
+ if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) {
+ result.push_back(p);
+ }
}
}
@@ -621,9 +628,16 @@ void FilesOrigin::enable(bool enabled, DirectoryStats& stats, time_t notAfter)
{
if (!enabled) {
++stats.originsNeededEnabled;
- std::set copy = m_Files;
+
+ std::set copy;
+
+ {
+ std::scoped_lock lock(m_Mutex);
+ copy = m_Files;
+ m_Files.clear();
+ }
+
m_FileRegister.lock()->removeOriginMulti(copy, m_ID, notAfter);
- m_Files.clear();
}
m_Disabled = !enabled;
@@ -631,6 +645,8 @@ void FilesOrigin::enable(bool enabled, DirectoryStats& stats, time_t notAfter)
void FilesOrigin::removeFile(FileEntry::Index index)
{
+ std::scoped_lock lock(m_Mutex);
+
auto iter = m_Files.find(index);
if (iter != m_Files.end()) {
@@ -640,6 +656,8 @@ void FilesOrigin::removeFile(FileEntry::Index index)
bool FilesOrigin::containsArchive(std::wstring archiveName)
{
+ std::scoped_lock lock(m_Mutex);
+
for (FileEntry::Index fileIdx : m_Files) {
if (FileEntry::Ptr p = m_FileRegister.lock()->getFile(fileIdx)) {
if (p->isFromArchive(archiveName)) {
@@ -665,6 +683,7 @@ FileRegister::~FileRegister()
bool FileRegister::indexValid(FileEntry::Index index) const
{
+ std::scoped_lock lock(m_Mutex);
return (m_Files.find(index) != m_Files.end());
}
@@ -675,6 +694,8 @@ FileEntry::Ptr FileRegister::createFile(
FileEntry::Ptr p;
stats.addFileToRegisterTimes += elapsed([&]{
+ std::scoped_lock lock(m_Mutex);
+
auto r = m_Files.insert_or_assign(
index, FileEntry::Ptr(new FileEntry(index, std::move(name), parent)));
@@ -692,6 +713,8 @@ FileEntry::Ptr FileRegister::createFile(
FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const
{
+ std::scoped_lock lock(m_Mutex);
+
auto iter = m_Files.find(index);
if (iter != m_Files.end()) {
@@ -703,6 +726,8 @@ FileEntry::Ptr FileRegister::getFile(FileEntry::Index index) const
bool FileRegister::removeFile(FileEntry::Index index)
{
+ std::scoped_lock lock(m_Mutex);
+
auto iter = m_Files.find(index);
if (iter != m_Files.end()) {
@@ -717,12 +742,15 @@ bool FileRegister::removeFile(FileEntry::Index index)
void FileRegister::removeOrigin(FileEntry::Index index, int originID)
{
+ std::unique_lock lock(m_Mutex);
+
auto iter = m_Files.find(index);
if (iter != m_Files.end()) {
if (iter->second->removeOrigin(originID)) {
- unregisterFile(iter->second);
m_Files.erase(iter);
+ lock.unlock();
+ unregisterFile(iter->second);
}
} else {
log::error(QObject::tr("invalid file index for remove (for origin): {}").toStdString(), index);
@@ -734,17 +762,21 @@ void FileRegister::removeOriginMulti(
{
std::vector removedFiles;
- for (auto iter = indices.begin(); iter != indices.end(); ) {
- auto pos = m_Files.find(*iter);
+ {
+ std::scoped_lock lock(m_Mutex);
+
+ for (auto iter = indices.begin(); iter != indices.end(); ) {
+ auto pos = m_Files.find(*iter);
- if (pos != m_Files.end()
- && (pos->second->lastAccessed() < notAfter)
- && pos->second->removeOrigin(originID)) {
- removedFiles.push_back(pos->second);
- m_Files.erase(pos);
- ++iter;
- } else {
- indices.erase(iter++);
+ if (pos != m_Files.end()
+ && (pos->second->lastAccessed() < notAfter)
+ && pos->second->removeOrigin(originID)) {
+ removedFiles.push_back(pos->second);
+ m_Files.erase(pos);
+ ++iter;
+ } else {
+ indices.erase(iter++);
+ }
}
}
@@ -776,6 +808,8 @@ void FileRegister::removeOriginMulti(
void FileRegister::sortOrigins()
{
+ std::scoped_lock lock(m_Mutex);
+
for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
iter->second->sortOrigins();
}
@@ -1228,6 +1262,8 @@ FileEntry::Ptr DirectoryEntry::insert(
std::wstring_view fileName, FilesOrigin &origin, FILETIME fileTime,
std::wstring_view archive, int order)
{
+ std::scoped_lock lock(m_FilesMutex);
+
std::wstring fileNameLower = ToLowerCopy(fileName);
auto iter = m_Files.find(fileNameLower);
@@ -1255,6 +1291,8 @@ FileEntry::Ptr DirectoryEntry::insert(
env::File& file, FilesOrigin &origin, std::wstring_view archive, int order,
DirectoryStats& stats)
{
+ std::scoped_lock lock(m_FilesMutex);
+
FilesMap::iterator itor;
stats.filesLookupTimes += elapsed([&]{
@@ -1307,7 +1345,12 @@ void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOf
[](void* pcx, std::wstring_view path) {
Context* cx = (Context*)pcx;
auto* current= cx->current.top();
- std::sort(current->m_SubDirectories.begin(), current->m_SubDirectories.end(), &DirCompareByName);
+
+ {
+ std::scoped_lock lock(current->m_SubDirMutex);
+ std::sort(current->m_SubDirectories.begin(), current->m_SubDirectories.end(), &DirCompareByName);
+ }
+
cx->current.pop();
},
@@ -1389,6 +1432,8 @@ void DirectoryEntry::addFiles(
DirectoryEntry *DirectoryEntry::getSubDirectory(
std::wstring_view name, bool create, int originID)
{
+ std::scoped_lock lock(m_SubDirMutex);
+
std::wstring nameLc = ToLowerCopy(name);
auto itor = m_SubDirectoriesLookup.find(nameLc);
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
index c8ec8a4e..71e8b32c 100644
--- a/src/shared/directoryentry.h
+++ b/src/shared/directoryentry.h
@@ -26,6 +26,8 @@ along with Mod Organizer. If not, see .
#include
#include