This is Unofficial EPICS BASE Doxygen Site
searchTimer.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 //
11 // L O S A L A M O S
12 // Los Alamos National Laboratory
13 // Los Alamos, New Mexico 87545
14 //
15 // Copyright, 1986, The Regents of the University of California.
16 //
17 // Author: Jeff Hill
18 //
19 
20 #include <stdexcept>
21 #include <string> // vxWorks 6.0 requires this include
22 #include <limits.h>
23 
24 #define epicsAssertAuthor "Jeff Hill johill@lanl.gov"
25 
26 #include "envDefs.h"
27 
28 #include "iocinf.h"
29 #include "udpiiu.h"
30 #include "nciu.h"
31 
32 static const unsigned initialTriesPerFrame = 1u; // initial UDP frames per search try
33 static const unsigned maxTriesPerFrame = 64u; // max UDP frames per search try
34 
35 //
36 // searchTimer::searchTimer ()
37 //
39  searchTimerNotify & iiuIn,
40  epicsTimerQueue & queueIn,
41  const unsigned indexIn,
42  epicsMutex & mutexIn,
43  bool boostPossibleIn ) :
44  timeAtLastSend ( epicsTime::getCurrent () ),
45  timer ( queueIn.createTimer () ),
46  iiu ( iiuIn ),
47  mutex ( mutexIn ),
48  framesPerTry ( initialTriesPerFrame ),
49  framesPerTryCongestThresh ( DBL_MAX ),
50  retry ( 0 ),
51  searchAttempts ( 0u ),
52  searchResponses ( 0u ),
53  index ( indexIn ),
54  dgSeqNoAtTimerExpireBegin ( 0u ),
55  dgSeqNoAtTimerExpireEnd ( 0u ),
56  boostPossible ( boostPossibleIn ),
57  stopped ( false )
58 {
59 }
60 
62 {
63  guard.assertIdenticalMutex ( this->mutex );
64  this->timer.start ( *this, this->period ( guard ) );
65 }
66 
68 {
69  assert ( this->chanListReqPending.count() == 0 );
70  assert ( this->chanListRespPending.count() == 0 );
71  this->timer.destroy ();
72 }
73 
75  epicsGuard < epicsMutex > & cbGuard,
77 {
78  this->stopped = true;
79  {
80  epicsGuardRelease < epicsMutex > unguard ( guard );
81  {
82  epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard );
83  this->timer.cancel ();
84  }
85  }
86 
87  while ( nciu * pChan = this->chanListReqPending.get () ) {
88  pChan->channelNode::listMember =
89  channelNode::cs_none;
90  pChan->serviceShutdownNotify ( cbGuard, guard );
91  }
92  while ( nciu * pChan = this->chanListRespPending.get () ) {
93  pChan->channelNode::listMember =
94  channelNode::cs_none;
95  pChan->serviceShutdownNotify ( cbGuard, guard );
96  }
97 }
98 
100  epicsGuard < epicsMutex > & guard, nciu & chan )
101 {
102  this->chanListReqPending.add ( chan );
103  chan.channelNode::setReqPendingState ( guard, this->index );
104 }
105 
107  epicsGuard < epicsMutex > & guard, searchTimer & dest )
108 {
109  while ( nciu * pChan = this->chanListRespPending.get () ) {
110  if ( this->searchAttempts > 0 ) {
111  this->searchAttempts--;
112  }
113  dest.installChannel ( guard, *pChan );
114  }
115  while ( nciu * pChan = this->chanListReqPending.get () ) {
116  dest.installChannel ( guard, *pChan );
117  }
118 }
119 
120 //
121 // searchTimer::expire ()
122 //
123 epicsTimerNotify::expireStatus searchTimer::expire (
124  const epicsTime & currentTime )
125 {
126  epicsGuard < epicsMutex > guard ( this->mutex );
127 
128  while ( nciu * pChan = this->chanListRespPending.get () ) {
129  pChan->channelNode::listMember =
130  channelNode::cs_none;
131  this->iiu.noSearchRespNotify (
132  guard, *pChan, this->index );
133  }
134 
135  this->timeAtLastSend = currentTime;
136 
137  // boost search period for channels not recently
138  // searched for if there was some success
139  if ( this->searchResponses && this->boostPossible ) {
140  while ( nciu * pChan = this->chanListReqPending.get () ) {
141  pChan->channelNode::listMember =
142  channelNode::cs_none;
143  this->iiu.boostChannel ( guard, *pChan );
144  }
145  }
146 
147  if ( this->searchAttempts ) {
148 #if 0
149  //
150  // dynamically adjust the number of UDP frames per
151  // try depending how many search requests are not
152  // replied to
153  //
154  // The variable this->framesPerTry
155  // determines the number of UDP frames to be sent
156  // each time that expire() is called.
157  // If this value is too high we will waste some
158  // network bandwidth. If it is too low we will
159  // use very little of the incoming UDP message
160  // buffer associated with the server's port and
161  // will therefore take longer to connect. We
162  // initialize this->framesPerTry to a prime number
163  // so that it is less likely that the
164  // same channel is in the last UDP frame
165  // sent every time that this is called (and
166  // potentially discarded by a CA server with
167  // a small UDP input queue).
168  //
169  // increase frames per try only if we see better than
170  // a 93.75% success rate for one pass through the list
171  //
172  if ( this->searchResponses >
173  ( this->searchAttempts - (this->searchAttempts/16u) ) ) {
174  // increase UDP frames per try if we have a good score
175  if ( this->framesPerTry < maxTriesPerFrame ) {
176  // a congestion avoidance threshold similar to TCP is now used
177  if ( this->framesPerTry < this->framesPerTryCongestThresh ) {
178  this->framesPerTry += this->framesPerTry;
179  }
180  else {
181  this->framesPerTry += (this->framesPerTry/8) + 1;
182  }
183  debugPrintf ( ("Increasing frame count to %u t=%u r=%u\n",
184  this->framesPerTry, this->searchAttempts, this->searchResponses) );
185  }
186  }
187  // if we detect congestion because we have less than a 87.5% success
188  // rate then gradually reduce the frames per try
189  else if ( this->searchResponses <
190  ( this->searchAttempts - (this->searchAttempts/8u) ) ) {
191  if ( this->framesPerTry > 1 ) {
192  this->framesPerTry--;
193  }
194  this->framesPerTryCongestThresh = this->framesPerTry/2 + 1;
195  debugPrintf ( ("Congestion detected - set frames per try to %f t=%u r=%u\n",
196  this->framesPerTry, this->searchAttempts, this->searchResponses) );
197  }
198 #else
199  if ( this->searchResponses == this->searchAttempts ) {
200  // increase UDP frames per try if we have a good score
201  if ( this->framesPerTry < maxTriesPerFrame ) {
202  // a congestion avoidance threshold similar to TCP is now used
203  if ( this->framesPerTry < this->framesPerTryCongestThresh ) {
204  double doubled = 2 * this->framesPerTry;
205  if ( doubled > this->framesPerTryCongestThresh ) {
206  this->framesPerTry = this->framesPerTryCongestThresh;
207  }
208  else {
209  this->framesPerTry = doubled;
210  }
211  }
212  else {
213  this->framesPerTry += 1.0 / this->framesPerTry;
214  }
215  debugPrintf ( ("Increasing frame count to %g t=%u r=%u\n",
216  this->framesPerTry, this->searchAttempts, this->searchResponses) );
217  }
218  }
219  else {
220  this->framesPerTryCongestThresh = this->framesPerTry / 2.0;
221  this->framesPerTry = 1u;
222  debugPrintf ( ("Congestion detected - set frames per try to %g t=%u r=%u\n",
223  this->framesPerTry, this->searchAttempts, this->searchResponses) );
224  }
225 #endif
226  }
227 
228  this->dgSeqNoAtTimerExpireBegin =
229  this->iiu.datagramSeqNumber ( guard );
230 
231  this->searchAttempts = 0;
232  this->searchResponses = 0;
233 
234  unsigned nFrameSent = 0u;
235  while ( true ) {
236  nciu * pChan = this->chanListReqPending.get ();
237  if ( ! pChan ) {
238  break;
239  }
240 
241  pChan->channelNode::listMember =
242  channelNode::cs_none;
243 
244  bool success = pChan->searchMsg ( guard );
245  if ( ! success ) {
246  if ( this->iiu.datagramFlush ( guard, currentTime ) ) {
247  nFrameSent++;
248  if ( nFrameSent < this->framesPerTry ) {
249  success = pChan->searchMsg ( guard );
250  }
251  }
252  if ( ! success ) {
253  this->chanListReqPending.push ( *pChan );
254  pChan->channelNode::setReqPendingState (
255  guard, this->index );
256  break;
257  }
258  }
259 
260  this->chanListRespPending.add ( *pChan );
261  pChan->channelNode::setRespPendingState (
262  guard, this->index );
263 
264  if ( this->searchAttempts < UINT_MAX ) {
265  this->searchAttempts++;
266  }
267  }
268 
269  // flush out the search request buffer
270  if ( this->iiu.datagramFlush ( guard, currentTime ) ) {
271  nFrameSent++;
272  }
273 
274  this->dgSeqNoAtTimerExpireEnd =
275  this->iiu.datagramSeqNumber ( guard ) - 1u;
276 
277 # ifdef DEBUG
278  if ( this->searchAttempts ) {
279  char buf[64];
280  currentTime.strftime ( buf, sizeof(buf), "%M:%S.%09f");
281  debugPrintf ( ("sent %u delay sec=%f Rts=%s\n",
282  nFrameSent, this->period(), buf ) );
283  }
284 # endif
285 
286  return expireStatus ( restart, this->period ( guard ) );
287 }
288 
289 void searchTimer :: show ( unsigned level ) const
290 {
291  epicsGuard < epicsMutex > guard ( this->mutex );
292  ::printf ( "searchTimer with period %f\n", this->period ( guard ) );
293  if ( level > 0 ) {
294  ::printf ( "channels with search request pending = %u\n",
295  this->chanListReqPending.count () );
296  if ( level > 1u ) {
297  tsDLIterConst < nciu > pChan =
298  this->chanListReqPending.firstIter ();
299  while ( pChan.valid () ) {
300  pChan->show ( level - 2u );
301  pChan++;
302  }
303  }
304  ::printf ( "channels with search response pending = %u\n",
305  this->chanListRespPending.count () );
306  if ( level > 1u ) {
307  tsDLIterConst < nciu > pChan =
308  this->chanListRespPending.firstIter ();
309  while ( pChan.valid () ) {
310  pChan->show ( level - 2u );
311  pChan++;
312  }
313  }
314  }
315 }
316 
317 //
318 // Reset the delay to the next search request if we get
319 // at least one response. However, dont reset this delay if we
320 // get a delayed response to an old search request.
321 //
323  epicsGuard < epicsMutex > & guard, nciu & chan,
324  ca_uint32_t respDatagramSeqNo, bool seqNumberIsValid,
325  const epicsTime & currentTime )
326 {
327  guard.assertIdenticalMutex ( this->mutex );
328  this->uninstallChan ( guard, chan );
329 
330  if ( this->stopped ) {
331  return;
332  }
333 
334  bool validResponse = true;
335  if ( seqNumberIsValid ) {
336  validResponse =
337  this->dgSeqNoAtTimerExpireBegin <= respDatagramSeqNo &&
338  this->dgSeqNoAtTimerExpireEnd >= respDatagramSeqNo;
339  }
340 
341  // if we receive a successful response then reset to a
342  // reasonable timer period
343  if ( validResponse ) {
344  double measured = currentTime - this->timeAtLastSend;
345  this->iiu.updateRTTE ( guard, measured );
346 
347  if ( this->searchResponses < UINT_MAX ) {
348  this->searchResponses++;
349  if ( this->searchResponses == this->searchAttempts ) {
350  if ( this->chanListReqPending.count () ) {
351  //
352  // when we get 100% success immediately
353  // send another search request
354  //
355  debugPrintf ( ( "All requests succesful, set timer delay to zero\n" ) );
356  this->timer.start ( *this, currentTime );
357  }
358  }
359  }
360  }
361 }
362 
364  epicsGuard < epicsMutex > & cacGuard, nciu & chan )
365 {
366  cacGuard.assertIdenticalMutex ( this->mutex );
367  unsigned ulistmem =
368  static_cast <unsigned> ( chan.channelNode::listMember );
369  unsigned uReqBase =
370  static_cast <unsigned> ( channelNode::cs_searchReqPending0 );
371  if ( ulistmem == this->index + uReqBase ) {
372  this->chanListReqPending.remove ( chan );
373  }
374  else {
375  unsigned uRespBase =
376  static_cast <unsigned > (
377  channelNode::cs_searchRespPending0 );
378  if ( ulistmem == this->index + uRespBase ) {
379  this->chanListRespPending.remove ( chan );
380  }
381  else {
382  throw std::runtime_error (
383  "uninstalling channel search timer, but channel "
384  "state is wrong" );
385  }
386  }
387  chan.channelNode::listMember = channelNode::cs_none;
388 }
389 
390 double searchTimer::period (
391  epicsGuard < epicsMutex > & guard ) const
392 {
393  guard.assertIdenticalMutex ( this->mutex );
394  return (1 << this->index ) * this->iiu.getRTTE ( guard );
395 }
396 
bool searchMsg(epicsGuard< epicsMutex > &)
Definition: nciu.cpp:214
#define assert(exp)
Declare that a condition should be true.
Definition: epicsAssert.h:70
void add(T &item)
Definition: tsDLList.h:313
virtual ca_uint32_t datagramSeqNumber(epicsGuard< epicsMutex > &) const =0
void destroy()
Definition: timer.cpp:47
void shutdown(epicsGuard< epicsMutex > &cbGuard, epicsGuard< epicsMutex > &guard)
Definition: searchTimer.cpp:74
Routines to get and set EPICS environment parameters.
tsDLIterConst< T > firstIter() const
Definition: tsDLList.h:459
#define printf
Definition: epicsStdio.h:41
virtual bool datagramFlush(epicsGuard< epicsMutex > &, const epicsTime &currentTime)=0
unsigned int ca_uint32_t
Definition: caProto.h:76
void assertIdenticalMutex(const T &) const
Definition: epicsGuard.h:80
unsigned count() const
Definition: tsDLList.h:181
void uninstallChanDueToSuccessfulSearchResponse(epicsGuard< epicsMutex > &, nciu &, ca_uint32_t respDatagramSeqNo, bool seqNumberIsValid, const epicsTime &currentTime)
bool valid() const
Definition: tsDLList.h:506
virtual ~searchTimerNotify()=0
virtual ~searchTimer()
Definition: searchTimer.cpp:67
void show(unsigned level) const
epicsMutex mutex
Definition: pvAccess.cpp:71
virtual void boostChannel(epicsGuard< epicsMutex > &, nciu &)=0
void installChannel(epicsGuard< epicsMutex > &, nciu &)
Definition: searchTimer.cpp:99
void uninstallChan(epicsGuard< epicsMutex > &, nciu &)
virtual void noSearchRespNotify(epicsGuard< epicsMutex > &, nciu &, unsigned)=0
void moveChannels(epicsGuard< epicsMutex > &, searchTimer &dest)
T * get()
Definition: tsDLList.h:261
void cancel()
Definition: timer.cpp:135
void push(T &item)
Definition: tsDLList.h:416
void show(unsigned level) const
Definition: nciu.cpp:472
void remove(T &item)
Definition: tsDLList.h:219
#define debugPrintf(argsInParen)
Definition: iocinf.h:30
searchTimer(class searchTimerNotify &, epicsTimerQueue &, const unsigned index, epicsMutex &, bool boostPossible)
Definition: searchTimer.cpp:38
#define false
Definition: flexdef.h:85
void start(class epicsTimerNotify &, const epicsTime &)
Definition: timer.cpp:59
virtual double getRTTE(epicsGuard< epicsMutex > &) const =0
Definition: nciu.h:127
virtual void updateRTTE(epicsGuard< epicsMutex > &, double rtte)=0
void start(epicsGuard< epicsMutex > &)
Definition: searchTimer.cpp:61