An Overlay-based
Virtual Network Substrate
SpoVNet

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

Last change on this file since 5641 was 5641, checked in by Christoph Mayer, 14 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.