LCOV - code coverage report
Current view: top level - include/boost/corosio - timer.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 20 20
Test Date: 2026-02-12 21:00:53 Functions: 100.0 % 11 11

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3              : // Copyright (c) 2026 Steve Gerbino
       4              : //
       5              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7              : //
       8              : // Official repository: https://github.com/cppalliance/corosio
       9              : //
      10              : 
      11              : #ifndef BOOST_COROSIO_TIMER_HPP
      12              : #define BOOST_COROSIO_TIMER_HPP
      13              : 
      14              : #include <boost/corosio/detail/config.hpp>
      15              : #include <boost/corosio/detail/except.hpp>
      16              : #include <boost/corosio/io_object.hpp>
      17              : #include <boost/capy/io_result.hpp>
      18              : #include <boost/capy/error.hpp>
      19              : #include <boost/capy/ex/executor_ref.hpp>
      20              : #include <boost/capy/ex/execution_context.hpp>
      21              : #include <boost/capy/ex/io_env.hpp>
      22              : #include <boost/capy/concept/executor.hpp>
      23              : #include <system_error>
      24              : 
      25              : #include <chrono>
      26              : #include <coroutine>
      27              : #include <cstddef>
      28              : #include <stop_token>
      29              : 
      30              : namespace boost::corosio {
      31              : 
      32              : /** An asynchronous timer for coroutine I/O.
      33              : 
      34              :     This class provides asynchronous timer operations that return
      35              :     awaitable types. The timer can be used to schedule operations
      36              :     to occur after a specified duration or at a specific time point.
      37              : 
      38              :     Multiple coroutines may wait concurrently on the same timer.
      39              :     When the timer expires, all waiters complete with success. When
      40              :     the timer is cancelled, all waiters complete with an error that
      41              :     compares equal to `capy::cond::canceled`.
      42              : 
      43              :     Each timer operation participates in the affine awaitable protocol,
      44              :     ensuring coroutines resume on the correct executor.
      45              : 
      46              :     @par Thread Safety
      47              :     Distinct objects: Safe.@n
      48              :     Shared objects: Unsafe.
      49              : 
      50              :     @par Semantics
      51              :     Wraps platform timer facilities via the io_context reactor.
      52              :     Operations dispatch to OS timer APIs (timerfd, IOCP timers,
      53              :     kqueue EVFILT_TIMER).
      54              : */
      55              : class BOOST_COROSIO_DECL timer : public io_object
      56              : {
      57              :     struct wait_awaitable
      58              :     {
      59              :         timer& t_;
      60              :         std::stop_token token_;
      61              :         mutable std::error_code ec_;
      62              : 
      63         8769 :         explicit wait_awaitable(timer& t) noexcept : t_(t) {}
      64              : 
      65         8769 :         bool await_ready() const noexcept
      66              :         {
      67         8769 :             return token_.stop_requested();
      68              :         }
      69              : 
      70         8769 :         capy::io_result<> await_resume() const noexcept
      71              :         {
      72         8769 :             if (token_.stop_requested())
      73            4 :                 return {capy::error::canceled};
      74         8765 :             return {ec_};
      75              :         }
      76              : 
      77         8769 :         auto await_suspend(
      78              :             std::coroutine_handle<> h,
      79              :             capy::io_env const* env) -> std::coroutine_handle<>
      80              :         {
      81         8769 :             token_ = env->stop_token;
      82         8769 :             return t_.get().wait(h, env->executor, token_, &ec_);
      83              :         }
      84              :     };
      85              : 
      86              : public:
      87              :     struct timer_impl : io_object_impl
      88              :     {
      89              :         virtual std::coroutine_handle<> wait(
      90              :             std::coroutine_handle<>,
      91              :             capy::executor_ref,
      92              :             std::stop_token,
      93              :             std::error_code*) = 0;
      94              :     };
      95              : 
      96              : public:
      97              :     /// The clock type used for time operations.
      98              :     using clock_type = std::chrono::steady_clock;
      99              : 
     100              :     /// The time point type for absolute expiry times.
     101              :     using time_point = clock_type::time_point;
     102              : 
     103              :     /// The duration type for relative expiry times.
     104              :     using duration = clock_type::duration;
     105              : 
     106              :     /** Destructor.
     107              : 
     108              :         Cancels any pending operations and releases timer resources.
     109              :     */
     110              :     ~timer();
     111              : 
     112              :     /** Construct a timer from an execution context.
     113              : 
     114              :         @param ctx The execution context that will own this timer.
     115              :     */
     116              :     explicit timer(capy::execution_context& ctx);
     117              : 
     118              :     /** Construct a timer with an initial absolute expiry time.
     119              : 
     120              :         @param ctx The execution context that will own this timer.
     121              :         @param t The initial expiry time point.
     122              :     */
     123              :     timer(capy::execution_context& ctx, time_point t);
     124              : 
     125              :     /** Construct a timer with an initial relative expiry time.
     126              : 
     127              :         @param ctx The execution context that will own this timer.
     128              :         @param d The initial expiry duration relative to now.
     129              :     */
     130              :     template<class Rep, class Period>
     131            2 :     timer(
     132              :         capy::execution_context& ctx,
     133              :         std::chrono::duration<Rep, Period> d)
     134            2 :         : timer(ctx)
     135              :     {
     136            2 :         expires_after(d);
     137            2 :     }
     138              : 
     139              :     /** Move constructor.
     140              : 
     141              :         Transfers ownership of the timer resources.
     142              : 
     143              :         @param other The timer to move from.
     144              :     */
     145              :     timer(timer&& other) noexcept;
     146              : 
     147              :     /** Move assignment operator.
     148              : 
     149              :         Closes any existing timer and transfers ownership.
     150              :         The source and destination must share the same execution context.
     151              : 
     152              :         @param other The timer to move from.
     153              : 
     154              :         @return Reference to this timer.
     155              : 
     156              :         @throws std::logic_error if the timers have different execution contexts.
     157              :     */
     158              :     timer& operator=(timer&& other);
     159              : 
     160              :     timer(timer const&) = delete;
     161              :     timer& operator=(timer const&) = delete;
     162              : 
     163              :     /** Cancel all pending asynchronous wait operations.
     164              : 
     165              :         All outstanding operations complete with an error code that
     166              :         compares equal to `capy::cond::canceled`.
     167              : 
     168              :         @return The number of operations that were cancelled.
     169              :     */
     170              :     std::size_t cancel();
     171              : 
     172              :     /** Cancel one pending asynchronous wait operation.
     173              : 
     174              :         The oldest pending wait is cancelled (FIFO order). It
     175              :         completes with an error code that compares equal to
     176              :         `capy::cond::canceled`.
     177              : 
     178              :         @return The number of operations that were cancelled (0 or 1).
     179              :     */
     180              :     std::size_t cancel_one();
     181              : 
     182              :     /** Return the timer's expiry time as an absolute time.
     183              : 
     184              :         @return The expiry time point. If no expiry has been set,
     185              :             returns a default-constructed time_point.
     186              :     */
     187              :     time_point expiry() const;
     188              : 
     189              :     /** Set the timer's expiry time as an absolute time.
     190              : 
     191              :         Any pending asynchronous wait operations will be cancelled.
     192              : 
     193              :         @param t The expiry time to be used for the timer.
     194              : 
     195              :         @return The number of pending operations that were cancelled.
     196              :     */
     197              :     std::size_t expires_at(time_point t);
     198              : 
     199              :     /** Set the timer's expiry time relative to now.
     200              : 
     201              :         Any pending asynchronous wait operations will be cancelled.
     202              : 
     203              :         @param d The expiry time relative to now.
     204              : 
     205              :         @return The number of pending operations that were cancelled.
     206              :     */
     207              :     std::size_t expires_after(duration d);
     208              : 
     209              :     /** Set the timer's expiry time relative to now.
     210              : 
     211              :         This is a convenience overload that accepts any duration type
     212              :         and converts it to the timer's native duration type. Any
     213              :         pending asynchronous wait operations will be cancelled.
     214              : 
     215              :         @param d The expiry time relative to now.
     216              : 
     217              :         @return The number of pending operations that were cancelled.
     218              :     */
     219              :     template<class Rep, class Period>
     220         8769 :     std::size_t expires_after(std::chrono::duration<Rep, Period> d)
     221              :     {
     222         8769 :         return expires_after(std::chrono::duration_cast<duration>(d));
     223              :     }
     224              : 
     225              :     /** Wait for the timer to expire.
     226              : 
     227              :         Multiple coroutines may wait on the same timer concurrently.
     228              :         When the timer expires, all waiters complete with success.
     229              : 
     230              :         The operation supports cancellation via `std::stop_token` through
     231              :         the affine awaitable protocol. If the associated stop token is
     232              :         triggered, only that waiter completes with an error that
     233              :         compares equal to `capy::cond::canceled`; other waiters are
     234              :         unaffected.
     235              : 
     236              :         @par Example
     237              :         @code
     238              :         timer t(ctx);
     239              :         t.expires_after(std::chrono::seconds(5));
     240              :         auto [ec] = co_await t.wait();
     241              :         if (ec == capy::cond::canceled)
     242              :         {
     243              :             // Cancelled via stop_token or cancel()
     244              :             co_return;
     245              :         }
     246              :         if (ec)
     247              :         {
     248              :             // Handle other errors
     249              :             co_return;
     250              :         }
     251              :         // Timer expired
     252              :         @endcode
     253              : 
     254              :         @return An awaitable that completes with `io_result<>`.
     255              :             Returns success (default error_code) when the timer expires,
     256              :             or an error code on failure. Compare against error conditions
     257              :             (e.g., `ec == capy::cond::canceled`) rather than error codes.
     258              : 
     259              :         @par Preconditions
     260              :         The timer must have an expiry time set via expires_at() or
     261              :         expires_after().
     262              :     */
     263         8769 :     auto wait()
     264              :     {
     265         8769 :         return wait_awaitable(*this);
     266              :     }
     267              : 
     268              : private:
     269        26394 :     timer_impl& get() const noexcept
     270              :     {
     271        26394 :         return *static_cast<timer_impl*>(impl_);
     272              :     }
     273              : };
     274              : 
     275              : } // namespace boost::corosio
     276              : 
     277              : #endif
        

Generated by: LCOV version 2.3