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
71 lines
1.7 KiB
C++
71 lines
1.7 KiB
C++
#include "vdm/util/thread_pool.hpp"
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <future>
|
|
#include <stdexcept>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
#include "vtest.hpp"
|
|
|
|
using vdm::ThreadPool;
|
|
|
|
VT_TEST(pool_runs_submitted_task_and_returns_value) {
|
|
ThreadPool pool(2);
|
|
auto f = pool.submit([](int a, int b) { return a + b; }, 20, 22);
|
|
VT_CHECK_EQ(f.get(), 42);
|
|
}
|
|
|
|
VT_TEST(pool_default_size_is_at_least_one) {
|
|
ThreadPool pool;
|
|
VT_CHECK(pool.size() >= 1);
|
|
}
|
|
|
|
VT_TEST(pool_runs_many_tasks_across_workers) {
|
|
ThreadPool pool(4);
|
|
constexpr int kN = 500;
|
|
std::atomic<int> sum{0};
|
|
std::vector<std::future<void>> fs;
|
|
fs.reserve(kN);
|
|
for (int i = 0; i < kN; ++i)
|
|
fs.push_back(pool.submit([&sum, i] { sum += i; }));
|
|
for (auto &f : fs)
|
|
f.get();
|
|
VT_CHECK_EQ(sum.load(), kN * (kN - 1) / 2);
|
|
}
|
|
|
|
VT_TEST(pool_propagates_exceptions_through_future) {
|
|
ThreadPool pool(1);
|
|
auto f = pool.submit([]() -> int { throw std::runtime_error("boom"); });
|
|
bool threw = false;
|
|
try {
|
|
f.get();
|
|
} catch (const std::runtime_error &) {
|
|
threw = true;
|
|
}
|
|
VT_CHECK(threw);
|
|
}
|
|
|
|
VT_TEST(pool_drains_queued_tasks_on_destruction) {
|
|
std::atomic<int> done{0};
|
|
{
|
|
ThreadPool pool(2);
|
|
for (int i = 0; i < 50; ++i)
|
|
pool.submit([&done] {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
++done;
|
|
});
|
|
// pool dtor here: must let all 50 finish
|
|
}
|
|
VT_CHECK_EQ(done.load(), 50);
|
|
}
|
|
|
|
VT_TEST(pool_future_from_void_task_is_waitable) {
|
|
ThreadPool pool(2);
|
|
std::atomic<bool> ran{false};
|
|
auto f = pool.submit([&ran] { ran = true; });
|
|
f.get();
|
|
VT_CHECK(ran.load());
|
|
}
|