/* * ThreadPool.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #pragma once #include "BlockingQueue.h" #include #include VCMI_LIB_NAMESPACE_BEGIN typedef std::function TRMGfunction ; typedef std::optional TRMGJob; //Credit to https://github.com/Liam0205/toy-threadpool/tree/master/yuuki class DLL_LINKAGE ThreadPool { private: using Lock = boost::unique_lock; mutable boost::shared_mutex mx; mutable boost::condition_variable_any cv; mutable boost::once_flag once; bool isInitialized = false; bool stopping = false; bool canceling = false; public: ThreadPool(); ~ThreadPool(); void init(size_t numThreads); void spawn(); void terminate(); void cancel(); public: bool initialized() const; bool running() const; int size() const; private: bool isRunning() const; public: auto async(std::function&& f) const -> boost::future; private: std::vector workers; mutable BlockingQueue tasks; }; ThreadPool::ThreadPool() : once(BOOST_ONCE_INIT) {}; ThreadPool::~ThreadPool() { terminate(); } inline void ThreadPool::init(size_t numThreads) { boost::call_once(once, [this, numThreads]() { Lock lock(mx); stopping = false; canceling = false; workers.reserve(numThreads); for (size_t i = 0; i < numThreads; ++i) { workers.emplace_back(std::bind(&ThreadPool::spawn, this)); } isInitialized = true; }); } bool ThreadPool::isRunning() const { return isInitialized && !stopping && !canceling; } inline bool ThreadPool::initialized() const { Lock lock(mx); return isInitialized; } inline bool ThreadPool::running() const { Lock lock(mx); return isRunning(); } inline int ThreadPool::size() const { Lock lock(mx); return workers.size(); } inline void ThreadPool::spawn() { while(true) { bool pop = false; TRMGfunction task; { Lock lock(mx); cv.wait(lock, [this, &pop, &task] { pop = tasks.pop(task); return canceling || stopping || pop; }); } if (canceling || (stopping && !pop)) { return; } task(); } } inline void ThreadPool::terminate() { { Lock lock(mx); if (isRunning()) { stopping = true; } else { return; } } cv.notify_all(); for (auto& worker : workers) { worker.join(); } } inline void ThreadPool::cancel() { { Lock lock(mx); if (running()) { canceling = true; } else { return; } } tasks.clear(); cv.notify_all(); for (auto& worker : workers) { worker.join(); } } auto ThreadPool::async(std::function&& f) const -> boost::future { using TaskT = boost::packaged_task; { Lock lock(mx); if (stopping || canceling) { throw std::runtime_error("Delegating task to a threadpool that has been terminated or canceled."); } } std::shared_ptr task = std::make_shared(f); boost::future fut = task->get_future(); tasks.emplace([task]() -> void { (*task)(); }); cv.notify_one(); return fut; } VCMI_LIB_NAMESPACE_END