1 // Copyright (C) 2006-2014 David Sugar, Tycho Softworks.
2 // Copyright (C) 2015 Cherokees of Idaho.
3 //
4 // This file is part of GNU uCommon C++.
5 //
6 // GNU uCommon C++ is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU Lesser General Public License as published
8 // by the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // GNU uCommon C++ is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public License
17 // along with GNU uCommon C++. If not, see <http://www.gnu.org/licenses/>.
18
19 #include <ucommon-config.h>
20 #include <ucommon/export.h>
21 #include <ucommon/timers.h>
22 #include <ucommon/thread.h>
23 #include <ucommon/cpr.h>
24
25 namespace ucommon {
26
27 #if _POSIX_TIMERS > 0 && defined(POSIX_TIMERS)
28 extern int _posix_clocking;
29 #endif
30
_difftime(time_t ref)31 static long _difftime(time_t ref)
32 {
33 time_t now;
34 time(&now);
35
36 return (long)difftime(ref, now);
37 }
38
39 #if _POSIX_TIMERS > 0 && defined(POSIX_TIMERS)
adj(struct timespec * ts)40 static void adj(struct timespec *ts)
41 {
42 assert(ts != NULL);
43
44 if(ts->tv_nsec >= 1000000000l)
45 ts->tv_sec += (ts->tv_nsec / 1000000000l);
46 ts->tv_nsec %= 1000000000l;
47 if(ts->tv_nsec < 0)
48 ts->tv_nsec = -ts->tv_nsec;
49 }
50 #else
adj(struct timeval * ts)51 static void adj(struct timeval *ts)
52 {
53 assert(ts != NULL);
54
55 if(ts->tv_usec >= 1000000l)
56 ts->tv_sec += (ts->tv_usec / 1000000l);
57 ts->tv_usec %= 1000000l;
58 if(ts->tv_usec < 0)
59 ts->tv_usec = -ts->tv_usec;
60 }
61 #endif
62
event(timeout_t timeout)63 TimerQueue::event::event(timeout_t timeout) :
64 Timer(), DLinkedObject()
65 {
66 set(timeout);
67 }
68
event(TimerQueue * tq,timeout_t timeout)69 TimerQueue::event::event(TimerQueue *tq, timeout_t timeout) :
70 Timer(), DLinkedObject()
71 {
72 set(timeout);
73 Timer::update();
74 attach(tq);
75 }
76
~event()77 TimerQueue::event::~event()
78 {
79 detach();
80 }
81
Timer()82 Timer::Timer()
83 {
84 clear();
85 }
86
Timer(timeout_t in)87 Timer::Timer(timeout_t in)
88 {
89 set();
90 operator+=(in);
91 }
92
Timer(const Timer & copy)93 Timer::Timer(const Timer& copy)
94 {
95 timer = copy.timer;
96 }
97
Timer(time_t in)98 Timer::Timer(time_t in)
99 {
100 set();
101 timer.tv_sec += _difftime(in);
102 }
103
104 #ifdef _MSWINDOWS_
105
ticks(void)106 Timer::tick_t Timer::ticks(void)
107 {
108 ULARGE_INTEGER timer;
109
110 GetSystemTimeAsFileTime((FILETIME*)&timer);
111 timer.QuadPart +=
112 (tick_t) (6893856000000000);
113 return timer.QuadPart;
114 }
115
116 #else
117
ticks(void)118 Timer::tick_t Timer::ticks(void)
119 {
120 struct timeval tv;
121 gettimeofday(&tv, NULL);
122 return ((tick_t)tv.tv_sec * (tick_t)10000000) +
123 ((tick_t)tv.tv_usec * 10) + (((tick_t)0x01B21DD2) << 32) + (tick_t)0x13814000;
124 }
125 #endif
126
set(timeout_t timeout)127 void Timer::set(timeout_t timeout)
128 {
129 set();
130 operator+=(timeout);
131 }
132
set(time_t time)133 void Timer::set(time_t time)
134 {
135 set();
136 timer.tv_sec += _difftime(time);
137 }
138
attach(TimerQueue * tq)139 void TimerQueue::event::attach(TimerQueue *tq)
140 {
141 if(tq == list())
142 return;
143
144 detach();
145 if(!tq)
146 return;
147
148 tq->modify();
149 enlist(tq);
150 Timer::update();
151 tq->update();
152 }
153
arm(timeout_t timeout)154 void TimerQueue::event::arm(timeout_t timeout)
155 {
156 TimerQueue *tq = list();
157 if(tq)
158 tq->modify();
159 set(timeout);
160 if(tq)
161 tq->update();
162 }
163
disarm(void)164 void TimerQueue::event::disarm(void)
165 {
166 TimerQueue *tq = list();
167 bool flag = is_active();
168
169 if(tq && flag)
170 tq->modify();
171 clear();
172 if(tq && flag)
173 tq->update();
174 }
175
update(void)176 void TimerQueue::event::update(void)
177 {
178 TimerQueue *tq = list();
179 if(Timer::update() && tq) {
180 tq->modify();
181 tq->update();
182 }
183 }
184
detach(void)185 void TimerQueue::event::detach(void)
186 {
187 TimerQueue *tq = list();
188 if(tq) {
189 tq->modify();
190 clear();
191 delist();
192 tq->update();
193 }
194 }
195
set(void)196 void Timer::set(void)
197 {
198 #if _POSIX_TIMERS > 0 && defined(POSIX_TIMERS)
199 clock_gettime(_posix_clocking, &timer);
200 #else
201 gettimeofday(&timer, NULL);
202 #endif
203 updated = true;
204 }
205
clear(void)206 void Timer::clear(void)
207 {
208 timer.tv_sec = 0;
209 #if _POSIX_TIMERS > 0 && defined(POSIX_TIMERS)
210 timer.tv_nsec = 0;
211 #else
212 timer.tv_usec = 0;
213 #endif
214 updated = false;
215 }
216
update(void)217 bool Timer::update(void)
218 {
219 bool rtn = updated;
220 updated = false;
221 return rtn;
222 }
223
is_active(void) const224 bool Timer::is_active(void) const
225 {
226 #if _POSIX_TIMERS > 0 && defined(POSIX_TIMERS)
227 if(!timer.tv_sec && !timer.tv_nsec)
228 return false;
229 #else
230 if(!timer.tv_sec && !timer.tv_usec)
231 return false;
232 #endif
233 return true;
234 }
235
get(void) const236 timeout_t Timer::get(void) const
237 {
238 timeout_t diff;
239 #if _POSIX_TIMERS > 0 && POSIX_TIMERS
240 struct timespec current;
241
242 clock_gettime(_posix_clocking, ¤t);
243 adj(¤t);
244 if(current.tv_sec > timer.tv_sec)
245 return 0;
246 if(current.tv_sec == timer.tv_sec && current.tv_nsec > timer.tv_nsec)
247 return 0;
248 diff = (timer.tv_sec - current.tv_sec) * 1000;
249 diff += ((timer.tv_nsec - current.tv_nsec) / 1000000l);
250 #else
251 struct timeval current;
252 gettimeofday(¤t, NULL);
253 adj(¤t);
254 if(current.tv_sec > timer.tv_sec)
255 return 0;
256 if(current.tv_sec == timer.tv_sec && current.tv_usec > timer.tv_usec)
257 return 0;
258 diff = (timer.tv_sec - current.tv_sec) * 1000;
259 diff += ((timer.tv_usec - current.tv_usec) / 1000);
260 #endif
261 return diff;
262 }
263
operator bool() const264 Timer::operator bool() const
265 {
266 if(get())
267 return false;
268
269 return true;
270 }
271
operator !() const272 bool Timer::operator!() const
273 {
274 if(get())
275 return true;
276
277 return false;
278 }
279
operator -(const Timer & source)280 timeout_t Timer::operator-(const Timer& source)
281 {
282 timeout_t tv = get(), dv = source.get();
283 if(!tv)
284 return 0;
285
286 if(tv == Timer::inf)
287 return Timer::inf;
288
289 if(dv == Timer::inf)
290 return tv;
291
292 if(dv > tv)
293 return 0;
294
295 return tv - dv;
296 }
297
operator ==(const Timer & source) const298 bool Timer::operator==(const Timer& source) const
299 {
300 return get() == source.get();
301 }
302
operator !=(const Timer & source) const303 bool Timer::operator!=(const Timer& source) const
304 {
305 return get() != source.get();
306 }
307
operator <(const Timer & source) const308 bool Timer::operator<(const Timer& source) const
309 {
310 return get() < source.get();
311 }
312
operator <=(const Timer & source) const313 bool Timer::operator<=(const Timer& source) const
314 {
315 return get() <= source.get();
316 }
317
operator >(const Timer & source) const318 bool Timer::operator>(const Timer& source) const
319 {
320 return get() > source.get();
321 }
322
operator >=(const Timer & source) const323 bool Timer::operator>=(const Timer& source) const
324 {
325 return get() >= source.get();
326 }
327
operator =(timeout_t to)328 Timer& Timer::operator=(timeout_t to)
329 {
330 #if _POSIX_TIMERS > 0 && defined(POSIX_TIMERS)
331 clock_gettime(_posix_clocking, &timer);
332 #else
333 gettimeofday(&timer, NULL);
334 #endif
335 operator+=(to);
336 return *this;
337 }
338
operator +=(timeout_t to)339 Timer& Timer::operator+=(timeout_t to)
340 {
341 if(!is_active())
342 set();
343
344 timer.tv_sec += (to / 1000);
345 #if _POSIX_TIMERS > 0 && defined(POSIX_TIMERS)
346 timer.tv_nsec += (to % 1000l) * 1000000l;
347 #else
348 timer.tv_usec += (to % 1000l) * 1000l;
349 #endif
350 adj(&timer);
351 updated = true;
352 return *this;
353 }
354
operator -=(timeout_t to)355 Timer& Timer::operator-=(timeout_t to)
356 {
357 if(!is_active())
358 set();
359 timer.tv_sec -= (to / 1000);
360 #if _POSIX_TIMERS > 0 && defined(POSIX_TIMERS)
361 timer.tv_nsec -= (to % 1000l) * 1000000l;
362 #else
363 timer.tv_usec -= (to % 1000l) * 1000l;
364 #endif
365 adj(&timer);
366 return *this;
367 }
368
369
operator +=(time_t abs)370 Timer& Timer::operator+=(time_t abs)
371 {
372 if(!is_active())
373 set();
374 timer.tv_sec += _difftime(abs);
375 updated = true;
376 return *this;
377 }
378
operator -=(time_t abs)379 Timer& Timer::operator-=(time_t abs)
380 {
381 if(!is_active())
382 set();
383 timer.tv_sec -= _difftime(abs);
384 return *this;
385 }
386
operator =(time_t abs)387 Timer& Timer::operator=(time_t abs)
388 {
389 #if _POSIX_TIMERS > 0 && defined(POSIX_TIMERS)
390 clock_gettime(_posix_clocking, &timer);
391 #else
392 gettimeofday(&timer, NULL);
393 #endif
394 if(!abs)
395 return *this;
396
397 timer.tv_sec += _difftime(abs);
398 updated = true;
399 return *this;
400 }
401
sync(Timer & t)402 void Timer::sync(Timer &t)
403 {
404 #if _POSIX_TIMERS > 0 && defined(HAVE_CLOCK_NANOSLEEP) && defined(POSIX_TIMERS)
405 clock_nanosleep(_posix_clocking, TIMER_ABSTIME, &t.timer, NULL);
406 #elif defined(_MSWINDOWS_)
407 SleepEx(t.get(), FALSE);
408 #else
409 usleep(t.get());
410 #endif
411 }
412
413
timeout(void)414 timeout_t TQEvent::timeout(void)
415 {
416 timeout_t timeout = get();
417 if(is_active() && !timeout) {
418 disarm();
419 expired();
420 timeout = get();
421 Timer::update();
422 }
423 return timeout;
424 }
425
TimerQueue()426 TimerQueue::TimerQueue() : OrderedIndex()
427 {
428 }
429
~TimerQueue()430 TimerQueue::~TimerQueue()
431 {
432 }
433
expire(void)434 timeout_t TimerQueue::expire(void)
435 {
436 timeout_t first = Timer::inf, next;
437 linked_pointer<TimerQueue::event> timer = begin();
438 TimerQueue::event *tp;
439
440 while(timer) {
441 tp = *timer;
442 timer.next();
443 next = tp->timeout();
444 if(next && next < first)
445 first = next;
446 }
447 return first;
448 }
449
operator +=(event & te)450 void TimerQueue::operator+=(event &te) { te.attach(this); }
451
operator -=(event & te)452 void TimerQueue::operator-=(event &te)
453 {
454 if(te.list() == this)
455 te.detach();
456 }
457
458 } // namespace ucommon
459