Wiselib
|
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