/* * $Id$ * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 * * Experimental Physics and Industrial Control System (EPICS) * * Copyright 1991, the Regents of the University of California, * and the University of Chicago Board of Governors. * * This software was produced under U.S. Government contracts: * (W-7405-ENG-36) at the Los Alamos National Laboratory, * and (W-31-109-ENG-38) at Argonne National Laboratory. * * Initial development by: * The Controls and Automation Group (AT-8) * Ground Test Accelerator * Accelerator Technology Division * Los Alamos National Laboratory * * Co-developed with * The Controls and Computing Group * Accelerator Systems Division * Advanced Photon Source * Argonne National Laboratory * */ #include #include "epicsTimer.h" #include "epicsEvent.h" #include "epicsAssert.h" #include "tsFreeList.h" static const double delayVerifyOffset = 1.0; // sec class delayVerify : public epicsTimerNotify { public: delayVerify ( double expectedDelay, epicsTimerQueue & ); void * operator new ( size_t size ); void operator delete ( void *pCadaver, size_t size ); void start ( const epicsTime &expireTime ); void setBegin ( const epicsTime & ); double delay () const; void checkError () const; protected: virtual ~delayVerify (); private: epicsTimer &timer; epicsTime beginStamp; epicsTime expireStamp; double expectedDelay; expireStatus expire ( const epicsTime & ); static tsFreeList < class delayVerify, 0x20 > freeList; static epicsMutex freeListMutex; }; static unsigned expireCount; static epicsEvent expireEvent; delayVerify::delayVerify ( double expectedDelayIn, epicsTimerQueue &queueIn ) : timer ( queueIn.createTimer() ), expectedDelay ( expectedDelayIn ) { } delayVerify::~delayVerify () { this->timer.destroy (); } inline void delayVerify::setBegin ( const epicsTime &beginIn ) { this->beginStamp = beginIn; } inline double delayVerify::delay () const { return delayVerifyOffset + this->expectedDelay; } void delayVerify::checkError () const { double actualDelay = this->expireStamp - this->beginStamp; double measuredError = actualDelay - delayVerifyOffset - this->expectedDelay; double percentError = measuredError / this->expectedDelay; percentError *= 100.0; if ( percentError > 0.5 ) { printf ( "TEST FAILED timer delay = %g sec, error = %g mSec (%f %%)\n", this->expectedDelay, measuredError * 1000.0, percentError ); } } inline void delayVerify::start ( const epicsTime &expireTime ) { this->timer.start ( *this, expireTime ); } tsFreeList < class delayVerify, 0x20 > delayVerify::freeList; epicsMutex delayVerify::freeListMutex; inline void * delayVerify::operator new ( size_t size ) { epicsAutoMutex locker ( delayVerify::freeListMutex ); return delayVerify::freeList.allocate ( size ); } inline void delayVerify::operator delete ( void *pCadaver, size_t size ) { epicsAutoMutex locker ( delayVerify::freeListMutex ); delayVerify::freeList.release ( pCadaver, size ); } epicsTimerNotify::expireStatus delayVerify::expire ( const epicsTime ¤tTime ) { this->expireStamp = currentTime; if ( --expireCount == 0u ) { expireEvent.signal (); } return noRestart; } // // verify reasonable timer interval accuracy // void testAccuracy () { static const unsigned nTimers = 25u; delayVerify *pTimers[nTimers]; unsigned i; epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMax ); for ( i = 0u; i < nTimers; i++ ) { pTimers[i] = new delayVerify ( ( nTimers - i ) * 0.1, queue ); assert ( pTimers[i] ); } expireCount = nTimers; for ( i = 0u; i < nTimers; i++ ) { epicsTime cur = epicsTime::getCurrent (); pTimers[i]->setBegin ( cur ); pTimers[i]->start ( cur + pTimers[i]->delay () ); } while ( expireCount != 0u ) { expireEvent.wait (); } for ( i = 0u; i < nTimers; i++ ) { pTimers[i]->checkError (); } queue.release (); } class cancelVerify : public epicsTimerNotify { public: cancelVerify ( epicsTimerQueue & ); void * operator new ( size_t size ); void operator delete ( void *pCadaver, size_t size ); void start ( const epicsTime &expireTime ); void cancel (); protected: virtual ~cancelVerify (); private: epicsTimer &timer; bool failOutIfExpireIsCalled; expireStatus expire ( const epicsTime & ); static tsFreeList < class cancelVerify, 0x20 > freeList; static epicsMutex freeListMutex; }; cancelVerify::cancelVerify ( epicsTimerQueue &queueIn ) : timer ( queueIn.createTimer () ), failOutIfExpireIsCalled ( false ) { } cancelVerify::~cancelVerify () { this->timer.destroy (); } inline void cancelVerify::start ( const epicsTime &expireTime ) { this->timer.start ( *this, expireTime ); } tsFreeList < class cancelVerify, 0x20 > cancelVerify::freeList; epicsMutex cancelVerify::freeListMutex; inline void * cancelVerify::operator new ( size_t size ) { epicsAutoMutex locker ( cancelVerify::freeListMutex ); return cancelVerify::freeList.allocate ( size ); } inline void cancelVerify::operator delete ( void *pCadaver, size_t size ) { epicsAutoMutex locker ( cancelVerify::freeListMutex ); cancelVerify::freeList.release ( pCadaver, size ); } inline void cancelVerify::cancel () { this->timer.cancel (); this->failOutIfExpireIsCalled = true; } epicsTimerNotify::expireStatus cancelVerify::expire ( const epicsTime & ) { double root = 3.14159; for ( unsigned i = 0u; i < 10000; i++ ) { root = sqrt ( root ); } assert ( ! this->failOutIfExpireIsCalled ); return noRestart; } // // verify that when cancel() is calle dthe timer never runs again // void testCancel () { static const unsigned nTimers = 25u; cancelVerify *pTimers[nTimers]; unsigned i; epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); for ( i = 0u; i < nTimers; i++ ) { pTimers[i] = new cancelVerify ( queue ); assert ( pTimers[i] ); } epicsTime cur = epicsTime::getCurrent (); for ( i = 0u; i < nTimers; i++ ) { pTimers[i]->start ( cur + 4.0 ); } epicsThreadSleep ( 5.0 ); for ( i = 0u; i < nTimers; i++ ) { pTimers[i]->cancel (); } epicsThreadSleep ( 1.0 ); queue.release (); } class periodicVerify : public epicsTimerNotify { public: periodicVerify ( epicsTimerQueue & ); void * operator new ( size_t size ); void operator delete ( void *pCadaver, size_t size ); void start ( const epicsTime &expireTime ); void cancel (); void verifyCount (); protected: virtual ~periodicVerify (); private: epicsTimer &timer; unsigned nExpire; bool failOutIfExpireIsCalled; expireStatus expire ( const epicsTime & ); static tsFreeList < class periodicVerify, 0x20 > freeList; static epicsMutex freeListMutex; }; periodicVerify::periodicVerify ( epicsTimerQueue & queueIn ) : timer ( queueIn.createTimer () ), nExpire ( 0u ), failOutIfExpireIsCalled ( false ) { } periodicVerify::~periodicVerify () { this->timer.destroy (); } inline void periodicVerify::start ( const epicsTime &expireTime ) { this->timer.start ( *this, expireTime ); } tsFreeList < class periodicVerify, 0x20 > periodicVerify::freeList; epicsMutex periodicVerify::freeListMutex; inline void * periodicVerify::operator new ( size_t size ) { epicsAutoMutex locker ( periodicVerify::freeListMutex ); return periodicVerify::freeList.allocate ( size ); } inline void periodicVerify::operator delete ( void *pCadaver, size_t size ) { epicsAutoMutex locker ( periodicVerify::freeListMutex ); periodicVerify::freeList.release ( pCadaver, size ); } inline void periodicVerify::cancel () { this->timer.cancel (); this->failOutIfExpireIsCalled = true; } inline void periodicVerify::verifyCount () { assert ( this->nExpire > 1u ); } epicsTimerNotify::expireStatus periodicVerify::expire ( const epicsTime & ) { this->nExpire++; double root = 3.14159; for ( unsigned i = 0u; i < 10000; i++ ) { root = sqrt ( root ); } assert ( ! this->failOutIfExpireIsCalled ); double delay = rand (); delay = delay / RAND_MAX; delay /= 10.0; return expireStatus ( restart, delay ); } // // verify periodic timers // void testPeriodic () { static const unsigned nTimers = 25u; periodicVerify *pTimers[nTimers]; unsigned i; epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); for ( i = 0u; i < nTimers; i++ ) { pTimers[i] = new periodicVerify ( queue ); assert ( pTimers[i] ); } epicsTime cur = epicsTime::getCurrent (); for ( i = 0u; i < nTimers; i++ ) { pTimers[i]->start ( cur ); } epicsThreadSleep ( 5.0 ); for ( i = 0u; i < nTimers; i++ ) { pTimers[i]->verifyCount (); pTimers[i]->cancel (); } epicsThreadSleep ( 1.0 ); queue.release (); } #if defined ( _MSC_VER ) # pragma warning ( push ) # pragma warning ( disable: 4660 ) #endif template class tsFreeList < delayVerify, 32, 0 >; template class tsFreeList < cancelVerify, 32, 0 >; template class tsFreeList < periodicVerify, 32, 0 >; #if defined ( _MSC_VER ) # pragma warning ( pop ) #endif void epicsTimerTest () { testAccuracy (); testCancel (); testPeriodic (); printf ( "test complete\n" ); }