Wiselib
wiselib.testing/external_interface/pc/pc_interruptible_timer.h
Go to the documentation of this file.
00001 // vim: set noexpandtab ts=4 sw=4:
00002 
00003 #ifndef PC_INTERRUPTIBLE_TIMER_H
00004 #define PC_INTERRUPTIBLE_TIMER_H
00005 
00006 #include <time.h>
00007 #include <errno.h>
00008 #include <stdio.h>
00009 #include <signal.h>
00010 #include <pthread.h>
00011 #include <sys/time.h>
00012 #include <unistd.h>
00013 
00014 #include <vector>
00015 #include <iostream>
00016 
00017 #include "util/delegates/delegate.hpp"
00018 #include "pc_os.h"
00019 #include "util/pstl/list_static.h"
00020 
00021 #define PC_INTERRUPTIBLE_TIMER_DEBUG
00022 
00023 namespace wiselib {
00024    template<typename OsModel_P, size_t MaxTimers_P>
00025    class PCInterruptibleTimerModel {
00026       public:
00027          typedef OsModel_P OsModel;
00028          typedef suseconds_t millis_t;
00029          typedef suseconds_t micros_t;
00030          typedef delegate1<void, void*> timer_delegate_t;
00031          typedef PCInterruptibleTimerModel<OsModel_P, MaxTimers_P> self_t;
00032          typedef self_t* self_pointer_t;
00033          
00034          enum Restrictions {
00035             MAX_TIMERS = MaxTimers_P
00036          };
00037          
00038          PCInterruptibleTimerModel();
00039          PCInterruptibleTimerModel(PCOs& os);
00040          
00041          template<typename T, void (T::*TMethod)(void*)>
00042          int set_timer(millis_t millis, T* obj, void* userdata);
00043          
00044          int sleep(millis_t duration) {
00045             return ssleep( duration );
00046          }
00047 
00048       private:
00049          struct Timer {
00050             timer_delegate_t callback_;
00051             micros_t offset_;
00052             void *userdata_;
00053          };
00054 
00055          typedef list_static<OsModel, Timer, MAX_TIMERS> timers_list_t;
00056 
00057          static int ssleep(millis_t millis) {
00058             timespec interval, remainder;
00059 
00060             interval.tv_sec = millis / 1000;
00061             interval.tv_nsec = (millis % 1000) * 1000000;
00062 
00063             // Unblock SIGALRM.
00064             sigset_t signal_set, old_signal_set;
00065             if ( ( sigemptyset( &signal_set ) == -1 ) ||
00066                   ( sigaddset( &signal_set, SIGALRM ) == -1 ) ||
00067                   pthread_sigmask( SIG_UNBLOCK, &signal_set, &old_signal_set ) )
00068             {
00069                perror( "Failed to unblock SIGALRM" );
00070             }
00071 
00072             // nanosleep does not use SIGALRM and therefore does not interfer with the timer.
00073             while( ( nanosleep( &interval, &remainder ) == -1 ) && ( errno == EINTR ) ) {
00074                interval.tv_sec = remainder.tv_sec;
00075                interval.tv_nsec = remainder.tv_nsec;
00076             }
00077 
00078             // Block SIGALRM if it was blocked before.
00079             if( sigismember( &old_signal_set, SIGALRM ) == 1 ) {
00080                if ( ( sigemptyset( &signal_set ) == -1 ) ||
00081                      ( sigaddset( &signal_set, SIGALRM ) == -1 ) ||
00082                      pthread_sigmask( SIG_BLOCK, &signal_set, 0 ) )
00083                {
00084                   perror( "Failed to block SIGALRM" );
00085                }
00086             }
00087 
00088             return OsModel::SUCCESS;
00089          }
00090          
00091          // Sorted list of timers.
00092          // Time is stored relative, i.e. how many miliseconds after the
00093          // predecessor in the list the timer should fire
00094          static timers_list_t timers;
00095          static timers_list_t to_call;
00096 
00097          static void timer_handler_(int signum);
00098    }; // class PCInterruptibleTimerModel
00099    
00100    template<typename OsModel_P, size_t MaxTimers_P>
00101    typename PCInterruptibleTimerModel<OsModel_P, MaxTimers_P>::timers_list_t
00102    PCInterruptibleTimerModel<OsModel_P, MaxTimers_P>::timers;
00103 
00104    template<typename OsModel_P, size_t MaxTimers_P>
00105    typename PCInterruptibleTimerModel<OsModel_P, MaxTimers_P>::timers_list_t
00106    PCInterruptibleTimerModel<OsModel_P, MaxTimers_P>::to_call;
00107 
00108 
00109    template<typename OsModel_P, size_t MaxTimers_P>
00110    PCInterruptibleTimerModel<OsModel_P, MaxTimers_P>::PCInterruptibleTimerModel() {
00111       struct sigaction alarm_action;
00112       alarm_action.sa_handler = &PCInterruptibleTimerModel::timer_handler_;
00113       alarm_action.sa_flags = 0;
00114 
00115       if( ( sigemptyset( &alarm_action.sa_mask ) == -1 ) ||
00116          ( sigaddset( &alarm_action.sa_mask, SIGALRM ) == -1 ) ||
00117          ( sigaction( SIGALRM, &alarm_action, 0 ) == -1 ) )
00118       {
00119          perror( "Failed to install SIGALRM-handler" );
00120       }
00121    }
00122    
00123    template<typename OsModel_P, size_t MaxTimers_P>
00124    PCInterruptibleTimerModel<OsModel_P, MaxTimers_P>::PCInterruptibleTimerModel(PCOs& os) {
00125       struct sigaction alarm_action;
00126       alarm_action.sa_handler = &PCInterruptibleTimerModel::timer_handler_;
00127       alarm_action.sa_flags = 0;
00128 
00129       if( ( sigemptyset( &alarm_action.sa_mask ) == -1 ) ||
00130             ( sigaddset( &alarm_action.sa_mask, SIGALRM ) == -1 ) ||
00131             ( sigaction( SIGALRM, &alarm_action, 0 ) == -1 ) )
00132       {
00133          perror( "Failed to install SIGALRM-handler" );
00134       }
00135    }
00136    
00137    template<typename OsModel_P, size_t MaxTimers_P>
00138    template<typename T, void (T::*TMethod)(void*)>
00139    int PCInterruptibleTimerModel<OsModel_P, MaxTimers_P>::
00140 	set_timer(millis_t millis, T* obj, void* userdata) {
00141       struct itimerval timer;
00142 
00143       // Call with millis==0 would deactivate the timer.
00144       if( millis <= 0 )
00145       {
00146          std::cerr << "WARNING: set_timer(): millis has to be at leat 1!" << std::endl;
00147          return OsModel::ERR_UNSPEC;
00148       }
00149 
00150       if( timers.full() )
00151       {
00152          std::cerr << "WARNING: Could not add timer. Timer-list is full!" << std::endl;
00153          return OsModel::ERR_UNSPEC;
00154       }
00155 
00156       // Block SIGALRM to avoid interrupting call of timer_handler.
00157       sigset_t signal_set, old_signal_set;
00158       if ( ( sigemptyset( &signal_set ) == -1 ) ||
00159             ( sigaddset( &signal_set, SIGALRM ) == -1 ) ||
00160             pthread_sigmask( SIG_BLOCK, &signal_set, &old_signal_set ) )
00161       {
00162          perror( "Failed to block SIGALRM" );
00163          return OsModel::ERR_UNSPEC;
00164       }
00165 
00166       getitimer(ITIMER_REAL, &timer);
00167       
00168       // Update front timer value with the timer value, so sorting the new
00169       // timer in is more natural
00170       if(!timers.empty()) {
00171          timers.front().offset_ = timer.it_value.tv_sec * 1000000 + timer.it_value.tv_usec;
00172       }
00173       
00174       // insertion-sort the new timer
00175       micros_t t = 0, t_prev = 0;
00176       typename timers_list_t::iterator iter = timers.begin();
00177       for(; iter != timers.end(); ++iter) {
00178          t += iter->offset_;
00179          
00180          if(millis*1000 < t) {
00181             break;
00182          }
00183          
00184          t_prev = t;
00185       } // for
00186             
00187       Timer new_timer;
00188       new_timer.callback_ = timer_delegate_t::from_method<T, TMethod>(obj);
00189       new_timer.offset_ = millis*1000 - t_prev;
00190       new_timer.userdata_ = userdata;
00191       
00192       // fix following offset
00193       if(iter != timers.end()) {
00194          assert(iter->offset_ >= new_timer.offset_);
00195          iter->offset_ -= new_timer.offset_;
00196       }
00197       timers.insert(iter, new_timer);
00198       
00199       // Set the timer to the offset of the first entry
00200       timer.it_interval.tv_sec = 0;
00201       timer.it_interval.tv_usec = 0;
00202       timer.it_value.tv_sec = timers.front().offset_ / 1000000;
00203       timer.it_value.tv_usec = timers.front().offset_ % 1000000;
00204 
00205       if( ( timer.it_value.tv_sec == 0 ) && ( timer.it_value.tv_usec < 100 ) )
00206          timer.it_value.tv_usec = 100;
00207 
00208 #ifdef PC_INTERRUPTIBLE_TIMER_DEBUG
00209       std::cout << " set_timer --> tvsec: "<< timer.it_value.tv_sec <<" --> usec: "<< timer.it_value.tv_usec << std::endl;
00210 #endif
00211 
00212       setitimer(ITIMER_REAL, &timer, 0);
00213 
00214       //Unblock alrm-signal if necessary
00215       if(  sigismember( &old_signal_set, SIGALRM ) == 1 )
00216       {
00217          if ( ( sigemptyset( &signal_set ) == -1 ) ||
00218                ( sigaddset( &signal_set, SIGALRM ) == -1 ) ||
00219                pthread_sigmask( SIG_UNBLOCK, &signal_set, 0 ) )
00220          {
00221             perror( "Failed to unblock SIGALRM" );
00222             return OsModel::ERR_UNSPEC;
00223          }
00224       }
00225 
00226       return OsModel::SUCCESS;
00227    }
00228    
00229    template<typename OsModel_P, size_t MaxTimers_P>
00230    void PCInterruptibleTimerModel<OsModel_P, MaxTimers_P>::
00231 	timer_handler_(int signum) {
00232 #ifdef PC_INTERRUPTIBLE_TIMER_DEBUG
00233       std::cout << "[ ";
00234 #endif
00235 
00236       int save_errno = errno;
00237 
00238       sigset_t signal_set;
00239       struct itimerval timer;
00240       Timer current;
00241 
00242       bool finished = false;
00243       while( !finished )
00244       {
00245          if( timers.empty() ) {
00246             finished = true;
00247          } else {
00248             getitimer(ITIMER_REAL, &timer);
00249             micros_t offset =  timer.it_value.tv_sec * 1000000 + timer.it_value.tv_usec;
00250 
00251 #ifdef PC_INTERRUPTIBLE_TIMER_DEBUG
00252             std::cout << "offset: " << offset << std::endl;
00253 #endif
00254 
00255             if( offset > 100 ) {
00256                finished = true;
00257             } else {
00258                current = timers.front();
00259                timers.pop_front();
00260 
00261                if( to_call.full() ) {
00262                   std::cout << "Warning: Could not execute timer-callback because container to_call is full." << std::endl;
00263                } else {
00264                   to_call.push_back( current );
00265                }
00266 
00267                if( !timers.empty() ) {
00268                   timer.it_interval.tv_sec = 0;
00269                   timer.it_interval.tv_usec = 0;
00270                   timer.it_value.tv_sec = timers.front().offset_ / 1000000;
00271                   timer.it_value.tv_usec = timers.front().offset_ % 1000000;
00272 
00273                   if( ( timer.it_value.tv_sec == 0 ) && ( timer.it_value.tv_usec < 100 ) )
00274                      timer.it_value.tv_usec = 100;
00275 
00276 #ifdef PC_INTERRUPTIBLE_TIMER_DEBUG
00277                   std::cout << " --> tvsec: "<< timer.it_value.tv_sec <<" --> usec: "<< timer.it_value.tv_usec << std::endl;
00278 #endif
00279 
00280                   setitimer(ITIMER_REAL, &timer, 0);
00281                }
00282             }
00283          }
00284       }
00285 
00286       finished = false;
00287       while( !finished )
00288       {
00289          // Block SIGALRM to avoid interrupting call of timer_handler.
00290          if ( ( sigemptyset( &signal_set ) == -1 ) ||
00291                ( sigaddset( &signal_set, SIGALRM ) == -1 ) ||
00292                pthread_sigmask( SIG_BLOCK, &signal_set, 0 ) )
00293          {
00294             perror( "Failed to block SIGALRM" );
00295          }
00296 
00297          if( to_call.empty() )
00298             finished = true;
00299          else
00300          {
00301             current = to_call.front();
00302             to_call.pop_front();
00303          }
00304 
00305          // Unblock SIGALRM.
00306          if ( ( sigemptyset( &signal_set ) == -1 ) ||
00307                ( sigaddset( &signal_set, SIGALRM ) == -1 ) ||
00308                pthread_sigmask( SIG_UNBLOCK, &signal_set, 0 ) )
00309          {
00310             perror( "Failed to unblock SIGALRM" );
00311          }
00312 
00313          if( !finished )
00314          {
00315 #ifdef PC_INTERRUPTIBLE_TIMER_DEBUG
00316             std::cout << "(" << std::endl;
00317 #endif
00318             current.callback_(current.userdata_);
00319 #ifdef PC_INTERRUPTIBLE_TIMER_DEBUG
00320             std::cout << ")" << std::endl;
00321 #endif
00322          }
00323       }
00324 
00325       errno = save_errno;
00326 
00327 #ifdef PC_INTERRUPTIBLE_TIMER_DEBUG
00328       std::cout << "]" << std::endl;
00329 #endif
00330    }
00331    
00332 } // namespace wiselib
00333 
00334 #endif // PC_INTERRUPTIBLE_TIMER_H
00335 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines