Skip to content

Add cancel_after / cancel_at timeout adapters#180

Merged
sgerbino merged 1 commit intocppalliance:developfrom
sgerbino:feature/cancel
Feb 26, 2026
Merged

Add cancel_after / cancel_at timeout adapters#180
sgerbino merged 1 commit intocppalliance:developfrom
sgerbino:feature/cancel

Conversation

@sgerbino
Copy link
Collaborator

@sgerbino sgerbino commented Feb 26, 2026

Lightweight free functions that race an IoAwaitable against a timer. A bidirectional std::stop_source ties the inner op and timeout together — whichever completes first cancels the other. Uses a fire-and-forget coroutine for the timer side, avoiding any timer_service modifications.

Resolves #119.

Summary by CodeRabbit

  • New Features
    • Deadline-based cancellation for I/O via cancel_at().
    • Timeout-based cancellation for I/O via cancel_after().
    • Support for native timer backends so cancellations integrate with platform timers.
    • Lightweight fire-and-forget timeout coroutine to drive cancellations.
    • Parent cancellation is propagated; operations auto-cancel on timeout.
    • Public headers exposing cancellation and socket-option support added.

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

📝 Walkthrough

Walkthrough

Adds deadline- and timeout-based cancellation utilities: new public cancel_at/cancel_after overloads, a detail awaitable that races an IoAwaitable against a timer using a shared stop_source, a fire-and-forget timeout coroutine, and native-timer adapter helpers; public header re-exports new APIs. (48 words)

Changes

Cohort / File(s) Summary
Public API
include/boost/corosio.hpp, include/boost/corosio/cancel.hpp
Expose new cancel_at / cancel_after helpers by adding public includes and declarations.
Core awaitable
include/boost/corosio/detail/cancel_at_awaitable.hpp
Add detail::cancel_at_awaitable<A,Timer,Owning>: races inner IoAwaitable vs timer, shares std::stop_source, forwards parent stop_token, manages stop_callback lifetime and move semantics.
Timeout coroutine
include/boost/corosio/detail/timeout_coro.hpp
Add detail::timeout_coro and make_timeout — a fire-and-forget coroutine owning an io_env that awaits a timer and requests stop on expiry.
Native timer adapters
include/boost/corosio/native/native.hpp, include/boost/corosio/native/native_cancel.hpp
Add native backend overloads cancel_at / cancel_after that instantiate cancel_at_awaitable with native_timer<Backend> (devirtualized paths for native timers).

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant API as cancel_at / cancel_after
    participant Await as cancel_at_awaitable
    participant Timer as Timer
    participant Timeout as timeout_coro
    participant Inner as Inner IoAwaitable
    participant StopSrc as std::stop_source

    Client->>API: call cancel_at(op, timer, deadline)
    API->>Await: construct awaitable(op, timer, deadline)
    Client->>Await: co_await
    Await->>StopSrc: create shared stop_source
    Await->>Timeout: make_timeout(timer, StopSrc)  -- fire-and-forget
    Timeout->>Timer: await timer.wait()
    par race
        Await->>Inner: resume inner with forwarded stop_token
        Inner-->>Await: completes with result/error
    and
        Timer-->>StopSrc: timer expires -> request_stop()
        StopSrc-->>Inner: stop requested (propagated)
    end
    alt Inner completed first
        Await->>Timeout: cancel timeout
        Await-->>Client: return inner result
    else Timer expired first
        Await-->>Client: return canceled condition
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hop between awaits and ticking time,
I nudge the clock when deadlines start to chime,
If tasks run long I tap a tiny bell,
A gentle stop — the coroutine sleeps well,
Hooray for tidy cancels, quick and prime! 🥕

🚥 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 'Add cancel_after / cancel_at timeout adapters' accurately summarizes the primary change—introducing two timeout adapter functions that race operations against timers.
Linked Issues check ✅ Passed The PR successfully implements the cancel_after and cancel_at functionality requested in issue #119, using a std::stop_source-based race mechanism to handle timeouts and return results or cancellation.
Out of Scope Changes check ✅ Passed All changes directly support the cancel_after and cancel_at adapters: new headers expose public API, detail headers implement core racing logic, and native headers provide backend-specific overloads. No extraneous modifications detected.

✏️ 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 26, 2026

An automated preview of the documentation is available at https://180.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-26 19:01:00 UTC

@cppalliance-bot
Copy link

cppalliance-bot commented Feb 26, 2026

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

Build time: 2026-02-26 19:07:43 UTC

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: 5

🧹 Nitpick comments (2)
include/boost/corosio/detail/timeout_coro.hpp (1)

58-62: unhandled_exception() swallowing exceptions is risky for maintenance/debugging.

For a fire-and-forget coroutine, doing nothing makes failures silent. Consider std::terminate() (or at least an assertion/log hook if the project has one) to avoid “hung timeout logic” being masked by unexpected exceptions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/boost/corosio/detail/timeout_coro.hpp` around lines 58 - 62, The
unhandled_exception() currently swallows exceptions silently in the timeout
coroutine; change its implementation in the coroutine promise (function
unhandled_exception) to terminate or surface the error (e.g., call
std::terminate() or your project's fatal/assert/log hook) so exceptions do not
get silently dropped and can’t mask timeout logic failures; update the promise
type (where initial_suspend(), final_suspend(), return_void(),
unhandled_exception() are defined) to invoke the chosen termination/logging
action.
include/boost/corosio/detail/cancel_at_awaitable.hpp (1)

37-44: Avoid storing a raw pointer in the stop callback; capture std::stop_source by value instead.

stop_forwarder currently stores std::stop_source* (Line 39) and assumes the awaiter won’t move after the callback is installed (comment on Line 72). Capturing a std::stop_source by value removes that fragility and makes the callback independent of the awaiter object’s address.

Proposed change
     struct stop_forwarder
     {
-        std::stop_source* src_;
+        std::stop_source src_;
         void operator()() const noexcept
         {
-            src_->request_stop();
+            src_.request_stop();
         }
     };
@@
         new (cb_buf_) stop_cb_type(
-            env->stop_token, stop_forwarder{&stop_src_});
+            env->stop_token, stop_forwarder{stop_src_});

Also applies to: 105-108, 126-134

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/boost/corosio/detail/cancel_at_awaitable.hpp` around lines 37 - 44,
The stop callback type stop_forwarder currently stores a raw std::stop_source*
(member src_) which is fragile if the awaiter moves; change stop_forwarder to
capture a std::stop_source by value (store std::stop_source src_ or
std::stop_source src_;) and update operator() to call src_.request_stop();
ensure proper noexcept specifiers remain and that the type remains
copyable/movable as needed. Apply the same fix to the other callback
types/structs in this file that mirror stop_forwarder’s pattern (the other
stop-forwarding structs around the other awaitable implementations) so none keep
raw pointers to a stop_source.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@include/boost/corosio/cancel.hpp`:
- Around line 79-87: The public helper cancel_after (which computes
timer::clock_type::now() + timeout) can overflow for extreme timeout durations;
update cancel_after to either document the valid timeout range or, better,
perform saturating arithmetic when computing the deadline: obtain now =
timer::clock_type::now(), compute a safe deadline by clamping now + timeout into
[timer::clock_type::time_point::min(), timer::clock_type::time_point::max()] (or
reuse the same logic as expires_after in timer.hpp), then call cancel_at with
that clamped time_point; mention the change in the function docstring if you add
a precondition instead of clamping.

In `@include/boost/corosio/detail/cancel_at_awaitable.hpp`:
- Around line 13-23: Add a file-level /* */ overview comment immediately after
the includes in include/boost/corosio/detail/cancel_at_awaitable.hpp that
summarizes the control flow and lifetime assumptions for this header: describe
the fire‑and‑forget timeout coroutine started via timeout_coro, how parent
stop_token forwarding is performed, how the interposed io_env (capy::ex::io_env)
is used to isolate/resume the awaited operation, and the ownership/lifetime
expectations for any internal handles or coroutine frames so callers know which
objects must outlive others; reference the namespace boost::corosio::detail and
the key concepts (timeout_coro, stop_token forwarding, interposed env, and the
cancel/await handshake) in the comment so readers can quickly map the overview
to the implementation.

In `@include/boost/corosio/detail/timeout_coro.hpp`:
- Around line 13-21: The header uses std::move and std::forward but doesn't
include <utility>, making the public header non-self-sufficient; add `#include`
<utility> to the top of include/boost/corosio/detail/timeout_coro.hpp alongside
the other includes so symbols used in functions that call std::move (around the
code referencing move at line ~47) and std::forward (around the code referencing
forward at line ~108) are defined for consumers of this header.
- Around line 13-23: Add a /* */ block comment immediately after the includes
that gives a high-level implementation overview: explain the role and lifecycle
of the self-destroying coroutine (how/when it frees itself), why and how owned
io_env storage is used, what the awaitable-transform wrapper does to
convert/forward awaitables (mention the transform semantics), and the MSVC
workaround rationale for specific promise/coroutine shims; reference the key
implementation symbols such as timeout_coro, io_awaitable_promise_base, io_env
and the awaitable transform wrapper so readers can map the narrative to the
code.

In `@include/boost/corosio/native/native_cancel.hpp`:
- Around line 22-47: Update the documentation for the native overload cancel_at
and its sibling native overloads (e.g., the native_timer<Backend> variant) to
match the timer overloads: add Completion conditions (use `@li` to list success vs
cancellation via capy::cond::canceled), Concurrency/overlap notes (state that
native_timer must outlive the awaitable, expiry is overwritten by new waits, and
overlapping waits on the same native_timer are forbidden), Error conditions and
`@throws` (document that timeouts/cancellation produce capy::cond::canceled and
any exceptions the inner awaitable may propagate), precise `@param`
lifetime/ownership annotations for op, t, and deadline, and include 2–3 `@par`
Example blocks showing normal completion, timeout cancellation, and shared-timer
misuse; keep wording parallel to the timer overload docs and reference
cancel_at, native_timer<Backend>, and capy::cond::canceled to help locate the
behavior.

---

Nitpick comments:
In `@include/boost/corosio/detail/cancel_at_awaitable.hpp`:
- Around line 37-44: The stop callback type stop_forwarder currently stores a
raw std::stop_source* (member src_) which is fragile if the awaiter moves;
change stop_forwarder to capture a std::stop_source by value (store
std::stop_source src_ or std::stop_source src_;) and update operator() to call
src_.request_stop(); ensure proper noexcept specifiers remain and that the type
remains copyable/movable as needed. Apply the same fix to the other callback
types/structs in this file that mirror stop_forwarder’s pattern (the other
stop-forwarding structs around the other awaitable implementations) so none keep
raw pointers to a stop_source.

In `@include/boost/corosio/detail/timeout_coro.hpp`:
- Around line 58-62: The unhandled_exception() currently swallows exceptions
silently in the timeout coroutine; change its implementation in the coroutine
promise (function unhandled_exception) to terminate or surface the error (e.g.,
call std::terminate() or your project's fatal/assert/log hook) so exceptions do
not get silently dropped and can’t mask timeout logic failures; update the
promise type (where initial_suspend(), final_suspend(), return_void(),
unhandled_exception() are defined) to invoke the chosen termination/logging
action.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f89148 and 94f14b6.

⛔ Files ignored due to path filters (1)
  • test/unit/cancel.cpp is excluded by !**/test/**
📒 Files selected for processing (6)
  • include/boost/corosio.hpp
  • include/boost/corosio/cancel.hpp
  • include/boost/corosio/detail/cancel_at_awaitable.hpp
  • include/boost/corosio/detail/timeout_coro.hpp
  • include/boost/corosio/native/native.hpp
  • include/boost/corosio/native/native_cancel.hpp

Comment on lines +79 to +87
auto cancel_after(
capy::IoAwaitable auto&& op,
timer& t,
timer::duration timeout)
{
return cancel_at(
std::forward<decltype(op)>(op), t,
timer::clock_type::now() + timeout);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find and examine timer.hpp to understand the type definitions
fd -a 'timer\.hpp$' include/boost/corosio

Repository: cppalliance/corosio

Length of output: 235


🏁 Script executed:

# Examine the cancel_after function and surrounding context
cat -n include/boost/corosio/cancel.hpp | head -100

Repository: cppalliance/corosio

Length of output: 3232


🏁 Script executed:

# Search for timer type definitions (clock_type, duration, time_point)
rg -n "using.*clock_type|using.*time_point|using.*duration" include/boost/corosio/timer.hpp -A 2

Repository: cppalliance/corosio

Length of output: 45


🏁 Script executed:

# Examine timer.hpp to find type definitions
cat -n include/boost/corosio/timer.hpp

Repository: cppalliance/corosio

Length of output: 7475


🏁 Script executed:

# Also check the public header structure
head -50 include/boost/corosio/timer.hpp

Repository: cppalliance/corosio

Length of output: 1677


🏁 Script executed:

# Examine io_timer.hpp to find the actual type definitions
cat -n include/boost/corosio/io/io_timer.hpp | head -150

Repository: cppalliance/corosio

Length of output: 5601


🏁 Script executed:

# Search for type aliases in io_timer.hpp
rg -n "clock_type|time_point|duration" include/boost/corosio/io/io_timer.hpp -B 2 -A 1

Repository: cppalliance/corosio

Length of output: 1047


Add documentation or guard against chrono overflow in cancel_after.

The expression timer::clock_type::now() + timeout can overflow for extreme timeout values (e.g., durations near the limits of std::chrono::steady_clock's ~292-year range). Although rare in practice, cancel_after is a public API and should either document a precondition on timeout or clamp the result to avoid undefined behavior. The companion method expires_after in timer.hpp shows awareness of extreme values; consistency suggests this should too.

Consider documenting the valid range for timeout in the docstring (e.g., @note The timeout duration must not cause integer overflow when added to the current time), or alternatively, implement saturating arithmetic to clamp the deadline to [time_point::min(), time_point::max()].

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/boost/corosio/cancel.hpp` around lines 79 - 87, The public helper
cancel_after (which computes timer::clock_type::now() + timeout) can overflow
for extreme timeout durations; update cancel_after to either document the valid
timeout range or, better, perform saturating arithmetic when computing the
deadline: obtain now = timer::clock_type::now(), compute a safe deadline by
clamping now + timeout into [timer::clock_type::time_point::min(),
timer::clock_type::time_point::max()] (or reuse the same logic as expires_after
in timer.hpp), then call cancel_at with that clamped time_point; mention the
change in the function docstring if you add a precondition instead of clamping.

@codecov
Copy link

codecov bot commented Feb 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.43%. Comparing base (eedd997) to head (e7d1711).
⚠️ Report is 2 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #180      +/-   ##
===========================================
+ Coverage    75.99%   76.43%   +0.43%     
===========================================
  Files           94       98       +4     
  Lines        10385    10523     +138     
  Branches      2371     2371              
===========================================
+ Hits          7892     8043     +151     
+ Misses        1777     1757      -20     
- Partials       716      723       +7     
Flag Coverage Δ
linux 83.46% <100.00%> (+0.49%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
include/boost/corosio/cancel.hpp 100.00% <100.00%> (ø)
...clude/boost/corosio/detail/cancel_at_awaitable.hpp 100.00% <100.00%> (ø)
include/boost/corosio/detail/timeout_coro.hpp 100.00% <100.00%> (ø)
include/boost/corosio/native/native_cancel.hpp 100.00% <100.00%> (ø)

... and 8 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 eedd997...e7d1711. 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: 1

♻️ Duplicate comments (1)
include/boost/corosio/cancel.hpp (1)

84-86: ⚠️ Potential issue | 🟠 Major

Guard deadline calculation against chrono overflow in cancel_after.

Line 86 uses timer::clock_type::now() + timeout directly. With extreme duration values, that addition can overflow. Please clamp or enforce/document a strict precondition.

Proposed fix (clamp positive overflow, treat non-positive as immediate deadline)
 auto cancel_after(
     capy::IoAwaitable auto&& op,
     timer& t,
     timer::duration timeout)
 {
+    auto const now = timer::clock_type::now();
+    auto deadline  = now;
+    if (timeout > timer::duration::zero())
+    {
+        auto const max_delta = timer::time_point::max() - now;
+        deadline             = (timeout > max_delta)
+                                   ? timer::time_point::max()
+                                   : now + timeout;
+    }
     return cancel_at(
-        std::forward<decltype(op)>(op), t,
-        timer::clock_type::now() + timeout);
+        std::forward<decltype(op)>(op), t, deadline);
 }
#!/bin/bash
# Verify current implementation and compare with timer deadline-handling patterns.
cat -n include/boost/corosio/cancel.hpp | sed -n '75,95p'
rg -n "expires_after|time_point::max|clock_type::now\\(\\) \\+" include/boost/corosio/timer.hpp -C3
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/boost/corosio/cancel.hpp` around lines 84 - 86, The call in
cancel_after that computes a deadline as timer::clock_type::now() + timeout can
overflow for extreme timeout values; modify cancel_after to first check timeout:
if timeout <= decltype(timeout)::zero() pass an immediate deadline (now()),
otherwise compute now + timeout but clamp to
timer::clock_type::time_point::max() on positive overflow (and optionally to
time_point::min() for large negative durations), then forward that clamped
time_point to cancel_at; update the implementation in cancel_after and reference
timer::clock_type::now(), the timeout parameter, cancel_at, and
timer::clock_type::time_point::max() when making the change.
🧹 Nitpick comments (1)
include/boost/corosio/detail/timeout_coro.hpp (1)

163-172: Tighten make_timeout docblock with return/completion details.

Please add explicit @return semantics and completion/error-condition notes (timer fired vs timer canceled/error) so this helper has a complete behavioral contract.

As per coding guidelines: “@return documentation — Describe the returned aggregate and its elements … Error conditions — Document the notable error_code values or conditions … Completion conditions — Bulleted @li list …”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/boost/corosio/detail/timeout_coro.hpp` around lines 163 - 172, Update
the docblock for make_timeout to include an explicit `@return` describing the
returned fire-and-forget coroutine handle/aggregate and its behavior (e.g., no
value, cancellation-only side-effect), and add completion and error-condition
notes: bulleted `@li` entries stating the coroutine completes when the timer fires
(and then it calls src.request_stop()/signals the stop source), when the timer
is canceled (no stop requested), or when the timer reports an error (document
notable error_code values or that errors propagate/log as applicable); place
these additions in the comment above make_timeout and reference the Timer
parameter behavior (timer fired vs timer canceled/error) and the
StopSource/stop_source side-effect so the behavioral contract is complete.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@include/boost/corosio/cancel.hpp`:
- Around line 22-49: Update the public docs for cancel_at and cancel_after to
include the required contract sections: add a "Completion conditions" bulleted
list describing exact cases when the returned awaitable resumes (inner op
completes successfully, inner op completes with error, deadline expires and op
is canceled, caller stop token cancels), a "Concurrency and overlap" paragraph
specifying how parent cancellation is forwarded and that the timer and inner
operation races (timer is cancelled if op completes first; reusing the same
timer requires synchronization), an "Error conditions" list describing which
errors may be produced (propagated op errors, timer errors, and cancellation
resulting in an error equal to capy::cond::canceled), and an "@throws" entry
describing exceptions the functions may throw (e.g., if timer enforcement or
parameter validation throws). Reference the functions by name (cancel_at,
cancel_after), mention the inner operation and timer/stop_token interactions,
and keep the descriptions precise and actionable to satisfy the coding guideline
checklist.

---

Duplicate comments:
In `@include/boost/corosio/cancel.hpp`:
- Around line 84-86: The call in cancel_after that computes a deadline as
timer::clock_type::now() + timeout can overflow for extreme timeout values;
modify cancel_after to first check timeout: if timeout <=
decltype(timeout)::zero() pass an immediate deadline (now()), otherwise compute
now + timeout but clamp to timer::clock_type::time_point::max() on positive
overflow (and optionally to time_point::min() for large negative durations),
then forward that clamped time_point to cancel_at; update the implementation in
cancel_after and reference timer::clock_type::now(), the timeout parameter,
cancel_at, and timer::clock_type::time_point::max() when making the change.

---

Nitpick comments:
In `@include/boost/corosio/detail/timeout_coro.hpp`:
- Around line 163-172: Update the docblock for make_timeout to include an
explicit `@return` describing the returned fire-and-forget coroutine
handle/aggregate and its behavior (e.g., no value, cancellation-only
side-effect), and add completion and error-condition notes: bulleted `@li` entries
stating the coroutine completes when the timer fires (and then it calls
src.request_stop()/signals the stop source), when the timer is canceled (no stop
requested), or when the timer reports an error (document notable error_code
values or that errors propagate/log as applicable); place these additions in the
comment above make_timeout and reference the Timer parameter behavior (timer
fired vs timer canceled/error) and the StopSource/stop_source side-effect so the
behavioral contract is complete.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94f14b6 and 306dfb9.

⛔ Files ignored due to path filters (1)
  • test/unit/cancel.cpp is excluded by !**/test/**
📒 Files selected for processing (6)
  • include/boost/corosio.hpp
  • include/boost/corosio/cancel.hpp
  • include/boost/corosio/detail/cancel_at_awaitable.hpp
  • include/boost/corosio/detail/timeout_coro.hpp
  • include/boost/corosio/native/native.hpp
  • include/boost/corosio/native/native_cancel.hpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • include/boost/corosio/native/native_cancel.hpp
  • include/boost/corosio/detail/cancel_at_awaitable.hpp

Lightweight free functions that race an IoAwaitable against a timer.
A bidirectional std::stop_source ties the inner op and timeout
together — whichever completes first cancels the other. Uses a
fire-and-forget coroutine for the timer side, avoiding any
timer_service modifications.
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.

🧹 Nitpick comments (2)
include/boost/corosio/native/native_cancel.hpp (1)

120-129: Consider documenting or guarding against duration overflow.

The expression timer::clock_type::now() + timeout can overflow for extreme timeout values. For consistency with the timer overloads in cancel.hpp, consider either documenting a precondition on timeout or implementing saturating arithmetic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/boost/corosio/native/native_cancel.hpp` around lines 120 - 129, The
cancel_after function currently computes timer::clock_type::now() + timeout
which can overflow for very large timeout values; update cancel_after
(template<auto Backend>) to guard against overflow by clamping/saturating the
target time_point (e.g., if timeout would push now() past
timer::clock_type::time_point::max() then use time_point::max()) or document a
precondition on timeout to match the behavior in cancel.hpp; locate the
computation in cancel_after and implement the saturating addition or explicit
range check before calling cancel_at(native_timer<Backend>& t, ...).
include/boost/corosio/cancel.hpp (1)

113-121: Consider documenting or guarding against duration overflow.

The expression timer::clock_type::now() + timeout can overflow for extreme timeout values near the limits of steady_clock's range. Consider either:

  1. Documenting a precondition on timeout (e.g., @pre timeout must not cause integer overflow when added to the current time), or
  2. Implementing saturating arithmetic to clamp the deadline.

This applies equally to the owning overload at lines 201-208.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/boost/corosio/cancel.hpp` around lines 113 - 121, The call in
cancel_after that computes timer::clock_type::now() + timeout (and the similar
owning overload) can overflow for extreme timeout values; update cancel_after
and the owning overload to either document a precondition that timeout must not
overflow when added to timer::clock_type::now() or implement saturating
arithmetic that clamps the resulting deadline to
timer::clock_type::time_point::max() (and min if applicable) before calling
cancel_at; reference the functions cancel_after, cancel_at and the
timer/timer::clock_type::now() usage when making the change so the guard or
clamp is applied at the moment the deadline is computed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@include/boost/corosio/cancel.hpp`:
- Around line 113-121: The call in cancel_after that computes
timer::clock_type::now() + timeout (and the similar owning overload) can
overflow for extreme timeout values; update cancel_after and the owning overload
to either document a precondition that timeout must not overflow when added to
timer::clock_type::now() or implement saturating arithmetic that clamps the
resulting deadline to timer::clock_type::time_point::max() (and min if
applicable) before calling cancel_at; reference the functions cancel_after,
cancel_at and the timer/timer::clock_type::now() usage when making the change so
the guard or clamp is applied at the moment the deadline is computed.

In `@include/boost/corosio/native/native_cancel.hpp`:
- Around line 120-129: The cancel_after function currently computes
timer::clock_type::now() + timeout which can overflow for very large timeout
values; update cancel_after (template<auto Backend>) to guard against overflow
by clamping/saturating the target time_point (e.g., if timeout would push now()
past timer::clock_type::time_point::max() then use time_point::max()) or
document a precondition on timeout to match the behavior in cancel.hpp; locate
the computation in cancel_after and implement the saturating addition or
explicit range check before calling cancel_at(native_timer<Backend>& t, ...).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 306dfb9 and e7d1711.

⛔ Files ignored due to path filters (2)
  • test/unit/cancel.cpp is excluded by !**/test/**
  • test/unit/native/native_cancel.cpp is excluded by !**/test/**
📒 Files selected for processing (6)
  • include/boost/corosio.hpp
  • include/boost/corosio/cancel.hpp
  • include/boost/corosio/detail/cancel_at_awaitable.hpp
  • include/boost/corosio/detail/timeout_coro.hpp
  • include/boost/corosio/native/native.hpp
  • include/boost/corosio/native/native_cancel.hpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • include/boost/corosio/native/native.hpp

@sgerbino sgerbino merged commit 21875c3 into cppalliance:develop Feb 26, 2026
20 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.

Feature request: cancel_after, cancel_at

2 participants