Orocos Real-Time Toolkit  2.6.0
Timer.cpp
00001 /***************************************************************************
00002   tag: FMTC  Tue Mar 11 21:49:25 CET 2008  Timer.cpp
00003 
00004                         Timer.cpp -  description
00005                            -------------------
00006     begin                : Tue March 11 2008
00007     copyright            : (C) 2008 FMTC
00008     email                : peter.soetens@fmtc.be
00009 
00010  ***************************************************************************
00011  *   This library is free software; you can redistribute it and/or         *
00012  *   modify it under the terms of the GNU General Public                   *
00013  *   License as published by the Free Software Foundation;                 *
00014  *   version 2 of the License.                                             *
00015  *                                                                         *
00016  *   As a special exception, you may use this file as part of a free       *
00017  *   software library without restriction.  Specifically, if other files   *
00018  *   instantiate templates or use macros or inline functions from this     *
00019  *   file, or you compile this file and link it with other files to        *
00020  *   produce an executable, this file does not by itself cause the         *
00021  *   resulting executable to be covered by the GNU General Public          *
00022  *   License.  This exception does not however invalidate any other        *
00023  *   reasons why the executable file might be covered by the GNU General   *
00024  *   Public License.                                                       *
00025  *                                                                         *
00026  *   This library is distributed in the hope that it will be useful,       *
00027  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00028  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00029  *   Lesser General Public License for more details.                       *
00030  *                                                                         *
00031  *   You should have received a copy of the GNU General Public             *
00032  *   License along with this library; if not, write to the Free Software   *
00033  *   Foundation, Inc., 59 Temple Place,                                    *
00034  *   Suite 330, Boston, MA  02111-1307  USA                                *
00035  *                                                                         *
00036  ***************************************************************************/
00037 
00038 
00039 #include "Timer.hpp"
00040 #include "MutexLock.hpp"
00041 #include "../Activity.hpp"
00042 #include "../Logger.hpp"
00043 
00044 namespace RTT {
00045     using namespace base;
00046     using namespace os;
00047 
00048     bool Timer::initialize() {
00049         // only start if non periodic.
00050         return this->getThread()->getPeriod() == 0;
00051     }
00052     void Timer::finalize() {}
00053 
00054     void Timer::step() {
00055         // no implementation for periodic execution.
00056     }
00057 
00058     void Timer::loop()
00059     {
00060         // This code is executed from mThread's thread
00061         while (!mdo_quit) {
00062             Time wake_up_time;
00063             TimerId next_timer_id = 0;
00064 
00065             // Select next timer.
00066             {// This scope is for MutexLock.
00067                 // find wake_up_time
00068                 // check timers queue.
00069                 MutexLock locker(m);
00070                 // We can't use infinite as the OS may internally use time_spec, which can not
00071                 // represent as much in the future (until 2038) // XXX Year-2038 Bug
00072                 wake_up_time = (TimeService::InfiniteNSecs/4)-1;
00073                 for (TimerIds::iterator it = mtimers.begin(); it != mtimers.end(); ++it) {
00074                     if ( it->first != 0 && it->first < wake_up_time  ) {
00075                         wake_up_time = it->first;
00076                         next_timer_id = it - mtimers.begin();
00077                     }
00078                 }
00079             }// MutexLock
00080 
00081             // Wait
00082             int ret = 0;
00083             if ( wake_up_time > mTimeserv->getNSecs() )
00084                 ret = msem.waitUntil( wake_up_time ); // case of no timers or running timers
00085             else
00086                 ret = -1; // case of timer overrun.
00087 
00088             // Timeout handling
00089             if (ret == -1) {
00090                 // a timer expired
00091                 // First: reset/reprogram the timer that expired:
00092                 {
00093                     MutexLock locker(m);
00094                     // detect corner case for resize:
00095                     if ( next_timer_id < int(mtimers.size()) ) {
00096                         // now clear or reprogram it.
00097                         TimerIds::iterator tim = mtimers.begin() + next_timer_id;
00098                         if ( tim->second ) {
00099                             // periodic timer
00100                             tim->first += tim->second;
00101                         } else {
00102                             // aperiodic timer
00103                             tim->first = 0;
00104                         }
00105                     }
00106                 }
00107                 // Second: send the timeout signal and allow (within the callback)
00108                 // to reprogram the timer.
00109                 // If we would first call timeout(), the code above would overwrite
00110                 // user settings.
00111                 timeout( next_timer_id );
00112             }
00113         }
00114     }
00115 
00116     bool Timer::breakLoop()
00117     {
00118         mdo_quit = true;
00119         msem.signal();
00120         return true;
00121     }
00122 
00123     Timer::Timer(TimerId max_timers, int scheduler, int priority)
00124         : mThread(0), msem(0), mdo_quit(false)
00125     {
00126         mTimeserv = TimeService::Instance();
00127         mtimers.resize(max_timers);
00128         if (scheduler != -1) {
00129             mThread = new Activity(scheduler, priority, 0.0, this, "Timer");
00130             mThread->start();
00131         }
00132     }
00133 
00134     Timer::~Timer()
00135     {
00136         delete mThread;
00137     }
00138 
00139 
00140     void Timer::timeout(TimerId timer_id)
00141     {
00142         // User must implement this method.
00143     }
00144 
00145     void Timer::setMaxTimers(TimerId max)
00146     {
00147         MutexLock locker(m);
00148         mtimers.resize(max, std::make_pair(Time(0), Time(0)) );
00149     }
00150 
00151     bool Timer::startTimer(TimerId timer_id, double period)
00152     {
00153         if ( timer_id < 0 || timer_id > int(mtimers.size()) || period < 0.0)
00154         {
00155             log(Error) << "Invalid timer id or period" << endlog();
00156             return false;
00157         }
00158 
00159         Time due_time = mTimeserv->getNSecs() + Seconds_to_nsecs( period );
00160 
00161         {
00162             MutexLock locker(m);
00163             mtimers[timer_id].first = due_time;
00164             mtimers[timer_id].second = Seconds_to_nsecs( period );
00165         }
00166         msem.signal();
00167         return true;
00168     }
00169 
00170     bool Timer::arm(TimerId timer_id, double wait_time)
00171     {
00172         if ( timer_id < 0 || timer_id > int(mtimers.size()) || wait_time < 0.0)
00173         {
00174             log(Error) << "Invalid timer id or wait time" << endlog();
00175             return false;
00176         }
00177 
00178         Time now = mTimeserv->getNSecs();
00179         Time due_time = now + Seconds_to_nsecs( wait_time );
00180 
00181         {
00182             MutexLock locker(m);
00183             mtimers[timer_id].first  = due_time;
00184             mtimers[timer_id].second = 0;
00185         }
00186         msem.signal();
00187         return true;
00188     }
00189 
00190     bool Timer::isArmed(TimerId timer_id) const
00191     {
00192         MutexLock locker(m);
00193         if (timer_id < 0 || timer_id > int(mtimers.size()) )
00194         {
00195             log(Error) << "Invalid timer id" << endlog();
00196             return false;
00197         }
00198         return mtimers[timer_id].first != 0;
00199     }
00200 
00201     double Timer::timeRemaining(TimerId timer_id) const
00202     {
00203         MutexLock locker(m);
00204         if (timer_id < 0 || timer_id > int(mtimers.size()) )
00205         {
00206             log(Error) << "Invalid timer id" << endlog();
00207             return 0.0;
00208         }
00209         Time now = mTimeserv->getNSecs();
00210         Time result = mtimers[timer_id].first - now;
00211         // detect corner cases.
00212         if ( result < 0 )
00213             return 0.0;
00214         return nsecs_to_Seconds( result );
00215     }
00216 
00217     bool Timer::killTimer(TimerId timer_id)
00218     {
00219         MutexLock locker(m);
00220         if (timer_id < 0 || timer_id > int(mtimers.size()) )
00221         {
00222             log(Error) << "Invalid timer id" << endlog();
00223             return false;
00224         }
00225         mtimers[timer_id].first = 0;
00226         mtimers[timer_id].second = 0;
00227         return true;
00228     }
00229 
00230 
00231 
00232 }