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