close Warning: Can't use blame annotator:
No changeset 1891 in the repository

source: source/ariba/communication/modules/transport/protlib/timer.cpp@ 5638

Last change on this file since 5638 was 5638, checked in by Christoph Mayer, 15 years ago

adress detection aufgeräumt, network info für bleutooth, data stream (hopeful crash fix), logging auf maemo nur warn, ...

File size: 16.5 KB
RevLine 
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.