core: add util layer — Result, Error taxonomy, bytes, event bus, pool, log
util/ carries no wire surface, so it lands before the contract freeze. - error: enum class Error, the engine-wide failure taxonomy; is_retryable enumerates every value (no default:) so -Wswitch forces the retry decision on each future addition. ErrorInfo carries context/http_status. - result: Result<T> over std::expected<T, ErrorInfo>, Result<void>, VDM_TRY / VDM_TRY_ASSIGN. Errors returned, never thrown, on the transfer path. - bytes: span aliases, LE load_le/store_le (debug-asserted precondition, not input validation), and a bounds-checked latching ByteReader for the .veloxpart.meta reader. - event_bus: typed thread-safe pub/sub; header states plainly that unsubscribe is not a quiesce point and download_task will need its own drain. - thread_pool: std::jthread pool; dtor joins in the body before members die (fixed a use-after-destruction on cv_/mu_). Header notes shutdown is drain-only and DAEMON will need a cancel mode. - log: sink interface (core does no I/O); DAEMON installs one. Tested: -Werror clean, 6 binaries green under plain / ASan+UBSan / TSan. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
// vdm/util/thread_pool.cpp
|
||||
|
||||
#include "vdm/util/thread_pool.hpp"
|
||||
|
||||
namespace vdm {
|
||||
|
||||
ThreadPool::ThreadPool(std::size_t threads) {
|
||||
if (threads == 0) {
|
||||
threads = std::thread::hardware_concurrency();
|
||||
if (threads == 0)
|
||||
threads = 1;
|
||||
}
|
||||
workers_.reserve(threads);
|
||||
for (std::size_t i = 0; i < threads; ++i)
|
||||
workers_.emplace_back([this] { worker_loop(); });
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool() {
|
||||
{
|
||||
std::lock_guard lk(mu_);
|
||||
stopping_ = true;
|
||||
}
|
||||
cv_.notify_all();
|
||||
// Join here, in the destructor body, while mu_/cv_/jobs_ are still alive. Relying on
|
||||
// std::jthread's implicit join would run it during member destruction — after cv_ and
|
||||
// mu_ are already gone, which the workers are still touching.
|
||||
for (auto &w : workers_)
|
||||
w.join();
|
||||
workers_.clear();
|
||||
}
|
||||
|
||||
void ThreadPool::worker_loop() {
|
||||
for (;;) {
|
||||
std::function<void()> job;
|
||||
{
|
||||
std::unique_lock lk(mu_);
|
||||
cv_.wait(lk, [this] { return stopping_ || !jobs_.empty(); });
|
||||
if (jobs_.empty()) {
|
||||
// Only reached when stopping_ and nothing left to do.
|
||||
return;
|
||||
}
|
||||
job = std::move(jobs_.front());
|
||||
jobs_.pop();
|
||||
}
|
||||
job();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace vdm
|
||||
Reference in New Issue
Block a user