diff --git a/src/ca/nciu.cpp b/src/ca/nciu.cpp index 2ce963c4d..5f7c0dbcf 100644 --- a/src/ca/nciu.cpp +++ b/src/ca/nciu.cpp @@ -191,7 +191,7 @@ void nciu::setServerAddressUnknown ( udpiiu & newiiu, { guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); this->piiu = & newiiu; - this->retry = disconnectRetrySetpoint; + this->retry = 0; this->typeCode = USHRT_MAX; this->count = 0u; this->sid = UINT_MAX; @@ -217,7 +217,7 @@ void nciu::accessRightsStateChange ( /* * nciu::searchMsg () */ -bool nciu::searchMsg ( udpiiu & iiu, unsigned & retryNoForThisChannel ) +bool nciu::searchMsg ( udpiiu & iiu ) { caHdr msg; bool success; @@ -238,7 +238,6 @@ bool nciu::searchMsg ( udpiiu & iiu, unsigned & retryNoForThisChannel ) if ( this->retry < UINT_MAX ) { this->retry++; } - retryNoForThisChannel = this->retry; } return success; @@ -516,15 +515,6 @@ void nciu::show ( } } -void nciu::beaconAnomalyNotify ( - epicsGuard < epicsMutex > & guard ) -{ - guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); - if ( this->retry > beaconAnomalyRetrySetpoint ) { - this->retry = beaconAnomalyRetrySetpoint; - } -} - void nciu::ioCompletionNotify ( epicsGuard < epicsMutex > &, class baseNMIU & io ) { diff --git a/src/ca/nciu.h b/src/ca/nciu.h index 9f6c86a4f..e67ff889d 100644 --- a/src/ca/nciu.h +++ b/src/ca/nciu.h @@ -108,9 +108,7 @@ public: epicsGuard < epicsMutex > & guard ); void setServerAddressUnknown ( udpiiu & newiiu, epicsGuard < epicsMutex > & guard ); - bool searchMsg ( class udpiiu & iiu, unsigned & retryNoForThisChannel ); - void beaconAnomalyNotify ( - epicsGuard < epicsMutex > & ); + bool searchMsg ( class udpiiu & iiu ); void serviceShutdownNotify (); void accessRightsStateChange ( const caAccessRights &, epicsGuard < epicsMutex > & cbGuard, diff --git a/src/ca/searchTimer.cpp b/src/ca/searchTimer.cpp index 629a6bd99..f22066652 100644 --- a/src/ca/searchTimer.cpp +++ b/src/ca/searchTimer.cpp @@ -24,18 +24,27 @@ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "tsMinMax.h" +#include "envDefs.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "searchTimer.h" #include "udpiiu.h" -static const unsigned maxSearchTries = 100u; // max tries on unchanged net static const unsigned initialTriesPerFrame = 1u; // initial UDP frames per search try static const unsigned maxTriesPerFrame = 64u; // max UDP frames per search try static const double minSearchPeriod = 30e-3; // seconds -static const double maxSearchPeriod = 5.0; // seconds +static const double maxSearchPeriodDefault = 5.0 * 60.0; // seconds +static const double maxSearchPeriodLowerLimit = 60.0; // seconds + +// This impacts the exponential backoff delay between search messages. +// This delay is two to the power of the minimum channel retry count +// times the estimated round trip time or the OS's delay quantum +// whichever is greater. So this results in about a one second delay. +static const unsigned successfulSearchRetrySetpoint = 6u; +static const unsigned beaconAnomalyRetrySetpoint = 6u; +static const unsigned disconnectRetrySetpoint = 6u; // // searchTimer::searchTimer () @@ -48,8 +57,8 @@ searchTimer::searchTimer ( udpiiu & iiuIn, mutex ( mutexIn ), framesPerTry ( initialTriesPerFrame ), framesPerTryCongestThresh ( DBL_MAX ), - minRetry ( 0 ), - minRetryThisPass ( UINT_MAX ), + maxPeriod ( maxSearchPeriodDefault ), + retry ( 0 ), searchAttempts ( 0u ), searchResponses ( 0u ), searchAttemptsThisPass ( 0u ), @@ -58,6 +67,25 @@ searchTimer::searchTimer ( udpiiu & iiuIn, dgSeqNoAtTimerExpireEnd ( 0u ), stopped ( false ) { + if ( envGetConfigParamPtr ( & EPICS_CA_MAX_SEARCH_PERIOD ) ) { + long longStatus = envGetDoubleConfigParam ( + & EPICS_CA_MAX_SEARCH_PERIOD, & this->maxPeriod ); + if ( ! longStatus ) { + if ( this->maxPeriod < maxSearchPeriodLowerLimit ) { + epicsPrintf ( "EPICS \"%s\" out of range (low)\n", + EPICS_CA_MAX_SEARCH_PERIOD.name ); + this->maxPeriod = maxSearchPeriodLowerLimit; + epicsPrintf ( "Setting \"%s\" = %f seconds\n", + EPICS_CA_MAX_SEARCH_PERIOD.name, this->maxPeriod ); + } + } + else { + epicsPrintf ( "EPICS \"%s\" wasnt a real number\n", + EPICS_CA_MAX_SEARCH_PERIOD.name ); + epicsPrintf ( "Setting \"%s\" = %f seconds\n", + EPICS_CA_MAX_SEARCH_PERIOD.name, this->maxPeriod ); + } + } } searchTimer::~searchTimer () @@ -71,9 +99,25 @@ void searchTimer::shutdown () this->timer.cancel (); } +void searchTimer::channelCreatedNotify ( + epicsGuard < udpMutex > & guard, + const epicsTime & currentTime, bool firstChannel ) +{ + this->newChannelNotify ( guard, currentTime, + firstChannel, 0 ); +} + +void searchTimer::channelDisconnectedNotify ( + epicsGuard < udpMutex > & guard, + const epicsTime & currentTime, bool firstChannel ) +{ + this->newChannelNotify ( guard, currentTime, + firstChannel, disconnectRetrySetpoint ); +} + void searchTimer::newChannelNotify ( epicsGuard < udpMutex > & guard, const epicsTime & currentTime, - bool firstChannel, unsigned minRetryNo ) + bool firstChannel, const unsigned minRetryNo ) { if ( ! this->stopped ) { if ( firstChannel ) { @@ -102,22 +146,18 @@ void searchTimer::beaconAnomalyNotify ( // lock must be applied void searchTimer::recomputeTimerPeriod ( - epicsGuard < udpMutex > & guard, unsigned minRetryNew ) // X aCC 431 + epicsGuard < udpMutex > & guard, const unsigned retryNew ) // X aCC 431 { - this->minRetry = minRetryNew; - - size_t retry = static_cast < size_t > - ( tsMin ( this->minRetry, maxSearchTries + 1u ) ); - - unsigned idelay = 1u << tsMin ( retry, CHAR_BIT * sizeof ( idelay ) - 1u ); + this->retry = retryNew; + unsigned idelay = 1u << tsMin ( this->retry, CHAR_BIT * sizeof ( idelay ) - 1u ); double delayFactor = tsMax ( this->iiu.roundTripDelayEstimate ( guard ) * 2.0, minSearchPeriod ); this->period = idelay * delayFactor; /* sec */ - this->period = tsMin ( maxSearchPeriod, this->period ); + this->period = tsMin ( this->maxPeriod, this->period ); } void searchTimer::recomputeTimerPeriodAndStartTimer ( epicsGuard < udpMutex > & guard, - const epicsTime & currentTime, unsigned minRetryNew, const double & initialDelay ) + const epicsTime & currentTime, const unsigned retryNew, const double & initialDelay ) { if ( this->iiu.unresolvedChannelCount ( guard ) == 0 || this->stopped ) { return; @@ -126,13 +166,13 @@ void searchTimer::recomputeTimerPeriodAndStartTimer ( epicsGuard < udpMutex > & bool start = false; double totalDelay = initialDelay; { - if ( this->minRetry <= minRetryNew ) { + if ( this->retry <= retryNew ) { return; } double oldPeriod = this->period; - this->recomputeTimerPeriod ( guard, minRetryNew ); + this->recomputeTimerPeriod ( guard, retryNew ); totalDelay += this->period; @@ -159,46 +199,57 @@ void searchTimer::recomputeTimerPeriodAndStartTimer ( epicsGuard < udpMutex > & } // -// searchTimer::notifySearchResponse () +// searchTimer::notifySuccessfulSearchResponse () // // Reset the delay to the next search request if we get // at least one response. However, dont reset this delay if we // get a delayed response to an old search request. // -void searchTimer::notifySearchResponse ( epicsGuard < udpMutex > & guard, +void searchTimer::notifySuccessfulSearchResponse ( epicsGuard < udpMutex > & guard, ca_uint32_t respDatagramSeqNo, bool seqNumberIsValid, const epicsTime & currentTime ) { if ( this->iiu.unresolvedChannelCount ( guard ) == 0 || this->stopped ) { return; } - bool reschedualNeeded = false; - { - if ( seqNumberIsValid ) { - if ( this->dgSeqNoAtTimerExpireBegin <= respDatagramSeqNo && - this->dgSeqNoAtTimerExpireEnd >= respDatagramSeqNo ) { - if ( this->searchResponses < UINT_MAX ) { - this->searchResponses++; - } - } - } - else if ( this->searchResponses < UINT_MAX ) { - this->searchResponses++; - } - - // - // when we get 100% success immediately send another search request - // - if ( this->searchResponses == this->searchAttempts ) { - reschedualNeeded = true; - } + bool validResponse = true; + if ( seqNumberIsValid ) { + validResponse = + this->dgSeqNoAtTimerExpireBegin <= respDatagramSeqNo && + this->dgSeqNoAtTimerExpireEnd >= respDatagramSeqNo; } - if ( reschedualNeeded ) { - debugPrintf ( ( "Response set timer delay to zero\n" ) ); - // avoid timer cancel block deadlock - epicsGuardRelease < udpMutex > unguard ( guard ); - this->timer.start ( *this, currentTime ); + // if we receive a successful response then reset to a + // reasonable timer period + if ( validResponse ) { + if ( this->searchResponses < UINT_MAX ) { + this->searchResponses++; + if ( this->searchResponses == this->searchAttempts ) { + debugPrintf ( ( "Response set timer delay to zero\n" ) ); + if ( this->retry > successfulSearchRetrySetpoint ) { + this->recomputeTimerPeriod ( + guard, successfulSearchRetrySetpoint ); + } + // avoid timer cancel block deadlock + epicsGuardRelease < udpMutex > unguard ( guard ); + // + // when we get 100% success immediately + // send another search request + // + this->timer.start ( *this, currentTime ); + } + else { + debugPrintf ( ( "Response set timer delay to beacon anomaly set point\n" ) ); + // + // otherwise, if making some progress then dont allow + // retry rate to drop below some reasonable minimum + // + if ( this->retry > successfulSearchRetrySetpoint ) { + this->recomputeTimerPeriodAndStartTimer ( + guard, currentTime, successfulSearchRetrySetpoint, 0.0 ); + } + } + } } } @@ -322,10 +373,8 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi // if we are making some progress then dont increase the // delay between search requests if ( this->searchResponsesThisPass == 0u ) { - this->recomputeTimerPeriod ( guard, this->minRetryThisPass ); + this->recomputeTimerPeriod ( guard, this->retry + 1 ); } - - this->minRetryThisPass = UINT_MAX; this->searchAttemptsThisPass = 0; this->searchResponsesThisPass = 0; @@ -333,8 +382,7 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi debugPrintf ( ("saw end of list\n") ); } - unsigned retryNoForThisChannel; - if ( ! this->iiu.searchMsg ( guard, retryNoForThisChannel ) ) { + if ( ! this->iiu.searchMsg ( guard ) ) { nFrameSent++; if ( nFrameSent >= this->framesPerTry ) { @@ -342,13 +390,9 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi } this->dgSeqNoAtTimerExpireEnd = this->iiu.datagramSeqNumber ( guard ); this->iiu.datagramFlush ( guard, currentTime ); - if ( ! this->iiu.searchMsg ( guard, retryNoForThisChannel ) ) { + if ( ! this->iiu.searchMsg ( guard ) ) { break; } - } - - if ( this->minRetryThisPass > retryNoForThisChannel ) { - this->minRetryThisPass = retryNoForThisChannel; } if ( this->searchAttempts < UINT_MAX ) { @@ -399,14 +443,11 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi this->period = DBL_MAX; return noRestart; } - else if ( this->minRetry < maxSearchTries ) { - return expireStatus ( restart, this->period ); - } - else { - debugPrintf ( ( "maximum search tries exceeded - giving up\n" ) ); - this->period = DBL_MAX; - return noRestart; - } + + // the code used to test this->minRetry < maxSearchTries here + // and return no restart if the maximum tries was exceeded + // prior to R3.14.7 + return expireStatus ( restart, this->period ); } void searchTimer::show ( unsigned /* level */ ) const diff --git a/src/ca/searchTimer.h b/src/ca/searchTimer.h index 513a6dd0b..1a0f96d6f 100644 --- a/src/ca/searchTimer.h +++ b/src/ca/searchTimer.h @@ -49,14 +49,15 @@ class searchTimer : private epicsTimerNotify { public: searchTimer ( class udpiiu &, epicsTimerQueue &, udpMutex & ); virtual ~searchTimer (); - void notifySearchResponse ( epicsGuard < udpMutex > &, + void notifySuccessfulSearchResponse ( epicsGuard < udpMutex > &, ca_uint32_t respDatagramSeqNo, bool seqNumberIsValid, const epicsTime & currentTime ); - void newChannelNotify ( epicsGuard < udpMutex > &, - const epicsTime &, bool firstChannel, - unsigned minRetryNo ); void beaconAnomalyNotify ( epicsGuard < udpMutex > &, const epicsTime & currentTime, const double & delay ); + void channelCreatedNotify ( epicsGuard < udpMutex > &, + const epicsTime &, bool firstChannel ); + void channelDisconnectedNotify ( epicsGuard < udpMutex > &, + const epicsTime &, bool firstChannel ); void shutdown (); void show ( unsigned level ) const; private: @@ -66,20 +67,23 @@ private: udpMutex & mutex; double framesPerTry; /* # of UDP frames per search try */ double framesPerTryCongestThresh; /* one half N tries w congest */ - unsigned minRetry; /* min retry number so far */ - unsigned minRetryThisPass; - unsigned searchAttempts; /* num search tries within this timer experation */ - unsigned searchResponses; /* num search resp within this timer experation */ + double maxPeriod; + unsigned retry; + unsigned searchAttempts; /* num search tries after last timer experation */ + unsigned searchResponses; /* num search resp after last timer experation */ unsigned searchAttemptsThisPass; /* num search tries within this pass */ unsigned searchResponsesThisPass; /* num search resp within this pass */ ca_uint32_t dgSeqNoAtTimerExpireBegin; ca_uint32_t dgSeqNoAtTimerExpireEnd; bool stopped; expireStatus expire ( const epicsTime & currentTime ); - void recomputeTimerPeriod ( epicsGuard < udpMutex > &, unsigned minRetryNew ); + void recomputeTimerPeriod ( epicsGuard < udpMutex > &, const unsigned minRetryNew ); void recomputeTimerPeriodAndStartTimer ( epicsGuard < udpMutex > &, - const epicsTime & currentTime, unsigned minRetryNew, + const epicsTime & currentTime, const unsigned minRetryNew, const double & initialDelay ); + void newChannelNotify ( epicsGuard < udpMutex > &, + const epicsTime &, bool firstChannel, + const unsigned minRetryNo ); searchTimer ( const searchTimer & ); searchTimer & operator = ( const searchTimer & ); }; diff --git a/src/ca/udpiiu.cpp b/src/ca/udpiiu.cpp index 17dc43282..32086d323 100644 --- a/src/ca/udpiiu.cpp +++ b/src/ca/udpiiu.cpp @@ -650,7 +650,8 @@ bool udpiiu::searchRespAction ( // X aCC 361 // deadlock can result if this is called while holding the primary // mutex (because the primary mutex is used in the search timer callback) epicsGuard < udpMutex > guard ( this->mutex ); - this->pSearchTmr->notifySearchResponse ( guard, this->lastReceivedSeqNo, + this->pSearchTmr->notifySuccessfulSearchResponse ( + guard, this->lastReceivedSeqNo, this->lastReceivedSeqNoIsValid, currentTime ); } @@ -1010,12 +1011,6 @@ void udpiiu::beaconAnomalyNotify ( { epicsGuard guard ( this->mutex ); - tsDLIter < nciu > chan = this->serverAddrRes.firstIter (); - while ( chan.valid () ) { - chan->beaconAnomalyNotify ( cacGuard ); - chan++; - } - static const double portTicksPerSec = 1000u; static const unsigned portBasedDelayMask = 0xff; @@ -1037,13 +1032,12 @@ void udpiiu::beaconAnomalyNotify ( this->pSearchTmr->beaconAnomalyNotify ( guard, currentTime, delay ); } -bool udpiiu::searchMsg ( epicsGuard < udpMutex > & /* guard */, - unsigned & retryNoForThisChannel ) +bool udpiiu::searchMsg ( epicsGuard < udpMutex > & /* guard */ ) { bool success; if ( nciu *pChan = this->serverAddrRes.get () ) { - success = pChan->searchMsg ( *this, retryNoForThisChannel ); + success = pChan->searchMsg ( *this ); if ( success ) { this->serverAddrRes.add ( *pChan ); } @@ -1074,8 +1068,8 @@ void udpiiu::installNewChannel ( const epicsTime & currentTime, nciu & chan ) chan.channelNode::listMember = channelNode::cs_serverAddrResPend; - this->pSearchTmr->newChannelNotify ( guard, currentTime, - firstChannel, 0 ); + this->pSearchTmr->channelCreatedNotify ( + guard, currentTime, firstChannel ); } void udpiiu::installDisconnectedChannel ( nciu & chan ) @@ -1090,10 +1084,7 @@ void udpiiu::govExpireNotify ( const epicsTime & currentTime ) { epicsGuard < udpMutex > guard ( this->mutex ); if ( this->disconnGovernor.count () ) { - bool firstChannel = false; - if ( this->serverAddrRes.count() == 0 ) { - firstChannel = true; - } + bool firstChannel = this->serverAddrRes.count() == 0; // push them to the front of the list so that // a search request is sent immediately, and // so that the new channel's retry count is @@ -1104,8 +1095,8 @@ void udpiiu::govExpireNotify ( const epicsTime & currentTime ) pChan->channelNode::listMember = channelNode::cs_serverAddrResPend; } - this->pSearchTmr->newChannelNotify ( guard, currentTime, - firstChannel, disconnectRetrySetpoint ); + this->pSearchTmr->channelDisconnectedNotify ( + guard, currentTime, firstChannel ); } } diff --git a/src/ca/udpiiu.h b/src/ca/udpiiu.h index 86373c1d8..0dfd56fcd 100644 --- a/src/ca/udpiiu.h +++ b/src/ca/udpiiu.h @@ -95,7 +95,7 @@ public: void installNewChannel ( const epicsTime & currentTime, nciu & ); void installDisconnectedChannel ( nciu & ); void repeaterRegistrationMessage ( unsigned attemptNumber ); - bool searchMsg ( epicsGuard < udpMutex > &, unsigned & retryNoForThisChannel ); + bool searchMsg ( epicsGuard < udpMutex > & ); void datagramFlush ( epicsGuard < udpMutex > &, const epicsTime & currentTime ); ca_uint32_t datagramSeqNumber ( epicsGuard < udpMutex > & ) const; void show ( unsigned level ) const; @@ -230,14 +230,6 @@ private: udpiiu & operator = ( const udpiiu & ); }; -// This impacts the exponential backoff delay between search messages. -// This delay is two to the power of the minimum channel retry count -// times the estimated round trip time or the OS's delay quantum -// whichever is greater. So this results in about a one second delay. -// -static const unsigned beaconAnomalyRetrySetpoint = 6u; -static const unsigned disconnectRetrySetpoint = 6u; - inline void udpMutex::lock () { this->mutex.lock ();