join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
timer.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_CORE_TIMER_HPP
26#define JOIN_CORE_TIMER_HPP
27
28// libjoin.
29#include <join/reactor.hpp>
30
31// C++.
32#include <functional>
33#include <stdexcept>
34#include <chrono>
35
36// C.
37#include <sys/timerfd.h>
38#include <unistd.h>
39
40namespace join
41{
42 class RealTime;
43 class Monotonic;
44
48 template <class ClockPolicy>
49 class BasicTimer : protected EventHandler
50 {
51 public:
56 BasicTimer (Reactor* reactor = nullptr)
57 : _handle (timerfd_create (_policy.type (), TFD_NONBLOCK | TFD_CLOEXEC))
58 , _reactor (reactor)
59 {
60 if (_handle == -1)
61 {
62 throw std::system_error (errno, std::system_category (), "timerfd_create failed");
63 }
64
65 if (_reactor == nullptr)
66 {
67 _reactor = ReactorThread::reactor ();
68 }
69
70 _reactor->addHandler (_handle, this);
71 }
72
77 BasicTimer (const BasicTimer& other) = delete;
78
84 BasicTimer& operator= (const BasicTimer& other) = delete;
85
90 BasicTimer (BasicTimer&& other) = delete;
91
97 BasicTimer& operator= (BasicTimer&& other) = delete;
98
102 virtual ~BasicTimer () noexcept
103 {
104 _reactor->delHandler (_handle);
105 if (_handle != -1)
106 {
107 close (_handle);
108 }
109 }
110
116 template <class Rep, class Period, typename Func>
117 void setOneShot (std::chrono::duration<Rep, Period> duration, Func&& callback)
118 {
119 auto ns = std::chrono::duration_cast<std::chrono::nanoseconds> (duration);
120 _callback = std::forward<Func> (callback);
121 _oneShot = true;
122 _ns = std::chrono::nanoseconds::zero ();
123
124 auto ts = toTimerSpec (ns);
125 timerfd_settime (handle (), 0, &ts, nullptr);
126 }
127
133 template <class Clock, class Duration, typename Func>
134 void setOneShot (std::chrono::time_point<Clock, Duration> timePoint, Func&& callback)
135 {
136 static_assert (
137 (std::is_same<ClockPolicy, RealTime>::value && std::is_same<Clock, std::chrono::system_clock>::value) ||
138 (std::is_same<ClockPolicy, Monotonic>::value &&
139 std::is_same<Clock, std::chrono::steady_clock>::value),
140 "Clock type mismatch timer policy");
141
142 auto elapsed = timePoint.time_since_epoch ();
143 auto ns = std::chrono::duration_cast<std::chrono::nanoseconds> (elapsed);
144 _callback = std::forward<Func> (callback);
145 _oneShot = true;
146 _ns = std::chrono::nanoseconds::zero ();
147
148 auto ts = toTimerSpec (ns);
149 timerfd_settime (handle (), TFD_TIMER_ABSTIME, &ts, nullptr);
150 }
151
157 template <class Rep, class Period, typename Func>
158 void setInterval (std::chrono::duration<Rep, Period> duration, Func&& callback)
159 {
160 auto ns = std::chrono::duration_cast<std::chrono::nanoseconds> (duration);
161 _callback = std::forward<Func> (callback);
162 _oneShot = false;
163 _ns = ns;
164
165 auto ts = toTimerSpec (ns, true);
166 timerfd_settime (handle (), 0, &ts, nullptr);
167 }
168
172 void cancel () noexcept
173 {
174 _callback = nullptr;
175 _oneShot = true;
176 _ns = std::chrono::nanoseconds::zero ();
177
178 struct itimerspec ts = {};
179 timerfd_settime (handle (), 0, &ts, nullptr);
180 }
181
186 bool active () const noexcept
187 {
188 struct itimerspec ts = {};
189 timerfd_gettime (handle (), &ts);
190 const bool hasValue = (ts.it_value.tv_sec != 0 || ts.it_value.tv_nsec != 0);
191 const bool hasInterval = (ts.it_interval.tv_sec != 0 || ts.it_interval.tv_nsec != 0);
192 return hasValue || hasInterval;
193 }
194
199 std::chrono::nanoseconds remaining () const
200 {
201 struct itimerspec ts = {};
202 timerfd_gettime (handle (), &ts);
203 return std::chrono::seconds (ts.it_value.tv_sec) + std::chrono::nanoseconds (ts.it_value.tv_nsec);
204 }
205
210 std::chrono::nanoseconds interval () const noexcept
211 {
212 return _ns;
213 }
214
219 bool oneShot () const noexcept
220 {
221 return _oneShot;
222 }
223
228 int type () const noexcept
229 {
230 return _policy.type ();
231 }
232
233 protected:
238 virtual void onReceive ([[maybe_unused]] int fd) override
239 {
240 uint64_t expirations;
241 ssize_t result = read (handle (), &expirations, sizeof (expirations));
242 if (result == sizeof (expirations) && _callback)
243 {
244 for (uint64_t i = 0; i < expirations; ++i)
245 {
246 _callback ();
247 }
248 }
249 }
250
257 static itimerspec toTimerSpec (std::chrono::nanoseconds ns, bool periodic = false) noexcept
258 {
259 struct itimerspec ts = {};
260 ts.it_value.tv_sec = ns.count () / NS_PER_SEC;
261 ts.it_value.tv_nsec = ns.count () % NS_PER_SEC;
262 if (periodic)
263 {
264 ts.it_interval.tv_sec = ts.it_value.tv_sec;
265 ts.it_interval.tv_nsec = ts.it_value.tv_nsec;
266 }
267 return ts;
268 }
269
274 int handle () const noexcept
275 {
276 return _handle;
277 }
278
279 private:
281 static constexpr uint64_t NS_PER_SEC = 1000000000ULL;
282
284 ClockPolicy _policy;
285
287 std::function<void ()> _callback;
288
290 std::chrono::nanoseconds _ns{};
291
293 bool _oneShot = true;
294
296 int _handle = -1;
297
299 Reactor* _reactor;
300 };
301
306 {
307 public:
309
313 constexpr RealTime () noexcept = default;
314
319 constexpr int type () const noexcept
320 {
321 return CLOCK_REALTIME;
322 }
323 };
324
329 {
330 public:
332
336 constexpr Monotonic () noexcept = default;
337
342 constexpr int type () const noexcept
343 {
344 return CLOCK_MONOTONIC;
345 }
346 };
347}
348
349#endif
base timer class.
Definition timer.hpp:50
virtual void onReceive(int fd) override
method called when data are ready to be read on handle.
Definition timer.hpp:238
std::chrono::nanoseconds interval() const noexcept
get the interval of the running periodic timer.
Definition timer.hpp:210
void setOneShot(std::chrono::duration< Rep, Period > duration, Func &&callback)
arm the timer as a one-shot timer.
Definition timer.hpp:117
int handle() const noexcept
get native handle.
Definition timer.hpp:274
void cancel() noexcept
cancel the timer.
Definition timer.hpp:172
std::chrono::nanoseconds remaining() const
get the remaining time until expiration.
Definition timer.hpp:199
static itimerspec toTimerSpec(std::chrono::nanoseconds ns, bool periodic=false) noexcept
convert nsec to itimerspec.
Definition timer.hpp:257
BasicTimer(const BasicTimer &other)=delete
copy constructor.
void setInterval(std::chrono::duration< Rep, Period > duration, Func &&callback)
arm the timer as a periodic timer.
Definition timer.hpp:158
bool active() const noexcept
check if timer is running.
Definition timer.hpp:186
void setOneShot(std::chrono::time_point< Clock, Duration > timePoint, Func &&callback)
arm the timer as a one-shot timer with absolute time.
Definition timer.hpp:134
BasicTimer(BasicTimer &&other)=delete
move constructor.
bool oneShot() const noexcept
check if timer is a one-shot timer.
Definition timer.hpp:219
int type() const noexcept
get the timer type.
Definition timer.hpp:228
BasicTimer & operator=(const BasicTimer &other)=delete
copy assignment operator.
BasicTimer(Reactor *reactor=nullptr)
create instance.
Definition timer.hpp:56
virtual ~BasicTimer() noexcept
destroy instance.
Definition timer.hpp:102
Event handler interface class.
Definition reactor.hpp:46
friend class Reactor
friendship with reactor.
Definition reactor.hpp:113
monotonic clock policy class.
Definition timer.hpp:329
constexpr Monotonic() noexcept=default
construct the timer policy instance by default.
constexpr int type() const noexcept
get timer type.
Definition timer.hpp:342
static Reactor * reactor()
get the global Reactor instance.
Definition reactor.cpp:438
Reactor class.
Definition reactor.hpp:120
int delHandler(int fd, bool sync=true) noexcept
delete handler from reactor.
Definition reactor.cpp:150
int addHandler(int fd, EventHandler *handler, bool sync=true) noexcept
add handler to reactor.
Definition reactor.cpp:94
real time clock policy class.
Definition timer.hpp:306
constexpr int type() const noexcept
get timer type.
Definition timer.hpp:319
constexpr RealTime() noexcept=default
construct the timer policy instance by default.
Definition acceptor.hpp:32