1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2026 Steve Gerbino
3  
// Copyright (c) 2026 Steve Gerbino
4  
//
4  
//
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
//
7  
//
8  
// Official repository: https://github.com/cppalliance/corosio
8  
// Official repository: https://github.com/cppalliance/corosio
9  
//
9  
//
10  

10  

11  
#ifndef BOOST_COROSIO_IO_IO_TIMER_HPP
11  
#ifndef BOOST_COROSIO_IO_IO_TIMER_HPP
12  
#define BOOST_COROSIO_IO_IO_TIMER_HPP
12  
#define BOOST_COROSIO_IO_IO_TIMER_HPP
13  

13  

14  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/config.hpp>
15  
#include <boost/corosio/io/io_object.hpp>
15  
#include <boost/corosio/io/io_object.hpp>
16  
#include <boost/capy/io_result.hpp>
16  
#include <boost/capy/io_result.hpp>
17  
#include <boost/capy/error.hpp>
17  
#include <boost/capy/error.hpp>
18  
#include <boost/capy/ex/executor_ref.hpp>
18  
#include <boost/capy/ex/executor_ref.hpp>
19  
#include <boost/capy/ex/io_env.hpp>
19  
#include <boost/capy/ex/io_env.hpp>
20  

20  

21  
#include <chrono>
21  
#include <chrono>
22  
#include <coroutine>
22  
#include <coroutine>
23  
#include <cstddef>
23  
#include <cstddef>
24  
#include <limits>
24  
#include <limits>
25  
#include <stop_token>
25  
#include <stop_token>
26  
#include <system_error>
26  
#include <system_error>
27  

27  

28  
namespace boost::corosio {
28  
namespace boost::corosio {
29  

29  

30  
/** Abstract base for asynchronous timers.
30  
/** Abstract base for asynchronous timers.
31  

31  

32  
    Provides the common timer interface: `wait`, `cancel`, and
32  
    Provides the common timer interface: `wait`, `cancel`, and
33  
    `expiry`. Concrete classes like @ref timer add the ability
33  
    `expiry`. Concrete classes like @ref timer add the ability
34  
    to set expiry times and cancel individual waiters.
34  
    to set expiry times and cancel individual waiters.
35  

35  

36  
    @par Thread Safety
36  
    @par Thread Safety
37  
    Distinct objects: Safe.
37  
    Distinct objects: Safe.
38  
    Shared objects: Unsafe.
38  
    Shared objects: Unsafe.
39  

39  

40  
    @see timer, io_object
40  
    @see timer, io_object
41  
*/
41  
*/
42  
class BOOST_COROSIO_DECL io_timer : public io_object
42  
class BOOST_COROSIO_DECL io_timer : public io_object
43  
{
43  
{
44  
    struct wait_awaitable
44  
    struct wait_awaitable
45  
    {
45  
    {
46  
        io_timer& t_;
46  
        io_timer& t_;
47  
        std::stop_token token_;
47  
        std::stop_token token_;
48  
        mutable std::error_code ec_;
48  
        mutable std::error_code ec_;
49  

49  

50  
        explicit wait_awaitable(io_timer& t) noexcept : t_(t) {}
50  
        explicit wait_awaitable(io_timer& t) noexcept : t_(t) {}
51  

51  

52  
        bool await_ready() const noexcept
52  
        bool await_ready() const noexcept
53  
        {
53  
        {
54  
            return token_.stop_requested();
54  
            return token_.stop_requested();
55  
        }
55  
        }
56  

56  

57  
        capy::io_result<> await_resume() const noexcept
57  
        capy::io_result<> await_resume() const noexcept
58  
        {
58  
        {
59  
            if (token_.stop_requested())
59  
            if (token_.stop_requested())
60  
                return {capy::error::canceled};
60  
                return {capy::error::canceled};
61  
            return {ec_};
61  
            return {ec_};
62  
        }
62  
        }
63  

63  

64  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
64  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
65  
            -> std::coroutine_handle<>
65  
            -> std::coroutine_handle<>
66  
        {
66  
        {
67  
            token_     = env->stop_token;
67  
            token_     = env->stop_token;
68  
            auto& impl = t_.get();
68  
            auto& impl = t_.get();
69  
            // Inline fast path: already expired and not in the heap
69  
            // Inline fast path: already expired and not in the heap
70  
            if (impl.heap_index_ == implementation::npos &&
70  
            if (impl.heap_index_ == implementation::npos &&
71  
                (impl.expiry_ == (time_point::min)() ||
71  
                (impl.expiry_ == (time_point::min)() ||
72  
                 impl.expiry_ <= clock_type::now()))
72  
                 impl.expiry_ <= clock_type::now()))
73  
            {
73  
            {
74  
                ec_    = {};
74  
                ec_    = {};
75  
                token_ = {};  // match normal path so await_resume
75  
                token_ = {};  // match normal path so await_resume
76  
                              // returns ec_, not a stale stop check
76  
                              // returns ec_, not a stale stop check
77  
                auto d = env->executor;
77  
                auto d = env->executor;
78  
                d.post(h);
78  
                d.post(h);
79  
                return std::noop_coroutine();
79  
                return std::noop_coroutine();
80  
            }
80  
            }
81  
            return impl.wait(h, env->executor, std::move(token_), &ec_);
81  
            return impl.wait(h, env->executor, std::move(token_), &ec_);
82  
        }
82  
        }
83  
    };
83  
    };
84  

84  

85  
public:
85  
public:
86  
    /** Backend interface for timer wait operations.
86  
    /** Backend interface for timer wait operations.
87  

87  

88  
        Holds per-timer state (expiry, heap position) and provides
88  
        Holds per-timer state (expiry, heap position) and provides
89  
        the virtual `wait` entry point that concrete timer services
89  
        the virtual `wait` entry point that concrete timer services
90  
        override.
90  
        override.
91  
    */
91  
    */
92  
    struct implementation : io_object::implementation
92  
    struct implementation : io_object::implementation
93  
    {
93  
    {
94  
        /// Sentinel value indicating the timer is not in the heap.
94  
        /// Sentinel value indicating the timer is not in the heap.
95  
        static constexpr std::size_t npos =
95  
        static constexpr std::size_t npos =
96  
            (std::numeric_limits<std::size_t>::max)();
96  
            (std::numeric_limits<std::size_t>::max)();
97  

97  

98  
        /// The absolute expiry time point.
98  
        /// The absolute expiry time point.
99  
        std::chrono::steady_clock::time_point expiry_{};
99  
        std::chrono::steady_clock::time_point expiry_{};
100  

100  

101  
        /// Index in the timer service's min-heap, or `npos`.
101  
        /// Index in the timer service's min-heap, or `npos`.
102  
        std::size_t heap_index_ = npos;
102  
        std::size_t heap_index_ = npos;
103  

103  

104  
        /// True if `wait()` has been called since last cancel.
104  
        /// True if `wait()` has been called since last cancel.
105  
        bool might_have_pending_waits_ = false;
105  
        bool might_have_pending_waits_ = false;
106  

106  

107  
        /// Initiate an asynchronous wait for the timer to expire.
107  
        /// Initiate an asynchronous wait for the timer to expire.
108  
        virtual std::coroutine_handle<> wait(
108  
        virtual std::coroutine_handle<> wait(
109  
            std::coroutine_handle<>,
109  
            std::coroutine_handle<>,
110  
            capy::executor_ref,
110  
            capy::executor_ref,
111  
            std::stop_token,
111  
            std::stop_token,
112  
            std::error_code*) = 0;
112  
            std::error_code*) = 0;
113  
    };
113  
    };
114  

114  

115  
    /// The clock type used for time operations.
115  
    /// The clock type used for time operations.
116  
    using clock_type = std::chrono::steady_clock;
116  
    using clock_type = std::chrono::steady_clock;
117  

117  

118  
    /// The time point type for absolute expiry times.
118  
    /// The time point type for absolute expiry times.
119  
    using time_point = clock_type::time_point;
119  
    using time_point = clock_type::time_point;
120  

120  

121  
    /// The duration type for relative expiry times.
121  
    /// The duration type for relative expiry times.
122  
    using duration = clock_type::duration;
122  
    using duration = clock_type::duration;
123  

123  

124  
    /** Cancel all pending asynchronous wait operations.
124  
    /** Cancel all pending asynchronous wait operations.
125  

125  

126  
        All outstanding operations complete with an error code that
126  
        All outstanding operations complete with an error code that
127  
        compares equal to `capy::cond::canceled`.
127  
        compares equal to `capy::cond::canceled`.
128  

128  

129  
        @return The number of operations that were cancelled.
129  
        @return The number of operations that were cancelled.
130  
    */
130  
    */
131  
    std::size_t cancel()
131  
    std::size_t cancel()
132  
    {
132  
    {
133  
        if (!get().might_have_pending_waits_)
133  
        if (!get().might_have_pending_waits_)
134  
            return 0;
134  
            return 0;
135  
        return do_cancel();
135  
        return do_cancel();
136  
    }
136  
    }
137  

137  

138  
    /** Return the timer's expiry time as an absolute time.
138  
    /** Return the timer's expiry time as an absolute time.
139  

139  

140  
        @return The expiry time point. If no expiry has been set,
140  
        @return The expiry time point. If no expiry has been set,
141  
            returns a default-constructed time_point.
141  
            returns a default-constructed time_point.
142  
    */
142  
    */
143  
    time_point expiry() const noexcept
143  
    time_point expiry() const noexcept
144  
    {
144  
    {
145  
        return get().expiry_;
145  
        return get().expiry_;
146  
    }
146  
    }
147  

147  

148  
    /** Wait for the timer to expire.
148  
    /** Wait for the timer to expire.
149  

149  

150  
        Multiple coroutines may wait on the same timer concurrently.
150  
        Multiple coroutines may wait on the same timer concurrently.
151  
        When the timer expires, all waiters complete with success.
151  
        When the timer expires, all waiters complete with success.
152  

152  

153  
        The operation supports cancellation via `std::stop_token` through
153  
        The operation supports cancellation via `std::stop_token` through
154  
        the affine awaitable protocol. If the associated stop token is
154  
        the affine awaitable protocol. If the associated stop token is
155  
        triggered, only that waiter completes with an error that
155  
        triggered, only that waiter completes with an error that
156  
        compares equal to `capy::cond::canceled`; other waiters are
156  
        compares equal to `capy::cond::canceled`; other waiters are
157  
        unaffected.
157  
        unaffected.
158  

158  

159  
        This timer must outlive the returned awaitable.
159  
        This timer must outlive the returned awaitable.
160  

160  

161  
        @return An awaitable that completes with `io_result<>`.
161  
        @return An awaitable that completes with `io_result<>`.
162  
    */
162  
    */
163  
    auto wait()
163  
    auto wait()
164  
    {
164  
    {
165  
        return wait_awaitable(*this);
165  
        return wait_awaitable(*this);
166  
    }
166  
    }
167  

167  

168  
protected:
168  
protected:
169  
    /** Dispatch cancel to the concrete implementation.
169  
    /** Dispatch cancel to the concrete implementation.
170  

170  

171  
        @return The number of operations that were cancelled.
171  
        @return The number of operations that were cancelled.
172  
    */
172  
    */
173  
    virtual std::size_t do_cancel() = 0;
173  
    virtual std::size_t do_cancel() = 0;
174  

174  

175  
    explicit io_timer(handle h) noexcept : io_object(std::move(h)) {}
175  
    explicit io_timer(handle h) noexcept : io_object(std::move(h)) {}
176  

176  

177  
    /// Move construct.
177  
    /// Move construct.
178  
    io_timer(io_timer&& other) noexcept : io_object(std::move(other)) {}
178  
    io_timer(io_timer&& other) noexcept : io_object(std::move(other)) {}
179  

179  

180  
    /// Move assign.
180  
    /// Move assign.
181  
    io_timer& operator=(io_timer&& other) noexcept
181  
    io_timer& operator=(io_timer&& other) noexcept
182  
    {
182  
    {
183  
        if (this != &other)
183  
        if (this != &other)
184  
            h_ = std::move(other.h_);
184  
            h_ = std::move(other.h_);
185  
        return *this;
185  
        return *this;
186  
    }
186  
    }
187  

187  

188  
    io_timer(io_timer const&)            = delete;
188  
    io_timer(io_timer const&)            = delete;
189  
    io_timer& operator=(io_timer const&) = delete;
189  
    io_timer& operator=(io_timer const&) = delete;
190  

190  

191  
    /// Return the underlying implementation.
191  
    /// Return the underlying implementation.
192  
    implementation& get() const noexcept
192  
    implementation& get() const noexcept
193  
    {
193  
    {
194  
        return *static_cast<implementation*>(h_.get());
194  
        return *static_cast<implementation*>(h_.get());
195  
    }
195  
    }
196  
};
196  
};
197  

197  

198  
} // namespace boost::corosio
198  
} // namespace boost::corosio
199  

199  

200  
#endif
200  
#endif