diff --git a/src/ca/searchTimer.cpp b/src/ca/searchTimer.cpp index d381d94aa..e83caddef 100644 --- a/src/ca/searchTimer.cpp +++ b/src/ca/searchTimer.cpp @@ -42,10 +42,12 @@ static const double maxSearchPeriod = 5.0; // seconds // // searchTimer::searchTimer () // -searchTimer::searchTimer ( udpiiu & iiuIn, epicsTimerQueue & queueIn ) : +searchTimer::searchTimer ( udpiiu & iiuIn, + epicsTimerQueue & queueIn, udpMutex & mutexIn ) : period ( 1e9 ), timer ( queueIn.createTimer () ), iiu ( iiuIn ), + mutex ( mutexIn ), framesPerTry ( initialTriesPerFrame ), framesPerTryCongestThresh ( UINT_MAX ), minRetry ( 0 ), @@ -64,29 +66,34 @@ searchTimer::~searchTimer () this->timer.destroy (); } -void searchTimer::newChannleNotify ( - const epicsTime & currentTime, bool firstChannel ) +void searchTimer::newChannelNotify ( + epicsGuard < udpMutex > & guard, const epicsTime & currentTime, bool firstChannel ) { if ( firstChannel ) { + this->recomputeTimerPeriod ( guard, 0 ); + double newPeriod = this->period; { - epicsGuard < searchTimerMutex > locker ( this->mutex ); - this->recomputeTimerPeriod ( 0 ); + // avoid timer cancel block deadlock + epicsGuardRelease < udpMutex > unguard ( guard ); + this->timer.start ( *this, currentTime + newPeriod ); } - this->timer.start ( *this, currentTime + this->period ); } else { - this->recomputeTimerPeriodAndStartTimer ( currentTime, 0, 0.0 ); + this->recomputeTimerPeriodAndStartTimer ( guard, currentTime, 0, 0.0 ); } } void searchTimer::beaconAnomalyNotify ( + epicsGuard < udpMutex > & guard, const epicsTime & currentTime, const double & delay ) { - this->recomputeTimerPeriodAndStartTimer ( currentTime, beaconAnomalyRetrySetpoint, delay ); + this->recomputeTimerPeriodAndStartTimer ( + guard, currentTime, beaconAnomalyRetrySetpoint, delay ); } // lock must be applied -void searchTimer::recomputeTimerPeriod ( unsigned minRetryNew ) +void searchTimer::recomputeTimerPeriod ( + epicsGuard < udpMutex > & guard, unsigned minRetryNew ) { this->minRetry = minRetryNew; @@ -94,43 +101,48 @@ void searchTimer::recomputeTimerPeriod ( unsigned minRetryNew ) ( tsMin ( this->minRetry, maxSearchTries + 1u ) ); unsigned idelay = 1u << tsMin ( retry, CHAR_BIT * sizeof ( idelay ) - 1u ); - this->period = idelay * this->iiu.roundTripDelayEstimate() * 2.0; /* sec */ + this->period = idelay * this->iiu.roundTripDelayEstimate ( guard ) * 2.0; /* sec */ this->period = tsMin ( maxSearchPeriod, this->period ); this->period = tsMax ( minSearchPeriod, this->period ); } -void searchTimer::recomputeTimerPeriodAndStartTimer ( +void searchTimer::recomputeTimerPeriodAndStartTimer ( epicsGuard < udpMutex > & guard, const epicsTime & currentTime, unsigned minRetryNew, const double & initialDelay ) { - bool start = false; - double totalDelay = initialDelay; - - epicsGuard < searchTimerMutex > locker ( this->mutex ); - - if ( this->iiu.channelCount() == 0 || this->minRetry <= minRetryNew ) { + if ( this->iiu.channelCount ( guard ) == 0 ) { return; } - double oldPeriod = this->period; + bool start = false; + double totalDelay = initialDelay; + { + if ( this->minRetry <= minRetryNew ) { + return; + } - this->recomputeTimerPeriod ( minRetryNew ); + double oldPeriod = this->period; - totalDelay += this->period; + this->recomputeTimerPeriod ( guard, minRetryNew ); - if ( totalDelay < oldPeriod ) { - epicsTimer::expireInfo info = this->timer.getExpireInfo (); - if ( info.active ) { - double delay = currentTime - info.expireTime; - if ( delay > totalDelay ) { + totalDelay += this->period; + + if ( totalDelay < oldPeriod ) { + epicsTimer::expireInfo info = this->timer.getExpireInfo (); + if ( info.active ) { + double delay = info.expireTime - currentTime; + if ( delay > totalDelay ) { + start = true; + } + } + else { start = true; } } - else { - start = true; - } } if ( start ) { + // avoid timer cancel block deadlock + epicsGuardRelease < udpMutex > unguard ( guard ); this->timer.start ( *this, currentTime + totalDelay ); } debugPrintf ( ( "changed search period to %f sec\n", this->period ) ); @@ -143,14 +155,15 @@ void searchTimer::recomputeTimerPeriodAndStartTimer ( // at least one response. However, dont reset this delay if we // get a delayed response to an old search request. // -void searchTimer::notifySearchResponse ( ca_uint32_t respDatagramSeqNo, - bool seqNumberIsValid, const epicsTime & currentTime ) +void searchTimer::notifySearchResponse ( epicsGuard < udpMutex > & guard, + ca_uint32_t respDatagramSeqNo, bool seqNumberIsValid, const epicsTime & currentTime ) { + if ( this->iiu.channelCount ( guard ) == 0 ) { + return; + } + bool reschedualNeeded = false; - - if ( this->iiu.channelCount() > 0 ) { - epicsGuard < searchTimerMutex > locker ( this->mutex ); - + { if ( seqNumberIsValid ) { if ( this->dgSeqNoAtTimerExpireBegin <= respDatagramSeqNo && this->dgSeqNoAtTimerExpireEnd >= respDatagramSeqNo ) { @@ -179,6 +192,9 @@ void searchTimer::notifySearchResponse ( ca_uint32_t respDatagramSeqNo, # endif // debugPrintf ( ( "Response set timer delay to zero. ts=%s\n", // buf ) ); + + // avoid timer cancel block deadlock + epicsGuardRelease < udpMutex > unguard ( guard ); this->timer.start ( *this, currentTime ); } } @@ -188,10 +204,11 @@ void searchTimer::notifySearchResponse ( ca_uint32_t respDatagramSeqNo, // epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTime ) // X aCC 361 { + epicsGuard < udpMutex > guard ( this->mutex ); + // check to see if there is nothing to do here - if ( this->iiu.channelCount () == 0 ) { + if ( this->iiu.channelCount ( guard ) == 0 ) { debugPrintf ( ( "all channels located - search timer terminating\n" ) ); - epicsGuard < searchTimerMutex > locker ( this->mutex ); this->period = DBL_MAX; return noRestart; } @@ -259,7 +276,7 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi this->searchResponsesThisPass = UINT_MAX; } - this->dgSeqNoAtTimerExpireBegin = this->iiu.datagramSeqNumber (); + this->dgSeqNoAtTimerExpireBegin = this->iiu.datagramSeqNumber ( guard ); this->searchAttempts = 0; this->searchResponses = 0; @@ -269,11 +286,11 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi while ( true ) { // check to see if we have reached the end of the list - if ( this->searchAttemptsThisPass >= this->iiu.channelCount () ) { + if ( this->searchAttemptsThisPass >= this->iiu.channelCount ( guard ) ) { // if we are making some progress then dont increase the // delay between search requests if ( this->searchResponsesThisPass == 0u ) { - this->recomputeTimerPeriod ( this->minRetryThisPass ); + this->recomputeTimerPeriod ( guard, this->minRetryThisPass ); } this->minRetryThisPass = UINT_MAX; @@ -285,17 +302,15 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi } unsigned retryNoForThisChannel; - if ( ! this->iiu.searchMsg ( retryNoForThisChannel ) ) { + if ( ! this->iiu.searchMsg ( guard, retryNoForThisChannel ) ) { nFrameSent++; if ( nFrameSent >= this->framesPerTry ) { break; } - - this->dgSeqNoAtTimerExpireEnd = this->iiu.datagramSeqNumber (); - this->iiu.datagramFlush ( currentTime ); - - if ( ! this->iiu.searchMsg ( retryNoForThisChannel ) ) { + this->dgSeqNoAtTimerExpireEnd = this->iiu.datagramSeqNumber ( guard ); + this->iiu.datagramFlush ( guard, currentTime ); + if ( ! this->iiu.searchMsg ( guard, retryNoForThisChannel ) ) { break; } } @@ -314,7 +329,7 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi // // dont send any of the channels twice within one try // - if ( nChanSent >= this->iiu.channelCount () ) { + if ( nChanSent >= this->iiu.channelCount ( guard ) ) { // // add one to nFrameSent because there may be // one more partial frame to be sent @@ -335,9 +350,9 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi } // flush out the search request buffer - this->iiu.datagramFlush ( currentTime ); + this->iiu.datagramFlush ( guard, currentTime ); - this->dgSeqNoAtTimerExpireEnd = this->iiu.datagramSeqNumber () - 1u; + this->dgSeqNoAtTimerExpireEnd = this->iiu.datagramSeqNumber ( guard ) - 1u; # ifdef DEBUG char buf[64]; @@ -347,19 +362,16 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi nFrameSent, this->period, buf ) ); # endif - if ( this->iiu.channelCount () == 0 ) { + if ( this->iiu.channelCount ( guard ) == 0 ) { debugPrintf ( ( "all channels connected\n" ) ); - epicsGuard < searchTimerMutex > locker ( this->mutex ); this->period = DBL_MAX; return noRestart; } else if ( this->minRetry < maxSearchTries ) { - epicsGuard < searchTimerMutex > locker ( this->mutex ); return expireStatus ( restart, this->period ); } else { debugPrintf ( ( "maximum search tries exceeded - giving up\n" ) ); - epicsGuard < searchTimerMutex > locker ( this->mutex ); this->period = DBL_MAX; return noRestart; }