Description
Cap'n Proto versions before 1.2 assume that successive CLOCK_MONOTONIC values never decrease. In some Linux virtualized environments, however, the clock can briefly move backwards, causing the event-loop wait to fail with a KJ exception ending with:
"can't advance backwards in time"
Cap'n Proto addressed this upstream in capnproto/capnproto#2261 and capnproto/capnproto#2296. Newer versions clamp a regressed timestamp to the previous monotonic value instead of raising this error.
The failing check in older versions is recoverable (KJ_REQUIRE(newTime >= time, "can't advance backwards in time") { return; } in TimerImpl::advanceTo()). This PR installs a kj::ExceptionCallback on the event loop thread for the duration of EventLoop::loop(). The callback logs a warning and returns for this specific error, so the timer keeps its previous time like newer versions do. Every other error is forwarded to the next callback unchanged. The workaround is compiled only for Cap'n Proto versions before 1.2, so builds against newer versions are unchanged.
Testing
Adds a Linux-only mpclocktest that overrides clock_gettime() to force a 10 ms CLOCK_MONOTONIC regression while the event loop is waiting. It is also added to the sanitize CI job.
Tested locally in Linux containers:
- Cap'n Proto 0.9.2, 1.0.1, 1.1.0 and 1.3.0: all tests pass, and
mpclocktestpassed 100+ repeated runs on each. - With 0.9.2 and the callback removed, the test fails with
can't advance backwards in time. - 0.9.2 with the
olddepswarning flags (-Werror), and clang with thellvmjob's flags plus clang-tidy and IWYU on the changed files: clean. - TSan build (uninstrumented Cap'n Proto 1.1.0):
mpclocktest50/50 runs with no reports.
Related
- bitcoin/bitcoin#36345
- capnproto/capnproto#2261
- capnproto/capnproto#2296