Files
pcas/src/libCom/timer/timer.cpp
2002-06-25 15:11:52 +00:00

219 lines
5.9 KiB
C++

/*
* $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 <typeinfo>
#define epicsExportSharedSymbols
#include "epicsGuard.h"
#include "timerPrivate.h"
#ifdef _MSC_VER
# pragma warning ( push )
# pragma warning ( disable:4660 )
#endif
template class tsFreeList < timer, 0x20 >;
template class epicsSingleton < tsFreeList < timer, 0x20 > >;
#ifdef _MSC_VER
# pragma warning ( pop )
#endif
epicsSingleton < tsFreeList < timer, 0x20 > > timer::pFreeList;
timer::timer ( timerQueue &queueIn ) :
curState ( stateLimbo ), pNotify ( 0 ), queue ( queueIn )
{
}
timer::~timer ()
{
this->cancel ();
}
void timer::destroy ()
{
delete this;
}
void timer::start ( epicsTimerNotify & notify, double delaySeconds )
{
this->start ( notify, epicsTime::getCurrent () + delaySeconds );
}
void timer::start ( epicsTimerNotify & notify, const epicsTime & expire )
{
epicsGuard < epicsMutex > locker ( this->queue.mutex );
this->privateCancel ( locker );
this->privateStart ( notify, expire );
}
void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire )
{
this->pNotify = & notify;
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;
# if defined(DEBUG) && 0
this->show ( 10u );
this->queue.show ( 10u );
# endif
debugPrintf ( ("Start of \"%s\" with delay %f at %p preempting %u\n",
typeid ( this->notify ).name (),
expire - epicsTime::getCurrent (),
this, preemptCount ) );
}
void timer::cancel ()
{
if ( this->curState == statePending || this->curState == stateActive ) {
epicsGuard < epicsMutex > locker ( this->queue.mutex );
this->privateCancel ( locker );
}
}
void timer::privateCancel ( epicsGuard < epicsMutex > & locker )
{
if ( this->curState == statePending ) {
if ( this->queue.timerList.first() == this ) {
this->queue.notify.reschedule ();
}
this->queue.timerList.remove ( *this );
this->curState = stateLimbo;
this->pNotify = 0;
}
else if ( this->curState == stateActive ) {
this->queue.cancelPending = true;
if ( this->queue.processThread != epicsThreadGetIdSelf() ) {
// make certain timer expire() does not run after cancel () returns,
// but dont require that lock is applied while calling expire()
{
epicsGuardRelease < epicsMutex > autoRelease ( locker );
while ( this->queue.cancelPending &&
this->queue.pExpireTmr == this ) {
this->queue.cancelBlockingEvent.wait ();
}
}
// in case other threads are waiting
this->queue.cancelBlockingEvent.signal ();
}
}
}
epicsTimer::expireInfo timer::getExpireInfo () const
{
// taking a lock here guarantees that users will not
// see brief intervals when a timer isnt active because
// it is is canceled when start is called
epicsGuard < epicsMutex > locker ( this->queue.mutex );
if ( this->curState == statePending || this->curState == stateActive ) {
return expireInfo ( true, this->exp );
}
return expireInfo ( false, epicsTime() );
}
void timer::show ( unsigned int level ) const
{
epicsGuard < epicsMutex > locker ( this->queue.mutex );
const char * pName = "<no notify attached>";
const char *pStateName;
if ( this->pNotify ) {
pName = typeid ( *this->pNotify ).name ();
}
double delay;
if ( this->curState == statePending || this->curState == stateActive ) {
delay = this->exp - epicsTime::getCurrent();
}
else {
delay = -DBL_MAX;
}
if ( this->curState == statePending ) {
pStateName = "pending";
}
else if ( this->curState == stateActive ) {
pStateName = "active";
}
else if ( this->curState == stateLimbo ) {
pStateName = "limbo";
}
else {
pStateName = "corrupt";
}
printf ( "%s, state = %s, delay = %f\n",
pName, pStateName, delay );
if ( level >= 1u && this->pNotify ) {
this->pNotify->show ( level - 1u );
}
}
timerQueue & timer::getPrivTimerQueue()
{
return this->queue;
}