source: trash/old-modules/transport/protlib/timer.cpp@ 9736

Last change on this file since 9736 was 5641, checked in by Christoph Mayer, 15 years ago
File size: 16.5 KB
Line 
1/// ----------------------------------------*- mode: C++; -*--
2/// @file timer.cpp
3/// Software timer interface
4/// ----------------------------------------------------------
5/// $Id: timer.cpp 2549 2007-04-02 22:17:37Z bless $
6/// $HeadURL: https://svn.ipv6.tm.uka.de/nsis/protlib/trunk/src/timer.cpp $
7// ===========================================================
8//
9// Copyright (C) 2005-2007, all rights reserved by
10// - Institute of Telematics, Universitaet Karlsruhe (TH)
11//
12// More information and contact:
13// https://projekte.tm.uka.de/trac/NSIS
14//
15// This program is free software; you can redistribute it and/or modify
16// it under the terms of the GNU General Public License as published by
17// the Free Software Foundation; version 2 of the License
18//
19// This program is distributed in the hope that it will be useful,
20// but WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22// GNU General Public License for more details.
23//
24// You should have received a copy of the GNU General Public License along
25// with this program; if not, write to the Free Software Foundation, Inc.,
26// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27//
28// ===========================================================
29/** @ingroup timer
30 * You can create a software timer and attach a callback object to it.
31 * Timers are only accessed through their TimerManager and timer ID.
32 * timer managers are thread-safe.
33 *
34 * Timers are stored in an ordered list to ease checking for elapsed timers.
35 * Additionally, their IDs are kept in a hash_map, so a pointer to a
36 * timer object can be obtained very fast.
37 */
38
39//#define DEBUG_TIMER
40
41#include <errno.h>
42
43#include "cleanuphandler.h"
44#include "timer.h"
45#include "logfile.h"
46
47namespace protlib {
48
49/** @addtogroup timer Timer
50 * @{
51 */
52
53 using namespace log;
54
55static inline
56void
57normalize_timespec(struct timespec& ts)
58{
59 while (ts.tv_nsec>=1000000000) {
60 ts.tv_sec++;
61 ts.tv_nsec -= 1000000000;
62 } // end while
63 if (ts.tv_sec<0) ts.tv_sec = 0;
64 if (ts.tv_nsec<0) ts.tv_nsec = 0;
65} // end normalize
66
67/**
68 * fills in timespec with sec+msec, msec may be larger than 999!
69 */
70static inline
71void
72fill_timespec(struct timespec& ts, int32 sec, int32 msec)
73{
74 if (sec<0) sec = 0;
75 if (msec<0) msec = 0;
76 ts.tv_sec = sec + (msec/1000);
77 ts.tv_nsec = (msec%1000)*1000000;
78 normalize_timespec(ts);
79} // end fill_timespec
80
81static inline
82void
83add_timespecs(struct timespec& ts1, struct timespec& ts2, struct timespec& res) {
84 res.tv_sec = ts1.tv_sec + ts2.tv_sec;
85 res.tv_nsec = ts1.tv_nsec + ts2.tv_nsec;
86 normalize_timespec(res);
87} // end add_timespecs
88
89static inline void gettimeofday_timespec(struct timespec& ts) {
90 struct timeval now = {0,0};
91 gettimeofday(&now,NULL);
92 ts.tv_sec = now.tv_sec;
93 ts.tv_nsec = now.tv_usec*1000;
94} // end gettimeofday_timespec
95
96/** Initialize a TimerManager object. */
97TimerManager::TimerManager()
98{
99 pthread_mutexattr_init(&mutex_attr);
100#ifdef _DEBUG
101 pthread_mutexattr_settype(&mutex_attr,PTHREAD_MUTEX_ERRORCHECK);
102#else
103 pthread_mutexattr_settype(&mutex_attr,PTHREAD_MUTEX_NORMAL);
104#endif
105
106 pthread_mutex_init(&mutex,&mutex_attr);
107 pthread_cond_init(&cond,NULL);
108
109 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "Creating TimerManager");
110} // end constructor TimerManager
111
112/** Destroy TimerManager. */
113TimerManager::~TimerManager() {
114#ifndef _NO_LOGGING
115 int num = cleanup();
116#else
117 cleanup();
118#endif
119 DLog("Timer", "Destroying TimerManager with " << num << " timers");
120 pthread_cond_destroy(&cond);
121 pthread_mutex_destroy(&mutex);
122 pthread_mutexattr_destroy(&mutex_attr);
123} // end destructor TimerManager
124
125/** Start a timer relative to current time.
126 * @return ID of the new timer.
127 */
128timer_id_t
129TimerManager::start_relative(TimerCallback* tc, int32 sec, int32 msec, timer_callback_param_t tcp)
130{
131 struct timespec timeofday = {0,0};
132 struct timespec reltime = {0,0};
133 struct timespec alarm = {0,0};
134 timer *t = NULL;
135 timer_id_t result = 0;
136 fill_timespec(reltime,sec,msec);
137 // calculate absolute alarm time
138 gettimeofday_timespec(timeofday);
139 add_timespecs(timeofday,reltime,alarm);
140 // begin critical section
141 pthread_mutex_lock(&mutex); // install_cleanup_mutex_lock(&mutex);
142 // start timer
143 t = new(nothrow) timer(alarm,tc,tcp);
144 if (t) {
145 result = t->id;
146 // insert into list
147 insert_into_list(t);
148 // insert into hash
149 hashmap[result] = t;
150 // wake up threads
151 pthread_cond_signal(&cond);
152 } else {
153 Log(ERROR_LOG,LOG_CRIT, "Timer", "TimerManager::start_relative() is unable to create timer object");
154 } // end if t
155 // end critical section
156 pthread_mutex_unlock(&mutex); // uninstall_cleanup(1);
157 return result;
158} // end start_relative
159
160/** Start an absolute timer.
161 * @return ID of the new timer.
162 */
163timer_id_t
164TimerManager::start_absolute(TimerCallback* tc, int32 sec, int32 msec, timer_callback_param_t tcp) {
165 struct timespec alarm = {0,0};
166 timer *t = NULL;
167 timer_id_t result = 0;
168 fill_timespec(alarm,sec,msec);
169 // begin critical section
170 pthread_mutex_lock(&mutex); // install_cleanup_mutex_lock(&mutex);
171 // start timer
172 t = new(nothrow) timer(alarm,tc,tcp);
173 if (t) {
174 result = t->id;
175 // insert into list
176 insert_into_list(t);
177 // insert into hash
178 hashmap[result] = t;
179 // wake up threads
180 pthread_cond_signal(&cond);
181 } else {
182 Log(ERROR_LOG,LOG_CRIT, "Timer", "TimerManager::start_absolute() is unable to create timer object");
183 } // end if t
184 // end critical section
185 pthread_mutex_unlock(&mutex); // uninstall_cleanup(1);
186 return result;
187} // end start_absolute
188
189/** Restart a timer and set the alarm relative to the current time.
190 * The timer must exist and go off in the future.
191 * @return true on success, false otherwise.
192 */
193bool
194TimerManager::restart_relative(timer_id_t id, int32 sec, int32 msec)
195{
196 struct timespec timeofday = {0,0};
197 struct timespec reltime = {0,0};
198 struct timespec alarm = {0,0};
199 timer *t = NULL;
200 bool result = false;
201 timer_hashmap_it_t hit;
202 fill_timespec(reltime,sec,msec);
203 // calculate absolute alarm time
204 gettimeofday_timespec(timeofday);
205 add_timespecs(timeofday,reltime,alarm);
206 // begin critical section
207 pthread_mutex_lock(&mutex); // install_cleanup_mutex_lock(&mutex);
208 // try to get timer with given ID
209 if ((hit=hashmap.find(id))!=hashmap.end()) t = hit->second; else t = NULL;
210 if (t) {
211 // delete from list, set new alarm and insert
212 delete_from_list(t);
213 t->time = alarm;
214 insert_into_list(t);
215 // wake up threads
216 pthread_cond_signal(&cond);
217 result = true;
218 } else {
219 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::restart_relative() is unable to restart timer " << id);
220 } // end if t
221 pthread_mutex_unlock(&mutex); // uninstall_cleanup(1);
222 return result;
223} // end restart_relative
224
225
226/** Restart a timer and set the alarm to an absolute time.
227 * The timer must exist and go off in the future.
228 * @return true on success, false otherwise.
229 */
230bool
231TimerManager::restart_absolute(timer_id_t id, int32 sec, int32 msec)
232{
233 struct timespec alarm = {0,0};
234 timer *t = NULL;
235 bool result = false;
236 timer_hashmap_it_t hit;
237 fill_timespec(alarm,sec,msec);
238 // begin critical section
239 pthread_mutex_lock(&mutex); // install_cleanup_mutex_lock(&mutex);
240 // try to get timer with given ID
241 if ((hit=hashmap.find(id))!=hashmap.end()) t = hit->second; else t = NULL;
242 if (t) {
243 // delete from list, set new alarm and insert
244 delete_from_list(t);
245 t->time = alarm;
246 insert_into_list(t);
247 // wake up threads
248 pthread_cond_signal(&cond);
249 result = true;
250 } else {
251 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::restart_relative() is unable to restart timer " << id);
252 } // end if t
253 pthread_mutex_unlock(&mutex); // uninstall_cleanup(1);
254 return result;
255} // end restart_absolute
256
257/** Stop the given timer. */
258bool TimerManager::stop(timer_id_t id)
259{
260 timer *t = NULL;
261 bool result = false;
262 timer_hashmap_it_t hit;
263 // begin critical section
264 pthread_mutex_lock(&mutex); // install_cleanup_mutex_lock(&mutex);
265 if ((hit=hashmap.find(id))!=hashmap.end()) {
266 t = hit->second;
267 // erase from hashmap
268 hashmap.erase(hit);
269 } else t = NULL;
270 // delete from list if t exists
271 if (t) {
272 delete_from_list(t);
273 // wake up threads
274 pthread_cond_signal(&cond);
275 result = true;
276 } else {
277 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::stop() is unable to stop timer " << id);
278 } // end if t
279 pthread_mutex_unlock(&mutex); // uninstall_cleanup(1);
280 return result;
281} // end stop
282
283/** Check if timers have elapsed and call their callbacks.
284 * @return the number of elapsed timers.
285 */
286uint32
287TimerManager::check_timers()
288{
289 timer *elapsed;
290 pthread_mutex_lock(&mutex); // install_cleanup_mutex_lock(&mutex);
291 elapsed = collect_elapsed();
292 pthread_mutex_unlock(&mutex); // uninstall_cleanup(1);
293 return process_elapsed();
294} // end check_timers
295
296/** Like check_timers() but waits msec milliseconds for a timer to elapse. */
297uint32
298TimerManager::check_timers_wait(int32 msec)
299{
300 struct timespec now = {0,0};
301 struct timespec reltime = {0,0};
302 struct timespec abstime = {0,0};
303 timer* elapsed;
304 int wait_res = 0;
305 fill_timespec(reltime,0,msec);
306 gettimeofday_timespec(now);
307 // calculate timespec for pthread_cond_timedwait()
308 add_timespecs(now,reltime,abstime);
309 timer maxwait(abstime,NULL,NULL,false);
310 // begin critical section
311 pthread_mutex_lock(&mutex); // install_cleanup_mutex_lock(&mutex);
312 // look for elapsed timers until timeout
313 elapsed = collect_elapsed();
314 while ((!elapsed) && (wait_res!=ETIMEDOUT)) {
315 // neither timeout nor elapsed timers
316 // if there is a timer in the list, wait until it elapses.
317 // otherwise wait abstime.
318 if (first() && ((*first())<=maxwait)) abstime = first()->time;
319 else abstime = maxwait.time;
320 // wait for condition or timeout
321 wait_res = pthread_cond_timedwait(&cond,&mutex,&abstime);
322#ifdef DEBUG_TIMER
323 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::check_timers_wait() - timedwait returned " << wait_res << ":" << strerror(wait_res));
324#endif
325 elapsed = collect_elapsed();
326#ifdef DEBUG_TIMER
327 if (elapsed)
328 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::check_timers_wait() - collect_elapsed() returned " << elapsed->id);
329#endif
330 } // end while !elapsed and !wait_res
331 // end critical section
332 pthread_mutex_unlock(&mutex); // uninstall_cleanup(1);
333 // now either timeout, cancellation or elapsed timers
334 return process_elapsed();
335} // end check_timers_wait
336
337/** Stop all timers. */
338uint32
339TimerManager::stop_all()
340{
341 uint32 result = 0;
342 pthread_mutex_lock(&mutex); // install_cleanup_mutex_lock(&mutex);
343 result = cleanup();
344 if (result) {
345 // wake up threads
346 pthread_cond_signal(&cond);
347 } // end if result
348 pthread_mutex_unlock(&mutex); // uninstall_cleanup(1);
349 return result;
350} // end stop_all_timers
351
352/** Stop all timers without locking the mutex.
353 * So this can be called safely inside the TimerManager constructor.
354 */
355uint32
356TimerManager::cleanup()
357{
358 uint32 num = 0;
359 // clear hashmap
360 hashmap.clear();
361
362 // delete all timers
363 timer *curr= 0;
364 while (!active_timerlist.empty())
365 {
366 if ( (curr= active_timerlist.front()) != 0 )
367 {
368 delete curr;
369 active_timerlist.pop_front();
370 num++;
371 }
372 } // end while
373 return num;
374} // end cleanup
375
376/** Insert a timer object into the timer list.
377 * Timers are stored in an ordered list, so when checking for elapsed timers
378 * it's enough to check timers beginning at the front end until one is
379 * still running.
380 */
381inline
382void
383TimerManager::insert_into_list(timer *t)
384{
385 timerlist_t::iterator listiter= active_timerlist.begin();
386 while(listiter != active_timerlist.end())
387 {
388 if ( *listiter )
389 {
390 // if current element is greater than *t, leave the loop
391 if (!( *(*listiter) <= *t ))
392 break;
393
394 }
395 listiter++;
396 } // end for
397 active_timerlist.insert(listiter,t);
398
399
400#ifdef DEBUG_TIMER
401 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::insert_into_list() - inserting timer " << t->id << " list now:");
402#endif
403
404#ifdef DEBUG_TIMER
405 for(timerlist_t::iterator listpiter= active_timerlist.begin();
406 listpiter != active_timerlist.end();
407 listpiter++)
408 {
409 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::insert_into_list() - timer list, timer#: " << (*listpiter ? (*listpiter)->id : 0));
410 }
411#endif
412
413} // end insert_into_list
414
415/** Delete a timer from the ordered timer list.
416 * The timer object is NOT freed because it may be needed for executing its
417 * callback or for restarting.
418 * The timer objects must be linked correctly, no checking is done.
419 */
420void
421TimerManager::delete_from_list(timer *t) {
422
423 if (!t) return;
424
425#ifdef DEBUG_TIMER
426 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::delete_from_list() - deleting timer " << t->id);
427#endif
428 timerlist_t::iterator listiter= find(active_timerlist.begin(),active_timerlist.end(),t);
429 if ( listiter != active_timerlist.end() )
430 active_timerlist.erase(listiter);
431#ifdef DEBUG_TIMER
432 if (first())
433 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::delete_from_list() - first timer now " << (first() ? first()->id : 0) );
434#endif
435} // end delete_from_list
436
437/** Collect all elapsed timers in the elapsed_timerlist and delete them already
438 * from the hashmap. The timers are deleted in process_elapsed().
439 * You must lock the TimerManager mutex before collecting timers.
440 * When timers have elapsed, the condition is signaled.
441 */
442TimerManager::timer*
443TimerManager::collect_elapsed()
444{
445 struct timespec tod;
446 gettimeofday_timespec(tod);
447 timer now(tod,NULL,NULL,false);
448
449 timerlist_t::iterator currentit = active_timerlist.begin();
450 timer* curr= first();
451#ifdef DEBUG_TIMER
452 if (curr)
453 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::collect_elapsed() - first timer is " << curr->id);
454#endif
455
456 // search the last timer in list <= now
457 while (curr && ((*curr)<=now))
458 { // current timer is already elapsed
459 hashmap.erase(curr->id);
460#ifdef DEBUG_TIMER
461 Log(DEBUG_LOG,LOG_NORMAL, "Timer", "TimerManager::collect_elapsed() - deleted elapsed timer " << curr->id);
462#endif
463 // remember it in elapsed list
464 elapsed_timerlist.push_back(curr);
465 // delete it from the active timer list
466 currentit= active_timerlist.erase(currentit);
467
468 curr= (currentit != active_timerlist.end()) ? *currentit : 0;
469 } // end while
470 // List is ordered, so all timers before currentit are elapsed.
471
472 timer* elapsed = elapsed_timerlist.empty() ? 0 : elapsed_timerlist.front();
473
474 // wake up threads
475 if (elapsed) pthread_cond_signal(&cond);
476
477 return elapsed;
478} // end collect_elapsed
479
480/** Process a list of timers by executing the callback routines and deleting
481 * the timer objects. They are NOT deleted from the TimerManager hashmap.
482 * The mutex should not be locked when callbacks are executed, because
483 * it is unpossible to start a new timer inside a callback routine when
484 * the mutex is still locked.
485 * @see collect_elapsed()
486 */
487uint32 TimerManager::process_elapsed()
488{
489 uint32 num = 0;
490 timer *tmp = NULL;
491
492 // for every elapsed timer (list should be prepared by collect_elapsed) do_callback()
493 for (timerlist_t::iterator elapsed_it= elapsed_timerlist.begin();
494 elapsed_it != elapsed_timerlist.end();
495 elapsed_it= elapsed_timerlist.erase(elapsed_it))
496 {
497 // invoke callback function on elapsed timer
498 if ( (tmp = *elapsed_it) != 0)
499 {
500 tmp->do_callback();
501 // when callback is finished, delete the stuff
502 delete tmp;
503 num++;
504 }
505 // get next elapsed timer
506 } // end for
507 return num;
508} // end process_elapsed
509
510/** Initialize a timer.
511 * If get_id is set (default) the timer gets a unique ID, otherwise the ID
512 * of the timer is 0.
513 */
514TimerManager::timer::timer(struct timespec& ts, TimerCallback* tc, timer_callback_param_t tcp, bool get_id) :
515 id (0),
516 time(ts),
517 callback(tc),
518 param(tcp)
519{
520 if (get_id) while (id==0) id = next_id++;
521} // end constructor timer
522
523/** This holds the timer ID of the next timer. */
524timer_id_t TimerManager::timer::next_id = 1;
525
526/** Check which timer goes off eralier.
527 * The timespec structores of the timers are expected to have correct
528 * format.
529 */
530inline
531bool
532TimerManager::timer::operator<=(const timer& t) {
533 if (time.tv_sec == t.time.tv_sec) {
534 // Seconds are equal, it depends on nanoseconds.
535 return (time.tv_nsec <= t.time.tv_nsec);
536 } else {
537 // Seconds are not equal, nanoseconds do not matter.
538 return (time.tv_sec < t.time.tv_sec);
539 }
540} // end operator<=
541
542inline
543void
544TimerManager::timer::do_callback() {
545 callback->timer_expired(id,param);
546} // end do_callback
547
548//@}
549
550} // end namespace protlib
Note: See TracBrowser for help on using the repository browser.