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  
    timer(timer&& other) noexcept;
90  
    timer(timer&& other) noexcept;
91  

91  

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

93  

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

95  

96  
        @param other The timer to move from.
96  
        @param other The timer to move from.
97  

97  

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

101  

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

104  

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

106  

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

110  

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

119  

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

121  

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

123  

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

125  

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

137  

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

139  

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

141  

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

143  

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

158  

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

160  

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

164  

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

166  

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

174  

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

177  

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

182  

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

189  

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

191  

192  
#endif
192  
#endif