Files
pcas/src/ca/virtualCircuit.h
2001-04-20 00:50:19 +00:00

500 lines
14 KiB
C++

/*
* $Id$
*
*
* 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 Jeffrey O. Hill
* johill@lanl.gov
* 505 665 1831
*/
#ifndef virtualCircuith
#define virtualCircuith
#include <new> // needed by comQueueSend
#include "epicsTimer.h"
#include "ipAddrToAsciiAsynchronous.h"
#include "comBuf.h"
#include "netiiu.h"
enum iiu_conn_state {iiu_connecting, iiu_connected, iiu_disconnected};
class nciu;
class tcpiiu;
class bufferReservoir {
public:
~bufferReservoir ();
void addOneBuffer ();
comBuf *fetchOneBuffer ();
unsigned nBytes ();
void drain ();
private:
tsDLList < comBuf > reservedBufs;
};
class comQueSend {
public:
comQueSend ( wireSendAdapter & );
~comQueSend ();
void clear ();
void reserveSpace ( unsigned msgSize );
unsigned occupiedBytes () const;
bool flushEarlyThreshold ( unsigned nBytesThisMsg ) const;
bool flushBlockThreshold ( unsigned nBytesThisMsg ) const;
bool dbr_type_ok ( unsigned type );
void pushUInt16 ( const ca_uint16_t value );
void pushUInt32 ( const ca_uint32_t value );
void pushFloat32 ( const ca_float32_t value );
void pushString ( const char *pVal, unsigned nElem );
void push_dbr_type ( unsigned type, const void *pVal, unsigned nElem );
comBuf * popNextComBufToSend ();
private:
wireSendAdapter & wire;
tsDLList < comBuf > bufs;
bufferReservoir reservoir;
unsigned nBytesPending;
void copy_dbr_string ( const void *pValue, unsigned nElem );
void copy_dbr_short ( const void *pValue, unsigned nElem );
void copy_dbr_float ( const void *pValue, unsigned nElem );
void copy_dbr_char ( const void *pValue, unsigned nElem );
void copy_dbr_long ( const void *pValue, unsigned nElem );
void copy_dbr_double ( const void *pValue, unsigned nElem );
typedef void ( comQueSend::*copyFunc_t ) (
const void *pValue, unsigned nElem );
static const copyFunc_t dbrCopyVector [39];
};
static const unsigned maxBytesPendingTCP = 0x4000;
class comQueRecv {
public:
comQueRecv ();
~comQueRecv ();
unsigned occupiedBytes () const;
bool copyOutBytes ( void *pBuf, unsigned nBytes );
void pushLastComBufReceived ( comBuf & );
void clear ();
private:
tsDLList < comBuf > bufs;
};
class tcpRecvWatchdog : private epicsTimerNotify {
public:
tcpRecvWatchdog ( tcpiiu &, double periodIn, epicsTimerQueue & queueIn );
virtual ~tcpRecvWatchdog ();
void rescheduleRecvTimer ();
void messageArrivalNotify ();
void beaconArrivalNotify ();
void beaconAnomalyNotify ();
void connectNotify ();
void cancel ();
void show ( unsigned level ) const;
private:
const double period;
epicsTimer &timer;
tcpiiu &iiu;
bool responsePending;
bool beaconAnomaly;
expireStatus expire ();
};
class tcpSendWatchdog : private epicsTimerNotify {
public:
tcpSendWatchdog ( tcpiiu &, double periodIn, epicsTimerQueue & queueIn );
virtual ~tcpSendWatchdog ();
void start ();
void cancel ();
private:
const double period;
epicsTimer &timer;
tcpiiu &iiu;
expireStatus expire ();
};
class hostNameCache : public ipAddrToAsciiAsynchronous {
public:
hostNameCache ( const osiSockAddr &addr, ipAddrToAsciiEngine &engine );
void destroy ();
void ioCompletionNotify ( const char *pHostName );
void hostName ( char *pBuf, unsigned bufLength ) const;
void * operator new ( size_t size );
void operator delete ( void *pCadaver, size_t size );
protected:
~hostNameCache ();
private:
bool ioComplete;
char hostNameBuf [128];
static tsFreeList < class hostNameCache, 16 > freeList;
static epicsMutex freeListMutex;
};
extern "C" void cacSendThreadTCP ( void *pParam );
extern "C" void cacRecvThreadTCP ( void *pParam );
class tcpiiu :
public netiiu, public tsDLNode < tcpiiu >,
private wireSendAdapter, private wireRecvAdapter {
public:
tcpiiu ( cac &cac, double connectionTimeout, epicsTimerQueue &timerQueue );
~tcpiiu ();
bool initiateConnect ( const osiSockAddr &addrIn, unsigned minorVersion,
class bhe &bhe, ipAddrToAsciiEngine &engineIn );
void connect ();
void processIncoming ();
void destroy ();
void cleanShutdown ();
void forcedShutdown ();
void beaconAnomalyNotify ();
void beaconArrivalNotify ();
bool fullyConstructed () const;
void flushRequest ();
bool flushBlockThreshold () const;
void flushRequestIfAboveEarlyThreshold ();
void blockUntilSendBacklogIsReasonable ( epicsMutex & );
virtual void show ( unsigned level ) const;
bool setEchoRequestPending ();
bool ca_v41_ok () const;
bool ca_v42_ok () const;
bool ca_v44_ok () const;
void hostName ( char *pBuf, unsigned bufLength ) const;
const char * pHostName () const; // deprecated - please do not use
bool isVirtaulCircuit ( const char *pChannelName, const osiSockAddr &addr ) const;
bool alive () const;
double beaconPeriod () const;
bhe * getBHE () const;
SOCKET getSock() const;
bool trueOnceOnly ();
private:
tcpRecvWatchdog recvDog;
tcpSendWatchdog sendDog;
comQueSend sendQue;
comQueRecv recvQue;
osiSockAddr addr;
hostNameCache *pHostNameCache;
caHdr curMsg;
unsigned long curDataMax;
class bhe *pBHE;
char *pCurData;
unsigned minorProtocolVersion;
iiu_conn_state state;
epicsEventId sendThreadFlushSignal;
epicsEventId recvThreadRingBufferSpaceAvailableSignal;
epicsEventId sendThreadExitSignal;
epicsEventId recvThreadExitSignal;
epicsEventId flushBlockSignal;
SOCKET sock;
unsigned contigRecvMsgCount;
unsigned blockingForFlush;
bool fullyConstructedFlag;
bool busyStateDetected; // only modified by the recv thread
bool flowControlActive; // only modified by the send process thread
bool echoRequestPending;
bool msgHeaderAvailable;
bool sockCloseCompleted;
bool f_trueOnceOnly;
bool earlyFlush;
unsigned sendBytes ( const void *pBuf, unsigned nBytesInBuf );
unsigned recvBytes ( void *pBuf, unsigned nBytesInBuf );
friend void cacSendThreadTCP ( void *pParam );
friend void cacRecvThreadTCP ( void *pParam );
void lastChannelDetachNotify ();
// send protocol stubs
void echoRequest ();
void noopRequest ();
void disableFlowControlRequest ();
void enableFlowControlRequest ();
void hostNameSetRequest ();
void userNameSetRequest ();
void writeRequest ( nciu &, unsigned type, unsigned nElem, const void *pValue );
void writeNotifyRequest ( nciu &, netWriteNotifyIO &, unsigned type, unsigned nElem, const void *pValue );
void readNotifyRequest ( nciu &, netReadNotifyIO &, unsigned type, unsigned nElem );
void createChannelRequest ( nciu & );
void clearChannelRequest ( nciu & );
void subscriptionRequest ( nciu &, netSubscription &subscr );
void subscriptionCancelRequest ( nciu &, netSubscription &subscr );
bool flush (); // only to be called by the send thread
};
inline bufferReservoir::~bufferReservoir ()
{
this->drain ();
}
inline comBuf *bufferReservoir::fetchOneBuffer ()
{
return this->reservedBufs.get ();
}
inline void bufferReservoir::addOneBuffer ()
{
comBuf *pBuf = new comBuf;
if ( ! pBuf ) {
throw std::bad_alloc();
}
this->reservedBufs.add ( *pBuf );
}
inline unsigned bufferReservoir::nBytes ()
{
return ( this->reservedBufs.count () * comBuf::capacityBytes () );
}
inline void bufferReservoir::drain ()
{
comBuf *pBuf;
while ( ( pBuf = this->reservedBufs.get () ) ) {
pBuf->destroy ();
}
}
inline bool comQueSend::dbr_type_ok ( unsigned type )
{
if ( type >= ( sizeof ( this->dbrCopyVector ) / sizeof ( this->dbrCopyVector[0] ) ) ) {
return false;
}
if ( ! this->dbrCopyVector [type] ) {
return false;
}
return true;
}
//
// 1) This routine does not return status because of the following
// argument. The routine can fail because the wire disconnects or
// because their isnt memory to create a buffer. For the former we
// just discard the message, but do not fail. For the latter we
// shutdown() the connection and discard the rest of the message
// (this eliminates the possibility of message fragments getting
// onto the wire).
//
// 2) Arguments here are a bit verbose until compilers all implement
// member template functions.
//
template < class T >
inline void comQueSend_copyIn ( unsigned &nBytesPending,
tsDLList < comBuf > &comBufList, bufferReservoir &reservoir,
const T *pVal, unsigned nElem )
{
nBytesPending += sizeof ( T ) * nElem;
comBuf *pComBuf = comBufList.last ();
if ( pComBuf ) {
unsigned nCopied = pComBuf->copyIn ( pVal, nElem );
if ( nElem > nCopied ) {
comQueSend_copyInWithReservour ( comBufList, reservoir, &pVal[nCopied],
nElem - nCopied );
}
}
else {
comQueSend_copyInWithReservour ( comBufList, reservoir, pVal, nElem );
}
}
template < class T >
void comQueSend_copyInWithReservour (
tsDLList < comBuf > &comBufList, bufferReservoir &reservoir,
const T *pVal, unsigned nElem )
{
unsigned nCopied = 0u;
while ( nElem > nCopied ) {
comBuf *pComBuf = reservoir.fetchOneBuffer ();
//
// This fails only if space was not preallocated.
// See comments at the top of this program on
// why space must always be preallocated.
//
assert ( pComBuf );
nCopied += pComBuf->copyIn ( &pVal[nCopied], nElem - nCopied );
comBufList.add ( *pComBuf );
}
}
template < class T >
inline void comQueSend_copyIn ( unsigned &nBytesPending,
tsDLList < comBuf > &comBufList, bufferReservoir &reservoir,
const T &val )
{
nBytesPending += sizeof ( T );
comBuf *pComBuf = comBufList.last ();
if ( pComBuf ) {
if ( pComBuf->copyIn ( &val, 1u ) >= 1u ) {
return;
}
}
pComBuf = reservoir.fetchOneBuffer ();
//
// This fails only if space was not preallocated.
// See comments at the top of this program on
// space must always be preallocated.
//
assert ( pComBuf );
pComBuf->copyIn ( &val, 1u );
comBufList.add ( *pComBuf );
}
inline void comQueSend::pushUInt16 ( const ca_uint16_t value )
{
comQueSend_copyIn ( this->nBytesPending,
this->bufs, this->reservoir, value );
}
inline void comQueSend::pushUInt32 ( const ca_uint32_t value )
{
comQueSend_copyIn ( this->nBytesPending,
this->bufs, this->reservoir, value );
}
inline void comQueSend::pushFloat32 ( const ca_float32_t value )
{
comQueSend_copyIn ( this->nBytesPending,
this->bufs, this->reservoir, value );
}
inline void comQueSend::pushString ( const char *pVal, unsigned nElem )
{
comQueSend_copyIn ( this->nBytesPending,
this->bufs, this->reservoir, pVal, nElem );
}
// it is assumed that dbr_type_ok() was called prior to calling this routine
// to check the type code
inline void comQueSend::push_dbr_type ( unsigned type, const void *pVal, unsigned nElem )
{
( this->*dbrCopyVector [type] ) ( pVal, nElem );
}
inline unsigned comQueSend::occupiedBytes () const
{
return this->nBytesPending;
}
inline bool comQueSend::flushBlockThreshold ( unsigned nBytesThisMsg ) const
{
return ( this->nBytesPending + nBytesThisMsg > 16 * comBuf::capacityBytes () );
}
inline bool comQueSend::flushEarlyThreshold ( unsigned nBytesThisMsg ) const
{
return ( this->nBytesPending + nBytesThisMsg > 4 * comBuf::capacityBytes () );
}
inline comBuf * comQueSend::popNextComBufToSend ()
{
comBuf *pBuf = this->bufs.get ();
if ( pBuf ) {
unsigned nBytesThisBuf = pBuf->occupiedBytes ();
assert ( this->nBytesPending >= nBytesThisBuf );
this->nBytesPending -= pBuf->occupiedBytes ();
}
else {
assert ( this->nBytesPending == 0u );
}
return pBuf;
}
inline bool tcpiiu::fullyConstructed () const
{
return this->fullyConstructedFlag;
}
inline void tcpiiu::hostName ( char *pBuf, unsigned bufLength ) const
{
if ( this->pHostNameCache ) {
this->pHostNameCache->hostName ( pBuf, bufLength );
}
else {
netiiu::hostName ( pBuf, bufLength );
}
}
// deprecated - please dont use - this is _not_ thread safe
inline const char * tcpiiu::pHostName () const
{
static char nameBuf [128];
this->hostName ( nameBuf, sizeof ( nameBuf ) );
return nameBuf; // ouch !!
}
inline void tcpiiu::flushRequest ()
{
epicsEventSignal ( this->sendThreadFlushSignal );
}
inline bool tcpiiu::ca_v44_ok () const
{
return CA_V44 ( CA_PROTOCOL_VERSION, this->minorProtocolVersion );
}
inline bool tcpiiu::ca_v41_ok () const
{
return CA_V41 ( CA_PROTOCOL_VERSION, this->minorProtocolVersion );
}
inline bool tcpiiu::alive () const
{
if ( this->state == iiu_connecting ||
this->state == iiu_connected ) {
return true;
}
else {
return false;
}
}
inline bhe * tcpiiu::getBHE () const
{
return this->pBHE;
}
inline void tcpiiu::beaconAnomalyNotify ()
{
this->recvDog.beaconAnomalyNotify ();
}
inline void tcpiiu::beaconArrivalNotify ()
{
this->recvDog.beaconArrivalNotify ();
}
inline bool tcpiiu::trueOnceOnly ()
{
if ( this->f_trueOnceOnly ) {
this->f_trueOnceOnly = false;
return true;
}
else {
return false;
}
}
inline SOCKET tcpiiu::getSock () const
{
return this->sock;
}
#endif // ifdef virtualCircuith