39#include <netinet/in.h>
40#include <sys/socket.h>
58using Clock = std::chrono::steady_clock;
62static void check(
bool ok,
const std::string& what) {
63 std::cout << (
ok ?
" ok " :
" FAIL ") << what << std::endl;
72 struct timeval tv{.tv_sec = 0, .tv_usec = 100000};
73 setsockopt(listen_fd, SOL_SOCKET, SO_RCVTIMEO, &tv,
sizeof(tv));
74 int c = accept(listen_fd,
nullptr,
nullptr);
78 setsockopt(c, SOL_SOCKET, SO_RCVTIMEO, &tv,
sizeof(tv));
79 (void)recv(c, buf,
sizeof(buf), 0);
85 int fd = socket(AF_INET, SOCK_STREAM, 0);
87 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt,
sizeof(opt));
88 struct sockaddr_in
a{};
89 a.sin_family = AF_INET;
90 a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
92 bind(fd,
reinterpret_cast<struct sockaddr*
>(&
a),
sizeof(
a));
94 socklen_t len =
sizeof(
a);
95 getsockname(fd,
reinterpret_cast<struct sockaddr*
>(&
a), &len);
96 out_port = ntohs(
a.sin_port);
101 std::cout <<
"=== SequencerService concurrency ===" << std::endl;
103 uint16_t ctrl_port = 0;
105 std::atomic<bool> ctrl_run{
true};
106 std::thread ctrl_thread(
fakeController, ctrl_fd, std::ref(ctrl_run));
111 const std::string cfg_path =
"test_concurrency_cfg.toml";
113 std::ofstream f(cfg_path);
114 f <<
"[database]\nhost=\"127.0.0.1\"\nport=1\n\n"
115 <<
"[controller_service]\nhost=\"127.0.0.1\"\nport=" << ctrl_port <<
"\n\n"
116 <<
"[fire]\nstate=\"Fire\"\nexpiry_target=\"Armed\"\n"
117 <<
"duration_ms=250\nextended_ms=500\n";
121 if (!svc.
init(cfg_path)) {
122 std::cerr <<
"FAILED: init (run from the build dir, with config/ reachable)" << std::endl;
126 std::remove(cfg_path.c_str());
139 constexpr int kThreads = 12;
140 constexpr int kIterationsPerThread = 20;
141 std::atomic<int> issued{0};
142 const char* targets[] = {
"Idle",
"Armed",
"Fire",
"Engine Abort"};
144 const auto t0 = Clock::now();
145 std::vector<std::thread> workers;
146 for (
int i = 0; i < kThreads; ++i) {
147 workers.emplace_back([&, i]() {
149 for (
int k = 0; k < kIterationsPerThread; ++k) {
155 for (
auto& w : workers)
157 const auto elapsed_ms =
158 std::chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - t0).count();
162 check(
true,
"survived concurrent transitions (no double-join / terminate)");
163 check(issued.load() == kThreads * kIterationsPerThread,
164 "every transition completed (" + std::to_string(issued.load()) +
"/" +
165 std::to_string(kThreads * kIterationsPerThread) +
" in " +
166 std::to_string(elapsed_ms) +
" ms)");
170 const bool accepted = svc.
transitionTo(std::string(
"Idle"));
171 check(accepted,
"service still accepts commands after the storm");
173 "reported state matches the last accepted transition");
178 std::remove(cfg_path.c_str());
180 std::cout << (
g_failures == 0 ?
"✅ PASS" :
"❌ FAIL") << std::endl;
static State fromName(const std::string &name)
Translate a CSV name (e.g.
Top-level sequencer service.
bool setDebugMode(bool enabled)
Enable or disable debug mode.
bool init(const std::string &config_path="config/config.toml")
Load config, CSVs, connect to Elodin.
State currentState() const
bool transitionTo(const std::string &state_name)
Request a state transition by name (e.g.
Parses state_transitions.csv and validates state transitions.
*brief create JSON pointer *sa reference_tokens const string_t & a
std::chrono::steady_clock Clock
*brief user defined string literal for JSON values *sa std::size_t n
State
System state IDs — must match the TypeScript SystemState enum in web-gui/shared/types....
static void check(bool ok, const std::string &what)
static void fakeController(int listen_fd, std::atomic< bool > &run)
Minimal stand-in for controller_service: accept, drain, close.
static int bindEphemeral(uint16_t &out_port)