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_TIMER_HPP
11  
#ifndef BOOST_COROSIO_TIMER_HPP
12  
#define BOOST_COROSIO_TIMER_HPP
12  
#define BOOST_COROSIO_TIMER_HPP
13  

13  

14  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/config.hpp>
15  
#include <boost/corosio/io/io_timer.hpp>
15  
#include <boost/corosio/io/io_timer.hpp>
16  
#include <boost/capy/ex/execution_context.hpp>
16  
#include <boost/capy/ex/execution_context.hpp>
17  
#include <boost/capy/concept/executor.hpp>
17  
#include <boost/capy/concept/executor.hpp>
18  

18  

19  
#include <chrono>
19  
#include <chrono>
20  
#include <cstddef>
20  
#include <cstddef>
21  

21  

22  
namespace boost::corosio {
22  
namespace boost::corosio {
23  

23  

24  
/** An asynchronous timer for coroutine I/O.
24  
/** An asynchronous timer for coroutine I/O.
25  

25  

26  
    This class provides asynchronous timer operations that return
26  
    This class provides asynchronous timer operations that return
27  
    awaitable types. The timer can be used to schedule operations
27  
    awaitable types. The timer can be used to schedule operations
28  
    to occur after a specified duration or at a specific time point.
28  
    to occur after a specified duration or at a specific time point.
29  

29  

30  
    Multiple coroutines may wait concurrently on the same timer.
30  
    Multiple coroutines may wait concurrently on the same timer.
31  
    When the timer expires, all waiters complete with success. When
31  
    When the timer expires, all waiters complete with success. When
32  
    the timer is cancelled, all waiters complete with an error that
32  
    the timer is cancelled, all waiters complete with an error that
33  
    compares equal to `capy::cond::canceled`.
33  
    compares equal to `capy::cond::canceled`.
34  

34  

35  
    Each timer operation participates in the affine awaitable protocol,
35  
    Each timer operation participates in the affine awaitable protocol,
36  
    ensuring coroutines resume on the correct executor.
36  
    ensuring coroutines resume on the correct executor.
37  

37  

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

41  

42  
    @par Semantics
42  
    @par Semantics
43  
    Wraps platform timer facilities via the io_context reactor.
43  
    Wraps platform timer facilities via the io_context reactor.
44  
    Operations dispatch to OS timer APIs (timerfd, IOCP timers,
44  
    Operations dispatch to OS timer APIs (timerfd, IOCP timers,
45  
    kqueue EVFILT_TIMER).
45  
    kqueue EVFILT_TIMER).
46  
*/
46  
*/
47  
class BOOST_COROSIO_DECL timer : public io_timer
47  
class BOOST_COROSIO_DECL timer : public io_timer
48  
{
48  
{
49  
public:
49  
public:
50  
    /// Alias for backward compatibility.
50  
    /// Alias for backward compatibility.
51  
    using implementation = io_timer::implementation;
51  
    using implementation = io_timer::implementation;
52  

52  

53  
    /** Destructor.
53  
    /** Destructor.
54  

54  

55  
        Cancels any pending operations and releases timer resources.
55  
        Cancels any pending operations and releases timer resources.
56  
    */
56  
    */
57  
    ~timer() override;
57  
    ~timer() override;
58  

58  

59  
    /** Construct a timer from an execution context.
59  
    /** Construct a timer from an execution context.
60  

60  

61  
        @param ctx The execution context that will own this timer.
61  
        @param ctx The execution context that will own this timer.
62  
    */
62  
    */
63  
    explicit timer(capy::execution_context& ctx);
63  
    explicit timer(capy::execution_context& ctx);
64  

64  

65  
    /** Construct a timer with an initial absolute expiry time.
65  
    /** Construct a timer with an initial absolute expiry time.
66  

66  

67  
        @param ctx The execution context that will own this timer.
67  
        @param ctx The execution context that will own this timer.
68  
        @param t The initial expiry time point.
68  
        @param t The initial expiry time point.
69  
    */
69  
    */
70  
    timer(capy::execution_context& ctx, time_point t);
70  
    timer(capy::execution_context& ctx, time_point t);
71  

71  

72  
    /** Construct a timer with an initial relative expiry time.
72  
    /** Construct a timer with an initial relative expiry time.
73  

73  

74  
        @param ctx The execution context that will own this timer.
74  
        @param ctx The execution context that will own this timer.
75  
        @param d The initial expiry duration relative to now.
75  
        @param d The initial expiry duration relative to now.
76  
    */
76  
    */
77  
    template<class Rep, class Period>
77  
    template<class Rep, class Period>
78  
    timer(capy::execution_context& ctx, std::chrono::duration<Rep, Period> d)
78  
    timer(capy::execution_context& ctx, std::chrono::duration<Rep, Period> d)
79  
        : timer(ctx)
79  
        : timer(ctx)
80  
    {
80  
    {
81  
        expires_after(d);
81  
        expires_after(d);
82  
    }
82  
    }
83  

83  

84  
    /** Move constructor.
84  
    /** Move constructor.
85  

85  

86  
        Transfers ownership of the timer resources.
86  
        Transfers ownership of the timer resources.
87  

87  

88  
        @param other The timer to move from.
88  
        @param other The timer to move from.
89  

89  

90  
        @pre No awaitables returned by @p other's methods exist.
90  
        @pre No awaitables returned by @p other's methods exist.
91  
        @pre The execution context associated with @p other must
91  
        @pre The execution context associated with @p other must
92  
            outlive this timer.
92  
            outlive this timer.
93  
    */
93  
    */
94  
    timer(timer&& other) noexcept;
94  
    timer(timer&& other) noexcept;
95  

95  

96  
    /** Move assignment operator.
96  
    /** Move assignment operator.
97  

97  

98  
        Closes any existing timer and transfers ownership.
98  
        Closes any existing timer and transfers ownership.
99  

99  

100  
        @param other The timer to move from.
100  
        @param other The timer to move from.
101  

101  

102  
        @pre No awaitables returned by either `*this` or @p other's
102  
        @pre No awaitables returned by either `*this` or @p other's
103  
            methods exist.
103  
            methods exist.
104  
        @pre The execution context associated with @p other must
104  
        @pre The execution context associated with @p other must
105  
            outlive this timer.
105  
            outlive this timer.
106  

106  

107  
        @return Reference to this timer.
107  
        @return Reference to this timer.
108  
    */
108  
    */
109  
    timer& operator=(timer&& other) noexcept;
109  
    timer& operator=(timer&& other) noexcept;
110  

110  

111  
    timer(timer const&)            = delete;
111  
    timer(timer const&)            = delete;
112  
    timer& operator=(timer const&) = delete;
112  
    timer& operator=(timer const&) = delete;
113  

113  

114  
    /** Cancel one pending asynchronous wait operation.
114  
    /** Cancel one pending asynchronous wait operation.
115  

115  

116  
        The oldest pending wait is cancelled (FIFO order). It
116  
        The oldest pending wait is cancelled (FIFO order). It
117  
        completes with an error code that compares equal to
117  
        completes with an error code that compares equal to
118  
        `capy::cond::canceled`.
118  
        `capy::cond::canceled`.
119  

119  

120  
        @return The number of operations that were cancelled (0 or 1).
120  
        @return The number of operations that were cancelled (0 or 1).
121  
    */
121  
    */
122  
    std::size_t cancel_one()
122  
    std::size_t cancel_one()
123  
    {
123  
    {
124  
        if (!get().might_have_pending_waits_)
124  
        if (!get().might_have_pending_waits_)
125  
            return 0;
125  
            return 0;
126  
        return do_cancel_one();
126  
        return do_cancel_one();
127  
    }
127  
    }
128  

128  

129  
    /** Set the timer's expiry time as an absolute time.
129  
    /** Set the timer's expiry time as an absolute time.
130  

130  

131  
        Any pending asynchronous wait operations will be cancelled.
131  
        Any pending asynchronous wait operations will be cancelled.
132  

132  

133  
        @param t The expiry time to be used for the timer.
133  
        @param t The expiry time to be used for the timer.
134  

134  

135  
        @return The number of pending operations that were cancelled.
135  
        @return The number of pending operations that were cancelled.
136  
    */
136  
    */
137  
    std::size_t expires_at(time_point t)
137  
    std::size_t expires_at(time_point t)
138  
    {
138  
    {
139  
        auto& impl   = get();
139  
        auto& impl   = get();
140  
        impl.expiry_ = t;
140  
        impl.expiry_ = t;
141  
        if (impl.heap_index_ == implementation::npos &&
141  
        if (impl.heap_index_ == implementation::npos &&
142  
            !impl.might_have_pending_waits_)
142  
            !impl.might_have_pending_waits_)
143  
            return 0;
143  
            return 0;
144  
        return do_update_expiry();
144  
        return do_update_expiry();
145  
    }
145  
    }
146  

146  

147  
    /** Set the timer's expiry time relative to now.
147  
    /** Set the timer's expiry time relative to now.
148  

148  

149  
        Any pending asynchronous wait operations will be cancelled.
149  
        Any pending asynchronous wait operations will be cancelled.
150  

150  

151  
        @param d The expiry time relative to now.
151  
        @param d The expiry time relative to now.
152  

152  

153  
        @return The number of pending operations that were cancelled.
153  
        @return The number of pending operations that were cancelled.
154  
    */
154  
    */
155  
    std::size_t expires_after(duration d)
155  
    std::size_t expires_after(duration d)
156  
    {
156  
    {
157  
        auto& impl = get();
157  
        auto& impl = get();
158  
        if (d <= duration::zero())
158  
        if (d <= duration::zero())
159  
            impl.expiry_ = (time_point::min)();
159  
            impl.expiry_ = (time_point::min)();
160  
        else
160  
        else
161  
            impl.expiry_ = clock_type::now() + d;
161  
            impl.expiry_ = clock_type::now() + d;
162  
        if (impl.heap_index_ == implementation::npos &&
162  
        if (impl.heap_index_ == implementation::npos &&
163  
            !impl.might_have_pending_waits_)
163  
            !impl.might_have_pending_waits_)
164  
            return 0;
164  
            return 0;
165  
        return do_update_expiry();
165  
        return do_update_expiry();
166  
    }
166  
    }
167  

167  

168  
    /** Set the timer's expiry time relative to now.
168  
    /** Set the timer's expiry time relative to now.
169  

169  

170  
        This is a convenience overload that accepts any duration type
170  
        This is a convenience overload that accepts any duration type
171  
        and converts it to the timer's native duration type. Any
171  
        and converts it to the timer's native duration type. Any
172  
        pending asynchronous wait operations will be cancelled.
172  
        pending asynchronous wait operations will be cancelled.
173  

173  

174  
        @param d The expiry time relative to now.
174  
        @param d The expiry time relative to now.
175  

175  

176  
        @return The number of pending operations that were cancelled.
176  
        @return The number of pending operations that were cancelled.
177  
    */
177  
    */
178  
    template<class Rep, class Period>
178  
    template<class Rep, class Period>
179  
    std::size_t expires_after(std::chrono::duration<Rep, Period> d)
179  
    std::size_t expires_after(std::chrono::duration<Rep, Period> d)
180  
    {
180  
    {
181  
        return expires_after(std::chrono::duration_cast<duration>(d));
181  
        return expires_after(std::chrono::duration_cast<duration>(d));
182  
    }
182  
    }
183  

183  

184  
protected:
184  
protected:
185  
    explicit timer(handle h) noexcept : io_timer(std::move(h)) {}
185  
    explicit timer(handle h) noexcept : io_timer(std::move(h)) {}
186  

186  

187  
private:
187  
private:
188  
    std::size_t do_cancel() override;
188  
    std::size_t do_cancel() override;
189  
    std::size_t do_cancel_one();
189  
    std::size_t do_cancel_one();
190  
    std::size_t do_update_expiry();
190  
    std::size_t do_update_expiry();
191  

191  

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

198  

199  
} // namespace boost::corosio
199  
} // namespace boost::corosio
200  

200  

201  
#endif
201  
#endif