1 #pragma once
2 
3 #include <function2/function2.hpp>
4 #include <sdeventplus/clock.hpp>
5 #include <sdeventplus/event.hpp>
6 #include <sdeventplus/source/time.hpp>
7 #include <sdeventplus/types.hpp>
8 
9 #include <chrono>
10 #include <optional>
11 
12 namespace sdeventplus
13 {
14 namespace utility
15 {
16 
17 namespace detail
18 {
19 template <ClockId Id>
20 class TimerData;
21 } // namespace detail
22 
23 /** @class Timer<Id>
24  *  @brief A simple, repeating timer around an sd_event time source
25  *  @details Adds a timer to the SdEvent loop that runs a user defined callback
26  *           at specified intervals. If no interval is provided to the timer,
27  *           it can be used for oneshot actions. Besides running callbacks, the
28  *           timer tracks whether or not it has expired since creation or since
29  *           the last clearExpired() or restart(). The concept of expiration is
30  *           orthogonal to the callback mechanism and can be ignored.
31  *
32  *           See example/{heartbeat_timer,delayed_echo}.cpp for usage examples.
33  */
34 template <ClockId Id>
35 class Timer
36 {
37   public:
38     /** @brief Type used to represent a time duration for the timer
39      *         interval or time remaining.
40      */
41     using Duration = typename Clock<Id>::duration;
42 
43     /** @brief Type of the user provided callback function when the
44      *         timer elapses.
45      */
46     using Callback = fu2::unique_function<void(Timer<Id>&)>;
47 
48     /** @brief Creates a new timer on the given event loop.
49      *         This timer is created enabled by default if passed an interval.
50      *
51      *  @param[in] event    - The event we are attaching to
52      *  @param[in] callback - The user provided callback run when elapsing
53      *                        This can be empty
54      *  @param[in] interval - Optional amount of time in-between timer
55      *                        expirations. std::nullopt means the interval
56      *                        will be provided later.
57      *  @param[in] accuracy - Optional amount of error tolerable in timer
58      *                        expiration. Defaults to 1ms.
59      *  @throws SdEventError for underlying sd_event errors
60      */
61     Timer(const Event& event, Callback&& callback,
62           std::optional<Duration> interval = std::nullopt,
63           typename source::Time<Id>::Accuracy accuracy =
64               std::chrono::milliseconds{1});
65 
66     /** @brief Constructs a new non-owning Timer from an existing timer
67      *         Does not take a reference on the passed in timer
68      *         Does not release the reference it is given
69      *         NOTE: This will still take a reference during future copies
70      *  @internal
71      *
72      *  @param[in] other - The other Timer to copy
73      *  @param[in]       - Denotes no reference taken or release
74      */
75     Timer(const Timer& timer, sdeventplus::internal::NoOwn);
76 
77     /** @brief Sets the callback
78      *
79      *  @param[in] callback - The function executed on timer elapse
80      */
81     void set_callback(Callback&& callback);
82 
83     /** @brief Gets the associated Event object
84      *
85      *  @return The Event
86      */
87     const Event& get_event() const;
88 
89     /** @brief Determines the floating nature of the timer
90      *
91      *  @throws SdEventError for underlying sd_event errors
92      *  @return The enabled status of the timer
93      */
94     bool get_floating() const;
95 
96     /** @brief Sets the floating nature of the timer
97      *         If set to true, the timer will continue to run after the
98      *         destruction of this handle.
99      *
100      *  @param[in] b - Whether or not the timer should float
101      *  @throws SdEventError for underlying sd_event errors
102      */
103     void set_floating(bool b);
104 
105     /** @brief Has the timer expired since creation or reset of expiration
106      *         state.
107      *
108      *  @return True if expired, false otherwise
109      */
110     bool hasExpired() const;
111 
112     /** @brief Is the timer currently running on the event loop.
113      *
114      *  @throws SdEventError for underlying sd_event errors
115      *  @return True if running, false otherwise
116      */
117     bool isEnabled() const;
118 
119     /** @brief Gets interval between timer expirations
120      *         The timer may not have a configured interval and is instead
121      *         operating as a one-shot timer.
122      *
123      *  @return The interval as an std::chrono::duration
124      */
125     std::optional<Duration> getInterval() const;
126 
127     /** @brief Gets time left before the timer expirations
128      *
129      *  @throws std::runtime_error if the timer is not enabled
130      *  @throws SdEventError for underlying sd_event errors
131      *  @return The remaining time as an std::chrono::duration
132      */
133     Duration getRemaining() const;
134 
135     /** @brief Sets whether or not the timer is running on the event loop
136      *         This does not alter the expiration time of the timer.
137      *
138      *  @param[in] enabled - Should the timer be enabled or disabled
139      *  @throws std::runtime_error If the timer has not been initialized
140      *  @throws SdEventError for underlying sd_event errors
141      */
142     void setEnabled(bool enabled);
143 
144     /** @brief Sets the amount of time left until the timer expires.
145      *         This does not affect the interval used for subsequent runs.
146      *
147      *  @param[in] remaining - The new amount of time left on the timer
148      *  @throws SdEventError for underlying sd_event errors
149      */
150     void setRemaining(Duration remaining);
151 
152     /** @brief Resets the amount of time left to the interval of the timer.
153      *
154      *  @throws SdEventError for underlying sd_event errors
155      */
156     void resetRemaining();
157 
158     /** @brief Sets the interval of the timer for future timer expirations.
159      *         This does not alter the current expiration time of the timer.
160      *
161      *  @param[in] interval - The new interval for the timer
162      */
163     void setInterval(std::optional<Duration> interval);
164 
165     /** @brief Resets the expired status of the timer. */
166     void clearExpired();
167 
168     /** @brief Restarts the timer as though it has been completely
169      *         re-initialized. Expired status is reset, interval is updated,
170      *         time remaining is set to the new interval, and the timer is
171      *         enabled if the interval is populated.
172      *
173      *  @param[in] interval - The new interval for the timer
174      *  @throws SdEventError for underlying sd_event errors
175      */
176     void restart(std::optional<Duration> interval);
177 
178     /** @brief Restarts the timer as though it has been completely
179      *         re-initialized. Expired status is reset, interval is removed,
180      *         time remaining is set to the new remaining, and the timer is
181      *         enabled as a one shot.
182      *
183      *  @param[in] interval - The new interval for the timer
184      *  @throws SdEventError for underlying sd_event errors
185      */
186     void restartOnce(Duration remaining);
187 
188   protected:
189     /** @brief Reference to the heap allocated Timer.
190      *         Lifetime and ownership is managed by the timeSource
191      */
192     detail::TimerData<Id>* userdata;
193     /** @brief Underlying sd_event time source that backs the timer */
194     source::Time<Id> timeSource;
195 
196     /** @brief Used as a helper to run our user defined callback on the
197      *         timeSource
198      */
199     void internalCallback();
200 
201     friend detail::TimerData<Id>;
202 };
203 
204 namespace detail
205 {
206 
207 template <ClockId Id>
208 class TimerData : public Timer<Id>
209 {
210   private:
211     /** @brief Tracks the expiration status of the timer */
212     bool expired;
213     /** @brief Tracks whether or not the expiration timeout is valid */
214     bool initialized;
215     /** @brief User defined callback run on each expiration */
216     typename Timer<Id>::Callback callback;
217     /** @brief Clock used for updating the time source */
218     Clock<Id> clock;
219     /** @brief Interval between each timer expiration */
220     std::optional<typename Timer<Id>::Duration> interval;
221 
222   public:
223     TimerData(const Timer<Id>& base, typename Timer<Id>::Callback&& callback,
224               std::optional<typename Timer<Id>::Duration> interval);
225 
226     friend Timer<Id>;
227 };
228 
229 } // namespace detail
230 } // namespace utility
231 } // namespace sdeventplus
232