12#include <system_error>
19constexpr int kMaxWorkerCount = 256;
20thread_local const void *currentPoolState =
nullptr;
21std::mutex eventResolveMu;
25 mutable std::mutex
mu;
26 std::condition_variable
cv;
28 std::queue<std::shared_ptr<Task::State>>
queue;
34 if (workerCount <= 0) {
35 workerCount =
static_cast<int>(std::thread::hardware_concurrency());
39#if defined(__EMSCRIPTEN__)
44 workerCount = std::min(workerCount, 4);
46 if (workerCount > kMaxWorkerCount)
47 throw eve::Exception(
"ThreadPool worker count exceeds limit (%d)", kMaxWorkerCount);
49 workerCount_ = workerCount;
50 state_ = std::make_shared<State>();
51 workers_.reserve(
static_cast<size_t>(workerCount_));
53 for (
int i = 0; i < workerCount_; ++i) {
55 workers_.emplace_back([
state] { workerMain(
state); });
59 std::lock_guard<std::mutex> lock(state_->mu);
60 state_->stopping =
true;
62 state_->cv.notify_all();
63 for (
auto &worker : workers_) {
64 if (worker.joinable())
82 std::lock_guard<std::mutex> lock(state_->mu);
83 return static_cast<int>(state_->queue.size());
87 std::lock_guard<std::mutex> lock(state_->mu);
88 return !state_->stopping;
95 auto state = std::make_shared<Task::State>(std::move(
fn));
98 std::lock_guard<std::mutex> lock(state_->mu);
99 if (state_->stopping) {
103 state_->queue.push(std::move(
state));
105 state_->cv.notify_one();
113 std::this_thread::sleep_for(std::chrono::milliseconds(ms));
118 if (channel ==
nullptr)
122 auto channelState = channel->state_;
123 return submit([channelState, msg = std::move(message), delayMs] {
125 std::this_thread::sleep_for(std::chrono::milliseconds(delayMs));
127 std::lock_guard<std::mutex> lock(channelState->mu);
128 channelState->queue.push(msg);
130 channelState->cv.notify_one();
136 throw eve::Exception(
"ThreadPool::submitPost: name must not be empty");
142 "ThreadPool::submitPost: no main-thread queue (event module not linked)");
146 std::lock_guard<std::mutex> lock(eventResolveMu);
151 std::this_thread::sleep_for(std::chrono::milliseconds(delayMs));
152 poster->postToMainThread(
name,
data);
158 if (currentPoolState ==
state.get())
159 throw eve::Exception(
"ThreadPool::waitAll cannot be called from its worker");
160 std::unique_lock<std::mutex> lock(
state->mu);
165 if (currentPoolState == state_.get())
166 throw eve::Exception(
"ThreadPool::stop cannot be called from its worker");
170void ThreadPool::stopImpl(
bool allowWorkerCaller) {
171 std::lock_guard<std::mutex> lifecycleLock(lifecycleMu_);
173 const bool calledByWorker = currentPoolState ==
state.get();
174 if (calledByWorker && !allowWorkerCaller)
175 throw eve::Exception(
"ThreadPool::stop cannot be called from its worker");
178 std::lock_guard<std::mutex> lock(
state->mu);
179 state->stopping =
true;
181 state->cv.notify_all();
187 if (calledByWorker) {
188 for (
auto &worker : workers_) {
189 if (worker.joinable())
196 for (
auto &
w : workers_) {
201 }
catch (
const std::system_error &) {
209void ThreadPool::workerMain(std::shared_ptr<State>
state) {
210 currentPoolState =
state.get();
212 std::shared_ptr<Task::State> task;
214 std::unique_lock<std::mutex> lock(
state->mu);
216 if (
state->stopping &&
state->queue.empty()) {
217 currentPoolState =
nullptr;
220 task = std::move(
state->queue.front());
229 std::lock_guard<std::mutex> lock(
state->mu);
232 state->idleCv.notify_all();
JobSystemThreadPool::State * state
SettlementPipeline::Stage fn
Thread-safe message queue (love2d-style Channel). Values are strings so the API stays overload-free f...
A job executed by a ThreadPool worker. Status strings (no enums): "pending" | "running" | "done" | "f...
void waitAll()
Block until idle. Throws when called by a worker belonging to this pool.
int getPendingCount() const
Task * submitSleep(int ms)
Sleep on a worker, then mark done — useful from scripts / tests.
ThreadPool(int workerCount)
void stop()
Stop accepting work and join workers. Worker calls are rejected. Idempotent.
Task * submitPost(std::string name, std::string data="", int delayMs=0)
Sleep, then post an Event on the main queue (thread-safe). Scripts poll via event....
int getWorkerCount() const
Task * submitPush(Channel *channel, std::string message, int delayMs=0)
Sleep, then push a message onto a channel (cross-thread signalling).
Task * submit(std::function< void()> fn)
Submit a C++ callable. Caller owns the Task wrapper; work owns its shared state.
std::condition_variable cv
std::queue< std::shared_ptr< Task::State > > queue
std::condition_variable idleCv