Skip to content

Conversation

@sgerbino
Copy link
Collaborator

@sgerbino sgerbino commented Feb 12, 2026

Move expiry_, heap_index_, and might_have_pending_waits_ from the private timer_impl to the public base class so cancel(), expires_at(), expires_after(), expiry(), and await_suspend can check for no-op conditions inline. Use time_point::min() as an already-expired sentinel to skip clock_gettime in expires_after() and wait() for zero-delay timers.

Summary by CodeRabbit

  • New Features

    • Optimized timer wait path with inline fast-path for immediate completion when timers are already expired.
    • Enhanced expiry state tracking for improved timer management.
  • Performance Improvements

    • Reduced overhead for timer cancellation operations through early-exit optimization when no pending waits exist.
    • Streamlined timer expiry updates to avoid unnecessary heap operations.

Move expiry_, heap_index_, and might_have_pending_waits_ from the private
timer_impl to the public base class so cancel(), expires_at(), expires_after(),
expiry(), and await_suspend can check for no-op conditions inline. Use
time_point::min() as an already-expired sentinel to skip clock_gettime in
expires_after() and wait() for zero-delay timers.
@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

The changes refactor timer expiry and cancellation handling by introducing inline fast-path optimization in the header file, extending timer_impl with new state tracking members, consolidating expiry management through a unified update mechanism in the service layer, and renaming public API methods in the core timer implementation.

Changes

Cohort / File(s) Summary
Header Fast-Path Optimization
include/boost/corosio/timer.hpp
Introduces inline fast-path in wait_suspend that bypasses underlying wait mechanism for non-heap timers with expired/default expiry. Adds state tracking members (expiry_, heap_index_, might_have_pending_waits_), private helper declarations (do_cancel, do_cancel_one, do_update_expiry), and modifies cancel(), cancel_one(), expires_at(), expires_after() to short-circuit when no pending waits. Makes expiry() noexcept.
Service Implementation Consolidation
src/corosio/src/detail/timer_service.cpp
Removes per-timer data members from timer_impl and consolidates expiry management by removing separate timer_service_expiry() and timer_service_expires_at() functions, replacing them with unified timer_service_update_expiry() function. Updates wait-fast path to check expiry against time_point::min().
Public API Refactoring
src/corosio/src/timer.cpp
Renames cancel() to do_cancel() and cancel_one() to do_cancel_one(). Removes public expiry() and expires_at() methods. Replaces expires_after(duration) with parameterless do_update_expiry(). Updates service hook calls to use timer_service_update_expiry().

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 ✨ Hops with glee through optimized ways,
Fast paths bloom where timers blaze,
State tracked cleanly, heap in place,
No pending waits need chase and race!
Service unified, oh what grace! 🌿

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (8 files):

⚔️ include/boost/corosio/timer.hpp (content)
⚔️ src/corosio/src/detail/epoll/op.hpp (content)
⚔️ src/corosio/src/detail/epoll/sockets.cpp (content)
⚔️ src/corosio/src/detail/epoll/sockets.hpp (content)
⚔️ src/corosio/src/detail/kqueue/op.hpp (content)
⚔️ src/corosio/src/detail/kqueue/sockets.cpp (content)
⚔️ src/corosio/src/detail/timer_service.cpp (content)
⚔️ src/corosio/src/timer.cpp (content)

These conflicts must be resolved before merging into develop.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: optimizing timer fast paths by eliminating cross-translation-unit calls on common operations, which aligns with the core changes moving members to the public base class for inline checks.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch pr/timer-optimizations
  • Post resolved changes as copyable diffs in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
src/corosio/src/detail/timer_service.cpp (2)

699-709: Verify ordering of the time_point::min() sentinel check.

The sentinel check expiry_ == (time_point::min)() short-circuits the clock_type::now() syscall for zero-delay timers — nice optimization. However, the two branches serve different semantic roles: time_point::min() means "treat as already expired" (skip clock), while expiry_ <= clock_type::now() is a genuine time comparison.

Consider whether a caller could legitimately set expiry_ to time_point::min() via expires_at(time_point::min()) without intending the sentinel semantics. Currently expires_after only sets the sentinel for d <= duration::zero() (header line 251–252), but expires_at unconditionally stores whatever t is passed (header line 233). If a user passes time_point::min() to expires_at, they'll hit the sentinel fast-path and skip clock_gettime, which is arguably correct (it is in the past), but it's worth a note that time_point::min() now has special semantics.


718-724: might_have_pending_waits_ is set after insert_waiter — verify no interleaving concern.

insert_waiter (line 719) adds the waiter to the heap/list under the mutex, then might_have_pending_waits_ is set (line 720), and only then is the stop-callback armed (line 723–724). Because the timer contract is "Shared objects: Unsafe" and stop-callback can't fire before it's registered, there's no interleaving window here. But the write to might_have_pending_waits_ is outside the mutex, while cancel_timer and process_expired also read/write it both inside and outside the mutex.

If the timer is ever used from a multi-threaded io_context where process_expired could run on a different OS thread, the non-atomic bool is a data race under the C++ memory model. If single-thread execution is a hard invariant, this is fine — but it might be worth a comment or an assertion to document that assumption.

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

100-114: Exposing expiry_, heap_index_, and might_have_pending_waits_ in a public header struct — acceptable but worth a note.

These members are now part of the ABI surface of timer_impl. This is the core enabler of the PR's optimization (inline fast-path checks in the header), so it's an intentional trade-off. A brief doc comment on the struct noting these are internal state for fast-path inlining (and not part of the user-facing contract) would help future maintainers.

📝 Suggested documentation
 public:
+    /// `@internal` Implementation base for timer.
+    /// Data members are exposed for inline fast-path checks
+    /// and must not be accessed by user code.
     struct timer_impl : io_object_impl
     {

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.

@codecov
Copy link

codecov bot commented Feb 12, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.24%. Comparing base (388d4b7) to head (3150cf0).
⚠️ Report is 2 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #137      +/-   ##
===========================================
+ Coverage    81.15%   81.24%   +0.08%     
===========================================
  Files           64       64              
  Lines         5667     5710      +43     
===========================================
+ Hits          4599     4639      +40     
- Misses        1068     1071       +3     
Files with missing lines Coverage Δ
include/boost/corosio/timer.hpp 98.18% <100.00%> (-1.82%) ⬇️
src/corosio/src/detail/timer_service.cpp 86.46% <100.00%> (-0.77%) ⬇️
src/corosio/src/timer.cpp 100.00% <100.00%> (ø)

... and 3 files 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 388d4b7...3150cf0. Read the comment docs.

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

@cppalliance-bot
Copy link

An automated preview of the documentation is available at https://137.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 20:10:21 UTC

@cppalliance-bot
Copy link

GCOVR code coverage report https://137.corosio.prtest3.cppalliance.org/gcovr/index.html
LCOV code coverage report https://137.corosio.prtest3.cppalliance.org/genhtml/index.html
Coverage Diff Report https://137.corosio.prtest3.cppalliance.org/diff-report/index.html

Build time: 2026-02-12 20:15:13 UTC

@sgerbino sgerbino merged commit 085506f into cppalliance:develop Feb 12, 2026
17 checks passed
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.

2 participants