Most errors found with: codespell -L cach,thst,odly,aslo,parm,parms,inpu,ges,prset,pevent,ptd,pring,valu,noo,noe,ned,inout,ro,siz,froms,nd,fo,singl,sart,multy,tthe,allong,ment,inate,nodel,tring,alse,ture,thi,wille,numer Some more manually found (its -> it's) c++20: Do not use apostrophe (e.g. can't) in unquoted #error message
252 lines
8.7 KiB
C++
252 lines
8.7 KiB
C++
/*************************************************************************\
|
|
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
|
* National Laboratory.
|
|
* Copyright (c) 2002 The Regents of the University of California, as
|
|
* Operator of Los Alamos National Laboratory.
|
|
* SPDX-License-Identifier: EPICS
|
|
* EPICS BASE is distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
\*************************************************************************/
|
|
/*
|
|
*
|
|
* L O S A L A M O S
|
|
* Los Alamos National Laboratory
|
|
* Los Alamos, New Mexico 87545
|
|
*
|
|
* Copyright, 1986, The Regents of the University of California.
|
|
*
|
|
* Author: Jeff Hill
|
|
*/
|
|
|
|
#define epicsAssertAuthor "Jeff Hill johill@lanl.gov"
|
|
|
|
#include "iocinf.h"
|
|
#include "cac.h"
|
|
#include "virtualCircuit.h"
|
|
|
|
//
|
|
// the recv watchdog timer is active when this object is created
|
|
//
|
|
tcpRecvWatchdog::tcpRecvWatchdog
|
|
( epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn,
|
|
epicsMutex & mutexIn, tcpiiu & iiuIn,
|
|
double periodIn, epicsTimerQueue & queueIn ) :
|
|
period ( periodIn ), timer ( queueIn.createTimer () ),
|
|
cbMutex ( cbMutexIn ), ctxNotify ( ctxNotifyIn ),
|
|
mutex ( mutexIn ), iiu ( iiuIn ),
|
|
probeResponsePending ( false ), beaconAnomaly ( true ),
|
|
probeTimeoutDetected ( false ), shuttingDown ( false )
|
|
{
|
|
}
|
|
|
|
tcpRecvWatchdog::~tcpRecvWatchdog ()
|
|
{
|
|
this->timer.destroy ();
|
|
}
|
|
|
|
epicsTimerNotify::expireStatus
|
|
tcpRecvWatchdog::expire ( const epicsTime & /* currentTime */ )
|
|
{
|
|
epicsGuard < epicsMutex > guard ( this->mutex );
|
|
if ( this->shuttingDown ) {
|
|
return noRestart;
|
|
}
|
|
if ( this->probeResponsePending ) {
|
|
if ( this->iiu.receiveThreadIsBusy ( guard ) ) {
|
|
return expireStatus ( restart, CA_ECHO_TIMEOUT );
|
|
}
|
|
|
|
{
|
|
# ifdef DEBUG
|
|
char hostName[128];
|
|
this->iiu.getHostName ( guard, hostName, sizeof (hostName) );
|
|
debugPrintf ( ( "CA server \"%s\" unresponsive after %g inactive sec"
|
|
"- disconnecting.\n",
|
|
hostName, this->period ) );
|
|
# endif
|
|
// to get the callback lock safely we must reorder
|
|
// the lock hierarchy
|
|
epicsGuardRelease < epicsMutex > unguard ( guard );
|
|
{
|
|
// callback lock is required because channel disconnect
|
|
// state change is initiated from this thread, and
|
|
// this can cause their disconnect notify callback
|
|
// to be invoked.
|
|
callbackManager mgr ( this->ctxNotify, this->cbMutex );
|
|
epicsGuard < epicsMutex > tmpGuard ( this->mutex );
|
|
this->iiu.receiveTimeoutNotify ( mgr, tmpGuard );
|
|
this->probeTimeoutDetected = true;
|
|
}
|
|
}
|
|
return noRestart;
|
|
}
|
|
else {
|
|
if ( this->iiu.receiveThreadIsBusy ( guard ) ) {
|
|
return expireStatus ( restart, this->period );
|
|
}
|
|
this->probeTimeoutDetected = false;
|
|
this->probeResponsePending = this->iiu.setEchoRequestPending ( guard );
|
|
debugPrintf ( ("circuit timed out - sending echo request\n") );
|
|
return expireStatus ( restart, CA_ECHO_TIMEOUT );
|
|
}
|
|
}
|
|
|
|
void tcpRecvWatchdog::beaconArrivalNotify (
|
|
epicsGuard < epicsMutex > & guard )
|
|
{
|
|
guard.assertIdenticalMutex ( this->mutex );
|
|
if ( ! ( this->shuttingDown || this->beaconAnomaly || this->probeResponsePending ) ) {
|
|
this->timer.start ( *this, this->period );
|
|
debugPrintf ( ("saw a normal beacon - resetting circuit receive watchdog\n") );
|
|
}
|
|
}
|
|
|
|
//
|
|
// be careful about using beacons to reset the connection
|
|
// time out watchdog until we have received a ping response
|
|
// from the IOC (this makes the software detect reconnects
|
|
// faster when the server is rebooted twice in rapid
|
|
// succession before a 1st or 2nd beacon has been received)
|
|
//
|
|
void tcpRecvWatchdog::beaconAnomalyNotify (
|
|
epicsGuard < epicsMutex > & guard )
|
|
{
|
|
guard.assertIdenticalMutex ( this->mutex );
|
|
this->beaconAnomaly = true;
|
|
debugPrintf ( ("Saw an abnormal beacon\n") );
|
|
}
|
|
|
|
void tcpRecvWatchdog::messageArrivalNotify (
|
|
epicsGuard < epicsMutex > & guard )
|
|
{
|
|
guard.assertIdenticalMutex ( this->mutex );
|
|
|
|
if ( ! ( this->shuttingDown || this->probeResponsePending ) ) {
|
|
this->beaconAnomaly = false;
|
|
this->timer.start ( *this, this->period );
|
|
debugPrintf ( ("received a message - resetting circuit recv watchdog\n") );
|
|
}
|
|
}
|
|
|
|
void tcpRecvWatchdog::probeResponseNotify (
|
|
epicsGuard < epicsMutex > & cbGuard )
|
|
{
|
|
bool restartNeeded = false;
|
|
double restartDelay = DBL_MAX;
|
|
{
|
|
epicsGuard < epicsMutex > guard ( this->mutex );
|
|
if ( this->probeResponsePending && ! this->shuttingDown ) {
|
|
restartNeeded = true;
|
|
if ( this->probeTimeoutDetected ) {
|
|
this->probeTimeoutDetected = false;
|
|
this->probeResponsePending = this->iiu.setEchoRequestPending ( guard );
|
|
restartDelay = CA_ECHO_TIMEOUT;
|
|
debugPrintf ( ("late probe response - sending another probe request\n") );
|
|
}
|
|
else {
|
|
this->probeResponsePending = false;
|
|
restartDelay = this->period;
|
|
this->iiu.responsiveCircuitNotify ( cbGuard, guard );
|
|
debugPrintf ( ("probe response on time - circuit was tagged reponsive if unresponsive\n") );
|
|
}
|
|
}
|
|
}
|
|
if ( restartNeeded ) {
|
|
this->timer.start ( *this, restartDelay );
|
|
debugPrintf ( ("recv wd restarted with delay %f\n", restartDelay) );
|
|
}
|
|
}
|
|
|
|
//
|
|
// The thread for outgoing requests in the client runs
|
|
// at a higher priority than the thread in the client
|
|
// that receives responses. Therefore, there could
|
|
// be considerable large array write send backlog that
|
|
// is delaying departure of an echo request and also
|
|
// interrupting delivery of an echo response.
|
|
// We must be careful not to timeout the echo response as
|
|
// long as we see indication of regular departures of
|
|
// message buffers from the client in a situation where
|
|
// we know that the TCP send queueing has been exceeded.
|
|
// The send watchdog will be responsible for detecting
|
|
// dead connections in this case.
|
|
//
|
|
void tcpRecvWatchdog::sendBacklogProgressNotify (
|
|
epicsGuard < epicsMutex > & guard )
|
|
{
|
|
guard.assertIdenticalMutex ( this->mutex );
|
|
|
|
// We don't set "beaconAnomaly" to be false here because, after we see a
|
|
// beacon anomaly (which could be transiently detecting a reboot) we will
|
|
// not trust the beacon as an indicator of a healthy server until we
|
|
// receive at least one message from the server.
|
|
if ( this->probeResponsePending && ! this->shuttingDown ) {
|
|
this->timer.start ( *this, CA_ECHO_TIMEOUT );
|
|
debugPrintf ( ("saw heavy send backlog - resetting circuit recv watchdog\n") );
|
|
}
|
|
}
|
|
|
|
void tcpRecvWatchdog::connectNotify (
|
|
epicsGuard < epicsMutex > & guard )
|
|
{
|
|
guard.assertIdenticalMutex ( this->mutex );
|
|
if ( this->shuttingDown ) {
|
|
return;
|
|
}
|
|
this->timer.start ( *this, this->period );
|
|
debugPrintf ( ("connected to the server - initiating circuit recv watchdog\n") );
|
|
}
|
|
|
|
void tcpRecvWatchdog::sendTimeoutNotify (
|
|
epicsGuard < epicsMutex > & /* cbGuard */,
|
|
epicsGuard < epicsMutex > & guard )
|
|
{
|
|
guard.assertIdenticalMutex ( this->mutex );
|
|
|
|
bool restartNeeded = false;
|
|
if ( ! ( this->probeResponsePending || this->shuttingDown ) ) {
|
|
this->probeResponsePending = this->iiu.setEchoRequestPending ( guard );
|
|
restartNeeded = true;
|
|
}
|
|
if ( restartNeeded ) {
|
|
this->timer.start ( *this, CA_ECHO_TIMEOUT );
|
|
}
|
|
debugPrintf ( ("TCP send timed out - sending echo request\n") );
|
|
}
|
|
|
|
void tcpRecvWatchdog::cancel ()
|
|
{
|
|
this->timer.cancel ();
|
|
debugPrintf ( ("canceling TCP recv watchdog\n") );
|
|
}
|
|
|
|
void tcpRecvWatchdog::shutdown ()
|
|
{
|
|
{
|
|
epicsGuard < epicsMutex > guard ( this->mutex );
|
|
this->shuttingDown = true;
|
|
}
|
|
this->timer.cancel ();
|
|
debugPrintf ( ("canceling TCP recv watchdog\n") );
|
|
}
|
|
|
|
double tcpRecvWatchdog::delay () const
|
|
{
|
|
return this->timer.getExpireDelay ();
|
|
}
|
|
|
|
void tcpRecvWatchdog::show ( unsigned level ) const
|
|
{
|
|
epicsGuard < epicsMutex > guard ( this->mutex );
|
|
|
|
::printf ( "Receive virtual circuit watchdog at %p, period %f\n",
|
|
static_cast <const void *> ( this ), this->period );
|
|
if ( level > 0u ) {
|
|
::printf ( "\t%s %s %s\n",
|
|
this->probeResponsePending ? "probe-response-pending" : "",
|
|
this->beaconAnomaly ? "beacon-anomaly-detected" : "",
|
|
this->probeTimeoutDetected ? "probe-response-timeout" : "" );
|
|
}
|
|
}
|
|
|