Skip to content

Conversation

@sgerbino
Copy link
Collaborator

@sgerbino sgerbino commented Feb 12, 2026

Refactor timer internals to support multiple concurrent waiters on a single timer via per-waiter waiter_node linked with intrusive_list. Add constructors with initial expiry, cancel_one() for FIFO single cancellation, and return cancelled waiter counts from cancel(), expires_at(), and expires_after(). Add a waiter node cache for the wait-then-complete hot path.

Introduce scheduler_impl as a private intermediary between the public scheduler interface and concrete backend schedulers (epoll, select, kqueue, iocp). Move timer_svc_ from each backend into scheduler_impl and remove the timer_svc() pure virtual from the public scheduler interface. Timer service lookup in timer_service_create now goes through timer_service_access to get the scheduler_impl and its cached timer_svc_ pointer. Update timer guide documentation with cancel_one, multi-waiter usage, and return values.

Resolves #126.
Resolves #97.

Summary by CodeRabbit

  • New Features

    • Timer constructors accept absolute time points or relative durations for direct scheduling.
    • Added cancel_one() to cancel a single waiter.
  • Improvements

    • Multiple coroutines can wait concurrently on the same timer.
    • Timer operations (cancel, expires_at, expires_after) now return counts of affected waiters for clearer feedback.

@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

Warning

Rate limit exceeded

@sgerbino has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 49 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Refactors timer internals to use per-waiter state (waiter_node) enabling multiple concurrent waiters, introduces scheduler_impl as an intermediary caching timer_service pointers used by platform schedulers, and changes timer APIs to return std::size_t counts; several scheduler headers/source files and dispatch logic adjusted accordingly.

Changes

Cohort / File(s) Summary
Timer core & service
src/corosio/src/detail/timer_service.cpp, src/corosio/src/timer.cpp, include/boost/corosio/timer.hpp
Replaced per-timer wait state with per-waiter waiter_node; added thread-local caches; changed internal APIs to return std::size_t counts; added constructors and cancel_one(); public timer methods now return operation counts.
Scheduler intermediary
src/corosio/src/detail/scheduler_impl.hpp
New scheduler_impl struct deriving from scheduler that caches timer_service* (timer_svc_) to centralize timer-service access.
Platform schedulers
src/corosio/src/detail/epoll/scheduler.hpp, src/corosio/src/detail/kqueue/scheduler.hpp, src/corosio/src/detail/select/scheduler.hpp, src/corosio/src/detail/iocp/scheduler.hpp
Platform schedulers now inherit from scheduler_impl instead of scheduler; per-scheduler timer_svc_ members removed and includes adjusted.
Scheduler sources: timer includes
src/corosio/src/detail/epoll/scheduler.cpp, src/corosio/src/detail/kqueue/scheduler.cpp, src/corosio/src/detail/select/scheduler.cpp
Added timer_service.hpp include where schedulers interact with timer service.
Dispatch logic
src/corosio/src/detail/dispatch_coro.hpp, include/boost/corosio/basic_io_context.hpp
Changed dispatch fast-path to use ex.target<>() lookup; added forward-declaration and friend detail::timer_service_access; adjusted constructor initializer ordering and include order.

Sequence Diagram(s)

sequenceDiagram
    participant Coro1 as Coroutine 1
    participant Coro2 as Coroutine 2
    participant Timer as Timer
    participant WN as Waiter Pool
    participant TS as Timer Service
    participant Sched as Scheduler

    Coro1->>Timer: call wait()
    Timer->>WN: allocate waiter_node for Coro1
    WN-->>Timer: waiter_node created
    Timer->>TS: register waiter_node (schedule/insert timer)
    TS->>TS: insert/update timer heap/list

    Coro2->>Timer: call wait() (same timer)
    Timer->>WN: allocate waiter_node for Coro2
    Timer->>TS: add waiter_node to timer's waiter list

    rect rgba(100, 200, 100, 0.5)
        Note over TS: timer expires
        TS->>WN: process_expired -> collect waiter_nodes
        TS->>Sched: post completions for waiter_nodes
    end

    Sched->>Coro1: resume (completion)
    Sched->>Coro2: resume (completion)
    Coro1->>WN: destroy/recycle waiter_node
    Coro2->>WN: destroy/recycle waiter_node
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped by clocks and lists today,

waiters nested in a careful way,
schedulers share a hidden key,
timers count what used to be,
cached tails now free to play.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the three main changes in the PR: multi-waiter support, cancel_one operation, and scheduler_impl intermediary.
Linked Issues check ✅ Passed PR fully addresses both issues #126 and #97 by implementing per-operation cancellation via stop_token and utilizing the previously unused timer_impl::token_ through per-waiter waiter_node structures.
Out of Scope Changes check ✅ Passed All changes are within scope; PR includes timer refactoring, scheduler_impl intermediary, multi-waiter support, cancel_one method, return type changes for cancellation counts, and necessary documentation updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cppalliance-bot
Copy link

cppalliance-bot commented Feb 12, 2026

An automated preview of the documentation is available at https://134.corosio.prtest3.cppalliance.org/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-02-12 04:35:58 UTC

@codecov
Copy link

codecov bot commented Feb 12, 2026

Codecov Report

❌ Patch coverage is 90.90909% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.15%. Comparing base (c98acaa) to head (f7b9719).
⚠️ Report is 2 commits behind head on develop.

Files with missing lines Patch % Lines
src/corosio/src/detail/timer_service.cpp 89.94% 18 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #134      +/-   ##
===========================================
+ Coverage    81.03%   81.15%   +0.11%     
===========================================
  Files           64       64              
  Lines         5557     5666     +109     
===========================================
+ Hits          4503     4598      +95     
- Misses        1054     1068      +14     
Files with missing lines Coverage Δ
include/boost/corosio/basic_io_context.hpp 95.52% <100.00%> (+0.06%) ⬆️
include/boost/corosio/detail/scheduler.hpp 100.00% <ø> (ø)
include/boost/corosio/timer.hpp 100.00% <100.00%> (+5.88%) ⬆️
src/corosio/src/detail/dispatch_coro.hpp 100.00% <100.00%> (ø)
src/corosio/src/detail/epoll/scheduler.cpp 83.50% <ø> (ø)
src/corosio/src/detail/epoll/scheduler.hpp 0.00% <ø> (ø)
src/corosio/src/detail/select/scheduler.cpp 73.42% <ø> (ø)
src/corosio/src/detail/select/scheduler.hpp 0.00% <ø> (ø)
src/corosio/src/timer.cpp 100.00% <100.00%> (ø)
src/corosio/src/detail/timer_service.cpp 87.19% <89.94%> (-1.24%) ⬇️

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c98acaa...f7b9719. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/corosio/src/detail/timer_service.cpp`:
- Around line 708-722: The insertion into the intrusive list in wait() (the
block calling waiters_.push_back(w)) races with cancel_waiter()'s
waiters_.remove(w); fix by serializing list mutations with the existing mutex_
(or by performing the push inside insert_timer so the insertion happens within
the same lock scope used for timer insertion). Concretely, either acquire mutex_
around the push_back in wait() and around any waiters_.empty() checks used in
update_timer/cancel_timer, or modify/extend insert_timer (e.g.,
insert_timer_and_waiter) to perform the first-waiter push inside its lock;
ensure cancel_waiter() continues to hold mutex_ while removing from
waiters_.remove.
- Around line 810-819: timer_service_create currently dereferences timer_svc_
from timer_service_access::get_scheduler(ioctx) without checking for nullptr;
add a null check after retrieving the pointer returned by
static_cast<timer_service_impl*>(...timer_svc_) and call
detail::throw_logic_error() (or equivalent) if svc is null before calling
svc->create_impl(), so the function safely handles an uninitialized timer_svc_.
🧹 Nitpick comments (2)
src/corosio/src/detail/timer_service.cpp (1)

331-356: Waiter node recycling doesn't reset stop_cb_ or other optional state.

create_waiter() hands back a previously-used node without clearing stop_cb_, ec_value_, or impl_. While wait() overwrites most fields and completion_op::operator()() resets stop_cb_ before recycling, it's worth verifying that every recycle path (especially shutdown(), where nodes are deleted rather than recycled) leaves no stale stop_cb_ engaged. A defensive stop_cb_.reset() in create_waiter() (or a reinitialise helper) would make the contract self-documenting.

include/boost/corosio/timer.hpp (1)

218-222: duration_cast truncation in template expires_after is worth a @note.

The duration_cast silently truncates sub-nanosecond precision (e.g., std::chrono::duration<double>steady_clock::duration). Consider adding a brief @note that the duration is truncated (not rounded) to the native resolution, mirroring Asio's documentation of the same overload.

Refactor timer internals to support multiple concurrent waiters on a
single timer via per-waiter waiter_node linked with intrusive_list.
Add constructors with initial expiry, cancel_one() for FIFO single
cancellation, and return cancelled waiter counts from cancel(),
expires_at(), and expires_after(). Add a waiter node cache for the
wait-then-complete hot path.

Introduce scheduler_impl as a private intermediary between the public
scheduler interface and concrete backend schedulers (epoll, select,
kqueue, iocp). Move timer_svc_ from each backend into scheduler_impl
and remove the timer_svc() pure virtual from the public scheduler
interface. Timer service lookup in timer_service_create now goes
through timer_service_access to get the scheduler_impl and its cached
timer_svc_ pointer. Update timer guide documentation with cancel_one,
multi-waiter usage, and return values.

Increase timer durations in cancel_one and stop-token-cancels-one
tests from 50ms to 500ms to avoid CI flakiness on slow machines.
@sgerbino sgerbino merged commit 9ed1f7d into cppalliance:develop Feb 12, 2026
18 checks passed
@sgerbino sgerbino deleted the pr/timer branch February 12, 2026 04:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Timers: per-operation cancellation timer_impl::token_ is unused

2 participants