diff --git a/src/libCom/timer/epicsTimer.cpp b/src/libCom/timer/epicsTimer.cpp new file mode 100644 index 000000000..91fa940d1 --- /dev/null +++ b/src/libCom/timer/epicsTimer.cpp @@ -0,0 +1,124 @@ +/* + * $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 + +#define epicsExportSharedSymbols +#include "epicsTimer.h" +#if 0 +#include "tsFreeList.h" +#include "tsDLList.h" +#include "epicsMutex.h" +#include "locationException.h" +#include "errlog.h" +#endif + +epicsTimerQueueNonThreaded::~epicsTimerQueueNonThreaded () {} +void epicsTimerNotify::show ( unsigned /* level */ ) const {} + +#if 0 +class epicsTimerForC : public epicsTimer { + epicsShareFunc virtual void expire (); + epicsShareFunc virtual void destroy (); + epicsShareFunc virtual bool again () const; + epicsShareFunc virtual double delay () const; + epicsShareFunc virtual void show ( unsigned level ) const; + const epicsTimerJumpTable &jt; + void * pPrivate; +public: + epicsTimerForC ( const epicsTimerJumpTable &jtIn, epicsTimerQueue &queue, void *pPrivateIn ); +}; + +epicsTimerForC::epicsTimerForC ( const epicsTimerJumpTable &jtIn, epicsTimerQueue &queue, void *pPrivateIn ) : + epicsTimer ( queue ), jt ( jtIn ), pPrivate ( pPrivateIn ) {} + +void epicsTimerForC::expire () +{ + ( *this->jt.expire ) ( this->pPrivate ); +} + +void epicsTimerForC::destroy () +{ + ( *this->jt.destroy ) ( this->pPrivate ); +} + +bool epicsTimerForC::again () const +{ + return ( *this->jt.again ) ( this->pPrivate ) ? true : false ; +} + +double epicsTimerForC::delay () const +{ + return ( *this->jt.delay ) ( this->pPrivate ); +} + +void epicsTimerForC::show ( unsigned level ) const +{ + ( *this->jt.show ) ( this->pPrivate, level ); +} + +extern "C" epicsShareFunc epicsTimerId epicsShareAPI epicsTimerCreate ( + const epicsTimerJumpTable *pjtIn, epicsTimerQueueId queueIdIn, void *pPrivateIn ) +{ + assert ( pjtIn ); + assert ( queueIdIn ); + return ( epicsTimerId ) new epicsTimerForC + ( *pjtIn, *static_cast < epicsTimerQueue * > ( queueIdIn ), pPrivateIn ); +} + +extern "C" epicsShareFunc void epicsShareAPI epicsTimerArm ( epicsTimerId tmrIdIn, double delay ) +{ + epicsTimerForC *pTmr = static_cast < epicsTimerForC * > ( tmrIdIn ); + assert ( pTmr ); + pTmr->reschedule ( delay ); +} + +extern "C" epicsShareFunc void epicsShareAPI epicsTimerCancel ( epicsTimerId tmrIdIn ) +{ + epicsTimerForC *pTmr = static_cast < epicsTimerForC * > ( tmrIdIn ); + assert ( pTmr ); + pTmr->cancel (); +} + +extern "C" epicsShareFunc double epicsShareAPI epicsTimerTimeRemaining ( epicsTimerId idIn ) +{ + epicsTimerForC *pTmr = static_cast < epicsTimerForC * > ( idIn ); + assert ( pTmr ); + return pTmr->timeRemaining (); +} + +extern "C" epicsShareFunc epicsTimeStamp epicsShareAPI epicsTimerExpirationDate ( epicsTimerId idIn ) +{ + epicsTimerForC *pTmr = static_cast < epicsTimerForC * > ( idIn ); + assert ( pTmr ); + return pTmr->expirationDate (); +} + +#endif \ No newline at end of file diff --git a/src/libCom/timer/epicsTimerPrivate.h b/src/libCom/timer/epicsTimerPrivate.h new file mode 100644 index 000000000..ae480edc6 --- /dev/null +++ b/src/libCom/timer/epicsTimerPrivate.h @@ -0,0 +1,161 @@ +/* + * $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 + * + */ + +#ifndef epicsTimerPrivate_h +#define epicsTimerPrivate_h + +#include "tsFreeList.h" +#include "tsDLList.h" +#include "epicsTimer.h" + +#ifdef DEBUG +# define debugPrintf(ARGSINPAREN) printf ARGSINPAREN +#else +# define debugPrintf(ARGSINPAREN) +#endif + +class timer : public epicsTimer, public tsDLNode < timer > { +public: + timer ( epicsTimerNotify &, class timerQueue & ); + void start ( const epicsTime & ); + void start ( double delaySeconds ); + void cancel (); + double getExpireDelay () const; + void show ( unsigned int level ) const; + void * operator new ( size_t size ); + void operator delete ( void *pCadaver, size_t size ); + class noMemory {}; // exception +protected: + ~timer (); +private: + enum state { statePending = 45, stateLimbo = 78 }; + epicsTime exp; // experation time + state curState; // current state + epicsTimerNotify ¬ify; // callback + timerQueue &queue; + void privateStart ( const epicsTime & ); + void privateCancel (); + double privateDelayToFirstExpire () const; + static tsFreeList < class timer, 0x20 > freeList; + friend class timerQueue; +}; + +class timerQueueNotify { +public: + virtual void reschedule () = 0; +}; + +class timerQueue { +public: + timerQueue ( timerQueueNotify ¬ify ); + ~timerQueue (); + void process (); + double delayToFirstExpire () const; + void show ( unsigned int level ) const; +private: + mutable epicsMutex mutex; + epicsEvent cancelBlockingEvent; + tsDLList < timer > timerList; + timerQueueNotify ¬ify; + timer *pExpireTmr; + epicsThreadId processThread; + bool cancelPending; + friend class timer; +}; + +class timerQueueThreadedMgrPrivate { +public: + timerQueueThreadedMgrPrivate (); +protected: + virtual ~timerQueueThreadedMgrPrivate () = 0; +private: + unsigned referenceCount; + friend class timerQueueThreadedMgr; +}; + +class timerQueueThreaded : public epicsTimerQueueThreaded, + public epicsThreadRunable, public timerQueueNotify, + public timerQueueThreadedMgrPrivate, + public tsDLNode < timerQueueThreaded > { +public: + timerQueueThreaded ( unsigned priority ); + epicsTimer & createTimer ( epicsTimerNotify & ); + void show ( unsigned int level ) const; + void * operator new ( size_t size ); + void operator delete ( void *pCadaver, size_t size ); + void release (); + int threadPriority () const; +protected: + ~timerQueueThreaded (); +private: + timerQueue queue; + epicsEvent rescheduleEvent; + epicsEvent exitEvent; + epicsThread thread; + bool exitFlag; + bool terminateFlag; + void run (); + void reschedule (); + static tsFreeList < class timerQueueThreaded, 0x8 > freeList; +}; + +class timerQueueThreadedMgr { +public: + ~timerQueueThreadedMgr (); + timerQueueThreaded & create ( bool okToShare, + int threadPriority = epicsThreadPriorityMin + 10 ); + void release ( timerQueueThreaded & ); +private: + epicsMutex mutex; + tsDLList < timerQueueThreaded > queueList; +}; + +inline void * timer::operator new ( size_t size ) +{ + return timer::freeList.allocate ( size ); +} + +inline void timer::operator delete ( void *pCadaver, size_t size ) +{ + timer::freeList.release ( pCadaver, size ); +} + +inline void * timerQueueThreaded::operator new ( size_t size ) +{ + return timerQueueThreaded::freeList.allocate ( size ); +} + +inline void timerQueueThreaded::operator delete ( void *pCadaver, size_t size ) +{ + timerQueueThreaded::freeList.release ( pCadaver, size ); +} + +#endif // epicsTimerPrivate_h + diff --git a/src/libCom/timer/timer.cpp b/src/libCom/timer/timer.cpp new file mode 100644 index 000000000..f64afd086 --- /dev/null +++ b/src/libCom/timer/timer.cpp @@ -0,0 +1,174 @@ + +/* + * $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 + * + */ + +#define epicsExportSharedSymbols +#include "epicsTimerPrivate.h" + +tsFreeList < class timer, 0x20 > timer::freeList; + +epicsTimer::~epicsTimer () {} + +timer::timer ( epicsTimerNotify ¬ifyIn, timerQueue &queueIn ) : + curState ( stateLimbo ), notify ( notifyIn ), queue ( queueIn ) +{ +} + +timer::~timer() +{ + this->cancel (); +} + +inline double timer::privateDelayToFirstExpire () const +{ + if ( this->curState == statePending ) { + double delay = this->exp - epicsTime::getCurrent (); + if ( delay < 0.0 ) { + delay = 0.0; + } + return delay; + } + else { + return -DBL_MAX; + } +} + +void timer::start ( double delaySeconds ) +{ + this->start ( epicsTime::getCurrent () + delaySeconds ); +} + +void timer::start ( const epicsTime & expire ) +{ + epicsAutoMutex locker ( this->queue.mutex ); + this->privateStart ( expire ); +} + +void timer::privateStart ( const epicsTime & expire ) +{ + this->privateCancel (); + + this->exp = expire; + +# ifdef DEBUG + unsigned preemptCount=0u; +# endif + + // + // insert into the pending queue + // + // Finds proper time sorted location using a linear search. + // + // **** this should use a binary tree ???? + // + tsDLIterBD < timer > pTmr = this->queue.timerList.lastIter (); + while ( true ) { + if ( ! pTmr.valid () ) { + // + // add to the beginning of the list + // + this->queue.timerList.push ( *this ); + this->queue.notify.reschedule (); + break; + } + if ( pTmr->exp <= this->exp ) { + // + // add after the item found that expires earlier + // + this->queue.timerList.insertAfter ( *this, *pTmr ); + break; + } +# ifdef DEBUG + preemptCount++; +# endif + --pTmr; + } + + this->curState = timer::statePending; + +# ifdef DEBUG + this->show ( 10u ); + this->queue.show ( 10u ); +# endif + + debugPrintf ( ("Arm of \"%s\" with delay %f at %p preempting %u\n", + typeid ( this->notify ).name (), + expire - epicsTime::getCurrent (), + this, preemptCount ) ); +} + +void timer::cancel () +{ + { + epicsAutoMutex locker ( this->queue.mutex ); + this->privateCancel (); + // dont wait if this was called indirectly by expire () + if ( this->queue.pExpireTmr == this && + this->queue.processThread != epicsThreadGetIdSelf () ) { + this->queue.cancelPending = true; + } + } + + // make certain timer expire() does not run after cancel () returns, + // but dont require that lock is applied while calling expire() + if ( this->queue.cancelPending ) { + while ( this->queue.cancelPending ) { + this->queue.cancelBlockingEvent.wait (); + } + // in case other threads are waiting + this->queue.cancelBlockingEvent.signal (); + } +} + +void timer::privateCancel () +{ + if ( this->curState == statePending ) { + this->queue.timerList.remove ( *this ); + this->curState = stateLimbo; + } +} + +double timer::getExpireDelay () const +{ + epicsAutoMutex locker ( this->queue.mutex ); + return this->privateDelayToFirstExpire (); +} + +void timer::show ( unsigned int level ) const +{ + double delay = this->getExpireDelay (); + printf ( "%s, state = %s, delay to expire = %g\n", + typeid ( this->notify ).name (), + this->curState == statePending ? "pending" : "limbo", + delay ); + if ( level >= 1u ) { + this->notify.show ( level - 1u ); + } +} diff --git a/src/libCom/timer/timerQueue.cpp b/src/libCom/timer/timerQueue.cpp new file mode 100644 index 000000000..39cd665f1 --- /dev/null +++ b/src/libCom/timer/timerQueue.cpp @@ -0,0 +1,159 @@ +/* + * $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 + * + */ + +#define epicsExportSharedSymbols +#include "epicsTimerPrivate.h" + +timerQueue::timerQueue ( timerQueueNotify ¬ifyIn ) : + notify ( notifyIn ), pExpireTmr ( 0 ), + processThread ( 0 ), cancelPending ( false ) +{ +} + +timerQueue::~timerQueue () +{ + timer *pTmr; + this->mutex.lock (); + while ( ( pTmr = this->timerList.get () ) ) { + pTmr->curState = timer::stateLimbo; + } +} + +double timerQueue::delayToFirstExpire () const +{ + epicsAutoMutex locker ( this->mutex ); + timer *pTmr = this->timerList.first (); + if ( pTmr ) { + return pTmr->privateDelayToFirstExpire (); + } + else { + return DBL_MAX; + } +} + +void timerQueue::process () +{ + epicsTime cur ( epicsTime::getCurrent () ); + + { + epicsAutoMutex locker ( this->mutex ); + + if ( this->pExpireTmr ) { + // if some other thread is processing the queue + // (or if this is a recursive call) + return; + } + + this->pExpireTmr = this->timerList.get (); + if ( ! this->pExpireTmr ) { + return; + } + if ( cur > this->pExpireTmr->exp ) { + + // + // Tag current epired tmr so that we can detect if call back + // is in progress when canceling the timer. + // +# ifdef DEBUG + this->pExpireTmr->show ( 0u ); +# endif + this->pExpireTmr->curState = timer::stateLimbo; + this->processThread = epicsThreadGetIdSelf (); + } + else { + // no activity + debugPrintf ( ( "no activity process\n" ) ); + this->timerList.push ( *this->pExpireTmr ); + this->pExpireTmr = 0; + return; + } + } + +# ifdef DEBUG + unsigned N = 0u; +# endif + + while ( true ) { + + debugPrintf ( ( "%5u expired \"%s\" with error %f sec\n", + N++, typeid ( this->pExpireTmr->notify ).name (), + cur - this->pExpireTmr->exp ) ); + + epicsTimerNotify::expireStatus expStat = this->pExpireTmr->notify.expire (); + + epicsAutoMutex locker ( this->mutex ); + + // + // only restart if they didnt cancel() the timer + // while the call back was running + // + if ( this->cancelPending ) { + // cancel () waits for this + this->cancelPending = false; + this->cancelBlockingEvent.signal (); + } + // restart as nec + else if ( expStat.restart () ) { + this->pExpireTmr->privateStart ( cur + expStat.expirationDelay () ); + } + + this->pExpireTmr = this->timerList.get (); + if ( ! this->pExpireTmr ) { + this->processThread = 0; + return; + } + if ( cur > this->pExpireTmr->exp ) { +# ifdef DEBUG + this->pExpireTmr->show ( 0u ); +# endif + this->pExpireTmr->curState = timer::stateLimbo; + } + else { + // no activity + this->timerList.push ( *this->pExpireTmr ); + this->pExpireTmr = 0; + this->processThread = 0; + return; + } + } +} + +void timerQueue::show ( unsigned level ) const +{ + epicsAutoMutex locker ( this->mutex ); + printf ( "epicsTimerQueue with %u items pending\n", this->timerList.count () ); + if ( level >= 1u ) { + tsDLIterConstBD < timer > iter = this->timerList.firstIter (); + while ( iter.valid () ) { + iter->show ( level - 1u ); + ++iter; + } + } +} diff --git a/src/libCom/timer/timerQueueThreaded.cpp b/src/libCom/timer/timerQueueThreaded.cpp new file mode 100644 index 000000000..5318a5271 --- /dev/null +++ b/src/libCom/timer/timerQueueThreaded.cpp @@ -0,0 +1,118 @@ +/* + * $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 + * + */ + +#define epicsExportSharedSymbols +#include "epicsTimerPrivate.h" + +static timerQueueThreadedMgr queueMgr; + +epicsTimerQueueThreaded::~epicsTimerQueueThreaded () {} + +epicsTimerQueueThreaded &epicsTimerQueueThreaded::create ( bool okToShare, int threadPriority ) +{ + return queueMgr.create ( okToShare, threadPriority ); +} + +tsFreeList < class timerQueueThreaded, 0x8 > timerQueueThreaded::freeList; + +timerQueueThreaded::timerQueueThreaded ( unsigned priority ) : + thread ( *this, "epicsTimerQueue", + epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ), + queue ( *this ), exitFlag ( false ), terminateFlag ( false ) +{ + this->thread.start (); +} + +timerQueueThreaded::~timerQueueThreaded () +{ + this->terminateFlag = true; + this->rescheduleEvent.signal (); + while ( ! this->exitFlag && ! this->thread.isSuspended () ) { + this->exitEvent.wait ( 1.0 ); + } + // in case other threads are waiting here also + this->exitEvent.signal (); +} + +int timerQueueThreaded::threadPriority () const +{ + return thread.getPriority (); +} + +void timerQueueThreaded::release () +{ + queueMgr.release ( *this ); +} + +void timerQueueThreaded::run () +{ + this->exitFlag = false; + while ( ! this->terminateFlag ) { + double delay = this->queue.delayToFirstExpire (); + if ( delay <= 0.0 ) { + this->queue.process (); + } + else { + debugPrintf ( ( "timer thread sleeping for %g sec (max)\n", delay ) ); + this->rescheduleEvent.wait ( delay ); + } + } + this->exitFlag = true; + this->exitEvent.signal (); // no access to queue after exitEvent signal +} + +epicsTimer & timerQueueThreaded::createTimer ( epicsTimerNotify & notify ) +{ + timer *pTmr = new timer ( notify, this->queue ); + if ( ! pTmr ) { + throwWithLocation ( timer::noMemory () ); + } + return *pTmr; +} + +void timerQueueThreaded::reschedule () +{ + this->rescheduleEvent.signal (); +} + +void timerQueueThreaded::show ( unsigned int level ) const +{ + printf ( "EPICS threaded timer queue at %p\n", this ); + if ( level >=1u ) { + this->queue.show ( level - 1u ); + printf ( "reschedule event\n" ); + this->rescheduleEvent.show ( level - 1u ); + printf ( "exit event\n" ); + this->exitEvent.show ( level - 1u ); + printf ( "exitFlag = %c, terminateFlag = %c\n", + this->exitFlag ? 'T' : 'F', + this->terminateFlag ? 'T' : 'F' ); + } +} diff --git a/src/libCom/timer/timerQueueThreadedMgr.cpp b/src/libCom/timer/timerQueueThreadedMgr.cpp new file mode 100644 index 000000000..edc759282 --- /dev/null +++ b/src/libCom/timer/timerQueueThreadedMgr.cpp @@ -0,0 +1,85 @@ +/* + * $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 + +#define epicsExportSharedSymbols +#include "epicsTimerPrivate.h" + +timerQueueThreadedMgr::~timerQueueThreadedMgr () +{ + epicsAutoMutex locker ( this->mutex ); + while ( timerQueueThreaded * pQ = this->queueList.get () ) { + timerQueueThreadedMgrPrivate *pPriv = pQ; + delete pPriv; + } +} + +timerQueueThreaded & timerQueueThreadedMgr::create ( + bool okToShare, int threadPriority ) +{ + epicsAutoMutex locker ( this->mutex ); + tsDLIterBD < timerQueueThreaded > iter = this->queueList.firstIter (); + while ( iter.valid () ) { + if ( iter->threadPriority () == threadPriority ) { + assert ( iter->timerQueueThreadedMgrPrivate::referenceCount < UINT_MAX ); + iter->timerQueueThreadedMgrPrivate::referenceCount++; + return *iter; + } + } + timerQueueThreaded *pQueue = new timerQueueThreaded ( threadPriority ); + if ( ! pQueue ) { + throwWithLocation ( timer::noMemory () ); + } + pQueue->timerQueueThreadedMgrPrivate::referenceCount = 1u; + this->queueList.add ( *pQueue ); + return *pQueue; +} + +void timerQueueThreadedMgr::release ( timerQueueThreaded &queue ) +{ + epicsAutoMutex locker ( this->mutex ); + assert ( queue.timerQueueThreadedMgrPrivate::referenceCount > 0u ); + queue.timerQueueThreadedMgrPrivate::referenceCount--; + if ( queue.timerQueueThreadedMgrPrivate::referenceCount == 0u ) { + this->queueList.remove ( queue ); + timerQueueThreadedMgrPrivate *pPriv = &queue; + delete pPriv; + } +} + +timerQueueThreadedMgrPrivate::timerQueueThreadedMgrPrivate () : + referenceCount ( 0u ) +{ +} + +timerQueueThreadedMgrPrivate::~timerQueueThreadedMgrPrivate () +{ +} \ No newline at end of file