diff --git a/es-app/src/Gamelist.cpp b/es-app/src/Gamelist.cpp index 918fe3f62..a32d1bc2c 100644 --- a/es-app/src/Gamelist.cpp +++ b/es-app/src/Gamelist.cpp @@ -113,7 +113,21 @@ std::vector loadGamelistFile(const std::string xmlpath, SystemData* s LOG(LogInfo) << "Parsing XML file \"" << xmlpath << "\"..."; pugi::xml_document doc; - pugi::xml_parse_result result = fromFile ? doc.load_file(WINSTRINGW(xmlpath).c_str()) : doc.load_string(xmlpath.c_str()); + pugi::xml_parse_result result; + std::vector buffer; + + if (fromFile) + { + buffer = Utils::FileSystem::readAllBytes(xmlpath); + if (!buffer.size()) + { + LOG(LogError) << "Error parsing XML file \"" << xmlpath << "\"!\n " << result.description(); + return ret; + } + result = doc.load_buffer_inplace(buffer.data(), buffer.size(), pugi::parse_default); + } + else + result = doc.load_string(xmlpath.c_str()); if (!result) { diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index 427814876..87fe7e18a 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -143,7 +143,7 @@ MetaDataId MetaDataList::getId(const std::string& key) const MetaDataList::MetaDataList(MetaDataListType type) : mType(type), mWasChanged(false), mRelativeTo(nullptr) { - + memset(mIndices, -1, sizeof(mIndices)); } void MetaDataList::loadFromXML(MetaDataListType type, pugi::xml_node& node, SystemData* system) @@ -291,16 +291,18 @@ void MetaDataList::appendToXML(pugi::xml_node& parent, bool ignoreDefaults, cons if (mddIter->id == MetaDataId::GenreIds) continue; - auto mapIter = mMap.find(mddIter->id); - if(mapIter != mMap.cend()) + auto idx = mIndices[mddIter->id]; + //auto mapIter = mMap.find(mddIter->id); + //if(mapIter != mMap.cend()) + if (idx >= 0) { // we have this value! // if it's just the default (and we ignore defaults), don't write it - if (ignoreDefaults && mapIter->second == mddIter->defaultValue) + if (ignoreDefaults && mValues[idx] == mddIter->defaultValue) // mapIter->second continue; // try and make paths relative if we can - std::string value = mapIter->second; + std::string value = mValues[idx]; // mapIter->second; if (mddIter->type == MD_PATH) { if (fullPaths && mRelativeTo != nullptr) @@ -362,21 +364,40 @@ void MetaDataList::set(MetaDataId id, const std::string& value) return; } - // Players -> remove "1-" - // if (mType == GAME_METADATA && id == 12 && Utils::String::startsWith(value, "1-")) // "players" - // { - // mMap[id] = Utils::String::replace(value, "1-", ""); - // return; - // } - - auto prev = mMap.find(id); - if (prev != mMap.cend() && prev->second == value) + auto idx = mIndices[id]; + if (idx >= 0 && mValues[idx] == value) +// auto prev = mMap.find(id); + // if (prev != mMap.cend() && prev->second == value) return; - if (mGameTypeMap[id] == MD_PATH && mRelativeTo != nullptr) // if it's a path, resolve relative paths - mMap[id] = Utils::FileSystem::createRelativePath(value, mRelativeTo->getStartPath(), true); + #define IS_TRIMCHAR(c) (c == ' ' || c == '\t' || c == '\r' || c == '\n') + + if (mGameTypeMap[id] == MD_PATH && mRelativeTo != nullptr) // if it's a path, resolve relative paths + { + if (idx < 0) + { + if (value.size()) + { + mIndices[id] = (int8_t)mValues.size(); + mValues.push_back(value[0] == '.' && value[1] == '/' ? value : Utils::FileSystem::createRelativePath(value, mRelativeTo->getStartPath(), true)); + } + } + else + mValues[idx] = value[0] == '.' && value[1] == '/' ? value : Utils::FileSystem::createRelativePath(value, mRelativeTo->getStartPath(), true); + } else - mMap[id] = Utils::String::trim(value); + { + if (idx < 0) + { + if (value.size()) + { + mIndices[id] = (int8_t)mValues.size(); + mValues.push_back(value.size() && IS_TRIMCHAR(value[0]) && IS_TRIMCHAR(value.back()) ? Utils::String::trim(value) : value); + } + } + else + mValues[idx] = value.size() && IS_TRIMCHAR(value[0]) && IS_TRIMCHAR(value.back()) ? Utils::String::trim(value) : value; + } mWasChanged = true; } @@ -386,13 +407,16 @@ const std::string MetaDataList::get(MetaDataId id, bool resolveRelativePaths) co if (id == MetaDataId::Name) return mName; - auto it = mMap.find(id); - if (it != mMap.end()) + auto idx = mIndices[id]; + if (idx >= 0) +// auto it = mMap.find(id); +// if (it != mMap.end()) { + if (resolveRelativePaths && mGameTypeMap[id] == MD_PATH && mRelativeTo != nullptr) // if it's a path, resolve relative paths - return Utils::FileSystem::resolveRelativePath(it->second, mRelativeTo->getStartPath(), true); + return Utils::FileSystem::resolveRelativePath(mValues[idx]/*it->second*/, mRelativeTo->getStartPath(), true); - return it->second; + return mValues[idx]; // it->second; } return mDefaultGameMap[id]; diff --git a/es-app/src/MetaData.h b/es-app/src/MetaData.h index 04a9364a4..aa93b1ffb 100644 --- a/es-app/src/MetaData.h +++ b/es-app/src/MetaData.h @@ -76,7 +76,10 @@ enum MetaDataId GenreIds = 39, Family = 40, Bezel = 41, - Tags = 42 + Tags = 42, + + // Important : Control value + MetaDataIdCount }; namespace MetaDataImportType @@ -182,9 +185,12 @@ private: std::string mName; MetaDataListType mType; - std::map mMap; +// std::map mMap; bool mWasChanged; SystemData* mRelativeTo; + + int8_t mIndices[MetaDataIdCount]; + std::vector mValues; static std::vector mMetaDataDecls; diff --git a/es-app/src/SystemData.cpp b/es-app/src/SystemData.cpp index c175a6fe5..16b67f077 100644 --- a/es-app/src/SystemData.cpp +++ b/es-app/src/SystemData.cpp @@ -110,9 +110,14 @@ SystemData::SystemData(const SystemMetadata& meta, SystemEnvironmentData* envDat } if (!Settings::IgnoreGamelist()) - parseGamelist(this, fileMap); + { + if (!mHidden && Settings::PackGamelists()) + packGamelist(this); + + parseGamelist(this, fileMap); + } - if (Settings::RemoveMultiDiskContent()) + if (Settings::RemoveMultiDiskContent() || Settings::BuildMultiDiskContentCache()) removeMultiDiskContent(fileMap); } else @@ -820,8 +825,16 @@ bool SystemData::loadConfig(Window* window) } pugi::xml_document doc; - pugi::xml_parse_result res = doc.load_file(WINSTRINGW(path).c_str()); + auto buffer = Utils::FileSystem::readAllBytes(path); + if (!buffer.size()) + { + LOG(LogError) << "Could not open es_systems.cfg file!"; + return false; + } + + // pugi::xml_parse_result res = doc.load_file(WINSTRINGW(path).c_str()); + pugi::xml_parse_result res = doc.load_buffer_inplace(buffer.data(), buffer.size(), pugi::parse_default); if (!res) { LOG(LogError) << "Could not parse es_systems.cfg file!"; diff --git a/es-app/src/main.cpp b/es-app/src/main.cpp index 3c9dcb4a4..2ff2fa5ab 100644 --- a/es-app/src/main.cpp +++ b/es-app/src/main.cpp @@ -43,6 +43,7 @@ #include "HttpReq.h" #include #include "ZaparooSupport.h" +#include "utils/ThreadPool.h" #ifdef WIN32 #include @@ -444,11 +445,11 @@ void launchStartupGame() } } -#include "utils/MathExpr.h" +// #include "utils/MathExpr.h" int main(int argc, char* argv[]) { - Utils::MathExpr::performUnitTests(); + // Utils::MathExpr::performUnitTests(); // signal(SIGABRT, signalHandler); signal(SIGFPE, signalHandler); @@ -530,55 +531,54 @@ int main(int argc, char* argv[]) } #endif - Scripting::fireEvent("start"); - - // metadata init - HttpReq::resetCookies(); - Genres::init(); - MetaDataList::initMetadata(); - Zaparoo::checkZaparooEnabledAsync(); + // Threaded initializations + auto threadPool = new Utils::ThreadPool(); + auto vlcInit = threadPool->queueWorkItem([] { VideoVlcComponent::init(); }); + threadPool->queueWorkItem([] { ApiSystem::getInstance()->getIpAddress(); }); + threadPool->queueWorkItem([] { MetaDataList::initMetadata(); }); + threadPool->queueWorkItem([] { MameNames::init(); }); + threadPool->queueWorkItem([] { Genres::init(); }); + threadPool->queueWorkItem([] { HttpReq::resetCookies(); }); + threadPool->start(); Window window; - SystemScreenSaver screensaver(&window); ViewController::init(&window); - CollectionSystemManager::init(&window); - - window.setReloadGamelistsCallback([&window] { - ViewController::reloadAllGames(&window, true, true); - }); - - VideoVlcComponent::init(); + window.setReloadGamelistsCallback([&window] { ViewController::reloadAllGames(&window, true, true); }); window.pushGui(ViewController::get()); - if(!window.init(true, false)) + if (!window.init(true, false)) { LOG(LogError) << "Window failed to initialize!"; return 1; } Renderer::setWindowResizable(false); - PowerSaver::init(); bool splashScreen = Settings::getInstance()->getBool("SplashScreen"); bool splashScreenProgress = Settings::getInstance()->getBool("SplashScreenProgress"); if (splashScreen) - { - std::string progressText = _("Loading..."); - if (splashScreenProgress) - progressText = _("Loading system config..."); + window.renderSplashScreen(splashScreenProgress ? _("Loading system config...") : _("Loading...")); - window.renderSplashScreen(progressText); - } + Scripting::fireEvent("start"); - MameNames::init(); + SystemScreenSaver screensaver(&window); + CollectionSystemManager::init(&window); + + Zaparoo::checkZaparooEnabledAsync(); + PowerSaver::init(); + InputConfig::AssignActionButtons(); + if (ApiSystem::getInstance()->isScriptingSupported(ApiSystem::PDFEXTRACTION)) + TextureData::PdfHandler = ApiSystem::getInstance(); + + threadPool->waitAllExcept(vlcInit); // Wait for what's necessary for loadSystemConfigFile const char* errorMsg = NULL; - if(!loadSystemConfigFile(splashScreen && splashScreenProgress ? &window : nullptr, &errorMsg)) + if (!loadSystemConfigFile(splashScreen && splashScreenProgress ? &window : nullptr, &errorMsg)) { // something went terribly wrong - if(errorMsg == NULL) + if (errorMsg == NULL) { LOG(LogError) << "Unknown error occured while parsing system config file."; Renderer::deinit(); @@ -607,17 +607,11 @@ int main(int argc, char* argv[]) } #endif - if (ApiSystem::getInstance()->isScriptingSupported(ApiSystem::PDFEXTRACTION)) - TextureData::PdfHandler = ApiSystem::getInstance(); - - ApiSystem::getInstance()->getIpAddress(); - // preload what we can right away instead of waiting for the user to select it // this makes for no delays when accessing content, but a longer startup time ViewController::get()->preload(); // Initialize input - InputConfig::AssignActionButtons(); InputManager::getInstance()->init(); SDL_StopTextInput(); @@ -635,6 +629,9 @@ int main(int argc, char* argv[]) ViewController::get()->goToStart(true); } + threadPool->wait(); + delete threadPool; + window.closeSplashScreen(); // Check if the device serial number is the same as stored in system.cfg. diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index 8f86fbd41..df1cf5881 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -711,14 +711,14 @@ ThemeData::ThemeData(bool temporary) ThemeFileCache* ThemeFileCache::_instance; -pugi::xml_document& ThemeFileCache::getXmlDocument(const std::string& path) +std::string& ThemeFileCache::getXmlDocument(const std::string& path) { std::unique_lock lock(_lock); auto it = _cache.find(path); if (it != _cache.cend()) - return *it->second; - + return it->second; + /* pugi::xml_document* doc = new pugi::xml_document(); pugi::xml_parse_result res = doc->load_file(WINSTRINGW(path).c_str()); if (!res) @@ -726,17 +726,18 @@ pugi::xml_document& ThemeFileCache::getXmlDocument(const std::string& path) ThemeException error; throw error << "XML parsing error: \n " << res.description(); } - - _cache[path] = doc; - return *doc; + */ + _cache[path] = Utils::FileSystem::readAllText(path); + std::string& buffer = _cache[path]; + return buffer; } void ThemeFileCache::clear() { std::unique_lock lock(_lock); - for (auto& item : _cache) - delete item.second; +// for (auto& item : _cache) +// delete item.second; _cache.clear(); } @@ -805,8 +806,8 @@ void ThemeData::loadFile(const std::string& system, const std::map properties; + std::unordered_map properties; template::value, int>::type = 0> const T get(const std::string& prop) const @@ -585,11 +585,11 @@ public: } public: - pugi::xml_document& getXmlDocument(const std::string& path); + std::string& getXmlDocument(const std::string& path); void clear(); private: - std::map _cache; + std::unordered_map _cache; std::mutex _lock; static ThemeFileCache* _instance; diff --git a/es-core/src/ThemeVariables.cpp b/es-core/src/ThemeVariables.cpp index 2686d48f3..61f3e1498 100644 --- a/es-core/src/ThemeVariables.cpp +++ b/es-core/src/ThemeVariables.cpp @@ -4,29 +4,41 @@ std::string ThemeVariables::resolvePlaceholders(const char* in) const { - if (in == nullptr || in[0] == 0) - return in; + if (in == nullptr || in[0] == 0) + return ""; - auto begin = strstr(in, "${"); - if (begin == nullptr) - return in; + std::string result; + result.reserve(strlen(in)); - auto end = strstr(begin, "}"); - if (end == nullptr) - return in; + std::string_view view(in); + size_t pos = 0; - std::string inStr(in); + while (true) + { + size_t start = view.find("${", pos); + if (start == std::string_view::npos) + { + result.append(view.substr(pos)); + break; + } - const size_t variableBegin = begin - in; - const size_t variableEnd = end - in; + result.append(view.substr(pos, start - pos)); - std::string prefix = inStr.substr(0, variableBegin); - std::string replace = inStr.substr(variableBegin + 2, variableEnd - (variableBegin + 2)); - std::string suffix = resolvePlaceholders(end + 1); + size_t end = view.find('}', start + 2); + if (end == std::string_view::npos) + { + result.append(view.substr(start)); + break; + } - auto it = this->find(replace); - if (it != this->cend()) - return prefix + it->second + suffix; - - return prefix + "" + suffix; + std::string_view varName = view.substr(start + 2, end - (start + 2)); + + auto it = this->find(varName); + if (it != this->cend()) + result.append(it->second); + + pos = end + 1; + } + + return result; } diff --git a/es-core/src/ThemeVariables.h b/es-core/src/ThemeVariables.h index 15a18fcf1..123bebbec 100644 --- a/es-core/src/ThemeVariables.h +++ b/es-core/src/ThemeVariables.h @@ -6,7 +6,7 @@ #include #include -class ThemeVariables : public std::map +class ThemeVariables : public std::map> { public: std::string resolvePlaceholders(const char* in) const; diff --git a/es-core/src/utils/FileSystemUtil.cpp b/es-core/src/utils/FileSystemUtil.cpp index e840ad5bb..47a2efd1e 100644 --- a/es-core/src/utils/FileSystemUtil.cpp +++ b/es-core/src/utils/FileSystemUtil.cpp @@ -229,32 +229,31 @@ namespace Utils FINDEX_INFO_LEVELS::FindExInfoBasic, &findData, FINDEX_SEARCH_OPS::FindExSearchNameMatch , NULL, FIND_FIRST_EX_LARGE_FETCH); - if(hFind != INVALID_HANDLE_VALUE) + if (hFind != INVALID_HANDLE_VALUE) { + std::string pathPrefix = path + "/"; + // loop over all files in the directory do { - std::string name = Utils::String::convertFromWideString(findData.cFileName); - - // ignore "." and ".." - if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && name == "." || name == "..") + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && + (findData.cFileName[0] == L'.' && (findData.cFileName[1] == L'\0' || (findData.cFileName[1] == L'.' && findData.cFileName[2] == L'\0')))) continue; - std::string fullName(getGenericPath(path + "/" + name)); + std::string fullName = getGenericPath(pathPrefix + Utils::String::convertFromWideString(findData.cFileName)); FileCache::add(fullName, std::move(FileCache((DWORD)findData.dwFileAttributes))); if (!includeHidden && (findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN) continue; - contentList.push_back(fullName); + contentList.push_back(fullName); if (_recursive && (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) { for (auto item : getDirContent(fullName, true, includeHidden)) contentList.push_back(item); } - } - while(FindNextFileW(hFind, &findData)); + } while (FindNextFileW(hFind, &findData)); FindClose(hFind); } @@ -338,11 +337,11 @@ namespace Utils { if (uDriveMask & 1) { - FileInfo fi; + contentList.emplace_back(); + FileInfo& fi = contentList.back(); fi.path = std::string(1, drive) + ":"; fi.hidden = false; - fi.directory = true; - contentList.push_back(std::move(fi)); + fi.directory = true; } drive++; @@ -352,9 +351,6 @@ namespace Utils return contentList; } - - - WIN32_FIND_DATAW findData; std::string wildcard = path + "/*"; @@ -364,6 +360,8 @@ namespace Utils if (hFind != INVALID_HANDLE_VALUE) { + std::string pathPrefix = path + "/"; + // loop over all files in the directory do { @@ -374,15 +372,14 @@ namespace Utils (findData.cFileName[0] == '.' && (findData.cFileName[1] == '.' || findData.cFileName[1] == 0))) continue; - FileInfo fi; - fi.path = path + "/" + Utils::String::convertFromWideString(findData.cFileName); + contentList.emplace_back(); + FileInfo& fi = contentList.back(); + fi.path = pathPrefix + Utils::String::convertFromWideString(findData.cFileName); fi.hidden = (findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN; fi.directory = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; fi.lastWriteTime = to_time_t(findData.ftLastWriteTime); - FileCache::add(fi.path, std::move(FileCache((DWORD)findData.dwFileAttributes))); - - contentList.push_back(std::move(fi)); + FileCache::add(fi.path, std::move(FileCache((DWORD)findData.dwFileAttributes))); } while (FindNextFileW(hFind, &findData)); @@ -1222,6 +1219,25 @@ namespace Utils file.seekg(0); } + std::vector readAllBytes(const std::string& fileName) + { + std::ifstream file(WINSTRINGW(fileName), std::ios::binary | std::ios::ate); + if (!file.is_open()) + return {}; + + std::streamsize size = file.tellg(); + if (size <= 0) + return {}; + + file.seekg(0, std::ios::beg); + + std::vector buffer(size); + if (!file.read(buffer.data(), size)) + return {}; + + return buffer; + } + std::list readAllLines(const std::string& fileName) { std::list lines; diff --git a/es-core/src/utils/FileSystemUtil.h b/es-core/src/utils/FileSystemUtil.h index 4bf4c1e13..3a8922fdb 100644 --- a/es-core/src/utils/FileSystemUtil.h +++ b/es-core/src/utils/FileSystemUtil.h @@ -66,6 +66,8 @@ namespace Utils std::string readAllText(const std::string& fileName); stringList readAllLines(const std::string& fileName); + std::vector readAllBytes(const std::string& fileName); + void writeAllText(const std::string& fileName, const std::string& text); bool copyFile(const std::string src, const std::string dst); void deleteDirectoryFiles(const std::string path, bool deleteDirectory = false); diff --git a/es-core/src/utils/StringUtil.cpp b/es-core/src/utils/StringUtil.cpp index b906710c0..9c812b481 100644 --- a/es-core/src/utils/StringUtil.cpp +++ b/es-core/src/utils/StringUtil.cpp @@ -413,7 +413,12 @@ namespace Utils if(strBegin == std::string::npos) return ""; - const size_t strEnd = _string.find_last_not_of(" \t\r\n"); + const size_t strEnd = _string.find_last_not_of(" \t\r\n"); + + const size_t len = strEnd - strBegin + 1; + if (strBegin == 0 && len == _string.length()) + return _string; + return _string.substr(strBegin, strEnd - strBegin + 1); } // trim diff --git a/es-core/src/utils/ThreadPool.cpp b/es-core/src/utils/ThreadPool.cpp index 5cf5f0f07..149a35ead 100644 --- a/es-core/src/utils/ThreadPool.cpp +++ b/es-core/src/utils/ThreadPool.cpp @@ -22,6 +22,8 @@ namespace Utils auto doWork = [&](size_t id) { #if WIN32 + SetThreadDescription(GetCurrentThread(), L"ThreadPool::thread"); + auto mask = (static_cast(1) << id); SetThreadAffinityMask(GetCurrentThread(), mask); #endif @@ -31,16 +33,17 @@ namespace Utils _mutex.lock(); if (!mWorkQueue.empty()) { - auto work = mWorkQueue.front(); + auto entry = mWorkQueue.front(); mWorkQueue.pop(); _mutex.unlock(); try { - work(); + entry.fn(); } catch (...) {} - + + entry.item->mDone.store(true); mNumWork--; } else @@ -72,14 +75,19 @@ namespace Utils t.join(); } - void ThreadPool::queueWorkItem(work_function work) + WorkItemPtr ThreadPool::queueWorkItem(work_function work) { + auto item = std::make_shared(); + _mutex.lock(); - mWorkQueue.push(work); + mWorkQueue.push({ work, item }); + mAllItems.push_back(item); mNumWork++; _mutex.unlock(); - } + return item; + } + void ThreadPool::wait() { if (!mRunning) @@ -88,6 +96,10 @@ namespace Utils mWaiting = true; while (mNumWork.load() > 0) std::this_thread::yield(); + + _mutex.lock(); + mAllItems.clear(); + _mutex.unlock(); } void ThreadPool::wait(work_function work, int delay) @@ -103,9 +115,81 @@ namespace Utils std::this_thread::yield(); std::this_thread::sleep_for(std::chrono::milliseconds(delay)); + } + + _mutex.lock(); + mAllItems.clear(); + _mutex.unlock(); + + } + + void WorkItem::wait() const + { + while (!mDone.load()) + { + std::this_thread::yield(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } + void ThreadPool::cleanupDoneItems() + { + _mutex.lock(); + + mAllItems.erase( + std::remove_if(mAllItems.begin(), mAllItems.end(), [](const std::shared_ptr& item) { return item->isDone(); }), + mAllItems.end()); + + _mutex.unlock(); + } + + void ThreadPool::waitAll(std::initializer_list items) + { + if (!mRunning) + start(); + + for (const auto& item : items) + item->wait(); + + cleanupDoneItems(); + } + + void ThreadPool::waitAllExcept(WorkItemPtr excluded) + { + waitAllExcept({ excluded }); + } + + void ThreadPool::waitAllExcept(std::initializer_list excluded) + { + if (!mRunning) + start(); + + std::vector toWait; + + _mutex.lock(); + for (auto& item : mAllItems) + { + bool isExcluded = false; + for (const auto& ex : excluded) + { + if (ex == item) + { + isExcluded = true; + break; + } + } + + if (!isExcluded) + toWait.push_back(item); + } + _mutex.unlock(); + + for (const auto& item : toWait) + item->wait(); + + cleanupDoneItems(); + } + void ThreadPool::stop() { _mutex.lock(); diff --git a/es-core/src/utils/ThreadPool.h b/es-core/src/utils/ThreadPool.h index 042f501e6..48fb2970b 100644 --- a/es-core/src/utils/ThreadPool.h +++ b/es-core/src/utils/ThreadPool.h @@ -9,6 +9,21 @@ namespace Utils { + class WorkItem + { + public: + WorkItem() : mDone(false) {} + + bool isDone() const { return mDone.load(); } + void wait() const; + + private: + friend class ThreadPool; + std::atomic mDone; + }; + + using WorkItemPtr = std::shared_ptr; + class ThreadPool { public: @@ -18,9 +33,16 @@ namespace Utils ~ThreadPool(); void start(); - void queueWorkItem(work_function work); + WorkItemPtr queueWorkItem(work_function work); + void wait(); void wait(work_function work, int delay = 50); + + void waitAll(std::initializer_list items); + + void waitAllExcept(WorkItemPtr excluded); + void waitAllExcept(std::initializer_list excluded); + void cancel() { mRunning = false; } void stop(); @@ -29,12 +51,21 @@ namespace Utils private: bool mRunning; bool mWaiting; - std::queue mWorkQueue; + + struct WorkEntry + { + work_function fn; + WorkItemPtr item; + }; + + std::queue mWorkQueue; std::atomic mNumWork; std::mutex _mutex; std::vector mThreads; int mThreadByCore; + std::vector> mAllItems; + void cleanupDoneItems(); }; }