This is Unofficial EPICS BASE Doxygen Site
timerQueue.cpp
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2002 The University of Chicago, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2002 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * EPICS BASE is distributed subject to a Software License Agreement found
7 * in file LICENSE that is included with this distribution.
8 \*************************************************************************/
9 /*
10  * Author Jeffrey O. Hill
11  * johill@lanl.gov
12  * 505 665 1831
13  */
14 
15 #include <stdio.h>
16 #include <float.h>
17 
18 #include "epicsGuard.h"
19 #include "timerPrivate.h"
20 #include "errlog.h"
21 
22 const double timerQueue :: exceptMsgMinPeriod = 60.0 * 5.0; // seconds
23 
24 epicsTimerQueue::~epicsTimerQueue () {}
25 
26 timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) :
27  mutex(__FILE__, __LINE__),
28  notify ( notifyIn ),
29  pExpireTmr ( 0 ),
30  processThread ( 0 ),
31  exceptMsgTimeStamp (
32  epicsTime :: getCurrent () - exceptMsgMinPeriod ),
33  cancelPending ( false )
34 {
35 }
36 
38 {
39  timer *pTmr;
40  while ( ( pTmr = this->timerList.get () ) ) {
41  pTmr->curState = timer::stateLimbo;
42  }
43 }
44 
45 void timerQueue ::
46  printExceptMsg ( const char * pName, const type_info & type )
47 {
48  char date[64];
49  double delay;
50  try {
51  epicsTime cur = epicsTime :: getCurrent ();
52  delay = cur - this->exceptMsgTimeStamp;
53  cur.strftime ( date, sizeof ( date ),
54  "%a %b %d %Y %H:%M:%S.%f" );
55  if ( delay >= exceptMsgMinPeriod ) {
56  this->exceptMsgTimeStamp = cur;
57  }
58  }
59  catch ( ... ) {
60  delay = DBL_MAX;
61  strcpy ( date, "UKN DATE" );
62  }
63  if ( delay >= exceptMsgMinPeriod ) {
64  // we dont touch the typeid for the timer expiration
65  // notify interface here because they might have
66  // destroyed the timer during its callback
67  errlogPrintf (
68  "timerQueue: Unexpected C++ exception \"%s\" "
69  "with type \"%s\" during timer expiration "
70  "callback at %s\n",
71  pName,
72  type.name (),
73  date );
74  errlogFlush ();
75  }
76 }
77 
78 double timerQueue::process ( const epicsTime & currentTime )
79 {
80  epicsGuard < epicsMutex > guard ( this->mutex );
81 
82  if ( this->pExpireTmr ) {
83  // if some other thread is processing the queue
84  // (or if this is a recursive call)
85  timer * pTmr = this->timerList.first ();
86  if ( pTmr ) {
87  double delay = pTmr->exp - currentTime;
88  if ( delay < 0.0 ) {
89  delay = 0.0;
90  }
91  return delay;
92  }
93  else {
94  return DBL_MAX;
95  }
96  }
97 
98  //
99  // Tag current epired tmr so that we can detect if call back
100  // is in progress when canceling the timer.
101  //
102  if ( this->timerList.first () ) {
103  if ( currentTime >= this->timerList.first ()->exp ) {
104  this->pExpireTmr = this->timerList.first ();
105  this->timerList.remove ( *this->pExpireTmr );
106  this->pExpireTmr->curState = timer::stateActive;
107  this->processThread = epicsThreadGetIdSelf ();
108 # ifdef DEBUG
109  this->pExpireTmr->show ( 0u );
110 # endif
111  }
112  else {
113  double delay = this->timerList.first ()->exp - currentTime;
114  debugPrintf ( ( "no activity process %f to next\n", delay ) );
115  return delay;
116  }
117  }
118  else {
119  return DBL_MAX;
120  }
121 
122 # ifdef DEBUG
123  unsigned N = 0u;
124 # endif
125 
126  double delay = DBL_MAX;
127  while ( true ) {
128  epicsTimerNotify *pTmpNotify = this->pExpireTmr->pNotify;
129  this->pExpireTmr->pNotify = 0;
130  epicsTimerNotify::expireStatus expStat ( epicsTimerNotify::noRestart );
131 
132  {
133  epicsGuardRelease < epicsMutex > unguard ( guard );
134 
135  debugPrintf ( ( "%5u expired \"%s\" with error %f sec\n",
136  N++, typeid ( this->pExpireTmr->notify ).name (),
137  currentTime - this->pExpireTmr->exp ) );
138  try {
139  expStat = pTmpNotify->expire ( currentTime );
140  }
141  catch ( std::exception & except ) {
142  printExceptMsg ( except.what (), typeid ( except ) );
143  }
144  catch ( ... ) {
145  printExceptMsg ( "non-standard exception", typeid ( void ) );
146  }
147  }
148 
149  //
150  // only restart if they didnt cancel() the timer
151  // while the call back was running
152  //
153  if ( this->cancelPending ) {
154  // 1) if another thread is canceling then cancel() waits for
155  // the event below
156  // 2) if this thread is canceling in the timer callback then
157  // dont touch timer or notify here because the cancel might
158  // have occurred because they destroyed the timer in the
159  // callback
160  this->cancelPending = false;
161  this->cancelBlockingEvent.signal ();
162  }
163  else {
164  this->pExpireTmr->curState = timer::stateLimbo;
165  if ( this->pExpireTmr->pNotify ) {
166  // pNotify was cleared above so if it is valid now we know that
167  // someone has started the timer from another thread and that
168  // predominates over the restart parameters from expire.
169  this->pExpireTmr->privateStart (
170  *this->pExpireTmr->pNotify, this->pExpireTmr->exp );
171  }
172  else if ( expStat.restart() ) {
173  // restart as nec
174  this->pExpireTmr->privateStart (
175  *pTmpNotify, currentTime + expStat.expirationDelay() );
176  }
177  }
178  this->pExpireTmr = 0;
179 
180  if ( this->timerList.first () ) {
181  if ( currentTime >= this->timerList.first ()->exp ) {
182  this->pExpireTmr = this->timerList.first ();
183  this->timerList.remove ( *this->pExpireTmr );
184  this->pExpireTmr->curState = timer::stateActive;
185 # ifdef DEBUG
186  this->pExpireTmr->show ( 0u );
187 # endif
188  }
189  else {
190  delay = this->timerList.first ()->exp - currentTime;
191  this->processThread = 0;
192  break;
193  }
194  }
195  else {
196  this->processThread = 0;
197  delay = DBL_MAX;
198  break;
199  }
200  }
201  return delay;
202 }
203 
204 epicsTimer & timerQueue::createTimer ()
205 {
206  return * new ( this->timerFreeList ) timer ( * this );
207 }
208 
210 {
211  return * new ( this->timerForCFreeList ) epicsTimerForC ( *this, pCallback, pArg );
212 }
213 
214 void timerQueue::show ( unsigned level ) const
215 {
216  epicsGuard < epicsMutex > locker ( this->mutex );
217  printf ( "epicsTimerQueue with %u items pending\n", this->timerList.count () );
218  if ( level >= 1u ) {
219  tsDLIterConst < timer > iter = this->timerList.firstIter ();
220  while ( iter.valid () ) {
221  iter->show ( level - 1u );
222  ++iter;
223  }
224  }
225 }
void errlogFlush(void)
Definition: errlog.c:529
void show(unsigned int level) const
Definition: timer.cpp:186
void show(unsigned int level) const
Definition: timerQueue.cpp:214
tsDLIterConst< T > firstIter() const
Definition: tsDLList.h:459
#define printf
Definition: epicsStdio.h:41
pvd::StructureConstPtr type
epicsTimer & createTimer()
Definition: timerQueue.cpp:204
unsigned count() const
Definition: tsDLList.h:181
bool valid() const
Definition: tsDLList.h:506
epicsTimerForC & createTimerForC(epicsTimerCallback pCallback, void *pArg)
Definition: timerQueue.cpp:209
void(* epicsTimerCallback)(void *pPrivate)
Definition: epicsTimer.h:134
epicsMutex mutex
Definition: pvAccess.cpp:71
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
timerQueue(epicsTimerQueueNotify &notify)
Definition: timerQueue.cpp:26
void date(const char *format)
T * get()
Definition: tsDLList.h:261
virtual ~timerQueue()
Definition: timerQueue.cpp:37
void remove(T &item)
Definition: tsDLList.h:219
#define debugPrintf(argsInParen)
Definition: iocinf.h:30
double process(const epicsTime &currentTime)
Definition: timerQueue.cpp:78
#define false
Definition: flexdef.h:85
T * first(void) const
Definition: tsDLList.h:190
LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetIdSelf(void)
Definition: osdThread.c:810