Commit Graph
3 Commits
Author SHA1 Message Date
dtourolle 00245f5760 fix: Network::set_error_handler must actually deliver the handler
🚦 CI / changes (pull_request) Successful in 13s
🚦 CI / docker (pull_request) Has been skipped
🚦 CI / test (pull_request) Successful in 8m28s
🚦 CI / tsan (pull_request) Successful in 3m30s
🚦 CI / docs (pull_request) Has been skipped
The handler was stored in a member and never read. A node's exception was
discarded at the node boundary and the only surviving evidence was a Closed
event, which reports that a node stopped but not why — the difference between
a diagnosis and a guess. StaticNetwork has always wired this; Network accepted
the handler and silently dropped it, which is worse than not offering the
setter, because the caller believes they have a listener.

start() now delivers it to each node, exactly as StaticNetwork does.

The type changes with it. It was void(name, exception_ptr), which cannot
express the keep-running decision the node side needs — so it is now
NodeErrorHandler, the same alias StaticNetwork uses. That is a breaking change
in principle; in practice nothing in the tree called this setter, which is how
it stayed dead long enough to be worth finding.

Verified by the new case: the node throws, the handler receives the name and
the exception, returns true, and the node goes on to process the next value.
148/148.
2026-08-05 18:05:24 +02:00
dtourolle 7a3e96cc99 fix: the watchdog must be interruptible, or stop() waits for its next tick
start_watchdog looped on std::this_thread::sleep_for(watchdog_interval_), and
request_stop() cannot wake a sleeping thread. stop_watchdog()'s join therefore
blocked until the current sleep expired: three seconds on every teardown at
the default interval, and unbounded for anyone who set a long one to keep the
periodic report quiet.

Now a condition_variable_any waited on with the stop token, so request_stop()
ends the wait immediately.

Found while writing the next commit's test, which sets a one-hour interval to
silence the report and consequently hung for an hour in stop().

The test needs one non-obvious thing, and says so: a pause between start() and
stop(). Without it the test races the watchdog — stop_watchdog() runs before
the thread has entered its loop, the token is already set when it does, and it
exits without ever waiting. That passes against the bug as well as the fix,
which is exactly what the first version of this test did.

Verified in both directions: without the fix the case is killed at a 25 s
timeout; with it, stop() returns in 0 ms.
2026-08-05 17:52:36 +02:00
dtourolle 5e77dc836b First attempt 2026-05-08 17:48:16 +02:00