diff --git a/src/ca/CASG.cpp b/src/ca/CASG.cpp index 5578c32bb..ed2ce7b2d 100644 --- a/src/ca/CASG.cpp +++ b/src/ca/CASG.cpp @@ -153,7 +153,7 @@ bool CASG::ioComplete () const return ( this->ioList.count () == 0u ); } -int CASG::put ( chid pChan, unsigned type, unsigned long count, const void *pValue ) +int CASG::put ( chid pChan, unsigned type, arrayElementCount count, const void *pValue ) { try { epicsAutoMutex locker ( this->mutex ); @@ -191,7 +191,7 @@ int CASG::put ( chid pChan, unsigned type, unsigned long count, const void *pVal { return ECA_NOTINSERVICE; } - catch ( cacChannel::noMemory & ) + catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } @@ -201,7 +201,7 @@ int CASG::put ( chid pChan, unsigned type, unsigned long count, const void *pVal } } -int CASG::get ( chid pChan, unsigned type, unsigned long count, void *pValue ) +int CASG::get ( chid pChan, unsigned type, arrayElementCount count, void *pValue ) { try { @@ -240,7 +240,7 @@ int CASG::get ( chid pChan, unsigned type, unsigned long count, void *pValue ) { return ECA_NOTINSERVICE; } - catch ( cacChannel::noMemory & ) + catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } @@ -300,4 +300,4 @@ int CASG::printf ( const char *pformat, ... ) return status; } - + diff --git a/src/ca/access.cpp b/src/ca/access.cpp index 9d43f2058..28e3bcf96 100644 --- a/src/ca/access.cpp +++ b/src/ca/access.cpp @@ -12,6 +12,8 @@ * */ +#include + #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" @@ -81,32 +83,6 @@ int fetchClientContext ( oldCAC **ppcac ) return status; } -/* - * Default Exception Handler - */ -extern "C" void ca_default_exception_handler ( struct exception_handler_args args ) -{ - if ( args.chid && args.op != CA_OP_OTHER ) { - ca_signal_formated ( - args.stat, - args.pFile, - args.lineNo, - "%s - with request chan=%s op=%ld data type=%s count=%ld", - args.ctx, - ca_name ( args.chid ), - args.op, - dbr_type_to_text ( args.type ), - args.count ); - } - else { - ca_signal_formated ( - args.stat, - args.pFile, - args.lineNo, - args.ctx ); - } -} - /* * ca_task_initialize () */ @@ -211,7 +187,7 @@ extern "C" int epicsShareAPI ca_task_exit () * backwards compatible entry point to ca_search_and_connect() */ extern "C" int epicsShareAPI ca_build_and_connect ( const char *name_str, chtype get_type, - unsigned long get_count, chid * chan, void *pvalue, + arrayElementCount get_count, chid * chan, void *pvalue, caCh *conn_func, void *puser ) { if ( get_type != TYPENOTCONN && pvalue != 0 && get_count != 0 ) { @@ -269,7 +245,7 @@ extern "C" int epicsShareAPI ca_clear_channel ( chid pChan ) * ca_array_get () */ extern "C" int epicsShareAPI ca_array_get ( chtype type, - unsigned long count, chid pChan, void *pValue ) + arrayElementCount count, chid pChan, void *pValue ) { oldCAC *pcac; int caStatus = fetchClientContext ( &pcac ); @@ -283,7 +259,7 @@ extern "C" int epicsShareAPI ca_array_get ( chtype type, unsigned tmpType = static_cast < unsigned > ( type ); autoPtrDestroy < getCopy > pNotify - ( new getCopy ( *pcac, tmpType, count, pValue ) ); + ( new getCopy ( *pcac, *pChan, tmpType, count, pValue ) ); if ( ! pNotify.get() ) { return ECA_ALLOCMEM; } @@ -317,10 +293,13 @@ extern "C" int epicsShareAPI ca_array_get ( chtype type, { return ECA_NOTINSERVICE; } - catch ( cacChannel::noMemory & ) + catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } + catch ( cacChannel::msgBodyCacheTooSmall ) { + return ECA_TOLARGE; + } catch ( ... ) { return ECA_GETFAIL; @@ -331,7 +310,7 @@ extern "C" int epicsShareAPI ca_array_get ( chtype type, * ca_array_get_callback () */ extern "C" int epicsShareAPI ca_array_get_callback ( chtype type, - unsigned long count, chid pChan, + arrayElementCount count, chid pChan, caEventCallBackFunc *pfunc, void *arg ) { if ( type < 0 ) { @@ -374,10 +353,13 @@ extern "C" int epicsShareAPI ca_array_get_callback ( chtype type, { return ECA_NOTINSERVICE; } - catch ( cacChannel::noMemory & ) + catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } + catch ( cacChannel::msgBodyCacheTooSmall ) { + return ECA_TOLARGE; + } catch ( ... ) { return ECA_GETFAIL; @@ -387,7 +369,7 @@ extern "C" int epicsShareAPI ca_array_get_callback ( chtype type, /* * ca_array_put_callback () */ -extern "C" int epicsShareAPI ca_array_put_callback ( chtype type, unsigned long count, +extern "C" int epicsShareAPI ca_array_put_callback ( chtype type, arrayElementCount count, chid pChan, const void *pValue, caEventCallBackFunc *pfunc, void *usrarg ) { if ( type < 0 ) { @@ -430,7 +412,7 @@ extern "C" int epicsShareAPI ca_array_put_callback ( chtype type, unsigned long { return ECA_NOTINSERVICE; } - catch ( cacChannel::noMemory & ) + catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } @@ -443,7 +425,7 @@ extern "C" int epicsShareAPI ca_array_put_callback ( chtype type, unsigned long /* * ca_array_put () */ -extern "C" int epicsShareAPI ca_array_put ( chtype type, unsigned long count, +extern "C" int epicsShareAPI ca_array_put ( chtype type, arrayElementCount count, chid pChan, const void *pValue ) { if ( type < 0 ) { @@ -479,7 +461,7 @@ extern "C" int epicsShareAPI ca_array_put ( chtype type, unsigned long count, { return ECA_NOTINSERVICE; } - catch ( cacChannel::noMemory & ) + catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } @@ -526,7 +508,7 @@ extern "C" int epicsShareAPI ca_add_exception_event ( caExceptionHandler *pfunc, * ca_add_masked_array_event */ extern "C" int epicsShareAPI ca_add_masked_array_event ( - chtype type, unsigned long count, chid pChan, + chtype type, arrayElementCount count, chid pChan, caEventCallBackFunc *pCallBack, void *pCallBackArg, ca_real, ca_real, ca_real, evid *monixptr, long mask ) @@ -553,20 +535,6 @@ extern "C" int epicsShareAPI ca_add_masked_array_event ( return ECA_BADMASK; } - /* - * Check for huge waveform - * - * (the count is not checked here against the native count - * when connected because this introduces a race condition - * for the client tool - the requested count is clipped to - * the actual count when the monitor request is sent so - * verifying that the requested count is valid here isnt - * required) - */ - if ( dbr_size_n ( type, count ) > MAX_MSG_SIZE - sizeof ( caHdr ) ) { - return ECA_TOLARGE; - } - try { autoPtrDestroy < oldSubscription > pSubsr ( new oldSubscription ( @@ -600,10 +568,13 @@ extern "C" int epicsShareAPI ca_add_masked_array_event ( { return ECA_NOTINSERVICE; } - catch ( cacChannel::noMemory & ) + catch ( std::bad_alloc & ) { return ECA_ALLOCMEM; } + catch ( cacChannel::msgBodyCacheTooSmall ) { + return ECA_TOLARGE; + } catch ( ... ) { return ECA_INTERNAL; @@ -732,56 +703,24 @@ extern "C" void epicsShareAPI ca_signal_formated ( long ca_status, const char *p int lineno, const char *pFormat, ... ) { oldCAC *pcac; - va_list theArgs; - static const char *severity[] = - { - "Warning", - "Success", - "Error", - "Info", - "Fatal", - "Fatal", - "Fatal", - "Fatal" - }; if ( caClientContextId ) { - pcac = (oldCAC *) epicsThreadPrivateGet ( caClientContextId ); + pcac = ( oldCAC * ) epicsThreadPrivateGet ( caClientContextId ); } else { - pcac = NULL; + pcac = 0; } - - va_start ( theArgs, pFormat ); - - pcac->printf ( "CA.Client.Diagnostic..............................................\n" ); - - pcac->printf ( " %s: \"%s\"\n", - severity[ CA_EXTRACT_SEVERITY ( ca_status ) ], - ca_message ( ca_status ) ); - if ( pFormat ) { - pcac->printf ( " Context: \"" ); - pcac->vPrintf ( pFormat, theArgs ); - pcac->printf ( "\"\n" ); + va_list theArgs; + va_start ( theArgs, pFormat ); + if ( pcac ) { + pcac->vSignal ( ca_status, pfilenm, lineno, pFormat, theArgs ); } - - if (pfilenm) { - pcac->printf ( " Source File: %s Line Number: %d\n", - pfilenm, lineno ); + else { + fprintf ( stderr, "file=%s line=%d: CA exception delivered to a thread w/o ca context\n", + pfilenm, lineno ); + vfprintf ( stderr, pFormat, theArgs ); } - - /* - * Terminate execution if unsuccessful - */ - if( ! ( ca_status & CA_M_SUCCESS ) && - CA_EXTRACT_SEVERITY ( ca_status ) != CA_K_WARNING ){ - errlogFlush(); - abort(); - } - - pcac->printf ( "..................................................................\n" ); - va_end ( theArgs ); } @@ -836,7 +775,7 @@ extern "C" int epicsShareAPI ca_v42_ok ( chid pChan ) */ extern "C" const char * epicsShareAPI ca_version () { - return CA_VERSION_STRING; + return CA_VERSION_STRING ( CA_MINOR_PROTOCOL_REVISION ); } /* @@ -866,7 +805,7 @@ extern "C" short epicsShareAPI ca_field_type ( chid pChan ) /* * ca_element_count () */ -extern "C" unsigned long epicsShareAPI ca_element_count ( chid pChan ) +extern "C" arrayElementCount epicsShareAPI ca_element_count ( chid pChan ) { return pChan->nativeElementCount (); } diff --git a/src/ca/acctst.c b/src/ca/acctst.c index 9ead2aef6..6d30ebc90 100644 --- a/src/ca/acctst.c +++ b/src/ca/acctst.c @@ -617,7 +617,7 @@ void performCtrlDoubleTest (chid chan) ca_element_count(chan), chan, pCtrlDbl); SEVCHK (status, "performCtrlDoubleTest, ca_array_get"); - status = ca_pend_io (20.0); + status = ca_pend_io (30.0); assert (status==ECA_NORMAL); /* @@ -1246,17 +1246,37 @@ void eventClearTest ( chid chan ) SEVCHK ( status, NULL); } + /* * array test * * verify that we can at least write and read back the same array * if multiple elements are present */ +static arrayReadNotifyComplete = 0; +static arrayWriteNotifyComplete = 0; +void arrayReadNotify ( struct event_handler_args args ) +{ + dbr_double_t *pWF = ( dbr_double_t * ) ( args.usr ); + dbr_double_t *pRF = ( dbr_double_t * ) ( args.dbr ); + int i; + for ( i = 0; i < args.count; i++ ) { + if ( pWF[i] != pRF[i] ) { + assert ( 0 ); + } + } + arrayReadNotifyComplete = 1; +} +void arrayWriteNotify ( struct event_handler_args args ) +{ + arrayWriteNotifyComplete = 1; +} void arrayTest ( chid chan ) { dbr_double_t *pRF, *pWF; unsigned i; int status; + evid id; if ( ! ca_write_access ( chan ) ) { printf ( "skipping array test - no write access\n" ); @@ -1293,7 +1313,7 @@ void arrayTest ( chid chan ) /* * verify read response matches values written */ - for ( i = 0; i < ca_element_count (chan); i++ ) { + for ( i = 0; i < ca_element_count ( chan ); i++ ) { assert ( pWF[i] == pRF[i] ); } @@ -1302,26 +1322,55 @@ void arrayTest ( chid chan ) */ { char *pRS; - /* clip to 16k message buffer limit */ - unsigned maxElem = ( ( 1 << 14 ) - 16 ) / MAX_STRING_SIZE; - unsigned nElem; - if ( maxElem > ca_element_count (chan) ) { - nElem = ca_element_count (chan); - } - else { - nElem = maxElem; - } - - pRS = malloc ( nElem * MAX_STRING_SIZE ); + pRS = malloc ( ca_element_count (chan) * MAX_STRING_SIZE ); assert ( pRS ); - status = ca_array_get ( DBR_STRING, nElem, chan, pRS ); + status = ca_array_get ( DBR_STRING, + ca_element_count (chan), chan, pRS ); SEVCHK ( status, "array read request failed" ); status = ca_pend_io ( 30.0 ); SEVCHK ( status, "array read failed" ); free ( pRS ); } + /* + * write some random numbers into the array + */ + for ( i = 0; i < ca_element_count (chan); i++ ) { + pWF[i] = rand (); + pRF[i] = - pWF[i]; + } + status = ca_array_put_callback ( DBR_DOUBLE, ca_element_count ( chan ), + chan, pWF, arrayWriteNotify, 0 ); + SEVCHK ( status, "array write notify request failed" ); + status = ca_array_get_callback ( DBR_DOUBLE, ca_element_count (chan), + chan, arrayReadNotify, pWF ); + SEVCHK ( status, "array read notify request failed" ); + + while ( ! arrayWriteNotifyComplete && ! arrayReadNotifyComplete ) { + ca_pend_event ( 0.1 ); + } + + /* + * write some random numbers into the array + */ + for ( i = 0; i < ca_element_count (chan); i++ ) { + pWF[i] = rand (); + pRF[i] = - pWF[i]; + } + arrayReadNotifyComplete = 0; + status = ca_array_put ( DBR_DOUBLE, ca_element_count ( chan ), + chan, pWF ); + SEVCHK ( status, "array write notify request failed" ); + status = ca_add_array_event ( DBR_DOUBLE, ca_element_count ( chan ), + chan, arrayReadNotify, pWF, 0.0, 0.0, 0.0, &id ); + SEVCHK ( status, "array subscription request failed" ); + while ( ! arrayReadNotifyComplete ) { + ca_pend_event ( 0.1 ); + } + status = ca_clear_event ( id ); + SEVCHK ( status, "clear event request failed" ); + free ( pRF ); free ( pWF ); @@ -1629,6 +1678,8 @@ int acctst ( char *pName, unsigned channelCount, unsigned repetitionCount ) printf ( "CA Client V%s, channel name \"%s\"\n", ca_version (), pName ); + putenv ( "EPICS_CA_MAX_ARRAY_BYTES=10000000" ); + verifyDataTypeMacros (); connections = ca_get_ioc_connection_count (); @@ -1646,6 +1697,7 @@ int acctst ( char *pName, unsigned channelCount, unsigned repetitionCount ) printf ( "testing with a local channel\n" ); } + arrayTest ( chan ); verifyMonitorSubscriptionFlushIO ( chan ); monitorSubscriptionFirstUpdateTest ( chan ); performGrEnumTest ( chan ); @@ -1665,7 +1717,6 @@ int acctst ( char *pName, unsigned channelCount, unsigned repetitionCount ) test_sync_groups ( chan ); performDeleteTest ( chan ); eventClearTest ( chan ); - arrayTest ( chan ); performMonitorUpdateTest ( chan ); /* diff --git a/src/ca/bhe.cpp b/src/ca/bhe.cpp index 99925b39b..e95bf5a52 100644 --- a/src/ca/bhe.cpp +++ b/src/ca/bhe.cpp @@ -43,11 +43,11 @@ bhe::~bhe () * * updates beacon period, and looks for beacon anomalies */ -bool bhe::updatePeriod ( epicsTime programBeginTime ) +bool bhe::updatePeriod ( const epicsTime & programBeginTime, + const epicsTime & currentTime ) { double currentPeriod; bool netChange = false; - epicsTime current = epicsTime::getCurrent (); if ( this->timeStamp == epicsTime () ) { @@ -61,7 +61,7 @@ bool bhe::updatePeriod ( epicsTime programBeginTime ) * a TCP/IP connection created the beacon. * (nothing to do but set the beacon time stamp and return) */ - this->timeStamp = current; + this->timeStamp = currentTime; return netChange; } @@ -69,7 +69,7 @@ bool bhe::updatePeriod ( epicsTime programBeginTime ) /* * compute the beacon period (if we have seen at least two beacons) */ - currentPeriod = current - this->timeStamp; + currentPeriod = currentTime - this->timeStamp; if ( this->averagePeriod < 0.0 ) { double totalRunningTime; @@ -158,7 +158,7 @@ bool bhe::updatePeriod ( epicsTime programBeginTime ) this->averagePeriod = currentPeriod * 0.125 + this->averagePeriod * 0.875; } - this->timeStamp = current; + this->timeStamp = currentTime; return netChange; } diff --git a/src/ca/bhe.h b/src/ca/bhe.h index b1f034851..ce64c9116 100644 --- a/src/ca/bhe.h +++ b/src/ca/bhe.h @@ -40,8 +40,10 @@ public: epicsShareFunc bhe ( const epicsTime &initialTimeStamp, const inetAddrID &addr ); tcpiiu *getIIU () const; void bindToIIU ( tcpiiu & ); + void unbindFromIIU (); epicsShareFunc void destroy (); - epicsShareFunc bool updatePeriod ( epicsTime programBeginTime ); + epicsShareFunc bool updatePeriod ( const epicsTime &programBeginTime, + const epicsTime & currentTime ); epicsShareFunc double period () const; epicsShareFunc void show ( unsigned level) const; epicsShareFunc void * operator new ( size_t size ); @@ -71,13 +73,14 @@ private: * between the 1st and 2nd beacons) */ inline bhe::bhe ( const epicsTime &initialTimeStamp, const inetAddrID &addr ) : - inetAddrID (addr), piiu (0), timeStamp (initialTimeStamp), averagePeriod ( - DBL_MAX ) + inetAddrID ( addr ), piiu ( 0 ), + timeStamp ( initialTimeStamp ), averagePeriod ( - DBL_MAX ) { # ifdef DEBUG { char name[64]; - ipAddrToDottedIP (&addr, name, sizeof(name)); - ::printf ("created beacon entry for %s\n", name); + addr.name ( name, sizeof ( name ) ); + ::printf ( "created beacon entry for %s\n", name ); } # endif } @@ -95,6 +98,13 @@ inline void bhe::bindToIIU ( tcpiiu &iiuIn ) } } +inline void bhe::unbindFromIIU () +{ + this->piiu = 0; + this->timeStamp = epicsTime(); + this->averagePeriod = - DBL_MAX; +} + #endif // ifdef bheh diff --git a/src/ca/caProto.h b/src/ca/caProto.h index 9bb6d7c63..68dbeb1b9 100644 --- a/src/ca/caProto.h +++ b/src/ca/caProto.h @@ -35,55 +35,57 @@ #define capStrOfX(A) capStrOf ( A ) /* - * CA protocol number + * CA protocol revision * TCP/UDP port number (bumped each major protocol change) */ -#define CA_PROTOCOL_VERSION 4 -#define CA_MINOR_VERSION 8 -#define CA_VERSION_STRING \ -( capStrOfX ( CA_PROTOCOL_VERSION ) "." capStrOfX ( CA_MINOR_VERSION ) ) +#define CA_MAJOR_PROTOCOL_REVISION 4 +#define CA_VERSION_STRING( MINOR_REVISION ) \ +( capStrOfX ( CA_MAJOR_PROTOCOL_REVISION ) "." capStrOfX ( MINOR_REVISION ) ) #define CA_UKN_MINOR_VERSION 0u /* unknown minor version */ -#if CA_PROTOCOL_VERSION == 4u -# define CA_V41(MAJOR,MINOR) ((MINOR)>=1u) -# define CA_V42(MAJOR,MINOR) ((MINOR)>=2u) -# define CA_V43(MAJOR,MINOR) ((MINOR)>=3u) -# define CA_V44(MAJOR,MINOR) ((MINOR)>=4u) -# define CA_V45(MAJOR,MINOR) ((MINOR)>=5u) -# define CA_V46(MAJOR,MINOR) ((MINOR)>=6u) -# define CA_V47(MAJOR,MINOR) ((MINOR)>=7u) -# define CA_V48(MAJOR,MINOR) ((MINOR)>=8u) -#elif CA_PROTOCOL_VERSION > 4u -# define CA_V41(MAJOR,MINOR) ( 1u ) -# define CA_V42(MAJOR,MINOR) ( 1u ) -# define CA_V43(MAJOR,MINOR) ( 1u ) -# define CA_V44(MAJOR,MINOR) ( 1u ) -# define CA_V45(MAJOR,MINOR) ( 1u ) -# define CA_V46(MAJOR,MINOR) ( 1u ) -# define CA_V47(MAJOR,MINOR) ( 1u ) -# define CA_V48(MAJOR,MINOR) ( 1u ) +#if CA_MAJOR_PROTOCOL_REVISION == 4u +# define CA_V41(MINOR) ((MINOR)>=1u) +# define CA_V42(MINOR) ((MINOR)>=2u) +# define CA_V43(MINOR) ((MINOR)>=3u) +# define CA_V44(MINOR) ((MINOR)>=4u) +# define CA_V45(MINOR) ((MINOR)>=5u) +# define CA_V46(MINOR) ((MINOR)>=6u) +# define CA_V47(MINOR) ((MINOR)>=7u) +# define CA_V48(MINOR) ((MINOR)>=8u) +# define CA_V49(MINOR) ((MINOR)>=9u) +#elif CA_MAJOR_PROTOCOL_REVISION > 4u +# define CA_V41(MINOR) ( 1u ) +# define CA_V42(MINOR) ( 1u ) +# define CA_V43(MINOR) ( 1u ) +# define CA_V44(MINOR) ( 1u ) +# define CA_V45(MINOR) ( 1u ) +# define CA_V46(MINOR) ( 1u ) +# define CA_V47(MINOR) ( 1u ) +# define CA_V48(MINOR) ( 1u ) +# define CA_V49(MINOR) ( 1u ) #else -# define CA_V41(MAJOR,MINOR) ( 0u ) -# define CA_V42(MAJOR,MINOR) ( 0u ) -# define CA_V43(MAJOR,MINOR) ( 0u ) -# define CA_V44(MAJOR,MINOR) ( 0u ) -# define CA_V45(MAJOR,MINOR) ( 0u ) -# define CA_V46(MAJOR,MINOR) ( 0u ) -# define CA_V47(MAJOR,MINOR) ( 0u ) -# define CA_V48(MAJOR,MINOR) ( 0u ) +# define CA_V41(MINOR) ( 0u ) +# define CA_V42(MINOR) ( 0u ) +# define CA_V43(MINOR) ( 0u ) +# define CA_V44(MINOR) ( 0u ) +# define CA_V45(MINOR) ( 0u ) +# define CA_V46(MINOR) ( 0u ) +# define CA_V47(MINOR) ( 0u ) +# define CA_V48(MINOR) ( 0u ) +# define CA_V49(MINOR) ( 0u ) #endif /* - * NOTE: These port numbers are only used if the CA repeater and + * These port numbers are only used if the CA repeater and * CA server port numbers cant be obtained from the EPICS * environment variables "EPICS_CA_REPEATER_PORT" and * "EPICS_CA_SERVER_PORT" */ #define CA_PORT_BASE IPPORT_USERRESERVED + 56U -#define CA_SERVER_PORT (CA_PORT_BASE+CA_PROTOCOL_VERSION*2u) -#define CA_REPEATER_PORT (CA_PORT_BASE+CA_PROTOCOL_VERSION*2u+1u) +#define CA_SERVER_PORT (CA_PORT_BASE+CA_MAJOR_PROTOCOL_REVISION*2u) +#define CA_REPEATER_PORT (CA_PORT_BASE+CA_MAJOR_PROTOCOL_REVISION*2u+1u) /* * 1500 (max of ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP) - * (the MTU of Ethernet is currently independent of speed varient) + * (the MTU of Ethernet is currently independent of its speed varient) */ #define ETHERNET_MAX_UDP ( 1500u - 20u - 8u ) #define MAX_UDP_RECV ( 0xffff + 16u ) /* allow large frames to be received in the future */ @@ -174,20 +176,19 @@ typedef ca_uint32_t caResId; * the common part of each message sent/recv by the * CA server. */ -typedef struct ca_hdr { +typedef struct ca_hdr { ca_uint16_t m_cmmd; /* operation to be performed */ - ca_uint16_t m_postsize; /* size of message extension */ - ca_uint16_t m_dataType; /* operation data type */ + ca_uint16_t m_postsize; /* size of payload */ + ca_uint16_t m_dataType; /* operation data type */ ca_uint16_t m_count; /* operation data count */ ca_uint32_t m_cid; /* channel identifier */ - ca_uint32_t m_available; /* undefined message location for use - * by client processes */ -}caHdr; + ca_uint32_t m_available; /* protocol stub dependent */ +} caHdr; /* * for monitor (event) message extension */ -struct mon_info{ +struct mon_info { ca_float32_t m_lval; /* low delta */ ca_float32_t m_hval; /* high delta */ ca_float32_t m_toval; /* period btween samples */ @@ -195,11 +196,6 @@ struct mon_info{ ca_uint16_t m_pad; /* extend to 32 bits */ }; -struct monops { /* monitor req opi to ioc */ - caHdr m_header; - struct mon_info m_info; -}; - /* * PV names greater than this length assumed to be invalid */ diff --git a/src/ca/cac.cpp b/src/ca/cac.cpp index c7a035437..fba577308 100644 --- a/src/ca/cac.cpp +++ b/src/ca/cac.cpp @@ -34,7 +34,7 @@ #include "net_convert.h" #undef epicsExportSharedSymbols -// TCP protocol jump table +// TCP response dispatch table const cac::pProtoStubTCP cac::tcpJumpTableCAC [] = { &cac::noopAction, @@ -67,6 +67,39 @@ const cac::pProtoStubTCP cac::tcpJumpTableCAC [] = &cac::verifyAndDisconnectChan }; +// TCP exception dispatch table +const cac::pExcepProtoStubTCP cac::tcpExcepJumpTableCAC [] = +{ + &cac::defaultExcep, // CA_PROTO_NOOP + &cac::eventAddExcep, // CA_PROTO_EVENT_ADD + &cac::defaultExcep, // CA_PROTO_EVENT_CANCEL + &cac::readExcep, // CA_PROTO_READ + &cac::writeExcep, // CA_PROTO_WRITE + &cac::defaultExcep, // CA_PROTO_SNAPSHOT + &cac::defaultExcep, // CA_PROTO_SEARCH + &cac::defaultExcep, // CA_PROTO_BUILD + &cac::defaultExcep, // CA_PROTO_EVENTS_OFF + &cac::defaultExcep, // CA_PROTO_EVENTS_ON + &cac::defaultExcep, // CA_PROTO_READ_SYNC + &cac::defaultExcep, // CA_PROTO_ERROR + &cac::defaultExcep, // CA_PROTO_CLEAR_CHANNEL + &cac::defaultExcep, // CA_PROTO_RSRV_IS_UP + &cac::defaultExcep, // CA_PROTO_NOT_FOUND + &cac::readNotifyExcep, // CA_PROTO_READ_NOTIFY + &cac::defaultExcep, // CA_PROTO_READ_BUILD + &cac::defaultExcep, // REPEATER_CONFIRM + &cac::defaultExcep, // CA_PROTO_CLAIM_CIU + &cac::writeNotifyExcep, // CA_PROTO_WRITE_NOTIFY + &cac::defaultExcep, // CA_PROTO_CLIENT_NAME + &cac::defaultExcep, // CA_PROTO_HOST_NAME + &cac::defaultExcep, // CA_PROTO_ACCESS_RIGHTS + &cac::defaultExcep, // CA_PROTO_ECHO + &cac::defaultExcep, // REPEATER_REGISTER + &cac::defaultExcep, // CA_PROTO_SIGNAL + &cac::defaultExcep, // CA_PROTO_CLAIM_CIU_FAILED + &cac::defaultExcep // CA_PROTO_SERVER_DISCONN +}; + // // cac::cac () // @@ -79,10 +112,13 @@ cac::cac ( cacNotify ¬ifyIn, bool enablePreemptiveCallbackIn ) : pudpiiu ( 0 ), pSearchTmr ( 0 ), pRepeaterSubscribeTmr ( 0 ), + tcpSmallRecvBufFreeList ( 0 ), + tcpLargeRecvBufFreeList ( 0 ), notify ( notifyIn ), ioNotifyInProgressId ( 0 ), initializingThreadsPriority ( epicsThreadGetPrioritySelf () ), threadsBlockingOnNotifyCompletion ( 0u ), + maxRecvBytesTCP ( MAX_TCP ), enablePreemptiveCallback ( enablePreemptiveCallbackIn ), ioInProgress ( false ) { @@ -130,9 +166,46 @@ cac::cac ( cacNotify ¬ifyIn, bool enablePreemptiveCallbackIn ) : this->printf ( "Defaulting \"%s\" = %f\n", EPICS_CA_CONN_TMO.name, this->connTMO); } + long maxBytesAsALong; + status = envGetLongConfigParam ( &EPICS_CA_MAX_ARRAY_BYTES, &maxBytesAsALong ); + if ( status || maxBytesAsALong < 0 ) { + errlogPrintf ( "cac: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" ); + } + else { + /* allow room for the protocol header so that they get the array size they requested */ + static const unsigned headerSize = sizeof ( caHdr ) + 2 * sizeof ( ca_uint32_t ); + ca_uint32_t maxBytes = ( unsigned ) maxBytesAsALong; + if ( maxBytes < 0xffffffff - headerSize ) { + maxBytes += headerSize; + } + else { + maxBytes = 0xffffffff; + } + if ( maxBytes < MAX_TCP ) { + errlogPrintf ( "cac: EPICS_CA_MAX_ARRAY_BYTES was rounded up to %u\n", MAX_TCP ); + } + else { + this->maxRecvBytesTCP = maxBytes; + } + } + freeListInitPvt ( &this->tcpSmallRecvBufFreeList, MAX_TCP, 1 ); + if ( ! this->tcpSmallRecvBufFreeList ) { + free ( this->pUserName ); + throwWithLocation ( caErrorCode ( ECA_ALLOCMEM ) ); + } + + freeListInitPvt ( &this->tcpLargeRecvBufFreeList, this->maxRecvBytesTCP, 1 ); + if ( ! this->tcpLargeRecvBufFreeList ) { + free ( this->pUserName ); + freeListCleanup ( this->tcpSmallRecvBufFreeList ); + throwWithLocation ( caErrorCode ( ECA_ALLOCMEM ) ); + } + this->pTimerQueue = & epicsTimerQueueActive::allocate ( false, abovePriority ); if ( ! this->pTimerQueue ) { free ( this->pUserName ); + freeListCleanup ( this->tcpSmallRecvBufFreeList ); + freeListCleanup ( this->tcpLargeRecvBufFreeList ); throwWithLocation ( caErrorCode ( ECA_ALLOCMEM ) ); } @@ -146,6 +219,8 @@ cac::cac ( cacNotify ¬ifyIn, bool enablePreemptiveCallbackIn ) : if ( ! this->pRecvProcThread ) { this->pTimerQueue->release (); free ( this->pUserName ); + freeListCleanup ( this->tcpSmallRecvBufFreeList ); + freeListCleanup ( this->tcpLargeRecvBufFreeList ); throwWithLocation ( caErrorCode ( ECA_ALLOCMEM ) ); } else if ( this->enablePreemptiveCallback ) { @@ -196,6 +271,9 @@ cac::~cac () delete this->pRepeaterSubscribeTmr; delete this->pSearchTmr; + freeListCleanup ( this->tcpSmallRecvBufFreeList ); + freeListCleanup ( this->tcpLargeRecvBufFreeList ); + { epicsAutoMutex autoMutex ( this->mutex ); if ( this->pudpiiu ) { @@ -242,11 +320,7 @@ void cac::processRecvBacklog () if ( ! piiu->alive () ) { assert ( this->pudpiiu && this->pSearchTmr ); - bhe *pBHE = piiu->getBHE (); - if ( pBHE ) { - this->beaconTable.remove ( *pBHE ); - pBHE->destroy (); - } + piiu->getBHE().unbindFromIIU (); if ( piiu->channelCount () ) { char hostNameTmp[64]; @@ -281,10 +355,13 @@ void cac::processRecvBacklog () } } if ( deadIIU.count() ) { - this->pSearchTmr->resetPeriod ( CA_RECAST_DELAY ); while ( tcpiiu *piiu = deadIIU.get() ) { piiu->destroy (); } + { + epicsAutoMutex autoMutex ( this->mutex ); + this->pSearchTmr->resetPeriod ( 0.0 ); + } } } @@ -381,11 +458,9 @@ void cac::signalRecvActivity () /* * cac::beaconNotify */ -void cac::beaconNotify ( const inetAddrID &addr ) +void cac::beaconNotify ( const inetAddrID &addr, const epicsTime ¤tTime ) { epicsAutoMutex autoMutex ( this->mutex ); - unsigned port; - bhe *pBHE; if ( ! this->pudpiiu ) { return; @@ -394,12 +469,12 @@ void cac::beaconNotify ( const inetAddrID &addr ) /* * look for it in the hash table */ - pBHE = this->beaconTable.lookup ( addr ); + bhe *pBHE = this->beaconTable.lookup ( addr ); if ( pBHE ) { /* * return if the beacon period has not changed significantly */ - if ( ! pBHE->updatePeriod ( this->programBeginTime ) ) { + if ( ! pBHE->updatePeriod ( this->programBeginTime, currentTime ) ) { return; } } @@ -411,7 +486,7 @@ void cac::beaconNotify ( const inetAddrID &addr ) * time that we have seen a server's beacon * shortly after the program started up) */ - pBHE = new bhe ( epicsTime::getCurrent (), addr ); + pBHE = new bhe ( currentTime, addr ); if ( pBHE ) { if ( this->beaconTable.add ( *pBHE ) < 0 ) { pBHE->destroy (); @@ -432,17 +507,13 @@ void cac::beaconNotify ( const inetAddrID &addr ) * order bits as a pseudo random delay to prevent every * one from replying at once. */ - port = this->pudpiiu->getPort (); - { - ca_real delay; - - delay = ( port & CA_RECAST_PORT_MASK ); - delay /= MSEC_PER_SEC; - delay += CA_RECAST_DELAY; - - if ( this->pudpiiu->channelCount () > 0u && this->pSearchTmr ) { - this->pSearchTmr->resetPeriod ( delay ); - } + if ( this->pSearchTmr ) { + static const double portTicksPerSec = 1000u; + static const unsigned portBasedDelayMask = 0xff; + unsigned port = this->pudpiiu->getPort (); + double delay = ( port & portBasedDelayMask ); + delay /= portTicksPerSec; + this->pSearchTmr->resetPeriod ( delay ); } this->pudpiiu->resetChannelRetryCounts (); @@ -613,7 +684,7 @@ void cac::installNetworkChannel ( nciu & chan, netiiu * & piiu ) this->chanTable.add ( chan ); this->pudpiiu->attachChannel ( chan ); piiu = this->pudpiiu; - this->pSearchTmr->resetPeriod ( CA_RECAST_DELAY ); + this->pSearchTmr->resetPeriod ( 0.0 ); } bool cac::setupUDP () @@ -670,8 +741,9 @@ void cac::repeaterSubscribeConfirmNotify () } bool cac::lookupChannelAndTransferToTCP ( unsigned cid, unsigned sid, - unsigned typeCode, unsigned long count, - unsigned minorVersionNumber, const osiSockAddr &addr ) + ca_uint16_t typeCode, arrayElementCount count, + unsigned minorVersionNumber, const osiSockAddr &addr, + const epicsTime & currentTime ) { unsigned retrySeqNumber; @@ -679,88 +751,82 @@ bool cac::lookupChannelAndTransferToTCP ( unsigned cid, unsigned sid, return false; } - { - epicsAutoMutex autoMutex ( this->mutex ); - nciu *chan; + epicsAutoMutex autoMutex ( this->mutex ); + nciu *chan; - /* - * ignore search replies for deleted channels - */ - chan = this->chanTable.lookup ( cid ); - if ( ! chan ) { - return true; + /* + * ignore search replies for deleted channels + */ + chan = this->chanTable.lookup ( cid ); + if ( ! chan ) { + return true; + } + + retrySeqNumber = chan->getRetrySeqNo (); + + /* + * Ignore duplicate search replies + */ + if ( chan->getPIIU()->isVirtaulCircuit( chan->pName(), addr ) ) { + return true; + } + + /* + * look for an existing virtual circuit + */ + tcpiiu *piiu; + bhe *pBHE = this->beaconTable.lookup ( addr.ia ); + if ( pBHE ) { + piiu = pBHE->getIIU (); + if ( piiu ) { + if ( ! piiu->alive () ) { + return true; + } } - - retrySeqNumber = chan->getRetrySeqNo (); - - /* - * Ignore duplicate search replies - */ - if ( chan->getPIIU()->isVirtaulCircuit( chan->pName(), addr ) ) { - return true; - } - - /* - * look for an existing virtual circuit - */ - tcpiiu *piiu; - bhe *pBHE = this->beaconTable.lookup ( addr.ia ); + } + else { + pBHE = new bhe ( epicsTime (), addr.ia ); if ( pBHE ) { - piiu = pBHE->getIIU (); - if ( piiu ) { - if ( ! piiu->alive () ) { - return true; - } + if ( this->beaconTable.add ( *pBHE ) < 0 ) { + pBHE->destroy (); + return true; } } else { - pBHE = new bhe ( epicsTime (), addr.ia ); - if ( pBHE ) { - if ( this->beaconTable.add ( *pBHE ) < 0 ) { - pBHE->destroy (); - return true; - } - } - else { - return true; - } - piiu = 0; + return true; } + piiu = 0; + } - if ( ! piiu ) { - piiu = new tcpiiu ( *this, this->connTMO, *this->pTimerQueue ); + if ( ! piiu ) { + try { + piiu = new tcpiiu ( *this, this->connTMO, *this->pTimerQueue, + addr, minorVersionNumber, *pBHE, this->ipToAEngine ); if ( ! piiu ) { return true; } - if ( piiu->fullyConstructed () ) { - this->iiuList.add ( *piiu ); - if ( ! piiu->initiateConnect ( addr, minorVersionNumber, - *pBHE, this->ipToAEngine ) ) { - this->iiuList.remove ( *piiu ); - piiu->destroy (); - return true; - } - } - else { - delete piiu; - return true; - } + this->iiuList.add ( *piiu ); + } - - this->pudpiiu->detachChannel ( *chan ); - chan->searchReplySetUp ( *piiu, sid, typeCode, count ); - piiu->attachChannel ( *chan ); - - chan->createChannelRequest (); - piiu->flushRequest (); - - if ( ! piiu->ca_v42_ok () ) { - chan->connect (); + catch ( ... ) { + this->printf ( "CAC: Exception caught during virtual circuit creation\n" ); + return true; } } + this->pudpiiu->detachChannel ( *chan ); + chan->searchReplySetUp ( *piiu, sid, typeCode, count ); + piiu->attachChannel ( *chan ); + + chan->createChannelRequest (); + piiu->flushRequest (); + + if ( ! piiu->ca_v42_ok () ) { + chan->connect (); + } + if ( this->pSearchTmr ) { - this->pSearchTmr->notifySearchResponse ( retrySeqNumber ); + this->pSearchTmr->notifySearchResponse ( retrySeqNumber, currentTime ); } return true; @@ -776,7 +842,7 @@ void cac::uninstallChannel ( nciu & chan ) chan.getPIIU()->detachChannel ( chan ); } -int cac::printf ( const char *pformat, ... ) +int cac::printf ( const char *pformat, ... ) const { va_list theArgs; int status; @@ -800,23 +866,21 @@ void cac::flushIfRequired ( nciu &chan ) // send thread which runs at a higher priority than the // send thread. The same applies to the UDP thread for // locking hierarchy reasons. - bool flushPermit = true; + bool blockPermit = true; if ( this->pRecvProcThread ) { if ( this->pRecvProcThread->isCurrentThread () ) { - flushPermit = false; + blockPermit = false; } } if ( this->pudpiiu ) { if ( this->pudpiiu->isCurrentThread () ) { - flushPermit = false; + blockPermit = false; } } - if ( flushPermit ) { + this->flushRequest (); + if ( blockPermit ) { chan.getPIIU()->blockUntilSendBacklogIsReasonable ( this->mutex ); } - else { - this->flushRequest (); - } } else { chan.getPIIU()->flushRequestIfAboveEarlyThreshold (); @@ -845,7 +909,7 @@ cacChannel::ioid cac::writeNotifyRequest ( nciu &chan, unsigned type, unsigned n return pIO.release()->getId (); } else { - throw cacChannel::noMemory (); + throw std::bad_alloc (); } } @@ -862,7 +926,7 @@ cacChannel::ioid cac::readNotifyRequest ( nciu &chan, unsigned type, unsigned nE return pIO.release()->getId (); } else { - throw cacChannel::noMemory (); + throw std::bad_alloc (); } } @@ -879,7 +943,7 @@ void cac::ioCancel ( nciu &chan, const cacChannel::ioid &id ) assert ( this->threadsBlockingOnNotifyCompletion < UINT_MAX ); this->threadsBlockingOnNotifyCompletion++; while ( this->ioInProgress && this->ioNotifyInProgressId == id ) { - epicsAutoMutex autoMutexRelease ( this->mutex ); + epicsAutoMutexRelease autoRelease ( this->mutex ); this->notifyCompletionEvent.wait ( 0.5 ); } assert ( this->threadsBlockingOnNotifyCompletion > 0u ); @@ -900,18 +964,18 @@ void cac::ioShow ( const cacChannel::ioid &id, unsigned level ) const } } -bool cac::ioCompletionNotify ( unsigned id, unsigned type, - unsigned long count, const void *pData ) +void cac::ioCompletionNotify ( unsigned id, unsigned type, + arrayElementCount count, const void *pData ) { baseNMIU * pmiu = this->ioTable.lookup ( id ); if ( ! pmiu ) { - return false; + return; } assert ( ! this->ioInProgress ); this->ioInProgress = true; this->ioNotifyInProgressId = id; { - epicsAutoMutexRelease ( this->mutex ); + epicsAutoMutexRelease autoRelease ( this->mutex ); pmiu->completion ( type, count, pData ); } // threads blocked canceling this IO will wait @@ -920,20 +984,19 @@ bool cac::ioCompletionNotify ( unsigned id, unsigned type, if ( this->threadsBlockingOnNotifyCompletion ) { this->notifyCompletionEvent.signal (); } - return true; } -bool cac::ioExceptionNotify ( unsigned id, int status, const char *pContext ) +void cac::ioExceptionNotify ( unsigned id, int status, const char *pContext ) { baseNMIU * pmiu = this->ioTable.lookup ( id ); if ( ! pmiu ) { - return false; + return; } assert ( ! this->ioInProgress ); this->ioInProgress = true; this->ioNotifyInProgressId = id; { - epicsAutoMutexRelease ( this->mutex ); + epicsAutoMutexRelease autoRelease ( this->mutex ); pmiu->exception ( status, pContext ); } // threads blocked canceling this IO will wait @@ -942,21 +1005,20 @@ bool cac::ioExceptionNotify ( unsigned id, int status, const char *pContext ) if ( this->threadsBlockingOnNotifyCompletion ) { this->notifyCompletionEvent.signal (); } - return true; } -bool cac::ioExceptionNotify ( unsigned id, int status, - const char *pContext, unsigned type, unsigned long count ) +void cac::ioExceptionNotify ( unsigned id, int status, + const char *pContext, unsigned type, arrayElementCount count ) { baseNMIU * pmiu = this->ioTable.lookup ( id ); if ( ! pmiu ) { - return false; + return; } assert ( ! this->ioInProgress ); this->ioInProgress = true; this->ioNotifyInProgressId = id; { - epicsAutoMutexRelease ( this->mutex ); + epicsAutoMutexRelease autoRelease ( this->mutex ); pmiu->exception ( status, pContext, type, count ); } // threads blocked canceling this IO will wait @@ -965,21 +1027,20 @@ bool cac::ioExceptionNotify ( unsigned id, int status, if ( this->threadsBlockingOnNotifyCompletion ) { this->notifyCompletionEvent.signal (); } - return true; } -bool cac::ioCompletionNotifyAndDestroy ( unsigned id ) +void cac::ioCompletionNotifyAndDestroy ( unsigned id ) { baseNMIU * pmiu = this->ioTable.remove ( id ); if ( ! pmiu ) { - return false; + return; } pmiu->channel().cacPrivateListOfIO::eventq.remove ( *pmiu ); assert ( ! this->ioInProgress ); this->ioInProgress = true; this->ioNotifyInProgressId = id; { - epicsAutoMutexRelease ( this->mutex ); + epicsAutoMutexRelease autoRelease ( this->mutex ); pmiu->completion (); } pmiu->destroy ( *this ); @@ -989,22 +1050,21 @@ bool cac::ioCompletionNotifyAndDestroy ( unsigned id ) if ( this->threadsBlockingOnNotifyCompletion ) { this->notifyCompletionEvent.signal (); } - return true; } -bool cac::ioCompletionNotifyAndDestroy ( unsigned id, - unsigned type, unsigned long count, const void *pData ) +void cac::ioCompletionNotifyAndDestroy ( unsigned id, + unsigned type, arrayElementCount count, const void *pData ) { baseNMIU * pmiu = this->ioTable.remove ( id ); if ( ! pmiu ) { - return false; + return; } assert ( ! this->ioInProgress ); this->ioInProgress = true; this->ioNotifyInProgressId = id; pmiu->channel().cacPrivateListOfIO::eventq.remove ( *pmiu ); { - epicsAutoMutexRelease ( this->mutex ); + epicsAutoMutexRelease autoRelease ( this->mutex ); pmiu->completion ( type, count, pData ); } pmiu->destroy ( *this ); @@ -1014,21 +1074,21 @@ bool cac::ioCompletionNotifyAndDestroy ( unsigned id, if ( this->threadsBlockingOnNotifyCompletion ) { this->notifyCompletionEvent.signal (); } - return true; } -bool cac::ioExceptionNotifyAndDestroy ( unsigned id, int status, const char *pContext ) +void cac::ioExceptionNotifyAndDestroy ( unsigned id, int status, + const char *pContext ) { baseNMIU * pmiu = this->ioTable.remove ( id ); if ( ! pmiu ) { - return false; + return; } assert ( ! this->ioInProgress ); this->ioInProgress = true; this->ioNotifyInProgressId = id; pmiu->channel().cacPrivateListOfIO::eventq.remove ( *pmiu ); { - epicsAutoMutexRelease ( this->mutex ); + epicsAutoMutexRelease autoRelease ( this->mutex ); pmiu->exception ( status, pContext ); } pmiu->destroy ( *this ); @@ -1038,22 +1098,21 @@ bool cac::ioExceptionNotifyAndDestroy ( unsigned id, int status, const char *pCo if ( this->threadsBlockingOnNotifyCompletion ) { this->notifyCompletionEvent.signal (); } - return true; } -bool cac::ioExceptionNotifyAndDestroy ( unsigned id, int status, - const char *pContext, unsigned type, unsigned long count ) +void cac::ioExceptionNotifyAndDestroy ( unsigned id, int status, + const char *pContext, unsigned type, arrayElementCount count ) { baseNMIU * pmiu = this->ioTable.remove ( id ); if ( ! pmiu ) { - return false; + return; } assert ( ! this->ioInProgress ); this->ioInProgress = true; this->ioNotifyInProgressId = id; pmiu->channel().cacPrivateListOfIO::eventq.remove ( *pmiu ); { - epicsAutoMutexRelease ( this->mutex ); + epicsAutoMutexRelease autoRelease ( this->mutex ); pmiu->exception ( status, pContext, type, count ); } pmiu->destroy ( *this ); @@ -1063,7 +1122,6 @@ bool cac::ioExceptionNotifyAndDestroy ( unsigned id, int status, if ( this->threadsBlockingOnNotifyCompletion ) { this->notifyCompletionEvent.signal (); } - return true; } // resubscribe for monitors from this channel @@ -1079,7 +1137,12 @@ void cac::connectAllIO ( nciu &chan ) next++; class netSubscription *pSubscr = pNetIO->isSubscription (); if ( pSubscr ) { - chan.getPIIU()->subscriptionRequest ( chan, *pSubscr ); + try { + chan.getPIIU()->subscriptionRequest ( chan, *pSubscr ); + } + catch (...) { + this->printf ( "cac: no memory to queue event subscription\n" ); + } } else { // it shouldnt be here at this point - so uninstall it @@ -1092,7 +1155,6 @@ void cac::connectAllIO ( nciu &chan ) chan.getPIIU()->flushRequest (); } while ( baseNMIU *pIO = tmpList.get () ) { - pIO->exception ( ECA_INTERNAL, "strange IO exists when connecting channel?" ); pIO->destroy ( *this ); } } @@ -1118,7 +1180,9 @@ void cac::disconnectAllIO ( nciu &chan ) } } while ( baseNMIU *pIO = tmpList.get () ) { - pIO->exception ( ECA_DISCONN, chan.pHostName() ); + char buf[128]; + sprintf ( buf, "host = %100s", chan.pHostName() ); + pIO->exception ( ECA_DISCONN, buf ); pIO->destroy ( *this ); } } @@ -1153,7 +1217,7 @@ void cac::recycleSubscription ( netSubscription &io ) } cacChannel::ioid cac::subscriptionRequest ( nciu &chan, unsigned type, - unsigned long nElem, unsigned mask, cacStateNotify ¬ifyIn ) + arrayElementCount nElem, unsigned mask, cacStateNotify ¬ifyIn ) { epicsAutoMutex autoMutex ( this->mutex ); autoPtrRecycle < netSubscription > pIO ( *this, netSubscription::factory ( @@ -1161,42 +1225,50 @@ cacChannel::ioid cac::subscriptionRequest ( nciu &chan, unsigned type, if ( pIO.get() ) { chan.cacPrivateListOfIO::eventq.add ( *pIO ); this->ioTable.add ( *pIO ); - if ( chan.connected() ) { - this->flushIfRequired ( chan ); - chan.getPIIU()->subscriptionRequest ( chan, *pIO ); + if ( chan.connected () ) { + try { + this->flushIfRequired ( chan ); + chan.getPIIU()->subscriptionRequest ( chan, *pIO ); + } + catch ( ... ) { + chan.cacPrivateListOfIO::eventq.remove ( *pIO ); + this->ioTable.remove ( *pIO ); + throw; + } } cacChannel::ioid id = pIO->getId (); - pIO.release(); + pIO.release (); return id; } else { - throw cacChannel::noMemory(); + throw std::bad_alloc(); } } -bool cac::noopAction ( tcpiiu &, const caHdr &, void * /* pMsgBdy */ ) +bool cac::noopAction ( tcpiiu &, const caHdrLargeArray &, void * /* pMsgBdy */ ) { return true; } -bool cac::echoRespAction ( tcpiiu &, const caHdr &, void * /* pMsgBdy */ ) +bool cac::echoRespAction ( tcpiiu &, const caHdrLargeArray &, void * /* pMsgBdy */ ) { return true; } -bool cac::writeNotifyRespAction ( tcpiiu &, const caHdr &hdr, void * /* pMsgBdy */ ) +bool cac::writeNotifyRespAction ( tcpiiu &, const caHdrLargeArray &hdr, void * /* pMsgBdy */ ) { int caStatus = hdr.m_cid; if ( caStatus == ECA_NORMAL ) { - return this->ioCompletionNotifyAndDestroy ( hdr.m_available ); + this->ioCompletionNotifyAndDestroy ( hdr.m_available ); } else { - return this->ioExceptionNotifyAndDestroy ( hdr.m_available, + this->ioExceptionNotifyAndDestroy ( hdr.m_available, caStatus, "write notify request rejected" ); } + return true; } -bool cac::readNotifyRespAction ( tcpiiu &iiu, const caHdr &hdr, void *pMsgBdy ) +bool cac::readNotifyRespAction ( tcpiiu &iiu, const caHdrLargeArray &hdr, void *pMsgBdy ) { /* @@ -1221,21 +1293,22 @@ bool cac::readNotifyRespAction ( tcpiiu &iiu, const caHdr &hdr, void *pMsgBdy ) pMsgBdy, pMsgBdy, false, hdr.m_count); } else { - caStatus = htonl ( ECA_BADTYPE ); + caStatus = ECA_BADTYPE; } # endif if ( caStatus == ECA_NORMAL ) { - return this->ioCompletionNotifyAndDestroy ( hdr.m_available, + this->ioCompletionNotifyAndDestroy ( hdr.m_available, hdr.m_dataType, hdr.m_count, pMsgBdy ); } else { - return this->ioExceptionNotifyAndDestroy ( hdr.m_available, + this->ioExceptionNotifyAndDestroy ( hdr.m_available, caStatus, "read failed", hdr.m_dataType, hdr.m_count ); } + return true; } -bool cac::eventRespAction ( tcpiiu &iiu, const caHdr &hdr, void *pMsgBdy ) +bool cac::eventRespAction ( tcpiiu &iiu, const caHdrLargeArray &hdr, void *pMsgBdy ) { int caStatus; @@ -1273,71 +1346,123 @@ bool cac::eventRespAction ( tcpiiu &iiu, const caHdr &hdr, void *pMsgBdy ) # endif if ( caStatus == ECA_NORMAL ) { - return this->ioCompletionNotify ( hdr.m_available, + this->ioCompletionNotify ( hdr.m_available, hdr.m_dataType, hdr.m_count, pMsgBdy ); } else { - return this->ioExceptionNotify ( hdr.m_available, + this->ioExceptionNotify ( hdr.m_available, caStatus, "subscription update failed", hdr.m_dataType, hdr.m_count ); } + return true; } -bool cac::readRespAction ( tcpiiu &, const caHdr &hdr, void *pMsgBdy ) +bool cac::readRespAction ( tcpiiu &, const caHdrLargeArray &hdr, void *pMsgBdy ) { - return this->ioCompletionNotifyAndDestroy ( hdr.m_available, + this->ioCompletionNotifyAndDestroy ( hdr.m_available, hdr.m_dataType, hdr.m_count, pMsgBdy ); + return true; } -bool cac::clearChannelRespAction ( tcpiiu &, const caHdr &, void * /* pMsgBdy */ ) +bool cac::clearChannelRespAction ( tcpiiu &, const caHdrLargeArray &, void * /* pMsgBdy */ ) { return true; // currently a noop } -bool cac::exceptionRespAction ( tcpiiu &iiu, const caHdr &hdr, void *pMsgBdy ) +bool cac::defaultExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ) { - const caHdr * req = reinterpret_cast < const caHdr * > ( pMsgBdy ); - char context[255]; + char buf[512]; char hostName[64]; - - const char *pName = reinterpret_cast < const char * > ( req + 1 ); - - iiu.hostName ( hostName, sizeof(hostName) ); - sprintf ( context, "detected by: %s for: %s", - hostName, pName); - - switch ( ntohs ( req->m_cmmd ) ) { - case CA_PROTO_READ_NOTIFY: - return this->ioExceptionNotifyAndDestroy ( ntohl ( req->m_available ), - ntohl ( hdr.m_available ), context, - ntohs ( req->m_dataType ), ntohs ( req->m_count ) ); - case CA_PROTO_READ: - return this->ioExceptionNotifyAndDestroy ( ntohl (req->m_available), - ntohl ( hdr.m_available ), context, - ntohs ( req->m_dataType ), ntohs ( req->m_count ) ); - case CA_PROTO_WRITE_NOTIFY: - return this->ioExceptionNotifyAndDestroy ( ntohl (req->m_available), - ntohl ( hdr.m_available ), context, - ntohs ( req->m_dataType ), ntohs ( req->m_count ) ); - case CA_PROTO_WRITE: - this->exception ( ntohl ( hdr.m_available ), - context, ntohs ( req->m_dataType ), ntohs ( req->m_count ), __FILE__, __LINE__); - return true; - case CA_PROTO_EVENT_ADD: - return this->ioExceptionNotify ( ntohl ( req->m_available ), - ntohl ( hdr.m_available ), context, - ntohs ( req->m_dataType ), ntohs ( req->m_count ) ); - case CA_PROTO_EVENT_CANCEL: - return this->ioExceptionNotifyAndDestroy ( ntohl ( req->m_available ), - ntohl ( hdr.m_available ), context ); - default: - this->exception ( ntohl ( hdr.m_available ), - context, __FILE__, __LINE__ ); - return true; - } + iiu.hostName ( hostName, sizeof ( hostName ) ); + sprintf ( buf, "host=%64s ctx=%400s", hostName, pCtx ); + this->notify.exception ( status, buf, 0, 0u ); + return true; } -bool cac::accessRightsRespAction ( tcpiiu &, const caHdr &hdr, void * /* pMsgBdy */ ) +bool cac::eventAddExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ) +{ + this->ioExceptionNotify ( hdr.m_available, status, pCtx, + hdr.m_dataType, hdr.m_count ); + return true; +} + +bool cac::readExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ) +{ + this->ioExceptionNotifyAndDestroy ( hdr.m_available, + status, pCtx, hdr.m_dataType, hdr.m_count ); + return true; +} + +bool cac::writeExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ) +{ + nciu * pChan = this->chanTable.lookup ( hdr.m_available ); + if ( pChan ) { + pChan->writeException ( status, pCtx, + hdr.m_dataType, hdr.m_count ); + } + return true; +} + +bool cac::readNotifyExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ) +{ + this->ioExceptionNotifyAndDestroy ( hdr.m_available, + status, pCtx, hdr.m_dataType, hdr.m_count ); + return true; +} + +bool cac::writeNotifyExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ) +{ + this->ioExceptionNotifyAndDestroy ( hdr.m_available, + status, pCtx, hdr.m_dataType, hdr.m_count ); + return true; +} + +bool cac::exceptionRespAction ( tcpiiu &iiu, const caHdrLargeArray &hdr, void *pMsgBdy ) +{ + const caHdr * pReq = reinterpret_cast < const caHdr * > ( pMsgBdy ); + unsigned bytesSoFar = sizeof ( *pReq ); + if ( hdr.m_postsize < bytesSoFar ) { + return false; + } + caHdrLargeArray req; + req.m_cmmd = ntohs ( pReq->m_cmmd ); + req.m_postsize = ntohs ( pReq->m_postsize ); + req.m_dataType = ntohs ( pReq->m_dataType ); + req.m_count = ntohs ( pReq->m_count ); + req.m_cid = ntohl ( pReq->m_cid ); + req.m_available = ntohl ( pReq->m_available ); + const ca_uint32_t * pLW = reinterpret_cast < const ca_uint32_t * > ( pReq + 1 ); + if ( req.m_postsize == 0xffff ) { + static const unsigned annexSize = + sizeof ( req.m_postsize ) + sizeof ( req.m_count ); + bytesSoFar += annexSize; + if ( hdr.m_postsize < bytesSoFar ) { + return false; + } + req.m_postsize = ntohl ( pLW[0] ); + req.m_count = ntohl ( pLW[1] ); + pLW += 2u; + } + + // execute the exception message + pExcepProtoStubTCP pStub; + if ( hdr.m_cmmd >= NELEMENTS ( cac::tcpExcepJumpTableCAC ) ) { + pStub = &cac::defaultExcep; + } + else { + pStub = cac::tcpExcepJumpTableCAC [req.m_cmmd]; + } + const char *pCtx = reinterpret_cast < const char * > ( pLW ); + return ( this->*pStub ) ( iiu, req, pCtx, hdr.m_available ); +} + +bool cac::accessRightsRespAction ( tcpiiu &, const caHdrLargeArray &hdr, void * /* pMsgBdy */ ) { nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); if ( pChan ) { @@ -1350,7 +1475,7 @@ bool cac::accessRightsRespAction ( tcpiiu &, const caHdr &hdr, void * /* pMsgBdy return true; } -bool cac::claimCIURespAction ( tcpiiu &iiu, const caHdr &hdr, void * /* pMsgBdy */ ) +bool cac::claimCIURespAction ( tcpiiu &iiu, const caHdrLargeArray &hdr, void *pMsgBdy ) { nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); if ( pChan ) { @@ -1365,22 +1490,22 @@ bool cac::claimCIURespAction ( tcpiiu &iiu, const caHdr &hdr, void * /* pMsgBdy return true; } else { - return false; + return true; // ignore claim response to deleted channel } } -bool cac::verifyAndDisconnectChan ( tcpiiu &, const caHdr &hdr, void * /* pMsgBdy */ ) +bool cac::verifyAndDisconnectChan ( tcpiiu &, const caHdrLargeArray &hdr, void * /* pMsgBdy */ ) { nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); if ( pChan ) { assert ( this->pudpiiu && this->pSearchTmr ); pChan->disconnect ( *this->pudpiiu ); - this->pSearchTmr->resetPeriod ( CA_RECAST_DELAY ); + this->pSearchTmr->resetPeriod ( 0.0 ); } return true; } -bool cac::badTCPRespAction ( tcpiiu &iiu, const caHdr &hdr, void * /* pMsgBdy */ ) +bool cac::badTCPRespAction ( tcpiiu &iiu, const caHdrLargeArray &hdr, void * /* pMsgBdy */ ) { char hostName[64]; iiu.hostName ( hostName, sizeof(hostName) ); @@ -1389,7 +1514,7 @@ bool cac::badTCPRespAction ( tcpiiu &iiu, const caHdr &hdr, void * /* pMsgBdy */ return false; } -void cac::executeResponse ( tcpiiu &iiu, caHdr &hdr, char *pMshBody ) +bool cac::executeResponse ( tcpiiu &iiu, caHdrLargeArray &hdr, char *pMshBody ) { // execute the response message pProtoStubTCP pStub; @@ -1399,7 +1524,59 @@ void cac::executeResponse ( tcpiiu &iiu, caHdr &hdr, char *pMshBody ) else { pStub = cac::tcpJumpTableCAC [hdr.m_cmmd]; } - ( this->*pStub ) ( iiu, hdr, pMshBody ); + return ( this->*pStub ) ( iiu, hdr, pMshBody ); } +void cac::signal ( int ca_status, const char *pfilenm, + int lineno, const char *pFormat, ... ) +{ + va_list theArgs; + va_start ( theArgs, pFormat ); + this->vSignal ( ca_status, pfilenm, lineno, pFormat, theArgs); + va_end ( theArgs ); +} + +void cac::vSignal ( int ca_status, const char *pfilenm, + int lineno, const char *pFormat, va_list args ) +{ + static const char *severity[] = + { + "Warning", + "Success", + "Error", + "Info", + "Fatal", + "Fatal", + "Fatal", + "Fatal" + }; + + this->printf ( "CA.Client.Diagnostic..............................................\n" ); + + this->printf ( " %s: \"%s\"\n", + severity[ CA_EXTRACT_SEVERITY ( ca_status ) ], + ca_message ( ca_status ) ); + + if ( pFormat ) { + this->printf ( " Context: \"" ); + this->vPrintf ( pFormat, args ); + this->printf ( "\"\n" ); + } + + if (pfilenm) { + this->printf ( " Source File: %s Line Number: %d\n", + pfilenm, lineno ); + } + + /* + * Terminate execution if unsuccessful + */ + if( ! ( ca_status & CA_M_SUCCESS ) && + CA_EXTRACT_SEVERITY ( ca_status ) != CA_K_WARNING ){ + errlogFlush(); + abort(); + } + + this->printf ( "..................................................................\n" ); +} diff --git a/src/ca/cac.h b/src/ca/cac.h index e5761bf1b..4adc9f8af 100644 --- a/src/ca/cac.h +++ b/src/ca/cac.h @@ -21,6 +21,7 @@ #include "ipAddrToAsciiAsynchronous.h" #include "epicsTimer.h" #include "epicsEvent.h" +#include "freeList.h" #include "nciu.h" @@ -93,6 +94,7 @@ public: struct CASG; class inetAddrID; +struct caHdrLargeArray; class cac : private cacRecycle { @@ -101,7 +103,8 @@ public: virtual ~cac (); // beacon management - void beaconNotify ( const inetAddrID &addr ); + void beaconNotify ( const inetAddrID &addr, + const epicsTime ¤tTime ); void repeaterSubscribeConfirmNotify (); // IIU routines @@ -122,7 +125,7 @@ public: void connectAllIO ( nciu &chan ); void disconnectAllIO ( nciu &chan ); void destroyAllIO ( nciu &chan ); - void executeResponse ( tcpiiu &, caHdr &, char *pMshBody ); + bool executeResponse ( tcpiiu &, caHdrLargeArray &, char *pMsgBody ); void ioCancel ( nciu &chan, const cacChannel::ioid &id ); void ioShow ( const cacChannel::ioid &id, unsigned level ) const; @@ -130,8 +133,8 @@ public: bool connectChannel ( unsigned id ); void installNetworkChannel ( nciu &, netiiu *&piiu ); bool lookupChannelAndTransferToTCP ( unsigned cid, unsigned sid, - unsigned typeCode, unsigned long count, unsigned minorVersionNumber, - const osiSockAddr & ); + ca_uint16_t typeCode, arrayElementCount count, unsigned minorVersionNumber, + const osiSockAddr &, const epicsTime & currentTime ); void uninstallChannel ( nciu & ); cacChannel & createChannel ( const char *name_str, cacChannelNotify &chan ); void registerService ( cacService &service ); @@ -144,7 +147,7 @@ public: cacChannel::ioid readNotifyRequest ( nciu &, unsigned type, unsigned nElem, cacReadNotify & ); cacChannel::ioid subscriptionRequest ( nciu &, unsigned type, - unsigned long nElem, unsigned mask, cacStateNotify & ); + arrayElementCount nElem, unsigned mask, cacStateNotify & ); // sync group routines CASG * lookupCASG ( unsigned id ); @@ -154,9 +157,6 @@ public: // exception generation void exception ( int status, const char *pContext, const char *pFileName, unsigned lineNo ); - void exception ( int status, const char *pContext, - unsigned type, unsigned long count, - const char *pFileName, unsigned lineNo ); // callback preemption control void enableCallbackPreemption (); @@ -165,21 +165,30 @@ public: // diagnostics unsigned connectionCount () const; void show ( unsigned level ) const; - int printf ( const char *pformat, ... ); - int vPrintf ( const char *pformat, va_list args ); + int printf ( const char *pformat, ... ) const; + int vPrintf ( const char *pformat, va_list args ) const; void ipAddrToAsciiAsynchronousRequestInstall ( ipAddrToAsciiAsynchronous & request ); + void signal ( int ca_status, const char *pfilenm, + int lineno, const char *pFormat, ... ); + void vSignal ( int ca_status, const char *pfilenm, + int lineno, const char *pFormat, va_list args ); // misc const char * userNamePointer () const; unsigned getInitializingThreadsPriority () const; - epicsMutex & mutexRef (); + void attachToClientCtx (); + char * allocateSmallBufferTCP (); + void releaseSmallBufferTCP ( char * ); + unsigned largeBufferSizeTCP () const; + char * allocateLargeBufferTCP (); + void releaseLargeBufferTCP ( char * ); private: ioCounterNet ioCounter; ipAddrToAsciiEngine ipToAEngine; cacServiceList services; - tsDLList iiuList; + tsDLList < tcpiiu > iiuList; chronIntIdResTable < nciu > chanTable; chronIntIdResTable @@ -208,10 +217,13 @@ private: class searchTimer *pSearchTmr; class repeaterSubscribeTimer *pRepeaterSubscribeTmr; + void *tcpSmallRecvBufFreeList; + void *tcpLargeRecvBufFreeList; cacNotify ¬ify; unsigned ioNotifyInProgressId; unsigned initializingThreadsPriority; unsigned threadsBlockingOnNotifyCompletion; + unsigned maxRecvBytesTCP; bool enablePreemptiveCallback; bool ioInProgress; bool setupUDP (); @@ -220,39 +232,57 @@ private: void recycleWriteNotifyIO ( netWriteNotifyIO &io ); void recycleSubscription ( netSubscription &io ); - bool ioCompletionNotify ( unsigned id, unsigned type, - unsigned long count, const void *pData ); - bool ioExceptionNotify ( unsigned id, + void ioCompletionNotify ( unsigned id, unsigned type, + arrayElementCount count, const void *pData ); + void ioExceptionNotify ( unsigned id, int status, const char *pContext ); - bool ioExceptionNotify ( unsigned id, int status, - const char *pContext, unsigned type, unsigned long count ); - bool ioCompletionNotifyAndDestroy ( unsigned id ); - bool ioCompletionNotifyAndDestroy ( unsigned id, - unsigned type, unsigned long count, const void *pData ); - bool ioExceptionNotifyAndDestroy ( unsigned id, + void ioExceptionNotify ( unsigned id, int status, + const char *pContext, unsigned type, arrayElementCount count ); + + void ioCompletionNotifyAndDestroy ( unsigned id ); + void ioCompletionNotifyAndDestroy ( unsigned id, + unsigned type, arrayElementCount count, const void *pData ); + void ioExceptionNotifyAndDestroy ( unsigned id, int status, const char *pContext ); - bool ioExceptionNotifyAndDestroy ( unsigned id, - int status, const char *pContext, unsigned type, unsigned long count ); + void ioExceptionNotifyAndDestroy ( unsigned id, + int status, const char *pContext, unsigned type, arrayElementCount count ); // recv protocol stubs - bool noopAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool echoRespAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool writeNotifyRespAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool readNotifyRespAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool eventRespAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool readRespAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool clearChannelRespAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool exceptionRespAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool accessRightsRespAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool claimCIURespAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool verifyAndDisconnectChan ( tcpiiu &, const caHdr &, void *pMsgBdy ); - bool badTCPRespAction ( tcpiiu &, const caHdr &, void *pMsgBdy ); + bool noopAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool echoRespAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool writeNotifyRespAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool readNotifyRespAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool eventRespAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool readRespAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool clearChannelRespAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool exceptionRespAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool accessRightsRespAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool claimCIURespAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool verifyAndDisconnectChan ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); + bool badTCPRespAction ( tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); typedef bool ( cac::*pProtoStubTCP ) ( - tcpiiu &, const caHdr &, void *pMsgBdy ); + tcpiiu &, const caHdrLargeArray &, void *pMsgBdy ); static const pProtoStubTCP tcpJumpTableCAC []; -}; -extern "C" void ca_default_exception_handler ( struct exception_handler_args args ); + bool defaultExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool eventAddExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool readExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool writeExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool clearChanExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool readNotifyExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool writeNotifyExcep ( tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + typedef bool ( cac::*pExcepProtoStubTCP ) ( + tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + static const pExcepProtoStubTCP tcpExcepJumpTableCAC []; +}; inline const char * cac::userNamePointer () const { @@ -300,18 +330,41 @@ inline void cac::exception ( int status, const char *pContext, this->notify.exception ( status, pContext, pFileName, lineNo ); } -inline void cac::exception ( int status, const char *pContext, - unsigned type, unsigned long count, - const char *pFileName, unsigned lineNo ) -{ - this->notify.exception ( status, pContext, type, count, pFileName, lineNo ); -} - -inline int cac::vPrintf ( const char *pformat, va_list args ) +inline int cac::vPrintf ( const char *pformat, va_list args ) const { return this->notify.vPrintf ( pformat, args ); } +inline void cac::attachToClientCtx () +{ + this->notify.attachToClientCtx (); +} + +inline char * cac::allocateSmallBufferTCP () +{ + return ( char * ) freeListMalloc ( this->tcpSmallRecvBufFreeList ); +} + +inline void cac::releaseSmallBufferTCP ( char *pBuf ) +{ + freeListFree ( this->tcpSmallRecvBufFreeList, pBuf ); +} + +inline unsigned cac::largeBufferSizeTCP () const +{ + return this->maxRecvBytesTCP; +} + +inline char * cac::allocateLargeBufferTCP () +{ + return ( char * ) freeListMalloc ( this->tcpLargeRecvBufFreeList ); +} + +inline void cac::releaseLargeBufferTCP ( char *pBuf ) +{ + freeListFree ( this->tcpLargeRecvBufFreeList, pBuf ); +} + inline bool recvProcessThread::isCurrentThread () const { return this->thread.isCurrentThread (); diff --git a/src/ca/cacIO.h b/src/ca/cacIO.h index 6c85841ab..b7ecaa2fc 100644 --- a/src/ca/cacIO.h +++ b/src/ca/cacIO.h @@ -45,12 +45,16 @@ class cacChannel; -// this should not be passing caerr.h status to the exception callback +typedef unsigned long arrayElementCount; + +// 1) this should not be passing caerr.h status to the exception callback +// 2) needless-to-say the data should be passed here using the new data access API class epicsShareClass cacWriteNotify { public: virtual ~cacWriteNotify () = 0; virtual void completion () = 0; - virtual void exception ( int status, const char *pContext ) = 0; + virtual void exception ( int status, const char *pContext, + unsigned type, arrayElementCount count ) = 0; }; // 1) this should not be passing caerr.h status to the exception callback @@ -59,9 +63,9 @@ class epicsShareClass cacReadNotify { public: virtual ~cacReadNotify () = 0; virtual void completion ( unsigned type, - unsigned long count, const void *pData ) = 0; + arrayElementCount count, const void *pData ) = 0; virtual void exception ( int status, - const char *pContext, unsigned type, unsigned long count ) = 0; + const char *pContext, unsigned type, arrayElementCount count ) = 0; }; // 1) this should not be passing caerr.h status to the exception callback @@ -70,9 +74,9 @@ class epicsShareClass cacStateNotify { public: virtual ~cacStateNotify () = 0; virtual void current ( unsigned type, - unsigned long count, const void *pData ) = 0; + arrayElementCount count, const void *pData ) = 0; virtual void exception ( int status, - const char *pContext, unsigned type, unsigned long count ) = 0; + const char *pContext, unsigned type, arrayElementCount count ) = 0; }; class caAccessRights { @@ -103,6 +107,10 @@ public: virtual void disconnectNotify () = 0; virtual void accessRightsNotify ( const caAccessRights & ) = 0; virtual void exception ( int status, const char *pContext ) = 0; + virtual void readException ( int status, const char *pContext, + unsigned type, arrayElementCount count, void *pValue ) = 0; + virtual void writeException ( int status, const char *pContext, + unsigned type, arrayElementCount count ) = 0; // not for public consumption -- can we get rid of this ???? virtual bool includeFirstConnectInCountOfOutstandingIO () const; }; @@ -125,21 +133,21 @@ public: virtual const char *pName () const = 0; virtual void show ( unsigned level ) const = 0; virtual void initiateConnect () = 0; - virtual void write ( unsigned type, unsigned long count, + virtual void write ( unsigned type, arrayElementCount count, const void *pValue ) = 0; // we may need to include an optimization for read copy here if we want to enable // reasonable performance of the old API. Adding it here means that the outstanding IO // count must be visible :-(. - virtual ioStatus read ( unsigned type, unsigned long count, + virtual ioStatus read ( unsigned type, arrayElementCount count, cacReadNotify &, ioid * = 0 ) = 0; - virtual ioStatus write ( unsigned type, unsigned long count, + virtual ioStatus write ( unsigned type, arrayElementCount count, const void *pValue, cacWriteNotify &, ioid * = 0 ) = 0; - virtual void subscribe ( unsigned type, unsigned long count, + virtual void subscribe ( unsigned type, arrayElementCount count, unsigned mask, cacStateNotify &, ioid * = 0 ) = 0; virtual void ioCancel ( const ioid & ) = 0; virtual void ioShow ( const ioid &, unsigned level ) const = 0; virtual short nativeType () const = 0; - virtual unsigned long nativeElementCount () const = 0; + virtual arrayElementCount nativeElementCount () const = 0; virtual caAccessRights accessRights () const; // defaults to unrestricted access virtual unsigned searchAttempts () const; // defaults to zero virtual double beaconPeriod () const; // defaults to negative DBL_MAX @@ -160,7 +168,7 @@ public: class noReadAccess {}; class notConnected {}; class unsupportedByService {}; - class noMemory {}; + class msgBodyCacheTooSmall {}; // hopefully this one goes away in the future private: cacChannelNotify & callback; @@ -169,17 +177,15 @@ private: class cacNotify { public: virtual ~cacNotify () = 0; -// exception mechanism needs to be designed - virtual void exception ( int status, const char *pContext, - const char *pFileName, unsigned lineNo ) = 0; - virtual void exception ( int status, const char *pContext, - unsigned type, unsigned long count, + virtual void exception ( int status, const char *pContext, const char *pFileName, unsigned lineNo ) = 0; // perhaps this should be phased out in deference to the exception mechanism - virtual int vPrintf ( const char *pformat, va_list args ) = 0; + virtual int vPrintf ( const char *pformat, va_list args ) const = 0; // this should probably be phased out (its not OS independent) virtual void fdWasCreated ( int fd ) = 0; virtual void fdWasDestroyed ( int fd ) = 0; +// backwards compatibility + virtual void attachToClientCtx () = 0; }; struct cacService : public tsDLNode < cacService > { diff --git a/src/ca/caerr.h b/src/ca/caerr.h index 6ad932689..76fbd18b0 100644 --- a/src/ca/caerr.h +++ b/src/ca/caerr.h @@ -142,7 +142,7 @@ READONLY char *ca_message_text[] "Unable to allocate additional dynamic memory", "Unknown IO channel", "Record field specified inappropriate for channel specified", -"The array or data structure specified will not fit in CA message buffer", +"The array or data structure specified is less than EPICS_CA_MAX_ARRAY_BYTES", "User specified timeout on IO operation expired", "Sorry, that feature is planned but not supported at this time", "The supplied string is unusually large", diff --git a/src/ca/casw.cpp b/src/ca/casw.cpp index 3ee3ec697..e3895adce 100644 --- a/src/ca/casw.cpp +++ b/src/ca/casw.cpp @@ -29,8 +29,8 @@ int main ( int, char ** ) char buf [0x4000]; const char *pCurBuf; const caHdr *pCurMsg; - unsigned serverPort; - unsigned repeaterPort; + ca_uint16_t serverPort; + ca_uint16_t repeaterPort; int status; serverPort = @@ -151,13 +151,14 @@ int main ( int, char ** ) } bool netChange; + epicsTime currentTime = epicsTime::getCurrent(); /* * look for it in the hash table */ bhe *pBHE = beaconTable.lookup ( ina ); if ( pBHE ) { - netChange = pBHE->updatePeriod ( programBeginTime ); + netChange = pBHE->updatePeriod ( programBeginTime, currentTime ); } else { /* @@ -168,7 +169,7 @@ int main ( int, char ** ) * shortly after the program started up) */ netChange = false; - pBHE = new bhe ( epicsTime::getCurrent (), ina ); + pBHE = new bhe ( currentTime, ina ); if ( pBHE ) { if ( beaconTable.add ( *pBHE ) < 0 ) { pBHE->destroy (); @@ -178,8 +179,7 @@ int main ( int, char ** ) if ( netChange ) { char date[64]; - epicsTime current = epicsTime::getCurrent (); - current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S"); + currentTime.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S"); char host[64]; ipAddrToA ( &ina, host, sizeof ( host ) ); printf ("CA server beacon anomaly: %s %s\n", date, host ); diff --git a/src/ca/catime.c b/src/ca/catime.c index 9124a28c2..92e98fe9c 100644 --- a/src/ca/catime.c +++ b/src/ca/catime.c @@ -49,13 +49,13 @@ typedef void tf ( ti *pItems, unsigned iterations, unsigned *pInlineIter ); * test_pend() */ LOCAL void test_pend( -ti *pItems, +ti *pItems, unsigned iterations, unsigned *pInlineIter ) { unsigned i; - int status; + int status; for (i=0; i + #include #include "epicsAssert.h" @@ -31,6 +33,7 @@ class wireSendAdapter { public: virtual unsigned sendBytes ( const void *pBuf, unsigned nBytesInBuf ) = 0; + virtual void forcedShutdown () = 0; }; class wireRecvAdapter { @@ -62,17 +65,20 @@ public: unsigned copyOutBytes ( void *pBuf, unsigned nBytes ); bool copyOutAllBytes ( void *pBuf, unsigned nBytes ); unsigned removeBytes ( unsigned nBytes ); - void * operator new ( size_t size ); + void * operator new ( size_t size, const std::nothrow_t & ); void operator delete ( void *pCadaver, size_t size ); bool flushToWire ( wireSendAdapter & ); unsigned fillFromWire ( wireRecvAdapter & ); + epicsUInt8 getByte (); + class insufficentBytesAvailable {}; protected: ~comBuf (); private: unsigned nextWriteIndex; unsigned nextReadIndex; - unsigned char buf [ comBufSize ]; // optimal for 100 Mb Ethernet LAN MTU - unsigned clipNElem ( unsigned elemSize, unsigned nElem ); + epicsUInt8 buf [ comBufSize ]; + unsigned unoccupiedElem ( unsigned elemSize, unsigned nElem ); + unsigned occupiedElem ( unsigned elemSize, unsigned nElem ); static tsFreeList < class comBuf, 0x20 > freeList; static epicsMutex freeListMutex; }; @@ -90,7 +96,7 @@ inline void comBuf::destroy () delete this; } -inline void * comBuf::operator new ( size_t size ) +inline void * comBuf::operator new ( size_t size, const std::nothrow_t & ) { epicsAutoMutex locker ( comBuf::freeListMutex ); return comBuf::freeList.allocate ( size ); @@ -109,17 +115,18 @@ inline unsigned comBuf::unoccupiedBytes () const inline unsigned comBuf::occupiedBytes () const { + // assert (this->nextWriteIndex >= this->nextReadIndex); return this->nextWriteIndex - this->nextReadIndex; } inline bool comBuf::copyInAllBytes ( const void *pBuf, unsigned nBytes ) { - if ( nBytes > this->unoccupiedBytes () ) { - return false; + if ( nBytes <= this->unoccupiedBytes () ) { + memcpy ( &this->buf[this->nextWriteIndex], pBuf, nBytes); + this->nextWriteIndex += nBytes; + return true; } - memcpy ( &this->buf[this->nextWriteIndex], pBuf, nBytes); - this->nextWriteIndex += nBytes; - return true; + return false; } inline unsigned comBuf::copyInBytes ( const void *pBuf, unsigned nBytes ) @@ -145,12 +152,12 @@ inline unsigned comBuf::copyIn ( comBuf &bufIn ) inline bool comBuf::copyOutAllBytes ( void *pBuf, unsigned nBytes ) { - if ( nBytes > this->occupiedBytes () ) { - return false; + if ( nBytes <= this->occupiedBytes () ) { + memcpy ( pBuf, &this->buf[this->nextReadIndex], nBytes); + this->nextReadIndex += nBytes; + return true; } - memcpy ( pBuf, &this->buf[this->nextReadIndex], nBytes); - this->nextReadIndex += nBytes; - return true; + return false; } inline unsigned comBuf::copyOutBytes ( void *pBuf, unsigned nBytes ) @@ -197,7 +204,7 @@ inline unsigned comBuf::fillFromWire ( wireRecvAdapter &wire ) return nNewBytes; } -inline unsigned comBuf::clipNElem ( unsigned elemSize, unsigned nElem ) +inline unsigned comBuf::unoccupiedElem ( unsigned elemSize, unsigned nElem ) { unsigned avail = this->unoccupiedBytes (); if ( elemSize * nElem > avail ) { @@ -218,75 +225,112 @@ inline unsigned comBuf::copyIn ( const epicsUInt8 *pValue, unsigned nElem ) return copyInBytes ( pValue, nElem ); } -inline unsigned comBuf::copyIn ( const epicsOldString *pValue, unsigned nElem ) -{ - return copyInBytes ( pValue, nElem * sizeof ( *pValue ) ); -} - inline unsigned comBuf::copyIn ( const epicsInt16 *pValue, unsigned nElem ) { - nElem = this->clipNElem ( sizeof (*pValue), nElem ); + nElem = this->unoccupiedElem ( sizeof (*pValue), nElem ); for ( unsigned i = 0u; i < nElem; i++ ) { - this->buf[this->nextWriteIndex++] = pValue[i] >> 8u; - this->buf[this->nextWriteIndex++] = pValue[i] >> 0u; + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 8u ); + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 0u ); } return nElem; } inline unsigned comBuf::copyIn ( const epicsUInt16 *pValue, unsigned nElem ) { - nElem = this->clipNElem ( sizeof (*pValue), nElem ); + nElem = this->unoccupiedElem ( sizeof (*pValue), nElem ); for ( unsigned i = 0u; i < nElem; i++ ) { - this->buf[this->nextWriteIndex++] = pValue[i] >> 8u; - this->buf[this->nextWriteIndex++] = pValue[i] >> 0u; + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 8u ); + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 0u ); } return nElem; } inline unsigned comBuf::copyIn ( const epicsInt32 *pValue, unsigned nElem ) { - nElem = this->clipNElem ( sizeof (*pValue), nElem ); + nElem = this->unoccupiedElem ( sizeof (*pValue), nElem ); for ( unsigned i = 0u; i < nElem; i++ ) { - this->buf[this->nextWriteIndex++] = pValue[i] >> 24u; - this->buf[this->nextWriteIndex++] = pValue[i] >> 16u; - this->buf[this->nextWriteIndex++] = pValue[i] >> 8u; - this->buf[this->nextWriteIndex++] = pValue[i] >> 0u; + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 24u ); + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 16u ); + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 8u ); + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 0u ); } return nElem; } inline unsigned comBuf::copyIn ( const epicsUInt32 *pValue, unsigned nElem ) { - nElem = this->clipNElem ( sizeof (*pValue), nElem ); + nElem = this->unoccupiedElem ( sizeof (*pValue), nElem ); for ( unsigned i = 0u; i < nElem; i++ ) { - this->buf[this->nextWriteIndex++] = pValue[i] >> 24u; - this->buf[this->nextWriteIndex++] = pValue[i] >> 16u; - this->buf[this->nextWriteIndex++] = pValue[i] >> 8u; - this->buf[this->nextWriteIndex++] = pValue[i] >> 0u; + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 24u ); + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 16u ); + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 8u ); + this->buf[this->nextWriteIndex++] = + static_cast < char > ( pValue[i] >> 0u ); } return nElem; } inline unsigned comBuf::copyIn ( const epicsFloat32 *pValue, unsigned nElem ) { - nElem = this->clipNElem ( sizeof (*pValue), nElem ); + nElem = this->unoccupiedElem ( sizeof (*pValue), nElem ); for ( unsigned i = 0u; i < nElem; i++ ) { // allow native floating point formats to be converted to IEEE osiConvertToWireFormat ( pValue[i], &this->buf[this->nextWriteIndex] ); - this->nextWriteIndex += 4u; + this->nextWriteIndex += sizeof ( *pValue ); } return nElem; } inline unsigned comBuf::copyIn ( const epicsFloat64 *pValue, unsigned nElem ) { - nElem = this->clipNElem ( sizeof (*pValue), nElem ); + nElem = this->unoccupiedElem ( sizeof (*pValue), nElem ); for ( unsigned i = 0u; i < nElem; i++ ) { // allow native floating point formats to be converted to IEEE osiConvertToWireFormat ( pValue[i], &this->buf[this->nextWriteIndex] ); - this->nextWriteIndex += 8u; + this->nextWriteIndex += sizeof ( *pValue ); } return nElem; } -#endif // comBuf +inline unsigned comBuf::copyIn ( const epicsOldString *pValue, unsigned nElem ) +{ + nElem = this->unoccupiedElem ( sizeof (*pValue), nElem ); + unsigned size = nElem * sizeof ( *pValue ); + memcpy ( &this->buf[ this->nextWriteIndex ], pValue, size ); + this->nextWriteIndex += size; + return nElem; +} + +inline unsigned comBuf::occupiedElem ( unsigned elemSize, unsigned nElem ) +{ + unsigned avail = this->occupiedBytes (); + if ( elemSize * nElem > avail ) { + return avail / elemSize; + } + else { + return nElem; + } +} + +inline epicsUInt8 comBuf::getByte () +{ + if ( this->occupiedBytes () ) { + return this->buf[ this->nextReadIndex++ ]; + } + else { + throw insufficentBytesAvailable (); + } +} + +#endif // ifndef comBufh diff --git a/src/ca/comQueRecv.cpp b/src/ca/comQueRecv.cpp index 2c20b2de9..1facb1b0c 100644 --- a/src/ca/comQueRecv.cpp +++ b/src/ca/comQueRecv.cpp @@ -59,27 +59,47 @@ unsigned comQueRecv::occupiedBytes () const return nBytes; } -bool comQueRecv::copyOutBytes ( void *pBuf, unsigned nBytes ) +unsigned comQueRecv::copyOutBytes ( void *pBuf, unsigned nBytes ) { char *pCharBuf = static_cast < char * > ( pBuf ); - // dont return partial message - if ( nBytes > this->occupiedBytes () ) { - return false; - } - - unsigned bytesLeft = nBytes; - while ( bytesLeft ) { + unsigned totalBytes = 0u; + do { comBuf * pComBuf = this->bufs.first (); - assert ( pComBuf ); - bytesLeft -= pComBuf->copyOutBytes ( &pCharBuf[nBytes-bytesLeft], bytesLeft ); + if ( ! pComBuf ) { + return totalBytes; + } + totalBytes += pComBuf->copyOutBytes ( &pCharBuf[totalBytes], nBytes - totalBytes ); if ( pComBuf->occupiedBytes () == 0u ) { this->bufs.remove ( *pComBuf ); pComBuf->destroy (); } } + while ( totalBytes < nBytes ); + return totalBytes; +} - return true; +unsigned comQueRecv::removeBytes ( unsigned nBytes ) +{ + unsigned totalBytes = 0u; + unsigned bytesLeft = nBytes; + while ( bytesLeft ) { + comBuf * pComBuf = this->bufs.first (); + if ( ! pComBuf ) { + return totalBytes; + } + unsigned nBytes = pComBuf->removeBytes ( bytesLeft ); + if ( pComBuf->occupiedBytes () == 0u ) { + this->bufs.remove ( *pComBuf ); + pComBuf->destroy (); + } + if ( nBytes == 0u) { + return totalBytes; + } + totalBytes += nBytes; + bytesLeft = nBytes - totalBytes; + } + return nBytes; } void comQueRecv::pushLastComBufReceived ( comBuf & bufIn ) @@ -98,3 +118,19 @@ void comQueRecv::pushLastComBufReceived ( comBuf & bufIn ) } } +epicsUInt8 comQueRecv::popUInt8 () +{ + comBuf *pComBuf = this->bufs.first (); + if ( pComBuf ) { + epicsUInt8 tmp = pComBuf->getByte (); + if ( pComBuf->occupiedBytes() == 0u ) { + this->bufs.remove ( *pComBuf ); + pComBuf->destroy (); + } + return tmp; + } + throw insufficentBytesAvailable (); +} + + + diff --git a/src/ca/comQueSend.cpp b/src/ca/comQueSend.cpp index 3e8ff6abf..549c19a91 100644 --- a/src/ca/comQueSend.cpp +++ b/src/ca/comQueSend.cpp @@ -87,66 +87,36 @@ void comQueSend::clear () this->nBytesPending -= pBuf->occupiedBytes (); pBuf->destroy (); } - this->reservoir.drain (); -} - - -// reserve sufficent space for entire message -// (this allows the recv thread to add a message -// to the que while some other thread is flushing -// and therefore prevents deadlocks, and it also -// allows proper status to be returned) -void comQueSend::reserveSpace ( unsigned msgSize ) -{ - unsigned bytesReserved; - - bytesReserved = this->reservoir.nBytes (); - - comBuf *pComBuf = this->bufs.last (); - if ( pComBuf ) { - bytesReserved += pComBuf->unoccupiedBytes (); - } - - while ( bytesReserved < msgSize ) { - reservoir.addOneBuffer (); - bytesReserved += comBuf::capacityBytes (); - } } void comQueSend::copy_dbr_string ( const void *pValue, unsigned nElem ) { - comQueSend_copyIn ( this->nBytesPending, this->bufs, this->reservoir, - static_cast ( pValue ), nElem ); + this->copyIn ( static_cast ( pValue ), nElem ); } void comQueSend::copy_dbr_short ( const void *pValue, unsigned nElem ) { - comQueSend_copyIn ( this->nBytesPending, this->bufs, this->reservoir, - static_cast ( pValue ), nElem ); + this->copyIn ( static_cast ( pValue ), nElem ); } void comQueSend::copy_dbr_float ( const void *pValue, unsigned nElem ) { - comQueSend_copyIn ( this->nBytesPending, this->bufs, this->reservoir, - static_cast ( pValue ), nElem ); + this->copyIn ( static_cast ( pValue ), nElem ); } void comQueSend::copy_dbr_char ( const void *pValue, unsigned nElem ) { - comQueSend_copyIn ( this->nBytesPending, this->bufs, this->reservoir, - static_cast ( pValue ), nElem ); + this->copyIn ( static_cast ( pValue ), nElem ); } void comQueSend::copy_dbr_long ( const void *pValue, unsigned nElem ) { - comQueSend_copyIn ( this->nBytesPending, this->bufs, this->reservoir, - static_cast ( pValue ), nElem ); + this->copyIn ( static_cast ( pValue ), nElem ); } void comQueSend::copy_dbr_double ( const void *pValue, unsigned nElem ) { - comQueSend_copyIn ( this->nBytesPending, this->bufs, this->reservoir, - static_cast ( pValue ), nElem ); + this->copyIn ( static_cast ( pValue ), nElem ); } const comQueSend::copyFunc_t comQueSend::dbrCopyVector [39] = { diff --git a/src/ca/convert.cpp b/src/ca/convert.cpp index 9cb831aa2..466799f71 100644 --- a/src/ca/convert.cpp +++ b/src/ca/convert.cpp @@ -21,6 +21,8 @@ #include "iocinf.h" #include "caProto.h" +typedef unsigned long arrayElementCount; + #define epicsExportSharedSymbols #include "net_convert.h" @@ -55,10 +57,10 @@ * */ LOCAL void cvrt_string( -const void *s, /* source */ -void *d, /* destination */ -int encode, /* cvrt HOST to NET if T */ -unsigned long num /* number of values */ +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ ) { char *pSrc = (char *) s; @@ -79,15 +81,15 @@ unsigned long num /* number of values */ * CVRT_SHORT() */ LOCAL void cvrt_short( -const void *s, /* source */ -void *d, /* destination */ -int encode, /* cvrt HOST to NET if T */ -unsigned long num /* number of values */ +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ ) { - dbr_short_t *pSrc = (dbr_short_t *) s; - dbr_short_t *pDest = (dbr_short_t *) d; - unsigned long i; + dbr_short_t *pSrc = (dbr_short_t *) s; + dbr_short_t *pDest = (dbr_short_t *) d; + arrayElementCount i; for(i=0; i nothing to do */ if (s == d) @@ -129,15 +131,15 @@ unsigned long num /* number of values */ * CVRT_LONG() */ LOCAL void cvrt_long( -const void *s, /* source */ -void *d, /* destination */ -int encode, /* cvrt HOST to NET if T */ -unsigned long num /* number of values */ +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ ) { - unsigned long i; - dbr_long_t *pSrc = (dbr_long_t *) s; - dbr_long_t *pDest = (dbr_long_t *) d; + arrayElementCount i; + dbr_long_t *pSrc = (dbr_long_t *) s; + dbr_long_t *pDest = (dbr_long_t *) d; for(i=0; ipPrivate; @@ -47,7 +47,7 @@ void getCallback::completion ( void getCallback::exception ( int status, const char * /* pContext */, - unsigned type, unsigned long count ) + unsigned type, arrayElementCount count ) { struct event_handler_args args; args.usr = this->pPrivate; diff --git a/src/ca/getCopy.cpp b/src/ca/getCopy.cpp index eb473a3d1..49573ecfb 100644 --- a/src/ca/getCopy.cpp +++ b/src/ca/getCopy.cpp @@ -24,9 +24,9 @@ tsFreeList < class getCopy, 1024 > getCopy::freeList; epicsMutex getCopy::freeListMutex; -getCopy::getCopy ( oldCAC &cacCtxIn, unsigned typeIn, - unsigned long countIn, void *pValueIn ) : - count ( countIn ), cacCtx ( cacCtxIn ), pValue ( pValueIn ), +getCopy::getCopy ( oldCAC &cacCtxIn, oldChannelNotify &chanIn, unsigned typeIn, + arrayElementCount countIn, void *pValueIn ) : + count ( countIn ), cacCtx ( cacCtxIn ), chan ( chanIn ), pValue ( pValueIn ), readSeq ( cacCtxIn.sequenceNumberOfOutstandingIO () ), type ( typeIn ) { cacCtxIn.incrementOutstandingIO (); @@ -37,7 +37,7 @@ getCopy::~getCopy () } void getCopy::completion ( unsigned typeIn, - unsigned long countIn, const void *pDataIn ) + arrayElementCount countIn, const void *pDataIn ) { if ( this->type == typeIn ) { memcpy ( this->pValue, pDataIn, dbr_size_n ( typeIn, countIn ) ); @@ -52,10 +52,10 @@ void getCopy::completion ( unsigned typeIn, } void getCopy::exception ( - int status, const char *pContext, unsigned typeIn, unsigned long countIn ) + int status, const char *pContext, unsigned typeIn, arrayElementCount countIn ) { - this->cacCtx.exception ( status, pContext, typeIn, - countIn, __FILE__, __LINE__ ); + this->cacCtx.exception ( status, pContext, + __FILE__, __LINE__, this->chan, this->type, this->count, CA_OP_GET ); delete this; } diff --git a/src/ca/inetAddrID.h b/src/ca/inetAddrID.h index 6190b5b7b..5ebc67d07 100644 --- a/src/ca/inetAddrID.h +++ b/src/ca/inetAddrID.h @@ -23,6 +23,7 @@ public: resTableIndex hash ( unsigned nBitsHashIndex ) const; static unsigned maxIndexBitWidth (); static unsigned minIndexBitWidth (); + void name ( char *pBuf, unsigned bufSize ) const; private: const struct sockaddr_in addr; }; @@ -60,6 +61,11 @@ inline unsigned inetAddrID::minIndexBitWidth () return 8u; } +inline void inetAddrID::name ( char *pBuf, unsigned bufSize ) const +{ + ipAddrToDottedIP ( &this->addr, pBuf, bufSize ); +} + #endif // ifdef inetAddrID diff --git a/src/ca/iocinf.h b/src/ca/iocinf.h index 94aef5991..bd842fca1 100644 --- a/src/ca/iocinf.h +++ b/src/ca/iocinf.h @@ -22,7 +22,7 @@ #define NO_PLACEMENT_DELETE #ifdef DEBUG -# define debugPrintf(argsInParen) printf argsInParen +# define debugPrintf(argsInParen) ::printf argsInParen #else # define debugPrintf(argsInParen) #endif @@ -38,18 +38,6 @@ #define MSEC_PER_SEC 1000L #define USEC_PER_SEC 1000000L -/* - * these control the duration and period of name resolution - * broadcasts - */ -#define MAXCONNTRIES 100 /* N conn retries on unchanged net */ - -#define INITIALTRIESPERFRAME 1u /* initial UDP frames per search try */ -#define MAXTRIESPERFRAME 64u /* max UDP frames per search try */ - -#define CA_RECAST_DELAY 0.5 /* initial delay to next search (sec) */ -#define CA_RECAST_PORT_MASK 0xff /* additional random search interval from port */ -#define CA_RECAST_PERIOD 5.0 /* quiescent search period (sec) */ #if defined (CLOCKS_PER_SEC) # define CAC_SIGNIFICANT_SELECT_DELAY ( 1.0 / CLOCKS_PER_SEC ) diff --git a/src/ca/nciu.cpp b/src/ca/nciu.cpp index bac541c00..fca7b0e0e 100644 --- a/src/ca/nciu.cpp +++ b/src/ca/nciu.cpp @@ -81,7 +81,7 @@ nciu::~nciu () } void nciu::connect ( unsigned nativeType, - unsigned long nativeCount, unsigned sidIn ) + unsigned nativeCount, unsigned sidIn ) { bool v41Ok; @@ -99,6 +99,12 @@ void nciu::connect ( unsigned nativeType, return; } + if ( ! dbf_type_is_valid ( nativeType ) ) { + this->cacCtx.printf ( + "CAC: Ignored conn resp with bad native data type CID=%u SID=%u?\n", + this->getId (), sidIn ); + return; + } if ( ! this->f_connectTimeOutSeen && ! this->f_previousConn ) { if ( this->f_firstConnectDecrementsOutstandingIO ) { @@ -112,7 +118,7 @@ void nciu::connect ( unsigned nativeType, else { v41Ok = false; } - this->typeCode = nativeType; + this->typeCode = static_cast < unsigned short > ( nativeType ); this->count = nativeCount; this->sid = sidIn; this->f_connected = true; @@ -131,6 +137,11 @@ void nciu::connect ( unsigned nativeType, // resubscribe for monitors from this channel this->cacCtx.connectAllIO ( *this ); +static bool once = 0; +if ( ! once ) { + printf ( "there is a problem here where we are calling through a defunct vf table\n" ); + once = 1; +} this->notify().connectNotify (); /* @@ -189,7 +200,7 @@ bool nciu::searchMsg ( unsigned short retrySeqNumber, unsigned &retryNoForThisCh msg.m_cmmd = htons ( CA_PROTO_SEARCH ); msg.m_available = this->getId (); msg.m_dataType = htons ( DONTREPLY ); - msg.m_count = htons ( CA_MINOR_VERSION ); + msg.m_count = htons ( CA_MINOR_PROTOCOL_REVISION ); msg.m_cid = this->getId (); success = this->piiu->pushDatagramMsg ( msg, @@ -199,7 +210,7 @@ bool nciu::searchMsg ( unsigned short retrySeqNumber, unsigned &retryNoForThisCh // increment the number of times we have tried // to find this channel // - if ( this->retry < MAXCONNTRIES ) { + if ( this->retry < UINT_MAX ) { this->retry++; } this->retrySeqNo = retrySeqNumber; @@ -226,7 +237,7 @@ void nciu::createChannelRequest () this->f_claimSent = true; } -cacChannel::ioStatus nciu::read ( unsigned type, unsigned long countIn, +cacChannel::ioStatus nciu::read ( unsigned type, arrayElementCount countIn, cacReadNotify ¬ify, ioid *pId ) { // @@ -270,7 +281,7 @@ void nciu::stringVerify ( const char *pStr, const unsigned count ) } void nciu::write ( unsigned type, - unsigned long countIn, const void *pValue ) + arrayElementCount countIn, const void *pValue ) { if ( ! this->accessRightState.writePermit() ) { throw noWriteAccess(); @@ -287,7 +298,7 @@ void nciu::write ( unsigned type, this->cacCtx.writeRequest ( *this, type, countIn, pValue ); } -cacChannel::ioStatus nciu::write ( unsigned type, unsigned long countIn, +cacChannel::ioStatus nciu::write ( unsigned type, arrayElementCount countIn, const void *pValue, cacWriteNotify ¬ify, ioid *pId ) { if ( ! this->accessRightState.writePermit() ) { @@ -309,7 +320,7 @@ cacChannel::ioStatus nciu::write ( unsigned type, unsigned long countIn, return cacChannel::iosAsynch; } -void nciu::subscribe ( unsigned type, unsigned long nElem, +void nciu::subscribe ( unsigned type, arrayElementCount nElem, unsigned mask, cacStateNotify ¬ify, ioid *pId ) { if ( INVALID_DB_REQ(type) ) { @@ -380,10 +391,10 @@ short nciu::nativeType () const return type; } -unsigned long nciu::nativeElementCount () const +arrayElementCount nciu::nativeElementCount () const { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); - unsigned long countOut; + arrayElementCount countOut; if ( this->f_connected ) { countOut = this->count; } @@ -471,4 +482,3 @@ void nciu::show ( unsigned level ) const - diff --git a/src/ca/nciu.h b/src/ca/nciu.h index 61374c356..559672572 100644 --- a/src/ca/nciu.h +++ b/src/ca/nciu.h @@ -23,6 +23,7 @@ #include "tsFreeList.h" #include "epicsMutex.h" +#define CA_MINOR_PROTOCOL_REVISION 9 #include "caProto.h" #define epicsExportSharedSymbols @@ -44,7 +45,7 @@ public: nciu ( cac &, netiiu &, cacChannelNotify &, const char *pNameIn ); void connect ( unsigned nativeType, - unsigned long nativeCount, unsigned sid ); + unsigned nativeCount, unsigned sid ); void connect (); void disconnect ( netiiu &newiiu ); bool searchMsg ( unsigned short retrySeqNumber, @@ -61,15 +62,16 @@ public: netiiu * getPIIU (); cac & getClient (); void searchReplySetUp ( netiiu &iiu, unsigned sidIn, - unsigned typeIn, unsigned long countIn ); + ca_uint16_t typeIn, arrayElementCount countIn ); void show ( unsigned level ) const; void connectTimeoutNotify (); const char *pName () const; unsigned nameLen () const; const char * pHostName () const; // deprecated - please do not use - unsigned long nativeElementCount () const; + arrayElementCount nativeElementCount () const; bool connected () const; bool previouslyConnected () const; + void writeException ( int status, const char *pContext, unsigned type, arrayElementCount count ); protected: ~nciu (); // force pool allocation private: @@ -80,22 +82,22 @@ private: netiiu *piiu; ca_uint32_t sid; // server id unsigned retry; // search retry number - unsigned short retrySeqNo; // search retry seq number - unsigned short nameLength; // channel name length - unsigned short typeCode; + ca_uint16_t retrySeqNo; // search retry seq number + ca_uint16_t nameLength; // channel name length + ca_uint16_t typeCode; unsigned f_connected:1; unsigned f_previousConn:1; // T if connected in the past unsigned f_claimSent:1; unsigned f_firstConnectDecrementsOutstandingIO:1; unsigned f_connectTimeOutSeen:1; void initiateConnect (); - ioStatus read ( unsigned type, unsigned long count, + ioStatus read ( unsigned type, arrayElementCount count, cacReadNotify &, ioid * ); - void write ( unsigned type, unsigned long count, + void write ( unsigned type, arrayElementCount count, const void *pValue ); - ioStatus write ( unsigned type, unsigned long count, + ioStatus write ( unsigned type, arrayElementCount count, const void *pValue, cacWriteNotify &, ioid * ); - void subscribe ( unsigned type, unsigned long nElem, + void subscribe ( unsigned type, arrayElementCount nElem, unsigned mask, cacStateNotify ¬ify, ioid * ); void ioCancel ( const ioid & ); void ioShow ( const ioid &, unsigned level ) const; @@ -161,7 +163,7 @@ inline void nciu::connect () } inline void nciu::searchReplySetUp ( netiiu &iiu, unsigned sidIn, - unsigned typeIn, unsigned long countIn ) + ca_uint16_t typeIn, arrayElementCount countIn ) { this->piiu = &iiu; this->typeCode = typeIn; @@ -194,4 +196,10 @@ inline void nciu::connectTimeoutNotify () this->f_connectTimeOutSeen = true; } +inline void nciu::writeException ( int status, + const char *pContext, unsigned type, arrayElementCount count ) +{ + this->notify().writeException ( status, pContext, type, count ); +} + #endif // ifdef nciuh diff --git a/src/ca/netIO.h b/src/ca/netIO.h index ccc55b19d..7ae4b2e22 100644 --- a/src/ca/netIO.h +++ b/src/ca/netIO.h @@ -27,11 +27,13 @@ public: virtual class netSubscription * isSubscription (); virtual void destroy ( class cacRecycle & ) = 0; // only called by cac virtual void completion () = 0; - virtual void exception ( int status, const char *pContext ) = 0; - virtual void completion ( unsigned type, - unsigned long count, const void *pData ) = 0; virtual void exception ( int status, - const char *pContext, unsigned type, unsigned long count ) = 0; + const char *pContext ) = 0; + virtual void exception ( int status, + const char *pContext, unsigned type, + arrayElementCount count ) = 0; + virtual void completion ( unsigned type, + arrayElementCount count, const void *pData ) = 0; void show ( unsigned level ) const; ca_uint32_t getID () const; nciu & channel () const; @@ -48,27 +50,29 @@ class netSubscription : public baseNMIU { public: static netSubscription * factory ( tsFreeList < class netSubscription, 1024 > &, - nciu &chan, unsigned type, unsigned long count, + nciu &chan, unsigned type, arrayElementCount count, unsigned mask, cacStateNotify ¬ify ); void show ( unsigned level ) const; - unsigned long getCount () const; + arrayElementCount getCount () const; unsigned getType () const; unsigned getMask () const; void destroy ( class cacRecycle & ); void completion (); - void exception ( int status, const char *pContext ); - void completion ( unsigned type, - unsigned long count, const void *pData ); void exception ( int status, - const char *pContext, unsigned type, unsigned long count ); + const char *pContext ); + void completion ( unsigned type, + arrayElementCount count, const void *pData ); + void exception ( int status, + const char *pContext, unsigned type, + arrayElementCount count ); protected: ~netSubscription (); private: - const unsigned long count; + const arrayElementCount count; cacStateNotify ¬ify; const unsigned type; const unsigned mask; - netSubscription ( nciu &chan, unsigned type, unsigned long count, + netSubscription ( nciu &chan, unsigned type, arrayElementCount count, unsigned mask, cacStateNotify ¬ify ); class netSubscription * isSubscription (); void * operator new ( size_t, @@ -89,9 +93,9 @@ public: void completion (); void exception ( int status, const char *pContext ); void completion ( unsigned type, - unsigned long count, const void *pData ); - void exception ( int status, - const char *pContext, unsigned type, unsigned long count ); + arrayElementCount count, const void *pData ); + void exception ( int status, const char *pContext, + unsigned type, arrayElementCount count ); protected: ~netReadNotifyIO (); private: @@ -115,9 +119,9 @@ public: void completion (); void exception ( int status, const char *pContext ); void completion ( unsigned type, - unsigned long count, const void *pData ); - void exception ( int status, - const char *pContext, unsigned type, unsigned long count ); + arrayElementCount count, const void *pData ); + void exception ( int status, const char *pContext, + unsigned type, arrayElementCount count ); protected: ~netWriteNotifyIO (); private: @@ -143,7 +147,7 @@ inline class nciu & baseNMIU::channel () const inline netSubscription * netSubscription::factory ( tsFreeList < class netSubscription, 1024 > &freeList, - nciu &chan, unsigned type, unsigned long count, + nciu &chan, unsigned type, arrayElementCount count, unsigned mask, cacStateNotify ¬ify ) { return new ( freeList ) netSubscription ( chan, type, @@ -169,9 +173,9 @@ inline void netSubscription::operator delete ( void *pCadaver, size_t size, } #endif -inline unsigned long netSubscription::getCount () const +inline arrayElementCount netSubscription::getCount () const { - unsigned long nativeCount = this->chan.nativeElementCount (); + arrayElementCount nativeCount = this->chan.nativeElementCount (); if ( this->count == 0u || this->count > nativeCount ) { return nativeCount; } diff --git a/src/ca/netReadNotifyIO.cpp b/src/ca/netReadNotifyIO.cpp index 922195c47..5d6a8560a 100644 --- a/src/ca/netReadNotifyIO.cpp +++ b/src/ca/netReadNotifyIO.cpp @@ -48,20 +48,20 @@ void netReadNotifyIO::completion () void netReadNotifyIO::exception ( int status, const char *pContext ) { - this->notify.exception ( status, pContext, UINT_MAX, 0 ); + this->notify.exception ( status, pContext, UINT_MAX, 0u ); } -void netReadNotifyIO::completion ( unsigned type, - unsigned long count, const void *pData ) -{ - this->notify.completion ( type, count, pData ); -} - -void netReadNotifyIO::exception ( int status, - const char *pContext, unsigned type, unsigned long count ) +void netReadNotifyIO::exception ( int status, const char *pContext, + unsigned type, arrayElementCount count ) { this->notify.exception ( status, pContext, type, count ); } +void netReadNotifyIO::completion ( unsigned type, + arrayElementCount count, const void *pData ) +{ + this->notify.completion ( type, count, pData ); +} + diff --git a/src/ca/netSubscription.cpp b/src/ca/netSubscription.cpp index a2577c5ed..450276d8e 100644 --- a/src/ca/netSubscription.cpp +++ b/src/ca/netSubscription.cpp @@ -22,11 +22,17 @@ #undef epicsExportSharedSymbols netSubscription::netSubscription ( nciu &chan, - unsigned typeIn, unsigned long countIn, + unsigned typeIn, arrayElementCount countIn, unsigned maskIn, cacStateNotify ¬ifyIn ) : baseNMIU ( chan ), count ( countIn ), notify ( notifyIn ), type ( typeIn ), mask ( maskIn ) { + if ( ! dbr_type_is_valid ( typeIn ) ) { + throw cacChannel::badType (); + } + if ( this->mask == 0u ) { + throw cacChannel::badEventSelection (); + } } netSubscription::~netSubscription () @@ -65,16 +71,17 @@ void netSubscription::exception ( int status, const char *pContext ) this->notify.exception ( status, pContext, UINT_MAX, 0 ); } +void netSubscription::exception ( int status, const char *pContext, + unsigned type, arrayElementCount count ) +{ + this->notify.exception ( status, pContext, type, count ); +} + void netSubscription::completion ( unsigned typeIn, - unsigned long countIn, const void *pDataIn ) + arrayElementCount countIn, const void *pDataIn ) { this->notify.current ( typeIn, countIn, pDataIn ); } -void netSubscription::exception ( int status, - const char *pContext, unsigned typeIn, unsigned long countIn ) -{ - this->notify.exception ( status, pContext, typeIn, countIn ); -} diff --git a/src/ca/netWriteNotifyIO.cpp b/src/ca/netWriteNotifyIO.cpp index 97b9c5c0e..3e93b260f 100644 --- a/src/ca/netWriteNotifyIO.cpp +++ b/src/ca/netWriteNotifyIO.cpp @@ -48,21 +48,22 @@ void netWriteNotifyIO::completion () void netWriteNotifyIO::exception ( int status, const char *pContext ) { - this->notify.exception ( status, pContext ); + this->notify.exception ( status, pContext, UINT_MAX, 0u ); } +void netWriteNotifyIO::exception ( int status, const char *pContext, + unsigned type, arrayElementCount count ) +{ + this->notify.exception ( status, pContext, type, count ); +} + + void netWriteNotifyIO::completion ( unsigned /* type */, - unsigned long /* count */, const void * /* pData */ ) + arrayElementCount /* count */, const void * /* pData */ ) { this->chan.getClient().printf ( "Write response with data ?\n" ); } -void netWriteNotifyIO::exception ( int status, - const char *pContext, unsigned /* type */, unsigned long /* count */ ) -{ - this->notify.exception ( status, pContext ); -} - diff --git a/src/ca/net_convert.h b/src/ca/net_convert.h index d00c90d1d..539b73523 100644 --- a/src/ca/net_convert.h +++ b/src/ca/net_convert.h @@ -67,7 +67,7 @@ extern "C" { * net format: big endian and IEEE float */ -typedef void CACVRTFUNC (const void *pSrc, void *pDest, int hton, unsigned long count); +typedef void CACVRTFUNC (const void *pSrc, void *pDest, int hton, arrayElementCount count); #ifdef CONVERSION_REQUIRED /* cvrt is (array of) (pointer to) (function returning) int */ diff --git a/src/ca/oldAccess.h b/src/ca/oldAccess.h index eb2edaf98..68d207e02 100644 --- a/src/ca/oldAccess.h +++ b/src/ca/oldAccess.h @@ -44,24 +44,24 @@ public: void show ( unsigned level ) const; void initiateConnect (); void read ( - unsigned type, unsigned long count, + unsigned type, arrayElementCount count, cacReadNotify ¬ify, cacChannel::ioid *pId = 0 ); void read ( - unsigned type, unsigned long count, + unsigned type, arrayElementCount count, void *pValue ); void write ( - unsigned type, unsigned long count, + unsigned type, arrayElementCount count, const void *pValue ); void write ( - unsigned type, unsigned long count, const void *pValue, + unsigned type, arrayElementCount count, const void *pValue, cacWriteNotify &, cacChannel::ioid *pId = 0 ); void subscribe ( - unsigned type, unsigned long count, unsigned mask, + unsigned type, arrayElementCount count, unsigned mask, cacStateNotify &, cacChannel::ioid & ); void ioCancel ( const cacChannel::ioid & ); void ioShow ( const cacChannel::ioid &, unsigned level ) const; short nativeType () const; - unsigned long nativeElementCount () const; + arrayElementCount nativeElementCount () const; caAccessRights accessRights () const; // defaults to unrestricted access unsigned searchAttempts () const; // defaults to zero double beaconPeriod () const; // defaults to negative DBL_MAX @@ -81,6 +81,10 @@ private: void disconnectNotify (); void accessRightsNotify ( const caAccessRights & ); void exception ( int status, const char *pContext ); + void readException ( int status, const char *pContext, + unsigned type, arrayElementCount count, void *pValue ); + void writeException ( int status, const char *pContext, + unsigned type, arrayElementCount count ); bool includeFirstConnectInCountOfOutstandingIO () const; static tsFreeList < struct oldChannelNotify, 1024 > freeList; static epicsMutex freeListMutex; @@ -88,8 +92,8 @@ private: class getCopy : public cacReadNotify { public: - getCopy ( oldCAC &cacCtx, unsigned type, - unsigned long count, void *pValue ); + getCopy ( oldCAC &cacCtx, oldChannelNotify &, unsigned type, + arrayElementCount count, void *pValue ); void destroy (); void show ( unsigned level ) const; void * operator new ( size_t size ); @@ -97,15 +101,16 @@ public: protected: ~getCopy (); // allocate only out of pool private: - unsigned long count; + arrayElementCount count; oldCAC &cacCtx; + oldChannelNotify &chan; void *pValue; unsigned readSeq; unsigned type; void completion ( - unsigned type, unsigned long count, const void *pData); - void exception ( - int status, const char *pContext, unsigned type, unsigned long count ); + unsigned type, arrayElementCount count, const void *pData); + void exception ( int status, + const char *pContext, unsigned type, arrayElementCount count ); static tsFreeList < class getCopy, 1024 > freeList; static epicsMutex freeListMutex; }; @@ -124,9 +129,9 @@ private: caEventCallBackFunc *pFunc; void *pPrivate; void completion ( - unsigned type, unsigned long count, const void *pData); - void exception ( - int status, const char *pContext, unsigned type, unsigned long count ); + unsigned type, arrayElementCount count, const void *pData); + void exception ( int status, + const char *pContext, unsigned type, arrayElementCount count ); static tsFreeList < class getCallback, 1024 > freeList; static epicsMutex freeListMutex; }; @@ -145,8 +150,8 @@ private: caEventCallBackFunc *pFunc; void *pPrivate; void completion (); - void exception ( - int status, const char *pContext ); + void exception ( int status, const char *pContext, + unsigned type, arrayElementCount count ); static tsFreeList < class putCallback, 1024 > freeList; static epicsMutex freeListMutex; }; @@ -155,7 +160,7 @@ struct oldSubscription : public cacStateNotify { public: oldSubscription ( oldChannelNotify &, - unsigned type, unsigned long nElem, unsigned mask, + unsigned type, arrayElementCount nElem, unsigned mask, caEventCallBackFunc *pFunc, void *pPrivate ); bool ioAttachOK (); void destroy (); @@ -171,9 +176,9 @@ private: void *pPrivate; bool subscribed; void current ( - unsigned type, unsigned long count, const void *pData ); - void exception ( - int status, const char *pContext, unsigned type, unsigned long count ); + unsigned type, arrayElementCount count, const void *pData ); + void exception ( int status, + const char *pContext, unsigned type, arrayElementCount count ); static tsFreeList < struct oldSubscription, 1024 > freeList; static epicsMutex freeListMutex; }; @@ -197,22 +202,24 @@ public: unsigned sequenceNumberOfOutstandingIO () const; void incrementOutstandingIO (); void decrementOutstandingIO ( unsigned sequenceNo ); - void exception ( int status, const char *pContext, + void exception ( int status, const char *pContext, const char *pFileName, unsigned lineNo ); void exception ( int status, const char *pContext, - unsigned type, unsigned long count, - const char *pFileName, unsigned lineNo ); -// perhaps these should be eliminated in deference to the exception mechanism - int printf ( const char *pformat, ... ); - int vPrintf ( const char *pformat, va_list args ); + const char *pFileName, unsigned lineNo, oldChannelNotify &chan, + unsigned type, arrayElementCount count, unsigned op ); CASG * lookupCASG ( unsigned id ); void installCASG ( CASG & ); void uninstallCASG ( CASG & ); void enableCallbackPreemption (); void disableCallbackPreemption (); +// perhaps these should be eliminated in deference to the exception mechanism + int printf ( const char *pformat, ... ) const; + int vPrintf ( const char *pformat, va_list args ) const; + void vSignal ( int ca_status, const char *pfilenm, + int lineno, const char *pFormat, va_list args ); private: - cac & clientCtx; mutable epicsMutex mutex; + cac & clientCtx; caExceptionHandler *ca_exception_func; void *ca_exception_arg; caPrintfFunc *pVPrintfFunc; @@ -221,6 +228,7 @@ private: // this should probably be phased out (its not OS independent) void fdWasCreated ( int fd ); void fdWasDestroyed ( int fd ); + void attachToClientCtx (); }; int fetchClientContext ( oldCAC **ppcac ); @@ -250,26 +258,26 @@ inline void oldChannelNotify::initiateConnect () this->io.initiateConnect (); } -inline void oldChannelNotify::read ( unsigned type, unsigned long count, +inline void oldChannelNotify::read ( unsigned type, arrayElementCount count, cacReadNotify ¬ify, cacChannel::ioid *pId ) { this->io.read ( type, count, notify, pId ); } inline void oldChannelNotify::write ( unsigned type, - unsigned long count, const void *pValue ) + arrayElementCount count, const void *pValue ) { this->io.write ( type, count, pValue ); } -inline void oldChannelNotify::write ( unsigned type, unsigned long count, +inline void oldChannelNotify::write ( unsigned type, arrayElementCount count, const void *pValue, cacWriteNotify ¬ify, cacChannel::ioid *pId ) { this->io.write ( type, count, pValue, notify, pId ); } inline void oldChannelNotify::subscribe ( unsigned type, - unsigned long count, unsigned mask, cacStateNotify ¬ify, + arrayElementCount count, unsigned mask, cacStateNotify ¬ify, cacChannel::ioid &idOut) { this->io.subscribe ( type, count, mask, notify, &idOut ); @@ -290,7 +298,7 @@ inline short oldChannelNotify::nativeType () const return this->io.nativeType (); } -inline unsigned long oldChannelNotify::nativeElementCount () const +inline arrayElementCount oldChannelNotify::nativeElementCount () const { return this->io.nativeElementCount (); } @@ -337,7 +345,7 @@ inline const char * oldChannelNotify::pHostName () const inline oldSubscription::oldSubscription ( oldChannelNotify &chanIn, - unsigned type, unsigned long nElem, unsigned mask, + unsigned type, arrayElementCount nElem, unsigned mask, caEventCallBackFunc *pFuncIn, void *pPrivateIn ) : chan ( chanIn ), id ( 0 ), pFunc ( pFuncIn ), pPrivate ( pPrivateIn ), subscribed ( false ) @@ -497,4 +505,11 @@ inline void oldCAC::disableCallbackPreemption () this->clientCtx.disableCallbackPreemption (); } +inline void oldCAC::vSignal ( int ca_status, const char *pfilenm, + int lineno, const char *pFormat, va_list args ) +{ + this->clientCtx.vSignal ( ca_status, pfilenm, + lineno, pFormat, args ); +} + #endif // ifndef oldAccessh diff --git a/src/ca/oldCAC.cpp b/src/ca/oldCAC.cpp index e2a513d62..555aa6b44 100644 --- a/src/ca/oldCAC.cpp +++ b/src/ca/oldCAC.cpp @@ -14,13 +14,15 @@ * 505 665 1831 */ +#include + #include "iocinf.h" #include "oldAccess.h" oldCAC::oldCAC ( bool enablePreemptiveCallback ) : clientCtx ( * new cac ( *this, enablePreemptiveCallback ) ), - ca_exception_func ( ca_default_exception_handler ), ca_exception_arg ( 0 ), + ca_exception_func ( 0 ), ca_exception_arg ( 0 ), pVPrintfFunc ( errlogVprintf ), fdRegFunc ( 0 ), fdRegArg ( 0 ) { if ( ! & this->clientCtx ) { @@ -36,14 +38,8 @@ oldCAC::~oldCAC () void oldCAC::changeExceptionEvent ( caExceptionHandler *pfunc, void *arg ) { epicsAutoMutex autoMutex ( this->mutex ); - if ( pfunc ) { - this->ca_exception_func = pfunc; - this->ca_exception_arg = arg; - } - else { - this->ca_exception_func = ca_default_exception_handler; - this->ca_exception_arg = NULL; - } + this->ca_exception_func = pfunc; + this->ca_exception_arg = arg; // should block here until releated callback in progress completes } @@ -67,8 +63,21 @@ void oldCAC::registerForFileDescriptorCallBack ( CAFDHANDLER *pFunc, void *pArg // should block here until releated callback in progress completes } +int oldCAC::printf ( const char *pformat, ... ) const +{ + va_list theArgs; + int status; -int oldCAC::vPrintf ( const char *pformat, va_list args ) + va_start ( theArgs, pformat ); + + status = this->oldCAC::vPrintf ( pformat, theArgs ); + + va_end ( theArgs ); + + return status; +} + +int oldCAC::vPrintf ( const char *pformat, va_list args ) const { caPrintfFunc *pFunc; { @@ -79,18 +88,12 @@ int oldCAC::vPrintf ( const char *pformat, va_list args ) return ( *pFunc ) ( pformat, args ); } else { - return vfprintf ( stderr, pformat, args ); + return ::vfprintf ( stderr, pformat, args ); } } -void oldCAC::exception ( int stat, const char *pCtx, const char *pFile, unsigned lineNo ) -{ - this->exception ( stat, pCtx, UINT_MAX, 0, pFile, lineNo ); -} - -void oldCAC::exception ( int stat, const char *ctx, - unsigned type, unsigned long count, - const char *pFile, unsigned lineNo ) +void oldCAC::exception ( int stat, const char *pCtx, + const char *pFile, unsigned lineNo ) { struct exception_handler_args args; caExceptionHandler *pFunc; @@ -104,17 +107,54 @@ void oldCAC::exception ( int stat, const char *ctx, // NOOP if they disable exceptions if ( pFunc ) { args.chid = NULL; - args.type = type; - args.count = count; + args.type = TYPENOTCONN; + args.count = 0; args.addr = NULL; args.stat = stat; args.op = CA_OP_OTHER; - args.ctx = ctx; + args.ctx = pCtx; args.pFile = pFile; args.lineNo = lineNo; args.usr = pArg; ( *pFunc ) ( args ); } + else { + this->clientCtx.signal ( stat, pFile, lineNo, pCtx ); + } +} + +void oldCAC::exception ( int status, const char *pContext, + const char *pFileName, unsigned lineNo, oldChannelNotify &chan, + unsigned type, arrayElementCount count, unsigned op ) +{ + struct exception_handler_args args; + caExceptionHandler *pFunc; + void *pArg; + { + epicsAutoMutex autoMutex ( this->mutex ); + pFunc = this->ca_exception_func; + pArg = this->ca_exception_arg; + } + + // NOOP if they disable exceptions + if ( pFunc ) { + args.chid = &chan; + args.type = type; + args.count = count; + args.addr = NULL; + args.stat = status; + args.op = op; + args.ctx = pContext; + args.pFile = pFileName; + args.lineNo = lineNo; + args.usr = pArg; + ( *pFunc ) ( args ); + } + else { + this->clientCtx.signal ( status, pFileName, lineNo, + "op=%u, channel=%s, type=%s, count=lu, ctx=\"%s\"", + op, ca_name ( &chan ), dbr_type_to_text ( type ), count, pContext ); + } } void oldCAC::fdWasCreated ( int fd ) @@ -145,20 +185,6 @@ void oldCAC::fdWasDestroyed ( int fd ) } } -int oldCAC::printf ( const char *pformat, ... ) -{ - va_list theArgs; - int status; - - va_start ( theArgs, pformat ); - - status = this->vPrintf ( pformat, theArgs ); - - va_end ( theArgs ); - - return status; -} - void oldCAC::show ( unsigned level ) const { ::printf ( "oldCAC at %p\n", @@ -179,3 +205,11 @@ void oldCAC::show ( unsigned level ) const } } +void oldCAC::attachToClientCtx () +{ + int status = ca_attach_context ( this ); + SEVCHK ( status, "error in virtual attach to client context" ); +} + + + diff --git a/src/ca/oldChannelNotify.cpp b/src/ca/oldChannelNotify.cpp index 59b6e5b8a..793393dc6 100644 --- a/src/ca/oldChannelNotify.cpp +++ b/src/ca/oldChannelNotify.cpp @@ -113,6 +113,20 @@ void oldChannelNotify::exception ( int status, const char *pContext ) ca_signal ( status, pContext ); } +void oldChannelNotify::readException ( int status, const char *pContext, + unsigned type, arrayElementCount count, void *pValue ) +{ + ca_signal_formated ( status, 0, 0u, "ctx=%s type=%s count=%u ptr=%p", + pContext, dbr_type_to_text ( type ), count, pValue ); +} + +void oldChannelNotify::writeException ( int status, const char *pContext, + unsigned type, arrayElementCount count ) +{ + ca_signal_formated ( status, 0, 0u, "ctx=%s type=%s count=%u", + pContext, dbr_type_to_text ( type ), count ); +} + bool oldChannelNotify::includeFirstConnectInCountOfOutstandingIO () const { return ( this->pConnCallBack == cacNoopConnHandler ); diff --git a/src/ca/oldSubscription.cpp b/src/ca/oldSubscription.cpp index 513c6f331..771b5e23f 100644 --- a/src/ca/oldSubscription.cpp +++ b/src/ca/oldSubscription.cpp @@ -22,7 +22,7 @@ oldSubscription::~oldSubscription () } void oldSubscription::current ( - unsigned type, unsigned long count, const void *pData) + unsigned type, arrayElementCount count, const void *pData) { struct event_handler_args args; @@ -37,7 +37,7 @@ void oldSubscription::current ( void oldSubscription::exception ( int status, const char * /* pContext */, - unsigned type, unsigned long count ) + unsigned type, arrayElementCount count ) { struct event_handler_args args; diff --git a/src/ca/putCallback.cpp b/src/ca/putCallback.cpp index 382acd108..21f71fccb 100644 --- a/src/ca/putCallback.cpp +++ b/src/ca/putCallback.cpp @@ -46,14 +46,15 @@ void putCallback::completion () } void putCallback::exception ( - int status, const char * /* pContext */ ) + int status, const char * /* pContext */, + unsigned type, arrayElementCount count ) { struct event_handler_args args; args.usr = this->pPrivate; args.chid = & this->chan; - args.type = TYPENOTCONN; - args.count = 0; + args.type = type; + args.count = count; args.status = status; args.dbr = 0; ( *this->pFunc ) (args); diff --git a/src/ca/recvProcessThread.cpp b/src/ca/recvProcessThread.cpp index e87652191..fadf3c397 100644 --- a/src/ca/recvProcessThread.cpp +++ b/src/ca/recvProcessThread.cpp @@ -47,8 +47,8 @@ recvProcessThread::~recvProcessThread () void recvProcessThread::run () { - int status = ca_attach_context ( this->pcac ); - SEVCHK ( status, "attaching to client context in recv process thread" ); + this->pcac->attachToClientCtx (); + while ( ! this->shutDown ) { { diff --git a/src/ca/repeaterSubscribeTimer.cpp b/src/ca/repeaterSubscribeTimer.cpp index 3f819e998..f012cf18b 100644 --- a/src/ca/repeaterSubscribeTimer.cpp +++ b/src/ca/repeaterSubscribeTimer.cpp @@ -22,10 +22,10 @@ #undef epicsExportSharedSymbols repeaterSubscribeTimer::repeaterSubscribeTimer ( udpiiu &iiuIn, epicsTimerQueue &queueIn ) : - timer ( queueIn.createTimer ( *this ) ), iiu ( iiuIn ), + timer ( queueIn.createTimer () ), iiu ( iiuIn ), attempts ( 0 ), registered ( false ), once ( false ) { - this->timer.start ( 10.0 ); + this->timer.start ( *this, 10.0 ); } repeaterSubscribeTimer::~repeaterSubscribeTimer () @@ -33,7 +33,7 @@ repeaterSubscribeTimer::~repeaterSubscribeTimer () delete & this->timer; } -epicsTimerNotify::expireStatus repeaterSubscribeTimer::expire () +epicsTimerNotify::expireStatus repeaterSubscribeTimer::expire ( const epicsTime & currentTime ) { static const unsigned nTriesToMsg = 50; if ( this->attempts > nTriesToMsg && ! this->once ) { diff --git a/src/ca/repeaterSubscribeTimer.h b/src/ca/repeaterSubscribeTimer.h index e2c96c579..7bef66e9a 100644 --- a/src/ca/repeaterSubscribeTimer.h +++ b/src/ca/repeaterSubscribeTimer.h @@ -34,7 +34,7 @@ private: unsigned attempts; bool registered; bool once; - expireStatus expire (); + expireStatus expire ( const epicsTime & currentTime ); }; #endif // ifdef repeaterSubscribeTimerh diff --git a/src/ca/searchTimer.cpp b/src/ca/searchTimer.cpp index 539d12748..514f765da 100644 --- a/src/ca/searchTimer.cpp +++ b/src/ca/searchTimer.cpp @@ -23,22 +23,35 @@ #include "udpiiu.h" #undef epicsExportSharedSymbols +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 initialRoundTripEstimate = 0.250; // seconds +static const double minSearchPeriod = 0.030; // seconds +static const double maxSearchPeriod = 5.0; // seconds + // // searchTimer::searchTimer () // searchTimer::searchTimer ( udpiiu &iiuIn, epicsTimerQueue &queueIn, epicsMutex &mutexIn ) : - timer ( queueIn.createTimer ( *this ) ), + period ( initialRoundTripEstimate * 2.0 ), + roundTripDelayEstimate ( initialRoundTripEstimate ), + timer ( queueIn.createTimer () ), mutex ( mutexIn ), iiu ( iiuIn ), - framesPerTry ( INITIALTRIESPERFRAME ), + framesPerTry ( initialTriesPerFrame ), framesPerTryCongestThresh ( UINT_MAX ), minRetry ( UINT_MAX ), retry ( 0u ), - searchTriesWithinThisPass ( 0u ), - searchResponsesWithinThisPass ( 0u ), + searchAttempts ( 0u ), + searchResponses ( 0u ), + searchAttemptsThisPass ( 0u ), + searchResponsesThisPass ( 0u ), retrySeqNo ( 0u ), retrySeqAtPassBegin ( 0u ), - period ( CA_RECAST_DELAY ) + active ( false ), + noDelay ( false ) { } @@ -52,59 +65,68 @@ searchTimer::~searchTimer () // void searchTimer::resetPeriod ( double delayToNextTry ) { - bool reschedule; - - if ( delayToNextTry < CA_RECAST_DELAY ) { - delayToNextTry = CA_RECAST_DELAY; - } - - { - epicsAutoMutex locker ( this->mutex ); - this->retry = 0; - if ( this->period > delayToNextTry ) { - reschedule = true; + bool start; + + delayToNextTry += initialRoundTripEstimate; + + this->retry = 0; + if ( this->iiu.channelCount () > 0 ) { + if ( this->period > delayToNextTry || ! this->active ) { + this->active = true; + this->noDelay = delayToNextTry == 0.0; + start = true; } else { - reschedule = false; + start = false; } - this->period = CA_RECAST_DELAY; - } - - if ( reschedule ) { - this->timer.start ( delayToNextTry ); - debugPrintf ( ("rescheduled search timer for completion in %f sec\n", delayToNextTry) ); } else { - this->timer.start ( delayToNextTry ); - debugPrintf ( ("if inactive, search timer started to completion in %f sec\n", delayToNextTry) ); + start = false; + } + // upper bound + //this->period = initialRoundTripEstimate * 2.0; + double newPeriod = this->roundTripDelayEstimate * 2.0; + if ( newPeriod <= initialRoundTripEstimate * 2.0 ) { + this->period = newPeriod; + } + else { + this->period = initialRoundTripEstimate * 2.0; + } + // lower bound + if ( this->period < minSearchPeriod ) { + this->period = minSearchPeriod; + } + + if ( start ) { + epicsAutoMutexRelease autoRelease ( this->mutex ); + this->timer.start ( *this, delayToNextTry ); + // debugPrintf ( ("rescheduled search timer for completion in %f sec\n", delayToNextTry) ); } } /* * searchTimer::setRetryInterval () */ -void searchTimer::setRetryInterval (unsigned retryNo) +void searchTimer::setRetryInterval ( unsigned retryNo ) { unsigned idelay; double delay; - epicsAutoMutex locker ( this->mutex ); - /* * set the retry number */ - this->retry = tsMin ( retryNo, MAXCONNTRIES + 1u ); + this->retry = tsMin ( retryNo, maxSearchTries + 1u ); /* * set the retry interval */ idelay = 1u << tsMin ( static_cast < size_t > ( this->retry ), CHAR_BIT * sizeof ( idelay ) - 1u ); - delay = idelay * CA_RECAST_DELAY; /* sec */ - /* - * place upper limit on the retry delay - */ - this->period = tsMin ( CA_RECAST_PERIOD, delay ); + delay = idelay * this->roundTripDelayEstimate * 2.0; /* sec */ + //delay = idelay * initialRoundTripEstimate * 2.0; /* sec */ + + this->period = tsMin ( maxSearchPeriod, delay ); + this->period = tsMax ( minSearchPeriod, this->period ); debugPrintf ( ("new CA search period is %f sec\n", this->period) ); } @@ -116,31 +138,55 @@ void searchTimer::setRetryInterval (unsigned retryNo) // at least one response. However, dont reset this delay if we // get a delayed response to an old search request. // -void searchTimer::notifySearchResponse ( unsigned short retrySeqNoIn ) +void searchTimer::notifySearchResponse ( unsigned short retrySeqNoIn, + const epicsTime & currentTime ) { bool reschedualNeeded; - { - epicsAutoMutex locker ( this->mutex ); + if ( this->retrySeqAtPassBegin <= retrySeqNoIn ) { + if ( this->searchResponses < UINT_MAX ) { + this->searchResponses++; + } + } - if ( this->retrySeqAtPassBegin <= retrySeqNoIn ) { - if ( this->searchResponsesWithinThisPass < UINT_MAX ) { - this->searchResponsesWithinThisPass++; - } - } + if ( retrySeqNoIn == this->retrySeqNo && ! this->noDelay ) { + double curRTT = currentTime - this->timeAtLastRetry; + this->roundTripDelayEstimate = + ( this->roundTripDelayEstimate + curRTT ) / 2.0; + this->period = this->roundTripDelayEstimate * 2.0; + this->period = tsMin ( maxSearchPeriod, this->period ); + this->period = tsMax ( minSearchPeriod, this->period ); + reschedualNeeded = true; + this->active = true; + this->noDelay = true; + } - reschedualNeeded = ( retrySeqNoIn == this->retrySeqNo ); + if ( this->searchResponses == this->searchAttempts ) { + reschedualNeeded = true; + this->active = true; + this->noDelay = true; + } + else { + reschedualNeeded = false; } if ( reschedualNeeded ) { - this->timer.start ( 0.0 ); + epicsAutoMutexRelease autoRelease (this->mutex ); +# if defined(DEBUG) && 0 + char buf[64]; + epicsTime ts = epicsTime::getCurrent(); + ts.strftime ( buf, sizeof(buf), "%M:%S.%09f"); +# endif + // debugPrintf ( ( "Response set timer delay to zero. ts=%s, RTT=%f sec\n", + // buf, this->roundTripDelayEstimate ) ); + this->timer.start ( *this, currentTime ); } } // // searchTimer::expire () // -epicsTimerNotify::expireStatus searchTimer::expire () +epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTime ) { epicsAutoMutex locker ( this->mutex ); unsigned nFrameSent = 0u; @@ -150,6 +196,9 @@ epicsTimerNotify::expireStatus searchTimer::expire () * check to see if there is nothing to do here */ if ( this->iiu.channelCount () == 0 ) { + this->active = false; + this->noDelay = false; + debugPrintf ( ( "all channels located - search timer terminating\n" ) ); return noRestart; } @@ -157,6 +206,7 @@ epicsTimerNotify::expireStatus searchTimer::expire () * increment the retry sequence number */ this->retrySeqNo++; /* allowed to roll over */ + this->timeAtLastRetry = currentTime; /* * dynamically adjust the number of UDP frames per @@ -185,12 +235,12 @@ epicsTimerNotify::expireStatus searchTimer::expire () * increase frames per try only if we see better than * a 93.75% success rate for one pass through the list */ - if (this->searchResponsesWithinThisPass > - (this->searchTriesWithinThisPass-(this->searchTriesWithinThisPass/16u)) ) { + if ( this->searchResponses > + ( this->searchAttempts - (this->searchAttempts/16u) ) ) { /* * increase UDP frames per try if we have a good score */ - if ( this->framesPerTry < MAXTRIESPERFRAME ) { + if ( this->framesPerTry < maxTriesPerFrame ) { /* * a congestion avoidance threshold similar to TCP is now used */ @@ -201,24 +251,38 @@ epicsTimerNotify::expireStatus searchTimer::expire () this->framesPerTry += (this->framesPerTry/8) + 1; } debugPrintf ( ("Increasing frame count to %u t=%u r=%u\n", - this->framesPerTry, this->searchTriesWithinThisPass, this->searchResponsesWithinThisPass) ); + this->framesPerTry, this->searchAttempts, this->searchResponses) ); } } /* * if we detect congestion because we have less than a 87.5% success * rate then gradually reduce the frames per try */ - else if ( this->searchResponsesWithinThisPass < - (this->searchTriesWithinThisPass-(this->searchTriesWithinThisPass/8u)) ) { - if (this->framesPerTry>1) { - this->framesPerTry--; - } - this->framesPerTryCongestThresh = this->framesPerTry/2 + 1; - debugPrintf ( ("Congestion detected - set frames per try to %u t=%u r=%u\n", - this->framesPerTry, this->searchTriesWithinThisPass, - this->searchResponsesWithinThisPass) ); + else if ( this->searchResponses < + ( this->searchAttempts - (this->searchAttempts/8u) ) ) { + if (this->framesPerTry>1) { + this->framesPerTry--; + } + this->framesPerTryCongestThresh = this->framesPerTry/2 + 1; + debugPrintf ( ("Congestion detected - set frames per try to %u t=%u r=%u\n", + this->framesPerTry, this->searchAttempts, this->searchResponses) ); } + if ( this->searchAttemptsThisPass <= UINT_MAX - this->searchAttempts ) { + this->searchAttemptsThisPass += this->searchAttempts; + } + else { + this->searchAttemptsThisPass = UINT_MAX; + } + if ( this->searchResponsesThisPass <= UINT_MAX - this->searchResponses ) { + this->searchResponsesThisPass += this->searchResponses; + } + else { + this->searchResponsesThisPass = UINT_MAX; + } + this->searchAttempts = 0; + this->searchResponses = 0; + while ( 1 ) { /* @@ -228,8 +292,8 @@ epicsTimerNotify::expireStatus searchTimer::expire () * dont increase the delay between search * requests */ - if ( this->searchTriesWithinThisPass >= this->iiu.channelCount () ) { - if ( this->searchResponsesWithinThisPass == 0u ) { + if ( this->searchAttemptsThisPass >= this->iiu.channelCount () ) { + if ( this->searchResponsesThisPass == 0u ) { debugPrintf ( ("increasing search try interval\n") ); this->setRetryInterval ( this->minRetry + 1u ); } @@ -250,8 +314,8 @@ epicsTimerNotify::expireStatus searchTimer::expire () */ this->retrySeqAtPassBegin = this->retrySeqNo; - this->searchTriesWithinThisPass = 0; - this->searchResponsesWithinThisPass = 0; + this->searchAttemptsThisPass = 0; + this->searchResponsesThisPass = 0; debugPrintf ( ("saw end of list\n") ); } @@ -275,8 +339,8 @@ epicsTimerNotify::expireStatus searchTimer::expire () this->minRetry = retryNoForThisChannel; } - if ( this->searchTriesWithinThisPass < UINT_MAX ) { - this->searchTriesWithinThisPass++; + if ( this->searchAttempts < UINT_MAX ) { + this->searchAttempts++; } if ( nChanSent < UINT_MAX ) { nChanSent++; @@ -308,15 +372,29 @@ epicsTimerNotify::expireStatus searchTimer::expire () // flush out the search request buffer this->iiu.datagramFlush (); - debugPrintf ( ("sent %u delay sec=%f\n", nFrameSent, this->period) ); +# ifdef DEBUG + char buf[64]; + epicsTime ts = epicsTime::getCurrent(); + ts.strftime ( buf, sizeof(buf), "%M:%S.%09f"); + debugPrintf ( ("sent %u delay sec=%f RTT=%f ts=%s\n", + nFrameSent, this->period, + this->roundTripDelayEstimate, buf ) ); +# endif if ( this->iiu.channelCount () == 0 ) { + debugPrintf ( ( "all channels connected\n" ) ); + this->active = false; + this->noDelay = false; return noRestart; } - else if ( this->retry < MAXCONNTRIES ) { + else if ( this->retry < maxSearchTries ) { + this->noDelay = this->period == 0.0; return expireStatus ( restart, this->period ); } else { + debugPrintf ( ( "maximum search tries exceeded - giving up\n" ) ); + this->active = false; + this->noDelay = false; return noRestart; } } diff --git a/src/ca/searchTimer.h b/src/ca/searchTimer.h index 0cfec4d5d..f1e766138 100644 --- a/src/ca/searchTimer.h +++ b/src/ca/searchTimer.h @@ -27,10 +27,13 @@ class searchTimer : private epicsTimerNotify { public: searchTimer ( udpiiu &iiu, epicsTimerQueue &queue, epicsMutex & ); virtual ~searchTimer (); - void notifySearchResponse ( unsigned short retrySeqNo ); + void notifySearchResponse ( unsigned short retrySeqNo, const epicsTime & currentTime ); void resetPeriod ( double delayToNextTry ); void show ( unsigned level ) const; private: + epicsTime timeAtLastRetry; + double period; /* period between tries */ + double roundTripDelayEstimate; epicsTimer &timer; epicsMutex &mutex; udpiiu &iiu; @@ -38,13 +41,16 @@ private: unsigned framesPerTryCongestThresh; /* one half N tries w congest */ unsigned minRetry; /* min retry number so far */ unsigned retry; - unsigned searchTriesWithinThisPass; /* num search tries within this pass through the list */ - unsigned searchResponsesWithinThisPass; /* num search resp within this pass through the list */ + unsigned searchAttempts; /* num search tries within this timer experation */ + unsigned searchResponses; /* num search resp within this timer experation */ + unsigned searchAttemptsThisPass; /* num search tries within this pass */ + unsigned searchResponsesThisPass; /* num search resp within this pass */ unsigned short retrySeqNo; /* search retry seq number */ unsigned short retrySeqAtPassBegin; /* search retry seq number at beg of pass through list */ - double period; /* period between tries */ - expireStatus expire (); - void setRetryInterval (unsigned retryNo); + bool active; + bool noDelay; + expireStatus expire ( const epicsTime & currentTime ); + void setRetryInterval ( unsigned retryNo ); }; #endif // ifdef searchTimerh diff --git a/src/ca/syncGroup.h b/src/ca/syncGroup.h index 6848ad6ea..53c7faec4 100644 --- a/src/ca/syncGroup.h +++ b/src/ca/syncGroup.h @@ -58,7 +58,7 @@ public: static syncGroupReadNotify * factory ( tsFreeList < class syncGroupReadNotify, 128 > &, struct CASG &, chid, unsigned type, - unsigned long count, void *pValueIn ); + arrayElementCount count, void *pValueIn ); void destroy ( casgRecycle & ); void show ( unsigned level ) const; protected: @@ -66,7 +66,7 @@ protected: private: void *pValue; syncGroupReadNotify ( struct CASG &sgIn, chid, - unsigned type, unsigned long count, void *pValueIn ); + unsigned type, arrayElementCount count, void *pValueIn ); void * operator new ( size_t, tsFreeList < class syncGroupReadNotify, 128 > & ); # if ! defined ( NO_PLACEMENT_DELETE ) @@ -74,9 +74,9 @@ private: tsFreeList < class syncGroupReadNotify, 128 > & ); # endif void completion ( - unsigned type, unsigned long count, const void *pData ); + unsigned type, arrayElementCount count, const void *pData ); void exception ( - int status, const char *pContext, unsigned type, unsigned long count ); + int status, const char *pContext, unsigned type, arrayElementCount count ); }; class syncGroupWriteNotify : public syncGroupNotify, public cacWriteNotify { @@ -84,7 +84,7 @@ public: static syncGroupWriteNotify * factory ( tsFreeList < class syncGroupWriteNotify, 128 > &, struct CASG &, chid, unsigned type, - unsigned long count, const void *pValueIn ); + arrayElementCount count, const void *pValueIn ); void destroy ( casgRecycle & ); void show ( unsigned level ) const; protected: @@ -92,7 +92,7 @@ protected: private: void *pValue; syncGroupWriteNotify ( struct CASG &, chid, unsigned type, - unsigned long count, const void *pValueIn ); + arrayElementCount count, const void *pValueIn ); void * operator new ( size_t, tsFreeList < class syncGroupWriteNotify, 128 > & ); # if ! defined ( NO_PLACEMENT_DELETE ) @@ -100,7 +100,7 @@ private: tsFreeList < class syncGroupWriteNotify, 128 > & ); # endif void completion (); - void exception (int status, const char *pContext ); + void exception ( int status, const char *pContext, unsigned type, arrayElementCount count ); }; class oldCAC; @@ -114,8 +114,8 @@ public: int block ( double timeout ); void reset (); void show ( unsigned level ) const; - int get ( chid pChan, unsigned type, unsigned long count, void *pValue ); - int put ( chid pChan, unsigned type, unsigned long count, const void *pValue ); + int get ( chid pChan, unsigned type, arrayElementCount count, void *pValue ); + int put ( chid pChan, unsigned type, arrayElementCount count, const void *pValue ); void destroyIO ( syncGroupNotify & ); void * operator new ( size_t size ); void operator delete ( void *pCadaver, size_t size ); diff --git a/src/ca/syncGroupReadNotify.cpp b/src/ca/syncGroupReadNotify.cpp index 3415b2efd..8eb3eaf5d 100644 --- a/src/ca/syncGroupReadNotify.cpp +++ b/src/ca/syncGroupReadNotify.cpp @@ -35,7 +35,7 @@ #include "oldAccess.h" syncGroupReadNotify::syncGroupReadNotify ( CASG &sgIn, chid pChan, - unsigned type, unsigned long count, void *pValueIn ) : + unsigned type, arrayElementCount count, void *pValueIn ) : syncGroupNotify ( sgIn, pChan ), pValue ( pValueIn ) { pChan->read ( type, count, *this, &this->id ); @@ -45,7 +45,7 @@ syncGroupReadNotify::syncGroupReadNotify ( CASG &sgIn, chid pChan, syncGroupReadNotify * syncGroupReadNotify::factory ( tsFreeList < class syncGroupReadNotify, 128 > &freeList, struct CASG &sg, chid chan, unsigned type, - unsigned long count, void *pValueIn ) + arrayElementCount count, void *pValueIn ) { return new ( freeList ) syncGroupReadNotify ( sg, chan, type, count, pValueIn); @@ -62,7 +62,7 @@ syncGroupReadNotify::~syncGroupReadNotify () } void syncGroupReadNotify::completion ( - unsigned type, unsigned long count, const void *pData ) + unsigned type, arrayElementCount count, const void *pData ) { if ( this->magic != CASG_MAGIC ) { this->sg.printf ( "cac: sync group io_complete(): bad sync grp op magic number?\n" ); @@ -79,7 +79,7 @@ void syncGroupReadNotify::completion ( } void syncGroupReadNotify::exception ( - int status, const char *pContext, unsigned type, unsigned long count ) + int status, const char *pContext, unsigned type, arrayElementCount count ) { ca_signal_formated ( status, __FILE__, __LINE__, "CA sync group read request failed with chan=%s type=%d count=%ld because \"%s\"\n", diff --git a/src/ca/syncGroupWriteNotify.cpp b/src/ca/syncGroupWriteNotify.cpp index 89fa4a400..98178e69b 100644 --- a/src/ca/syncGroupWriteNotify.cpp +++ b/src/ca/syncGroupWriteNotify.cpp @@ -35,7 +35,7 @@ #include "oldAccess.h" syncGroupWriteNotify::syncGroupWriteNotify ( CASG &sgIn, chid pChan, unsigned type, - unsigned long count, const void *pValueIn ) : + arrayElementCount count, const void *pValueIn ) : syncGroupNotify ( sgIn, pChan ) { pChan->write ( type, count, pValueIn, *this, &this->id ); @@ -45,7 +45,7 @@ syncGroupWriteNotify::syncGroupWriteNotify ( CASG &sgIn, chid pChan, unsigned ty syncGroupWriteNotify * syncGroupWriteNotify::factory ( tsFreeList < class syncGroupWriteNotify, 128 > &freeList, struct CASG &sg, chid chan, unsigned type, - unsigned long count, const void *pValueIn ) + arrayElementCount count, const void *pValueIn ) { return new ( freeList ) syncGroupWriteNotify ( sg, chan, type, count, pValueIn); @@ -73,11 +73,11 @@ void syncGroupWriteNotify::completion () } void syncGroupWriteNotify::exception ( - int status, const char *pContext ) + int status, const char *pContext, unsigned type, arrayElementCount count ) { ca_signal_formated ( status, __FILE__, __LINE__, - "CA sync group write request for channel \"%s\" failed because \"%s\"\n", - this->chan->pName(), pContext); + "CA sync group write request for channel \"%s\" failed because \"%s\" type=%s count=%u\n", + this->chan->pName(), pContext, dbr_type_to_text ( type ), count); // // This notify is left installed at this point as a place holder indicating that // all requests have not been completed. This notify is not uninstalled until diff --git a/src/ca/syncgrp.cpp b/src/ca/syncgrp.cpp index ccd3dc8d4..d8b6c9864 100644 --- a/src/ca/syncgrp.cpp +++ b/src/ca/syncgrp.cpp @@ -189,12 +189,12 @@ extern "C" int epicsShareAPI ca_sg_test ( const CA_SYNC_GID gid ) * ca_sg_array_put() */ extern "C" int epicsShareAPI ca_sg_array_put ( const CA_SYNC_GID gid, chtype type, - unsigned long count, chid pChan, const void *pValue ) + arrayElementCount count, chid pChan, const void *pValue ) { CASG *pcasg; oldCAC *pcac; int caStatus; - + caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; @@ -205,14 +205,15 @@ extern "C" int epicsShareAPI ca_sg_array_put ( const CA_SYNC_GID gid, chtype typ return ECA_BADSYNCGRP; } - return pcasg->put ( pChan, type, count, pValue ); + return pcasg->put ( pChan, type, + static_cast < unsigned > ( count ), pValue ); } /* * ca_sg_array_get() */ extern "C" int epicsShareAPI ca_sg_array_get ( const CA_SYNC_GID gid, chtype type, - unsigned long count, chid pChan, void *pValue ) + arrayElementCount count, chid pChan, void *pValue ) { CASG *pcasg; oldCAC *pcac; @@ -228,5 +229,6 @@ extern "C" int epicsShareAPI ca_sg_array_get ( const CA_SYNC_GID gid, chtype typ return ECA_BADSYNCGRP; } - return pcasg->get ( pChan, type, count, pValue ); + return pcasg->get ( pChan, type, + static_cast < unsigned > ( count ), pValue ); } diff --git a/src/ca/tcpRecvWatchdog.cpp b/src/ca/tcpRecvWatchdog.cpp index f18a7006f..31a28b2b7 100644 --- a/src/ca/tcpRecvWatchdog.cpp +++ b/src/ca/tcpRecvWatchdog.cpp @@ -20,7 +20,7 @@ // tcpRecvWatchdog::tcpRecvWatchdog ( tcpiiu &iiuIn, double periodIn, epicsTimerQueue & queueIn ) : - period ( periodIn ), timer ( queueIn.createTimer ( *this ) ), + period ( periodIn ), timer ( queueIn.createTimer () ), iiu ( iiuIn ), responsePending ( false ), beaconAnomaly ( true ) { @@ -31,7 +31,7 @@ tcpRecvWatchdog::~tcpRecvWatchdog () delete & this->timer; } -epicsTimerNotify::expireStatus tcpRecvWatchdog::expire () +epicsTimerNotify::expireStatus tcpRecvWatchdog::expire ( const epicsTime & currentTime ) { if ( this->responsePending ) { this->cancel (); @@ -52,7 +52,7 @@ epicsTimerNotify::expireStatus tcpRecvWatchdog::expire () void tcpRecvWatchdog::beaconArrivalNotify () { if ( ! this->beaconAnomaly && ! this->responsePending ) { - this->timer.start ( this->period ); + this->timer.start ( *this, this->period ); debugPrintf ( ("Saw a normal beacon - reseting TCP recv watchdog\n") ); } } @@ -74,13 +74,35 @@ void tcpRecvWatchdog::messageArrivalNotify () { this->beaconAnomaly = false; this->responsePending = false; - this->timer.start ( this->period ); + this->timer.start ( *this, this->period ); debugPrintf ( ("received a message - reseting TCP recv watchdog\n") ); } +// +// 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 () +{ + this->beaconAnomaly = false; + this->responsePending = false; + this->timer.start ( *this, this->period ); + debugPrintf ( ("saw heavy send backlog - reseting TCP recv watchdog\n") ); +} + void tcpRecvWatchdog::connectNotify () { - this->timer.start ( this->period ); + this->timer.start ( *this, this->period ); debugPrintf ( ("connected to the server - reseting TCP recv watchdog\n") ); } diff --git a/src/ca/tcpSendWatchdog.cpp b/src/ca/tcpSendWatchdog.cpp index cf0098725..9febe9dc8 100644 --- a/src/ca/tcpSendWatchdog.cpp +++ b/src/ca/tcpSendWatchdog.cpp @@ -17,7 +17,7 @@ tcpSendWatchdog::tcpSendWatchdog ( tcpiiu &iiuIn, double periodIn, epicsTimerQueue & queueIn ) : - period ( periodIn ), timer ( queueIn.createTimer ( *this ) ), + period ( periodIn ), timer ( queueIn.createTimer () ), iiu ( iiuIn ) { } @@ -27,7 +27,7 @@ tcpSendWatchdog::~tcpSendWatchdog () delete & this->timer; } -epicsTimerNotify::expireStatus tcpSendWatchdog::expire () +epicsTimerNotify::expireStatus tcpSendWatchdog::expire ( const epicsTime & currentTime ) { char hostName[128]; this->iiu.hostName ( hostName, sizeof ( hostName ) ); @@ -39,7 +39,7 @@ epicsTimerNotify::expireStatus tcpSendWatchdog::expire () void tcpSendWatchdog::start () { - this->timer.start ( this->period ); + this->timer.start ( *this, this->period ); } void tcpSendWatchdog::cancel () diff --git a/src/ca/tcpiiu.cpp b/src/ca/tcpiiu.cpp index 9b0a49adf..52c9f3b65 100644 --- a/src/ca/tcpiiu.cpp +++ b/src/ca/tcpiiu.cpp @@ -10,7 +10,6 @@ * * Author: Jeff Hill */ - #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "localHostName.h" @@ -43,51 +42,58 @@ extern "C" void cacSendThreadTCP ( void *pParam ) { tcpiiu *piiu = ( tcpiiu * ) pParam; - while ( true ) { - bool flowControlLaborNeeded; - bool echoLaborNeeded; + try { + while ( true ) { + bool flowControlLaborNeeded; + bool echoLaborNeeded; - epicsEventMustWait ( piiu->sendThreadFlushSignal ); + piiu->sendThreadFlushEvent.wait (); - if ( piiu->state != iiu_connected ) { - break; - } - - { - epicsAutoMutex autoMutex ( piiu->pCAC()->mutexRef() ); - flowControlLaborNeeded = piiu->busyStateDetected != piiu->flowControlActive; - echoLaborNeeded = piiu->echoRequestPending; - piiu->echoRequestPending = false; - } - - if ( flowControlLaborNeeded ) { - if ( piiu->flowControlActive ) { - piiu->disableFlowControlRequest (); - piiu->flowControlActive = false; - debugPrintf ( ( "fc off\n" ) ); + if ( piiu->state != iiu_connected ) { + break; } - else { - piiu->enableFlowControlRequest (); - piiu->flowControlActive = true; - debugPrintf ( ( "fc on\n" ) ); - } - } - if ( echoLaborNeeded ) { - if ( CA_V43 ( CA_PROTOCOL_VERSION, piiu->minorProtocolVersion ) ) { - piiu->echoRequest (); + { + epicsAutoMutex autoMutex ( piiu->pCAC()->mutexRef() ); + flowControlLaborNeeded = + piiu->busyStateDetected != piiu->flowControlActive; + echoLaborNeeded = piiu->echoRequestPending; + piiu->echoRequestPending = false; } - else { - piiu->noopRequest (); - } - } - if ( ! piiu->flush () ) { - break; + if ( flowControlLaborNeeded ) { + if ( piiu->flowControlActive ) { + piiu->disableFlowControlRequest (); + piiu->flowControlActive = false; + debugPrintf ( ( "fc off\n" ) ); + } + else { + piiu->enableFlowControlRequest (); + piiu->flowControlActive = true; + debugPrintf ( ( "fc on\n" ) ); + } + } + + if ( echoLaborNeeded ) { + if ( CA_V43 ( piiu->minorProtocolVersion ) ) { + piiu->echoRequest (); + } + else { + piiu->noopRequest (); + } + } + + if ( ! piiu->flush () ) { + break; + } } } + catch ( ... ) { + piiu->printf ("cac: tcp send thread received an exception - diconnecting\n"); + piiu->forcedShutdown (); + } - epicsEventSignal ( piiu->sendThreadExitSignal ); + piiu->sendThreadExitEvent.signal (); } unsigned tcpiiu::sendBytes ( const void *pBuf, @@ -217,15 +223,15 @@ extern "C" void cacRecvThreadTCP ( void *pParam ) tid = epicsThreadCreate ( "CAC-TCP-send", priorityOfSend, epicsThreadGetStackSize ( epicsThreadStackMedium ), cacSendThreadTCP, piiu ); if ( ! tid ) { - epicsEventSignal ( piiu->recvThreadExitSignal ); - epicsEventSignal ( piiu->sendThreadExitSignal ); + piiu->recvThreadExitEvent.signal (); + piiu->sendThreadExitEvent.signal (); piiu->cleanShutdown (); return; } } else { - epicsEventSignal ( piiu->recvThreadExitSignal ); - epicsEventSignal ( piiu->sendThreadExitSignal ); + piiu->recvThreadExitEvent.signal (); + piiu->sendThreadExitEvent.signal (); piiu->cleanShutdown (); return; } @@ -234,12 +240,12 @@ extern "C" void cacRecvThreadTCP ( void *pParam ) unsigned nBytes = 0u; while ( piiu->state == iiu_connected ) { if ( nBytes >= maxBytesPendingTCP ) { - epicsEventMustWait ( piiu->recvThreadRingBufferSpaceAvailableSignal ); + piiu->recvThreadRingBufferSpaceAvailableEvent.wait (); epicsAutoMutex autoMutex ( piiu->pCAC()->mutexRef() ); nBytes = piiu->recvQue.occupiedBytes (); } else { - comBuf * pComBuf = new comBuf; + comBuf * pComBuf = new ( std::nothrow ) comBuf; if ( pComBuf ) { unsigned nBytesIn = pComBuf->fillFromWire ( *piiu ); if ( nBytesIn ) { @@ -247,7 +253,7 @@ extern "C" void cacRecvThreadTCP ( void *pParam ) { epicsAutoMutex autoMutex ( piiu->pCAC()->mutexRef() ); nBytes = piiu->recvQue.occupiedBytes (); - msgHeaderButNoBody = piiu->msgHeaderAvailable; + msgHeaderButNoBody = piiu->oldMsgHeaderAvailable; piiu->recvQue.pushLastComBufReceived ( *pComBuf ); if ( nBytesIn == pComBuf->capacityBytes () ) { if ( piiu->contigRecvMsgCount >= contiguousMsgCountWhichTriggersFlowControl ) { @@ -261,15 +267,18 @@ extern "C" void cacRecvThreadTCP ( void *pParam ) piiu->contigRecvMsgCount = 0u; piiu->busyStateDetected = false; } + piiu->unacknowledgedSendBytes = 0u; // reschedule connection activity watchdog piiu->recvDog.messageArrivalNotify (); } + // wake up recv thread only if // 1) there are currently no bytes in the queue // 2) if the recv thread is currently blocking for an incomplete msg if ( nBytes < sizeof ( caHdr ) || msgHeaderButNoBody ) { - piiu->pCAC ()->signalRecvActivity (); + piiu->pCAC()->signalRecvActivity (); } + if ( nBytes <= UINT_MAX - nBytesIn ) { nBytes += nBytesIn; } @@ -292,126 +301,62 @@ extern "C" void cacRecvThreadTCP ( void *pParam ) } } - epicsEventSignal ( piiu->recvThreadExitSignal ); + piiu->recvThreadExitEvent.signal (); } // // tcpiiu::tcpiiu () // -tcpiiu::tcpiiu ( cac &cac, double connectionTimeout, epicsTimerQueue &timerQueue ) : +tcpiiu::tcpiiu ( cac &cac, double connectionTimeout, + epicsTimerQueue &timerQueue, const osiSockAddr &addrIn, + unsigned minorVersion, class bhe &bheIn, + ipAddrToAsciiEngine &engineIn ) : netiiu ( &cac ), recvDog ( *this, connectionTimeout, timerQueue ), sendDog ( *this, connectionTimeout, timerQueue ), sendQue ( *this ), - pHostNameCache ( 0 ), - curDataMax ( 0ul ), - pBHE ( 0 ), - pCurData ( 0 ), - minorProtocolVersion ( 0u ), + addr ( addrIn ), + curDataMax ( MAX_TCP ), + curDataBytes ( 0ul ), + pHostNameCache ( new hostNameCache ( addrIn, engineIn ) ), + BHE ( bheIn ), + pCurData ( cac.allocateSmallBufferTCP () ), + minorProtocolVersion ( minorVersion ), state ( iiu_connecting ), sock ( INVALID_SOCKET ), contigRecvMsgCount ( 0u ), blockingForFlush ( 0u ), + socketLibrarySendBufferSize ( 0u ), + unacknowledgedSendBytes ( 0u ), busyStateDetected ( false ), flowControlActive ( false ), echoRequestPending ( false ), + oldMsgHeaderAvailable ( false ), msgHeaderAvailable ( false ), sockCloseCompleted ( false ), f_trueOnceOnly ( true ), earlyFlush ( false ) { - this->addr.sa.sa_family = AF_UNSPEC; - - this->sendThreadExitSignal = epicsEventCreate ( epicsEventEmpty ); - if ( ! this->sendThreadExitSignal ) { - this->printf ("CA: unable to create CA client send thread exit semaphore\n"); - this->fullyConstructedFlag = false; - return; + if ( ! this->pCurData ) { + throw std::bad_alloc (); } - this->recvThreadExitSignal = epicsEventCreate ( epicsEventEmpty ); - if ( ! this->recvThreadExitSignal ) { - this->printf ("CA: unable to create CA client send thread exit semaphore\n"); - epicsEventDestroy (this->sendThreadExitSignal); - this->fullyConstructedFlag = false; - return; + if ( ! this->pHostNameCache.get () ) { + throw std::bad_alloc (); } - this->sendThreadFlushSignal = epicsEventCreate ( epicsEventEmpty ); - if ( ! this->sendThreadFlushSignal ) { - this->printf ("CA: unable to create sendThreadFlushSignal object\n"); - epicsEventDestroy (this->sendThreadExitSignal); - this->fullyConstructedFlag = false; - return; - } - - this->flushBlockSignal = epicsEventCreate ( epicsEventEmpty ); - if ( ! this->flushBlockSignal ) { - this->printf ("CA: unable to create flushBlockSignal object\n"); - epicsEventDestroy (this->sendThreadExitSignal); - epicsEventDestroy (this->sendThreadFlushSignal); - this->fullyConstructedFlag = false; - return; - } - - this->recvThreadRingBufferSpaceAvailableSignal = epicsEventCreate ( epicsEventEmpty ); - if ( ! this->recvThreadRingBufferSpaceAvailableSignal ) { - this->printf ("CA: unable to create recvThreadRingBufferSpaceAvailableSignal object\n"); - epicsEventDestroy (this->sendThreadExitSignal); - epicsEventDestroy (this->sendThreadFlushSignal); - epicsEventDestroy (this->flushBlockSignal); - this->fullyConstructedFlag = false; - return; - } - - this->fullyConstructedFlag = true; -} - -/* - * tcpiiu::initiateConnect () - */ -bool tcpiiu::initiateConnect ( const osiSockAddr &addrIn, unsigned minorVersion, - class bhe &bhe, ipAddrToAsciiEngine &engineIn ) -{ - unsigned priorityOfRecv; - epicsThreadBooleanStatus tbs; - epicsThreadId tid; - int status; - int flag; - - this->addr = addrIn; - - this->pHostNameCache = new hostNameCache ( addrIn, engineIn ); - if ( ! this->pHostNameCache ) { - return false; - } - - this->pBHE = &bhe; - bhe.bindToIIU ( *this ); - - this->state = iiu_connecting; - this->minorProtocolVersion = minorVersion; - - this->contigRecvMsgCount = 0u; - this->busyStateDetected = false; - this->flowControlActive = false; - this->echoRequestPending = false; - this->msgHeaderAvailable = false; - this->sockCloseCompleted = false; - - // first message informs server of user and host name of client - this->userNameSetRequest (); - this->hostNameSetRequest (); + this->BHE.bindToIIU ( *this ); this->sock = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( this->sock == INVALID_SOCKET ) { this->printf ( "CAC: unable to create virtual circuit because \"%s\"\n", SOCKERRSTR ( SOCKERRNO ) ); - return false; + cac.releaseSmallBufferTCP ( this->pCurData ); + throw std::bad_alloc (); } - flag = true; - status = setsockopt ( this->sock, IPPROTO_TCP, TCP_NODELAY, + int flag = true; + int status = setsockopt ( this->sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof ( flag ) ); if ( status < 0 ) { this->printf ("CAC: problems setting socket option TCP_NODELAY = \"%s\"\n", @@ -426,6 +371,12 @@ bool tcpiiu::initiateConnect ( const osiSockAddr &addrIn, unsigned minorVersion, SOCKERRSTR ( SOCKERRNO ) ); } + // load message queue with messages informing server + // of user and host name of client + this->userNameSetRequest (); + this->hostNameSetRequest (); + + # if 0 { int i; @@ -451,24 +402,44 @@ bool tcpiiu::initiateConnect ( const osiSockAddr &addrIn, unsigned minorVersion, } # endif + { + int nBytes; + int sizeOfParameter = static_cast < int > ( sizeof ( nBytes ) ); + status = getsockopt ( this->sock, SOL_SOCKET, SO_SNDBUF, + ( char * ) &nBytes, &sizeOfParameter ); + if ( status < 0 || nBytes < 0 || + sizeOfParameter != static_cast < int > ( sizeof ( nBytes ) ) ) { + this->printf ("CAC: problems getting socket option SO_SNDBUF = \"%s\"\n", + SOCKERRSTR ( SOCKERRNO ) ); + this->socketLibrarySendBufferSize = 0u; + } + else { + this->socketLibrarySendBufferSize = static_cast < unsigned > ( nBytes ); + } + } + memset ( (void *) &this->curMsg, '\0', sizeof ( this->curMsg ) ); - tbs = epicsThreadHighestPriorityLevelBelow ( this->pCAC ()->getInitializingThreadsPriority (), &priorityOfRecv ); + unsigned priorityOfRecv; + epicsThreadBooleanStatus tbs = epicsThreadHighestPriorityLevelBelow ( + this->pCAC ()->getInitializingThreadsPriority (), &priorityOfRecv ); if ( tbs != epicsThreadBooleanStatusSuccess ) { priorityOfRecv = this->pCAC ()->getInitializingThreadsPriority (); } - tid = epicsThreadCreate ("CAC-TCP-recv", priorityOfRecv, - epicsThreadGetStackSize (epicsThreadStackMedium), cacRecvThreadTCP, this); + epicsThreadId tid = epicsThreadCreate ( "CAC-TCP-recv", priorityOfRecv, + epicsThreadGetStackSize ( epicsThreadStackMedium ), + cacRecvThreadTCP, this); if ( tid == 0 ) { this->printf ("CA: unable to create CA client receive thread\n"); + cac.releaseSmallBufferTCP ( this->pCurData ); socket_close ( this->sock ); - return false; + throw std::bad_alloc (); } - - return true; } + + /* * tcpiiu::connect () */ @@ -529,9 +500,6 @@ void tcpiiu::connect () */ void tcpiiu::cleanShutdown () { - this->sendDog.cancel (); - this->recvDog.cancel (); - epicsAutoMutex autoMutex ( this->pCAC()->mutexRef() ); if ( this->state == iiu_connected ) { @@ -564,8 +532,8 @@ void tcpiiu::cleanShutdown () this->state = iiu_disconnected; } } - epicsEventSignal ( this->sendThreadFlushSignal ); - epicsEventSignal ( this->recvThreadRingBufferSpaceAvailableSignal ); + this->sendThreadFlushEvent.signal (); + this->recvThreadRingBufferSpaceAvailableEvent.signal (); this->pCAC ()->signalRecvActivity (); } @@ -574,9 +542,6 @@ void tcpiiu::cleanShutdown () */ void tcpiiu::forcedShutdown () { - this->sendDog.cancel (); - this->recvDog.cancel (); - epicsAutoMutex autoMutex ( this->pCAC()->mutexRef() ); if ( this->state != iiu_disconnected ) { @@ -603,9 +568,9 @@ void tcpiiu::forcedShutdown () } } - epicsEventSignal ( this->sendThreadFlushSignal ); - epicsEventSignal ( this->recvThreadRingBufferSpaceAvailableSignal ); - this->pCAC ()->signalRecvActivity (); + this->sendThreadFlushEvent.signal (); + this->recvThreadRingBufferSpaceAvailableEvent.signal (); + this->pCAC()->signalRecvActivity (); } // @@ -613,21 +578,18 @@ void tcpiiu::forcedShutdown () // tcpiiu::~tcpiiu () { - if ( ! this->fullyConstructedFlag ) { - return; - } + this->sendDog.cancel (); + this->recvDog.cancel (); this->cleanShutdown (); // wait for send thread to exit static const double shutdownDelay = 15.0; - epicsEventWaitStatus semStat; while ( true ) { - semStat = epicsEventWaitWithTimeout ( this->sendThreadExitSignal, shutdownDelay ); - if ( semStat == epicsEventWaitOK ) { + bool signaled = this->sendThreadExitEvent.wait ( shutdownDelay ); + if ( signaled ) { break; } - assert ( semStat == epicsEventWaitTimeout ); if ( ! this->sockCloseCompleted ) { printf ( "Gave up waiting for \"shutdown()\" to force send thread to exit after %f sec\n", shutdownDelay); @@ -645,11 +607,10 @@ tcpiiu::~tcpiiu () // wait for recv thread to exit while ( true ) { - semStat = epicsEventWaitWithTimeout ( this->recvThreadExitSignal, shutdownDelay ); - if ( semStat == epicsEventWaitOK ) { + bool signaled = this->recvThreadExitEvent.wait ( shutdownDelay ); + if ( signaled ) { break; } - assert ( semStat == epicsEventWaitTimeout ); if ( ! this->sockCloseCompleted ) { printf ( "Gave up waiting for \"shutdown()\" to force receive thread to exit after %f sec\n", shutdownDelay); @@ -683,39 +644,24 @@ tcpiiu::~tcpiiu () epicsAutoMutex autoMutex ( this->pCAC()->mutexRef() ); if ( this->pCurData ) { - delete [] this->pCurData; - this->pCurData = 0; - this->curDataMax = 0u; + if ( this->curDataMax == MAX_TCP ) { + this->pCAC()->releaseSmallBufferTCP ( this->pCurData ); + } + else { + this->pCAC()->releaseLargeBufferTCP ( this->pCurData ); + } } - this->addr.sa.sa_family = AF_UNSPEC; - - this->minorProtocolVersion = 0u; - if ( this->pHostNameCache ) { - this->pHostNameCache->destroy (); - this->pHostNameCache = 0; - } - this->pBHE = 0; this->sendQue.clear (); this->recvQue.clear (); } // wakeup user threads blocking for send backlog to be reduced // and wait for them to stop using this IIU - epicsEventSignal ( this->flushBlockSignal ); + this->flushBlockEvent.signal (); while ( this->blockingForFlush ) { epicsThreadSleep ( 0.1 ); } - - epicsEventDestroy ( this->sendThreadExitSignal ); - epicsEventDestroy ( this->recvThreadExitSignal ); - epicsEventDestroy ( this->sendThreadFlushSignal ); - epicsEventDestroy ( this->recvThreadRingBufferSpaceAvailableSignal ); - epicsEventDestroy ( this->flushBlockSignal ); - - if ( this->pHostNameCache ) { - this->pHostNameCache->destroy (); - } } void tcpiiu::destroy () @@ -752,14 +698,12 @@ bool tcpiiu::isVirtaulCircuit ( const char *pChannelName, const osiSockAddr &add if ( ! match ) { epicsAutoMutex locker ( this->pCAC()->mutexRef() ); char acc[64]; - if ( this->pHostNameCache ) { - this->pHostNameCache->hostName ( acc, sizeof ( acc ) ); - assert ( this->pCAC () ); - msgForMultiplyDefinedPV *pMsg = new msgForMultiplyDefinedPV ( - *this->pCAC (), pChannelName, acc, addrIn ); - if ( pMsg ) { - this->pCAC ()->ipAddrToAsciiAsynchronousRequestInstall ( *pMsg ); - } + this->pHostNameCache->hostName ( acc, sizeof ( acc ) ); + assert ( this->pCAC () ); + msgForMultiplyDefinedPV *pMsg = new msgForMultiplyDefinedPV ( + *this->pCAC (), pChannelName, acc, addrIn ); + if ( pMsg ) { + this->pCAC ()->ipAddrToAsciiAsynchronousRequestInstall ( *pMsg ); } } @@ -770,16 +714,9 @@ void tcpiiu::show ( unsigned level ) const { epicsAutoMutex locker ( this->pCAC()->mutexRef() ); char buf[256]; - if ( this->pHostNameCache ) { - this->pHostNameCache->hostName ( buf, sizeof ( buf ) ); - } - else { - strncpy ( buf, "", sizeof ( buf ) ); - buf [ sizeof ( buf ) - 1 ] = '\0'; - } - ::printf ( "Virtual circuit to \"%s\" at version %u.%u state %u\n", - buf, CA_PROTOCOL_VERSION, this->minorProtocolVersion, - this->state ); + this->pHostNameCache->hostName ( buf, sizeof ( buf ) ); + ::printf ( "Virtual circuit to \"%s\" at version %s state %u\n", + buf, CA_VERSION_STRING ( this->minorProtocolVersion ), this->state ); if ( level > 1u ) { this->netiiu::show ( level - 1u ); } @@ -792,20 +729,16 @@ void tcpiiu::show ( unsigned level ) const if ( level > 3u ) { ::printf ( "\tvirtual circuit socket identifier %d\n", this->sock ); ::printf ( "\tsend thread flush signal:\n" ); - epicsEventShow ( this->sendThreadFlushSignal, level-3u ); + this->sendThreadFlushEvent.show ( level-3u ); ::printf ( "\trecv thread buffer space available signal:\n" ); - epicsEventShow ( this->recvThreadRingBufferSpaceAvailableSignal, level-3u ); + this->recvThreadRingBufferSpaceAvailableEvent.show ( level-3u ); ::printf ( "\tsend thread exit signal:\n" ); - epicsEventShow ( this->sendThreadExitSignal, level-3u ); + this->sendThreadExitEvent.show ( level-3u ); ::printf ( "\trecv thread exit signal:\n" ); - epicsEventShow ( this->recvThreadExitSignal, level-3u ); - ::printf ( "\tfully constructed bool %u\n", this->fullyConstructedFlag ); + this->recvThreadExitEvent.show ( level-3u ); ::printf ("\techo pending bool = %u\n", this->echoRequestPending ); - ::printf ("\treceive message header available bool = %u\n", this->msgHeaderAvailable ); - if ( this->pBHE ) { - this->pBHE->show ( level - 3u ); - } ::printf ( "IO identifier hash table:\n" ); + this->BHE.show ( level - 3u ); } } @@ -816,7 +749,7 @@ bool tcpiiu::setEchoRequestPending () this->echoRequestPending = true; } this->flushRequest (); - if ( CA_V43 (CA_PROTOCOL_VERSION, this->minorProtocolVersion ) ) { + if ( CA_V43 ( this->minorProtocolVersion ) ) { // we send an echo return true; } @@ -826,12 +759,149 @@ bool tcpiiu::setEchoRequestPending () } } +// +// tcpiiu::processIncoming() +// +void tcpiiu::processIncoming () +{ + while ( 1 ) { + + // + // fetch a complete message header + // + unsigned nBytes = this->recvQue.occupiedBytes (); + + if ( ! this->msgHeaderAvailable ) { + if ( ! this->oldMsgHeaderAvailable ) { + if ( nBytes < sizeof ( caHdr ) ) { + return; + } + this->curMsg.m_cmmd = this->recvQue.popUInt16 (); + this->curMsg.m_postsize = this->recvQue.popUInt16 (); + this->curMsg.m_dataType = this->recvQue.popUInt16 (); + this->curMsg.m_count = this->recvQue.popUInt16 (); + this->curMsg.m_cid = this->recvQue.popUInt32 (); + this->curMsg.m_available = this->recvQue.popUInt32 (); + this->oldMsgHeaderAvailable = true; + } + if ( this->curMsg.m_postsize == 0xffff ) { + static const unsigned annexSize = + sizeof ( this->curMsg.m_postsize ) + sizeof ( this->curMsg.m_count ); + if ( this->recvQue.occupiedBytes () < annexSize ) { + return; + } + this->curMsg.m_postsize = this->recvQue.popUInt32 (); + this->curMsg.m_count = this->recvQue.popUInt32 (); + } + this->msgHeaderAvailable = true; + debugPrintf ( + ( "%s Cmd=%3u Type=%3u Count=%8u Size=%8u", + this->pHostName (), + this->curMsg.m_cmmd, + this->curMsg.m_dataType, + this->curMsg.m_count, + this->curMsg.m_postsize) ); + debugPrintf ( + ( " Avail=%8u Cid=%8u\n", + this->curMsg.m_available, + this->curMsg.m_cid) ); + } + + // + // make sure we have a large enough message body cache + // + if ( this->curMsg.m_postsize > this->curDataMax ) { + if ( this->curDataMax == MAX_TCP && + this->pCAC()->largeBufferSizeTCP() >= this->curMsg.m_postsize ) { + char * pBuf = this->pCAC()->allocateLargeBufferTCP (); + if ( pBuf ) { + this->pCAC()->releaseSmallBufferTCP ( this->pCurData ); + this->pCurData = pBuf; + this->curDataMax = this->pCAC()->largeBufferSizeTCP (); + } + else { + this->printf ("CAC: not enough memory for message body cache (ignoring response message)\n"); + } + } + } + + if ( this->curMsg.m_postsize <= this->curDataMax ) { + if ( this->curMsg.m_postsize > 0u ) { + this->curDataBytes += this->recvQue.copyOutBytes ( + &this->pCurData[this->curDataBytes], + this->curMsg.m_postsize - this->curDataBytes ); + if ( this->curDataBytes < this->curMsg.m_postsize ) { + this->recvThreadRingBufferSpaceAvailableEvent.signal (); + return; + } + } + bool msgOK = this->pCAC()->executeResponse ( *this, + this->curMsg, this->pCurData ); + if ( ! msgOK ) { + this->cleanShutdown (); + return; + } + } + else { + static bool once = false; + if ( ! once ) { + this->printf ( + "CAC: response with payload size=%u > EPICS_CA_MAX_ARRAY_BYTES ignored\n", + this->curMsg.m_postsize ); + } + this->curDataBytes += this->recvQue.removeBytes ( + this->curMsg.m_postsize - this->curDataBytes ); + if ( this->curDataBytes < this->curMsg.m_postsize ) { + this->recvThreadRingBufferSpaceAvailableEvent.signal (); + return; + } + } + + if ( nBytes >= maxBytesPendingTCP && + this->recvQue.occupiedBytes () < maxBytesPendingTCP ) { + this->recvThreadRingBufferSpaceAvailableEvent.signal (); + } + + this->oldMsgHeaderAvailable = false; + this->msgHeaderAvailable = false; + this->curDataBytes = 0u; + } +} + +inline void insertRequestHeader ( + comQueSend &sendQue, ca_uint16_t request, ca_uint32_t payloadSize, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint32_t requestDependent, bool v49Ok ) +{ + if ( payloadSize < 0xffff && nElem < 0xffff ) { + sendQue.pushUInt16 ( request ); + sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( payloadSize ) ); + sendQue.pushUInt16 ( dataType ); + sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( nElem ) ); + sendQue.pushUInt32 ( cid ); + sendQue.pushUInt32 ( requestDependent ); + } + else if ( v49Ok ) { + sendQue.pushUInt16 ( request ); + sendQue.pushUInt16 ( 0xffff ); + sendQue.pushUInt16 ( dataType ); + sendQue.pushUInt16 ( 0u ); + sendQue.pushUInt32 ( cid ); + sendQue.pushUInt32 ( requestDependent ); + sendQue.pushUInt32 ( payloadSize ); + sendQue.pushUInt32 ( nElem ); + } + else { + throw cacChannel::outOfBounds (); + } +} + /* * tcpiiu::hostNameSetRequest () */ void tcpiiu::hostNameSetRequest () { - if ( ! CA_V41 ( CA_PROTOCOL_VERSION, this->minorProtocolVersion ) ) { + if ( ! CA_V41 ( this->minorProtocolVersion ) ) { return; } @@ -846,14 +916,12 @@ void tcpiiu::hostNameSetRequest () epicsAutoMutex locker ( this->pCAC()->mutexRef() ); - this->sendQue.reserveSpace ( postSize + 16u ); this->sendQue.pushUInt16 ( CA_PROTO_HOST_NAME ); // cmd - this->sendQue.pushUInt16 ( postSize ); // postsize + this->sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( postSize ) ); // postsize this->sendQue.pushUInt16 ( 0u ); // dataType this->sendQue.pushUInt16 ( 0u ); // count - this->sendQue.pushUInt32 ( static_cast ( 0u ) ); // cid - this->sendQue.pushUInt32 ( static_cast ( 0u ) ); // available - + this->sendQue.pushUInt32 ( 0u ); // cid + this->sendQue.pushUInt32 ( 0u ); // available this->sendQue.pushString ( pName, size ); this->sendQue.pushString ( nillBytes, postSize - size ); } @@ -863,7 +931,7 @@ void tcpiiu::hostNameSetRequest () */ void tcpiiu::userNameSetRequest () { - if ( ! CA_V41 ( CA_PROTOCOL_VERSION, this->minorProtocolVersion ) ) { + if ( ! CA_V41 ( this->minorProtocolVersion ) ) { return; } @@ -877,15 +945,12 @@ void tcpiiu::userNameSetRequest () } epicsAutoMutex locker ( this->pCAC()->mutexRef() ); - - this->sendQue.reserveSpace ( postSize + 16u ); this->sendQue.pushUInt16 ( CA_PROTO_CLIENT_NAME ); // cmd this->sendQue.pushUInt16 ( postSize ); // postsize this->sendQue.pushUInt16 ( 0u ); // dataType this->sendQue.pushUInt16 ( 0u ); // count this->sendQue.pushUInt32 ( 0u ); // cid this->sendQue.pushUInt32 ( 0u ); // available - this->sendQue.pushString ( pName, size ); this->sendQue.pushString ( nillBytes, postSize - size ); } @@ -898,7 +963,6 @@ void tcpiiu::disableFlowControlRequest () epicsAutoMutex locker ( this->pCAC()->mutexRef() ); - this->sendQue.reserveSpace ( 16u ); this->sendQue.pushUInt16 ( CA_PROTO_EVENTS_ON ); // cmd this->sendQue.pushUInt16 ( 0u ); // postsize this->sendQue.pushUInt16 ( 0u ); // dataType @@ -915,7 +979,6 @@ void tcpiiu::enableFlowControlRequest () epicsAutoMutex locker ( this->pCAC()->mutexRef() ); - this->sendQue.reserveSpace ( 16u ); this->sendQue.pushUInt16 ( CA_PROTO_EVENTS_OFF ); // cmd this->sendQue.pushUInt16 ( 0u ); // postsize this->sendQue.pushUInt16 ( 0u ); // dataType @@ -932,7 +995,6 @@ void tcpiiu::noopRequest () epicsAutoMutex locker ( this->pCAC()->mutexRef() ); - this->sendQue.reserveSpace ( 16u ); this->sendQue.pushUInt16 ( CA_PROTO_NOOP ); // cmd this->sendQue.pushUInt16 ( 0u ); // postsize this->sendQue.pushUInt16 ( 0u ); // dataType @@ -949,7 +1011,6 @@ void tcpiiu::echoRequest () epicsAutoMutex locker ( this->pCAC()->mutexRef() ); - this->sendQue.reserveSpace ( 16u ); this->sendQue.pushUInt16 ( CA_PROTO_ECHO ); // cmd this->sendQue.pushUInt16 ( 0u ); // postsize this->sendQue.pushUInt16 ( 0u ); // dataType @@ -958,110 +1019,51 @@ void tcpiiu::echoRequest () this->sendQue.pushUInt32 ( 0u ); // available } -// -// tcpiiu::processIncoming() -// -void tcpiiu::processIncoming () +inline void insertRequestWithPayLoad ( + comQueSend &sendQue, ca_uint16_t request, + unsigned dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint32_t requestDependent, const void *pPayload, + bool v49Ok ) { - while ( 1 ) { - unsigned nBytes; - - // - // fetch a complete message header - // - nBytes = this->recvQue.occupiedBytes (); - - if ( ! this->msgHeaderAvailable ) { - - this->msgHeaderAvailable = this->recvQue.copyOutBytes ( - &this->curMsg, sizeof ( this->curMsg ) ); - - if ( ! this->msgHeaderAvailable ) { - return; - } - - // - // fix endian of bytes - // - this->curMsg.m_cmmd = ntohs ( this->curMsg.m_cmmd ); - this->curMsg.m_postsize = ntohs ( this->curMsg.m_postsize ); - this->curMsg.m_dataType = ntohs ( this->curMsg.m_dataType ); - this->curMsg.m_count = ntohs ( this->curMsg.m_count ); - this->curMsg.m_cid = ntohl ( this->curMsg.m_cid ); - this->curMsg.m_available = ntohl ( this->curMsg.m_available ); - - debugPrintf ( - ( "%s Cmd=%3d Type=%3d Count=%4d Size=%4d", - this->pHostName (), - this->curMsg.m_cmmd, - this->curMsg.m_dataType, - this->curMsg.m_count, - this->curMsg.m_postsize) ); - - debugPrintf ( - ( " Avail=%8x Cid=%6d\n", - this->curMsg.m_available, - this->curMsg.m_cid) ); - } - - // - // dont allow huge msg body until - // the client library supports it - // - if ( this->curMsg.m_postsize > ( unsigned ) MAX_TCP ) { - this->msgHeaderAvailable = false; - this->printf ( "CAC: message body was too large ( disconnecting )\n" ); - this->cleanShutdown (); - return; - } - - // - // make sure we have a large enough message body cache - // - if ( this->curMsg.m_postsize > this->curDataMax ) { - - /* - * scalar DBR_STRING is sometimes clipped to the - * actual string size so make sure this cache is - * as large as one DBR_STRING so they will - * not page fault if they read MAX_STRING_SIZE - * bytes (instead of the actual string size). - */ - unsigned cacheSize = this->curMsg.m_postsize * 2u; - if ( cacheSize < MAX_STRING_SIZE ) { - cacheSize = MAX_STRING_SIZE; - } - - char *pData = new char [cacheSize]; - if ( ! pData ) { - this->printf ("CAC: not enough memory for message body cache (disconnecting)\n"); - this->cleanShutdown (); - return; - } - if ( this->pCurData ) { - delete [] this->pCurData; - } - this->pCurData = pData; - this->curDataMax = cacheSize; - } - - if ( this->curMsg.m_postsize > 0u ) { - bool msgBodyAvailable = this->recvQue.copyOutBytes ( - this->pCurData, this->curMsg.m_postsize ); - if ( ! msgBodyAvailable ) { - return; - } - } - - if ( nBytes >= maxBytesPendingTCP && - this->recvQue.occupiedBytes () < maxBytesPendingTCP ) { - epicsEventSignal ( this->recvThreadRingBufferSpaceAvailableSignal ); - } - - this->pCAC()->executeResponse ( *this, this->curMsg, this->pCurData ); - - this->msgHeaderAvailable = false; + if ( ! sendQue.dbr_type_ok ( dataType ) ) { + throw cacChannel::badType(); } + ca_uint32_t size; + bool stringOptim; + if ( dataType == DBR_STRING && nElem == 1 ) { + const char *pStr = static_cast < const char * > ( pPayload ); + size = strlen ( pStr ) + 1u; + if ( size > MAX_STRING_SIZE ) { + throw cacChannel::outOfBounds(); + } + stringOptim = true; + } + else { + unsigned maxBytes; + if ( v49Ok ) { + maxBytes = 0xffffffff; + } + else { + maxBytes = MAX_TCP; + } + unsigned maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; + if ( nElem >= maxElem ) { + throw cacChannel::outOfBounds(); + } + size = dbr_size_n ( dataType, nElem ); + stringOptim = false; + } + ca_uint32_t payloadSize = CA_MESSAGE_ALIGN ( size ); + insertRequestHeader ( sendQue, request, payloadSize, + static_cast ( dataType ), + nElem, cid, requestDependent, v49Ok ); + if ( stringOptim ) { + sendQue.pushString ( static_cast < const char * > ( pPayload ), size ); + } + else { + sendQue.push_dbr_type ( dataType, pPayload, nElem ); + } + sendQue.pushString ( nillBytes, payloadSize - size ); } void tcpiiu::writeRequest ( nciu &chan, unsigned type, unsigned nElem, const void *pValue ) @@ -1069,120 +1071,54 @@ void tcpiiu::writeRequest ( nciu &chan, unsigned type, unsigned nElem, const voi if ( ! chan.connected () ) { throw cacChannel::notConnected(); } - - if ( ! this->sendQue.dbr_type_ok ( type ) ) { - throw cacChannel::badType(); - } - - if ( nElem > 0xffff) { - throw cacChannel::outOfBounds(); - } - - bool stringOptim; - unsigned size; - if ( type == DBR_STRING && nElem == 1 ) { - const char *pstr = static_cast < const char * > ( pValue ); - size = strlen ( pstr ) + 1; - stringOptim = true; - } - else { - size = dbr_size_n ( type, nElem ); - stringOptim = false; - } - - unsigned postcnt = CA_MESSAGE_ALIGN ( size ); - if ( postcnt > 0xffff ) { - throw cacChannel::unsupportedByService(); - } - - this->sendQue.reserveSpace ( postcnt + 16u ); - this->sendQue.pushUInt16 ( CA_PROTO_WRITE ); // cmd - this->sendQue.pushUInt16 ( postcnt ); // postsize - this->sendQue.pushUInt16 ( type ); // dataType - this->sendQue.pushUInt16 ( nElem ); // count - this->sendQue.pushUInt32 ( chan.getSID () ); // cid - this->sendQue.pushUInt32 ( ~0UL ); // available - if ( stringOptim ) { - this->sendQue.pushString ( static_cast < const char * > ( pValue ), size ); - } - else { - this->sendQue.push_dbr_type ( type, pValue, nElem ); - } - this->sendQue.pushString ( nillBytes, postcnt - size ); + insertRequestWithPayLoad ( this->sendQue, CA_PROTO_WRITE, + type, nElem, chan.getSID(), chan.getCID(), pValue, + CA_V49 ( this->minorProtocolVersion ) ); } + void tcpiiu::writeNotifyRequest ( nciu &chan, netWriteNotifyIO &io, unsigned type, unsigned nElem, const void *pValue ) { if ( ! chan.connected () ) { throw cacChannel::notConnected(); } - if ( ! this->ca_v41_ok () ) { throw cacChannel::unsupportedByService(); } - - if ( ! this->sendQue.dbr_type_ok ( type ) ) { - throw cacChannel::badType(); - } - - if ( nElem > 0xffff ) { - throw cacChannel::unsupportedByService(); - } - - ca_uint32_t size; - bool stringOptim; - if ( type == DBR_STRING && nElem == 1 ) { - char *pstr = (char *) pValue; - size = strlen ( pstr ) +1; - stringOptim = true; - } - else { - size = dbr_size_n ( type, nElem ); - stringOptim = false; - } - ca_uint32_t postcnt = CA_MESSAGE_ALIGN ( size ); - if ( postcnt > 0xffff ) { - throw cacChannel::unsupportedByService(); - } - - this->sendQue.reserveSpace ( postcnt + 16u ); - this->sendQue.pushUInt16 ( CA_PROTO_WRITE_NOTIFY ); // cmd - this->sendQue.pushUInt16 ( postcnt ); // postsize - this->sendQue.pushUInt16 ( type ); // dataType - this->sendQue.pushUInt16 ( nElem ); // count - this->sendQue.pushUInt32 ( chan.getSID () ); // cid - this->sendQue.pushUInt32 ( io.getID () ); // available - if ( stringOptim ) { - this->sendQue.pushString ( static_cast < const char * > ( pValue ), size ); - } - else { - this->sendQue.push_dbr_type ( type, pValue, nElem ); - } - this->sendQue.pushString ( nillBytes, postcnt - size ); + insertRequestWithPayLoad ( this->sendQue, CA_PROTO_WRITE_NOTIFY, + type, nElem, chan.getSID(), io.getID(), pValue, + CA_V49 ( this->minorProtocolVersion ) ); } void tcpiiu::readNotifyRequest ( nciu &chan, netReadNotifyIO &io, - unsigned type, unsigned nElem ) + unsigned dataType, unsigned nElem ) { if ( ! chan.connected () ) { - throw cacChannel::notConnected(); + throw cacChannel::notConnected (); } - - if ( nElem > 0xffff) { - throw cacChannel::unsupportedByService(); - } - if ( type > 0xffff) { - throw cacChannel::unsupportedByService(); - } - - this->sendQue.reserveSpace ( 16u ); - this->sendQue.pushUInt16 ( CA_PROTO_READ_NOTIFY ); // cmd - this->sendQue.pushUInt16 ( 0u ); // postsize - this->sendQue.pushUInt16 ( type ); // dataType - this->sendQue.pushUInt16 ( nElem ); // count - this->sendQue.pushUInt32 ( chan.getSID () ); // cid - this->sendQue.pushUInt32 ( io.getID () ); // available + if ( ! dbr_type_is_valid ( dataType ) ) { + throw cacChannel::badType(); + } + if ( nElem > chan.nativeElementCount() ) { + throw cacChannel::outOfBounds (); + } + unsigned maxBytes; + if ( CA_V49 ( this->minorProtocolVersion ) ) { + maxBytes = this->pCAC()->largeBufferSizeTCP (); + } + else { + maxBytes = MAX_TCP; + } + unsigned maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; + if ( nElem > maxElem ) { + throw cacChannel::msgBodyCacheTooSmall (); + } + insertRequestHeader ( this->sendQue, + CA_PROTO_READ_NOTIFY, 0u, + static_cast < ca_uint16_t > ( dataType ), + nElem, chan.getSID(), io.getID(), + CA_V49 ( this->minorProtocolVersion ) ); } void tcpiiu::createChannelRequest ( nciu &chan ) @@ -1203,11 +1139,10 @@ void tcpiiu::createChannelRequest ( nciu &chan ) unsigned postCnt = CA_MESSAGE_ALIGN ( nameLength ); - if ( postCnt > 0xffff ) { + if ( postCnt >= 0xffff ) { throw cacChannel::unsupportedByService(); } - this->sendQue.reserveSpace ( postCnt + 16u ); this->sendQue.pushUInt16 ( CA_PROTO_CLAIM_CIU ); // cmd this->sendQue.pushUInt16 ( postCnt ); // postsize this->sendQue.pushUInt16 ( 0u ); // dataType @@ -1218,7 +1153,7 @@ void tcpiiu::createChannelRequest ( nciu &chan ) // here to communicate the minor version number // starting with CA 4.1. // - this->sendQue.pushUInt32 ( CA_MINOR_VERSION ); // available + this->sendQue.pushUInt32 ( CA_MINOR_PROTOCOL_REVISION ); // available if ( nameLength ) { this->sendQue.pushString ( pName, nameLength ); } @@ -1229,15 +1164,14 @@ void tcpiiu::createChannelRequest ( nciu &chan ) void tcpiiu::clearChannelRequest ( nciu &chan ) { - // we go ahead and send this even if the channel isnt connected - // because we dont want to leak the resource in the server - this->sendQue.reserveSpace ( 16u ); - this->sendQue.pushUInt16 ( CA_PROTO_CLEAR_CHANNEL ); // cmd - this->sendQue.pushUInt16 ( 0u ); // postsize - this->sendQue.pushUInt16 ( 0u ); // dataType - this->sendQue.pushUInt16 ( 0u ); // count - this->sendQue.pushUInt32 ( chan.getSID () ); // cid - this->sendQue.pushUInt32 ( chan.getCID () ); // available + if ( chan.connected () ) { + this->sendQue.pushUInt16 ( CA_PROTO_CLEAR_CHANNEL ); // cmd + this->sendQue.pushUInt16 ( 0u ); // postsize + this->sendQue.pushUInt16 ( 0u ); // dataType + this->sendQue.pushUInt16 ( 0u ); // count + this->sendQue.pushUInt32 ( chan.getSID () ); // cid + this->sendQue.pushUInt32 ( chan.getCID () ); // available + } } // @@ -1249,50 +1183,46 @@ void tcpiiu::subscriptionRequest ( nciu &chan, netSubscription & subscr ) if ( ! chan.connected() ) { return; } - - if ( subscr.getType() > 0xffff ) { - this->pCAC()->printf ( "CAC: subscriptionRequest() ignored because of unexpected bad type that was checked earlier\n" ); - return; + unsigned mask = subscr.getMask(); + if ( mask > 0xffff ) { + mask &= 0xffff; + this->pCAC()->printf ( "CAC: subscriptionRequest() truncated unusual event select mask\n" ); } - - if ( subscr.getMask() > 0xffff || subscr.getMask () == 0u ) { - this->pCAC()->printf ( "CAC: subscriptionRequest() ignored because of unexpected bad mask that was checked earlier\n" ); - return; + arrayElementCount nElem = subscr.getCount (); + unsigned maxBytes; + if ( CA_V49 ( this->minorProtocolVersion ) ) { + maxBytes = this->pCAC()->largeBufferSizeTCP (); } - - unsigned long count = subscr.getCount (); - if ( count == 0u || count > 0xffff ) { - this->pCAC()->printf ( "CAC: subscriptionRequest() ignored because of unexpected bad count that was checked earlier\n" ); - return; + else { + maxBytes = MAX_TCP; } - - this->sendQue.reserveSpace ( 32u ); - - // header - this->sendQue.pushUInt16 ( CA_PROTO_EVENT_ADD ); // cmd - this->sendQue.pushUInt16 ( 16u ); // postsize - this->sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( subscr.getType () ) ); // dataType - this->sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( count ) ); // count - this->sendQue.pushUInt32 ( chan.getSID() ); // cid - this->sendQue.pushUInt32 ( subscr.getID() ); // available + unsigned dataType = subscr.getType (); + unsigned maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; + if ( nElem > maxElem ) { + throw cacChannel::msgBodyCacheTooSmall (); + } + insertRequestHeader ( this->sendQue, + CA_PROTO_EVENT_ADD, 16u, + static_cast < ca_uint16_t > ( dataType ), + nElem, chan.getSID(), subscr.getID(), + CA_V49 ( this->minorProtocolVersion ) ); // extension this->sendQue.pushFloat32 ( 0.0 ); // m_lval this->sendQue.pushFloat32 ( 0.0 ); // m_hval this->sendQue.pushFloat32 ( 0.0 ); // m_toval - this->sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( subscr.getMask () ) ); // m_mask + this->sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( mask ) ); // m_mask this->sendQue.pushUInt16 ( 0u ); // m_pad } void tcpiiu::subscriptionCancelRequest ( nciu &chan, netSubscription &subscr ) { - this->sendQue.reserveSpace ( 16u ); - this->sendQue.pushUInt16 ( CA_PROTO_EVENT_CANCEL ); // cmd - this->sendQue.pushUInt16 ( 0u ); // postsize - this->sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( subscr.getType () ) ); // dataType - this->sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( subscr.getCount () ) ); // count - this->sendQue.pushUInt32 ( chan.getSID () ); // cid - this->sendQue.pushUInt32 ( subscr.getID() ); // available + insertRequestHeader ( this->sendQue, + CA_PROTO_EVENT_CANCEL, 0u, + static_cast < ca_uint16_t > ( subscr.getType () ), + static_cast < ca_uint16_t > ( subscr.getCount () ), + chan.getSID(), subscr.getID(), + CA_V49 ( this->minorProtocolVersion ) ); } void tcpiiu::lastChannelDetachNotify () @@ -1308,9 +1238,16 @@ bool tcpiiu::flush () { epicsAutoMutex autoMutex ( this->pCAC()->mutexRef() ); pBuf = this->sendQue.popNextComBufToSend (); - if ( ! pBuf ) { + if ( pBuf ) { + this->unacknowledgedSendBytes += pBuf->occupiedBytes (); + if ( this->unacknowledgedSendBytes > + this->socketLibrarySendBufferSize ) { + this->recvDog.sendBacklogProgressNotify (); + } + } + else { if ( this->blockingForFlush ) { - epicsEventSignal ( this->flushBlockSignal ); + this->flushBlockEvent.signal (); } this->earlyFlush = false; return true; @@ -1327,7 +1264,7 @@ bool tcpiiu::flush () pBuf->destroy (); } if ( this->blockingForFlush ) { - epicsEventSignal ( this->flushBlockSignal ); + this->flushBlockEvent.signal (); } return false; } @@ -1336,18 +1273,21 @@ bool tcpiiu::flush () void tcpiiu::blockUntilSendBacklogIsReasonable ( epicsMutex &mutex ) { + this->pCAC()->enableCallbackPreemption (); assert ( this->blockingForFlush < UINT_MAX ); this->blockingForFlush++; while ( this->sendQue.flushBlockThreshold(0u) && this->state == iiu_connected ) { - epicsAutoMutexRelease autoMutex ( mutex ); - this->pCAC()->enableCallbackPreemption (); - epicsEventWaitWithTimeout ( this->flushBlockSignal, 5.0 ); - this->pCAC()->disableCallbackPreemption (); + epicsAutoMutexRelease autoRelease ( mutex ); + this->flushBlockEvent.wait ( 5.0 ); } assert ( this->blockingForFlush > 0u ); this->blockingForFlush--; if ( this->blockingForFlush ) { - epicsEventSignal ( this->flushBlockSignal ); + this->flushBlockEvent.signal (); + } + { + epicsAutoMutexRelease autoRelease ( mutex ); + this->pCAC()->disableCallbackPreemption (); } } @@ -1355,7 +1295,7 @@ void tcpiiu::flushRequestIfAboveEarlyThreshold () { if ( ! this->earlyFlush && this->sendQue.flushEarlyThreshold(0u) ) { this->earlyFlush = true; - epicsEventSignal ( this->sendThreadFlushSignal ); + this->sendThreadFlushEvent.signal (); } } @@ -1366,18 +1306,13 @@ bool tcpiiu::flushBlockThreshold () const double tcpiiu::beaconPeriod () const { - if ( this->pBHE ) { - return this->pBHE->period (); - } - else { - return netiiu::beaconPeriod (); - } + return this->BHE.period (); } // not inline because its virtual bool tcpiiu::ca_v42_ok () const { - return CA_V42 ( CA_PROTOCOL_VERSION, this->minorProtocolVersion ); + return CA_V42 ( this->minorProtocolVersion ); } diff --git a/src/ca/test_event.cpp b/src/ca/test_event.cpp index b730adf8b..2a73dc9b7 100644 --- a/src/ca/test_event.cpp +++ b/src/ca/test_event.cpp @@ -22,26 +22,26 @@ extern "C" void epicsShareAPI ca_test_event ( struct event_handler_args args ) switch ( args.type ) { case DBR_STRING: - printf ( "CAC: Value:\t<%s>\n", (const char *) args.dbr ); + printf ( "CAC: Value:\t<%s>\n", (dbr_string_t *) args.dbr ); break; case DBR_CHAR: - printf ( "CAC: Value:\t<%d>\n", *(char *) args.dbr ); + printf ( "CAC: Value:\t<%d>\n", *(dbr_char_t *) args.dbr ); break; #if DBR_INT != DBR_SHORT case DBR_INT: #endif case DBR_SHORT: case DBR_ENUM: - printf ( "CAC: Value:\t<%d>\n", *(short *) args.dbr ); + printf ( "CAC: Value:\t<%d>\n", *(dbr_short_t *) args.dbr ); break; case DBR_LONG: - printf ( "CAC: Value:\t<%ld>\n", *(long *)args.dbr ); + printf ( "CAC: Value:\t<%ld>\n", *(dbr_long_t *)args.dbr ); break; case DBR_FLOAT: - printf ( "CAC: Value:\t<%f>\n", *(float *)args.dbr ); + printf ( "CAC: Value:\t<%f>\n", *(dbr_float_t *)args.dbr ); break; case DBR_DOUBLE: - printf ( "CAC: Value:\t<%f>\n", *(double *)args.dbr ); + printf ( "CAC: Value:\t<%f>\n", *(dbr_double_t *)args.dbr ); break; case DBR_STS_STRING: printf ( "CAC: Value:\t<%s>\n", ((struct dbr_sts_string *)args.dbr)->value ); diff --git a/src/ca/udpiiu.cpp b/src/ca/udpiiu.cpp index 022937cf0..2a725c072 100644 --- a/src/ca/udpiiu.cpp +++ b/src/ca/udpiiu.cpp @@ -135,7 +135,7 @@ udpiiu::udpiiu ( cac &cac ) : this->recvThreadExitSignal = epicsEventMustCreate ( epicsEventEmpty ); if ( ! this->recvThreadExitSignal ) { socket_close ( this->sock ); - throwWithLocation ( noMemory () ); + throwWithLocation ( std::bad_alloc () ); } /* @@ -164,7 +164,7 @@ udpiiu::udpiiu ( cac &cac ) : this->printf ("CA: unable to create UDP receive thread\n"); epicsEventDestroy (this->recvThreadExitSignal); socket_close (this->sock); - throwWithLocation ( noMemory () ); + throwWithLocation ( std::bad_alloc () ); } } @@ -232,8 +232,8 @@ void udpiiu::recvMsg () SOCKERRSTR (errnoCpy) ); } else if ( status > 0 ) { - this->postMsg ( src, - this->recvBuf, (unsigned long) status ); + this->postMsg ( src, this->recvBuf, (arrayElementCount) status, + epicsTime::getCurrent() ); } return; } @@ -485,21 +485,25 @@ void udpiiu::shutdown () epicsEventMustWait ( this->recvThreadExitSignal ); } -bool udpiiu::badUDPRespAction ( const caHdr &msg, const osiSockAddr &netAddr ) +bool udpiiu::badUDPRespAction ( const caHdr &msg, + const osiSockAddr &netAddr, const epicsTime ¤tTime ) { char buf[64]; sockAddrToDottedIP ( &netAddr.sa, buf, sizeof ( buf ) ); - this->printf ( "CAC: undecipherable ( bad msg code %u ) UDP message from %s\n", - msg.m_cmmd, buf ); + char date[64]; + currentTime.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S"); + this->printf ( "CAC: undecipherable ( bad msg code %u ) UDP message from %s at %s\n", + msg.m_cmmd, buf, date ); return false; } -bool udpiiu::noopAction ( const caHdr &, const osiSockAddr & ) +bool udpiiu::noopAction ( const caHdr &, const osiSockAddr &, const epicsTime & ) { return true; } -bool udpiiu::searchRespAction ( const caHdr &msg, const osiSockAddr &addr ) +bool udpiiu::searchRespAction ( const caHdr &msg, + const osiSockAddr &addr, const epicsTime ¤tTime ) { osiSockAddr serverAddr; unsigned minorVersion; @@ -527,7 +531,7 @@ bool udpiiu::searchRespAction ( const caHdr &msg, const osiSockAddr &addr ) * so that we can have multiple servers on one host */ serverAddr.ia.sin_family = AF_INET; - if ( CA_V48 ( CA_PROTOCOL_VERSION,minorVersion ) ) { + if ( CA_V48 ( minorVersion ) ) { if ( msg.m_cid != INADDR_BROADCAST ) { /* * Leave address in network byte order (m_cid has not been @@ -540,7 +544,7 @@ bool udpiiu::searchRespAction ( const caHdr &msg, const osiSockAddr &addr ) } serverAddr.ia.sin_port = htons ( msg.m_dataType ); } - else if ( CA_V45 (CA_PROTOCOL_VERSION,minorVersion) ) { + else if ( CA_V45 (minorVersion) ) { serverAddr.ia.sin_port = htons ( msg.m_dataType ); serverAddr.ia.sin_addr = addr.ia.sin_addr; } @@ -549,19 +553,20 @@ bool udpiiu::searchRespAction ( const caHdr &msg, const osiSockAddr &addr ) serverAddr.ia.sin_addr = addr.ia.sin_addr; } - if ( CA_V42 ( CA_PROTOCOL_VERSION, minorVersion ) ) { + if ( CA_V42 ( minorVersion ) ) { return this->pCAC ()->lookupChannelAndTransferToTCP - ( msg.m_available, msg.m_cid, USHRT_MAX, - 0, minorVersion, serverAddr ); + ( msg.m_available, msg.m_cid, 0xffff, + 0, minorVersion, serverAddr, currentTime ); } else { return this->pCAC ()->lookupChannelAndTransferToTCP ( msg.m_available, msg.m_cid, msg.m_dataType, - msg.m_count, minorVersion, serverAddr ); + msg.m_count, minorVersion, serverAddr, currentTime ); } } -bool udpiiu::beaconAction ( const caHdr &msg, const osiSockAddr &net_addr ) +bool udpiiu::beaconAction ( const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime ¤tTime ) { struct sockaddr_in ina; @@ -596,49 +601,54 @@ bool udpiiu::beaconAction ( const caHdr &msg, const osiSockAddr &net_addr ) ina.sin_port = htons ( this->serverPort ); } - this->pCAC ()->beaconNotify ( ina ); + this->pCAC ()->beaconNotify ( ina, currentTime ); return true; } -bool udpiiu::repeaterAckAction ( const caHdr &, const osiSockAddr &) +bool udpiiu::repeaterAckAction ( const caHdr &, + const osiSockAddr &, const epicsTime &) { this->pCAC ()->repeaterSubscribeConfirmNotify (); return true; } -bool udpiiu::notHereRespAction ( const caHdr &, const osiSockAddr &) +bool udpiiu::notHereRespAction ( const caHdr &, + const osiSockAddr &, const epicsTime & ) { return true; } -bool udpiiu::exceptionRespAction ( const caHdr &msg, const osiSockAddr &net_addr ) +bool udpiiu::exceptionRespAction ( const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime ¤tTime ) { const caHdr &reqMsg = * ( &msg + 1 ); char name[64]; - sockAddrToDottedIP ( &net_addr.sa, name, sizeof ( name ) ); + char date[64]; + currentTime.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S"); if ( msg.m_postsize > sizeof ( caHdr ) ){ - errlogPrintf ( "error condition \"%s\" detected by %s with context \"%s\"\n", + errlogPrintf ( "error condition \"%s\" detected by %s with context \"%s\" at %s\n", ca_message ( htonl ( msg.m_available ) ), - name, reinterpret_cast ( &reqMsg + 1 ) ); + name, reinterpret_cast ( &reqMsg + 1 ), date ); } else{ - errlogPrintf ( "error condition \"%s\" detected by %s\n", - ca_message ( htonl ( msg.m_available ) ), name ); + errlogPrintf ( "error condition \"%s\" detected by %s at %s\n", + ca_message ( htonl ( msg.m_available ) ), name, date ); } return true; } void udpiiu::postMsg ( const osiSockAddr &net_addr, - char *pInBuf, unsigned long blockSize ) + char *pInBuf, arrayElementCount blockSize, + const epicsTime ¤tTime) { caHdr *pCurMsg; while ( blockSize ) { - unsigned long size; + arrayElementCount size; if ( blockSize < sizeof ( *pCurMsg ) ) { char buf[64]; @@ -694,7 +704,7 @@ void udpiiu::postMsg ( const osiSockAddr &net_addr, else { pStub = &udpiiu::badUDPRespAction; } - bool success = ( this->*pStub ) ( *pCurMsg, net_addr ); + bool success = ( this->*pStub ) ( *pCurMsg, net_addr, currentTime ); if ( ! success ) { char buf[256]; sockAddrToDottedIP ( &net_addr.sa, buf, sizeof ( buf ) ); @@ -712,9 +722,9 @@ void udpiiu::postMsg ( const osiSockAddr &net_addr, */ bool udpiiu::pushDatagramMsg ( const caHdr &msg, const void *pExt, ca_uint16_t extsize ) { - unsigned long msgsize; - ca_uint16_t allignedExtSize; - caHdr *pbufmsg; + arrayElementCount msgsize; + ca_uint16_t allignedExtSize; + caHdr *pbufmsg; allignedExtSize = CA_MESSAGE_ALIGN ( extsize ); msgsize = sizeof ( caHdr ) + allignedExtSize; diff --git a/src/ca/udpiiu.h b/src/ca/udpiiu.h index 8da28c133..78741e703 100644 --- a/src/ca/udpiiu.h +++ b/src/ca/udpiiu.h @@ -39,6 +39,8 @@ epicsShareFunc void epicsShareAPI caRepeaterRegistrationMessage ( SOCKET sock, u extern "C" epicsShareFunc void caRepeaterThread ( void *pDummy ); epicsShareFunc void ca_repeater ( void ); +class epicsTime; + class udpiiu : public netiiu { public: udpiiu ( class cac & ); @@ -46,7 +48,8 @@ public: void shutdown (); void recvMsg (); void postMsg ( const osiSockAddr &net_addr, - char *pInBuf, unsigned long blockSize ); + char *pInBuf, arrayElementCount blockSize, + const epicsTime ¤Time); void repeaterRegistrationMessage ( unsigned attemptNumber ); void datagramFlush (); unsigned getPort () const; @@ -55,7 +58,6 @@ public: // exceptions class noSocket {}; - class noMemory {}; SOCKET getSock () const; @@ -75,19 +77,27 @@ private: bool pushDatagramMsg ( const caHdr &msg, const void *pExt, ca_uint16_t extsize ); - typedef bool ( udpiiu::*pProtoStubUDP ) ( const caHdr &, const osiSockAddr & ); + typedef bool ( udpiiu::*pProtoStubUDP ) ( const caHdr &, + const osiSockAddr &, const epicsTime & ); // UDP protocol dispatch table static const pProtoStubUDP udpJumpTableCAC[]; // UDP protocol stubs - bool noopAction ( const caHdr &, const osiSockAddr & ); - bool badUDPRespAction ( const caHdr &msg, const osiSockAddr &netAddr ); - bool searchRespAction ( const caHdr &msg, const osiSockAddr &net_addr ); - bool exceptionRespAction ( const caHdr &msg, const osiSockAddr &net_addr ); - bool beaconAction ( const caHdr &msg, const osiSockAddr &net_addr ); - bool notHereRespAction ( const caHdr &msg, const osiSockAddr &net_addr ); - bool repeaterAckAction ( const caHdr &msg, const osiSockAddr &net_addr ); + bool noopAction ( const caHdr &, + const osiSockAddr &, const epicsTime & ); + bool badUDPRespAction ( const caHdr &msg, + const osiSockAddr &netAddr, const epicsTime & ); + bool searchRespAction ( const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime & ); + bool exceptionRespAction ( const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime & ); + bool beaconAction ( const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime & ); + bool notHereRespAction ( const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime & ); + bool repeaterAckAction ( const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime & ); friend void cacRecvThreadUDP ( void *pParam ); }; diff --git a/src/ca/virtualCircuit.h b/src/ca/virtualCircuit.h index dfde35ee5..465a53fa7 100644 --- a/src/ca/virtualCircuit.h +++ b/src/ca/virtualCircuit.h @@ -21,33 +21,23 @@ #include // needed by comQueueSend #include "epicsTimer.h" +#include "epicsMemory.h" #include "ipAddrToAsciiAsynchronous.h" +#include "osiWireFormat.h" #include "comBuf.h" #include "netiiu.h" -enum iiu_conn_state {iiu_connecting, iiu_connected, iiu_disconnected}; +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; @@ -55,13 +45,12 @@ public: 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 pushString ( const char *pVal, unsigned nChar ); 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 ); @@ -72,6 +61,63 @@ private: typedef void ( comQueSend::*copyFunc_t ) ( const void *pValue, unsigned nElem ); static const copyFunc_t dbrCopyVector [39]; + + // + // visual C++ version 6.0 does not allow out of + // class member template function definition + // + template < class T > + inline void copyIn ( const T *pVal, unsigned nElem ) + { + unsigned nCopied; + comBuf *pComBuf = this->bufs.last (); + if ( pComBuf ) { + nCopied = pComBuf->copyIn ( pVal, nElem ); + this->nBytesPending += nCopied * sizeof ( T ); + } + else { + nCopied = 0u; + } + while ( nElem > nCopied ) { + comBuf *pComBuf = new ( std::nothrow ) comBuf; + if ( ! pComBuf ) { + this->wire.forcedShutdown (); + throw std::bad_alloc (); + } + unsigned nNew = pComBuf->copyIn ( &pVal[nCopied], nElem - nCopied ); + nCopied += nNew; + this->nBytesPending += nNew * sizeof ( T ); + this->bufs.add ( *pComBuf ); + } + } + + // + // visual C++ version 6.0 does not allow out of + // class member template function definition + // + template < class T > + inline void copyIn ( const T &val ) + { + comBuf *pComBuf = this->bufs.last (); + if ( pComBuf ) { + if ( pComBuf->copyIn ( &val, 1u ) >= 1u ) { + this->nBytesPending += sizeof ( T ); + return; + } + } + pComBuf = new ( std::nothrow ) comBuf; + if ( ! pComBuf ) { + this->wire.forcedShutdown (); + throw std::bad_alloc (); + } + if ( pComBuf->copyIn ( &val, 1u ) == 0u ) { + this->wire.forcedShutdown (); + throw -1; + } + this->bufs.add ( *pComBuf ); + this->nBytesPending += sizeof ( T ); + return; + } }; static const unsigned maxBytesPendingTCP = 0x4000; @@ -81,9 +127,21 @@ public: comQueRecv (); ~comQueRecv (); unsigned occupiedBytes () const; - bool copyOutBytes ( void *pBuf, unsigned nBytes ); + unsigned copyOutBytes ( void *pBuf, unsigned nBytes ); + unsigned removeBytes ( unsigned nBytes ); void pushLastComBufReceived ( comBuf & ); void clear (); + epicsInt8 popInt8 (); + epicsUInt8 popUInt8 (); + epicsInt16 popInt16 (); + epicsUInt16 popUInt16 (); + epicsInt32 popInt32 (); + epicsUInt32 popUInt32 (); + epicsFloat32 popFloat32 (); + epicsFloat64 popFloat64 (); + void popString ( epicsOldString * ); + + class insufficentBytesAvailable {}; private: tsDLList < comBuf > bufs; }; @@ -93,6 +151,7 @@ public: tcpRecvWatchdog ( tcpiiu &, double periodIn, epicsTimerQueue & queueIn ); virtual ~tcpRecvWatchdog (); void rescheduleRecvTimer (); + void sendBacklogProgressNotify (); void messageArrivalNotify (); void beaconArrivalNotify (); void beaconAnomalyNotify (); @@ -105,7 +164,7 @@ private: tcpiiu &iiu; bool responsePending; bool beaconAnomaly; - expireStatus expire (); + expireStatus expire ( const epicsTime & currentTime ); }; class tcpSendWatchdog : private epicsTimerNotify { @@ -118,19 +177,18 @@ private: const double period; epicsTimer &timer; tcpiiu &iiu; - expireStatus expire (); + expireStatus expire ( const epicsTime & currentTime ); }; class hostNameCache : public ipAddrToAsciiAsynchronous { public: hostNameCache ( const osiSockAddr &addr, ipAddrToAsciiEngine &engine ); + ~hostNameCache (); 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]; @@ -141,14 +199,25 @@ private: extern "C" void cacSendThreadTCP ( void *pParam ); extern "C" void cacRecvThreadTCP ( void *pParam ); +// a modified ca header with capacity for large arrays +struct caHdrLargeArray { + ca_uint32_t m_postsize; // size of message extension + ca_uint32_t m_count; // operation data count + ca_uint32_t m_cid; // channel identifier + ca_uint32_t m_available; // protocol stub dependent + ca_uint16_t m_dataType; // operation data type + ca_uint16_t m_cmmd; // operation to be performed +}; + class tcpiiu : public netiiu, public tsDLNode < tcpiiu >, private wireSendAdapter, private wireRecvAdapter { public: - tcpiiu ( cac &cac, double connectionTimeout, epicsTimerQueue &timerQueue ); + tcpiiu ( cac &cac, double connectionTimeout, + epicsTimerQueue &timerQueue, const osiSockAddr &addrIn, + unsigned minorVersion, class bhe &bhe, + ipAddrToAsciiEngine &engineIn ); ~tcpiiu (); - bool initiateConnect ( const osiSockAddr &addrIn, unsigned minorVersion, - class bhe &bhe, ipAddrToAsciiEngine &engineIn ); void connect (); void processIncoming (); void destroy (); @@ -157,7 +226,6 @@ public: void beaconAnomalyNotify (); void beaconArrivalNotify (); - bool fullyConstructed () const; void flushRequest (); bool flushBlockThreshold () const; void flushRequestIfAboveEarlyThreshold (); @@ -168,13 +236,14 @@ public: bool ca_v41_ok () const; bool ca_v42_ok () const; bool ca_v44_ok () const; + bool ca_v49_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; + bhe & getBHE () const; SOCKET getSock() const; bool trueOnceOnly (); @@ -184,26 +253,29 @@ private: tcpSendWatchdog sendDog; comQueSend sendQue; comQueRecv recvQue; + caHdrLargeArray curMsg; osiSockAddr addr; - hostNameCache *pHostNameCache; - caHdr curMsg; - unsigned long curDataMax; - class bhe *pBHE; + arrayElementCount curDataMax; + arrayElementCount curDataBytes; + epics_auto_ptr < hostNameCache > pHostNameCache; + class bhe & BHE; char *pCurData; unsigned minorProtocolVersion; iiu_conn_state state; - epicsEventId sendThreadFlushSignal; - epicsEventId recvThreadRingBufferSpaceAvailableSignal; - epicsEventId sendThreadExitSignal; - epicsEventId recvThreadExitSignal; - epicsEventId flushBlockSignal; + epicsEvent sendThreadFlushEvent; + epicsEvent recvThreadRingBufferSpaceAvailableEvent; + epicsEvent sendThreadExitEvent; + epicsEvent recvThreadExitEvent; + epicsEvent flushBlockEvent; SOCKET sock; unsigned contigRecvMsgCount; unsigned blockingForFlush; - bool fullyConstructedFlag; + unsigned socketLibrarySendBufferSize; + unsigned unacknowledgedSendBytes; bool busyStateDetected; // only modified by the recv thread bool flowControlActive; // only modified by the send process thread bool echoRequestPending; + bool oldMsgHeaderAvailable; bool msgHeaderAvailable; bool sockCloseCompleted; bool f_trueOnceOnly; @@ -235,38 +307,6 @@ private: 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] ) ) ) { @@ -278,105 +318,24 @@ inline bool comQueSend::dbr_type_ok ( unsigned type ) 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 ); + this->copyIn ( value ); } inline void comQueSend::pushUInt32 ( const ca_uint32_t value ) { - comQueSend_copyIn ( this->nBytesPending, - this->bufs, this->reservoir, value ); + this->copyIn ( value ); } inline void comQueSend::pushFloat32 ( const ca_float32_t value ) { - comQueSend_copyIn ( this->nBytesPending, - this->bufs, this->reservoir, value ); + this->copyIn ( value ); } -inline void comQueSend::pushString ( const char *pVal, unsigned nElem ) +inline void comQueSend::pushString ( const char *pVal, unsigned nChar ) { - comQueSend_copyIn ( this->nBytesPending, - this->bufs, this->reservoir, pVal, nElem ); + this->copyIn ( pVal, nChar ); } // it is assumed that dbr_type_ok() was called prior to calling this routine @@ -415,19 +374,80 @@ inline comBuf * comQueSend::popNextComBufToSend () return pBuf; } -inline bool tcpiiu::fullyConstructed () const + +inline epicsInt8 comQueRecv::popInt8 () { - return this->fullyConstructedFlag; + return static_cast < epicsInt8 > ( this->popUInt8() ); +} + +inline epicsUInt16 comQueRecv::popUInt16 () +{ + epicsUInt16 tmp = this->popUInt8 (); + tmp <<= 8u; + tmp |= this->popUInt8 (); + return tmp; +} + +inline epicsInt16 comQueRecv::popInt16 () +{ + epicsInt16 tmp = this->popInt8 (); + tmp <<= 8u; + tmp |= this->popInt8 (); + return tmp; +} + +inline epicsUInt32 comQueRecv::popUInt32 () +{ + epicsUInt32 tmp = this->popUInt8 (); + tmp <<= 24u; + tmp |= this->popUInt8 () << 16u; + tmp |= this->popUInt8 () << 8u; + tmp |= this->popUInt8 (); + return tmp; +} + +inline epicsInt32 comQueRecv::popInt32 () +{ + epicsInt32 tmp = this->popInt8 (); + tmp <<= 24u; + tmp |= this->popInt8 () << 16u; + tmp |= this->popInt8 () << 8u; + tmp |= this->popInt8 (); + return tmp; +} + +inline epicsFloat32 comQueRecv::popFloat32 () +{ + epicsFloat32 tmp; + epicsUInt8 wire[ sizeof ( tmp ) ]; + for ( unsigned i = 0u; i < sizeof ( tmp ); i++ ) { + wire[i] = this->popUInt8 (); + } + osiConvertFromWireFormat ( tmp, wire ); + return tmp; +} + +inline epicsFloat64 comQueRecv::popFloat64 () +{ + epicsFloat64 tmp; + epicsUInt8 wire[ sizeof ( tmp ) ]; + for ( unsigned i = 0u; i < sizeof ( tmp ); i++ ) { + wire[i] = this->popUInt8 (); + } + osiConvertFromWireFormat ( tmp, wire ); + return tmp; +} + +inline void comQueRecv::popString ( epicsOldString *pStr ) +{ + for ( unsigned i = 0u; i < sizeof ( *pStr ); i++ ) { + pStr[0][i] = this->popInt8 (); + } } inline void tcpiiu::hostName ( char *pBuf, unsigned bufLength ) const { - if ( this->pHostNameCache ) { - this->pHostNameCache->hostName ( pBuf, bufLength ); - } - else { - netiiu::hostName ( pBuf, bufLength ); - } + this->pHostNameCache->hostName ( pBuf, bufLength ); } // deprecated - please dont use - this is _not_ thread safe @@ -440,17 +460,22 @@ inline const char * tcpiiu::pHostName () const inline void tcpiiu::flushRequest () { - epicsEventSignal ( this->sendThreadFlushSignal ); -} - -inline bool tcpiiu::ca_v44_ok () const -{ - return CA_V44 ( CA_PROTOCOL_VERSION, this->minorProtocolVersion ); + this->sendThreadFlushEvent.signal (); } inline bool tcpiiu::ca_v41_ok () const { - return CA_V41 ( CA_PROTOCOL_VERSION, this->minorProtocolVersion ); + return CA_V41 ( this->minorProtocolVersion ); +} + +inline bool tcpiiu::ca_v44_ok () const +{ + return CA_V44 ( this->minorProtocolVersion ); +} + +inline bool tcpiiu::ca_v49_ok () const +{ + return CA_V49 ( this->minorProtocolVersion ); } inline bool tcpiiu::alive () const @@ -464,9 +489,9 @@ inline bool tcpiiu::alive () const } } -inline bhe * tcpiiu::getBHE () const +inline bhe & tcpiiu::getBHE () const { - return this->pBHE; + return this->BHE; } inline void tcpiiu::beaconAnomalyNotify () diff --git a/src/rsrv/camessage.c b/src/rsrv/camessage.c index 6292c80ac..9e20161a1 100644 --- a/src/rsrv/camessage.c +++ b/src/rsrv/camessage.c @@ -48,8 +48,9 @@ #include "db_field_log.h" #include "callback.h" #include "asDbLib.h" -#include "net_convert.h" +typedef unsigned long arrayElementCount; +#include "net_convert.h" #include "server.h" static caHdr nill_msg; @@ -58,8 +59,8 @@ static caHdr nill_msg; LOCAL EVENTFUNC read_reply; -#define logBadId(CLIENT, MP)\ -logBadIdWithFileAndLineno(CLIENT, MP, __FILE__, __LINE__) +#define logBadId(CLIENT, MP, PPL)\ +logBadIdWithFileAndLineno(CLIENT, MP, PPL, __FILE__, __LINE__) #if 0 /* @@ -94,7 +95,7 @@ LOCAL void *casCalloc(size_t count, size_t size) * * used to be a macro */ -LOCAL struct channel_in_use *MPTOPCIU (const caHdr *mp) +LOCAL struct channel_in_use *MPTOPCIU (const caHdrLargeArray *mp) { struct channel_in_use *pciu; const unsigned id = mp->m_cid; @@ -106,82 +107,46 @@ LOCAL struct channel_in_use *MPTOPCIU (const caHdr *mp) return pciu; } -/* send_err() +/* vsend_err() * * reflect error msg back to the client * * send buffer lock must be on while in this routine * */ -LOCAL void send_err( -const caHdr *curp, -int status, -struct client *client, -const char *pformat, - ... +LOCAL void vsend_err( +const caHdrLargeArray *curp, +int status, +struct client *client, +const char *pformat, +va_list args ) { - va_list args; struct channel_in_use *pciu; - int size; - caHdr *reply; + caHdr *pReqOut; char *pMsgString; + ca_uint32_t size; + ca_uint32_t cid; + int success; - va_start(args, pformat); - - /* - * allocate plenty of space for a sprintf() buffer - */ - reply = (caHdr *) ALLOC_MSG(client, 512); - if (!reply){ - int logMsgArgs[6]; - size_t i; - - for(i=0; i< NELEMENTS(logMsgArgs); i++){ - logMsgArgs[i] = va_arg(args, int); - } - - errlogPrintf( "caserver: Unable to deliver err msg to client => \"%s\"\n", - (int) ca_message(status)); - errlogPrintf( - (char *) pformat, - logMsgArgs[0], - logMsgArgs[1], - logMsgArgs[2], - logMsgArgs[3], - logMsgArgs[4], - logMsgArgs[5]); - - return; - } - - reply[0] = nill_msg; - reply[0].m_cmmd = CA_PROTO_ERROR; - reply[0].m_available = status; - - switch (curp->m_cmmd) { + switch ( curp->m_cmmd ) { case CA_PROTO_EVENT_ADD: case CA_PROTO_EVENT_CANCEL: case CA_PROTO_READ: case CA_PROTO_READ_NOTIFY: case CA_PROTO_WRITE: case CA_PROTO_WRITE_NOTIFY: - /* - * - * Verify the channel - * - */ pciu = MPTOPCIU(curp); if(pciu){ - reply->m_cid = (unsigned long) pciu->cid; + cid = pciu->cid; } else{ - reply->m_cid = ~0L; + cid = 0xffffffff; } break; case CA_PROTO_SEARCH: - reply->m_cid = curp->m_cid; + cid = curp->m_cid; break; case CA_PROTO_EVENTS_ON: @@ -189,35 +154,79 @@ const char *pformat, case CA_PROTO_READ_SYNC: case CA_PROTO_SNAPSHOT: default: - reply->m_cid = ~0L; + cid = 0xffffffff; break; } + /* + * allocate plenty of space for a sprintf() buffer + */ + success = cas_copy_in_header ( client, + CA_PROTO_ERROR, 512, 0, 0, cid, status, &pReqOut ); + if ( ! success ) { + errlogPrintf ( "caserver: Unable to deliver err msg to client => \"%s\"\n", + ca_message (status) ); + errlogVprintf ( pformat, args ); + return; + } + /* * copy back the request protocol * (in network byte order) */ - reply[1].m_postsize = htons (curp->m_postsize); - reply[1].m_cmmd = htons (curp->m_cmmd); - reply[1].m_dataType = htons (curp->m_dataType); - reply[1].m_count = htons (curp->m_count); - reply[1].m_cid = curp->m_cid; - reply[1].m_available = curp->m_available; + if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && + CA_V49( client->minor_version_number ) ) { + ca_uint32_t *pLW = ( ca_uint32_t * ) ( pReqOut + 1 ); + pReqOut->m_cmmd = htons ( curp->m_cmmd ); + pReqOut->m_postsize = htons ( 0xffff ); + pReqOut->m_dataType = htons ( curp->m_dataType ); + pReqOut->m_count = htons ( 0u ); + pReqOut->m_cid = htonl ( curp->m_cid ); + pReqOut->m_available = htonl ( curp->m_available ); + pLW[0] = htonl ( curp->m_postsize ); + pLW[1] = htonl ( curp->m_count ); + pMsgString = ( char * ) ( pLW + 2 ); + size = sizeof ( caHdr ) + 2 * sizeof ( *pLW ); + } + else { + pReqOut->m_cmmd = htons (curp->m_cmmd); + pReqOut->m_postsize = htons ( ( (ca_uint16_t) curp->m_postsize ) ); + pReqOut->m_dataType = htons (curp->m_dataType); + pReqOut->m_count = htons ( ( (ca_uint16_t) curp->m_count ) ); + pReqOut->m_cid = htonl (curp->m_cid); + pReqOut->m_available = htonl (curp->m_available); + pMsgString = ( char * ) ( pReqOut + 1 ); + size = sizeof ( caHdr ); + } /* * add their context string into the protocol */ - pMsgString = (char *) (reply+2); - status = vsprintf(pMsgString, pformat, args); + status = vsprintf ( pMsgString, pformat, args ); + if ( status >= 0 ) { + size += ( ( ca_uint32_t ) status ) + 1u; + } + cas_commit_msg ( client, size ); +} - /* - * force string post size to be the true size rounded to even - * boundary - */ - size = strlen(pMsgString)+1; - size += sizeof(*curp); - reply->m_postsize = size; - END_MSG(client); +/* send_err() + * + * reflect error msg back to the client + * + * send buffer lock must be on while in this routine + * + */ +LOCAL void send_err ( +const caHdrLargeArray *curp, +int status, +struct client *client, +const char *pformat, + ... ) +{ + va_list args; + va_start ( args, pformat ); + vsend_err ( curp, status, client, pformat, args ); + va_end ( args ); } /* log_header() @@ -226,10 +235,11 @@ const char *pformat, * */ LOCAL void log_header ( - const char *pContext, - struct client *client, - const caHdr *mp, - unsigned mnum + const char *pContext, + struct client *client, + const caHdrLargeArray *mp, + const void *pPayLoad, + unsigned mnum ) { struct channel_in_use *pciu; @@ -253,45 +263,90 @@ LOCAL void log_header ( "CAS: Request from %s => available=0x%x \tN=%u paddr=%x\n", hostName, mp->m_available, mnum, (pciu?&pciu->addr:NULL)); - if (mp->m_cmmd==CA_PROTO_WRITE && mp->m_dataType==DBF_STRING) { + if (mp->m_cmmd==CA_PROTO_WRITE && mp->m_dataType==DBF_STRING && pPayLoad ) { epicsPrintf ( "CAS: Request from %s => \tThe string written: %s \n", - hostName, (mp+1)); + hostName, pPayLoad ); } } +/* log_old_header () + */ +LOCAL void log_old_header ( + const char *pContext, + struct client *client, + const caHdr *mp, + const void *pPayLoad, + unsigned mnum +) +{ + caHdrLargeArray msg; + msg.m_cmmd = mp->m_cmmd; + msg.m_postsize = mp->m_postsize; + msg.m_count = mp->m_count; + msg.m_dataType = mp->m_dataType; + msg.m_cid = mp->m_cid; + msg.m_available = mp->m_available; + log_header ( pContext, client, &msg, pPayLoad, mnum ); +} + /* * logBadIdWithFileAndLineno() */ LOCAL void logBadIdWithFileAndLineno( -struct client *client, -caHdr *mp, -char *pFileName, -unsigned lineno +struct client *client, +caHdrLargeArray *mp, +const void *pPayload, +char *pFileName, +unsigned lineno ) { - log_header("bad resource ID", client, mp,0); - SEND_LOCK(client); - send_err( - mp, - ECA_INTERNAL, - client, - "Bad Resource ID at %s.%d", - pFileName, - lineno); - SEND_UNLOCK(client); + log_header ( "bad resource ID", client, mp, pPayload, 0 ); + SEND_LOCK ( client ); + send_err ( mp, ECA_INTERNAL, client, "Bad Resource ID at %s.%d", + pFileName, lineno ); + SEND_UNLOCK ( client ); +} + +/* + * bad_udp_cmd_action() + */ +LOCAL int bad_udp_cmd_action ( caHdrLargeArray *mp, + const void *pPayload, struct client *pClient ) +{ + log_header ("invalid (damaged?) request code from UDP", + pClient, mp, pPayload, 0); + return RSRV_ERROR; +} + +/* + * udp_echo_action() + */ +LOCAL int udp_echo_action ( caHdrLargeArray *mp, + const void *pPayload, struct client *pClient ) +{ + char *pPayloadOut; + int success; + SEND_LOCK ( pClient ); + success = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, + mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, + &pPayloadOut ); + if ( success ) { + memcpy ( pPayloadOut, pPayload, mp->m_postsize ); + cas_commit_msg ( pClient, mp->m_postsize ); + } + SEND_UNLOCK ( pClient ); + return RSRV_OK; } /* * bad_tcp_cmd_action() */ -LOCAL int bad_tcp_cmd_action( -caHdr *mp, -struct client *client -) +LOCAL int bad_tcp_cmd_action ( caHdrLargeArray *mp, const void *pPayload, + struct client *client ) { const char *pCtx = "invalid (damaged?) request code from TCP"; - log_header (pCtx, client, mp, 0); + log_header ( pCtx, client, mp, pPayload, 0 ); /* * by default, clients dont recover @@ -305,24 +360,10 @@ struct client *client } /* - * bad_udp_cmd_action() + * tcp_noop_action() */ -LOCAL int bad_udp_cmd_action( -caHdr *mp, -struct client *client -) -{ - log_header ("invalid (damaged?) request code from UDP", client, mp, 0); - return RSRV_ERROR; -} - -/* - * noop_action() - */ -LOCAL int noop_action( -caHdr *mp, -struct client *client -) +LOCAL int tcp_noop_action ( caHdrLargeArray *mp, const void *pPayload, + struct client *client ) { return RSRV_OK; } @@ -330,51 +371,40 @@ struct client *client /* * echo_action() */ -LOCAL int echo_action( -caHdr *mp, -struct client *client -) +LOCAL int echo_action ( caHdrLargeArray *mp, + const void *pPayload, struct client *pClient ) { - caHdr *reply; - - SEND_LOCK(client); - reply = ALLOC_MSG(client, 0); - if (reply) { - /* - * header (host) will we converted in send, - * content is still in net format: - */ - *reply = *mp; - END_MSG(client); + char *pPayloadOut; + int success; + SEND_LOCK ( pClient ); + success = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, + mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, + &pPayloadOut ); + if ( success ) { + memcpy ( pPayloadOut, pPayload, mp->m_postsize ); + cas_commit_msg ( pClient, mp->m_postsize ); } - SEND_UNLOCK(client); - + SEND_UNLOCK ( pClient ); return RSRV_OK; } /* * events_on_action () */ -LOCAL int events_on_action ( -caHdr *mp, -struct client *client -) +LOCAL int events_on_action ( caHdrLargeArray *mp, + const void *pPayload, struct client *pClient ) { - db_event_flow_ctrl_mode_off(client->evuser); - + db_event_flow_ctrl_mode_off ( pClient->evuser ); return RSRV_OK; } /* * events_off_action () */ -LOCAL int events_off_action ( -caHdr *mp, -struct client *client -) +LOCAL int events_off_action ( caHdrLargeArray *mp, + const void *pPayload, struct client *pClient ) { - db_event_flow_ctrl_mode_on(client->evuser); - + db_event_flow_ctrl_mode_on ( pClient->evuser ); return RSRV_OK; } @@ -386,147 +416,124 @@ struct client *client * substantial complication introduced here by the need for backwards * compatibility */ -LOCAL void no_read_access_event( -struct client *client, -struct event_ext *pevext -) +LOCAL void no_read_access_event ( struct client *pClient, + struct event_ext *pevext ) { - caHdr *reply; - int v41; - - v41 = CA_V41(CA_PROTOCOL_VERSION,client->minor_version_number); + char *pPayloadOut; + int success; /* * continue to return an exception * on failure to pre v41 clients */ - if(!v41){ - send_err( - &pevext->msg, - ECA_GETFAIL, - client, - RECORD_NAME(&pevext->pciu->addr)); + if ( ! CA_V41 ( pClient->minor_version_number ) ) { + send_err ( &pevext->msg, ECA_GETFAIL, pClient, + RECORD_NAME ( &pevext->pciu->addr ) ); return; } - reply = (caHdr *) ALLOC_MSG(client, pevext->size); - if (!reply) { - send_err( - &pevext->msg, - ECA_TOLARGE, - client, - RECORD_NAME(&pevext->pciu->addr)); - return; + /* + * New clients recv the status of the + * operation directly to the + * event/put/get callback. + * + * Fetched value is zerod in case they + * use it even when the status indicates + * failure. + * + * The m_cid field in the protocol + * header is abused to carry the status + */ + success = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size, + pevext->msg.m_dataType, pevext->msg.m_count, ECA_NORDACCESS, + pevext->msg.m_available, &pPayloadOut ); + if ( success ) { + memset ( pPayloadOut, 0, pevext->size ); + cas_commit_msg ( pClient, pevext->size ); } - else{ - /* - * New clients recv the status of the - * operation directly to the - * event/put/get callback. - * - * Fetched value is zerod in case they - * use it even when the status indicates - * failure. - * - * The m_cid field in the protocol - * header is abused to carry the status - */ - *reply = pevext->msg; - reply->m_postsize = pevext->size; - reply->m_cid = ECA_NORDACCESS; - memset((void *)(reply+1), 0, pevext->size); - END_MSG(client); + else { + send_err ( &pevext->msg, ECA_TOLARGE, pClient, + RECORD_NAME ( &pevext->pciu->addr ) ); } } /* * read_reply() */ -LOCAL void read_reply( -void *pArg, -struct dbAddr *paddr, -int eventsRemaining, -db_field_log *pfl -) +LOCAL void read_reply ( void *pArg, struct dbAddr *paddr, + int eventsRemaining, db_field_log *pfl ) { + ca_uint32_t cid; + void *pPayload; struct event_ext *pevext = pArg; - struct client *client = pevext->pciu->client; - struct channel_in_use *pciu = pevext->pciu; - caHdr *reply; - int status; - int strcnt; - int v41; + struct client *pClient = pevext->pciu->client; + struct channel_in_use *pciu = pevext->pciu; + int status; + int success; + int strcnt; + int v41; - if (pevext->send_lock) - SEND_LOCK(client); - - reply = (caHdr *) ALLOC_MSG(client, pevext->size); - if (!reply) { - send_err(&pevext->msg, ECA_TOLARGE, client, RECORD_NAME(paddr)); - if (!eventsRemaining) - cas_send_msg(client,!pevext->send_lock); - if (pevext->send_lock) - SEND_UNLOCK(client); - return; - } + if ( pevext->send_lock ) + SEND_LOCK ( pClient ); /* - * setup response message + * New clients recv the status of the + * operation directly to the + * event/put/get callback. + * + * The m_cid field in the protocol + * header is abused to carry the status, + * but get calls still use the + * m_cid field to identify the channel */ - *reply = pevext->msg; - reply->m_postsize = pevext->size; - reply->m_cid = (unsigned long)pciu->cid; + v41 = CA_V41 ( pClient->minor_version_number ); + if ( v41 ) { + cid = ECA_NORMAL; + } + else { + cid = pciu->cid; + } + + success = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size, + pevext->msg.m_dataType, pevext->msg.m_count, cid, pevext->msg.m_available, + &pPayload ); + if ( ! success ) { + send_err ( &pevext->msg, ECA_TOLARGE, pClient, RECORD_NAME ( paddr ) ); + if ( ! eventsRemaining ) + cas_send_msg ( pClient, ! pevext->send_lock ); + if ( pevext->send_lock ) + SEND_UNLOCK ( pClient ); + return; + } /* * verify read access */ - v41 = CA_V41(CA_PROTOCOL_VERSION,client->minor_version_number); - if(!asCheckGet(pciu->asClientPVT)){ - if(reply->m_cmmd==CA_PROTO_READ){ - if(v41){ - status = ECA_NORDACCESS; - } - else{ - status = ECA_GETFAIL; - } - /* - * old client & plain get & search/get - * continue to return an exception - * on failure - */ - send_err(&pevext->msg, status, - client, RECORD_NAME(paddr)); - } - else{ - no_read_access_event(client, pevext); - } - if (!eventsRemaining) - cas_send_msg(client,!pevext->send_lock); - if (pevext->send_lock){ - SEND_UNLOCK(client); + if ( ! asCheckGet ( pciu->asClientPVT ) ) { + no_read_access_event ( pClient, pevext ); + if ( ! eventsRemaining ) + cas_send_msg ( pClient, !pevext->send_lock ); + if ( pevext->send_lock ) { + SEND_UNLOCK ( pClient ); } return; } - status = db_get_field ( - paddr, - pevext->msg.m_dataType, - reply + 1, - pevext->msg.m_count, - pfl); - if (status < 0) { + status = db_get_field ( paddr, pevext->msg.m_dataType, + pPayload, pevext->msg.m_count, pfl); + if ( status < 0 ) { /* * I cant wait to redesign this protocol from scratch! */ - if(!v41||reply->m_cmmd==CA_PROTO_READ){ + if ( ! v41 ) { /* * old client & plain get * continue to return an exception * on failure */ - send_err(&pevext->msg, ECA_GETFAIL, client, RECORD_NAME(paddr)); + send_err ( &pevext->msg, ECA_GETFAIL, pClient, RECORD_NAME ( paddr ) ); } - else{ + else { /* * New clients recv the status of the * operation directly to the @@ -539,86 +546,153 @@ db_field_log *pfl * The m_cid field in the protocol * header is abused to carry the status */ - memset((void *)(reply+1), 0, pevext->size); - reply->m_cid = ECA_GETFAIL; - END_MSG(client); + memset ( pPayload, 0, pevext->size ); + cas_set_header_cid ( pClient, ECA_GETFAIL ); + cas_commit_msg ( pClient, pevext->size ); } } - else{ /* status of db_get_field >= 0 - * - * New clients recv the status of the - * operation directly to the - * event/put/get callback. - * - * The m_cid field in the protocol - * header is abused to carry the status - * - * get calls still use the - * m_cid field to identify the channel. - */ - if( v41 && reply->m_cmmd!=CA_PROTO_READ){ - reply->m_cid = ECA_NORMAL; - } - -#ifdef CONVERSION_REQUIRED - /* - * assert() is safe here because the type was - * checked by db_get_field() - */ - if (pevext->msg.m_dataType >= NELEMENTS(cac_dbr_cvrt)) { - memset((void *)(reply+1), 0, pevext->size); - reply->m_cid = ECA_GETFAIL; - } - else { - /* use type as index into conversion jumptable */ - (* cac_dbr_cvrt[pevext->msg.m_dataType]) - ( reply + 1, - reply + 1, - TRUE, /* host -> net format */ - pevext->msg.m_count); - } -#endif + else { +# ifdef CONVERSION_REQUIRED + /* + * assert() is safe here because the type was + * checked by db_get_field() + */ + if ( pevext->msg.m_dataType >= NELEMENTS (cac_dbr_cvrt) ) { + memset ( pPayload, 0, pevext->size ); + cas_set_header_cid ( pClient, ECA_GETFAIL ); + } + else { + /* use type as index into conversion jumptable */ + ( *cac_dbr_cvrt[pevext->msg.m_dataType] ) + ( pPayload, pPayload, TRUE /* host -> net format */, + pevext->msg.m_count ); + } +# endif /* * force string message size to be the true size rounded to even * boundary */ - if (pevext->msg.m_dataType == DBR_STRING - && pevext->msg.m_count == 1) { + if ( pevext->msg.m_dataType == DBR_STRING + && pevext->msg.m_count == 1 ) { /* add 1 so that the string terminator will be shipped */ - strcnt = strlen((char *)(reply + 1)) + 1; - reply->m_postsize = strcnt; + strcnt = strlen ( (char *) pPayload ) + 1; + cas_commit_msg ( pClient, strcnt ); + } + else { + cas_commit_msg ( pClient, pevext->size ); } - - END_MSG(client); } /* * Ensures timely response for events, but does que * them up like db requests when the OPI does not keep up. */ - if (!eventsRemaining) - cas_send_msg(client,!pevext->send_lock); + if ( ! eventsRemaining ) + cas_send_msg ( pClient, ! pevext->send_lock ); - if (pevext->send_lock) - SEND_UNLOCK(client); + if ( pevext->send_lock ) + SEND_UNLOCK ( pClient ); return; } /* - * read_action() + * read_action () */ -LOCAL int read_action( -caHdr *mp, -struct client *client -) +LOCAL int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *pClient ) +{ + struct channel_in_use *pciu; + ca_uint32_t payloadSize; + void *pPayload; + int status; + int strcnt; + int success; + int v41; + + pciu = MPTOPCIU ( mp ); + if ( ! pciu ) { + logBadId ( pClient, mp, 0 ); + return RSRV_ERROR; + } + + SEND_LOCK ( pClient ); + +# ifdef CONVERSION_REQUIRED + if ( mp->m_dataType >= NELEMENTS ( cac_dbr_cvrt ) ) { + send_err ( mp, ECA_BADTYPE, pClient, RECORD_NAME ( &pciu->addr ) ); + SEND_UNLOCK ( pClient ); + } +# endif + + payloadSize = dbr_size_n ( mp->m_dataType, mp->m_count ); + success = cas_copy_in_header ( pClient, mp->m_cmmd, payloadSize, + mp->m_dataType, mp->m_count, pciu->cid, mp->m_available, &pPayload ); + if ( ! success ) { + send_err ( mp, ECA_TOLARGE, pClient, RECORD_NAME ( &pciu->addr ) ); + SEND_UNLOCK ( pClient ); + return RSRV_OK; + } + + /* + * verify read access + */ + v41 = CA_V41 ( pClient->minor_version_number ); + if ( ! asCheckGet ( pciu->asClientPVT ) ) { + if ( v41 ) { + status = ECA_NORDACCESS; + } + else{ + status = ECA_GETFAIL; + } + send_err ( mp, status, + pClient, RECORD_NAME ( &pciu->addr ) ); + SEND_UNLOCK ( pClient ); + return RSRV_OK; + } + + status = db_get_field ( &pciu->addr, mp->m_dataType, + pPayload, mp->m_count, 0 ); + if ( status < 0 ) { + send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( &pciu->addr ) ); + SEND_UNLOCK ( pClient ); + return RSRV_OK; + } + +# ifdef CONVERSION_REQUIRED + /* use type as index into conversion jumptable */ + (* cac_dbr_cvrt[mp->m_dataType]) + ( pPayload, pPayload, TRUE /* host -> net format */, + mp->m_count ); +# endif + /* + * force string message size to be the true size rounded to even + * boundary + */ + if ( mp->m_dataType == DBR_STRING && mp->m_count == 1 ) { + /* add 1 so that the string terminator will be shipped */ + strcnt = strlen ( (char *) pPayload ) + 1; + cas_commit_msg ( pClient, strcnt ); + } + else { + cas_commit_msg ( pClient, payloadSize ); + } + + SEND_UNLOCK ( pClient ); + + return RSRV_OK; +} + +/* + * read_notify_action() + */ +LOCAL int read_notify_action ( caHdrLargeArray *mp, const void *pPayload, struct client *client ) { struct channel_in_use *pciu; struct event_ext evext; - pciu = MPTOPCIU(mp); - if(!pciu){ - logBadId(client, mp); + pciu = MPTOPCIU ( mp ); + if ( !pciu ) { + logBadId ( client, mp, pPayload ); return RSRV_ERROR; } @@ -626,7 +700,7 @@ struct client *client evext.pciu = pciu; evext.send_lock = TRUE; evext.pdbev = NULL; - evext.size = dbr_size_n(mp->m_dataType, mp->m_count); + evext.size = dbr_size_n ( mp->m_dataType, mp->m_count ); /* * Arguments to this routine organized in @@ -637,7 +711,7 @@ struct client *client * Hold argument set true so the send message * buffer is not flushed once each call. */ - read_reply(&evext, &pciu->addr, TRUE, NULL); + read_reply ( &evext, &pciu->addr, TRUE, NULL ); return RSRV_OK; } @@ -645,10 +719,8 @@ struct client *client /* * write_action() */ -LOCAL int write_action( -caHdr *mp, -struct client *client -) +LOCAL int write_action ( caHdrLargeArray *mp, + void *pPayload, struct client *client ) { struct channel_in_use *pciu; int v41; @@ -657,14 +729,12 @@ struct client *client pciu = MPTOPCIU(mp); if(!pciu){ - logBadId(client, mp); + logBadId(client, mp, pPayload); return RSRV_ERROR; } if(!rsrvCheckPut(pciu)){ - v41 = CA_V41( - CA_PROTOCOL_VERSION, - client->minor_version_number); + v41 = CA_V41(client->minor_version_number); if(v41){ status = ECA_NOWTACCESS; } @@ -683,7 +753,7 @@ struct client *client #ifdef CONVERSION_REQUIRED if (mp->m_dataType >= NELEMENTS(cac_dbr_cvrt)) { - log_header ("invalid data type", client, mp, 0); + log_header ("invalid data type", client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, @@ -696,19 +766,23 @@ struct client *client /* use type as index into conversion jumptable */ (* cac_dbr_cvrt[mp->m_dataType]) - ( mp + 1, - mp + 1, + ( pPayload, + pPayload, FALSE, /* net -> host format */ mp->m_count); #endif - asWritePvt = asTrapWriteBefore(pciu->asClientPVT, - pciu->client->pUserName,pciu->client->pHostName,(void *)&pciu->addr); + asWritePvt = asTrapWriteBefore ( pciu->asClientPVT, + pciu->client->pUserName ? pciu->client->pUserName : "", + pciu->client->pHostName ? pciu->client->pHostName : "", + (void *) &pciu->addr ); + status = db_put_field( &pciu->addr, mp->m_dataType, - mp + 1, + pPayload, mp->m_count); + asTrapWriteAfter(asWritePvt); if (status < 0) { SEND_LOCK(client); @@ -719,27 +793,27 @@ struct client *client RECORD_NAME(&pciu->addr)); SEND_UNLOCK(client); } + return RSRV_OK; } /* * host_name_action() */ -LOCAL int host_name_action( -caHdr *mp, -struct client *client -) +LOCAL int host_name_action ( caHdrLargeArray *mp, void *pPayload, + struct client *client ) { struct channel_in_use *pciu; - unsigned size; - char *pName; - char *pMalloc; - int status; + unsigned size; + char *pName; + char *pMalloc; + int status; - pName = (char *)(mp+1); + pName = (char *) pPayload; size = strlen(pName)+1; if (size > 512) { - log_header ("bad (very long) host name", client, mp, 0); + log_header ( "bad (very long) host name", + client, mp, pPayload, 0 ); SEND_LOCK(client); send_err( mp, @@ -755,7 +829,8 @@ struct client *client */ pMalloc = malloc(size); if(!pMalloc){ - log_header ("no space in pool for new host name", client, mp, 0); + log_header ( "no space in pool for new host name", + client, mp, pPayload, 0 ); SEND_LOCK(client); send_err( mp, @@ -782,12 +857,13 @@ struct client *client while(pciu){ status = asChangeClient( pciu->asClientPVT, - asDbGetAsl(&pciu->addr), - client->pUserName, - client->pHostName); + asDbGetAsl ( &pciu->addr ), + client->pUserName ? client->pUserName : 0, + client->pHostName ? client->pHostName : 0 ); if(status != 0 && status != S_asLib_asNotActive){ epicsMutexUnlock(client->addrqLock); - log_header ("unable to install new host name into access security", client, mp, 0); + log_header ("unable to install new host name into access security", + client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, @@ -801,9 +877,8 @@ struct client *client } epicsMutexUnlock(client->addrqLock); - DLOG(2, "CAS: host_name_action for \"%s\"\n", - (int) client->pHostName, - NULL, NULL, NULL, NULL, NULL); + DLOG(2, ( "CAS: host_name_action for \"%s\"\n", + client->pHostName ? client->pHostName : 0 ) ); return RSRV_OK; } @@ -812,21 +887,20 @@ struct client *client /* * client_name_action() */ -LOCAL int client_name_action( -caHdr *mp, -struct client *client -) +LOCAL int client_name_action ( caHdrLargeArray *mp, void *pPayload, + struct client *client ) { struct channel_in_use *pciu; - unsigned size; - char *pName; - char *pMalloc; - int status; + unsigned size; + char *pName; + char *pMalloc; + int status; - pName = (char *)(mp+1); + pName = (char *) pPayload; size = strlen(pName)+1; if (size > 512) { - log_header ("a very long user name was specified", client, mp, 0); + log_header ("a very long user name was specified", + client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, @@ -842,7 +916,8 @@ struct client *client */ pMalloc = malloc(size); if(!pMalloc){ - log_header ("no memory for new user name", client, mp, 0); + log_header ("no memory for new user name", + client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, @@ -861,8 +936,8 @@ struct client *client epicsMutexMustLock(client->addrqLock); pName = client->pUserName; client->pUserName = pMalloc; - if(pName){ - free(pName); + if ( pName ) { + free ( pName ); } pciu = (struct channel_in_use *) client->addrq.node.next; @@ -870,11 +945,12 @@ struct client *client status = asChangeClient( pciu->asClientPVT, asDbGetAsl(&pciu->addr), - client->pUserName, - client->pHostName); + client->pUserName ? client->pUserName : "", + client->pHostName ? client->pHostName : ""); if(status != 0 && status != S_asLib_asNotActive){ epicsMutexUnlock(client->addrqLock); - log_header ("unable to install new user name into access security", client, mp, 0); + log_header ("unable to install new user name into access security", + client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, @@ -888,9 +964,8 @@ struct client *client } epicsMutexUnlock(client->addrqLock); - DLOG (2, "CAS: client_name_action for \"%s\"\n", - (int) client->pUserName, - NULL, NULL, NULL, NULL, NULL); + DLOG (2, ( "CAS: client_name_action for \"%s\"\n", + client->pUserName ? client->pUserName : "" ) ); return RSRV_OK; } @@ -970,43 +1045,42 @@ unsigned cid /* * access_rights_reply() */ -LOCAL void access_rights_reply(struct channel_in_use *pciu) +LOCAL void access_rights_reply ( struct channel_in_use *pciu ) { - struct client *pclient; - caHdr *reply; unsigned ar; - int v41; + int v41; + int success; - pclient = pciu->client; - - assert(pclient != prsrv_cast_client); + assert ( pciu->client != prsrv_cast_client ); /* * noop if this is an old client */ - v41 = CA_V41(CA_PROTOCOL_VERSION,pclient->minor_version_number); - if(!v41){ + v41 = CA_V41 ( pciu->client->minor_version_number ); + if ( ! v41 ){ return; } ar = 0; /* none */ - if(asCheckGet(pciu->asClientPVT)){ + if ( asCheckGet ( pciu->asClientPVT ) ) { ar |= CA_PROTO_ACCESS_RIGHT_READ; } - if(rsrvCheckPut(pciu)){ + if ( rsrvCheckPut ( pciu ) ) { ar |= CA_PROTO_ACCESS_RIGHT_WRITE; } - SEND_LOCK(pclient); - reply = (caHdr *)ALLOC_MSG(pclient, 0); - assert(reply); + SEND_LOCK ( pciu->client ); - *reply = nill_msg; - reply->m_cmmd = CA_PROTO_ACCESS_RIGHTS; - reply->m_cid = pciu->cid; - reply->m_available = ar; - END_MSG(pclient); - SEND_UNLOCK(pclient); + success = cas_copy_in_header ( pciu->client, CA_PROTO_ACCESS_RIGHTS, 0, + 0, 0, pciu->cid, ar, 0 ); + /* + * OK to just ignore the request if the connection drops + */ + if ( ! success ) { + return; + } + cas_commit_msg ( pciu->client, 0u ); + SEND_UNLOCK ( pciu->client ); } /* @@ -1069,14 +1143,12 @@ LOCAL void casAccessRightsCB(ASCLIENTPVT ascpvt, asClientStatus type) /* * claim_ciu_action() */ -LOCAL int claim_ciu_action( -caHdr *mp, -struct client *client -) +LOCAL int claim_ciu_action ( caHdrLargeArray *mp, + void *pPayload, client *client ) { - int v42; - int status; - struct channel_in_use *pciu; + int v42; + int status; + struct channel_in_use *pciu; /* * The available field is used (abused) @@ -1086,15 +1158,16 @@ struct client *client */ client->minor_version_number = mp->m_available; - if (CA_V44(CA_PROTOCOL_VERSION,client->minor_version_number)) { + if (CA_V44(client->minor_version_number)) { struct dbAddr tmp_addr; - char *pName = (char *)(mp+1); + char *pName = (char *) pPayload; /* * check the sanity of the message */ if (mp->m_postsize<=1) { - log_header ("empty PV name in UDP search request?", client, mp, 0); + log_header ( "empty PV name in UDP search request?", + client, mp, pPayload, 0 ); return RSRV_OK; } pName[mp->m_postsize-1] = '\0'; @@ -1104,18 +1177,16 @@ struct client *client return RSRV_OK; } - DLOG(2,"CAS: claim_ciu_action found '%s', type %d, count %d\n", - (int) pName, - tmp_addr.dbr_field_type, - tmp_addr.no_elements, - NULL, NULL, NULL); + DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %d\n", + pName, tmp_addr.dbr_field_type, tmp_addr.no_elements) ); pciu = casCreateChannel ( client, &tmp_addr, mp->m_cid); if (!pciu) { - log_header ("no memory to create new channel", client, mp, 0); + log_header ("no memory to create new channel", + client, mp, pPayload, 0); SEND_LOCK(client); send_err(mp, ECA_ALLOCMEM, @@ -1189,10 +1260,11 @@ struct client *client &pciu->asClientPVT, asDbGetMemberPvt(&pciu->addr), asDbGetAsl(&pciu->addr), - client->pUserName, - client->pHostName); + client->pUserName ? client->pUserName : "", + client->pHostName ? client->pHostName : ""); if(status != 0 && status != S_asLib_asNotActive){ - log_header ("No room for security table", client, mp, 0); + log_header ("No room for security table", + client, mp, pPayload, 0); SEND_LOCK(client); send_err(mp, ECA_ALLOCMEM, client, "No room for security table"); SEND_UNLOCK(client); @@ -1205,15 +1277,10 @@ struct client *client */ asPutClientPvt(pciu->asClientPVT, pciu); - v42 = CA_V42( - CA_PROTOCOL_VERSION, - client->minor_version_number); + v42 = CA_V42(client->minor_version_number); /* * register for asynch updates of access rights changes - * (only after the lock is released, we are added to - * the correct client, and the clients version is - * known) */ status = asRegisterClientCallback( pciu->asClientPVT, @@ -1225,7 +1292,8 @@ struct client *client access_rights_reply(pciu); } else if (status!=0) { - log_header ("No room for access security state change subscription", client, mp, 0); + log_header ("No room for access security state change subscription", + client, mp, pPayload, 0); SEND_LOCK(client); send_err(mp, ECA_ALLOCMEM, client, "No room for access security state change subscription"); @@ -1234,39 +1302,39 @@ struct client *client } if(v42){ - caHdr *claim_reply; + ca_uint32_t nElem; + int success; - SEND_LOCK(client); - claim_reply = (caHdr *) ALLOC_MSG(client, 0); - assert (claim_reply); + SEND_LOCK ( client ); - *claim_reply = nill_msg; - claim_reply->m_cmmd = CA_PROTO_CLAIM_CIU; - claim_reply->m_dataType = pciu->addr.dbr_field_type; - if (pciu->addr.no_elements<0) { - claim_reply->m_count = 0; - } - else if (pciu->addr.no_elements>0xffff) { - claim_reply->m_count = 0xffff; + if ( pciu->addr.no_elements < 0 ) { + nElem = 0; } else { - claim_reply->m_count = (ca_uint16_t) pciu->addr.no_elements; + if ( ! CA_V49 ( client->minor_version_number ) ) { + if ( pciu->addr.no_elements >= 0xffff ) { + nElem = 0xfffe; + } + else { + nElem = (ca_uint32_t) pciu->addr.no_elements; + } + } + else { + nElem = (ca_uint32_t) pciu->addr.no_elements; + } + } + success = cas_copy_in_header ( + client, CA_PROTO_CLAIM_CIU, 0u, + pciu->addr.dbr_field_type, nElem, pciu->cid, + pciu->sid, NULL ); + if ( success ) { + cas_commit_msg ( client, 0u ); } - claim_reply->m_cid = pciu->cid; - claim_reply->m_available = pciu->sid; - - DBLOCK(3, - printf ("claim_cui reply:\n"); - log_header (NULL, client, claim_reply, 0); - ) - - END_MSG(client); SEND_UNLOCK(client); } return RSRV_OK; } - /* * write_notify_call_back() * @@ -1320,12 +1388,14 @@ void write_notify_reply(void *pArg) { RSRVPUTNOTIFY *ppnb; struct client *pClient; - caHdr *preply; pClient = pArg; SEND_LOCK(pClient); while(TRUE){ + ca_uint32_t status; + int success; + /* * independent lock used here in order to * avoid any possibility of blocking @@ -1342,19 +1412,6 @@ void write_notify_reply(void *pArg) break; } - /* - * aquire sufficient output buffer - */ - preply = ALLOC_MSG(pClient, 0); - if (!preply) { - /* - * inability to aquire buffer space - * Indicates corruption - */ - errlogPrintf("CA server corrupted - put call back(s) discarded\n"); - break; - } - *preply = ppnb->msg; /* * * Map from DB status to CA status @@ -1364,76 +1421,84 @@ void write_notify_reply(void *pArg) */ if(ppnb->dbPutNotify.status){ if(ppnb->dbPutNotify.status == S_db_Blocked){ - preply->m_cid = ECA_PUTCBINPROG; + status = ECA_PUTCBINPROG; } else{ - preply->m_cid = ECA_PUTFAIL; + status = ECA_PUTFAIL; } } else{ - preply->m_cid = ECA_NORMAL; + status = ECA_NORMAL; + } + success = cas_copy_in_header ( pClient, CA_PROTO_WRITE_NOTIFY, + 0u, ppnb->msg.m_dataType, ppnb->msg.m_count, status, + ppnb->msg.m_available, 0 ); + if ( ! success ) { + /* + * inability to aquire buffer space + * Indicates corruption + */ + errlogPrintf("CA server corrupted - put call back(s) discarded\n"); + break; } /* commit the message */ - END_MSG(pClient); + cas_commit_msg ( pClient, 0u ); ppnb->busy = FALSE; } - cas_send_msg(pClient,FALSE); + cas_send_msg ( pClient, FALSE ); - SEND_UNLOCK(pClient); + SEND_UNLOCK ( pClient ); /* * wakeup the TCP thread if it is waiting for a cb to complete */ - epicsEventSignal(pClient->blockSem); + epicsEventSignal ( pClient->blockSem ); } /* * putNotifyErrorReply */ -LOCAL void putNotifyErrorReply (struct client *client, caHdr *mp, int statusCA) +LOCAL void putNotifyErrorReply ( struct client *client, caHdrLargeArray *mp, int statusCA ) { - caHdr *preply; + int success; - SEND_LOCK (client); - preply = ALLOC_MSG (client, 0); - if (!preply) { - errlogPrintf ("%s at %d: should always get sufficent space for put notify error reply\n", - __FILE__, __LINE__); - return; - } - - *preply = *mp; + SEND_LOCK ( client ); /* * the cid field abused to contain status * during put cb replies */ - preply->m_cid = statusCA; - END_MSG(client); - SEND_UNLOCK(client); + success = cas_copy_in_header ( client, CA_PROTO_WRITE_NOTIFY, + 0u, mp->m_dataType, mp->m_count, statusCA, + mp->m_available, 0 ); + if ( ! success ) { + errlogPrintf ("%s at %d: should always get sufficent space for put notify error reply\n", + __FILE__, __LINE__); + return; + } + cas_commit_msg ( client, 0u ); + SEND_UNLOCK ( client ); } /* * write_notify_action() */ -LOCAL int write_notify_action( -caHdr *mp, -struct client *client -) +LOCAL int write_notify_action ( caHdrLargeArray *mp, const void *pPayload, + struct client *client ) { - unsigned long size; - int status; - struct channel_in_use *pciu; + unsigned size; + int status; + struct channel_in_use *pciu; pciu = MPTOPCIU(mp); if(!pciu){ - logBadId(client, mp); + logBadId ( client, mp, pPayload ); return RSRV_ERROR; } if (mp->m_dataType > LAST_BUFFER_TYPE) { - log_header ("bad put notify data type", client, mp, 0); + log_header ("bad put notify data type", client, mp, pPayload, 0); putNotifyErrorReply (client, mp, ECA_BADTYPE); return RSRV_ERROR; } @@ -1453,7 +1518,8 @@ struct client *client while(pciu->pPutNotify->busy){ status = epicsEventWaitWithTimeout(client->blockSem,60.0); if(status != epicsEventWaitOK && pciu->pPutNotify->busy){ - log_header("put call back time out", client, mp,0); + log_header("put call back time out", client, + mp, pPayload, 0); dbNotifyCancel(&pciu->pPutNotify->dbPutNotify); pciu->pPutNotify->busy = FALSE; putNotifyErrorReply (client, mp, ECA_PUTCBINPROG); @@ -1479,7 +1545,8 @@ struct client *client pciu->pPutNotify = (RSRVPUTNOTIFY *) casCalloc(1, sizeof(*pciu->pPutNotify)+size); if(!pciu->pPutNotify){ - log_header ("no memory to initiate put notify", client, mp, 0); + log_header ( "no memory to initiate put notify", + client, mp, pPayload, 0 ); putNotifyErrorReply (client, mp, ECA_ALLOCMEM); return RSRV_ERROR; } @@ -1496,12 +1563,12 @@ struct client *client #ifdef CONVERSION_REQUIRED /* use type as index into conversion jumptable */ (* cac_dbr_cvrt[mp->m_dataType]) - ( mp + 1, + ( pPayload, pciu->pPutNotify->dbPutNotify.pbuffer, FALSE, /* net -> host format */ mp->m_count); #else - memcpy(pciu->pPutNotify->dbPutNotify.pbuffer, (void *)(mp+1), size); + memcpy(pciu->pPutNotify->dbPutNotify.pbuffer, pPayLoad, size); #endif status = dbPutNotifyMapType(&pciu->pPutNotify->dbPutNotify, mp->m_dataType); if(status){ @@ -1528,21 +1595,22 @@ struct client *client * event_add_action() * */ -LOCAL int event_add_action (caHdr *mp, struct client *client) +LOCAL int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client *client) { - struct monops *pmo = (struct monops *) mp; + struct mon_info *pmi = (struct mon_info *) pPayload; struct channel_in_use *pciu; struct event_ext *pevext; - pciu = MPTOPCIU(mp); - if(!pciu){ - logBadId(client, mp); + pciu = MPTOPCIU ( mp ); + if ( ! pciu ) { + logBadId ( client, mp, pPayload ); return RSRV_ERROR; } pevext = (struct event_ext *) freeListCalloc (rsrvEventFreeList); if (!pevext) { - log_header ("no memory to add subscription", client, mp, 0); + log_header ("no memory to add subscription", + client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, @@ -1553,22 +1621,11 @@ LOCAL int event_add_action (caHdr *mp, struct client *client) return RSRV_ERROR; } -#ifdef CONVERSION_REQUIRED - - /* I convert here is the full message that we get, - * though only m_mask seems to be used - */ - dbr_ntohf (&pmo->m_info.m_lval , &pmo->m_info.m_lval); - dbr_ntohf (&pmo->m_info.m_hval , &pmo->m_info.m_hval); - dbr_ntohf (&pmo->m_info.m_toval, &pmo->m_info.m_toval); - pmo->m_info.m_mask = ntohs (pmo->m_info.m_mask); -#endif - pevext->msg = *mp; pevext->pciu = pciu; pevext->send_lock = TRUE; pevext->size = dbr_size_n(mp->m_dataType, mp->m_count); - pevext->mask = pmo->m_info.m_mask; + pevext->mask = ntohs ( pmi->m_mask ); epicsMutexMustLock(client->eventqLock); ellAdd( &pciu->eventq, &pevext->node); @@ -1577,7 +1634,8 @@ LOCAL int event_add_action (caHdr *mp, struct client *client) pevext->pdbev = db_add_event (client->evuser, &pciu->addr, read_reply, pevext, pevext->mask); if (pevext->pdbev == NULL) { - log_header ("no memory to add subscription to db", client, mp, 0); + log_header ("no memory to add subscription to db", + client, mp, pPayload, 0); SEND_LOCK(client); send_err (mp, ECA_ADDFAIL, client, RECORD_NAME(&pciu->addr)); SEND_UNLOCK(client); @@ -1610,8 +1668,8 @@ LOCAL int event_add_action (caHdr *mp, struct client *client) * messages sent by the server). */ - DLOG(3, "event_add_action: db_post_single_event (0x%X)\n", - (int) pevext->pdbev, 0, 0, 0, 0, 0); + DLOG ( 3, ("event_add_action: db_post_single_event (0x%X)\n", + pevext->pdbev) ); db_post_single_event(pevext->pdbev); /* @@ -1619,42 +1677,36 @@ LOCAL int event_add_action (caHdr *mp, struct client *client) */ if(!asCheckGet(pciu->asClientPVT)){ db_event_disable(pevext->pdbev); - DLOG(3, "Disable event because cannot read\n", - 0, 0, 0, 0, 0, 0); + DLOG ( 3, ( "Disable event because cannot read\n" ) ); } return RSRV_OK; } - - /* * clear_channel_reply() */ - LOCAL int clear_channel_reply( - caHdr *mp, - struct client *client - ) - { - caHdr *reply; +LOCAL int clear_channel_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client ) +{ struct event_ext *pevext; struct channel_in_use *pciu; int status; + int success; /* - * - * Verify the channel - * - */ + * + * Verify the channel + * + */ pciu = MPTOPCIU(mp); if(pciu?pciu->client!=client:TRUE){ - logBadId(client, mp); + logBadId ( client, mp, pPayload ); return RSRV_ERROR; } /* - * if a put notify is outstanding then cancel it - */ + * if a put notify is outstanding then cancel it + */ if(pciu->pPutNotify){ if(pciu->pPutNotify->busy){ dbNotifyCancel(&pciu->pPutNotify->dbPutNotify); @@ -1693,14 +1745,15 @@ LOCAL int event_add_action (caHdr *mp, struct client *client) * send delete confirmed message */ SEND_LOCK(client); - reply = (caHdr *) ALLOC_MSG(client, 0); - if (!reply) { + success = cas_copy_in_header ( client, CA_PROTO_CLEAR_CHANNEL, + 0u, mp->m_dataType, mp->m_count, mp->m_cid, + mp->m_available, NULL ); + if ( ! success ) { SEND_UNLOCK(client); return RSRV_ERROR; } - *reply = *mp; - END_MSG(client); + cas_commit_msg ( client, 0u ); SEND_UNLOCK(client); epicsMutexMustLock(client->addrqLock); @@ -1720,7 +1773,7 @@ LOCAL int event_add_action (caHdr *mp, struct client *client) status = bucketRemoveItemUnsignedId (pCaBucket, &pciu->sid); if(status != S_bucket_success){ errMessage (status, "Bad resource id during channel clear"); - logBadId(client, mp); + logBadId ( client, mp, pPayload ); } UNLOCK_CLIENTQ; freeListFree(rsrvChanFreeList, pciu); @@ -1728,8 +1781,6 @@ LOCAL int event_add_action (caHdr *mp, struct client *client) return RSRV_OK; } - - /* * * event_cancel_reply() @@ -1738,11 +1789,11 @@ LOCAL int event_add_action (caHdr *mp, struct client *client) * Much more efficient now since the event blocks hang off the channel in use * blocks not all together off the client block. */ -LOCAL int event_cancel_reply (caHdr *mp, struct client *client) +LOCAL int event_cancel_reply ( caHdrLargeArray *mp, const void *pPayload, struct client *client ) { struct channel_in_use *pciu; - caHdr *reply; struct event_ext *pevext; + int success; /* * @@ -1751,7 +1802,7 @@ LOCAL int event_cancel_reply (caHdr *mp, struct client *client) */ pciu = MPTOPCIU(mp); if (pciu?pciu->client!=client:TRUE) { - logBadId(client, mp); + logBadId ( client, mp, pPayload ); return RSRV_ERROR; } @@ -1791,15 +1842,15 @@ LOCAL int event_cancel_reply (caHdr *mp, struct client *client) * send delete confirmed message */ SEND_LOCK(client); - reply = (caHdr *) ALLOC_MSG(client, 0); - if (!reply) { + + success = cas_copy_in_header ( client, pevext->msg.m_cmmd, + 0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid, + pevext->msg.m_available, NULL ); + if ( ! success ) { SEND_UNLOCK(client); return RSRV_ERROR; } - *reply = pevext->msg; - reply->m_postsize = 0; - - END_MSG(client); + cas_commit_msg ( client, 0 ); SEND_UNLOCK(client); freeListFree (rsrvEventFreeList, pevext); @@ -1810,26 +1861,19 @@ LOCAL int event_cancel_reply (caHdr *mp, struct client *client) /* * read_sync_reply() */ -LOCAL int read_sync_reply( -caHdr *mp, -struct client *client -) +LOCAL int read_sync_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { - caHdr *reply; - + int success; SEND_LOCK(client); - reply = (caHdr *) ALLOC_MSG(client, 0); - if (!reply) { + success = cas_copy_in_header ( client, mp->m_cmmd, + 0u, mp->m_dataType, mp->m_count, mp->m_cid, + mp->m_available, NULL ); + if ( ! success ) { SEND_UNLOCK(client); return RSRV_ERROR; } - - *reply = *mp; - - END_MSG(client); - + cas_commit_msg ( client, 0 ); SEND_UNLOCK(client); - return RSRV_OK; } @@ -1839,38 +1883,38 @@ struct client *client * Only when requested by the client * send search failed reply */ -LOCAL void search_fail_reply (caHdr *mp, struct client *client) +LOCAL void search_fail_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client) { - caHdr *reply; - - SEND_LOCK(client); - reply = (caHdr *) ALLOC_MSG(client, 0); - if (!reply) { - errlogPrintf ("%s at %d: should always get sufficent space for search fail reply\n", - __FILE__, __LINE__); + int success; + SEND_LOCK ( client ); + success = cas_copy_in_header ( client, CA_PROTO_NOT_FOUND, + 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); + if ( ! success ) { + errlogPrintf ( "%s at %d: should always get sufficent space for search fail reply?\n", + __FILE__, __LINE__ ); return; } - *reply = *mp; - reply->m_cmmd = CA_PROTO_NOT_FOUND; - reply->m_postsize = 0; - - END_MSG(client); - SEND_UNLOCK(client); + cas_commit_msg ( client, 0 ); + SEND_UNLOCK ( client ); +} +/* + * udp_noop_action() + */ +LOCAL int udp_noop_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) +{ + return RSRV_OK; } /* * search_reply() */ -LOCAL int search_reply( - caHdr *mp, - struct client *client - ) +LOCAL int search_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { struct dbAddr tmp_addr; - caHdr *search_reply; - unsigned short *pMinorVersion; - char *pName = (char *)(mp+1); + int success; + ca_uint16_t *pMinorVersion; + char *pName = (char *) pPayload; int status; unsigned sid; ca_uint16_t count; @@ -1881,7 +1925,8 @@ LOCAL int search_reply( * check the sanity of the message */ if (mp->m_postsize<=1) { - log_header ("empty PV name in UDP search request?", client, mp, 0); + log_header ("empty PV name in UDP search request?", + client, mp, pPayload, 0); return RSRV_OK; } pName[mp->m_postsize-1] = '\0'; @@ -1889,15 +1934,9 @@ LOCAL int search_reply( /* Exit quickly if channel not on this node */ status = db_name_to_addr (pName, &tmp_addr); if (status) { - DLOG (2, "CAS: Lookup for channel \"%s\" failed\n", - (int)(mp+1), - NULL, - NULL, - NULL, - NULL, - NULL); + DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) ); if (mp->m_dataType == DOREPLY) - search_fail_reply(mp, client); + search_fail_reply ( mp, pPayload, client ); return RSRV_OK; } @@ -1909,7 +1948,7 @@ LOCAL int search_reply( && freeListItemsAvail (rsrvEventFreeList) > 0; if ( ! casSufficentSpaceInPool && ! spaceAvailOnFreeList ) { SEND_LOCK(client); - send_err (mp, ECA_ALLOCMEM, client, "Server memory exhausted"); + send_err ( mp, ECA_ALLOCMEM, client, "Server memory exhausted" ); SEND_UNLOCK(client); return RSRV_OK; } @@ -1923,7 +1962,7 @@ LOCAL int search_reply( * * m_count, m_cid are already in host format... */ - if (CA_V44(CA_PROTOCOL_VERSION, mp->m_count)) { + if (CA_V44(mp->m_count)) { sid = ~0U; count = 0; type = ca_server_port; @@ -1931,25 +1970,20 @@ LOCAL int search_reply( else { struct channel_in_use *pchannel; - pchannel = casCreateChannel ( - client, - &tmp_addr, - mp->m_cid); + pchannel = casCreateChannel ( client, &tmp_addr, mp->m_cid ); if (!pchannel) { SEND_LOCK(client); - send_err(mp, - ECA_ALLOCMEM, - client, - RECORD_NAME(&tmp_addr)); - SEND_UNLOCK(client); + send_err ( mp, ECA_ALLOCMEM, client, + RECORD_NAME ( &tmp_addr ) ); + SEND_UNLOCK ( client ); return RSRV_OK; } sid = pchannel->sid; - if (tmp_addr.no_elements<0) { + if ( tmp_addr.no_elements < 0 ) { count = 0; } - else if (tmp_addr.no_elements>0xffff) { - count = 0xffff; + else if ( tmp_addr.no_elements > 0xffff ) { + count = 0xfffe; } else { count = (ca_uint16_t) tmp_addr.no_elements; @@ -1957,66 +1991,36 @@ LOCAL int search_reply( type = (ca_uint16_t) tmp_addr.dbr_field_type; } - SEND_LOCK(client); - - search_reply = (caHdr *) - ALLOC_MSG(client, sizeof(*pMinorVersion)); - if (!search_reply) { - SEND_UNLOCK(client); + SEND_LOCK ( client ); + success = cas_copy_in_header ( client, CA_PROTO_SEARCH, + sizeof(*pMinorVersion), type, count, + sid, mp->m_available, &pMinorVersion ); + if ( ! success ) { + SEND_UNLOCK ( client ); return RSRV_ERROR; } - *search_reply = *mp; - search_reply->m_postsize = sizeof(*pMinorVersion); - - /* this field for rmt machines where paddr invalid */ - search_reply->m_dataType = type; - search_reply->m_count = count; - search_reply->m_cid = sid; - /* * Starting with CA V4.1 the minor version number * is appended to the end of each search reply. * This value is ignored by earlier clients. */ - pMinorVersion = (unsigned short *)(search_reply+1); - *pMinorVersion = htons(CA_MINOR_VERSION); + *pMinorVersion = htons ( CA_MINOR_PROTOCOL_REVISION ); - END_MSG(client); - SEND_UNLOCK(client); + cas_commit_msg ( client, sizeof ( *pMinorVersion ) ); + SEND_UNLOCK ( client ); return RSRV_OK; } -/* - * cac_send_heartbeat() - * **** lock must be applied while in this routine **** - */ -void cas_send_heartbeat (struct client *pc) -{ - caHdr *reply; - - reply = (caHdr *) ALLOC_MSG(pc, 0); - if (!reply) { - return; - } - - *reply = nill_msg; - reply->m_cmmd = CA_PROTO_NOOP; - - END_MSG(pc); - - return; -} - -typedef int (*pProtoStub) (caHdr *mp, struct client *client); +typedef int (*pProtoStubTCP) (caHdrLargeArray *mp, void *pPayload, struct client *client); /* * TCP protocol jump table */ -LOCAL const pProtoStub tcpJumpTable[] = +LOCAL const pProtoStubTCP tcpJumpTable[] = { - noop_action, + tcp_noop_action, event_add_action, event_cancel_reply, read_action, @@ -2031,7 +2035,7 @@ LOCAL const pProtoStub tcpJumpTable[] = clear_channel_reply, bad_tcp_cmd_action, bad_tcp_cmd_action, - read_action, + read_notify_action, bad_tcp_cmd_action, bad_tcp_cmd_action, claim_ciu_action, @@ -2049,9 +2053,10 @@ LOCAL const pProtoStub tcpJumpTable[] = /* * UDP protocol jump table */ -LOCAL const pProtoStub udpJumpTable[] = +typedef int (*pProtoStubUDP) (caHdrLargeArray *mp, void *pPayload, struct client *client); +LOCAL const pProtoStubUDP udpJumpTable[] = { - noop_action, + udp_noop_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, @@ -2074,7 +2079,7 @@ LOCAL const pProtoStub udpJumpTable[] = bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, - echo_action, + udp_echo_action, bad_udp_cmd_action, bad_udp_cmd_action, bad_udp_cmd_action, @@ -2085,94 +2090,125 @@ LOCAL const pProtoStub udpJumpTable[] = /* * CAMESSAGE() */ -int camessage (struct client *client, struct message_buffer *recv) +int camessage ( struct client *client ) { - ca_uint16_t tmp_postsize; - unsigned nmsg = 0; - unsigned long msgsize; - unsigned long bytes_left; - int status; - caHdr *mp; + unsigned nmsg = 0; + unsigned msgsize; + unsigned bytes_left; + int status; - if(!pCaBucket){ + if ( ! pCaBucket ) { pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE); if(!pCaBucket){ return RSRV_ERROR; } } + + /* drain remnents of large messages that will not fit */ + if ( client->recvBytesToDrain ) { + if ( client->recvBytesToDrain >= client->recv.cnt ) { + client->recvBytesToDrain -= client->recv.cnt; + client->recv.stk = client->recv.cnt; + return RSRV_OK; + } + else { + client->recv.stk += client->recvBytesToDrain; + client->recvBytesToDrain = 0u; + } + } - DLOG (2, "CAS: Parsing %d(decimal) bytes\n", - recv->cnt, NULL, NULL, NULL, NULL, NULL); + DLOG ( 2, ( "CAS: Parsing %d(decimal) bytes\n", recv->cnt ) ); - bytes_left = recv->cnt; - while (bytes_left) - { - - /* assert that we have at least a complete caHdr */ - if(bytes_left < sizeof(*mp)) + bytes_left = client->recv.cnt; + while ( bytes_left ) + { + caHdrLargeArray msg; + caHdr *mp; + void *pBody; + + /* wait for at least a complete caHdr */ + if ( bytes_left < sizeof(*mp) ) return RSRV_OK; - mp = (caHdr *) &recv->buf[recv->stk]; - + mp = (caHdr *) &client->recv.buf[client->recv.stk]; + msg.m_cmmd = ntohs ( mp->m_cmmd ); + msg.m_postsize = ntohs ( mp->m_postsize ); + msg.m_dataType = ntohs ( mp->m_dataType ); + msg.m_count = ntohs ( mp->m_count ); + msg.m_cid = ntohl ( mp->m_cid ); + msg.m_available = ntohl ( mp->m_available ); + + if ( CA_V49(client->minor_version_number) && msg.m_postsize == 0xffff ) { + ca_uint32_t *pLW = ( ca_uint32_t * ) ( mp + 1 ); + if ( bytes_left < sizeof(*mp) + 2 * sizeof(*pLW) ) + return RSRV_OK; + msg.m_postsize = ntohl ( pLW[0] ); + msg.m_count = ntohl ( pLW[1] ); + msgsize = msg.m_postsize + sizeof(*mp) + 2 * sizeof ( *pLW ); + pBody = ( void * ) ( pLW + 2 ); + } + else { + msgsize = msg.m_postsize + sizeof(*mp); + pBody = ( void * ) ( mp + 1 ); + } + /* problem: we have a complete header, * but before we check msgsize we don't know * if we have a complete message body * -> we may be called again with the same header * after receiving the full message */ - tmp_postsize = ntohs (mp->m_postsize); - msgsize = tmp_postsize + sizeof(*mp); - - if (msgsize > bytes_left) { - if ( msgsize > sizeof(client->recv.buf) ) { + if ( msgsize > client->recv.maxstk ) { + casExpandRecvBuffer ( client, msgsize ); + if ( msgsize > client->recv.maxstk ) { const char *pCtx = "rsrv: CA request message too large"; - send_err ( mp, ECA_TOLARGE, client, pCtx ); - log_header ( pCtx , client, mp, nmsg ); - return RSRV_ERROR; + send_err ( &msg, ECA_TOLARGE, client, pCtx ); + log_header ( pCtx , client, &msg, 0, nmsg ); + assert ( client->recv.cnt <= client->recv.maxstk ); + assert ( msgsize >= bytes_left ); + client->recvBytesToDrain = msgsize - bytes_left; + client->recv.stk = client->recv.cnt; + return RSRV_OK; } + } + + /* + * wait for complete message body + */ + if ( msgsize > bytes_left ) { return RSRV_OK; } - - /* Have complete message (header + content) - * -> convert the header elements - */ - mp->m_cmmd = ntohs (mp->m_cmmd); - mp->m_postsize = tmp_postsize; - mp->m_dataType = ntohs (mp->m_dataType); - mp->m_count = ntohs (mp->m_count); - mp->m_cid = ntohl (mp->m_cid); - mp->m_available = ntohl (mp->m_available); - + nmsg++; - if (CASDEBUG > 2) - log_header (NULL, client, mp, nmsg); + if ( CASDEBUG > 2 ) + log_header (NULL, client, &msg, pBody, nmsg); - if (client==prsrv_cast_client) { - if (mp->m_cmmdm_cmmd])(mp, client); + if ( client == prsrv_cast_client ) { + if ( msg.m_cmmd < NELEMENTS ( udpJumpTable ) ) { + status = ( *udpJumpTable[msg.m_cmmd] )( &msg, pBody, client ); if (status!=RSRV_OK) { return RSRV_ERROR; } } else { - return bad_udp_cmd_action (mp, client); + return bad_udp_cmd_action ( &msg, pBody, client ); } } else { - if (mp->m_cmmdm_cmmd])(mp, client); - if (status!=RSRV_OK) { + if ( msg.m_cmmd < NELEMENTS(tcpJumpTable) ) { + status = ( *tcpJumpTable[msg.m_cmmd] ) ( &msg, pBody, client ); + if ( status != RSRV_OK ) { return RSRV_ERROR; } } else { - return bad_tcp_cmd_action (mp, client); + return bad_tcp_cmd_action ( &msg, pBody, client ); } } - recv->stk += msgsize; - bytes_left = recv->cnt - recv->stk; + client->recv.stk += msgsize; + bytes_left = client->recv.cnt - client->recv.stk; } return RSRV_OK; diff --git a/src/rsrv/camsgtask.c b/src/rsrv/camsgtask.c index 1d0bcb06b..2341436f1 100644 --- a/src/rsrv/camsgtask.c +++ b/src/rsrv/camsgtask.c @@ -48,24 +48,23 @@ * * CA server TCP client task (one spawned for each client) */ -void camsgtask (struct client *client) +void camsgtask ( struct client *client ) { - int nchars; - int status; + int nchars; + int status; - client->tid = epicsThreadGetIdSelf (); + casAttachThreadToClient ( client ); - taskwdInsert (epicsThreadGetIdSelf(), NULL, NULL); - - while (TRUE) { + while ( TRUE ) { client->recv.stk = 0; - nchars = recv (client->sock, &client->recv.buf[client->recv.cnt], - (int)(sizeof(client->recv.buf)-client->recv.cnt), 0); - if (nchars==0){ - if (CASDEBUG>0) { + assert ( client->recv.maxstk >= client->recv.cnt ); + nchars = recv ( client->sock, &client->recv.buf[client->recv.cnt], + (int) ( client->recv.maxstk - client->recv.cnt ), 0 ); + if ( nchars == 0 ){ + if ( CASDEBUG > 0 ) { errlogPrintf ( "CAS: nill message disconnect ( %u bytes request )\n", - sizeof (client->recv.buf) - client->recv.cnt ); + sizeof ( client->recv.buf ) - client->recv.cnt ); } break; } @@ -75,19 +74,19 @@ void camsgtask (struct client *client) /* * normal conn lost conditions */ - if ( (anerrno!=SOCK_ECONNABORTED&& - anerrno!=SOCK_ECONNRESET&& - anerrno!=SOCK_ETIMEDOUT)|| - CASDEBUG>2) { - errlogPrintf ("CAS: client disconnect(errno=%d)\n", anerrno); + if ( ( anerrno != SOCK_ECONNABORTED && + anerrno != SOCK_ECONNRESET && + anerrno != SOCK_ETIMEDOUT ) || + CASDEBUG > 2 ) { + errlogPrintf ( "CAS: client disconnect(errno=%d)\n", anerrno ); } break; } - epicsTimeGetCurrent (&client->time_at_last_recv); - client->recv.cnt += (unsigned long) nchars; + epicsTimeGetCurrent ( &client->time_at_last_recv ); + client->recv.cnt += ( unsigned ) nchars; - status = camessage (client, &client->recv); + status = camessage ( client ); if (status == 0) { /* * if there is a partial message @@ -138,6 +137,10 @@ void camsgtask (struct client *client) cas_send_msg(client, TRUE); } } - - destroy_client (client); + + LOCK_CLIENTQ; + ellDelete ( &clientQ, &client->node ); + UNLOCK_CLIENTQ; + + destroy_tcp_client ( client ); } diff --git a/src/rsrv/caserverio.c b/src/rsrv/caserverio.c index f833d9f05..291ba11ca 100644 --- a/src/rsrv/caserverio.c +++ b/src/rsrv/caserverio.c @@ -37,8 +37,10 @@ #include "osiSock.h" #include "epicsTime.h" #include "errlog.h" -#include "net_convert.h" +typedef unsigned long arrayElementCount; + +#include "net_convert.h" #include "server.h" /* @@ -46,65 +48,27 @@ * * (channel access server send message) */ -void cas_send_msg (struct client *pclient, int lock_needed) +void cas_send_msg ( struct client *pclient, int lock_needed ) { int status; - if (CASDEBUG>2 && pclient->send.stk) { - errlogPrintf ("CAS: Sending a message of %d bytes\n", pclient->send.stk); + if ( CASDEBUG > 2 && pclient->send.stk ) { + errlogPrintf ( "CAS: Sending a message of %d bytes\n", pclient->send.stk ); } - if (pclient->disconnect) { - if (CASDEBUG>2) { - errlogPrintf ("CAS: msg Discard for sock %d addr %x\n", - pclient->sock, pclient->addr.sin_addr.s_addr); + if ( pclient->disconnect ) { + if ( CASDEBUG > 2 ) { + errlogPrintf ( "CAS: msg Discard for sock %d addr %x\n", + pclient->sock, pclient->addr.sin_addr.s_addr ); } return; } - if(lock_needed){ - SEND_LOCK(pclient); + if ( lock_needed ) { + SEND_LOCK ( pclient ); } - if (pclient->send.stk) { -#ifdef CONVERSION_REQUIRED - /* Convert all caHdr into net format. - * The remaining bytes must already be in - * net format, because here we have no clue - * how to convert them. - */ - char *buf; - unsigned long msg_size, num_bytes; - caHdr *mp; - - - buf = (char *) pclient->send.buf; - num_bytes = pclient->send.stk; - - /* convert only if we have at least a complete caHdr */ - while (num_bytes >= sizeof(caHdr)) - { - mp = (caHdr *) buf; - - msg_size = sizeof (caHdr) + mp->m_postsize; - - DLOG(3,"CAS: sending cmmd %d, postsize %d\n", - mp->m_cmmd, (int)mp->m_postsize, - 0, 0, 0, 0); - - /* convert the complete header into host format */ - mp->m_cmmd = htons (mp->m_cmmd); - mp->m_postsize = htons (mp->m_postsize); - mp->m_dataType = htons (mp->m_dataType); - mp->m_count = htons (mp->m_count); - mp->m_cid = htonl (mp->m_cid); - mp->m_available = htonl (mp->m_available); - - /* get next message: */ - buf += msg_size; - num_bytes -= msg_size; - } -#endif + if ( pclient->send.stk ) { status = sendto (pclient->sock, pclient->send.buf, pclient->send.stk, 0, (struct sockaddr *)&pclient->addr, sizeof(pclient->addr)); @@ -156,39 +120,52 @@ void cas_send_msg (struct client *pclient, int lock_needed) SEND_UNLOCK(pclient); } - DLOG(3, "------------------------------\n\n", 0,0,0,0,0,0); + DLOG ( 3, ( "------------------------------\n\n" ) ); return; } /* * - * cas_alloc_msg() + * cas_copy_in_header() * - * see also ALLOC_MSG()/END_MSG() in server.h - * - * (allocate space in the outgoing message buffer) + * Allocate space in the outgoing message buffer and + * copy in message header. Return pointer to message body. * * send lock must be on while in this routine * - * returns 1) a valid ptr to msg buffer space - * 2) NULL (msg will not fit) + * Returns a valid ptr to message body or NULL if the msg + * will not fit. */ -caHdr *cas_alloc_msg (struct client *pclient, unsigned extsize) +int cas_copy_in_header ( + struct client *pclient, ca_uint16_t response, ca_uint32_t payloadSize, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint32_t responseSpecific, void **ppPayload ) { - unsigned msgsize; + unsigned msgSize; - extsize = CA_MESSAGE_ALIGN(extsize); - - if ( extsize > UINT_MAX - sizeof(caHdr) ) { - return NULL; - } - msgsize = extsize + sizeof(caHdr); - if ( msgsize > pclient->send.maxstk ) { - return NULL; + if ( payloadSize > UINT_MAX - sizeof ( caHdr ) - 8u ) { + return FALSE; } - if ( pclient->send.stk > pclient->send.maxstk - msgsize ) { + payloadSize = CA_MESSAGE_ALIGN ( payloadSize ); + + msgSize = payloadSize + sizeof ( caHdr ); + if ( payloadSize >= 0xffff || nElem >= 0xffff ) { + if ( ! CA_V49 ( pclient->minor_version_number ) ) { + return FALSE; + } + msgSize += 2 * sizeof ( ca_uint32_t ); + } + + if ( msgSize > pclient->send.maxstk ) { + casExpandSendBuffer ( pclient, msgSize ); + if ( msgSize > pclient->send.maxstk ) { + return FALSE; + } + } + + if ( pclient->send.stk > pclient->send.maxstk - msgSize ) { if ( pclient->disconnect ) { pclient->send.stk = 0; } @@ -197,8 +174,95 @@ caHdr *cas_alloc_msg (struct client *pclient, unsigned extsize) } } - /* - * it fits END_MSG will push it on the stack - */ - return (caHdr *) &pclient->send.buf[pclient->send.stk]; + if ( payloadSize < 0xffff && nElem < 0xffff ) { + caHdr *pMsg = ( caHdr * ) &pclient->send.buf[pclient->send.stk]; + pMsg->m_cmmd = htons ( response ); + pMsg->m_postsize = htons ( ( ( ca_uint16_t ) payloadSize ) ); + pMsg->m_dataType = htons ( dataType ); + pMsg->m_count = htons ( ( ( ca_uint16_t ) nElem ) ); + pMsg->m_cid = htonl ( cid ); + pMsg->m_available = htonl ( responseSpecific ); + if ( ppPayload ) { + *ppPayload = ( void * ) ( pMsg + 1 ); + } + } + else { + caHdr *pMsg = ( caHdr * ) &pclient->send.buf[pclient->send.stk]; + ca_uint32_t *pW32 = ( ca_uint32_t * ) ( pMsg + 1 ); + pMsg->m_cmmd = htons ( response ); + pMsg->m_postsize = htons ( 0xffff ); + pMsg->m_dataType = htons ( dataType ); + pMsg->m_count = htons ( 0u ); + pMsg->m_cid = htonl ( cid ); + pMsg->m_available = htonl ( responseSpecific ); + pW32[0] = htonl ( payloadSize ); + pW32[1] = htonl ( nElem ); + if ( ppPayload ) { + *ppPayload = ( void * ) ( pW32 + 2 ); + } + } + + return TRUE; +} + +void cas_set_header_cid ( struct client *pClient, ca_uint32_t cid ) +{ + caHdr *pMsg = ( caHdr * ) &pClient->send.buf[pClient->send.stk]; + pMsg->m_cid = htonl ( cid ); +} + +void cas_commit_msg ( struct client *pClient, ca_uint32_t size ) +{ + caHdr * pMsg = ( caHdr * ) &pClient->send.buf[pClient->send.stk]; + size = CA_MESSAGE_ALIGN ( size ); + if ( pMsg->m_postsize == htons ( 0xffff ) ) { + ca_uint32_t * pLW = ( ca_uint32_t * ) ( pMsg + 1 ); + assert ( size <= ntohl ( *pLW ) ); + pLW[0] = htonl ( size ); + size += sizeof ( caHdr ) + 2 * sizeof ( *pLW ); + } + else { + assert ( size <= ntohs ( pMsg->m_postsize ) ); + pMsg->m_postsize = htons ( (ca_uint16_t) size ); + size += sizeof ( caHdr ); + } + pClient->send.stk += size; +} + +/* + * this assumes that we have already checked to see + * if sufficent bytes are available + */ +ca_uint16_t rsrvGetUInt16 ( struct message_buffer *recv ) +{ + ca_uint16_t tmp; + /* + * this assumes that we have already checked to see + * if sufficent bytes are available + */ + assert ( recv->cnt - recv->stk >= 2u ); + tmp = recv->buf[recv->stk++]; + tmp <<= 8u; + tmp |= recv->buf[recv->stk++]; + return tmp; +} + +/* + * this assumes that we have already checked to see + * if sufficent bytes are available + */ +ca_uint16_t rsrvGetUInt32 ( struct message_buffer *recv ) +{ + ca_uint16_t tmp; + /* + * this assumes that we have already checked to see + * if sufficent bytes are available + */ + assert ( recv->cnt - recv->stk >= 4u ); + tmp = recv->buf[recv->stk++]; + tmp <<= 24u; + tmp |= recv->buf[recv->stk++] << 16u; + tmp |= recv->buf[recv->stk++] << 8u; + tmp |= recv->buf[recv->stk++]; + return tmp; } diff --git a/src/rsrv/caservertask.c b/src/rsrv/caservertask.c index 205215b3f..f6d64d56d 100644 --- a/src/rsrv/caservertask.c +++ b/src/rsrv/caservertask.c @@ -57,213 +57,6 @@ #define DELETE_TASK(NAME)\ if(threadNameToId(NAME)!=0)threadDestroy(threadNameToId(NAME)); -/* - * create_base_client () - */ -struct client *create_base_client () -{ - struct client *client; - - client = freeListMalloc (rsrvClientFreeList); - if (!client) { - epicsPrintf ("CAS: no space in pool for a new client\n"); - return NULL; - } - - /* - * The following inits to zero done instead of a bfill since the send - * and recv buffers are large and don't need initialization. - * - * memset(client, 0, sizeof(*client)); - */ - - client->blockSem = epicsEventCreate(epicsEventEmpty); - if(!client->blockSem){ - freeListFree(rsrvClientFreeList, client); - return NULL; - } - - /* - * user name initially unknown - */ - client->pUserName = malloc(1); - if(!client->pUserName){ - epicsEventDestroy(client->blockSem); - freeListFree(rsrvClientFreeList, client); - return NULL; - } - client->pUserName[0] = '\0'; - - /* - * host name initially unknown - */ - client->pHostName = malloc(1); - if(!client->pHostName){ - epicsEventDestroy(client->blockSem); - free(client->pUserName); - freeListFree(rsrvClientFreeList, client); - return NULL; - } - client->pHostName[0] = '\0'; - - ellInit(&client->addrq); - ellInit(&client->putNotifyQue); - memset((char *)&client->addr, 0, sizeof(client->addr)); - client->tid = 0; - client->sock = INVALID_SOCKET; - client->send.stk = 0ul; - client->send.cnt = 0ul; - client->recv.stk = 0ul; - client->recv.cnt = 0ul; - client->evuser = NULL; - client->disconnect = FALSE; /* for TCP only */ - epicsTimeGetCurrent(&client->time_at_last_send); - epicsTimeGetCurrent(&client->time_at_last_recv); - client->proto = IPPROTO_UDP; - client->minor_version_number = CA_UKN_MINOR_VERSION; - - client->send.maxstk = MAX_UDP_SEND; - - client->lock = epicsMutexMustCreate(); - client->putNotifyLock = epicsMutexMustCreate(); - client->addrqLock = epicsMutexMustCreate(); - client->eventqLock = epicsMutexMustCreate(); - - client->recv.maxstk = MAX_UDP_RECV; - return client; -} - -/* - * create_client () - */ -struct client *create_client (SOCKET sock) -{ - int status; - struct client *client; - int true = TRUE; - osiSocklen_t addrSize; - unsigned priorityOfEvents; - - /* - * see TCP(4P) this seems to make unsolicited single events much - * faster. I take care of queue up as load increases. - */ - status = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, - (char *)&true, sizeof(true)); - if (status < 0) { - errlogPrintf ("CAS: TCP_NODELAY option set failed\n"); - socket_close (sock); - return NULL; - } - - /* - * turn on KEEPALIVE so if the client crashes - * this task will find out and exit - */ - status = setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, - (char *)&true, sizeof(true)); - if (status < 0) { - errlogPrintf ("CAS: SO_KEEPALIVE option set failed\n"); - socket_close (sock); - return NULL; - } - - /* - * some concern that vxWorks will run out of mBuf's - * if this change is made - * - * joh 11-10-98 - */ -#if 0 - /* - * set TCP buffer sizes to be synergistic - * with CA internal buffering - */ - i = MAX_MSG_SIZE; - status = setsockopt (sock, SOL_SOCKET, SO_SNDBUF, (char *)&i, sizeof(i)); - if (status < 0) { - errlogPrintf ("CAS: SO_SNDBUF set failed\n"); - socket_close (sock); - return NULL; - } - i = MAX_MSG_SIZE; - status = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *)&i, sizeof(i)); - if (status < 0) { - errlogPrintf ("CAS: SO_RCVBUF set failed\n"); - socket_close (sock); - return NULL; - } -#endif - - client = (struct client *) create_base_client (); - if (!client) { - errlogPrintf("CAS: client init failed\n"); - socket_close (sock); - return NULL; - } - - client->proto = IPPROTO_TCP; - client->send.maxstk = MAX_TCP; - client->recv.maxstk = MAX_TCP; - client->sock = sock; - - addrSize = sizeof (client->addr); - status = getpeername (sock, (struct sockaddr *)&client->addr, - &addrSize); - if (status < 0) { - epicsPrintf ("CAS: peer address fetch failed\n"); - destroy_client (client); - return NULL; - } - - client->evuser = (struct event_user *) db_init_events(); - if (!client->evuser) { - errlogPrintf ("CAS: unable to init the event facility\n"); - destroy_client (client); - return NULL; - } - - status = db_add_extra_labor_event (client->evuser, write_notify_reply, client); - if (status != DB_EVENT_OK) { - errlogPrintf("CAS: unable to setup the event facility\n"); - destroy_client (client); - return NULL; - } - - { - unsigned priorityOfSelf = epicsThreadGetPrioritySelf (); - epicsThreadBooleanStatus tbs; - - tbs = epicsThreadLowestPriorityLevelAbove ( priorityOfSelf, &priorityOfEvents ); - if ( tbs != epicsThreadBooleanStatusSuccess ) { - priorityOfEvents = priorityOfSelf; - } - } - - status = db_start_events ( client->evuser, "CAS-event", - NULL, NULL, priorityOfEvents ); - if (status != DB_EVENT_OK) { - errlogPrintf("CAS: unable to start the event facility\n"); - destroy_client (client); - return NULL; - } - - client->recv.cnt = 0ul; - - if (CASDEBUG>0) { - char buf[64]; - ipAddrToDottedIP (&client->addr, buf, sizeof(buf)); - errlogPrintf ("CAS: conn req from %s\n", buf); - } - - LOCK_CLIENTQ; - ellAdd (&clientQ, &client->node); - UNLOCK_CLIENTQ; - - return client; -} - - /* * * req_server() @@ -404,18 +197,23 @@ LOCAL int req_server (void) continue; } else { + struct client *pClient; unsigned priorityOfClient; epicsThreadId id; - struct client *pClient; - pClient = create_client (clientSock); - if (!pClient) { + pClient = create_tcp_client ( clientSock ); + if ( ! pClient ) { errlogPrintf ( "CAS: unable to create new client because \"%s\"\n", - strerror (errno) ); - epicsThreadSleep(15.0); + strerror ( errno ) ); + epicsThreadSleep ( 15.0 ); continue; } + LOCK_CLIENTQ; + ellAdd ( &clientQ, &pClient->node ); + UNLOCK_CLIENTQ; + + /* * go up two levels in priority so that the event task is above the * task waiting in accept () @@ -433,10 +231,10 @@ LOCAL int req_server (void) } id = epicsThreadCreate ( "CAS-client", priorityOfClient, - epicsThreadGetStackSize (epicsThreadStackBig), - (EPICSTHREADFUNC)camsgtask, (void *)pClient ); - if (id==0) { - destroy_client ( pClient ); + epicsThreadGetStackSize ( epicsThreadStackBig ), + ( EPICSTHREADFUNC ) camsgtask, pClient ); + if ( id == 0 ) { + destroy_tcp_client ( pClient ); errlogPrintf ( "CAS: task creation for new client failed\n" ); epicsThreadSleep ( 15.0 ); continue; @@ -451,21 +249,50 @@ LOCAL int req_server (void) epicsShareFunc int epicsShareAPI rsrv_init (void) { epicsThreadId tid; + long maxBytesAsALong; + long status; clientQlock = epicsMutexMustCreate(); - ellInit (&clientQ); - freeListInitPvt (&rsrvClientFreeList, sizeof(struct client), 8); - freeListInitPvt (&rsrvChanFreeList, sizeof(struct channel_in_use), 512); - freeListInitPvt (&rsrvEventFreeList, sizeof(struct event_ext), 512); - ellInit (&beaconAddrList); + ellInit ( &clientQ ); + freeListInitPvt ( &rsrvClientFreeList, sizeof(struct client), 8 ); + freeListInitPvt ( &rsrvChanFreeList, sizeof(struct channel_in_use), 512 ); + freeListInitPvt ( &rsrvEventFreeList, sizeof(struct event_ext), 512 ); + freeListInitPvt ( &rsrvSmallBufFreeListTCP, MAX_TCP, 16 ); + + status = envGetLongConfigParam ( &EPICS_CA_MAX_ARRAY_BYTES, &maxBytesAsALong ); + if ( status || maxBytesAsALong < 0 ) { + errlogPrintf ( "cas: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" ); + rsrvSizeofLargeBufTCP = MAX_TCP; + } + else { + /* allow room for the protocol header so that they get the array size they requested */ + static const unsigned headerSize = sizeof ( caHdr ) + 2 * sizeof ( ca_uint32_t ); + ca_uint32_t maxBytes = ( unsigned ) maxBytesAsALong; + if ( maxBytes < 0xffffffff - headerSize ) { + maxBytes += headerSize; + } + else { + maxBytes = 0xffffffff; + } + if ( maxBytes < MAX_TCP ) { + errlogPrintf ( "cas: EPICS_CA_MAX_ARRAY_BYTES was rounded up to %u\n", MAX_TCP ); + rsrvSizeofLargeBufTCP = MAX_TCP; + } + else { + rsrvSizeofLargeBufTCP = maxBytes; + } + } + freeListInitPvt ( &rsrvLargeBufFreeListTCP, rsrvSizeofLargeBufTCP, 1 ); + + ellInit ( &beaconAddrList ); prsrv_cast_client = NULL; pCaBucket = NULL; - tid = epicsThreadCreate ("CAS-TCP", + tid = epicsThreadCreate ( "CAS-TCP", epicsThreadPriorityChannelAccessServer, epicsThreadGetStackSize(epicsThreadStackMedium), - (EPICSTHREADFUNC)req_server,0); + (EPICSTHREADFUNC)req_server, 0); if ( tid == 0 ) { epicsPrintf ( "CAS: unable to start connection request thread\n" ); } @@ -483,9 +310,9 @@ LOCAL void log_one_client (struct client *client, unsigned level) char *pproto; double send_delay; double recv_delay; - unsigned long bytes_reserved; + unsigned bytes_reserved; char *state[] = {"up", "down"}; - epicsTimeStamp current; + epicsTimeStamp current; char clientHostName[256]; ipAddrToDottedIP (&client->addr, clientHostName, sizeof(clientHostName)); @@ -504,12 +331,11 @@ LOCAL void log_one_client (struct client *client, unsigned level) send_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_send); recv_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_recv); - printf( "%s(%s): User=\"%s\", V%d.%u, Channel Count=%d\n", + printf( "%s(%s): User=\"%s\", V%s, Channel Count=%d\n", clientHostName, - client->pHostName, - client->pUserName, - CA_PROTOCOL_VERSION, - client->minor_version_number, + client->pHostName ? client->pHostName : "", + client->pUserName ? client->pUserName : "", + CA_VERSION_STRING ( client->minor_version_number ), ellCount(&client->addrq)); if (level>=1) { printf ("\tTask Id=%p, Protocol=%3s, Socket FD=%d\n", client->tid, @@ -587,11 +413,11 @@ void epicsShareAPI casr (unsigned level) return; } - printf ("Channel Access Server V%d.%d\n", - CA_PROTOCOL_VERSION, CA_MINOR_VERSION); + printf ("Channel Access Server V%s\n", + CA_VERSION_STRING ( CA_MINOR_PROTOCOL_REVISION ) ); LOCK_CLIENTQ - client = (struct client *) ellNext (&clientQ); + client = (struct client *) ellNext ( &clientQ ); if (!client) { printf("No clients connected.\n"); } @@ -636,72 +462,118 @@ void epicsShareAPI casr (unsigned level) /* * destroy_client () */ -void destroy_client (struct client *client) +void destroy_client ( struct client *client ) +{ + if ( ! client ) { + return; + } + + if ( client->tid != 0 ) { + taskwdRemove ( client->tid ); + } + + + if ( client->sock != INVALID_SOCKET ) { + if ( socket_close (client->sock) < 0) { + errlogPrintf( "CAS: Unable to close socket\n" ); + } + } + + if ( client->proto == IPPROTO_TCP ) { + if ( client->send.type == mbtSmallTCP ) { + if ( client->send.buf ) { + freeListFree ( rsrvSmallBufFreeListTCP, client->send.buf ); + } + if ( client->recv.buf ) { + freeListFree ( rsrvSmallBufFreeListTCP, client->recv.buf ); + } + } + else if ( client->send.type == mbtLargeTCP ) { + if ( client->send.buf ) { + freeListFree ( rsrvLargeBufFreeListTCP, client->send.buf ); + } + if ( client->recv.buf ) { + freeListFree ( rsrvLargeBufFreeListTCP, client->recv.buf ); + } + } + else { + errlogPrintf ( "Currupt buffer type code during cleanup?\n" ); + } + } + else if ( client->proto == IPPROTO_UDP ) { + if ( client->send.buf ) { + free ( client->send.buf ); + } + if ( client->recv.buf ) { + free ( client->recv.buf ); + } + } + + epicsMutexDestroy ( client->eventqLock ); + + epicsMutexDestroy ( client->addrqLock ); + + epicsMutexDestroy ( client->putNotifyLock ); + + epicsMutexDestroy ( client->lock ); + + epicsEventDestroy ( client->blockSem ); + + if ( client->pUserName ) { + free ( client->pUserName ); + } + + if ( client->pHostName ) { + free ( client->pHostName ); + } + + freeListFree ( rsrvClientFreeList, client ); +} + +void destroy_tcp_client ( struct client *client ) { struct event_ext *pevext; struct channel_in_use *pciu; int status; - if (!client) { - return; - } - - if (client->proto != IPPROTO_TCP) { - errlogPrintf ("CAS: non TCP client delete ignored\n"); - return; + if ( CASDEBUG > 0 ) { + errlogPrintf ( "CAS: Connection %d Terminated\n", client->sock ); } - LOCK_CLIENTQ; - ellDelete (&clientQ, &client->node); - UNLOCK_CLIENTQ; - - if (CASDEBUG>0) { - errlogPrintf ("CAS: Connection %d Terminated\n", client->sock); + if ( client->evuser ) { + db_event_flow_ctrl_mode_off ( client->evuser ); } - /* - * exit flow control so the event system will - * shutdown correctly - */ - db_event_flow_ctrl_mode_off (client->evuser); - - /* - * Server task deleted first since close() is not reentrant - */ - if ( client->tid != 0 ) { - taskwdRemove (client->tid); - } - - while(TRUE){ - epicsMutexMustLock (client->addrqLock); - pciu = (struct channel_in_use *) ellGet(&client->addrq); - epicsMutexUnlock (client->addrqLock); - if (!pciu) { + while ( TRUE ){ + epicsMutexMustLock ( client->addrqLock ); + pciu = (struct channel_in_use *) ellGet ( &client->addrq ); + epicsMutexUnlock ( client->addrqLock ); + if ( ! pciu ) { break; } /* * put notify in progress needs to be deleted */ - if (pciu->pPutNotify) { - if (pciu->pPutNotify->busy) { - dbNotifyCancel (&pciu->pPutNotify->dbPutNotify); + if ( pciu->pPutNotify ) { + if ( pciu->pPutNotify->busy ) { + dbNotifyCancel ( &pciu->pPutNotify->dbPutNotify ); } } - while (TRUE){ + while ( TRUE ) { /* * AS state change could be using this list */ - epicsMutexMustLock (client->eventqLock); + epicsMutexMustLock ( client->eventqLock ); - pevext = (struct event_ext *) ellGet(&pciu->eventq); - epicsMutexUnlock (client->eventqLock); - if(!pevext){ + pevext = (struct event_ext *) ellGet ( &pciu->eventq ); + epicsMutexUnlock ( client->eventqLock ); + if ( ! pevext ) { break; } - if (pevext->pdbev) { + if ( pevext->pdbev ) { db_cancel_event (pevext->pdbev); } freeListFree (rsrvEventFreeList, pevext); @@ -712,60 +584,242 @@ void destroy_client (struct client *client) free(pciu->pPutNotify); } LOCK_CLIENTQ; - status = bucketRemoveItemUnsignedId ( - pCaBucket, - &pciu->sid); + status = bucketRemoveItemUnsignedId ( pCaBucket, &pciu->sid); UNLOCK_CLIENTQ; - if(status != S_bucket_success){ - errPrintf ( - status, - __FILE__, - __LINE__, - "Bad id=%d at close", - pciu->sid); + if ( status != S_bucket_success ) { + errPrintf ( status, __FILE__, __LINE__, + "Bad id=%d at close", pciu->sid); } status = asRemoveClient(&pciu->asClientPVT); - if(status!=0 && status != S_asLib_asNotActive){ - printf("And the status is %x \n", status); - errPrintf(status, __FILE__, __LINE__, "asRemoveClient"); + if ( status && status != S_asLib_asNotActive ) { + printf ( "bad asRemoveClient() status was %x \n", status ); + errPrintf ( status, __FILE__, __LINE__, "asRemoveClient" ); } - /* - * place per channel block onto the - * free list - */ - freeListFree (rsrvChanFreeList, pciu); + freeListFree ( rsrvChanFreeList, pciu ); } if ( client->evuser ) { db_close_events (client->evuser); } - if (client->sock!=INVALID_SOCKET) { - if ( socket_close (client->sock) < 0) { - errlogPrintf("CAS: Unable to close socket\n"); + destroy_client ( client ); +} + +/* + * create_client () + */ +struct client * create_client ( SOCKET sock, int proto ) +{ + struct client *client; + + client = freeListCalloc ( rsrvClientFreeList ); + if ( ! client ) { + epicsPrintf ("CAS: no space in pool for a new client\n"); + return NULL; + } + + client->sock = sock; + client->proto = proto; + + client->blockSem = epicsEventCreate ( epicsEventEmpty ); + client->lock = epicsMutexCreate(); + client->putNotifyLock = epicsMutexCreate(); + client->addrqLock = epicsMutexCreate(); + client->eventqLock = epicsMutexCreate(); + if ( ! client->blockSem || ! client->lock || ! client->putNotifyLock || + ! client->addrqLock || ! client->eventqLock ) { + destroy_client ( client ); + return NULL; + } + + client->pUserName = NULL; + client->pHostName = NULL; + ellInit ( &client->addrq ); + ellInit ( &client->putNotifyQue ); + memset ( (char *)&client->addr, 0, sizeof (client->addr) ); + client->tid = 0; + + if ( proto == IPPROTO_TCP ) { + client->send.buf = (char *) freeListCalloc ( rsrvSmallBufFreeListTCP ); + client->send.maxstk = MAX_TCP; + client->send.type = mbtSmallTCP; + client->recv.buf = (char *) freeListCalloc ( rsrvSmallBufFreeListTCP ); + client->recv.maxstk = MAX_TCP; + client->recv.type = mbtSmallTCP; + } + else if ( proto == IPPROTO_UDP ) { + client->send.buf = malloc ( MAX_UDP_SEND ); + client->send.maxstk = MAX_UDP_SEND; + client->send.type = mbtUDP; + client->recv.buf = malloc ( MAX_UDP_RECV ); + client->recv.maxstk = MAX_UDP_RECV; + client->recv.type = mbtUDP; + } + if ( ! client->send.buf || ! client->recv.buf ) { + destroy_client ( client ); + return NULL; + } + client->send.stk = 0u; + client->send.cnt = 0u; + client->recv.stk = 0u; + client->recv.cnt = 0u; + client->evuser = NULL; + client->disconnect = FALSE; + epicsTimeGetCurrent ( &client->time_at_last_send ); + epicsTimeGetCurrent ( &client->time_at_last_recv ); + client->minor_version_number = CA_UKN_MINOR_VERSION; + client->recvBytesToDrain = 0u; + + return client; +} + +void casAttachThreadToClient ( struct client *pClient ) +{ + pClient->tid = epicsThreadGetIdSelf (); + taskwdInsert ( pClient->tid, NULL, NULL ); +} + +void casExpandSendBuffer ( struct client *pClient, ca_uint32_t size ) +{ + if ( pClient->send.type == mbtSmallTCP && rsrvSizeofLargeBufTCP > MAX_TCP ) { + char *pNewBuf = ( char * ) freeListCalloc ( rsrvLargeBufFreeListTCP ); + memcpy ( pNewBuf, pClient->send.buf, pClient->send.stk ); + pClient->send.buf = pNewBuf; + pClient->send.maxstk = rsrvSizeofLargeBufTCP; + pClient->send.type = mbtLargeTCP; + } +} + +void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ) +{ + if ( pClient->recv.type == mbtSmallTCP && rsrvSizeofLargeBufTCP > MAX_TCP ) { + char *pNewBuf = ( char * ) freeListCalloc ( rsrvLargeBufFreeListTCP ); + assert ( pClient->recv.cnt >= pClient->recv.stk ); + memcpy ( pNewBuf, &pClient->recv.buf[pClient->recv.stk], pClient->recv.cnt - pClient->recv.stk ); + pClient->recv.buf = pNewBuf; + pClient->recv.cnt = pClient->recv.cnt - pClient->recv.stk; + pClient->recv.stk = 0u; + pClient->recv.maxstk = rsrvSizeofLargeBufTCP; + pClient->recv.type = mbtLargeTCP; + } +} + +/* + * create_tcp_client () + */ +struct client *create_tcp_client ( SOCKET sock ) +{ + int status; + struct client *client; + int true = TRUE; + osiSocklen_t addrSize; + unsigned priorityOfEvents; + + client = create_client ( sock, IPPROTO_TCP ); + if ( ! client ) { + errlogPrintf ("CAS: no space in pool for a new TCP client\n"); + return NULL; + } + + /* + * see TCP(4P) this seems to make unsolicited single events much + * faster. I take care of queue up as load increases. + */ + status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &true, sizeof (true) ); + if (status < 0) { + errlogPrintf ( "CAS: TCP_NODELAY option set failed\n" ); + destroy_client ( client ); + return NULL; + } + + /* + * turn on KEEPALIVE so if the client crashes + * this task will find out and exit + */ + status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, + (char *) &true, sizeof (true) ); + if ( status < 0 ) { + errlogPrintf ( "CAS: SO_KEEPALIVE option set failed\n" ); + destroy_client ( client ); + return NULL; + } + + /* + * some concern that vxWorks will run out of mBuf's + * if this change is made + * + * joh 11-10-98 + */ +#if 0 + /* + * set TCP buffer sizes to be synergistic + * with CA internal buffering + */ + i = MAX_MSG_SIZE; + status = setsockopt ( sock, SOL_SOCKET, SO_SNDBUF, (char *) &i, sizeof (i) ); + if (status < 0) { + errlogPrintf ( "CAS: SO_SNDBUF set failed\n" ); + destroy_client ( client ); + return NULL; + } + i = MAX_MSG_SIZE; + status = setsockopt ( sock, SOL_SOCKET, SO_RCVBUF, (char *) &i, sizeof (i) ); + if (status < 0) { + errlogPrintf ( "CAS: SO_RCVBUF set failed\n" ); + destroy_client ( client ); + return NULL; + } +#endif + + addrSize = sizeof ( client->addr ); + status = getpeername ( sock, (struct sockaddr *)&client->addr, + &addrSize ); + if ( status < 0 ) { + epicsPrintf ("CAS: peer address fetch failed\n"); + destroy_tcp_client (client); + return NULL; + } + + client->evuser = (struct event_user *) db_init_events (); + if ( ! client->evuser ) { + errlogPrintf ("CAS: unable to init the event facility\n"); + destroy_tcp_client (client); + return NULL; + } + + status = db_add_extra_labor_event (client->evuser, write_notify_reply, client); + if (status != DB_EVENT_OK) { + errlogPrintf("CAS: unable to setup the event facility\n"); + destroy_tcp_client (client); + return NULL; + } + + { + unsigned priorityOfSelf = epicsThreadGetPrioritySelf (); + epicsThreadBooleanStatus tbs; + + tbs = epicsThreadLowestPriorityLevelAbove ( priorityOfSelf, &priorityOfEvents ); + if ( tbs != epicsThreadBooleanStatusSuccess ) { + priorityOfEvents = priorityOfSelf; } } - epicsMutexDestroy (client->eventqLock); - - epicsMutexDestroy (client->addrqLock); - - epicsMutexDestroy (client->putNotifyLock); - - epicsMutexDestroy (client->lock); - - epicsEventDestroy (client->blockSem); - - if (client->pUserName) { - free (client->pUserName); + status = db_start_events ( client->evuser, "CAS-event", + NULL, NULL, priorityOfEvents ); + if (status != DB_EVENT_OK) { + errlogPrintf("CAS: unable to start the event facility\n"); + destroy_tcp_client (client); + return NULL; } - if (client->pHostName) { - free (client->pHostName); + if ( CASDEBUG > 0 ) { + char buf[64]; + ipAddrToDottedIP ( &client->addr, buf, sizeof(buf) ); + errlogPrintf ( "CAS: conn req from %s\n", buf ); } - client->minor_version_number = CA_UKN_MINOR_VERSION; - - freeListFree (rsrvClientFreeList, client); + return client; } + diff --git a/src/rsrv/cast_server.c b/src/rsrv/cast_server.c index 76802489b..9cc03c541 100644 --- a/src/rsrv/cast_server.c +++ b/src/rsrv/cast_server.c @@ -60,7 +60,7 @@ #include "server.h" -#define TIMEOUT 60.0 /* sec */ +#define TIMEOUT 60.0 /* sec */ /* * clean_addrq @@ -233,34 +233,33 @@ int cast_server(void) * possible * */ - while (TRUE) { - prsrv_cast_client = create_base_client (); - if (prsrv_cast_client) { + while ( TRUE ) { + prsrv_cast_client = create_client ( IOC_cast_sock, IPPROTO_UDP ); + if ( prsrv_cast_client ) { break; } epicsThreadSleep(300.0); } - prsrv_cast_client->sock = IOC_cast_sock; - prsrv_cast_client->tid = epicsThreadGetIdSelf (); + casAttachThreadToClient ( prsrv_cast_client ); while (TRUE) { status = recvfrom ( IOC_cast_sock, prsrv_cast_client->recv.buf, - sizeof(prsrv_cast_client->recv.buf), + prsrv_cast_client->recv.maxstk, 0, (struct sockaddr *)&new_recv_addr, &recv_addr_size); if (status<0) { epicsPrintf ("CAS: UDP recv error (errno=%s)\n", SOCKERRSTR(SOCKERRNO)); - epicsThreadSleep(1.0); + epicsThreadSleep(1.0); } else { - prsrv_cast_client->recv.cnt = (unsigned long) status; + prsrv_cast_client->recv.cnt = (unsigned) status; prsrv_cast_client->recv.stk = 0ul; - epicsTimeGetCurrent(&prsrv_cast_client->time_at_last_recv); + epicsTimeGetCurrent(&prsrv_cast_client->time_at_last_recv); /* * If we are talking to a new client flush to the old one @@ -292,8 +291,7 @@ int cast_server(void) if (CASDEBUG>2) count = ellCount (&prsrv_cast_client->addrq); - status = camessage( - prsrv_cast_client,&prsrv_cast_client->recv); + status = camessage ( prsrv_cast_client ); if(status == RSRV_OK){ if(prsrv_cast_client->recv.cnt != prsrv_cast_client->recv.stk){ diff --git a/src/rsrv/server.h b/src/rsrv/server.h index 66d6cedf9..b806f818c 100644 --- a/src/rsrv/server.h +++ b/src/rsrv/server.h @@ -41,6 +41,7 @@ #include "asLib.h" #include "dbAddr.h" #include "dbNotify.h" +#define CA_MINOR_PROTOCOL_REVISION 9 #include "caProto.h" #include "ellLib.h" #include "epicsTime.h" @@ -51,47 +52,38 @@ #define LOCAL static +/* a modified ca header with capacity for large arrays */ +typedef struct caHdrLargeArray { + ca_uint32_t m_postsize; /* size of message extension */ + ca_uint32_t m_count; /* operation data count */ + ca_uint32_t m_cid; /* channel identifier */ + ca_uint32_t m_available; /* protocol stub dependent */ + ca_uint16_t m_dataType; /* operation data type */ + ca_uint16_t m_cmmd; /* operation to be performed */ +}caHdrLargeArray; + /* * !! buf must be the first item in this structure !! * This guarantees that buf will have 8 byte natural * alignment * - * Conversions: - * The contents of message_buffer has to be converted - * from network to host format and vice versa. - * For efficiency reasons, the caHdr structure that's common - * to all messages is converted only once: - * 1) from net to host just after receiving it in camessage() - * 2) from host to net in cas_send_msg() - * - * The remaining message_buffer content, however, is always - * in net format! - * - * The terminating unsigned long pad0 field is there to force the + * The terminating unsigned pad0 field is there to force the * length of the message_buffer to be a multiple of 8 bytes. * This is due to the sequential placing of two message_buffer * structures (trans, rec) within the client structure. * Eight-byte alignment is required by the Sparc 5 and other RISC * processors. - * - * CAVEAT: This assumes the following: - * o an array of MAX_MSG_SIZE chars takes a multiple of 8 bytes. - * o four unsigned longs also take up a multiple of 8 bytes - * (usually 2). - * NOTE: - * o we should solve the above message alignment problems by - * allocating the message buffers - * */ +enum messageBufferType { mbtUDP, mbtSmallTCP, mbtLargeTCP }; struct message_buffer { - char buf[MAX_MSG_SIZE]; - unsigned long stk; - unsigned long maxstk; - unsigned long cnt; - unsigned long pad0; /* force 8 byte alignement */ + char *buf; + unsigned stk; + unsigned maxstk; + unsigned cnt; + enum messageBufferType type; }; -struct client { +typedef struct client { ELLNODE node; struct message_buffer send; struct message_buffer recv; @@ -102,8 +94,8 @@ struct client { ELLLIST addrq; ELLLIST putNotifyQue; struct sockaddr_in addr; - epicsTimeStamp time_at_last_send; - epicsTimeStamp time_at_last_recv; + epicsTimeStamp time_at_last_send; + epicsTimeStamp time_at_last_recv; void *evuser; char *pUserName; char *pHostName; @@ -112,8 +104,9 @@ struct client { int proto; epicsThreadId tid; unsigned minor_version_number; + unsigned recvBytesToDrain; char disconnect; /* disconnect detected */ -}; +} client; /* @@ -122,8 +115,8 @@ struct client { typedef struct rsrv_put_notify { ELLNODE node; PUTNOTIFY dbPutNotify; - caHdr msg; - unsigned long valueSize; /* size of block pointed to by dbPutNotify */ + caHdrLargeArray msg; + unsigned valueSize; /* size of block pointed to by dbPutNotify */ int busy; /* put notify in progress */ } RSRVPUTNOTIFY; @@ -139,7 +132,7 @@ struct channel_in_use { RSRVPUTNOTIFY *pPutNotify; /* potential active put notify */ const unsigned cid; /* client id */ const unsigned sid; /* server id */ - epicsTimeStamp time_at_creation; /* for UDP timeout */ + epicsTimeStamp time_at_creation; /* for UDP timeout */ struct dbAddr addr; ASCLIENTPVT asClientPVT; }; @@ -151,7 +144,7 @@ struct channel_in_use { */ struct event_ext { ELLNODE node; - caHdr msg; + caHdrLargeArray msg; struct channel_in_use *pciu; struct event_block *pdbev; /* ptr to db event block */ unsigned size; /* for speed */ @@ -173,13 +166,10 @@ struct event_ext { * for debug-level dependent messages: */ #ifdef DEBUG -# define DLOG(level, fmt, a1, a2, a3, a4, a5, a6) \ - if (CASDEBUG > level) errlogPrintf (fmt, a1, a2, a3, a4, a5, a6) -# define DBLOCK(level, code) \ - if (CASDEBUG > level) { code; } +# define DLOG(LEVEL,ARGSINPAREN) \ + if (CASDEBUG > LEVEL) errlogPrintf ARGSINPAREN #else -# define DLOG(level, fmt, a1, a2, a3, a4, a5, a6) -# define DBLOCK(level, code) +# define DLOG(LEVEL,ARGSINPAREN) #endif GLBLTYPE int CASDEBUG; @@ -194,6 +184,9 @@ GLBLTYPE BUCKET *pCaBucket; GLBLTYPE void *rsrvClientFreeList; GLBLTYPE void *rsrvChanFreeList; GLBLTYPE void *rsrvEventFreeList; +GLBLTYPE void *rsrvSmallBufFreeListTCP; +GLBLTYPE void *rsrvLargeBufFreeListTCP; +GLBLTYPE unsigned rsrvSizeofLargeBufTCP; #define CAS_HASH_TABLE_SIZE 4096 @@ -202,37 +195,37 @@ GLBLTYPE int casSufficentSpaceInPool; #define SEND_LOCK(CLIENT) epicsMutexMustLock((CLIENT)->lock) #define SEND_UNLOCK(CLIENT) epicsMutexUnlock((CLIENT)->lock) -#define EXTMSGPTR(CLIENT)\ - ((caHdr *) &(CLIENT)->send.buf[(CLIENT)->send.stk]) - -/* - * ALLOC_MSG get a ptr to space in the buffer - * END_MSG push a message onto the buffer stack - * - */ -#define ALLOC_MSG(CLIENT, EXTSIZE) cas_alloc_msg (CLIENT, EXTSIZE) - -#define END_MSG(CLIENT)\ - EXTMSGPTR(CLIENT)->m_postsize = CA_MESSAGE_ALIGN(EXTMSGPTR(CLIENT)->m_postsize),\ - (CLIENT)->send.stk += sizeof(caHdr) + EXTMSGPTR(CLIENT)->m_postsize - #define LOCK_CLIENTQ epicsMutexMustLock (clientQlock); #define UNLOCK_CLIENTQ epicsMutexUnlock (clientQlock); void camsgtask (struct client *client); void cas_send_msg (struct client *pclient, int lock_needed); -caHdr *cas_alloc_msg (struct client *pclient, unsigned extsize); int rsrv_online_notify_task (void); -void cac_send_heartbeat (void); int cast_server (void); -struct client *create_base_client (); -int camessage (struct client *client, - struct message_buffer *recv); -void cas_send_heartbeat (struct client *pc); -void write_notify_reply (void *pArg); -int rsrvCheckPut (const struct channel_in_use *pciu); -struct client *create_client (SOCKET sock); -void destroy_client (struct client *client); +struct client *create_client (); +void destroy_client ( struct client * ); +struct client *create_tcp_client ( SOCKET sock ); +void destroy_tcp_client ( struct client * ); +void casAttachThreadToClient ( struct client * ); +int camessage ( struct client *client ); +void write_notify_reply ( void *pArg ); +int rsrvCheckPut ( const struct channel_in_use *pciu ); + +/* + * inclming protocol maintetnance + */ +void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ); + +/* + * outgoing protocol maintenance + */ +void casExpandSendBuffer ( struct client *pClient, ca_uint32_t size ); +int cas_copy_in_header ( + struct client *pClient, ca_uint16_t response, ca_uint32_t payloadSize, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint32_t responseSpecific, void **pPayload ); +void cas_set_header_cid ( struct client *pClient, ca_uint32_t ); +void cas_commit_msg ( struct client *pClient, ca_uint32_t size ); /* * !!KLUDGE!!