made compatible with timer library API changes
This commit is contained in:
@@ -40,7 +40,6 @@ static const double delayVerifyOffset = 1.0; // sec
|
||||
class delayVerify : public epicsTimerNotify {
|
||||
public:
|
||||
delayVerify ( double expectedDelay, epicsTimerQueue & );
|
||||
expireStatus expire ();
|
||||
void * operator new ( size_t size );
|
||||
void operator delete ( void *pCadaver, size_t size );
|
||||
void start ( const epicsTime &expireTime );
|
||||
@@ -54,6 +53,7 @@ private:
|
||||
epicsTime beginStamp;
|
||||
epicsTime expireStamp;
|
||||
double expectedDelay;
|
||||
expireStatus expire ( const epicsTime & );
|
||||
static tsFreeList < class delayVerify, 0x20 > freeList;
|
||||
static epicsMutex freeListMutex;
|
||||
};
|
||||
@@ -62,7 +62,7 @@ static unsigned expireCount;
|
||||
static epicsEvent expireEvent;
|
||||
|
||||
delayVerify::delayVerify ( double expectedDelayIn, epicsTimerQueue &queue ) :
|
||||
timer ( queue.createTimer ( *this ) ), expectedDelay ( expectedDelayIn )
|
||||
timer ( queue.createTimer() ), expectedDelay ( expectedDelayIn )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ void delayVerify::checkError () const
|
||||
|
||||
inline void delayVerify::start ( const epicsTime &expireTime )
|
||||
{
|
||||
this->timer.start ( expireTime );
|
||||
this->timer.start ( *this, expireTime );
|
||||
}
|
||||
|
||||
tsFreeList < class delayVerify, 0x20 > delayVerify::freeList;
|
||||
@@ -113,9 +113,9 @@ inline void delayVerify::operator delete ( void *pCadaver, size_t size )
|
||||
delayVerify::freeList.release ( pCadaver, size );
|
||||
}
|
||||
|
||||
epicsTimerNotify::expireStatus delayVerify::expire ()
|
||||
epicsTimerNotify::expireStatus delayVerify::expire ( const epicsTime ¤tTime )
|
||||
{
|
||||
this->expireStamp = epicsTime::getCurrent ();
|
||||
this->expireStamp = currentTime;
|
||||
if ( --expireCount == 0u ) {
|
||||
expireEvent.signal ();
|
||||
}
|
||||
@@ -156,7 +156,6 @@ void testAccuracy ()
|
||||
class cancelVerify : public epicsTimerNotify {
|
||||
public:
|
||||
cancelVerify ( epicsTimerQueue & );
|
||||
expireStatus expire ();
|
||||
void * operator new ( size_t size );
|
||||
void operator delete ( void *pCadaver, size_t size );
|
||||
void start ( const epicsTime &expireTime );
|
||||
@@ -166,12 +165,13 @@ protected:
|
||||
private:
|
||||
epicsTimer &timer;
|
||||
bool failOutIfExpireIsCalled;
|
||||
expireStatus expire ( const epicsTime & );
|
||||
static tsFreeList < class cancelVerify, 0x20 > freeList;
|
||||
static epicsMutex freeListMutex;
|
||||
};
|
||||
|
||||
cancelVerify::cancelVerify ( epicsTimerQueue &queue ) :
|
||||
timer ( queue.createTimer ( *this ) ), failOutIfExpireIsCalled ( false )
|
||||
timer ( queue.createTimer () ), failOutIfExpireIsCalled ( false )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ cancelVerify::~cancelVerify ()
|
||||
|
||||
inline void cancelVerify::start ( const epicsTime &expireTime )
|
||||
{
|
||||
this->timer.start ( expireTime );
|
||||
this->timer.start ( *this, expireTime );
|
||||
}
|
||||
|
||||
tsFreeList < class cancelVerify, 0x20 > cancelVerify::freeList;
|
||||
@@ -206,7 +206,7 @@ inline void cancelVerify::cancel ()
|
||||
this->failOutIfExpireIsCalled = true;
|
||||
}
|
||||
|
||||
epicsTimerNotify::expireStatus cancelVerify::expire ()
|
||||
epicsTimerNotify::expireStatus cancelVerify::expire ( const epicsTime & )
|
||||
{
|
||||
double root = 3.14159;
|
||||
for ( unsigned i = 0u; i < 10000; i++ ) {
|
||||
@@ -247,7 +247,6 @@ void testCancel ()
|
||||
class periodicVerify : public epicsTimerNotify {
|
||||
public:
|
||||
periodicVerify ( epicsTimerQueue & );
|
||||
expireStatus expire ();
|
||||
void * operator new ( size_t size );
|
||||
void operator delete ( void *pCadaver, size_t size );
|
||||
void start ( const epicsTime &expireTime );
|
||||
@@ -259,12 +258,13 @@ private:
|
||||
unsigned nExpire;
|
||||
epicsTimer &timer;
|
||||
bool failOutIfExpireIsCalled;
|
||||
expireStatus expire ( const epicsTime & );
|
||||
static tsFreeList < class periodicVerify, 0x20 > freeList;
|
||||
static epicsMutex freeListMutex;
|
||||
};
|
||||
|
||||
periodicVerify::periodicVerify ( epicsTimerQueue &queue ) :
|
||||
nExpire ( 0u ), timer ( queue.createTimer ( *this ) ), failOutIfExpireIsCalled ( false )
|
||||
nExpire ( 0u ), timer ( queue.createTimer () ), failOutIfExpireIsCalled ( false )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ periodicVerify::~periodicVerify ()
|
||||
|
||||
inline void periodicVerify::start ( const epicsTime &expireTime )
|
||||
{
|
||||
this->timer.start ( expireTime );
|
||||
this->timer.start ( *this, expireTime );
|
||||
}
|
||||
|
||||
tsFreeList < class periodicVerify, 0x20 > periodicVerify::freeList;
|
||||
@@ -304,7 +304,7 @@ inline void periodicVerify::verifyCount ()
|
||||
assert ( this->nExpire > 1u );
|
||||
}
|
||||
|
||||
epicsTimerNotify::expireStatus periodicVerify::expire ()
|
||||
epicsTimerNotify::expireStatus periodicVerify::expire ( const epicsTime & )
|
||||
{
|
||||
this->nExpire++;
|
||||
double root = 3.14159;
|
||||
|
||||
@@ -43,7 +43,7 @@ protected:
|
||||
private:
|
||||
epicsTimerCallback pCallBack;
|
||||
void * pPrivate;
|
||||
expireStatus expire ();
|
||||
expireStatus expire ( const epicsTime & currentTime );
|
||||
static tsFreeList < epicsTimerForC > freeList;
|
||||
static epicsMutex freeListMutex;
|
||||
};
|
||||
@@ -70,7 +70,7 @@ tsFreeList < epicsTimerForC > epicsTimerForC::freeList;
|
||||
epicsMutex epicsTimerForC::freeListMutex;
|
||||
|
||||
epicsTimerForC::epicsTimerForC ( timerQueue &queue, epicsTimerCallback pCBIn, void *pPrivateIn ) :
|
||||
timer ( *this, queue ), pCallBack ( pCBIn ), pPrivate ( pPrivateIn )
|
||||
timer ( queue ), pCallBack ( pCBIn ), pPrivate ( pPrivateIn )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ inline void epicsTimerForC::operator delete ( void *pCadaver, size_t size )
|
||||
epicsTimerForC::freeList.release ( pCadaver, size );
|
||||
}
|
||||
|
||||
epicsTimerNotify::expireStatus epicsTimerForC::expire ()
|
||||
epicsTimerNotify::expireStatus epicsTimerForC::expire ( const epicsTime & )
|
||||
{
|
||||
( *this->pCallBack ) ( this->pPrivate );
|
||||
return noRestart;
|
||||
@@ -171,17 +171,10 @@ extern "C" void epicsShareAPI
|
||||
pQueue->destroy ();
|
||||
}
|
||||
|
||||
extern "C" void epicsShareAPI
|
||||
extern "C" double epicsShareAPI
|
||||
epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId pQueue )
|
||||
{
|
||||
pQueue->process ();
|
||||
}
|
||||
|
||||
extern "C" double epicsShareAPI
|
||||
epicsTimerQueuePassiveGetDelayToNextExpire (
|
||||
epicsTimerQueuePassiveId pQueue )
|
||||
{
|
||||
return pQueue->getNextExpireDelay ();
|
||||
return pQueue->process ( epicsTime::getCurrent() );
|
||||
}
|
||||
|
||||
extern "C" epicsTimerId epicsShareAPI epicsTimerQueuePassiveCreateTimer (
|
||||
@@ -243,13 +236,13 @@ extern "C" void epicsShareAPI epicsTimerDestroy ( epicsTimerId pTmr )
|
||||
extern "C" void epicsShareAPI epicsTimerStartTime (
|
||||
epicsTimerId pTmr, const epicsTimeStamp *pTime )
|
||||
{
|
||||
pTmr->start ( *pTime );
|
||||
pTmr->start ( *pTmr, *pTime );
|
||||
}
|
||||
|
||||
extern "C" void epicsShareAPI epicsTimerStartDelay (
|
||||
epicsTimerId pTmr, double delaySeconds )
|
||||
{
|
||||
pTmr->start ( delaySeconds );
|
||||
pTmr->start ( *pTmr, delaySeconds );
|
||||
}
|
||||
|
||||
extern "C" void epicsShareAPI epicsTimerCancel ( epicsTimerId pTmr )
|
||||
|
||||
@@ -20,6 +20,11 @@ of this distribution.
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
//
|
||||
// Notes:
|
||||
// 1) epicsTimer does not hold its lock when calling callbacks.
|
||||
//
|
||||
|
||||
// code using a timer must implement epicsTimerNotify
|
||||
class epicsTimerNotify {
|
||||
public:
|
||||
@@ -35,23 +40,29 @@ public:
|
||||
double delay;
|
||||
};
|
||||
// return noRestart OR return expireStatus ( restart, 30.0 /* sec */ );
|
||||
virtual expireStatus expire () = 0;
|
||||
virtual expireStatus expire ( const epicsTime & currentTime ) = 0;
|
||||
virtual epicsShareFunc void show ( unsigned int level ) const;
|
||||
};
|
||||
|
||||
class epicsTimer {
|
||||
public:
|
||||
virtual ~epicsTimer () = 0;
|
||||
virtual void start ( const epicsTime & ) = 0;
|
||||
virtual void start ( double delaySeconds ) = 0;
|
||||
virtual void start ( epicsTimerNotify &, const epicsTime & ) = 0;
|
||||
virtual void start ( epicsTimerNotify &, double delaySeconds ) = 0;
|
||||
virtual void cancel () = 0;
|
||||
virtual double getExpireDelay () const = 0;
|
||||
struct expireInfo {
|
||||
expireInfo ( bool active, const epicsTime &expireTime );
|
||||
bool active;
|
||||
epicsTime expireTime;
|
||||
};
|
||||
virtual expireInfo getExpireInfo () const = 0;
|
||||
double getExpireDelay ();
|
||||
virtual void show ( unsigned int level ) const = 0;
|
||||
};
|
||||
|
||||
class epicsTimerQueue {
|
||||
public:
|
||||
virtual epicsTimer & createTimer ( epicsTimerNotify & ) = 0;
|
||||
virtual epicsTimer & createTimer () = 0;
|
||||
virtual void show ( unsigned int level ) const = 0;
|
||||
protected:
|
||||
virtual ~epicsTimerQueue () = 0;
|
||||
@@ -77,8 +88,7 @@ class epicsTimerQueuePassive : public epicsTimerQueue {
|
||||
public:
|
||||
static epicsShareFunc epicsTimerQueuePassive & create ( epicsTimerQueueNotify & );
|
||||
virtual ~epicsTimerQueuePassive () = 0;
|
||||
virtual void process () = 0;
|
||||
virtual double getNextExpireDelay () const = 0;
|
||||
virtual double process ( const epicsTime & currentTime ) = 0; // returns delay to next expire
|
||||
};
|
||||
|
||||
inline epicsTimerNotify::expireStatus::expireStatus ( restart_t restart ) :
|
||||
@@ -105,6 +115,27 @@ inline double epicsTimerNotify::expireStatus::expirationDelay () const
|
||||
return this->delay;
|
||||
}
|
||||
|
||||
inline epicsTimer::expireInfo::expireInfo ( bool activeIn,
|
||||
const epicsTime & expireTimeIn ) :
|
||||
active ( activeIn ), expireTime ( expireTimeIn )
|
||||
{
|
||||
}
|
||||
|
||||
inline double epicsTimer::getExpireDelay ()
|
||||
{
|
||||
epicsTimer::expireInfo info = this->getExpireInfo ();
|
||||
if ( info.active ) {
|
||||
double delay = info.expireTime - epicsTime::getCurrent ();
|
||||
if ( delay < 0.0 ) {
|
||||
delay = 0.0;
|
||||
}
|
||||
return delay;
|
||||
}
|
||||
else {
|
||||
return - DBL_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
@@ -133,11 +164,8 @@ epicsShareFunc void epicsShareAPI
|
||||
epicsShareFunc epicsTimerId epicsShareAPI
|
||||
epicsTimerQueuePassiveCreateTimer (
|
||||
epicsTimerQueuePassiveId queueid, epicsTimerCallback pCallback, void *pArg );
|
||||
epicsShareFunc void epicsShareAPI
|
||||
epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId );
|
||||
epicsShareFunc double epicsShareAPI
|
||||
epicsTimerQueuePassiveGetDelayToNextExpire (
|
||||
epicsTimerQueuePassiveId );
|
||||
epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId );
|
||||
epicsShareFunc void epicsShareAPI epicsTimerQueuePassiveShow (
|
||||
epicsTimerQueuePassiveId id, unsigned int level );
|
||||
|
||||
|
||||
@@ -1,714 +0,0 @@
|
||||
/*
|
||||
* $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 assert // allow use of epicsAssert.h
|
||||
#include <assert.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <float.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "osiTimer.h"
|
||||
#include "locationException.h"
|
||||
#include "errlog.h"
|
||||
|
||||
class osiTimerThread : public epicsThreadRunable {
|
||||
public:
|
||||
osiTimerThread (osiTimerQueue &, unsigned priority);
|
||||
virtual ~osiTimerThread();
|
||||
private:
|
||||
friend class osiTimer;
|
||||
friend class osiTimerQueue;
|
||||
epicsThread thread;
|
||||
osiTimerQueue &queue;
|
||||
|
||||
virtual void run();
|
||||
};
|
||||
|
||||
//
|
||||
// default global timer queue
|
||||
//
|
||||
osiTimerQueue osiDefaultTimerQueue ( osiTimerQueue::mtsNoManagerThread );
|
||||
|
||||
//
|
||||
// osiTimer::osiTimer ()
|
||||
//
|
||||
// create an active timer that will expire in delay seconds
|
||||
//
|
||||
osiTimer::osiTimer (double delay, osiTimerQueue & queueIn) :
|
||||
queue (queueIn)
|
||||
{
|
||||
this->arm (delay);
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::osiTimer ()
|
||||
//
|
||||
// create an active timer that will expire in delay seconds
|
||||
//
|
||||
osiTimer::osiTimer (double delay) :
|
||||
queue (osiDefaultTimerQueue)
|
||||
{
|
||||
this->arm (delay);
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::osiTimer ()
|
||||
//
|
||||
// create an inactive timer
|
||||
//
|
||||
osiTimer::osiTimer (osiTimerQueue & queueIn) :
|
||||
curState (osiTimer::stateIdle), queue (queueIn)
|
||||
{
|
||||
epicsAutoMutex ( this->queue.mutex );
|
||||
this->queue.timerLists[stateIdle].add (*this);
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::osiTimer ()
|
||||
//
|
||||
// create an inactive timer
|
||||
//
|
||||
osiTimer::osiTimer () :
|
||||
curState (osiTimer::stateIdle), queue (osiDefaultTimerQueue)
|
||||
{
|
||||
epicsAutoMutex ( this->queue.mutex );
|
||||
this->queue.timerLists[stateIdle].add (*this);
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::~osiTimer()
|
||||
//
|
||||
osiTimer::~osiTimer()
|
||||
{
|
||||
if ( this->curState == stateLimbo ) {
|
||||
return; // queue was destroyed
|
||||
}
|
||||
|
||||
epicsAutoMutex ( this->queue.mutex );
|
||||
//
|
||||
// signal the timer queue if this
|
||||
// occurring during the expire call
|
||||
// back
|
||||
//
|
||||
if (this == this->queue.pExpireTmr) {
|
||||
this->queue.pExpireTmr = 0;
|
||||
}
|
||||
this->queue.timerLists[this->curState].remove(*this);
|
||||
this->curState = stateLimbo;
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::cancel ()
|
||||
//
|
||||
void osiTimer::cancel ()
|
||||
{
|
||||
if ( this->curState == stateLimbo ) {
|
||||
return; // queue was destroyed
|
||||
}
|
||||
|
||||
{
|
||||
epicsAutoMutex ( this->queue.mutex );
|
||||
|
||||
//
|
||||
// signal the timer queue if this
|
||||
// occurring during the expire call
|
||||
// back
|
||||
//
|
||||
if (this == this->queue.pExpireTmr) {
|
||||
this->queue.pExpireTmr = 0;
|
||||
}
|
||||
|
||||
this->queue.timerLists[this->curState].remove (*this);
|
||||
this->curState = stateIdle;
|
||||
this->queue.timerLists[stateIdle].add (*this);
|
||||
}
|
||||
|
||||
this->destroy ();
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::reschedule()
|
||||
//
|
||||
// pull this timer out of the queue and reinstall
|
||||
// it with a new experation time
|
||||
//
|
||||
void osiTimer::reschedule ()
|
||||
{
|
||||
this->reschedule ( this->delay () );
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::reschedule()
|
||||
//
|
||||
// pull this timer out of the queue ans reinstall
|
||||
// it with a new experation time
|
||||
//
|
||||
void osiTimer::reschedule (double newDelay)
|
||||
{
|
||||
if ( this->curState == stateLimbo ) {
|
||||
return; // queue was destroyed
|
||||
}
|
||||
|
||||
epicsAutoMutex ( this->queue.mutex );
|
||||
|
||||
//
|
||||
// signal the timer queue if this
|
||||
// occurring during the expire call
|
||||
// back
|
||||
//
|
||||
if ( this == this->queue.pExpireTmr ) {
|
||||
this->queue.pExpireTmr = 0;
|
||||
}
|
||||
this->queue.timerLists[this->curState].remove (*this);
|
||||
this->curState = stateLimbo;
|
||||
this->arm (newDelay);
|
||||
}
|
||||
|
||||
//
|
||||
// Start the timer with delay this->delay() if inactive
|
||||
//
|
||||
epicsShareFunc void osiTimer::activate ()
|
||||
{
|
||||
this->activate ( this->delay () );
|
||||
}
|
||||
|
||||
//
|
||||
// Start the timer with delay newDelay if inactive
|
||||
//
|
||||
epicsShareFunc void osiTimer::activate ( double newDelay )
|
||||
{
|
||||
if ( this->curState == stateLimbo ) {
|
||||
return; // queue was destroyed
|
||||
}
|
||||
|
||||
epicsAutoMutex ( this->queue.mutex );
|
||||
|
||||
if ( this->curState == stateIdle ) {
|
||||
//
|
||||
// signal the timer queue if this
|
||||
// occurring during the expire call
|
||||
// back
|
||||
//
|
||||
if ( this == this->queue.pExpireTmr ) {
|
||||
this->queue.pExpireTmr = 0;
|
||||
}
|
||||
this->queue.timerLists[this->curState].remove (*this);
|
||||
this->curState = stateLimbo;
|
||||
this->arm (newDelay);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::arm ()
|
||||
//
|
||||
void osiTimer::arm (double initialDelay)
|
||||
{
|
||||
bool first;
|
||||
|
||||
# ifdef DEBUG
|
||||
unsigned preemptCount = 0u;
|
||||
# endif
|
||||
|
||||
{
|
||||
epicsAutoMutex ( this->queue.mutex );
|
||||
|
||||
//
|
||||
// create manager thread on demand so we dont have threads hanging
|
||||
// around that are not used
|
||||
//
|
||||
if ( ! this->queue.terminateFlag && this->queue.pMgrThread == NULL ) {
|
||||
this->queue.pMgrThread = new osiTimerThread (this->queue, this->queue.mgrThreadPriority);
|
||||
if ( this->queue.pMgrThread == NULL ) {
|
||||
throwWithLocation ( noMemory () );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// calculate absolute expiration time
|
||||
//
|
||||
this->exp = epicsTime::getCurrent () + initialDelay;
|
||||
|
||||
//
|
||||
// insert into the pending queue
|
||||
//
|
||||
// Finds proper time sorted location using
|
||||
// a linear search.
|
||||
//
|
||||
// **** this should use a binary tree ????
|
||||
//
|
||||
tsDLIterBD<osiTimer> iter = this->queue.timerLists[statePending].lastIter ();
|
||||
while (1) {
|
||||
if ( ! iter.valid () ) {
|
||||
//
|
||||
// add to the beginning of the list
|
||||
//
|
||||
this->queue.timerLists[statePending].push (*this);
|
||||
first = true;
|
||||
break;
|
||||
}
|
||||
if ( iter->exp <= this->exp ) {
|
||||
//
|
||||
// add after the item found that expires earlier
|
||||
//
|
||||
this->queue.timerLists[statePending].insertAfter (*this, *iter);
|
||||
first = false;
|
||||
break;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
preemptCount++;
|
||||
# endif
|
||||
--iter;
|
||||
}
|
||||
|
||||
this->curState = osiTimer::statePending;
|
||||
|
||||
# ifdef DEBUG
|
||||
this->show (10u);
|
||||
# endif
|
||||
|
||||
# ifdef DEBUG
|
||||
//
|
||||
// name virtual function isnt always useful here because this is
|
||||
// often called inside the constructor (unless we are
|
||||
// rearming the same timer)
|
||||
//
|
||||
printf ("Arm of \"%s\" with delay %f at %lx preempting %u\n",
|
||||
this->name(), initialDelay, (unsigned long)this, preemptCount);
|
||||
# endif
|
||||
}
|
||||
|
||||
if (first) {
|
||||
this->queue.rescheduleEvent.signal ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::again()
|
||||
//
|
||||
bool osiTimer::again () const
|
||||
{
|
||||
//
|
||||
// default is to run the timer only once
|
||||
//
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::destroy()
|
||||
//
|
||||
void osiTimer::destroy ()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::delay()
|
||||
//
|
||||
double osiTimer::delay () const
|
||||
{
|
||||
throwWithLocation ( noDelaySpecified () );
|
||||
return DBL_MAX; // never here
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::show()
|
||||
//
|
||||
void osiTimer::show (unsigned level) const
|
||||
{
|
||||
epicsTime cur = epicsTime::getCurrent ();
|
||||
printf ("osiTimer at %p for \"%s\" with again = %d\n",
|
||||
static_cast <const void *> (this), this->name(), this->again());
|
||||
if (level>=1u) {
|
||||
double delay = this->exp - cur;
|
||||
printf ("\tdelay to expire = %f, state = %d\n",
|
||||
delay, this->curState);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::name()
|
||||
// virtual base default
|
||||
//
|
||||
const char *osiTimer::name() const
|
||||
{
|
||||
return "osiTimer";
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimer::timeRemaining()
|
||||
//
|
||||
// return the number of seconds remaining before
|
||||
// this timer will expire
|
||||
//
|
||||
double osiTimer::timeRemaining () const
|
||||
{
|
||||
double delay;
|
||||
|
||||
epicsAutoMutex ( this->queue.mutex );
|
||||
|
||||
switch (this->curState) {
|
||||
case statePending:
|
||||
delay = this->exp - epicsTime::getCurrent();
|
||||
if ( delay < 0.0 ) {
|
||||
delay = 0.0;
|
||||
}
|
||||
break;
|
||||
|
||||
case stateIdle:
|
||||
delay = DBL_MAX;
|
||||
break;
|
||||
|
||||
case stateExpired:
|
||||
delay = 0.0;
|
||||
break;
|
||||
|
||||
case stateLimbo:
|
||||
errlogPrintf ("time remaning fetched on a osiTimer with no queue?\n");
|
||||
delay = DBL_MAX; // queue was destroyed
|
||||
break;
|
||||
|
||||
default:
|
||||
errlogPrintf ("saw osiTimer in undefined state\n");
|
||||
delay = DBL_MAX;
|
||||
break;
|
||||
}
|
||||
|
||||
return delay;
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimerQueue::osiTimerQueue ()
|
||||
//
|
||||
epicsShareFunc osiTimerQueue::osiTimerQueue ( managerThreadSelect mts,
|
||||
unsigned managerThreadPriority ) :
|
||||
pExpireTmr (0), pMgrThread (0), mgrThreadPriority (managerThreadPriority),
|
||||
inProcess (false), exitFlag (false)
|
||||
{
|
||||
this->terminateFlag = ( mts != mtsCreateManagerThread );
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimerQueue::~osiTimerQueue()
|
||||
//
|
||||
osiTimerQueue::~osiTimerQueue()
|
||||
{
|
||||
osiTimer *pTmr;
|
||||
|
||||
this->terminateManagerThread ();
|
||||
|
||||
this->mutex.lock ();
|
||||
|
||||
//
|
||||
// destroy any unexpired timers
|
||||
//
|
||||
while ( ( pTmr = this->timerLists[osiTimer::statePending].get () ) ) {
|
||||
pTmr->curState = osiTimer::stateLimbo;
|
||||
pTmr->destroy ();
|
||||
}
|
||||
|
||||
//
|
||||
// destroy any expired timers
|
||||
//
|
||||
while ( (pTmr = this->timerLists[osiTimer::stateExpired].get()) ) {
|
||||
pTmr->curState = osiTimer::stateLimbo;
|
||||
pTmr->destroy ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimerQueue::terminateManagerThread ()
|
||||
//
|
||||
void osiTimerQueue::terminateManagerThread ()
|
||||
{
|
||||
// stop current thread if started and
|
||||
// prevent launching of thread in the future
|
||||
this->terminateFlag = true;
|
||||
if ( this->pMgrThread ) {
|
||||
|
||||
this->rescheduleEvent.signal ();
|
||||
|
||||
while ( ! this->exitFlag && ! this->pMgrThread->thread.isSuspended () ) {
|
||||
this->exitEvent.wait ( 1.0 );
|
||||
}
|
||||
|
||||
{
|
||||
epicsAutoMutex ( this->mutex );
|
||||
if ( this->pMgrThread ) {
|
||||
delete this->pMgrThread;
|
||||
this->pMgrThread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// in case other threads are waiting here also
|
||||
this->exitEvent.signal ();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimerThread::osiTimerThread ()
|
||||
//
|
||||
osiTimerThread::osiTimerThread (osiTimerQueue &queueIn, unsigned priority) :
|
||||
thread( *this, "osiTimerQueue",
|
||||
epicsThreadGetStackSize (epicsThreadStackMedium), priority) ,
|
||||
queue (queueIn)
|
||||
{
|
||||
this->thread.start ();
|
||||
}
|
||||
|
||||
osiTimerThread::~osiTimerThread() {};
|
||||
|
||||
//
|
||||
// osiTimerThread::run ()
|
||||
//
|
||||
void osiTimerThread::run ()
|
||||
{
|
||||
queue.exitFlag = false;
|
||||
while ( ! queue.terminateFlag ) {
|
||||
queue.privateProcess ();
|
||||
double delay = queue.delayToFirstExpire ();
|
||||
queue.rescheduleEvent.wait ( delay );
|
||||
}
|
||||
queue.exitFlag = true;
|
||||
queue.exitEvent.signal (); // no access to queue after exitEvent signal
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimerQueue::delayToFirstExpire ()
|
||||
//
|
||||
double osiTimerQueue::delayToFirstExpire () const
|
||||
{
|
||||
osiTimer *pTmr;
|
||||
double delay;
|
||||
|
||||
epicsAutoMutex locker ( this->mutex );
|
||||
|
||||
pTmr = this->timerLists[osiTimer::statePending].first ();
|
||||
if (pTmr) {
|
||||
delay = pTmr->timeRemaining ();
|
||||
# ifdef DEBUG
|
||||
printf ("delay for %f sec for %s\n", delay, pTmr->name () );
|
||||
# endif
|
||||
}
|
||||
else {
|
||||
//
|
||||
// no timer in the queue - return a long delay - 30 min
|
||||
//
|
||||
delay = 30u * epicsTime::secPerMin;
|
||||
}
|
||||
|
||||
return delay; // seconds
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimerQueue::process()
|
||||
//
|
||||
void osiTimerQueue::process ()
|
||||
{
|
||||
if ( this->terminateFlag ) {
|
||||
this->privateProcess ();
|
||||
}
|
||||
}
|
||||
|
||||
void osiTimerQueue::privateProcess ()
|
||||
{
|
||||
epicsTime cur ( epicsTime::getCurrent () );
|
||||
osiTimer *pTmr;
|
||||
|
||||
this->mutex.lock ();
|
||||
|
||||
// no recursion
|
||||
if (this->inProcess) {
|
||||
this->mutex.unlock ();
|
||||
return;
|
||||
}
|
||||
this->inProcess = true;
|
||||
|
||||
|
||||
tsDLIterBD <osiTimer> iter = this->timerLists[osiTimer::statePending].firstIter ();
|
||||
while ( iter.valid () ) {
|
||||
if ( iter->exp > cur ) {
|
||||
break;
|
||||
}
|
||||
tsDLIterBD <osiTimer> tmp = iter;
|
||||
++tmp;
|
||||
this->timerLists[osiTimer::statePending].remove(*iter);
|
||||
iter->curState = osiTimer::stateExpired;
|
||||
this->timerLists[osiTimer::stateExpired].add(*iter);
|
||||
iter = tmp;
|
||||
}
|
||||
|
||||
//
|
||||
// I am careful to prevent problems if they access the
|
||||
// above list while in an "expire()" call back
|
||||
//
|
||||
while ( ( pTmr = this->timerLists[osiTimer::stateExpired].get () ) ) {
|
||||
|
||||
pTmr->curState = osiTimer::stateIdle;
|
||||
this->timerLists[osiTimer::stateIdle].add (*pTmr);
|
||||
|
||||
//
|
||||
// Tag current tmr so that we
|
||||
// can detect if it was deleted
|
||||
// during the expire call back
|
||||
//
|
||||
this->pExpireTmr = pTmr;
|
||||
|
||||
//
|
||||
// remove lock while calling their callback
|
||||
//
|
||||
this->mutex.unlock ();
|
||||
|
||||
#ifdef DEBUG
|
||||
double diff = cur-pTmr->exp;
|
||||
printf ("expired %lx for \"%s\" with error %f\n",
|
||||
(unsigned long)pTmr, pTmr->name(), diff);
|
||||
#endif
|
||||
|
||||
pTmr->expire();
|
||||
|
||||
this-> mutex.lock();
|
||||
|
||||
if ( this->pExpireTmr == pTmr ) {
|
||||
if ( pTmr->again () ) {
|
||||
this->timerLists[osiTimer::stateIdle].remove (*pTmr);
|
||||
pTmr->arm (pTmr->delay());
|
||||
}
|
||||
else {
|
||||
pTmr->destroy ();
|
||||
}
|
||||
}
|
||||
else {
|
||||
//
|
||||
// no recursive calls to process allowed
|
||||
//
|
||||
assert (this->pExpireTmr == 0);
|
||||
}
|
||||
}
|
||||
this->inProcess = false;
|
||||
|
||||
this->mutex.unlock();
|
||||
}
|
||||
|
||||
//
|
||||
// osiTimerQueue::show() const
|
||||
//
|
||||
void osiTimerQueue::show(unsigned level) const
|
||||
{
|
||||
epicsAutoMutex locker ( this->mutex );
|
||||
printf("osiTimerQueue with %u items pending and %u items expired\n",
|
||||
this->timerLists[osiTimer::statePending].count(),
|
||||
this->timerLists[osiTimer::stateExpired].count());
|
||||
tsDLIterConstBD <osiTimer> iter = this->timerLists[osiTimer::statePending].firstIter ();
|
||||
while ( iter.valid () ) {
|
||||
iter->show(level);
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" epicsShareFunc osiTimerQueueId epicsShareAPI osiTimerQueueCreate ( unsigned managerThreadPriority )
|
||||
{
|
||||
return (osiTimerQueueId) new osiTimerQueue ( osiTimerQueue::mtsCreateManagerThread, managerThreadPriority );
|
||||
}
|
||||
|
||||
class osiTimerForC : public osiTimer {
|
||||
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 osiTimerJumpTable &jt;
|
||||
void * pPrivate;
|
||||
public:
|
||||
osiTimerForC (const osiTimerJumpTable &jtIn, osiTimerQueue &queue, void *pPrivateIn);
|
||||
};
|
||||
|
||||
osiTimerForC::osiTimerForC (const osiTimerJumpTable &jtIn, osiTimerQueue &queue, void *pPrivateIn) :
|
||||
osiTimer (queue), jt (jtIn), pPrivate (pPrivateIn) {}
|
||||
|
||||
void osiTimerForC::expire ()
|
||||
{
|
||||
(*this->jt.expire) (this->pPrivate);
|
||||
}
|
||||
|
||||
void osiTimerForC::destroy ()
|
||||
{
|
||||
(*this->jt.destroy) (this->pPrivate);
|
||||
}
|
||||
|
||||
bool osiTimerForC::again () const
|
||||
{
|
||||
return (*this->jt.again) (this->pPrivate) ? true : false ;
|
||||
}
|
||||
|
||||
double osiTimerForC::delay () const
|
||||
{
|
||||
return (*this->jt.delay) (this->pPrivate);
|
||||
}
|
||||
|
||||
void osiTimerForC::show (unsigned level) const
|
||||
{
|
||||
(*this->jt.show) (this->pPrivate, level);
|
||||
}
|
||||
|
||||
extern "C" epicsShareFunc osiTimerId epicsShareAPI osiTimerCreate (const osiTimerJumpTable *pjtIn, osiTimerQueueId queueIdIn, void *pPrivateIn)
|
||||
{
|
||||
assert (pjtIn);
|
||||
assert (queueIdIn);
|
||||
return (osiTimerId) new osiTimerForC (*pjtIn, *static_cast<osiTimerQueue *>(queueIdIn), pPrivateIn);
|
||||
}
|
||||
|
||||
extern "C" epicsShareFunc void epicsShareAPI osiTimerArm (osiTimerId tmrIdIn, double delay)
|
||||
{
|
||||
osiTimerForC *pTmr = static_cast<osiTimerForC *>(tmrIdIn);
|
||||
assert (pTmr);
|
||||
pTmr->reschedule (delay);
|
||||
}
|
||||
|
||||
extern "C" epicsShareFunc void epicsShareAPI osiTimerCancel (osiTimerId tmrIdIn)
|
||||
{
|
||||
osiTimerForC *pTmr = static_cast<osiTimerForC *>(tmrIdIn);
|
||||
assert (pTmr);
|
||||
pTmr->cancel ();
|
||||
}
|
||||
|
||||
extern "C" epicsShareFunc double epicsShareAPI osiTimerTimeRemaining (osiTimerId idIn)
|
||||
{
|
||||
osiTimerForC *pTmr = static_cast<osiTimerForC *> (idIn);
|
||||
assert (pTmr);
|
||||
return pTmr->timeRemaining ();
|
||||
}
|
||||
|
||||
extern "C" epicsShareFunc epicsTimeStamp epicsShareAPI osiTimerExpirationDate (osiTimerId idIn)
|
||||
{
|
||||
osiTimerForC *pTmr = static_cast<osiTimerForC *> (idIn);
|
||||
assert (pTmr);
|
||||
return pTmr->expirationDate ();
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
/*
|
||||
* $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 osiTimerHInclude
|
||||
#define osiTimerHInclude
|
||||
|
||||
#include "shareLib.h" /* reset share lib defines */
|
||||
#include "epicsThread.h"
|
||||
#include "epicsTime.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include "epicsTime.h"
|
||||
#include "tsDLList.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsEvent.h"
|
||||
|
||||
class osiTimerQueue;
|
||||
|
||||
/*
|
||||
* osiTimer
|
||||
*/
|
||||
class osiTimer : public tsDLNode <osiTimer> {
|
||||
public:
|
||||
class noDelaySpecified {}; /* exception */
|
||||
class noMemory {}; /* exception */
|
||||
|
||||
/*
|
||||
* Create an active timer that will expire delay seconds after it is created
|
||||
* or create an inactive timer respectively.
|
||||
*
|
||||
* ** Warning ** default timer queue has no manager thread and is typically
|
||||
* processed by the file descriptor manager
|
||||
*/
|
||||
epicsShareFunc osiTimer ( double delay, osiTimerQueue & queueIn );
|
||||
epicsShareFunc osiTimer ( double delay );
|
||||
|
||||
/*
|
||||
* Create an inactive timer
|
||||
*
|
||||
* ** Warning ** default timer queue has no manager thread and is typically
|
||||
* processed by the file descriptor manager
|
||||
*/
|
||||
epicsShareFunc osiTimer ( osiTimerQueue & queueIn );
|
||||
epicsShareFunc osiTimer ();
|
||||
|
||||
/*
|
||||
* change the timers expiration to newDelay
|
||||
* seconds after when reschedule() is called
|
||||
*/
|
||||
epicsShareFunc void reschedule (double newDelay);
|
||||
|
||||
/*
|
||||
* change the timers expiration to this->delay()
|
||||
* seconds after when reschedule() is called
|
||||
*/
|
||||
epicsShareFunc void reschedule ();
|
||||
|
||||
/*
|
||||
* Start the timer with delay newDelay if inactive.
|
||||
*/
|
||||
epicsShareFunc void activate ( double newDelay );
|
||||
|
||||
/*
|
||||
* Start the timer with delay this->delay() if inactive.
|
||||
*/
|
||||
epicsShareFunc void activate ();
|
||||
|
||||
/*
|
||||
* inactivate the timer and call the virtual destroy()
|
||||
* member function
|
||||
*/
|
||||
epicsShareFunc void cancel ();
|
||||
|
||||
/*
|
||||
* return the number of seconds remaining before
|
||||
* this osiTimer will expire or the expiration date
|
||||
* respectively
|
||||
*/
|
||||
epicsShareFunc double timeRemaining () const; /* returns seconds, but inefficent */
|
||||
epicsShareFunc epicsTime expirationDate () const; /* efficent */
|
||||
|
||||
/*
|
||||
* called when the osiTimer expires
|
||||
*/
|
||||
epicsShareFunc virtual void expire () = 0;
|
||||
|
||||
/*
|
||||
* called if
|
||||
* 1) osiTimer exists and the osiTimerQueue is deleted
|
||||
* 2) when the osiTimer expires and again() returns false
|
||||
*
|
||||
* osiTimer::destroy() does a "delete this"
|
||||
*
|
||||
* if the derived class replaces this function then it
|
||||
* is taking responsibility for freeing (deleting)
|
||||
* timer resources when they are nolonger needed.
|
||||
*/
|
||||
epicsShareFunc virtual void destroy ();
|
||||
|
||||
/*
|
||||
* returning true indicates that the
|
||||
* osiTimer should be rearmed with delay
|
||||
* "delay()" when it expires
|
||||
*
|
||||
* the defaut osiTimer::again() returns false
|
||||
* (run the osiTimer once only)
|
||||
*/
|
||||
epicsShareFunc virtual bool again () const;
|
||||
|
||||
/*
|
||||
* returns the delay prior to expire
|
||||
* for subsequent iterations (if "again()"
|
||||
* returns true)
|
||||
*
|
||||
* the default osiTimer::delay() throws the
|
||||
* exception type noDelaySpecified, but it will
|
||||
* not be called unless the again() virtual
|
||||
* function returns true.
|
||||
*/
|
||||
epicsShareFunc virtual double delay () const;
|
||||
|
||||
epicsShareFunc virtual void show (unsigned level) const;
|
||||
|
||||
/*
|
||||
* for diagnostics
|
||||
*/
|
||||
epicsShareFunc virtual const char *name () const;
|
||||
|
||||
epicsShareFunc virtual ~osiTimer ();
|
||||
|
||||
private:
|
||||
friend class osiTimerQueue;
|
||||
enum state {statePending, stateExpired, stateIdle,
|
||||
numberOfTimerLists, stateLimbo};
|
||||
|
||||
epicsTime exp; /* experation time */
|
||||
state curState; /* current state */
|
||||
osiTimerQueue &queue; /* pointer to current timer queue */
|
||||
|
||||
/*
|
||||
* place osiTimer in the pending queue
|
||||
*/
|
||||
void arm (double initialDelay);
|
||||
};
|
||||
|
||||
/*
|
||||
* osiTimerQueue
|
||||
*/
|
||||
class osiTimerQueue {
|
||||
friend class osiTimer;
|
||||
friend class osiTimerThread;
|
||||
public:
|
||||
enum managerThreadSelect {
|
||||
mtsNoManagerThread, // you must call the process method (or the fdManager) to expire timers
|
||||
mtsCreateManagerThread // manager thread expires timers asnychronously
|
||||
};
|
||||
epicsShareFunc osiTimerQueue ( managerThreadSelect mts,
|
||||
unsigned managerThreadPriority = epicsThreadPriorityMin );
|
||||
epicsShareFunc virtual ~osiTimerQueue();
|
||||
epicsShareFunc double delayToFirstExpire () const; /* returns seconds */
|
||||
epicsShareFunc void process ();
|
||||
epicsShareFunc void show (unsigned level) const;
|
||||
private:
|
||||
mutable epicsMutex mutex;
|
||||
epicsEvent rescheduleEvent;
|
||||
epicsEvent exitEvent;
|
||||
tsDLList <osiTimer> timerLists [osiTimer::numberOfTimerLists];
|
||||
osiTimer *pExpireTmr;
|
||||
class osiTimerThread *pMgrThread;
|
||||
unsigned mgrThreadPriority;
|
||||
bool inProcess;
|
||||
bool terminateFlag;
|
||||
bool exitFlag;
|
||||
|
||||
void install (osiTimer &tmr, double delay);
|
||||
void privateProcess ();
|
||||
void terminateManagerThread ();
|
||||
};
|
||||
|
||||
/*
|
||||
* ** Warning **
|
||||
* the default timer queue has no manager thread and is typically
|
||||
* processed by the file descriptor manager
|
||||
*/
|
||||
epicsShareExtern osiTimerQueue osiDefaultTimerQueue;
|
||||
|
||||
inline epicsTime osiTimer::expirationDate () const
|
||||
{
|
||||
return this->exp;
|
||||
}
|
||||
|
||||
#endif /* ifdef __cplusplus */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* osiTimerJumpTable
|
||||
*/
|
||||
typedef struct osiTimerJumpTable {
|
||||
void (*expire) (void *pPrivate); /* called when timer expires */
|
||||
void (*destroy) (void *pPrivate); /* called after expire() when again() is false */
|
||||
int (*again) (void *pPrivate); /* rearm after expire when again() is true */
|
||||
double (*delay) (void *pPrivate); /* rearm delay */
|
||||
void (*show) (void *pPrivate, unsigned level); /* diagnostic */
|
||||
}osiTimerJumpTable;
|
||||
|
||||
typedef void * osiTimerQueueId;
|
||||
/* see epicsThread.h for the range of priorities allowed here */
|
||||
epicsShareFunc osiTimerQueueId epicsShareAPI osiTimerQueueCreate (unsigned managerThreadPriority);
|
||||
|
||||
typedef void * osiTimerId;
|
||||
epicsShareFunc osiTimerId epicsShareAPI osiTimerCreate (const osiTimerJumpTable *, osiTimerQueueId, void *pPrivate);
|
||||
epicsShareFunc void epicsShareAPI osiTimerArm (osiTimerId, double delay);
|
||||
epicsShareFunc void epicsShareAPI osiTimerCancel (osiTimerId);
|
||||
epicsShareFunc double epicsShareAPI osiTimerTimeRemaining (osiTimerId);
|
||||
epicsShareFunc epicsTimeStamp epicsShareAPI osiTimerExpirationDate (osiTimerId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* osiTimerHInclude */
|
||||
|
||||
@@ -37,8 +37,8 @@ epicsMutex timer::freeListMutex;
|
||||
|
||||
epicsTimer::~epicsTimer () {}
|
||||
|
||||
timer::timer ( epicsTimerNotify ¬ifyIn, timerQueue &queueIn ) :
|
||||
curState ( stateLimbo ), notify ( notifyIn ), queue ( queueIn )
|
||||
timer::timer ( timerQueue &queueIn ) :
|
||||
curState ( stateLimbo ), pNotify ( 0 ), queue ( queueIn )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -47,21 +47,21 @@ timer::~timer()
|
||||
this->cancel ();
|
||||
}
|
||||
|
||||
void timer::start ( double delaySeconds )
|
||||
void timer::start ( epicsTimerNotify & notify, double delaySeconds )
|
||||
{
|
||||
this->start ( epicsTime::getCurrent () + delaySeconds );
|
||||
this->start ( notify, epicsTime::getCurrent () + delaySeconds );
|
||||
}
|
||||
|
||||
void timer::start ( const epicsTime & expire )
|
||||
void timer::start ( epicsTimerNotify & notify, const epicsTime & expire )
|
||||
{
|
||||
epicsAutoMutex locker ( this->queue.mutex );
|
||||
this->privateStart ( expire );
|
||||
this->privateCancel ();
|
||||
this->privateStart ( notify, expire );
|
||||
}
|
||||
|
||||
void timer::privateStart ( const epicsTime & expire )
|
||||
void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire )
|
||||
{
|
||||
this->privateCancel ();
|
||||
|
||||
this->pNotify = & notify;
|
||||
this->exp = expire;
|
||||
|
||||
# ifdef DEBUG
|
||||
@@ -100,12 +100,12 @@ void timer::privateStart ( const epicsTime & expire )
|
||||
|
||||
this->curState = timer::statePending;
|
||||
|
||||
# ifdef DEBUG
|
||||
# if defined(DEBUG) && 0
|
||||
this->show ( 10u );
|
||||
this->queue.show ( 10u );
|
||||
# endif
|
||||
|
||||
debugPrintf ( ("Arm of \"%s\" with delay %f at %p preempting %u\n",
|
||||
debugPrintf ( ("Start of \"%s\" with delay %f at %p preempting %u\n",
|
||||
typeid ( this->notify ).name (),
|
||||
expire - epicsTime::getCurrent (),
|
||||
this, preemptCount ) );
|
||||
@@ -113,49 +113,68 @@ void timer::privateStart ( const epicsTime & expire )
|
||||
|
||||
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 ();
|
||||
}
|
||||
epicsAutoMutex locker ( this->queue.mutex );
|
||||
this->privateCancel ();
|
||||
}
|
||||
|
||||
void timer::privateCancel ()
|
||||
{
|
||||
if ( this->curState == statePending ) {
|
||||
this->queue.timerList.remove ( *this );
|
||||
this->curState = stateLimbo;
|
||||
while ( true ) {
|
||||
if ( this->curState == statePending ) {
|
||||
this->queue.timerList.remove ( *this );
|
||||
this->curState = stateLimbo;
|
||||
this->pNotify = 0;
|
||||
}
|
||||
// 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()
|
||||
{
|
||||
epicsAutoMutexRelease autoRelease ( this->queue.mutex );
|
||||
while ( this->queue.cancelPending ) {
|
||||
this->queue.cancelBlockingEvent.wait ();
|
||||
}
|
||||
// in case other threads are waiting
|
||||
this->queue.cancelBlockingEvent.signal ();
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double timer::getExpireDelay () const
|
||||
epicsTimer::expireInfo timer::getExpireInfo () const
|
||||
{
|
||||
epicsAutoMutex locker ( this->queue.mutex );
|
||||
return this->privateDelayToFirstExpire ();
|
||||
if ( this->curState == statePending || this->queue.pExpireTmr == this ) {
|
||||
return expireInfo ( true, this->exp );
|
||||
}
|
||||
else {
|
||||
return expireInfo ( false, epicsTime() );
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
epicsAutoMutex locker ( this->queue.mutex );
|
||||
const char * pName = "<no notify attached>";
|
||||
if ( this->pNotify ) {
|
||||
pName = typeid ( *this->pNotify ).name ();
|
||||
}
|
||||
double delay;
|
||||
if ( this->curState == statePending ) {
|
||||
delay = this->exp - epicsTime::getCurrent();
|
||||
}
|
||||
else {
|
||||
delay = -DBL_MAX;
|
||||
}
|
||||
printf ( "%s, state = %s, delay = %f\n",
|
||||
pName, this->curState == statePending ? "pending" : "limbo",
|
||||
delay );
|
||||
if ( level >= 1u ) {
|
||||
this->notify.show ( level - 1u );
|
||||
if ( level >= 1u && this->pNotify ) {
|
||||
this->pNotify->show ( level - 1u );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,11 +43,11 @@
|
||||
|
||||
class timer : public epicsTimer, public tsDLNode < timer > {
|
||||
public:
|
||||
timer ( epicsTimerNotify &, class timerQueue & );
|
||||
void start ( const epicsTime & );
|
||||
void start ( double delaySeconds );
|
||||
timer ( class timerQueue & );
|
||||
void start ( class epicsTimerNotify &, const epicsTime & );
|
||||
void start ( class epicsTimerNotify &, double delaySeconds );
|
||||
void cancel ();
|
||||
double getExpireDelay () const;
|
||||
expireInfo getExpireInfo () const;
|
||||
void show ( unsigned int level ) const;
|
||||
void * operator new ( size_t size );
|
||||
void operator delete ( void *pCadaver, size_t size );
|
||||
@@ -58,11 +58,10 @@ private:
|
||||
enum state { statePending = 45, stateLimbo = 78 };
|
||||
epicsTime exp; // experation time
|
||||
state curState; // current state
|
||||
epicsTimerNotify ¬ify; // callback
|
||||
epicsTimerNotify *pNotify; // callback
|
||||
timerQueue &queue;
|
||||
void privateStart ( const epicsTime & );
|
||||
void privateStart ( epicsTimerNotify & notify, const epicsTime & );
|
||||
void privateCancel ();
|
||||
double privateDelayToFirstExpire () const;
|
||||
static tsFreeList < class timer, 0x20 > freeList;
|
||||
static epicsMutex freeListMutex;
|
||||
friend class timerQueue;
|
||||
@@ -72,8 +71,7 @@ class timerQueue {
|
||||
public:
|
||||
timerQueue ( epicsTimerQueueNotify ¬ify );
|
||||
~timerQueue ();
|
||||
void process ();
|
||||
double delayToFirstExpire () const;
|
||||
double process ( const epicsTime & currentTime );
|
||||
void show ( unsigned int level ) const;
|
||||
private:
|
||||
mutable epicsMutex mutex;
|
||||
@@ -102,7 +100,7 @@ class timerQueueActive : public epicsTimerQueueActive,
|
||||
public:
|
||||
timerQueueActive ( bool okToShare, unsigned priority );
|
||||
~timerQueueActive () = 0;
|
||||
epicsTimer & createTimer ( epicsTimerNotify & );
|
||||
epicsTimer & createTimer ();
|
||||
void show ( unsigned int level ) const;
|
||||
bool sharingOK () const;
|
||||
int threadPriority () const;
|
||||
@@ -149,9 +147,8 @@ extern timerQueueActiveMgr queueMgr;
|
||||
class timerQueuePassive : public epicsTimerQueuePassive {
|
||||
public:
|
||||
timerQueuePassive ( epicsTimerQueueNotify & );
|
||||
epicsTimer & createTimer ( epicsTimerNotify & );
|
||||
void process ();
|
||||
double getNextExpireDelay () const;
|
||||
epicsTimer & createTimer ();
|
||||
double process ( const epicsTime & currentTime );
|
||||
void show ( unsigned int level ) const;
|
||||
void release ();
|
||||
timerQueue & getTimerQueue ();
|
||||
@@ -177,20 +174,6 @@ inline void timer::operator delete ( void *pCadaver, size_t size )
|
||||
timer::freeList.release ( pCadaver, size );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool timerQueueActive::sharingOK () const
|
||||
{
|
||||
return this->okToShare;
|
||||
|
||||
@@ -48,39 +48,35 @@ timerQueue::~timerQueue ()
|
||||
}
|
||||
}
|
||||
|
||||
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 () );
|
||||
|
||||
double timerQueue::process ( const epicsTime & currentTime )
|
||||
{
|
||||
{
|
||||
epicsAutoMutex locker ( this->mutex );
|
||||
|
||||
if ( this->pExpireTmr ) {
|
||||
// if some other thread is processing the queue
|
||||
// (or if this is a recursive call)
|
||||
return;
|
||||
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 epired tmr so that we can detect if call back
|
||||
// is in progress when canceling the timer.
|
||||
//
|
||||
this->pExpireTmr = this->timerList.first ();
|
||||
if ( this->pExpireTmr ) {
|
||||
if ( cur >= this->pExpireTmr->exp ) {
|
||||
if ( currentTime >= this->pExpireTmr->exp ) {
|
||||
this->timerList.remove ( *this->pExpireTmr );
|
||||
//
|
||||
// Tag current epired tmr so that we can detect if call back
|
||||
// is in progress when canceling the timer.
|
||||
//
|
||||
this->pExpireTmr->curState = timer::stateLimbo;
|
||||
this->processThread = epicsThreadGetIdSelf ();
|
||||
# ifdef DEBUG
|
||||
@@ -88,13 +84,14 @@ void timerQueue::process ()
|
||||
# endif
|
||||
}
|
||||
else {
|
||||
double delay = this->pExpireTmr->exp - currentTime;
|
||||
this->pExpireTmr = 0;
|
||||
debugPrintf ( ( "no activity process %f to next\n", this->pExpireTmr->exp - cur ) );
|
||||
return;
|
||||
debugPrintf ( ( "no activity process %f to next\n", delay ) );
|
||||
return delay;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return;
|
||||
return DBL_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,9 +103,10 @@ void timerQueue::process ()
|
||||
|
||||
debugPrintf ( ( "%5u expired \"%s\" with error %f sec\n",
|
||||
N++, typeid ( this->pExpireTmr->notify ).name (),
|
||||
cur - this->pExpireTmr->exp ) );
|
||||
currentTime - this->pExpireTmr->exp ) );
|
||||
|
||||
epicsTimerNotify::expireStatus expStat = this->pExpireTmr->notify.expire ();
|
||||
epicsTimerNotify::expireStatus expStat =
|
||||
this->pExpireTmr->pNotify->expire ( currentTime );
|
||||
|
||||
epicsAutoMutex locker ( this->mutex );
|
||||
|
||||
@@ -120,15 +118,21 @@ void timerQueue::process ()
|
||||
// cancel() waits for this
|
||||
this->cancelPending = false;
|
||||
this->cancelBlockingEvent.signal ();
|
||||
this->pExpireTmr->pNotify = 0;
|
||||
}
|
||||
// restart as nec
|
||||
else if ( expStat.restart() ) {
|
||||
this->pExpireTmr->privateStart ( cur + expStat.expirationDelay() );
|
||||
this->pExpireTmr->privateStart (
|
||||
*this->pExpireTmr->pNotify,
|
||||
currentTime + expStat.expirationDelay() );
|
||||
}
|
||||
else {
|
||||
this->pExpireTmr->pNotify = 0;
|
||||
}
|
||||
|
||||
this->pExpireTmr = this->timerList.first ();
|
||||
if ( this->pExpireTmr ) {
|
||||
if ( cur >= this->pExpireTmr->exp ) {
|
||||
if ( currentTime >= this->pExpireTmr->exp ) {
|
||||
this->timerList.remove ( *this->pExpireTmr );
|
||||
this->pExpireTmr->curState = timer::stateLimbo;
|
||||
# ifdef DEBUG
|
||||
@@ -136,14 +140,15 @@ void timerQueue::process ()
|
||||
# endif
|
||||
}
|
||||
else {
|
||||
double delay = this->pExpireTmr->exp - currentTime;
|
||||
this->pExpireTmr = 0;
|
||||
this->processThread = 0;
|
||||
return;
|
||||
return delay;
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->processThread = 0;
|
||||
return;
|
||||
return DBL_MAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,22 +63,17 @@ void timerQueueActive::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 );
|
||||
}
|
||||
double delay = this->queue.process ( epicsTime::getCurrent() );
|
||||
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 & timerQueueActive::createTimer ( epicsTimerNotify & notify )
|
||||
epicsTimer & timerQueueActive::createTimer ()
|
||||
{
|
||||
timer *pTmr = new timer ( notify, this->queue );
|
||||
timer *pTmr = new timer ( this->queue );
|
||||
if ( ! pTmr ) {
|
||||
throwWithLocation ( timer::noMemory () );
|
||||
}
|
||||
|
||||
@@ -50,23 +50,18 @@ timerQueuePassive::timerQueuePassive ( epicsTimerQueueNotify ¬ifyIn ) :
|
||||
|
||||
timerQueuePassive::~timerQueuePassive () {}
|
||||
|
||||
epicsTimer & timerQueuePassive::createTimer ( epicsTimerNotify & notifyIn )
|
||||
epicsTimer & timerQueuePassive::createTimer ()
|
||||
{
|
||||
timer *pTmr = new timer ( notifyIn, this->queue );
|
||||
timer *pTmr = new timer ( this->queue );
|
||||
if ( ! pTmr ) {
|
||||
throwWithLocation ( timer::noMemory () );
|
||||
}
|
||||
return *pTmr;
|
||||
}
|
||||
|
||||
void timerQueuePassive::process ()
|
||||
double timerQueuePassive::process ( const epicsTime & currentTime )
|
||||
{
|
||||
this->queue.process ();
|
||||
}
|
||||
|
||||
double timerQueuePassive::getNextExpireDelay () const
|
||||
{
|
||||
return this->queue.delayToFirstExpire ();
|
||||
return this->queue.process ( currentTime );
|
||||
}
|
||||
|
||||
void timerQueuePassive::show ( unsigned int level ) const
|
||||
|
||||
Reference in New Issue
Block a user