libdoip  0.1.0
DoIP (Diagnostics over Internet Protocol) ISO 13400 C++17 Library
TimerManager.h
Go to the documentation of this file.
1 
2 #include <atomic>
3 #include <chrono>
4 #include <condition_variable>
5 #include <functional>
6 #include <map>
7 #include <mutex>
8 #include <optional>
9 #include <thread>
10 #include <vector>
11 
12 #include "DoIPTimes.h"
13 
14 namespace doip {
15 
16 template <typename TimerIdType = uint8_t>
17 class TimerManager {
18  public:
19  using TimerId = TimerIdType;
20 
21  struct TimerEntry {
22  std::chrono::steady_clock::time_point expiry;
23  std::function<void(TimerIdType)> callback;
24  std::chrono::milliseconds interval;
25  bool periodic;
27  bool enabled = true;
28  };
29 
30  TimerManager() : m_running(true) {
31  m_thread = std::thread([this]() { run(); });
32  }
33 
35  stop();
36  }
37 
38  /**
39  * @brief Add a timer.
40  *
41  * @param duration the timer duration in ms
42  * @param callback the callback function to invoke when timer expired. Must not be null.
43  * @param periodic true, if timer should start again when expired
44  * @return std::optional<TimerId>
45  */
46  [[nodiscard]]
47  std::optional<TimerId> addTimer(TimerId id, std::chrono::milliseconds duration,
48  std::function<void(TimerIdType)> callback,
49  bool periodic = false) {
50  if (!callback) {
51  return std::nullopt;
52  }
53 
54  std::lock_guard<std::mutex> lock(m_mutex);
55 
56  TimerEntry entry;
57  entry.expiry = std::chrono::steady_clock::now() + duration;
58  entry.callback = std::move(callback);
59  entry.interval = duration;
60  entry.periodic = periodic;
61  entry.id = id;
62  entry.enabled = true;
63 
64  m_timers[id] = std::move(entry);
65 
66  m_cv.notify_one();
67 
68  return id;
69  }
70 
71  /**
72  * @brief Removes the timer.
73  *
74  * @param id the id of the timer
75  * @return true timer was removed
76  * @return false timer with given id does not exist
77  */
78  bool removeTimer(TimerId id) {
79  std::lock_guard<std::mutex> lock(m_mutex);
80  return m_timers.erase(id) > 0;
81  }
82 
83  [[nodiscard]]
84  bool restartTimer(TimerId id) {
85  std::lock_guard<std::mutex> lock(m_mutex);
86  auto it = m_timers.find(id);
87  if (it == m_timers.end()) {
88  return false;
89  }
90 
91  it->second.expiry = std::chrono::steady_clock::now() + it->second.interval;
92  m_cv.notify_one();
93  return true;
94  }
95 
96  /**
97  * @brief Updates a timer duration. The current timer is stopped and then
98  * started with the new duration.
99  *
100  * @param id the id of the timer
101  * @param newDuration the new duration in ms
102  * @return true timer was updated
103  * @return false timer with given id does not exist
104  */
105  [[nodiscard]]
106  bool updateTimer(TimerId id, std::chrono::milliseconds newDuration) {
107  std::lock_guard<std::mutex> lock(m_mutex);
108  auto it = m_timers.find(id);
109  if (it == m_timers.end()) {
110  return false;
111  }
112 
113  it->second.interval = newDuration;
114  it->second.expiry = std::chrono::steady_clock::now() + newDuration;
115  m_cv.notify_one();
116  return true;
117  }
118 
119  /**
120  * @brief Disables a timer.
121  *
122  * @param id the id of the timer
123  * @return true timer was disabled
124  * @return false timer with given id does not exist
125  */
127  std::lock_guard<std::mutex> lock(m_mutex);
128  auto it = m_timers.find(id);
129  if (it == m_timers.end()) {
130  return false;
131  }
132  it->second.enabled = false;
133  return true;
134  }
135 
136  /**
137  * @brief Enables a disabled timer. If the specified timer is not
138  * disabled, the function has no effect.
139  *
140  * @param id the id of the timer
141  * @return true timer was enabled
142  * @return false timer with given id does not exist
143  */
144  [[nodiscard]]
145  bool enableTimer(TimerId id) {
146  std::lock_guard<std::mutex> lock(m_mutex);
147  auto it = m_timers.find(id);
148  if (it == m_timers.end()) {
149  return false;
150  }
151  if (!it->second.enabled) {
152  it->second.enabled = true;
153  it->second.expiry = std::chrono::steady_clock::now() + it->second.interval;
154  m_cv.notify_one();
155  }
156  return true;
157  }
158 
159  /**
160  * @brief Restarts a timer (disables and enables it).
161  *
162  * @param id the id of the timer
163  * @return true timer was restarted
164  * @return false timer with given id does not exist
165  */
166  [[nodiscard]]
167  bool resetTimer(TimerId id) {
168  return disableTimer(id) && enableTimer(id);
169  }
170 
171  /**
172  * @brief Stops all timers and clears the timer list.
173  */
174  void stopAll() {
175  std::lock_guard<std::mutex> lock(m_mutex);
176  m_timers.clear();
177  }
178 
179  /**
180  * @brief Check if specified timer exists.
181  *
182  * @param id the id of the timer
183  * @return true timer exists
184  * @return false timer with given id does not exist
185  */
186  [[nodiscard]]
187  bool hasTimer(TimerId id) const {
188  std::lock_guard<std::mutex> lock(m_mutex);
189  return m_timers.find(id) != m_timers.end();
190  }
191 
192  /**
193  * @brief The number of timers.
194  *
195  * @return size_t number of timers.
196  */
197  [[nodiscard]]
198  size_t timerCount() const {
199  std::lock_guard<std::mutex> lock(m_mutex);
200  return m_timers.size();
201  }
202 
203  /**
204  * @brief Stops all timers and the timer manager.
205  */
206  void stop() {
207  if (m_running.exchange(false)) {
208  m_cv.notify_all();
209  if (m_thread.joinable()) {
210  m_thread.join();
211  }
212  }
213  }
214 
215  private:
216  std::map<TimerId, TimerEntry> m_timers;
217  mutable std::mutex m_mutex;
218  std::condition_variable m_cv;
219  std::thread m_thread;
220  std::atomic<bool> m_running{false};
221 
222  void run() {
223  while (m_running) {
224  std::unique_lock<std::mutex> lock(m_mutex);
225 
226  if (m_timers.empty()) {
227  m_cv.wait(lock, [this]() {
228  return !m_running || !m_timers.empty();
229  });
230  continue;
231  }
232 
233  auto now = std::chrono::steady_clock::now();
234  auto nextExpiry = std::chrono::steady_clock::time_point::max();
235 
236  for (const auto &[id, timer] : m_timers) {
237  if (timer.enabled && timer.expiry < nextExpiry) {
238  nextExpiry = timer.expiry;
239  }
240  }
241 
242  if (nextExpiry > now) {
243  m_cv.wait_until(lock, nextExpiry, [this]() {
244  return !m_running;
245  });
246  continue;
247  }
248 
249  std::vector<TimerId> expired;
250  for (const auto &[id, timer] : m_timers) {
251  if (timer.enabled && timer.expiry <= now) {
252  expired.push_back(id);
253  }
254  }
255 
256  lock.unlock();
257 
258  for (TimerId id : expired) {
259  lock.lock();
260  auto it = m_timers.find(id);
261  if (it == m_timers.end() || !it->second.enabled) {
262  lock.unlock();
263  continue;
264  }
265 
266  auto callback = it->second.callback;
267  bool periodic = it->second.periodic;
268  auto interval = it->second.interval;
269 
270  if (periodic) {
271  it->second.expiry = std::chrono::steady_clock::now() + interval;
272  } else {
273  m_timers.erase(it);
274  }
275 
276  lock.unlock();
277 
278  try {
279  callback(id);
280  } catch (...) {
281  // Swallow exceptions to prevent thread termination
282  }
283  }
284  }
285  }
286 };
287 
288 } // namespace doip
bool disableTimer(TimerId id)
Disables a timer.
Definition: TimerManager.h:126
bool updateTimer(TimerId id, std::chrono::milliseconds newDuration)
Updates a timer duration.
Definition: TimerManager.h:106
bool resetTimer(TimerId id)
Restarts a timer (disables and enables it).
Definition: TimerManager.h:167
size_t timerCount() const
The number of timers.
Definition: TimerManager.h:198
TimerIdType TimerId
Definition: TimerManager.h:19
bool removeTimer(TimerId id)
Removes the timer.
Definition: TimerManager.h:78
void stop()
Stops all timers and the timer manager.
Definition: TimerManager.h:206
bool restartTimer(TimerId id)
Definition: TimerManager.h:84
bool enableTimer(TimerId id)
Enables a disabled timer.
Definition: TimerManager.h:145
void stopAll()
Stops all timers and clears the timer list.
Definition: TimerManager.h:174
bool hasTimer(TimerId id) const
Check if specified timer exists.
Definition: TimerManager.h:187
std::optional< TimerId > addTimer(TimerId id, std::chrono::milliseconds duration, std::function< void(TimerIdType)> callback, bool periodic=false)
Add a timer.
Definition: TimerManager.h:47
Definition: AnsiColors.h:3
std::chrono::steady_clock::time_point expiry
Definition: TimerManager.h:22
std::function< void(TimerIdType)> callback
Definition: TimerManager.h:23
std::chrono::milliseconds interval
Definition: TimerManager.h:24