PeriodicThread.cpp

00001 /***************************************************************************
00002   tag: Peter Soetens  Mon May 10 19:10:28 CEST 2004  PeriodicThread.cxx 
00003 
00004                         PeriodicThread.cxx -  description
00005                            -------------------
00006     begin                : Mon May 10 2004
00007     copyright            : (C) 2004 Peter Soetens
00008     email                : peter.soetens@mech.kuleuven.ac.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 #include "fosi_internal_interface.hpp"
00039 #include <os/PeriodicThread.hpp>
00040 #include <os/Time.hpp>
00041 #include "os/threads.hpp"
00042 #include "Logger.hpp"
00043 
00044 #include "../rtt-config.h"
00045 
00046 #ifdef OROPKG_OS_THREAD_SCOPE
00047 # include "dev/DigitalOutInterface.hpp"
00048 #endif
00049 
00050 namespace RTT
00051 { namespace OS {
00052     using RTT::Logger;
00053     using namespace detail;
00054 
00055     void *periodicThread(void* t) 
00056     {
00060         PeriodicThread* task = static_cast<OS::PeriodicThread*> (t);
00061         // acquire the resulting scheduler type.
00062         //task->msched_type = rtos_task_get_scheduler( task->getTask() );
00063         task->configure();
00064 
00065 #ifdef OROPKG_OS_THREAD_SCOPE
00066         // order thread scope toggle bit on thread number
00067         unsigned int bit = task->threadNumber();
00068         if ( task->d )
00069             task->d->switchOff( bit );
00070 #endif // OROPKG_OS_THREAD_SCOPE
00071         int overruns = 0;
00072         while ( !task->prepareForExit ) {
00073 #ifndef ORO_EMBEDDED
00074             try {
00075 #endif // !ORO_EMBEDDED
00076 
00079                 while(1) 
00080                     {
00081                         if( !task->running ) { // more efficient than calling isRunning()
00082                             // consider this the 'configuration state'
00083                             overruns = 0;
00084                             // drop out of periodic mode:
00085                             rtos_task_set_period(task->getTask(), 0);
00086                             //                             Logger::log() << Logger::Info <<rtos_task_get_name(&rtos_task)<<"  signals done !" << Logger::endl;
00087                             rtos_sem_signal( &(task->confDone) ); // signal we are ready for command
00088                             //                             Logger::log() << Logger::Info <<rtos_task_get_name(&rtos_task)<<"  sleeps !" << Logger::endl;
00089                             rtos_sem_wait( &(task->sem) );      // wait for command.
00090                             //                             Logger::log() << Logger::Info <<rtos_task_get_name(&rtos_task)<<"  awoken !" << Logger::endl;
00091                             task->configure();             // check for reconfigure
00092                             //                             Logger::log() << Logger::Info <<rtos_task_get_name(&rtos_task)<<"  config done !" << Logger::endl;
00093                             if ( task->prepareForExit )    // check for exit
00094                                 {
00095                                     break; // break while(1) {}
00096                                 }
00097                             // end of configuration
00098                         } else {
00099                             // task->isRunning()
00100 #ifdef OROPKG_OS_THREAD_SCOPE
00101                             if ( task->d )
00102                                 task->d->switchOn( bit );
00103 #endif
00104                             {
00105                                 task->step(); // one cycle
00106                             }
00107 #ifdef OROPKG_OS_THREAD_SCOPE
00108                             if ( task->d )
00109                                 task->d->switchOff( bit );
00110 #endif
00111                             // return non-zero to indicate overrun.
00112                             if ( rtos_task_wait_period( task->getTask() ) != 0) {
00113                                 ++overruns;
00114                                 if ( overruns == task->maxOverRun )
00115                                     break;
00116                             } else if ( overruns != 0 )
00117                                 --overruns;
00118                         }
00119                     }
00120                 if ( overruns == task->maxOverRun ) {
00121 #ifdef OROPKG_OS_THREAD_SCOPE
00122                     if ( task->d )
00123                         task->d->switchOff( bit );
00124 #endif
00125                     task->emergencyStop();
00126                     Logger::In in(rtos_task_get_name(task->getTask()));
00127                     Logger::log() << Logger::Fatal << rtos_task_get_name(task->getTask()) <<" got too many periodic overruns in step() ("<< overruns << " times), stopped Thread !"<<Logger::nl;
00128                     Logger::log() <<" See PeriodicThread::setMaxOverrun() for info." << Logger::endl;
00129                 }
00130 #ifndef ORO_EMBEDDED
00131             } catch( ... ) {
00132 #ifdef OROPKG_OS_THREAD_SCOPE
00133                 if ( task->d )
00134                     task->d->switchOff( bit );
00135 #endif
00136                 task->emergencyStop();
00137                 Logger::In in(rtos_task_get_name(task->getTask()));
00138                 Logger::log() << Logger::Fatal << rtos_task_get_name(task->getTask()) <<" caught a C++ exception, stopped thread !"<<Logger::endl;
00139             }
00140 #endif // !ORO_EMBEDDED
00141         } // while (!prepareForExit)
00142     
00143         rtos_sem_signal( &(task->confDone));
00144 
00145         return 0;
00146     }
00147 
00148     void PeriodicThread::emergencyStop()
00149     {
00150         // set state to not running
00151         this->running = false;
00152         // execute finalize in current mode, even if hard.
00153         this->finalize();
00154     }
00155 
00156     PeriodicThread::PeriodicThread(int _priority, 
00157                                    const std::string & name, 
00158                                    Seconds periods, 
00159                                    RunnableInterface* r) :
00160         msched_type(ORO_SCHED_RT), running(false), prepareForExit(false),
00161         wait_for_step(true), runComp(r), 
00162         maxOverRun( OROSEM_OS_PERIODIC_THREADS_MAX_OVERRUN),
00163         period(Seconds_to_nsecs(periods))    // Do not call setPeriod(), since the semaphores are not yet used !
00164 #ifdef OROPKG_OS_THREAD_SCOPE
00165                                                          ,d(NULL)
00166 #endif
00167     {
00168         this->setup(_priority, name);
00169     }
00170 
00171     PeriodicThread::PeriodicThread(int scheduler, int _priority, 
00172                                    const std::string & name, 
00173                                    Seconds periods, 
00174                                    RunnableInterface* r) :
00175         msched_type(scheduler), running(false), prepareForExit(false),
00176         wait_for_step(true), runComp(r), 
00177         maxOverRun( OROSEM_OS_PERIODIC_THREADS_MAX_OVERRUN),
00178         period(Seconds_to_nsecs(periods))    // Do not call setPeriod(), since the semaphores are not yet used !
00179 #ifdef OROPKG_OS_THREAD_SCOPE
00180                                                          ,d(NULL)
00181 #endif
00182     {
00183         log(Info) << "Creating PeriodicThread for scheduler: "<< scheduler << endlog();
00184         this->setup(_priority, name);
00185     }
00186 
00187     void PeriodicThread::setup(int _priority, const std::string& name) 
00188     {
00189         Logger::In in("PeriodicThread");
00190         int ret;
00191         
00192         ret = rtos_sem_init(&sem, 0);
00193         if ( ret != 0 ) {
00194             Logger::log() << Logger::Critical << "Could not allocate configuration semaphore 'sem' for "<< rtos_task_get_name(&rtos_task) <<". Throwing std::bad_alloc."<<Logger::endl;
00195             rtos_sem_destroy( &sem );
00196 #ifndef ORO_EMBEDDED
00197             throw std::bad_alloc();
00198 #else
00199             return;
00200 #endif
00201         }
00202 
00203         ret = rtos_sem_init(&confDone, 0);
00204         if ( ret != 0 ) {
00205             Logger::log() << Logger::Critical << "Could not allocate configuration semaphore 'confDone' for "<< rtos_task_get_name(&rtos_task) <<". Throwing std::bad_alloc."<<Logger::endl;
00206             rtos_sem_destroy( &sem );
00207 #ifndef ORO_EMBEDDED
00208             throw std::bad_alloc();
00209 #else
00210             return;
00211 #endif
00212         }
00213 
00214         if (runComp)
00215             runComp->setThread(this);
00216 #ifdef OROPKG_OS_THREAD_SCOPE
00217         // Check if threadscope device already exists
00218         {
00219             if ( DigitalOutInterface::nameserver.getObject("ThreadScope") ){
00220                 d = DigitalOutInterface::nameserver.getObject("ThreadScope");
00221             }
00222             else{
00223                 log(Warning) << "Failed to find 'ThreadScope' object in DigitalOutInterface::nameserver." << endlog();
00224             }
00225         }
00226 #endif
00227         int rv = rtos_task_create(&rtos_task, _priority, name.c_str(), msched_type, periodicThread, this );
00228         if ( rv != 0 ) {
00229             Logger::log() << Logger::Critical << "Could not create thread "
00230                           << rtos_task_get_name(&rtos_task) <<"."<<Logger::endl;
00231             rtos_sem_destroy( &sem );
00232             rtos_sem_destroy( &confDone );
00233 #ifndef ORO_EMBEDDED
00234             throw std::bad_alloc();
00235 #else
00236             return;
00237 #endif
00238         }
00239  
00240         rtos_sem_wait(&confDone); // wait until thread is created.
00241  
00242         const char* modname = getName();
00243         Logger::In in2(modname);
00244         log(Info)<< "PeriodicThread created with scheduler type '"<< getScheduler() <<"', priority " << getPriority()
00245                  <<" and period "<< getPeriod() << "." << endlog();
00246 #ifdef OROPKG_OS_THREAD_SCOPE
00247         if (d){
00248             unsigned int bit = threadNumber();
00249             log(Info) << "ThreadScope :"<< modname <<" toggles bit "<< bit << endlog();
00250         }
00251 #endif
00252     }
00253     
00254     PeriodicThread::~PeriodicThread() 
00255     {
00256         Logger::In in("~PeriodicThread");
00257         if (this->isRunning())
00258             this->stop();
00259 
00260         log(Debug) << "Terminating "<< this->getName() <<endlog();
00261         terminate();
00262         log(Debug) << " done"<< endlog();
00263         rtos_sem_destroy(&confDone);
00264         rtos_sem_destroy(&sem);
00265 
00266         if (runComp)
00267             runComp->setThread(0);
00268 
00269     }
00270 
00271     bool PeriodicThread::run( RunnableInterface* r)
00272     {
00273         if ( isRunning() )
00274             return false;
00275         if (runComp)
00276             runComp->setThread(0);
00277         runComp = r;
00278         if (runComp)
00279             runComp->setThread(this);
00280         return true;
00281     }
00282 
00283 
00284     bool PeriodicThread::start() 
00285     {
00286         if ( running ) return false;
00287 
00288         Logger::log() << Logger::Debug << "Periodic Thread "<< rtos_task_get_name(&rtos_task) <<" started."<<Logger::endl;
00289 
00290         bool result;
00291         result = this->initialize();
00292 
00293         if (result == false) {
00294             Logger::log() << Logger::Critical << "Periodic Thread "<< rtos_task_get_name(&rtos_task) <<" failed to initialize()."<<Logger::endl;
00295             return false;
00296         }
00297 
00298         running=true;
00299 
00300         // be sure that confDone is set to zero
00301         // could be in case stop() times out.
00302         rtos_sem_trywait( &confDone );
00303         // signal start :
00304         rtos_task_make_periodic(&rtos_task, period );
00305         int ret = rtos_sem_signal(&sem);
00306         if ( ret != 0 )
00307             Logger::log() << Logger::Critical <<"PeriodicThread::start(): sem_signal returns "<< ret << Logger::endl;
00308         // do not wait, we did our job.
00309 
00310         return true;
00311     }
00312 
00313     bool PeriodicThread::stop() 
00314     {
00315         if ( !running ) return false;
00316 
00317         Logger::log() << Logger::Debug << "Periodic Thread "<< rtos_task_get_name(&rtos_task) <<" stopping...";
00318 
00319         running=false;
00320         int cnt = 0;
00321         int ret = -1;
00322 
00323         // wait until the loop detects running == false
00324         // we wait 10 times 5*period.
00325         while ( ret == -1 && cnt < 10 )
00326             {
00327                 // given time in argument is relative to 'now'
00328                 ret = rtos_sem_wait_timed( &confDone, 5*period );
00329                 // if ret == 0, confDone was signaled.
00330                 cnt++;
00331             } 
00332 
00333         if ( ret != 0 ) {
00334             Logger::log() << Logger::Debug << " failed."<<Logger::endl;
00335             Logger::log() << Logger::Critical << "The "<< rtos_task_get_name(&rtos_task) <<" thread seems to be blocked ( ret was "<< ret<<" .)"<<Logger::nl;
00336         }
00337         else
00338             Logger::log() << Logger::Debug << " done."<<Logger::endl;
00339         // drop out of periodic mode.
00340         rtos_task_make_periodic(&rtos_task, 0);
00341 
00342         //std::cout <<"Finalizing thread after "<<cnt<<" tries !"<<std::endl;
00343         // from now on, the thread waits on sem.
00344 
00345         this->finalize();
00346 
00347         return true;
00348     }
00349 
00350     bool PeriodicThread::isRunning() const
00351     {
00352         return running;
00353     }
00354 
00355     bool PeriodicThread::setScheduler(int sched_type)
00356     {
00357         if ( !running ) 
00358             {
00359                 if ( OS::CheckScheduler(sched_type) == false)
00360                     return false;
00361                 if ( this->getScheduler() == sched_type ) {
00362                     log(Debug) << "Scheduler type for Thread "<< rtos_task_get_name(&rtos_task) <<" is already configured as "<< sched_type << endlog();
00363                     return true;
00364                 }
00365 
00366                 log(Error) << "Setting scheduler type for Thread "<< rtos_task_get_name(&rtos_task) <<" to "<< sched_type;
00367                 msched_type = sched_type; 
00368                 rtos_sem_signal(&sem);
00369                 rtos_sem_wait(&confDone);
00370             }
00371         else {
00372             log() << "Failed to change scheduler for "<< rtos_task_get_name(&rtos_task) <<" since thread is still running."<<endlog(Warning);
00373             return false;
00374         }
00375         if (msched_type != rtos_task_get_scheduler(&rtos_task) )
00376             {
00377                 msched_type = rtos_task_get_scheduler(&rtos_task);
00378                 log() << ": failed."<<endlog(Error);
00379                 return false;
00380             }
00381         log() << ": done."<<endlog(Debug);
00382         return true;
00383 
00384 #if 0 
00385         // Alternative is not possible for RTAI: can only rt_make_hard_realtime() of calling thread!
00386         if ( rtos_task_set_scheduler(&rtos_task, sched_type) == 0)
00387             return true;
00388         return false;
00389 #endif
00390     }
00391     int PeriodicThread::getScheduler() const
00392     {
00393         return rtos_task_get_scheduler(&rtos_task);
00394     }
00395 
00396     void PeriodicThread::configure()
00397     {
00398         // reconfigure period
00399         if ( wait_for_step )
00400             rtos_task_set_period(&rtos_task, period );
00401         else
00402             rtos_task_set_period(&rtos_task, 0);
00403 
00404         if ( msched_type != rtos_task_get_scheduler(&rtos_task) )
00405             {
00406                 rtos_task_set_scheduler( &rtos_task, msched_type );
00407                 msched_type = rtos_task_get_scheduler(&rtos_task);
00408             }
00409     }
00410         
00411 
00412     void PeriodicThread::step()
00413     {
00414         if ( runComp )        
00415             runComp->step();
00416     }
00417 
00418     bool PeriodicThread::initialize()
00419     {
00420         if ( runComp )
00421             return runComp->initialize();
00422             
00423         return true;
00424     }
00425 
00426     void PeriodicThread::finalize()
00427     {
00428         if ( runComp )
00429             runComp->finalize();
00430     }
00431 
00432     bool PeriodicThread::setPeriod( double s )
00433     {
00434         if ( isRunning() )
00435             return false;
00436 
00437         period = Seconds_to_nsecs(s);
00438 
00439         // signal change to thread.
00440         rtos_sem_signal(&sem);
00441         rtos_sem_wait(&confDone);
00442 
00443         return true;
00444     }
00445 
00446     bool PeriodicThread::setPeriod(secs s, nsecs ns) 
00447     {
00448         if ( isRunning() ) return false;
00449         period = ns + 1000*1000*1000*s;
00450 
00451         // signal change to thread.
00452         rtos_sem_signal(&sem);
00453         rtos_sem_wait(&confDone);
00454 
00455         return true;
00456     }
00457 
00458     bool PeriodicThread::setPeriod( TIME_SPEC p) 
00459     {
00460         return this->setPeriod( p.tv_sec, p.tv_nsec );
00461     }
00462 
00463     void PeriodicThread::getPeriod(secs& s, nsecs& ns) const
00464     {
00465         s = period/(1000*1000*1000);
00466         ns = period - s*1000*1000*1000;
00467     }
00468 
00469     bool PeriodicThread::setPriority(int p) 
00470     {
00471         return rtos_task_set_priority(&rtos_task, p) == 0;
00472     }
00473 
00474     int PeriodicThread::getPriority() const 
00475     {
00476         return rtos_task_get_priority(&rtos_task);
00477     }
00478 
00479     double PeriodicThread::getPeriod() const
00480     {
00481         return nsecs_to_Seconds(period);
00482     }
00483 
00484     nsecs PeriodicThread::getPeriodNS() const
00485     {
00486         return period;
00487     }
00488 
00489     void PeriodicThread::yield() {
00490         rtos_task_yield( &rtos_task );
00491     }
00492 
00493 
00494     void PeriodicThread::continuousStepping(bool yes_no)
00495     {
00496         wait_for_step = !yes_no;
00497         // do not change the period here, do it in configure()
00498     }
00499 
00500     void PeriodicThread::terminate()
00501     {
00502         // avoid callling twice.
00503         if (prepareForExit) return;
00504 
00505         prepareForExit = true;
00506         rtos_sem_signal(&sem);
00507     
00508         rtos_sem_wait(&confDone); // this is strictly not necessary
00509         rtos_task_delete(&rtos_task); // this joins the thread.
00510 
00511     }
00512 
00513     bool PeriodicThread::setToStop()
00514     {
00515         running = false;
00516         this->finalize();
00517         return true;
00518     }
00519 
00520     const char* PeriodicThread::getName() const
00521     {
00522         return rtos_task_get_name(&rtos_task);
00523     }
00524 
00525     void PeriodicThread::setMaxOverrun( int m )
00526     {
00527         maxOverRun = m;
00528     }
00529 
00530     int PeriodicThread::getMaxOverrun() const
00531     {
00532         return maxOverRun;
00533     }
00534 
00535 }}
00536     

Generated on Tue Mar 25 17:41:48 2008 for OrocosReal-TimeToolkit by  doxygen 1.5.3