cxxitimer 2.0.5
A C++ library to handle linux interval timer
Loading...
Searching...
No Matches
cxxitimer.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2022 Nikolas Koesling <nikolas@koesling.info>.
3 * This program is free software. You can redistribute it and/or modify it under the terms of the MIT License.
4 */
5
6#include "cxxitimer.hpp"
7
8#include <cmath>
9#include <iostream>
10#include <sysexits.h>
11
12
13namespace cxxitimer {
14
15//* timeval to stop timer
16static constexpr itimerval STOP_TIMER = {};
17
18//* number of usec per second
19constexpr auto USEC_PER_SEC = static_cast<double>(1000000);
20
21
22bool ITimer_Real::instance_exists = false;
24bool ITimer_Prof::instance_exists = false;
25
26ITimer::ITimer(int type, const timeval &interval) noexcept
27 : timer_value(interval),
28 timer_interval(interval),
29 type(type),
30 speed_factor(1.0), // normal speed
31 running(false) // not running
32{}
33
34ITimer::ITimer(int type, double interval) noexcept
37 type(type),
38 speed_factor(1.0), // normal speed
39 running(false) // not running
40{}
41
42ITimer::ITimer(int type, const timeval &interval, const timeval &value) noexcept
43 : timer_value(value),
44 timer_interval(interval),
45 type(type),
46 speed_factor(1.0), // normal speed
47 running(false) // not running
48{}
49
50ITimer::ITimer(int type, double interval, double value) noexcept
53 type(type),
54 speed_factor(1.0), // normal speed
55 running(false) // not running
56{}
57
58
60 // stop timer if running
61 if (running) {
62 try {
63 stop();
64 } catch (const std::exception &e) {
65 std::cerr << "Exception in destructor (" << __PRETTY_FUNCTION__ << "): " << e.what() << '\n';
66 exit(EX_SOFTWARE);
67 }
68 }
69}
70
71void ITimer::adjust_speed(double new_factor) {
72 if (!running) throw std::runtime_error("timer not running");
73
74 const timeval new_interval = timer_interval / new_factor;
75 if (new_interval.tv_sec == 0 && new_interval.tv_usec == 0)
76 throw std::runtime_error("invalid timer values due to a too small speed factor");
77
78 itimerval val {};
79 int tmp = setitimer(type, &STOP_TIMER, &val);
80 if (tmp) throw std::system_error(errno, std::generic_category(), "call of setitimer failed");
81
82 // set timer interval
83 val.it_interval = new_interval;
84
85 // scale timer value
86 val.it_value *= speed_factor / new_factor;
87
88 // set new timer value
89 tmp = setitimer(type, &val, nullptr);
90 if (tmp) throw std::system_error(errno, std::generic_category(), "call of setitimer failed");
91
92 // save speed factor
93 speed_factor = new_factor;
94}
95
96void ITimer::start() {
97 if (running) throw std::logic_error("timer already started");
98
99 // create scaled timer value
100 const itimerval timer_val = {.it_interval = timer_interval / speed_factor, .it_value = timer_value / speed_factor};
101
102 if (timer_val.it_interval.tv_sec < 0 || timer_val.it_interval.tv_usec < 0)
103 throw std::runtime_error("timer interval is negative");
104
105 if (timer_val.it_value.tv_sec < 0 || timer_val.it_value.tv_usec < 0)
106 throw std::runtime_error("timer value is negative");
107
108 if (timer_val.it_interval.tv_sec == 0 && timer_val.it_interval.tv_usec == 0)
109 throw std::runtime_error("invalid timer values due to a too small speed factor");
110
111 if (timer_val.it_value.tv_sec == 0 && timer_val.it_value.tv_usec == 0)
112 throw std::runtime_error("timer value is zero (would disarm the timer)");
113
114 // start timer;
115 int tmp = setitimer(type, &timer_val, nullptr);
116 if (tmp < 0) throw std::system_error(errno, std::generic_category(), "call of setitimer failed");
117
118 running = true;
119}
120
121void ITimer::stop() {
122 if (!running) throw std::logic_error("timer already stopped");
123
124 // stop timer and save value
125 itimerval timer_val {};
126 int tmp = setitimer(type, &STOP_TIMER, &timer_val);
127 if (tmp < 0) throw std::system_error(errno, std::generic_category(), "call of setitimer failed");
128
129 // normalize value
130 timer_value = timer_val.it_value * speed_factor;
131
132 running = false;
133}
134
135void ITimer::set_speed_factor(double factor) {
136 // check speed_factor
137 if (factor <= 0.0) throw std::invalid_argument("negative values not allowed");
138
139 if (std::isnan(factor) || std::isinf(factor)) throw std::invalid_argument("invalid double value");
140
141 if (running) adjust_speed(factor);
142 else
143 speed_factor = factor;
144}
145
146void ITimer::set_interval_value(const timeval &interval, const timeval &value) {
147 if (running) throw std::logic_error("cannot set interval/value if timer is running");
148
149 this->timer_interval = interval;
150 this->timer_value = value;
151}
152
153void ITimer::set_interval_value(double interval, double value) {
155}
156
158 // adjust speed if running
159 if (running) adjust_speed(1.0);
160 else
161 speed_factor = 1.0;
162}
163
164void ITimer::to_fstream(std::ofstream &fstream) const {
165 itimerval val {};
166 if (running) {
167 int tmp = getitimer(type, &val);
168 if (tmp < 0) throw std::system_error(errno, std::generic_category(), "call of getitimer failed");
169
170 val.it_value *= speed_factor;
171 } else {
172 val.it_value = timer_value;
173 }
174
175 val.it_interval = timer_interval;
176
177 fstream.write(reinterpret_cast<char *>(&val), sizeof(val));
178}
179
180void ITimer::from_fstream(std::ifstream &fstream) {
181 if (running) throw std::logic_error("timer is running");
182
183 itimerval val {};
184 fstream.read(reinterpret_cast<char *>(&val), sizeof(val));
185 timer_interval = val.it_interval;
186 timer_value = val.it_value;
187}
188
189timeval ITimer::get_timer_value() const {
190 if (running) {
191 itimerval temp {};
192 int tmp = getitimer(type, &temp);
193 if (tmp < 0) throw std::system_error(errno, std::generic_category(), "call of getitimer failed");
194 return temp.it_value * speed_factor;
195 } else
196 return timer_value;
197}
198
199ITimer_Real::ITimer_Real(const timeval &interval) : ITimer(ITIMER_REAL, interval) {
200 // prevent multiple instances
201 if (instance_exists) throw std::logic_error("instance exists");
202 instance_exists = true;
203}
204
205ITimer_Real::ITimer_Real(const timeval &interval, const timeval &value) : ITimer(ITIMER_REAL, interval, value) {
206 // prevent multiple instances
207 if (instance_exists) throw std::logic_error("instance exists");
208 instance_exists = true;
209}
210
211ITimer_Real::ITimer_Real(double interval) : ITimer(ITIMER_REAL, interval) {
212 // prevent multiple instances
213 if (instance_exists) throw std::logic_error("instance exists");
214 instance_exists = true;
215}
216
217ITimer_Real::ITimer_Real(double interval, double value) : ITimer(ITIMER_REAL, interval, value) {
218 // prevent multiple instances
219 if (instance_exists) throw std::logic_error("instance exists");
220 instance_exists = true;
221}
222
224 // allow new instance
225 instance_exists = false;
226}
227
228ITimer_Virtual::ITimer_Virtual(const timeval &interval) : ITimer(ITIMER_VIRTUAL, interval) {
229 // prevent multiple instances
230 if (instance_exists) throw std::logic_error("instance exists");
231 instance_exists = true;
232}
233
234ITimer_Virtual::ITimer_Virtual(const timeval &interval, const timeval &value)
235 : ITimer(ITIMER_VIRTUAL, interval, value) {
236 // prevent multiple instances
237 if (instance_exists) throw std::logic_error("instance exists");
238 instance_exists = true;
239}
240
241ITimer_Virtual::ITimer_Virtual(double interval) : ITimer(ITIMER_VIRTUAL, interval) {
242 // prevent multiple instances
243 if (instance_exists) throw std::logic_error("instance exists");
244 instance_exists = true;
245}
246
247ITimer_Virtual::ITimer_Virtual(double interval, double value) : ITimer(ITIMER_VIRTUAL, interval, value) {
248 // prevent multiple instances
249 if (instance_exists) throw std::logic_error("instance exists");
250 instance_exists = true;
251}
252
254 // allow new instance
255 instance_exists = false;
256}
257
258ITimer_Prof::ITimer_Prof(const timeval &interval) : ITimer(ITIMER_PROF, interval) {
259 // prevent multiple instances
260 if (instance_exists) throw std::logic_error("instance exists");
261 instance_exists = true;
262}
263
264ITimer_Prof::ITimer_Prof(const timeval &interval, const timeval &value) : ITimer(ITIMER_PROF, interval, value) {
265 // prevent multiple instances
266 if (instance_exists) throw std::logic_error("instance exists");
267 instance_exists = true;
268}
269
270ITimer_Prof::ITimer_Prof(double interval) : ITimer(ITIMER_PROF, interval) {
271 // prevent multiple instances
272 if (instance_exists) throw std::logic_error("instance exists");
273 instance_exists = true;
274}
275
276ITimer_Prof::ITimer_Prof(double interval, double value) : ITimer(ITIMER_PROF, interval, value) {
277 // prevent multiple instances
278 if (instance_exists) throw std::logic_error("instance exists");
279 instance_exists = true;
280}
281
283 // allow new instance
284 instance_exists = false;
285}
286
287timeval &operator*=(timeval &left, double right) noexcept {
288 double timer_value = timeval_to_double(left) * right;
289 left = double_to_timeval(timer_value);
290 return left;
291}
292
293itimerval &operator*=(itimerval &left, double right) noexcept {
294 left.it_interval *= right;
295 left.it_value *= right;
296 return left;
297}
298
299timeval operator*(const timeval &left, double right) noexcept {
300 auto ret_val = left;
301 ret_val *= right;
302 return ret_val;
303}
304
305itimerval operator*(const itimerval &left, double right) noexcept {
306 auto ret_val = left;
307 ret_val *= right;
308 return ret_val;
309}
310
311timeval &operator/=(timeval &left, double right) noexcept {
312 double timer_value = timeval_to_double(left) / right;
313 left = double_to_timeval(timer_value);
314 return left;
315}
316
317itimerval &operator/=(itimerval &left, double right) noexcept {
318 left.it_interval /= right;
319 left.it_value /= right;
320 return left;
321}
322
323timeval operator/(const timeval &left, double right) noexcept {
324 auto ret_val = left;
325 ret_val /= right;
326 return ret_val;
327}
328
329itimerval operator/(const itimerval &left, double right) noexcept {
330 auto ret_val = left;
331 ret_val /= right;
332 return ret_val;
333}
334
335double timeval_to_double(const timeval &time) noexcept {
336 double ret_val = static_cast<double>(time.tv_sec) + static_cast<double>(time.tv_usec) / USEC_PER_SEC;
337 return ret_val;
338}
339
340timeval double_to_timeval(const double time) noexcept {
341 auto sec = static_cast<time_t>(time);
342 auto usec = static_cast<suseconds_t>((time - static_cast<double>(sec)) * USEC_PER_SEC);
343 if (usec < 0) {
344 sec -= 1;
345 usec += static_cast<suseconds_t>(USEC_PER_SEC);
346 }
347 return timeval {.tv_sec = sec, .tv_usec = usec};
348}
349
350} // namespace cxxitimer
class ITimer_Prof
ITimer_Prof(double interval)
create ITimer_Prof instance
ITimer_Prof(double interval, double value)
create ITimer_Prof instance
ITimer_Prof(const timeval &interval={.tv_sec=1,.tv_usec=0})
create ITimer_Prof instance
static bool instance_exists
ITimer_Prof(const timeval &interval, const timeval &value)
create ITimer_Prof instance
class ITimer_Real
ITimer_Real(const timeval &interval={.tv_sec=1,.tv_usec=0})
create ITimer_Real instance
ITimer_Real(double interval)
create ITimer_Real instance
ITimer_Real(const timeval &interval, const timeval &value)
create ITimer_Real instance
static bool instance_exists
ITimer_Real(double interval, double value)
create ITimer_Real instance
class ITimer_Virtual
ITimer_Virtual(const timeval &interval, const timeval &value)
create ITimer_Virtual instance
ITimer_Virtual(const timeval &interval={.tv_sec=1,.tv_usec=0})
create ITimer_Virtual instance
ITimer_Virtual(double interval, double value)
create ITimer_Virtual instance
ITimer_Virtual(double interval)
create ITimer_Virtual instance
abstract class ITimer
Definition cxxitimer.hpp:16
void start()
start timer
Definition cxxitimer.cpp:96
void to_fstream(std::ofstream &fstream) const
write to binary file stream
double speed_factor
speed adjustment factor
Definition cxxitimer.hpp:34
ITimer(int type, double interval, double value) noexcept
Definition cxxitimer.cpp:50
ITimer(int type, const timeval &interval={.tv_sec=1,.tv_usec=0}) noexcept
Definition cxxitimer.cpp:26
ITimer(int type, double interval) noexcept
Definition cxxitimer.cpp:34
timeval get_timer_value() const
get timer value
timeval timer_interval
Definition cxxitimer.hpp:22
void from_fstream(std::ifstream &fstream)
read from binary filestream
void set_interval_value(double interval, double value)
set interval and value
virtual ~ITimer()
Destroy the timer instance.
Definition cxxitimer.cpp:59
void set_speed_to_normal()
set speed to normal
void set_speed_factor(double factor)
set speed factor
ITimer(int type, const timeval &interval, const timeval &value) noexcept
Definition cxxitimer.cpp:42
void set_interval_value(const timeval &interval, const timeval &value)
set interval and value
void stop()
stop timer
virtual void adjust_speed(double new_factor)
Definition cxxitimer.cpp:71
itimerval & operator/=(itimerval &left, double right) noexcept
timeval double_to_timeval(const double time) noexcept
timeval & operator*=(timeval &left, double right) noexcept
constexpr auto USEC_PER_SEC
Definition cxxitimer.cpp:19
timeval operator*(const timeval &left, double right) noexcept
itimerval operator*(const itimerval &left, double right) noexcept
itimerval operator/(const itimerval &left, double right) noexcept
itimerval & operator*=(itimerval &left, double right) noexcept
timeval & operator/=(timeval &left, double right) noexcept
double timeval_to_double(const timeval &time) noexcept
static constexpr itimerval STOP_TIMER
Definition cxxitimer.cpp:16
timeval operator/(const timeval &left, double right) noexcept