diff --git a/src/ca/cac.cpp b/src/ca/cac.cpp index 5cf2d72cb..1953fe1ee 100644 --- a/src/ca/cac.cpp +++ b/src/ca/cac.cpp @@ -602,8 +602,8 @@ bool cac::transferChanToVirtCircuit ( } } - this->pudpiiu->uninstallChan ( guard, *pChan ); - piiu->installChannel ( guard, *pChan, sid, typeCode, count ); + this->pudpiiu->uninstallChan ( cbGuard, guard, *pChan ); + piiu->installChannel ( cbGuard, guard, *pChan, sid, typeCode, count ); if ( ! piiu->ca_v42_ok () ) { // connect to old server with lock applied @@ -697,7 +697,7 @@ void cac::destroyChannel ( nciu & chan ) // o chan destroy exception has been delivered { epicsGuard < cacMutex > guard ( this->mutex ); - chan.getPIIU()->uninstallChan ( guard, chan ); + chan.getPIIU()->uninstallChan ( cbGuard, guard, chan ); } } @@ -1370,9 +1370,9 @@ void cac::disconnectChannel ( { assert ( this->pudpiiu ); this->disconnectAllIO ( guard, chan, true ); - chan.getPIIU()->uninstallChan ( guard, chan ); - chan.disconnect ( *this->pudpiiu, cbGuard, guard ); + chan.getPIIU()->uninstallChan ( cbGuard, guard, chan ); this->pudpiiu->installDisconnectedChannel ( currentTime, chan ); + chan.circuitHangupNotify ( *this->pudpiiu, cbGuard, guard ); } bool cac::badTCPRespAction ( epicsGuard < callbackMutex > &, tcpiiu & iiu, @@ -1472,17 +1472,23 @@ void cac::disconnectNotify ( tcpiiu & iiu ) iiu.disconnectNotify ( guard ); } +void cac::unresponsiveCircuitNotify ( tcpiiu & iiu ) +{ + epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); + epicsGuard < cacMutex > guard ( this->mutex ); + iiu.unresponsiveCircuitNotify ( cbGuard, guard ); +} + void cac::initiateAbortShutdown ( tcpiiu & iiu ) { int exception = ECA_DISCONN; char hostNameTmp[64]; bool exceptionNeeded = false; epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); - { epicsGuard < cacMutex > guard ( this->mutex ); - if ( iiu.channelCount() ) { + if ( iiu.channelCount( cbGuard ) ) { iiu.hostName ( hostNameTmp, sizeof ( hostNameTmp ) ); if ( iiu.connecting () ) { exception = ECA_CONNSEQTMO; @@ -1509,7 +1515,7 @@ void cac::destroyIIU ( tcpiiu & iiu ) { epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); epicsGuard < cacMutex > guard ( this->mutex ); - if ( iiu.channelCount() ) { + if ( iiu.channelCount ( cbGuard ) ) { char hostNameTmp[64]; iiu.hostName ( hostNameTmp, sizeof ( hostNameTmp ) ); genLocalExcep ( cbGuard, *this, ECA_DISCONN, hostNameTmp ); diff --git a/src/ca/nciu.cpp b/src/ca/nciu.cpp index edf9b16de..5f4525641 100644 --- a/src/ca/nciu.cpp +++ b/src/ca/nciu.cpp @@ -55,7 +55,8 @@ nciu::nciu ( cac & cacIn, netiiu & iiuIn, cacChannelNotify & chanIn, typeCode ( USHRT_MAX ), priority ( static_cast ( pri ) ), f_connected ( false ), - f_claimSent ( false ) + f_createChanReqSent ( false ), + f_createChanRespReceived ( false ) { size_t nameLengthTmp = strlen ( pNameIn ) + 1; @@ -113,16 +114,16 @@ void nciu::connect ( unsigned nativeType, epicsGuard < callbackMutex > & cbGuard, epicsGuard < cacMutex > & guard ) { - if ( ! this->f_claimSent ) { + if ( ! this->f_createChanReqSent ) { this->cacCtx.printf ( "CAC: Ignored conn resp to chan lacking virtual circuit CID=%u SID=%u?\n", this->getId (), sidIn ); return; } - if ( this->f_connected ) { + if ( this->f_createChanRespReceived ) { this->cacCtx.printf ( - "CAC: Ignored conn resp to conn chan CID=%u SID=%u?\n", + "CAC: Ignored create channel resp to conn chan CID=%u SID=%u?\n", this->getId (), sidIn ); return; } @@ -138,6 +139,7 @@ void nciu::connect ( unsigned nativeType, this->count = nativeCount; this->sid = sidIn; this->f_connected = true; + this->f_createChanRespReceived = true; /* * if less than v4.1 then the server will never @@ -156,16 +158,6 @@ void nciu::connect ( unsigned nativeType, { epicsGuardRelease < cacMutex > unguard ( guard ); - // channel uninstal routine grabs the callback lock so - // a channel will not be deleted while a call back is - // in progress - // - // the callback lock is also taken when a channel - // disconnects to prevent a race condition with the - // code below - ie we hold the callback lock here - // so a chanel cant be destroyed out from under us. - this->notify().connectNotify (); - /* * if less than v4.1 then the server will never * send access rights and we know that there @@ -175,14 +167,47 @@ void nciu::connect ( unsigned nativeType, if ( ! v41Ok ) { this->notify().accessRightsNotify ( this->accessRightState ); } + + // channel uninstal routine grabs the callback lock so + // a channel will not be deleted while a call back is + // in progress + // + // the callback lock is also taken when a channel + // disconnects to prevent a race condition with the + // code below - ie we hold the callback lock here + // so a chanel cant be destroyed out from under us. + this->notify().connectNotify (); } } -void nciu::disconnect ( - netiiu & newiiu, epicsGuard < callbackMutex > & cbGuard, +void nciu::unresponsiveCircuitNotify ( + epicsGuard < callbackMutex > & cbGuard, epicsGuard < cacMutex > & guard ) { - bool currentlyConnected = this->f_connected; + if ( this->f_connected ) { + this->f_connected = false; + epicsGuardRelease < cacMutex > autoMutexRelease ( guard ); + this->notify().disconnectNotify (); + caAccessRights noRights; + this->notify().accessRightsNotify ( noRights ); + } +} + +void nciu::responsiveCircuitNotify ( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < cacMutex > & guard ) +{ + if ( ! this->f_connected ) { + this->f_connected = true; + epicsGuardRelease < cacMutex > autoMutexRelease ( guard ); + this->notify().connectNotify (); + this->notify().accessRightsNotify ( this->accessRightState ); + } +} + +void nciu::circuitHangupNotify ( class udpiiu & newiiu, + epicsGuard < callbackMutex > & cbGuard, epicsGuard < cacMutex > & guard ) +{ this->piiu = & newiiu; this->retry = disconnectRetrySetpoint; this->typeCode = USHRT_MAX; @@ -190,9 +215,10 @@ void nciu::disconnect ( this->sid = UINT_MAX; this->accessRightState.clrReadPermit(); this->accessRightState.clrWritePermit(); - this->f_claimSent = false; - this->f_connected = false; - if ( currentlyConnected ) { + this->f_createChanReqSent = false; + this->f_createChanRespReceived = false; + if ( this->f_connected ) { + this->f_connected = false; epicsGuardRelease < cacMutex > autoMutexRelease ( guard ); this->notify().disconnectNotify (); this->notify().accessRightsNotify ( this->accessRightState ); @@ -259,11 +285,12 @@ void nciu::createChannelRequest ( tcpiiu & iiu, epicsGuard < cacMutex > & guard ) { iiu.createChannelRequest ( *this, guard ); - this->f_claimSent = true; + this->f_createChanReqSent = true; } -cacChannel::ioStatus nciu::read ( unsigned type, arrayElementCount countIn, - cacReadNotify ¬ify, ioid *pId ) +cacChannel::ioStatus nciu::read ( + unsigned type, arrayElementCount countIn, + cacReadNotify ¬ify, ioid *pId ) { // // fail out if their arguments are invalid diff --git a/src/ca/tcpiiu.cpp b/src/ca/tcpiiu.cpp index e27b649db..d613609e2 100644 --- a/src/ca/tcpiiu.cpp +++ b/src/ca/tcpiiu.cpp @@ -37,6 +37,7 @@ #include "net_convert.h" #include "bhe.h" #include "epicsSignal.h" +#include "caerr.h" const unsigned mSecPerSec = 1000u; const unsigned uSecPerSec = 1000u * mSecPerSec; @@ -344,7 +345,12 @@ void tcpRecvThread::run () // only one recv thread at a time may call callbacks // - pendEvent() blocks until threads waiting for // this lock get a chance to run - epicsGuard < callbackMutex > guard ( this->cbMutex ); + epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); + + if ( this->iiu.softDisconnect ) { + epicsGuard < cacMutex > cacGuard ( this->iiu.cacRef.mutexRef() ); + this->iiu.responsiveCircuitNotify ( cbGuard, cacGuard ); + } // force the receive watchdog to be reset every 5 frames unsigned contiguousFrameCount = 0; @@ -368,7 +374,7 @@ void tcpRecvThread::run () pComBuf = new ( this->iiu.comBufMemMgr ) comBuf; // execute receive labor - bool protocolOK = this->iiu.processIncoming ( currentTime, guard ); + bool protocolOK = this->iiu.processIncoming ( currentTime, cbGuard ); if ( ! protocolOK ) { this->iiu.cacRef.initiateAbortShutdown ( this->iiu ); break; @@ -449,7 +455,8 @@ tcpiiu::tcpiiu ( cac & cac, callbackMutex & cbMutex, double connectionTimeout, earlyFlush ( false ), recvProcessPostponedFlush ( false ), discardingPendingData ( false ), - socketHasBeenClosed ( false ) + socketHasBeenClosed ( false ), + softDisconnect ( false ) { this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( this->sock == INVALID_SOCKET ) { @@ -637,6 +644,52 @@ void tcpiiu::disconnectNotify ( epicsGuard < cacMutex > & ) this->sendThreadFlushEvent.signal (); } +void tcpiiu::responsiveCircuitNotify ( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < cacMutex > & guard ) +{ + this->softDisconnect = false; + tsDLIter < nciu > pChan = this->channelList.firstIter (); + while ( pChan.valid() ) { + // The cac lock is released herein so there is concern that + // the list could be changed while we are traversing it. + // However, this occurs only if a circuit disconnects, + // a user deletes a channel, or a server disconnects a + // channel. The callback lock must be taken in all of + // these situations so this code is protected. + pChan->responsiveCircuitNotify ( cbGuard, guard ); + pChan++; + } +} + +void tcpiiu::unresponsiveCircuitNotify ( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < cacMutex > & guard ) +{ + this->recvDog.cancel(); + this->sendDog.cancel(); + this->softDisconnect = true; + if ( this->channelList.count() ) { + char hostNameTmp[128]; + this->hostName ( hostNameTmp, sizeof ( hostNameTmp ) ); + { + epicsGuardRelease < cacMutex > guardRelease ( guard ); + genLocalExcep ( cbGuard, this->cacRef, ECA_UNRESPTMO, hostNameTmp ); + } + tsDLIter < nciu > pChan = this->channelList.firstIter (); + while ( pChan.valid() ) { + // The cac lock is released herein so there is concern that + // the list could be changed while we are traversing it. + // However, this occurs only if a circuit disconnects, + // a user deletes a channel, or a server disconnects a + // channel. The callback lock must be taken in all of + // these situations so this code is protected. + pChan->unresponsiveCircuitNotify ( cbGuard, guard ); + pChan++; + } + } +} + void tcpiiu::initiateAbortShutdown ( epicsGuard < callbackMutex > &, epicsGuard < cacMutex > & guard ) { @@ -750,6 +803,7 @@ void tcpiiu::show ( unsigned level ) const this->recvThread.show ( level-2u ); ::printf ("\techo pending bool = %u\n", this->echoRequestPending ); ::printf ( "IO identifier hash table:\n" ); + tsDLIterConst < nciu > pChan = this->channelList.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 2u ); @@ -1301,7 +1355,9 @@ void tcpiiu::removeAllChannels ( } } -void tcpiiu::installChannel ( epicsGuard < cacMutex > & guard, +void tcpiiu::installChannel ( + epicsGuard < callbackMutex > &, + epicsGuard < cacMutex > & guard, nciu & chan, unsigned sidIn, ca_uint16_t typeIn, arrayElementCount countIn ) { @@ -1311,8 +1367,9 @@ void tcpiiu::installChannel ( epicsGuard < cacMutex > & guard, this->flushRequest (); } -void tcpiiu::uninstallChan - ( epicsGuard < cacMutex > & guard, nciu & chan ) +void tcpiiu::uninstallChan ( + epicsGuard < callbackMutex > &, + epicsGuard < cacMutex > & guard, nciu & chan ) { this->channelList.remove ( chan ); if ( channelList.count() == 0 ) {