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/detail/continuation_op.hpp>
15  
#include <boost/corosio/detail/continuation_op.hpp>
16  
#include <boost/corosio/io/io_object.hpp>
16  
#include <boost/corosio/io/io_object.hpp>
17  
#include <boost/capy/io_result.hpp>
17  
#include <boost/capy/io_result.hpp>
18  
#include <boost/capy/error.hpp>
18  
#include <boost/capy/error.hpp>
19  
#include <boost/capy/ex/executor_ref.hpp>
19  
#include <boost/capy/ex/executor_ref.hpp>
20  
#include <boost/capy/ex/io_env.hpp>
20  
#include <boost/capy/ex/io_env.hpp>
21  

21  

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

28  

29  
namespace boost::corosio {
29  
namespace boost::corosio {
30  

30  

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

32  

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

36  

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

40  

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

51  

52  
        explicit wait_awaitable(io_timer& t) noexcept : t_(t) {}
52  
        explicit wait_awaitable(io_timer& t) noexcept : t_(t) {}
53  

53  

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

58  

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

65  

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

87  

88  
public:
88  
public:
89  
    /** Backend interface for timer wait operations.
89  
    /** Backend interface for timer wait operations.
90  

90  

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

100  

101  
        /// The absolute expiry time point.
101  
        /// The absolute expiry time point.
102  
        std::chrono::steady_clock::time_point expiry_{};
102  
        std::chrono::steady_clock::time_point expiry_{};
103  

103  

104  
        /// Index in the timer service's min-heap, or `npos`.
104  
        /// Index in the timer service's min-heap, or `npos`.
105  
        std::size_t heap_index_ = npos;
105  
        std::size_t heap_index_ = npos;
106  

106  

107  
        /// True if `wait()` has been called since last cancel.
107  
        /// True if `wait()` has been called since last cancel.
108  
        bool might_have_pending_waits_ = false;
108  
        bool might_have_pending_waits_ = false;
109  

109  

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

118  

119  
    /// The clock type used for time operations.
119  
    /// The clock type used for time operations.
120  
    using clock_type = std::chrono::steady_clock;
120  
    using clock_type = std::chrono::steady_clock;
121  

121  

122  
    /// The time point type for absolute expiry times.
122  
    /// The time point type for absolute expiry times.
123  
    using time_point = clock_type::time_point;
123  
    using time_point = clock_type::time_point;
124  

124  

125  
    /// The duration type for relative expiry times.
125  
    /// The duration type for relative expiry times.
126  
    using duration = clock_type::duration;
126  
    using duration = clock_type::duration;
127  

127  

128  
    /** Cancel all pending asynchronous wait operations.
128  
    /** Cancel all pending asynchronous wait operations.
129  

129  

130  
        All outstanding operations complete with an error code that
130  
        All outstanding operations complete with an error code that
131  
        compares equal to `capy::cond::canceled`.
131  
        compares equal to `capy::cond::canceled`.
132  

132  

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

141  

142  
    /** Return the timer's expiry time as an absolute time.
142  
    /** Return the timer's expiry time as an absolute time.
143  

143  

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

151  

152  
    /** Wait for the timer to expire.
152  
    /** Wait for the timer to expire.
153  

153  

154  
        Multiple coroutines may wait on the same timer concurrently.
154  
        Multiple coroutines may wait on the same timer concurrently.
155  
        When the timer expires, all waiters complete with success.
155  
        When the timer expires, all waiters complete with success.
156  

156  

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

162  

163  
        This timer must outlive the returned awaitable.
163  
        This timer must outlive the returned awaitable.
164  

164  

165  
        @return An awaitable that completes with `io_result<>`.
165  
        @return An awaitable that completes with `io_result<>`.
166  
    */
166  
    */
167  
    auto wait()
167  
    auto wait()
168  
    {
168  
    {
169  
        return wait_awaitable(*this);
169  
        return wait_awaitable(*this);
170  
    }
170  
    }
171  

171  

172  
protected:
172  
protected:
173  
    /** Dispatch cancel to the concrete implementation.
173  
    /** Dispatch cancel to the concrete implementation.
174  

174  

175  
        @return The number of operations that were cancelled.
175  
        @return The number of operations that were cancelled.
176  
    */
176  
    */
177  
    virtual std::size_t do_cancel() = 0;
177  
    virtual std::size_t do_cancel() = 0;
178  

178  

179  
    explicit io_timer(handle h) noexcept : io_object(std::move(h)) {}
179  
    explicit io_timer(handle h) noexcept : io_object(std::move(h)) {}
180  

180  

181  
    /// Move construct.
181  
    /// Move construct.
182  
    io_timer(io_timer&& other) noexcept : io_object(std::move(other)) {}
182  
    io_timer(io_timer&& other) noexcept : io_object(std::move(other)) {}
183  

183  

184  
    /// Move assign.
184  
    /// Move assign.
185  
    io_timer& operator=(io_timer&& other) noexcept
185  
    io_timer& operator=(io_timer&& other) noexcept
186  
    {
186  
    {
187  
        if (this != &other)
187  
        if (this != &other)
188  
            h_ = std::move(other.h_);
188  
            h_ = std::move(other.h_);
189  
        return *this;
189  
        return *this;
190  
    }
190  
    }
191  

191  

192  
    io_timer(io_timer const&)            = delete;
192  
    io_timer(io_timer const&)            = delete;
193  
    io_timer& operator=(io_timer const&) = delete;
193  
    io_timer& operator=(io_timer const&) = delete;
194  

194  

195  
    /// Return the underlying implementation.
195  
    /// Return the underlying implementation.
196  
    implementation& get() const noexcept
196  
    implementation& get() const noexcept
197  
    {
197  
    {
198  
        return *static_cast<implementation*>(h_.get());
198  
        return *static_cast<implementation*>(h_.get());
199  
    }
199  
    }
200  
};
200  
};
201  

201  

202  
} // namespace boost::corosio
202  
} // namespace boost::corosio
203  

203  

204  
#endif
204  
#endif