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  
                auto d = env->executor;
75  
                auto d = env->executor;
76  
                d.post(h);
76  
                d.post(h);
77  
                return std::noop_coroutine();
77  
                return std::noop_coroutine();
78  
            }
78  
            }
79  
            return impl.wait(h, env->executor, std::move(token_), &ec_);
79  
            return impl.wait(h, env->executor, std::move(token_), &ec_);
80  
        }
80  
        }
81  
    };
81  
    };
82  

82  

83  
public:
83  
public:
84  
    struct implementation : io_object::implementation
84  
    struct implementation : io_object::implementation
85  
    {
85  
    {
86  
        static constexpr std::size_t npos =
86  
        static constexpr std::size_t npos =
87  
            (std::numeric_limits<std::size_t>::max)();
87  
            (std::numeric_limits<std::size_t>::max)();
88  

88  

89  
        std::chrono::steady_clock::time_point expiry_{};
89  
        std::chrono::steady_clock::time_point expiry_{};
90  
        std::size_t heap_index_        = npos;
90  
        std::size_t heap_index_        = npos;
91  
        bool might_have_pending_waits_ = false;
91  
        bool might_have_pending_waits_ = false;
92  

92  

93  
        virtual std::coroutine_handle<> wait(
93  
        virtual std::coroutine_handle<> wait(
94  
            std::coroutine_handle<>,
94  
            std::coroutine_handle<>,
95  
            capy::executor_ref,
95  
            capy::executor_ref,
96  
            std::stop_token,
96  
            std::stop_token,
97  
            std::error_code*) = 0;
97  
            std::error_code*) = 0;
98  
    };
98  
    };
99  

99  

100  
    /// The clock type used for time operations.
100  
    /// The clock type used for time operations.
101  
    using clock_type = std::chrono::steady_clock;
101  
    using clock_type = std::chrono::steady_clock;
102  

102  

103  
    /// The time point type for absolute expiry times.
103  
    /// The time point type for absolute expiry times.
104  
    using time_point = clock_type::time_point;
104  
    using time_point = clock_type::time_point;
105  

105  

106  
    /// The duration type for relative expiry times.
106  
    /// The duration type for relative expiry times.
107  
    using duration = clock_type::duration;
107  
    using duration = clock_type::duration;
108  

108  

109  
    /** Cancel all pending asynchronous wait operations.
109  
    /** Cancel all pending asynchronous wait operations.
110  

110  

111  
        All outstanding operations complete with an error code that
111  
        All outstanding operations complete with an error code that
112  
        compares equal to `capy::cond::canceled`.
112  
        compares equal to `capy::cond::canceled`.
113  

113  

114  
        @return The number of operations that were cancelled.
114  
        @return The number of operations that were cancelled.
115  
    */
115  
    */
116  
    std::size_t cancel()
116  
    std::size_t cancel()
117  
    {
117  
    {
118  
        if (!get().might_have_pending_waits_)
118  
        if (!get().might_have_pending_waits_)
119  
            return 0;
119  
            return 0;
120  
        return do_cancel();
120  
        return do_cancel();
121  
    }
121  
    }
122  

122  

123  
    /** Return the timer's expiry time as an absolute time.
123  
    /** Return the timer's expiry time as an absolute time.
124  

124  

125  
        @return The expiry time point. If no expiry has been set,
125  
        @return The expiry time point. If no expiry has been set,
126  
            returns a default-constructed time_point.
126  
            returns a default-constructed time_point.
127  
    */
127  
    */
128  
    time_point expiry() const noexcept
128  
    time_point expiry() const noexcept
129  
    {
129  
    {
130  
        return get().expiry_;
130  
        return get().expiry_;
131  
    }
131  
    }
132  

132  

133  
    /** Wait for the timer to expire.
133  
    /** Wait for the timer to expire.
134  

134  

135  
        Multiple coroutines may wait on the same timer concurrently.
135  
        Multiple coroutines may wait on the same timer concurrently.
136  
        When the timer expires, all waiters complete with success.
136  
        When the timer expires, all waiters complete with success.
137  

137  

138  
        The operation supports cancellation via `std::stop_token` through
138  
        The operation supports cancellation via `std::stop_token` through
139  
        the affine awaitable protocol. If the associated stop token is
139  
        the affine awaitable protocol. If the associated stop token is
140  
        triggered, only that waiter completes with an error that
140  
        triggered, only that waiter completes with an error that
141  
        compares equal to `capy::cond::canceled`; other waiters are
141  
        compares equal to `capy::cond::canceled`; other waiters are
142  
        unaffected.
142  
        unaffected.
143  

143  

 
144 +
        This timer must outlive the returned awaitable.
 
145 +

144  
        @return An awaitable that completes with `io_result<>`.
146  
        @return An awaitable that completes with `io_result<>`.
145  
    */
147  
    */
146  
    auto wait()
148  
    auto wait()
147  
    {
149  
    {
148  
        return wait_awaitable(*this);
150  
        return wait_awaitable(*this);
149  
    }
151  
    }
150  

152  

151  
protected:
153  
protected:
152  
    /** Dispatch cancel to the concrete implementation.
154  
    /** Dispatch cancel to the concrete implementation.
153  

155  

154  
        @return The number of operations that were cancelled.
156  
        @return The number of operations that were cancelled.
155  
    */
157  
    */
156  
    virtual std::size_t do_cancel() = 0;
158  
    virtual std::size_t do_cancel() = 0;
157  

159  

158  
    explicit io_timer(handle h) noexcept : io_object(std::move(h)) {}
160  
    explicit io_timer(handle h) noexcept : io_object(std::move(h)) {}
159  

161  

160  
    /// Move construct.
162  
    /// Move construct.
161  
    io_timer(io_timer&& other) noexcept : io_object(std::move(other)) {}
163  
    io_timer(io_timer&& other) noexcept : io_object(std::move(other)) {}
162  

164  

163  
    /// Move assign.
165  
    /// Move assign.
164  
    io_timer& operator=(io_timer&& other) noexcept
166  
    io_timer& operator=(io_timer&& other) noexcept
165  
    {
167  
    {
166  
        if (this != &other)
168  
        if (this != &other)
167  
            h_ = std::move(other.h_);
169  
            h_ = std::move(other.h_);
168  
        return *this;
170  
        return *this;
169  
    }
171  
    }
170  

172  

171  
    io_timer(io_timer const&)            = delete;
173  
    io_timer(io_timer const&)            = delete;
172  
    io_timer& operator=(io_timer const&) = delete;
174  
    io_timer& operator=(io_timer const&) = delete;
173  

175  

174  
    /// Return the underlying implementation.
176  
    /// Return the underlying implementation.
175  
    implementation& get() const noexcept
177  
    implementation& get() const noexcept
176  
    {
178  
    {
177  
        return *static_cast<implementation*>(h_.get());
179  
        return *static_cast<implementation*>(h_.get());
178  
    }
180  
    }
179  
};
181  
};
180  

182  

181  
} // namespace boost::corosio
183  
} // namespace boost::corosio
182  

184  

183  
#endif
185  
#endif