Files
epics-base/modules/libcom/src/timer/timer.cpp
Andrew Johnson 3c99391d93 Added SPDX License ID to all EPICS-original source files
In some cases the license-identification header was missing,
so I added that as well. Replaced the remaining headers that
specifically identified "Versions 3.13.7 and higher".

Makefiles and the build system were deliberately excluded.
2020-08-03 11:53:01 -05:00

243 lines
7.1 KiB
C++

/*************************************************************************\
* 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 <typeinfo>
#include <string>
#include <stdexcept>
#include <stdio.h>
#include "epicsGuard.h"
#include "timerPrivate.h"
#include "errlog.h"
#ifdef _MSC_VER
# pragma warning ( push )
# pragma warning ( disable:4660 )
#endif
template class tsFreeList < timer, 0x20 >;
#ifdef _MSC_VER
# pragma warning ( pop )
#endif
timer::timer ( timerQueue & queueIn ) :
queue ( queueIn ), curState ( stateLimbo ), pNotify ( 0 )
{
}
timer::~timer ()
{
this->cancel ();
}
void timer::destroy ()
{
timerQueue & queueTmp = this->queue;
this->~timer ();
queueTmp.timerFreeList.release ( 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->privateStart ( notify, expire );
}
void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire )
{
this->pNotify = & notify;
this->exp = expire - ( this->queue.notify.quantum () / 2.0 );
bool reschedualNeeded = false;
if ( this->curState == stateActive ) {
// above expire time and notify will override any restart parameters
// that may be returned from the timer expire callback
return;
}
else if ( this->curState == statePending ) {
this->queue.timerList.remove ( *this );
if ( this->queue.timerList.first() == this &&
this->queue.timerList.count() > 0 ) {
reschedualNeeded = true;
}
}
# 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 ????
//
tsDLIter < timer > pTmr = this->queue.timerList.lastIter ();
while ( true ) {
if ( ! pTmr.valid () ) {
//
// add to the beginning of the list
//
this->queue.timerList.push ( *this );
reschedualNeeded = true;
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 ( reschedualNeeded ) {
this->queue.notify.reschedule ();
}
# 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 ()
{
bool reschedual = false;
bool wakeupCancelBlockingThreads = false;
{
epicsGuard < epicsMutex > locker ( this->queue.mutex );
this->pNotify = 0;
if ( this->curState == statePending ) {
this->queue.timerList.remove ( *this );
this->curState = stateLimbo;
if ( this->queue.timerList.first() == this &&
this->queue.timerList.count() > 0 ) {
reschedual = true;
}
}
else if ( this->curState == stateActive ) {
this->queue.cancelPending = true;
this->curState = timer::stateLimbo;
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()
while ( this->queue.cancelPending &&
this->queue.pExpireTmr == this ) {
epicsGuardRelease < epicsMutex > autoRelease ( locker );
this->queue.cancelBlockingEvent.wait ();
}
// in case other threads are waiting
wakeupCancelBlockingThreads = true;
}
}
}
if ( reschedual ) {
this->queue.notify.reschedule ();
}
if ( wakeupCancelBlockingThreads ) {
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 );
double delay;
if ( this->curState == statePending || this->curState == stateActive ) {
try {
delay = this->exp - epicsTime::getCurrent();
}
catch ( ... ) {
delay = - DBL_MAX;
}
}
else {
delay = -DBL_MAX;
}
const char *pStateName;
if ( this->curState == statePending ) {
pStateName = "pending";
}
else if ( this->curState == stateActive ) {
pStateName = "active";
}
else if ( this->curState == stateLimbo ) {
pStateName = "limbo";
}
else {
pStateName = "corrupt";
}
printf ( "timer, state = %s, delay = %f\n",
pStateName, delay );
if ( level >= 1u && this->pNotify ) {
this->pNotify->show ( level - 1u );
}
}
void timer::operator delete ( void * )
{
// Visual C++ .net appears to require operator delete if
// placement operator delete is defined? I smell a ms rat
// because if I declare placement new and delete, but
// comment out the placement delete definition there are
// no undefined symbols.
errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked",
__FILE__, __LINE__ );
}
void epicsTimerForC::operator delete ( void * )
{
// Visual C++ .net appears to require operator delete if
// placement operator delete is defined? I smell a ms rat
// because if I declare placement new and delete, but
// comment out the placement delete definition there are
// no undefined symbols.
errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked",
__FILE__, __LINE__ );
}