From 90d0b54a69e1901f5070b2402f81ec77503e760c Mon Sep 17 00:00:00 2001 From: MihailRis Date: Thu, 11 Apr 2024 15:48:27 +0300 Subject: [PATCH] thread pool update + refactor --- src/assets/AssetsLoader.cpp | 73 +++++++++++++++----- src/assets/AssetsLoader.h | 8 ++- src/assets/assetload_funcs.cpp | 30 +++----- src/assets/assetload_funcs.h | 18 ++--- src/coders/imageio.cpp | 49 ++++++++++++++ src/coders/imageio.h | 19 ++++++ src/coders/png.cpp | 6 +- src/coders/png.h | 6 +- src/engine.cpp | 40 ++++++----- src/engine.h | 2 + src/files/WorldConverter.cpp | 6 ++ src/files/WorldConverter.h | 5 ++ src/frontend/menu/menu.cpp | 2 + src/frontend/menu/menu_pause.cpp | 13 ++-- src/graphics/render/ChunksRenderer.cpp | 1 + src/interfaces/Task.h | 2 + src/util/ThreadPool.h | 94 ++++++++++++++++++++------ 17 files changed, 271 insertions(+), 103 deletions(-) create mode 100644 src/coders/imageio.cpp create mode 100644 src/coders/imageio.h diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index c34f554b..e1b550c5 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -6,6 +6,8 @@ #include #include +#include "../util/ThreadPool.h" + #include "../constants.h" #include "../data/dynamic.h" #include "../debug/Logger.h" @@ -21,37 +23,42 @@ static debug::Logger logger("assets-loader"); AssetsLoader::AssetsLoader(Assets* assets, const ResPaths* paths) : assets(assets), paths(paths) { - addLoader(AssetType::shader, assetload::shader); - addLoader(AssetType::texture, assetload::texture); - addLoader(AssetType::font, assetload::font); - addLoader(AssetType::atlas, assetload::atlas); + addLoader(AssetType::shader, assetload::shader); + addLoader(AssetType::texture, assetload::texture); + addLoader(AssetType::font, assetload::font); + addLoader(AssetType::atlas, assetload::atlas); addLoader(AssetType::layout, assetload::layout); addLoader(AssetType::sound, assetload::sound); } void AssetsLoader::addLoader(AssetType tag, aloader_func func) { - loaders[tag] = func; + loaders[tag] = func; } void AssetsLoader::add(AssetType tag, const std::string filename, const std::string alias, std::shared_ptr settings) { - entries.push(aloader_entry{tag, filename, alias, settings}); + entries.push(aloader_entry{tag, filename, alias, settings}); } bool AssetsLoader::hasNext() const { - return !entries.empty(); + return !entries.empty(); +} + +aloader_func AssetsLoader::getLoader(AssetType tag) { + auto found = loaders.find(tag); + if (found == loaders.end()) { + throw std::runtime_error( + "unknown asset tag "+std::to_string(static_cast(tag)) + ); + } + return found->second; } bool AssetsLoader::loadNext() { - const aloader_entry& entry = entries.front(); - logger.info() << "loading " << entry.filename << " as " << entry.alias; - auto found = loaders.find(entry.tag); - if (found == loaders.end()) { - logger.error() << "unknown asset tag " << static_cast(entry.tag); - return false; - } - aloader_func loader = found->second; + const aloader_entry& entry = entries.front(); + logger.info() << "loading " << entry.filename << " as " << entry.alias; try { - auto postfunc = loader(*this, assets, paths, entry.filename, entry.alias, entry.config); + aloader_func loader = getLoader(entry.tag); + auto postfunc = loader(this, paths, entry.filename, entry.alias, entry.config); postfunc(assets); entries.pop(); return true; @@ -198,5 +205,37 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) { } const ResPaths* AssetsLoader::getPaths() const { - return paths; + return paths; +} + +class LoaderWorker : public util::Worker, assetload::postfunc> { + AssetsLoader* loader; +public: + LoaderWorker(AssetsLoader* loader) : loader(loader) { + } + + assetload::postfunc operator()(const std::shared_ptr& entry) override { + aloader_func loadfunc = loader->getLoader(entry->tag); + return loadfunc(loader, loader->getPaths(), entry->filename, entry->alias, entry->config); + } +}; + +std::shared_ptr AssetsLoader::startTask(runnable onDone) { + auto pool = std::make_shared< + util::ThreadPool, assetload::postfunc> + >( + "assets-loader-pool", + [=](){return std::make_shared(this);}, + [=](assetload::postfunc& func) { + func(assets); + } + ); + pool->setOnComplete(onDone); + while (!entries.empty()) { + const aloader_entry& entry = entries.front(); + auto ptr = std::make_shared(entry); + pool->enqueueJob(ptr); + entries.pop(); + } + return pool; } diff --git a/src/assets/AssetsLoader.h b/src/assets/AssetsLoader.h index 39dea158..e03b5871 100644 --- a/src/assets/AssetsLoader.h +++ b/src/assets/AssetsLoader.h @@ -2,6 +2,8 @@ #define ASSETS_ASSETS_LOADER_H #include "Assets.h" +#include "../interfaces/Task.h" +#include "../delegates.h" #include #include @@ -45,8 +47,7 @@ struct SoundCfg : AssetCfg { }; using aloader_func = std::function startTask(runnable onDone); + const ResPaths* getPaths() const; + aloader_func getLoader(AssetType tag); }; #endif // ASSETS_ASSETS_LOADER_H diff --git a/src/assets/assetload_funcs.cpp b/src/assets/assetload_funcs.cpp index d57d8b9a..fbb2bf7b 100644 --- a/src/assets/assetload_funcs.cpp +++ b/src/assets/assetload_funcs.cpp @@ -8,7 +8,7 @@ #include "../audio/audio.h" #include "../files/files.h" #include "../files/engine_paths.h" -#include "../coders/png.h" +#include "../coders/imageio.h" #include "../coders/json.h" #include "../coders/GLSLExtension.h" #include "../graphics/core/Shader.h" @@ -30,15 +30,14 @@ static bool animation( ); assetload::postfunc assetload::texture( - AssetsLoader&, - Assets* assets, + AssetsLoader*, const ResPaths* paths, const std::string filename, const std::string name, std::shared_ptr ) { std::shared_ptr image ( - png::load_image(paths->find(filename+".png").u8string()) + imageio::read(paths->find(filename+".png").u8string()).release() ); return [name, image](auto assets) { assets->store(Texture::from(image.get()), name); @@ -46,8 +45,7 @@ assetload::postfunc assetload::texture( } assetload::postfunc assetload::shader( - AssetsLoader&, - Assets* assets, + AssetsLoader*, const ResPaths* paths, const std::string filename, const std::string name, @@ -72,15 +70,12 @@ assetload::postfunc assetload::shader( } static bool appendAtlas(AtlasBuilder& atlas, const fs::path& file) { - // png is only supported format - if (file.extension() != ".png") - return false; std::string name = file.stem().string(); // skip duplicates if (atlas.has(name)) { return false; } - std::unique_ptr image(png::load_image(file.string())); + auto image = imageio::read(file.string()); image->fixAlphaColor(); atlas.add(name, image.release()); @@ -88,8 +83,7 @@ static bool appendAtlas(AtlasBuilder& atlas, const fs::path& file) { } assetload::postfunc assetload::atlas( - AssetsLoader&, - Assets* assets, + AssetsLoader*, const ResPaths* paths, const std::string directory, const std::string name, @@ -114,8 +108,7 @@ assetload::postfunc assetload::atlas( } assetload::postfunc assetload::font( - AssetsLoader&, - Assets* assets, + AssetsLoader*, const ResPaths* paths, const std::string filename, const std::string name, @@ -125,8 +118,7 @@ assetload::postfunc assetload::font( for (size_t i = 0; i <= 4; i++) { std::string name = filename + "_" + std::to_string(i) + ".png"; name = paths->find(name).string(); - std::unique_ptr image (png::load_image(name)); - pages->push_back(std::move(image)); + pages->push_back(std::move(imageio::read(name))); } return [=](auto assets) { int res = pages->at(0)->getHeight() / 16; @@ -139,8 +131,7 @@ assetload::postfunc assetload::font( } assetload::postfunc assetload::layout( - AssetsLoader& loader, - Assets* assets, + AssetsLoader*, const ResPaths* paths, const std::string file, const std::string name, @@ -159,8 +150,7 @@ assetload::postfunc assetload::layout( }; } assetload::postfunc assetload::sound( - AssetsLoader& loader, - Assets* assets, + AssetsLoader*, const ResPaths* paths, const std::string file, const std::string name, diff --git a/src/assets/assetload_funcs.h b/src/assets/assetload_funcs.h index abbb8196..8bcaf728 100644 --- a/src/assets/assetload_funcs.h +++ b/src/assets/assetload_funcs.h @@ -15,40 +15,35 @@ struct AssetCfg; /// @brief see AssetsLoader.h: aloader_func namespace assetload { postfunc texture( - AssetsLoader&, - Assets*, + AssetsLoader*, const ResPaths* paths, const std::string filename, const std::string name, std::shared_ptr settings ); postfunc shader( - AssetsLoader&, - Assets*, + AssetsLoader*, const ResPaths* paths, const std::string filename, const std::string name, std::shared_ptr settings ); postfunc atlas( - AssetsLoader&, - Assets*, + AssetsLoader*, const ResPaths* paths, const std::string directory, const std::string name, std::shared_ptr settings ); postfunc font( - AssetsLoader&, - Assets*, + AssetsLoader*, const ResPaths* paths, const std::string filename, const std::string name, std::shared_ptr settings ); postfunc layout( - AssetsLoader&, - Assets*, + AssetsLoader*, const ResPaths* paths, const std::string file, const std::string name, @@ -56,8 +51,7 @@ namespace assetload { ); postfunc sound( - AssetsLoader&, - Assets*, + AssetsLoader*, const ResPaths* paths, const std::string file, const std::string name, diff --git a/src/coders/imageio.cpp b/src/coders/imageio.cpp new file mode 100644 index 00000000..511abd23 --- /dev/null +++ b/src/coders/imageio.cpp @@ -0,0 +1,49 @@ +#include "imageio.h" + +#include "png.h" +#include "../graphics/core/ImageData.h" + +#include +#include +#include + +namespace fs = std::filesystem; + +using image_reader = std::function; +using image_writer = std::function; + +static std::unordered_map readers { + {".png", png::load_image}, +}; + +static std::unordered_map writers { + {".png", png::write_image}, +}; + +bool imageio::is_read_supported(const std::string& extension) { + return readers.find(extension) != readers.end(); +} + +bool imageio::is_write_supported(const std::string& extension) { + return writers.find(extension) != writers.end(); +} + +inline std::string extensionOf(const std::string& filename) { + return fs::u8path(filename).extension().u8string(); +} + +std::unique_ptr imageio::read(const std::string& filename) { + auto found = readers.find(extensionOf(filename)); + if (found == readers.end()) { + throw std::runtime_error("file format is not supported (read): "+filename); + } + return std::unique_ptr(found->second(filename)); +} + +void imageio::write(const std::string& filename, const ImageData* image) { + auto found = writers.find(extensionOf(filename)); + if (found == writers.end()) { + throw std::runtime_error("file format is not supported (write): "+filename); + } + return found->second(filename, image); +} diff --git a/src/coders/imageio.h b/src/coders/imageio.h new file mode 100644 index 00000000..afe75d4a --- /dev/null +++ b/src/coders/imageio.h @@ -0,0 +1,19 @@ +#ifndef CODERS_IMAGEIO_H_ +#define CODERS_IMAGEIO_H_ + +#include +#include + +class ImageData; + +namespace imageio { + inline const std::string PNG = ".png"; + + bool is_read_supported(const std::string& extension); + bool is_write_supported(const std::string& extension); + + std::unique_ptr read(const std::string& filename); + void write(const std::string& filename, const ImageData* image); +} + +#endif // CODERS_IMAGEIO_H_ diff --git a/src/coders/png.cpp b/src/coders/png.cpp index 349f4f26..6736afe0 100644 --- a/src/coders/png.cpp +++ b/src/coders/png.cpp @@ -340,7 +340,7 @@ ImageData* _png_load(const char* file){ } #endif -ImageData* png::load_image(std::string filename) { +ImageData* png::load_image(const std::string& filename) { ImageData* image (_png_load(filename.c_str())); if (image == nullptr) { throw std::runtime_error("could not load image "+filename); @@ -348,13 +348,13 @@ ImageData* png::load_image(std::string filename) { return image; } -Texture* png::load_texture(std::string filename) { +Texture* png::load_texture(const std::string& filename) { std::unique_ptr image (load_image(filename)); auto texture = Texture::from(image.get()); texture->setNearestFilter(); return texture; } -void png::write_image(std::string filename, const ImageData* image) { +void png::write_image(const std::string& filename, const ImageData* image) { _png_write(filename.c_str(), image->getWidth(), image->getHeight(), (const ubyte*)image->getData(), image->getFormat() == ImageFormat::rgba8888); } diff --git a/src/coders/png.h b/src/coders/png.h index f824a3c7..07680d32 100644 --- a/src/coders/png.h +++ b/src/coders/png.h @@ -8,9 +8,9 @@ class Texture; class ImageData; namespace png { - extern ImageData* load_image(std::string filename); - extern void write_image(std::string filename, const ImageData* image); - extern Texture* load_texture(std::string filename); + extern ImageData* load_image(const std::string& filename); + extern void write_image(const std::string& filename, const ImageData* image); + extern Texture* load_texture(const std::string& filename); } #endif /* CODERS_PNG_H_ */ diff --git a/src/engine.cpp b/src/engine.cpp index cbf12069..d5903f8c 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -6,7 +6,7 @@ #include "audio/audio.h" #include "coders/GLSLExtension.h" #include "coders/json.h" -#include "coders/png.h" +#include "coders/imageio.h" #include "content/ContentLoader.h" #include "core_defs.h" #include "files/files.h" @@ -70,7 +70,6 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths) auto resdir = paths->getResources(); - logger.info() << "loading assets"; std::vector roots {resdir}; resPaths = std::make_unique(resdir, roots); try { @@ -115,17 +114,21 @@ void Engine::updateTimers() { void Engine::updateHotkeys() { if (Events::jpressed(keycode::F2)) { - std::unique_ptr image(Window::takeScreenshot()); - image->flipY(); - fs::path filename = paths->getScreenshotFile("png"); - png::write_image(filename.string(), image.get()); - std::cout << "saved screenshot as " << filename << std::endl; + saveScreenshot(); } if (Events::jpressed(keycode::F11)) { Window::toggleFullscreen(); } } +void Engine::saveScreenshot() { + std::unique_ptr image(Window::takeScreenshot()); + image->flipY(); + fs::path filename = paths->getScreenshotFile("png"); + imageio::write(filename.string(), image.get()); + logger.info() << "saved screenshot as "+filename.u8string(); +} + void Engine::mainloop() { logger.info() << "starting menu screen"; setScreen(std::make_shared(this)); @@ -203,10 +206,17 @@ void Engine::loadAssets() { auto new_assets = std::make_unique(); AssetsLoader loader(new_assets.get(), resPaths.get()); AssetsLoader::addDefaults(loader, content.get()); - while (loader.hasNext()) { - if (!loader.loadNext()) { - new_assets.reset(); - throw std::runtime_error("could not to load assets"); + + bool threading = false; + if (threading) { + auto task = loader.startTask([=](){}); + task->waitForEnd(); + } else { + while (loader.hasNext()) { + if (!loader.loadNext()) { + new_assets.reset(); + throw std::runtime_error("could not to load assets"); + } } } if (assets) { @@ -216,7 +226,6 @@ void Engine::loadAssets() { } } -// TODO: refactor this void Engine::loadContent() { auto resdir = paths->getResources(); ContentBuilder contentBuilder; @@ -263,12 +272,7 @@ void Engine::loadWorldContent(const fs::path& folder) { } void Engine::loadAllPacks() { - PacksManager manager; - manager.setSources({ - paths->getWorldFolder()/fs::path("content"), - paths->getUserfiles()/fs::path("content"), - paths->getResources()/fs::path("content") - }); + PacksManager manager = createPacksManager(paths->getWorldFolder()); manager.scan(); auto allnames = manager.getAllNames(); contentPacks = manager.getAll(manager.assembly(allnames)); diff --git a/src/engine.h b/src/engine.h index 400b081d..84eb7715 100644 --- a/src/engine.h +++ b/src/engine.h @@ -123,6 +123,8 @@ public: /// @brief Enqueue function call to the end of current frame in draw thread void postRunnable(runnable callback); + void saveScreenshot(); + PacksManager createPacksManager(const fs::path& worldFolder); SettingsHandler& getSettingsHandler(); diff --git a/src/files/WorldConverter.cpp b/src/files/WorldConverter.cpp index d64953aa..0ad8e666 100644 --- a/src/files/WorldConverter.cpp +++ b/src/files/WorldConverter.cpp @@ -92,6 +92,12 @@ void WorldConverter::write() { wfile->write(nullptr, content); } +void WorldConverter::waitForEnd() { + while (isActive()) { + update(); + } +} + uint WorldConverter::getWorkRemaining() const { return tasks.size(); } diff --git a/src/files/WorldConverter.h b/src/files/WorldConverter.h index feabda2f..8cdd127f 100644 --- a/src/files/WorldConverter.h +++ b/src/files/WorldConverter.h @@ -58,6 +58,11 @@ public: tasks = {}; } + bool isActive() const override { + return !tasks.empty(); + } + + void waitForEnd() override; void write(); uint getWorkRemaining() const override; diff --git a/src/frontend/menu/menu.cpp b/src/frontend/menu/menu.cpp index 72fe1c76..2fd0eec0 100644 --- a/src/frontend/menu/menu.cpp +++ b/src/frontend/menu/menu.cpp @@ -201,6 +201,7 @@ void menus::open_world(std::string name, Engine* engine, bool confirmConvert) { try { engine->loadWorldContent(folder); } catch (const contentpack_error& error) { + engine->setScreen(std::make_shared(engine)); // could not to find or read pack guiutil::alert( engine->getGUI(), langs::get(L"error.pack-not-found")+L": "+ @@ -208,6 +209,7 @@ void menus::open_world(std::string name, Engine* engine, bool confirmConvert) { ); return; } catch (const std::runtime_error& error) { + engine->setScreen(std::make_shared(engine)); guiutil::alert( engine->getGUI(), langs::get(L"Content Error", L"menu")+L": "+ util::str2wstr_utf8(error.what()) diff --git a/src/frontend/menu/menu_pause.cpp b/src/frontend/menu/menu_pause.cpp index 06175ee5..93a2f95b 100644 --- a/src/frontend/menu/menu_pause.cpp +++ b/src/frontend/menu/menu_pause.cpp @@ -1,11 +1,12 @@ #include "menu.h" #include "menu_commons.h" -#include "../../coders/png.h" +#include "../../coders/imageio.h" #include "../../content/PacksManager.h" #include "../../content/ContentLUT.h" #include "../../engine.h" #include "../../files/WorldFiles.h" +#include "../../graphics/core/Texture.h" #include "../../graphics/ui/gui_util.h" #include "../../logic/LevelController.h" #include "../../util/stringutil.h" @@ -52,7 +53,8 @@ std::shared_ptr create_pack_panel( if (assets->getTexture(icon) == nullptr) { auto iconfile = pack.folder/fs::path("icon.png"); if (fs::is_regular_file(iconfile)) { - assets->store(png::load_texture(iconfile.string()), icon); + auto image = imageio::read(iconfile.string()); + assets->store(Texture::from(image.get()), icon); } else { icon = "gui/no_icon"; } @@ -183,12 +185,7 @@ void create_content_panel(Engine* engine, LevelController* controller) { auto mainPanel = menus::create_page(engine, "content", 550, 0.0f, 5); auto paths = engine->getPaths(); - PacksManager manager; - manager.setSources({ - paths->getWorldFolder()/fs::path("content"), - paths->getUserfiles()/fs::path("content"), - paths->getResources()/fs::path("content") - }); + PacksManager manager = engine->createPacksManager(paths->getWorldFolder()); manager.scan(); std::vector scanned = manager.getAll(manager.getAllNames()); diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp index de272a36..8b20414c 100644 --- a/src/graphics/render/ChunksRenderer.cpp +++ b/src/graphics/render/ChunksRenderer.cpp @@ -46,6 +46,7 @@ ChunksRenderer::ChunksRenderer( }) { threadPool.setStandaloneResults(false); + threadPool.setStopOnFail(false); renderer = std::make_unique( RENDERER_CAPACITY, level->content, cache, settings ); diff --git a/src/interfaces/Task.h b/src/interfaces/Task.h index 318dbf68..0b3a2597 100644 --- a/src/interfaces/Task.h +++ b/src/interfaces/Task.h @@ -9,9 +9,11 @@ class Task { public: virtual ~Task() {} + virtual bool isActive() const = 0; virtual uint getWorkRemaining() const = 0; virtual uint getWorkDone() const = 0; virtual void update() = 0; + virtual void waitForEnd() = 0; virtual void terminate() = 0; }; diff --git a/src/util/ThreadPool.h b/src/util/ThreadPool.h index 4f5bed77..e992f139 100644 --- a/src/util/ThreadPool.h +++ b/src/util/ThreadPool.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -14,8 +15,9 @@ namespace util { -template +template struct ThreadPoolResult { + J job; std::condition_variable& variable; int workerIndex; bool& locked; @@ -34,7 +36,7 @@ template class ThreadPool : public Task { debug::Logger logger; std::queue jobs; - std::queue> results; + std::queue> results; std::mutex resultsMutex; std::vector threads; std::condition_variable jobsMutexCondition; @@ -46,7 +48,9 @@ class ThreadPool : public Task { std::atomic busyWorkers = 0; std::atomic jobsDone = 0; bool working = true; + bool failed = false; bool standaloneResults = true; + bool stopOnFail = true; void threadLoop(int index, std::shared_ptr> worker) { std::condition_variable variable; @@ -59,7 +63,7 @@ class ThreadPool : public Task { jobsMutexCondition.wait(lock, [this] { return !jobs.empty() || !working; }); - if (!working) { + if (!working || failed) { break; } job = jobs.front(); @@ -71,7 +75,7 @@ class ThreadPool : public Task { R result = (*worker)(job); { std::lock_guard lock(resultsMutex); - results.push(ThreadPoolResult {variable, index, locked, result}); + results.push(ThreadPoolResult {job, variable, index, locked, result}); if (!standaloneResults) { locked = true; } @@ -88,6 +92,10 @@ class ThreadPool : public Task { if (onJobFailed) { onJobFailed(job); } + if (stopOnFail) { + std::lock_guard lock(jobsMutex); + failed = true; + } logger.error() << "uncaught exception: " << err.what(); } jobsDone++; @@ -109,6 +117,10 @@ public: terminate(); } + bool isActive() const override { + return working; + } + void terminate() override { if (!working) { return; @@ -120,7 +132,7 @@ public: { std::lock_guard lock(resultsMutex); while (!results.empty()) { - ThreadPoolResult entry = results.front(); + ThreadPoolResult entry = results.front(); results.pop(); if (!standaloneResults) { entry.locked = false; @@ -136,24 +148,54 @@ public: } void update() override { - std::lock_guard lock(resultsMutex); - while (!results.empty()) { - ThreadPoolResult entry = results.front(); - results.pop(); - - resultConsumer(entry.entry); - - if (!standaloneResults) { - entry.locked = false; - entry.variable.notify_all(); - } + if (!working) { + return; + } + if (failed) { + throw std::runtime_error("some job failed"); } - if (onComplete && busyWorkers == 0) { - std::lock_guard lock(jobsMutex); - if (jobs.empty()) { - onComplete(); + bool complete = false; + { + std::lock_guard lock(resultsMutex); + while (!results.empty()) { + ThreadPoolResult entry = results.front(); + results.pop(); + + try { + resultConsumer(entry.entry); + } catch (std::exception& err) { + logger.error() << err.what(); + if (onJobFailed) { + onJobFailed(entry.job); + } + if (stopOnFail) { + std::lock_guard lock(jobsMutex); + failed = true; + complete = false; + } + break; + } + + if (!standaloneResults) { + entry.locked = false; + entry.variable.notify_all(); + } } + + if (onComplete && busyWorkers == 0) { + std::lock_guard lock(jobsMutex); + if (jobs.empty()) { + onComplete(); + complete = true; + } + } + } + if (failed) { + throw std::runtime_error("some job failed"); + } + if (complete) { + terminate(); } } @@ -170,6 +212,10 @@ public: standaloneResults = flag; } + void setStopOnFail(bool flag) { + stopOnFail = flag; + } + /// @brief onJobFailed called on exception thrown in worker thread. /// Use engine.postRunnable when calling terminate() void setOnJobFailed(consumer callback) { @@ -189,6 +235,14 @@ public: uint getWorkDone() const override { return jobsDone; } + + virtual void waitForEnd() override { + using namespace std::chrono_literals; + while (working) { + std::this_thread::sleep_for(2ms); + update(); + } + } }; } // namespace util