#pragma once
#include <condition_variable>
#include <cstdint>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <type_traits>
#include <utility>
#include <vector>
#include "threading_utils.h"
#include "xgboost/string_view.h"
namespace xgboost::common {
class ThreadPool {
std::mutex mu_;
std::queue<std::function<void()>> tasks_;
std::condition_variable cv_;
std::vector<std::thread> pool_;
bool stop_{false};
public:
template <typename InitFn>
explicit ThreadPool(StringView name, std::int32_t n_threads, InitFn&& init_fn) {
for (std::int32_t i = 0; i < n_threads; ++i) {
pool_.emplace_back([&, init_fn = std::forward<InitFn>(init_fn)] {
init_fn();
while (true) {
std::unique_lock lock{mu_};
cv_.wait(lock, [this] { return !this->tasks_.empty() || stop_; });
if (this->stop_) {
while (!tasks_.empty()) {
auto fn = tasks_.front();
tasks_.pop();
fn();
}
return;
}
auto fn = tasks_.front();
tasks_.pop();
lock.unlock();
fn();
}
});
std::string name_i = name.c_str() + std::string{"-"} + std::to_string(i); NameThread(&pool_.back(), name_i);
}
}
~ThreadPool() {
std::unique_lock lock{mu_};
stop_ = true;
lock.unlock();
for (auto& t : pool_) {
if (t.joinable()) {
std::unique_lock lock{mu_};
this->cv_.notify_one();
lock.unlock();
}
}
for (auto& t : pool_) {
if (t.joinable()) {
t.join();
}
}
}
template <typename Fn, typename R = std::invoke_result_t<Fn>>
auto Submit(Fn&& fn) {
auto p{std::make_shared<std::promise<R>>()};
auto fut = p->get_future();
auto ffn = std::function{[task = std::move(p), fn = std::forward<Fn>(fn)]() mutable {
if constexpr (std::is_void_v<R>) {
fn();
task->set_value();
} else {
task->set_value(fn());
}
}};
std::unique_lock lock{mu_};
this->tasks_.push(std::move(ffn));
lock.unlock();
cv_.notify_one();
return fut;
}
[[nodiscard]] auto NumWorkers() const {
return static_cast<std::int32_t>(pool_.size());
}
};
}