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 +

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

95  

92  
    /** Move assignment operator.
96  
    /** Move assignment operator.
93  

97  

94  
        Closes any existing timer and transfers ownership.
98  
        Closes any existing timer and transfers ownership.
95  

99  

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

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

106  

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

110  

102  
    timer(timer const&)            = delete;
111  
    timer(timer const&)            = delete;
103  
    timer& operator=(timer const&) = delete;
112  
    timer& operator=(timer const&) = delete;
104  

113  

105  
    /** Cancel one pending asynchronous wait operation.
114  
    /** Cancel one pending asynchronous wait operation.
106  

115  

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

119  

111  
        @return The number of operations that were cancelled (0 or 1).
120  
        @return The number of operations that were cancelled (0 or 1).
112  
    */
121  
    */
113  
    std::size_t cancel_one()
122  
    std::size_t cancel_one()
114  
    {
123  
    {
115  
        if (!get().might_have_pending_waits_)
124  
        if (!get().might_have_pending_waits_)
116  
            return 0;
125  
            return 0;
117  
        return do_cancel_one();
126  
        return do_cancel_one();
118  
    }
127  
    }
119  

128  

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

130  

122  
        Any pending asynchronous wait operations will be cancelled.
131  
        Any pending asynchronous wait operations will be cancelled.
123  

132  

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

134  

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

146  

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

148  

140  
        Any pending asynchronous wait operations will be cancelled.
149  
        Any pending asynchronous wait operations will be cancelled.
141  

150  

142  
        @param d The expiry time relative to now.
151  
        @param d The expiry time relative to now.
143  

152  

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

167  

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

169  

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

173  

165  
        @param d The expiry time relative to now.
174  
        @param d The expiry time relative to now.
166  

175  

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

183  

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

186  

178  
private:
187  
private:
179  
    std::size_t do_cancel() override;
188  
    std::size_t do_cancel() override;
180  
    std::size_t do_cancel_one();
189  
    std::size_t do_cancel_one();
181  
    std::size_t do_update_expiry();
190  
    std::size_t do_update_expiry();
182  

191  

183  
    /// Return the underlying implementation.
192  
    /// Return the underlying implementation.
184  
    implementation& get() const noexcept
193  
    implementation& get() const noexcept
185  
    {
194  
    {
186  
        return *static_cast<implementation*>(h_.get());
195  
        return *static_cast<implementation*>(h_.get());
187  
    }
196  
    }
188  
};
197  
};
189  

198  

190  
} // namespace boost::corosio
199  
} // namespace boost::corosio
191  

200  

192  
#endif
201  
#endif