diff --git a/src/libCom/fdmgr/fdManager.cpp b/src/libCom/fdmgr/fdManager.cpp index c07ee1834..f53c054b3 100644 --- a/src/libCom/fdmgr/fdManager.cpp +++ b/src/libCom/fdmgr/fdManager.cpp @@ -315,6 +315,13 @@ void fdManager::reschedule () { } +double fdManager::quantum () +{ + // hopefully its a reasonable guess that select() and epicsThreadSleep() + // will have the same sleep quantum + return epicsThreadSleepQuantum (); +} + // // lookUpFD() // diff --git a/src/libCom/fdmgr/fdManager.h b/src/libCom/fdmgr/fdManager.h index 25a5ea746..9cf76f77f 100644 --- a/src/libCom/fdmgr/fdManager.h +++ b/src/libCom/fdmgr/fdManager.h @@ -100,6 +100,7 @@ private: // fdReg *pCBReg; void reschedule (); + double quantum (); epicsShareFunc void installReg (fdReg ®); epicsShareFunc void removeReg (fdReg ®); void lazyInitTimerQueue (); diff --git a/src/libCom/test/epicsThreadTest.cpp b/src/libCom/test/epicsThreadTest.cpp index e9953b50c..51f3c2ef5 100644 --- a/src/libCom/test/epicsThreadTest.cpp +++ b/src/libCom/test/epicsThreadTest.cpp @@ -10,7 +10,7 @@ /* epicsThreadTest.cpp */ /* Author: Marty Kraimer Date: 26JAN2000 */ -/* sleep accuracy tests by Jeff Hill */ +/* sleep accuracy and sleep quantum tests by Jeff Hill */ /* epicsThreadGetIdSelf performance by Jeff Hill */ #include @@ -59,28 +59,67 @@ void myThread::run() errlogPrintf("threadFunc %d stopping argvalue %p\n",myPrivate,argvalue); } -static double threadSleepMeasureDelayError( const double & delay ) +static double threadSleepMeasureDelayError ( const double & delay ) { epicsTime beg = epicsTime::getCurrent(); epicsThreadSleep ( delay ); epicsTime end = epicsTime::getCurrent(); double meas = end - beg; double error = fabs ( delay - meas ); - printf ( "epicsThreadSleep ( %10f ) delay err %10f sec\n", - delay, error ); return error; } -static void threadSleepTest() +static void threadSleepQuantumTest () { + static const unsigned iterations = 100u; + const double quantum = epicsThreadSleepQuantum (); double errorSum = 0.0; - int i; - for ( i = 0u; i < 20; i++ ) { - double delay = ldexp ( 1.0 , -i ); - errorSum += threadSleepMeasureDelayError ( delay ); + for ( unsigned i = 0u; i < iterations; i++ ) { + errorSum += + threadSleepMeasureDelayError ( 1e-15 ); + } + double quantumEstimate = errorSum / iterations; + double quantumError = fabs ( quantumEstimate - quantum ) / quantum; + const char * pTol = 0; + if ( quantumError > 0.1 ) { + pTol = "10%"; + } + else if ( quantumError > 0.01 ) { + pTol = "1%"; + } + else if ( quantumError > 0.001 ) { + pTol = ".1%"; + } + if ( pTol ) { + printf ( + "The epicsThreadSleepQuantum() call returns %f sec.\n", + quantum ); + printf ( + "This doesnt match the quantum estimate " + "of %f sec within %s.\n", + quantumEstimate, pTol ); + } +} + +static void threadSleepTest () +{ + static const int iterations = 20; + const double quantum = epicsThreadSleepQuantum (); + double errorSum = threadSleepMeasureDelayError ( 0.0 ); + for ( int i = 0u; i < iterations; i++ ) { + double delay = ldexp ( 1.0 , -i ); + double error = + threadSleepMeasureDelayError ( delay ); + errorSum += error; + if ( error > quantum + quantum / 8.0 ) { + printf ( "epicsThreadSleep ( %10f ) delay err %10f sec\n", + delay, error ); + } + } + double averageError = errorSum / ( iterations + 1 ); + if ( averageError > quantum ) { + printf ( "Average sleep delay error was %f sec\n", averageError ); } - errorSum += threadSleepMeasureDelayError ( 0.0 ); - printf ( "Average error %f sec\n", errorSum / ( i + 1 ) ); } static void epicsThreadGetIdSelfPerfTest () @@ -117,6 +156,7 @@ extern "C" void threadTest(int ntasks,int verbose) epicsThreadGetIdSelfPerfTest (); threadSleepTest(); + threadSleepQuantumTest(); errVerbose = verbose; errlogInit(4096); diff --git a/src/libCom/test/epicsTimerTest.cpp b/src/libCom/test/epicsTimerTest.cpp index cefdc822a..58111f8d2 100644 --- a/src/libCom/test/epicsTimerTest.cpp +++ b/src/libCom/test/epicsTimerTest.cpp @@ -32,7 +32,7 @@ public: void start ( const epicsTime &expireTime ); void setBegin ( const epicsTime & ); double delay () const; - void checkError () const; + double checkError () const; protected: virtual ~delayVerify (); private: @@ -67,7 +67,7 @@ inline double delayVerify::delay () const return delayVerifyOffset + this->expectedDelay; } -void delayVerify::checkError () const +double delayVerify::checkError () const { const double messageThresh = 0.5; // percent double actualDelay = this->expireStamp - this->beginStamp; @@ -78,6 +78,7 @@ void delayVerify::checkError () const printf ( "delay error > %g %%, delay = %g sec, error = %g mSec (%f %%)\n", messageThresh, this->expectedDelay, measuredError * 1000.0, percentError ); } + return measuredError; } inline void delayVerify::start ( const epicsTime &expireTime ) @@ -119,9 +120,13 @@ void testAccuracy () while ( expireCount != 0u ) { expireEvent.wait (); } + double averageMeasuredError; for ( i = 0u; i < nTimers; i++ ) { - pTimers[i]->checkError (); + averageMeasuredError += pTimers[i]->checkError (); } + averageMeasuredError /= nTimers; + printf ("average timer delay error %f mS\n", + averageMeasuredError * 1000 ); queue.release (); } diff --git a/src/libCom/timer/epicsTimer.cpp b/src/libCom/timer/epicsTimer.cpp index a3d2613c8..8302bac54 100644 --- a/src/libCom/timer/epicsTimer.cpp +++ b/src/libCom/timer/epicsTimer.cpp @@ -79,10 +79,14 @@ void epicsTimerQueueActiveForC::release () pMgr->release ( *this ); } -epicsTimerQueuePassiveForC::epicsTimerQueuePassiveForC - ( epicsTimerQueueRescheduleCallback pCallbackIn, void * pPrivateIn ) : +epicsTimerQueuePassiveForC::epicsTimerQueuePassiveForC ( + epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, + epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, + void * pPrivateIn ) : timerQueuePassive ( * static_cast < epicsTimerQueueNotify * > ( this ) ), - pCallback ( pCallbackIn ), pPrivate ( pPrivateIn ) + pRescheduleCallback ( pRescheduleCallbackIn ), + pSleepQuantumCallback ( pSleepQuantumCallbackIn ), + pPrivate ( pPrivateIn ) { } @@ -92,7 +96,12 @@ epicsTimerQueuePassiveForC::~epicsTimerQueuePassiveForC () void epicsTimerQueuePassiveForC::reschedule () { - (*this->pCallback) ( this->pPrivate ); + (*this->pRescheduleCallback) ( this->pPrivate ); +} + +double epicsTimerQueuePassiveForC::quantum () +{ + return (*this->pSleepQuantumCallback) ( this->pPrivate ); } void epicsTimerQueuePassiveForC::destroy () @@ -138,10 +147,16 @@ epicsShareFunc double epicsTimerNotify::expireStatus::expirationDelay () const } extern "C" epicsTimerQueuePassiveId epicsShareAPI - epicsTimerQueuePassiveCreate ( epicsTimerQueueRescheduleCallback pCallbackIn, void *pPrivateIn ) + epicsTimerQueuePassiveCreate ( + epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, + epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, + void * pPrivateIn ) { try { - return new epicsTimerQueuePassiveForC ( pCallbackIn, pPrivateIn ); + return new epicsTimerQueuePassiveForC ( + pRescheduleCallbackIn, + pSleepQuantumCallbackIn, + pPrivateIn ); } catch ( ... ) { return 0; diff --git a/src/libCom/timer/epicsTimer.h b/src/libCom/timer/epicsTimer.h index cf83ce0c8..7735e392c 100644 --- a/src/libCom/timer/epicsTimer.h +++ b/src/libCom/timer/epicsTimer.h @@ -89,6 +89,9 @@ public: // called when a new timer is inserted into the queue and the // delay to the next expire has changed virtual void reschedule () = 0; + // if there is a quantum in the scheduling of timer intervals + // return this quantum in seconds. If unknown then return zero. + virtual double quantum () = 0; protected: virtual ~epicsTimerQueueNotify () = 0; }; @@ -142,9 +145,11 @@ epicsShareFunc void epicsShareAPI /* passive timer queue */ typedef struct epicsTimerQueuePassiveForC * epicsTimerQueuePassiveId; -typedef void ( *epicsTimerQueueRescheduleCallback ) ( void *pPrivate ); +typedef void ( * epicsTimerQueueNotifyReschedule ) ( void * pPrivate ); +typedef double ( * epicsTimerQueueNotifyQuantum ) ( void * pPrivate ); epicsShareFunc epicsTimerQueuePassiveId epicsShareAPI - epicsTimerQueuePassiveCreate ( epicsTimerQueueRescheduleCallback, void *pPrivate ); + epicsTimerQueuePassiveCreate ( epicsTimerQueueNotifyReschedule, + epicsTimerQueueNotifyQuantum, void *pPrivate ); epicsShareFunc void epicsShareAPI epicsTimerQueuePassiveDestroy ( epicsTimerQueuePassiveId ); epicsShareFunc epicsTimerId epicsShareAPI diff --git a/src/libCom/timer/timer.cpp b/src/libCom/timer/timer.cpp index 67cb8e83a..ae8f684f3 100644 --- a/src/libCom/timer/timer.cpp +++ b/src/libCom/timer/timer.cpp @@ -66,7 +66,7 @@ void timer::start ( epicsTimerNotify & notify, const epicsTime & expire ) void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire ) { this->pNotify = & notify; - this->exp = expire; + this->exp = expire - this->queue.sleepQuantumOverTwo; bool reschedualNeeded = false; if ( this->curState == stateActive ) { diff --git a/src/libCom/timer/timerPrivate.h b/src/libCom/timer/timerPrivate.h index ed8fe973e..b530ed604 100644 --- a/src/libCom/timer/timerPrivate.h +++ b/src/libCom/timer/timerPrivate.h @@ -100,6 +100,7 @@ private: mutable epicsMutex mutex; epicsEvent cancelBlockingEvent; tsDLList < timer > timerList; + const double sleepQuantumOverTwo; epicsTimerQueueNotify & notify; timer * pExpireTmr; epicsThreadId processThread; @@ -142,6 +143,7 @@ private: bool terminateFlag; void run (); void reschedule (); + double quantum (); epicsTimerQueue & getEpicsTimerQueue (); timerQueueActive ( const timerQueueActive & ); timerQueueActive & operator = ( const timerQueueActive & ); @@ -178,17 +180,23 @@ protected: timerQueuePassive & operator = ( const timerQueuePassive & ); }; -struct epicsTimerQueuePassiveForC : public epicsTimerQueueNotify, public timerQueuePassive { +struct epicsTimerQueuePassiveForC : + public epicsTimerQueueNotify, public timerQueuePassive { public: - epicsTimerQueuePassiveForC ( epicsTimerQueueRescheduleCallback pCallback, void *pPrivate ); + epicsTimerQueuePassiveForC ( + epicsTimerQueueNotifyReschedule, + epicsTimerQueueNotifyQuantum, + void * pPrivate ); void destroy (); protected: ~epicsTimerQueuePassiveForC (); private: - epicsTimerQueueRescheduleCallback pCallback; - void *pPrivate; + epicsTimerQueueNotifyReschedule pRescheduleCallback; + epicsTimerQueueNotifyQuantum pSleepQuantumCallback; + void * pPrivate; static epicsSingleton < tsFreeList < epicsTimerQueuePassiveForC, 0x10 > > pFreeList; void reschedule (); + double quantum (); }; struct epicsTimerQueueActiveForC : public timerQueueActive, diff --git a/src/libCom/timer/timerQueue.cpp b/src/libCom/timer/timerQueue.cpp index 5d105713a..7f9cd5cf1 100644 --- a/src/libCom/timer/timerQueue.cpp +++ b/src/libCom/timer/timerQueue.cpp @@ -22,8 +22,9 @@ epicsTimerQueue::~epicsTimerQueue () {} timerQueue::timerQueue ( epicsTimerQueueNotify ¬ifyIn ) : + sleepQuantumOverTwo ( notifyIn.quantum () / 2.0 ), notify ( notifyIn ), pExpireTmr ( 0 ), - processThread ( 0 ), cancelPending ( false ) + processThread ( 0 ), cancelPending ( false ) { } diff --git a/src/libCom/timer/timerQueueActive.cpp b/src/libCom/timer/timerQueueActive.cpp index 3eda1fdf8..e7ee0a3c4 100644 --- a/src/libCom/timer/timerQueueActive.cpp +++ b/src/libCom/timer/timerQueueActive.cpp @@ -86,6 +86,11 @@ void timerQueueActive::reschedule () this->rescheduleEvent.signal (); } +double timerQueueActive::quantum () +{ + return epicsThreadSleepQuantum (); +} + void timerQueueActive::show ( unsigned int level ) const { printf ( "EPICS threaded timer queue at %p\n",