#ifndef ALSK_ALSK_EXECUTOR_UTILITY_POOL_H #define ALSK_ALSK_EXECUTOR_UTILITY_POOL_H #include #include #include #include #include #include #include #include namespace alsk { namespace exec { namespace util { struct Pool { using Task = std::function; using TaskInfo = std::tuple>; private: std::atomic_bool _running; std::vector _threads; std::list _tasks; std::mutex _mutexTasks; std::condition_variable _cvTasks; std::mutex _mutexProcessed; std::condition_variable _cvProcessed; public: Pool(): _running{true} {} Pool(Pool const& o): _running{true} { config(o._threads.size()); } Pool(Pool&& o): _running{true} { config(o._threads.size()); } ~Pool() { terminate(); } Pool const& operator=(Pool const& o) { if(this == &o) return *this; config(o._threads.size()); return *this; } Pool const& operator=(Pool&& o) { if(this == &o) return *this; config(o._threads.size()); return *this; } void config(unsigned int cores) { terminate(); _running = true; if(cores == 0) return; --cores; // main thread will work too _threads.reserve(cores); while(cores--) _threads.emplace_back([&]{ worker(); }); } template, std::enable_if_t{}>* = nullptr> std::future run(F&& task) { std::future future; { std::lock_guard lg{_mutexTasks}; _tasks.emplace_back(std::forward(task), std::promise{}); future = std::get<1>(_tasks.back()).get_future(); } _cvTasks.notify_one(); return future; } template, std::enable_if_t{}>* = nullptr> std::future run(F&& task, std::promise& promise) { std::future future = promise.get_future(); run([task=std::forward(task), &promise]{ promise.set_value(task()); }); return future; } template void wait(Futures& futures) { while(tryProcessOne()); for(auto& future: futures) future.wait(); } protected: void terminate() { { std::lock_guard lk{_mutexTasks}; _running = false; } _cvTasks.notify_all(); for(auto& thread: _threads) thread.join(); _threads.clear(); } void worker() { auto test = [&]{ return !_running || _tasks.size(); }; for(;;) { TaskInfo taskInfo; { std::unique_lock lk{_mutexTasks}; if(!test()) _cvTasks.wait(lk, test); if(!_running) return; taskInfo = std::move(_tasks.front()); _tasks.pop_front(); } process(taskInfo); } } bool tryProcessOne() { TaskInfo taskInfo; { std::unique_lock lk{_mutexTasks}; if(_tasks.empty()) return false; taskInfo = std::move(_tasks.front()); _tasks.pop_front(); } process(taskInfo); return true; } void process(TaskInfo& taskInfo) { std::get<0>(taskInfo)(); std::get<1>(taskInfo).set_value(); _cvProcessed.notify_all(); } }; } } } #endif