diff --git a/rtt/Activity.cpp b/rtt/Activity.cpp index 92e98e68b..08b6e58ee 100644 --- a/rtt/Activity.cpp +++ b/rtt/Activity.cpp @@ -143,6 +143,14 @@ namespace RTT return update_period; } + bool Activity::setAperiodicTriggerTimeout(NANO_TIME timeout) { + if (timeout < 0.0) + return false; + + Thread::setAperiodicTriggerTimeout(timeout); + return true; + } + bool Activity::trigger() { if ( ! Thread::isActive() ) return false; diff --git a/rtt/Activity.hpp b/rtt/Activity.hpp index 0a57abe6f..8d7311a29 100644 --- a/rtt/Activity.hpp +++ b/rtt/Activity.hpp @@ -185,6 +185,8 @@ namespace RTT virtual bool setPeriod(Seconds period); + virtual bool setAperiodicTriggerTimeout(NANO_TIME timeout) override; + virtual unsigned getCpuAffinity() const; virtual bool setCpuAffinity(unsigned cpu); diff --git a/rtt/base/ActivityInterface.hpp b/rtt/base/ActivityInterface.hpp index b91ad7f66..67c23c4b8 100644 --- a/rtt/base/ActivityInterface.hpp +++ b/rtt/base/ActivityInterface.hpp @@ -161,6 +161,17 @@ namespace RTT */ virtual bool setPeriod(Seconds s) = 0; + /** + * Set a trigger timeout for aperiodic activities. + * + * When set, an aperiodic activity will be triggered after this long. + * Note that this is not the same as a periodic task: the period is not + * fixed (the timeout starts at the last time the thread started waiting) + * and on at least Linux, it is not using a monotonic clock. + * + * @return true if it could be updated, false otherwise. + */ + virtual bool setAperiodicTriggerTimeout(NANO_TIME timeout) = 0; /** * Get the cpu affinity of this activity diff --git a/rtt/extras/FileDescriptorActivity.cpp b/rtt/extras/FileDescriptorActivity.cpp index 460d8c917..8ff90a3f8 100644 --- a/rtt/extras/FileDescriptorActivity.cpp +++ b/rtt/extras/FileDescriptorActivity.cpp @@ -189,6 +189,12 @@ void FileDescriptorActivity::setTimeout_us(int timeout_us) log(Error) << "Ignoring invalid timeout (" << timeout_us << ")" << endlog(); } } + +bool FileDescriptorActivity::setAperiodicTriggerTimeout(NANO_TIME timeout) { + setTimeout_us(timeout / 1000); + return true; +} + void FileDescriptorActivity::watch(int fd) { RTT::os::MutexLock lock(m_lock); if (fd < 0) diff --git a/rtt/extras/FileDescriptorActivity.hpp b/rtt/extras/FileDescriptorActivity.hpp index 0e9ce96f3..274f35c3b 100644 --- a/rtt/extras/FileDescriptorActivity.hpp +++ b/rtt/extras/FileDescriptorActivity.hpp @@ -262,6 +262,11 @@ namespace RTT { namespace extras { */ void setTimeout(int timeout); + /** Same as setTimeout, from the common Activity interface + * + */ + bool setAperiodicTriggerTimeout(NANO_TIME timeout) override; + /** Sets the timeout, in microseconds, for waiting on the IO. Set to 0 * for blocking behaviour (no timeout). * @pre 0 <= timeout (otherwise an error is logged and \a timeout_us diff --git a/rtt/extras/FileDescriptorSimulationActivity.hpp b/rtt/extras/FileDescriptorSimulationActivity.hpp index a110928d8..8dbcb699c 100644 --- a/rtt/extras/FileDescriptorSimulationActivity.hpp +++ b/rtt/extras/FileDescriptorSimulationActivity.hpp @@ -51,7 +51,7 @@ namespace RTT { namespace extras { the component, and similarly for \a work(TimeOut) and \a hasTimeout(). Currently \a hasError() always returns false - there is no way to simulate this with the current implementation. - + \note Component OwnThread operations are executed by the MainThread, which is correct for a unit test case that is directly executing. */ @@ -122,6 +122,9 @@ namespace RTT { namespace extras { /// Does nothing void setTimeout_us(int timeout_us); + /// Does nothing + bool setAperiodicTriggerTimeout(NANO_TIME) override { return true; }; + /// Return 0 int getTimeout() const; diff --git a/rtt/extras/PeriodicActivity.cpp b/rtt/extras/PeriodicActivity.cpp index 57b8209e3..a729e2163 100644 --- a/rtt/extras/PeriodicActivity.cpp +++ b/rtt/extras/PeriodicActivity.cpp @@ -165,6 +165,10 @@ namespace RTT { return false; } + bool PeriodicActivity::setAperiodicTriggerTimeout(NANO_TIME timeout) { + return false; + } + unsigned PeriodicActivity::getCpuAffinity() const { return thread_->getCpuAffinity(); diff --git a/rtt/extras/PeriodicActivity.hpp b/rtt/extras/PeriodicActivity.hpp index 295f3dc6e..3f3d44ca1 100644 --- a/rtt/extras/PeriodicActivity.hpp +++ b/rtt/extras/PeriodicActivity.hpp @@ -188,6 +188,8 @@ namespace RTT virtual bool setPeriod(Seconds s); + virtual bool setAperiodicTriggerTimeout(NANO_TIME timeout); + virtual unsigned getCpuAffinity() const; virtual bool setCpuAffinity(unsigned cpu); diff --git a/rtt/extras/SequentialActivity.cpp b/rtt/extras/SequentialActivity.cpp index ac79a5ca6..ee25c8215 100644 --- a/rtt/extras/SequentialActivity.cpp +++ b/rtt/extras/SequentialActivity.cpp @@ -65,6 +65,10 @@ namespace RTT { return false; } + bool SequentialActivity::setAperiodicTriggerTimeout(NANO_TIME timeout) { + return (timeout == 0); + } + unsigned SequentialActivity::getCpuAffinity() const { return ~0; diff --git a/rtt/extras/SequentialActivity.hpp b/rtt/extras/SequentialActivity.hpp index 33c882953..fcbdd0df7 100644 --- a/rtt/extras/SequentialActivity.hpp +++ b/rtt/extras/SequentialActivity.hpp @@ -87,6 +87,8 @@ namespace RTT bool setPeriod(Seconds s); + bool setAperiodicTriggerTimeout(NANO_TIME timeout) override; + unsigned getCpuAffinity() const; bool setCpuAffinity(unsigned cpu); diff --git a/rtt/extras/SlaveActivity.cpp b/rtt/extras/SlaveActivity.cpp index ebd005e7d..ac46afede 100644 --- a/rtt/extras/SlaveActivity.cpp +++ b/rtt/extras/SlaveActivity.cpp @@ -77,6 +77,10 @@ namespace RTT { return true; } + bool SlaveActivity::setAperiodicTriggerTimeout(NANO_TIME timeout) { + return (timeout == 0); + } + unsigned SlaveActivity::getCpuAffinity() const { if (mmaster) diff --git a/rtt/extras/SlaveActivity.hpp b/rtt/extras/SlaveActivity.hpp index 3c10266e9..22f164351 100644 --- a/rtt/extras/SlaveActivity.hpp +++ b/rtt/extras/SlaveActivity.hpp @@ -122,6 +122,8 @@ namespace RTT bool setPeriod(Seconds s); + bool setAperiodicTriggerTimeout(NANO_TIME timeout) override; + unsigned getCpuAffinity() const; bool setCpuAffinity(unsigned cpu); diff --git a/rtt/os/Thread.cpp b/rtt/os/Thread.cpp index 844bcf7e5..bd5c2b399 100644 --- a/rtt/os/Thread.cpp +++ b/rtt/os/Thread.cpp @@ -69,7 +69,7 @@ namespace RTT { void Thread::setStackSize(unsigned int ssize) { default_stack_size = ssize; } void Thread::setLockTimeoutNoPeriod(double timeout_in_s) { lock_timeout_no_period_in_s = timeout_in_s; } - + void Thread::setLockTimeoutPeriodFactor(double factor) { lock_timeout_period_factor = factor; } void *thread_function(void* t) @@ -112,7 +112,14 @@ namespace RTT { // drop out of periodic mode: rtos_task_set_period(task->getTask(), 0); } - rtos_sem_wait(&(task->sem)); // wait for command. + + if (!task->running || task->period != 0 || task->aperiodicTriggerTimeout == 0) { + rtos_sem_wait(&(task->sem)); // wait for command. + } + else { + rtos_sem_wait_timed(&(task->sem), task->aperiodicTriggerTimeout); // wait for trigger. + } + task->configure(); // check for reconfigure if (task->prepareForExit) // check for exit { @@ -242,6 +249,7 @@ namespace RTT { #ifdef OROPKG_OS_THREAD_SCOPE ,d(NULL) #endif + , aperiodicTriggerTimeout(0) , stopTimeout(0) { this->setup(_priority, cpu_affinity, name); @@ -430,7 +438,7 @@ namespace RTT { // breakLoop was ok, wait for loop() to return. } // always take this lock, but after breakLoop was called ! - MutexTimedLock lock(breaker, getStopTimeout()); + MutexTimedLock lock(breaker, getStopTimeout()); if ( !lock.isSuccessful() ) { log(Error) << "Failed to stop thread " << this->getName() << ": breakLoop() returned true, but loop() function did not return after " << getStopTimeout() <<" seconds."<secondsSince( ts ); - if ( s < this->getThread()->getPeriod() *0.9 ) { // if elapsed time is smaller than 10% of period, something went wrong + if ( s < expectedPeriod *0.9 ) { // if elapsed time is smaller than 10% of period, something went wrong ++underfail; //rtos_printf("UnderFailPeriod: %f \n", s); } - else if ( s > this->getThread()->getPeriod() *1.1 ) { // if elapsed time is smaller than 10% of period, something went wrong + else if ( s > expectedPeriod *1.1 ) { // if elapsed time is smaller than 10% of period, something went wrong ++overfail; //rtos_printf("OverFailPeriod: %f \n", s); } @@ -395,17 +398,40 @@ BOOST_AUTO_TEST_CASE( testThread ) { bool r = false; // create - boost::scoped_ptr run( new TestPeriodic() ); + boost::scoped_ptr run( new TestPeriodic(0.1) ); boost::scoped_ptr t( new Activity(ORO_SCHED_RT, os::HighestPriority, 0.1, 0, "PThread") ); t->run( run.get() ); r = t->start(); BOOST_CHECK_MESSAGE( r, "Failed to start Thread"); - usleep(1000*100); + usleep(1000*1000); + r = t->stop(); + BOOST_CHECK_MESSAGE( r, "Failed to stop Thread" ); + BOOST_CHECK_MESSAGE( run->stepped == true, "Step not executed" ); + BOOST_CHECK_MESSAGE(run->succ > 5, "Periodic Failure: less than 5 steps within 1s"); + BOOST_CHECK_EQUAL_MESSAGE(run->overfail, 0, "Periodic Failure: period of step() too long !"); + BOOST_CHECK_EQUAL_MESSAGE(run->underfail, 0, "Periodic Failure: period of step() too short!"); +} + +BOOST_AUTO_TEST_CASE( testAperiodicTriggerTimeout ) +{ + bool r = false; + // create + boost::scoped_ptr run( new TestPeriodic(0.1) ); + + boost::scoped_ptr t( new Activity(ORO_SCHED_RT, os::HighestPriority, 0, 0, "PThread") ); + t->setAperiodicTriggerTimeout(100 * 1000 * 1000); + //t->setAperiodicTriggerTimeout(0); + t->run( run.get() ); + + r = t->start(); + BOOST_CHECK_MESSAGE( r, "Failed to start Thread"); + usleep(1000*1000); r = t->stop(); BOOST_CHECK_MESSAGE( r, "Failed to stop Thread" ); BOOST_CHECK_MESSAGE( run->stepped == true, "Step not executed" ); + BOOST_CHECK_MESSAGE(run->succ > 5, "Less than 5 steps within 1s"); BOOST_CHECK_EQUAL_MESSAGE(run->overfail, 0, "Periodic Failure: period of step() too long !"); BOOST_CHECK_EQUAL_MESSAGE(run->underfail, 0, "Periodic Failure: period of step() too short!"); } @@ -447,7 +473,7 @@ BOOST_AUTO_TEST_CASE( testAffinity ) int numCPU = sysconf( _SC_NPROCESSORS_ONLN ); if (1 < numCPU) { - boost::scoped_ptr run( new TestPeriodic() ); + boost::scoped_ptr run( new TestPeriodic(0.1) ); boost::scoped_ptr t( new Activity(ORO_SCHED_RT, os::HighestPriority, 0.1, ~0, 0, "PThread") ); // returned affinity depends on the number of actual CPUs, and won't be "~0" unsigned mask=0;