382 lines
7.4 KiB
C++
382 lines
7.4 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
|
|
*
|
|
*/
|
|
|
|
//
|
|
// NOTES:
|
|
// 1) this should use a binary tree to speed up inserts?
|
|
// 2) when making this safe for multi threading consider the possibility of
|
|
// object delete just after finding an active fdRegI on
|
|
// the list but before callBack().
|
|
//
|
|
//
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
|
|
#define epicsExportSharedSymbols
|
|
#include "osiTimer.h"
|
|
|
|
osiTimerQueue osiDefaultTimerQueue;
|
|
|
|
//
|
|
// osiTimer::arm()
|
|
//
|
|
epicsShareFunc void osiTimer::arm (double *pInitialDelay)
|
|
{
|
|
# ifdef DEBUG
|
|
unsigned preemptCount=0u;
|
|
# endif
|
|
|
|
//
|
|
// calculate absolute expiration time
|
|
// (dont call base's delay() virtual func
|
|
// in the constructor)
|
|
//
|
|
if (pInitialDelay) {
|
|
this->exp = osiTime::getCurrent() + *pInitialDelay;
|
|
}
|
|
else {
|
|
this->exp = osiTime::getCurrent() + this->delay();
|
|
}
|
|
|
|
//
|
|
// 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.pending.last();
|
|
while (1) {
|
|
if (iter==tsDLIterBD<osiTimer>::eol()) {
|
|
//
|
|
// add to the beginning of the list
|
|
//
|
|
this->queue.pending.push (*this);
|
|
break;
|
|
}
|
|
if (iter->exp <= this->exp) {
|
|
//
|
|
// add after the item found that expires earlier
|
|
//
|
|
this->queue.pending.insertAfter (*this, *iter);
|
|
break;
|
|
}
|
|
# ifdef DEBUG
|
|
preemptCount++;
|
|
# endif
|
|
--iter;
|
|
}
|
|
this->state = osiTimer::statePending;
|
|
|
|
# ifdef DEBUG
|
|
this->queue.show(10u);
|
|
# endif
|
|
|
|
# ifdef DEBUG
|
|
double theDelay;
|
|
if (pInitialDelay) {
|
|
theDelay = *pInitialDelay;
|
|
}
|
|
else {
|
|
theDelay = this->delay();
|
|
}
|
|
//
|
|
// 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(), theDelay, (unsigned long)this, preemptCount);
|
|
# endif
|
|
|
|
}
|
|
|
|
//
|
|
// osiTimer::~osiTimer()
|
|
//
|
|
epicsShareFunc osiTimer::~osiTimer()
|
|
{
|
|
//
|
|
// signal the timer queue if this
|
|
// was deleted during its expire call
|
|
// back
|
|
//
|
|
if (this == this->queue.pExpireTmr) {
|
|
this->queue.pExpireTmr = 0;
|
|
}
|
|
switch (this->state) {
|
|
case osiTimer::statePending:
|
|
this->queue.pending.remove(*this);
|
|
break;
|
|
case osiTimer::stateExpired:
|
|
this->queue.expired.remove(*this);
|
|
break;
|
|
case osiTimer::stateLimbo:
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
this->state = osiTimer::stateLimbo;
|
|
}
|
|
|
|
//
|
|
// osiTimer::again()
|
|
//
|
|
epicsShareFunc bool osiTimer::again() const
|
|
{
|
|
//
|
|
// default is to run the timer only once
|
|
//
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// osiTimer::destroy()
|
|
//
|
|
epicsShareFunc void osiTimer::destroy()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
//
|
|
// osiTimer::delay()
|
|
//
|
|
epicsShareFunc double osiTimer::delay() const
|
|
{
|
|
throw noDelaySpecified();
|
|
}
|
|
|
|
epicsShareFunc void osiTimer::show (unsigned level) const
|
|
{
|
|
osiTime cur(osiTime::getCurrent());
|
|
double delay;
|
|
|
|
printf ("osiTimer at %p for \"%s\" with again = %d\n",
|
|
this, this->name(), this->again());
|
|
if (this->exp >= cur) {
|
|
delay = this->exp - cur;
|
|
}
|
|
else {
|
|
delay = cur - this->exp;
|
|
delay = -delay;
|
|
}
|
|
if (level>=1u) {
|
|
printf ("\tdelay to expire = %f, state = %d\n",
|
|
delay, this->state);
|
|
}
|
|
}
|
|
|
|
//
|
|
// osiTimerQueue::delayToFirstExpire()
|
|
//
|
|
double osiTimerQueue::delayToFirstExpire() const
|
|
{
|
|
osiTimer *pTmr;
|
|
double delay;
|
|
|
|
pTmr = this->pending.first();
|
|
if (pTmr) {
|
|
delay = pTmr->timeRemaining();
|
|
}
|
|
else {
|
|
//
|
|
// no timer in the queue - return a long delay - 30 min
|
|
//
|
|
delay = 30u * osiTime::secPerMin;
|
|
}
|
|
#ifdef DEBUG
|
|
printf ("delay to first item on the queue %f\n", (double) delay);
|
|
#endif
|
|
return delay; // seconds
|
|
}
|
|
|
|
//
|
|
// osiTimerQueue::process()
|
|
//
|
|
void osiTimerQueue::process()
|
|
{
|
|
osiTime cur(osiTime::getCurrent());
|
|
osiTimer *pTmr;
|
|
|
|
// no recursion
|
|
if (this->inProcess) {
|
|
return;
|
|
}
|
|
this->inProcess = true;
|
|
|
|
tsDLIterBD<osiTimer> iter = this->pending.first();
|
|
while ( iter!=tsDLIterBD<osiTimer>::eol() ) {
|
|
if (iter->exp >= cur) {
|
|
break;
|
|
}
|
|
tsDLIterBD<osiTimer> tmp = iter;
|
|
++tmp;
|
|
this->pending.remove(*iter);
|
|
iter->state = osiTimer::stateExpired;
|
|
this->expired.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->expired.get()) ) {
|
|
|
|
pTmr->state = osiTimer::stateLimbo;
|
|
|
|
#ifdef DEBUG
|
|
double diff = cur-pTmr->exp;
|
|
printf ("expired %lx for \"%s\" with error %f\n",
|
|
(unsigned long)pTmr, pTmr->name(), diff);
|
|
#endif
|
|
|
|
//
|
|
// Tag current tmr so that we
|
|
// can detect if it was deleted
|
|
// during the expire call back
|
|
//
|
|
this->pExpireTmr = pTmr;
|
|
pTmr->expire();
|
|
if (this->pExpireTmr == pTmr) {
|
|
if (pTmr->again()) {
|
|
pTmr->arm();
|
|
}
|
|
else {
|
|
pTmr->destroy();
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// no recursive calls to process allowed
|
|
//
|
|
assert(this->pExpireTmr == 0);
|
|
}
|
|
}
|
|
this->inProcess = false;
|
|
}
|
|
|
|
//
|
|
// osiTimerQueue::show() const
|
|
//
|
|
void osiTimerQueue::show(unsigned level) const
|
|
{
|
|
printf("osiTimerQueue with %u items pending and %u items expired\n",
|
|
this->pending.count(), this->expired.count());
|
|
tsDLIterBD<osiTimer> iter(this->pending.first());
|
|
while ( iter!=tsDLIterBD<osiTimer>::eol() ) {
|
|
iter->show(level);
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
//
|
|
// osiTimerQueue::~osiTimerQueue()
|
|
//
|
|
osiTimerQueue::~osiTimerQueue()
|
|
{
|
|
osiTimer *pTmr;
|
|
|
|
//
|
|
// destroy any unexpired timers
|
|
//
|
|
while ( (pTmr = this->pending.get()) ) {
|
|
pTmr->state = osiTimer::stateLimbo;
|
|
pTmr->destroy();
|
|
}
|
|
|
|
//
|
|
// destroy any expired timers
|
|
//
|
|
while ( (pTmr = this->expired.get()) ) {
|
|
pTmr->state = osiTimer::stateLimbo;
|
|
pTmr->destroy();
|
|
}
|
|
}
|
|
|
|
//
|
|
// osiTimer::name()
|
|
// virtual base default
|
|
//
|
|
epicsShareFunc const char *osiTimer::name() const
|
|
{
|
|
return "osiTimer";
|
|
}
|
|
|
|
//
|
|
// osiTimer::reschedule()
|
|
//
|
|
// pull this timer out of the queue ans reinstall
|
|
// it with a new experation time
|
|
//
|
|
epicsShareFunc void osiTimer::reschedule (double newDelay)
|
|
{
|
|
//
|
|
// signal the timer queue if this
|
|
// occurrring during the expire call
|
|
// back
|
|
//
|
|
if (this == this->queue.pExpireTmr) {
|
|
this->queue.pExpireTmr = 0;
|
|
}
|
|
switch (this->state) {
|
|
case osiTimer::statePending:
|
|
this->queue.pending.remove(*this);
|
|
break;
|
|
case osiTimer::stateExpired:
|
|
this->queue.expired.remove(*this);
|
|
break;
|
|
case osiTimer::stateLimbo:
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
this->state = osiTimer::stateLimbo;
|
|
this->arm (&newDelay);
|
|
}
|
|
|
|
//
|
|
// osiTimer::timeRemaining()
|
|
//
|
|
// return the number of seconds remaining before
|
|
// this timer will expire
|
|
//
|
|
epicsShareFunc double osiTimer::timeRemaining ()
|
|
{
|
|
double remaining = this->exp - osiTime::getCurrent();
|
|
if (remaining>0.0) {
|
|
return remaining;
|
|
}
|
|
else {
|
|
return 0.0;
|
|
}
|
|
} |