From adbf42a85ade68e2ebe3937750024d521eede253 Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Thu, 11 Jun 2026 10:29:50 -0700 Subject: [PATCH] test(timer): fix flaky StopTokenCancelsOne on IOCP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test raced a 10ms delay timer against the 500ms shared timer t to drive cancellation. Under a runtime stall both expire together and t completes w1 with success before the delay-driven request_stop() runs, so the cancellation assertion fails intermittently in CI. Drop the delay timer; call request_stop() from a posted task instead. run_async queues it FIFO, so both waiters register on t before the cancel runs — deterministic, no wall-clock margin. --- test/unit/timer.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/unit/timer.cpp b/test/unit/timer.cpp index 32339b4d2..c4731b880 100644 --- a/test/unit/timer.cpp +++ b/test/unit/timer.cpp @@ -633,14 +633,12 @@ struct timer_test { io_context ioc(Backend); timer t(ioc); - timer delay(ioc); std::stop_source stop_src; bool w1 = false, w2 = false; std::error_code ec1, ec2; t.expires_after(std::chrono::milliseconds(500)); - delay.expires_after(std::chrono::milliseconds(10)); // w1 has a stop_token — will be cancelled individually auto wait_task = [&]() -> capy::task<> { @@ -656,9 +654,16 @@ struct timer_test w2 = true; }; + // Cancel w1 once both waiters are registered. Posting through the + // executor (rather than racing a wall-clock delay timer against t's + // expiry) makes the ordering deterministic: run_async queues the + // tasks, and the IOCP/reactor loop drains posted completions in FIFO + // order, so both wait tasks run and suspend on t before this task + // calls request_stop(). t's deadline is far enough out that w2 then + // completes via a normal expiry afterwards. auto cancel_one = [&]() -> capy::task<> { - (void)co_await delay.wait(); stop_src.request_stop(); + co_return; }; capy::run_async(ioc.get_executor(), stop_src.get_token())(wait_task());