/*************************************************************************\ * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * SPDX-License-Identifier: EPICS * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 */ #include #include #include "epicsGuard.h" #include "timerPrivate.h" #include "errlog.h" const double timerQueue :: exceptMsgMinPeriod = 60.0 * 5.0; // seconds epicsTimerQueue::~epicsTimerQueue () {} timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) : mutex(__FILE__, __LINE__), notify ( notifyIn ), pExpireTmr ( 0 ), processThread ( 0 ), exceptMsgTimeStamp ( epicsTime :: getCurrent () - exceptMsgMinPeriod ), cancelPending ( false ) { } timerQueue::~timerQueue () { timer *pTmr; while ( ( pTmr = this->timerList.get () ) ) { pTmr->curState = timer::stateLimbo; } } void timerQueue :: printExceptMsg ( const char * pName, const type_info & type ) { char date[64]; double delay; try { epicsTime cur = epicsTime :: getCurrent (); delay = cur - this->exceptMsgTimeStamp; cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f" ); if ( delay >= exceptMsgMinPeriod ) { this->exceptMsgTimeStamp = cur; } } catch ( ... ) { delay = DBL_MAX; strcpy ( date, "UKN DATE" ); } if ( delay >= exceptMsgMinPeriod ) { // we don't touch the typeid for the timer expiration // notify interface here because they might have // destroyed the timer during its callback errlogPrintf ( "timerQueue: Unexpected C++ exception \"%s\" " "with type \"%s\" during timer expiration " "callback at %s\n", pName, type.name (), date ); errlogFlush (); } } double timerQueue::process ( const epicsTime & currentTime ) { epicsGuard < epicsMutex > guard ( this->mutex ); if ( this->pExpireTmr ) { // if some other thread is processing the queue // (or if this is a recursive call) timer * pTmr = this->timerList.first (); if ( pTmr ) { double delay = pTmr->exp - currentTime; if ( delay < 0.0 ) { delay = 0.0; } return delay; } else { return DBL_MAX; } } // // Tag current expired tmr so that we can detect if call back // is in progress when canceling the timer. // if ( this->timerList.first () ) { if ( currentTime >= this->timerList.first ()->exp ) { this->pExpireTmr = this->timerList.first (); this->timerList.remove ( *this->pExpireTmr ); this->pExpireTmr->curState = timer::stateActive; this->processThread = epicsThreadGetIdSelf (); # ifdef DEBUG this->pExpireTmr->show ( 0u ); # endif } else { double delay = this->timerList.first ()->exp - currentTime; debugPrintf ( ( "no activity process %f to next\n", delay ) ); return delay; } } else { return DBL_MAX; } # ifdef DEBUG unsigned N = 0u; # endif double delay = DBL_MAX; while ( true ) { epicsTimerNotify *pTmpNotify = this->pExpireTmr->pNotify; this->pExpireTmr->pNotify = 0; epicsTimerNotify::expireStatus expStat ( epicsTimerNotify::noRestart ); { epicsGuardRelease < epicsMutex > unguard ( guard ); debugPrintf ( ( "%5u expired \"%s\" with error %f sec\n", N++, typeid ( this->pExpireTmr->pNotify ).name (), currentTime - this->pExpireTmr->exp ) ); try { expStat = pTmpNotify->expire ( currentTime ); } catch ( std::exception & except ) { printExceptMsg ( except.what (), typeid ( except ) ); } catch ( ... ) { printExceptMsg ( "non-standard exception", typeid ( void ) ); } } // // only restart if they didnt cancel() the timer // while the call back was running // if ( this->cancelPending ) { // 1) if another thread is canceling then cancel() waits for // the event below // 2) if this thread is canceling in the timer callback then // don't touch timer or notify here because the cancel might // have occurred because they destroyed the timer in the // callback this->cancelPending = false; this->cancelBlockingEvent.signal (); } else { this->pExpireTmr->curState = timer::stateLimbo; if ( this->pExpireTmr->pNotify ) { // pNotify was cleared above so if it is valid now we know that // someone has started the timer from another thread and that // predominates over the restart parameters from expire. this->pExpireTmr->privateStart ( *this->pExpireTmr->pNotify, this->pExpireTmr->exp ); } else if ( expStat.restart() ) { // restart as nec this->pExpireTmr->privateStart ( *pTmpNotify, currentTime + expStat.expirationDelay() ); } } this->pExpireTmr = 0; if ( this->timerList.first () ) { if ( currentTime >= this->timerList.first ()->exp ) { this->pExpireTmr = this->timerList.first (); this->timerList.remove ( *this->pExpireTmr ); this->pExpireTmr->curState = timer::stateActive; # ifdef DEBUG this->pExpireTmr->show ( 0u ); # endif } else { delay = this->timerList.first ()->exp - currentTime; this->processThread = 0; break; } } else { this->processThread = 0; delay = DBL_MAX; break; } } return delay; } epicsTimer & timerQueue::createTimer () { return * new ( this->timerFreeList ) timer ( * this ); } epicsTimerForC & timerQueue::createTimerForC ( epicsTimerCallback pCallback, void *pArg ) { return * new ( this->timerForCFreeList ) epicsTimerForC ( *this, pCallback, pArg ); } void timerQueue::show ( unsigned level ) const { epicsGuard < epicsMutex > locker ( this->mutex ); printf ( "epicsTimerQueue with %u items pending\n", this->timerList.count () ); if ( level >= 1u ) { tsDLIterConst < timer > iter = this->timerList.firstIter (); while ( iter.valid () ) { iter->show ( level - 1u ); ++iter; } } }