diff --git a/src/ca/Makefile b/src/ca/Makefile index 92d1e9ec8..7f0ca5973 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -20,7 +20,7 @@ LIBSRCS += cacNotify.cpp LIBSRCS += cacNotifyIO.cpp LIBSRCS += cacServiceList.cpp LIBSRCS += access.cpp -LIBSRCS += processThread.cpp +LIBSRCS += recvProcessThread.cpp LIBSRCS += iocinf.cpp LIBSRCS += convert.cpp LIBSRCS += test_event.cpp @@ -38,7 +38,6 @@ LIBSRCS += netReadNotifyIO.cpp LIBSRCS += netWriteNotifyIO.cpp LIBSRCS += netSubscription.cpp LIBSRCS += cac.cpp -LIBSRCS += conn.cpp LIBSRCS += tcpSendWatchdog.cpp LIBSRCS += tcpRecvWatchdog.cpp LIBSRCS += bhe.cpp @@ -50,6 +49,12 @@ LIBSRCS += syncgrp.cpp LIBSRCS += CASG.cpp LIBSRCS += syncGroupNotify.cpp LIBSRCS += localHostName.cpp +LIBSRCS += claimsPendingIIU.cpp +LIBSRCS += claimMsgCache.cpp +LIBSRCS += comQueRecv.cpp +LIBSRCS += comQueSend.cpp +LIBSRCS += cacPrivate.cpp +LIBSRCS += hostNameCache.cpp LIBRARY=ca diff --git a/src/ca/access.cpp b/src/ca/access.cpp index 9586b4a01..297fa53db 100644 --- a/src/ca/access.cpp +++ b/src/ca/access.cpp @@ -25,6 +25,7 @@ #include "iocinf.h" #include "oldAccess.h" +#include "cac_IL.h" threadPrivateId caClientContextId; @@ -229,7 +230,7 @@ int epicsShareAPI ca_search_and_connect (const char *name_str, chid *chanptr, int caStatus; cac *pcac; - caStatus = fetchClientContext (&pcac); + caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } @@ -238,16 +239,23 @@ int epicsShareAPI ca_search_and_connect (const char *name_str, chid *chanptr, return ECA_EMPTYSTR; } - pChan = new oldChannel (conn_func, puser); + pChan = new oldChannel ( conn_func, puser ); if ( ! pChan ) { return ECA_ALLOCMEM; } + // we must set *chanptr here before we are 100% certain that + // the channel can be created in case *chanptr is inside + // of their structure at address puser and they reference + // it in a connection handler that is called by createChannelIO() + chid tmp = *chanptr; + *chanptr = pChan; + if ( pcac->createChannelIO ( name_str, *pChan ) ) { - *chanptr = pChan; return ECA_NORMAL; } else { + *chanptr = tmp; pChan->destroy (); return ECA_ALLOCMEM; } @@ -428,7 +436,7 @@ int epicsShareAPI ca_pend (ca_real timeout, int early) cac *pcac; int status; - status = fetchClientContext (&pcac); + status = fetchClientContext ( &pcac ); if ( status != ECA_NORMAL ) { return status; } @@ -660,16 +668,16 @@ int epicsShareAPI ca_replace_printf_handler (caPrintfFunc *ca_printf_func) /* * ca_printf() */ -int ca_printf (const char *pformat, ...) +int ca_printf ( const char *pformat, ... ) { va_list theArgs; int status; - va_start (theArgs, pformat); + va_start ( theArgs, pformat ); - status = ca_vPrintf (pformat, theArgs); + status = ca_vPrintf ( pformat, theArgs ); - va_end (theArgs); + va_end ( theArgs ); return status; } @@ -677,31 +685,27 @@ int ca_printf (const char *pformat, ...) /* * ca_vPrintf() */ -int ca_vPrintf (const char *pformat, va_list args) +int ca_vPrintf ( const char *pformat, va_list args ) { - caPrintfFunc *ca_printf_func; - if ( caClientContextId ) { cac *pcac = (cac *) threadPrivateGet ( caClientContextId ); - if (pcac) { - ca_printf_func = pcac->ca_printf_func; + if ( pcac ) { + return pcac->vPrintf ( pformat, args ); } else { - ca_printf_func = errlogVprintf; + return ( *errlogVprintf ) ( pformat, args ); } } else { - ca_printf_func = errlogVprintf; + return ( *errlogVprintf ) ( pformat, args ); } - - return (*ca_printf_func) ( pformat, args ); } /* * ca_field_type() */ -short epicsShareAPI ca_field_type (chid pChan) +short epicsShareAPI ca_field_type ( chid pChan ) { return pChan->nativeType (); } diff --git a/src/ca/acctst.c b/src/ca/acctst.c index 2719363c3..4f992d8d8 100644 --- a/src/ca/acctst.c +++ b/src/ca/acctst.c @@ -6,32 +6,25 @@ /* * ANSI */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include /* * EPICS */ -#include "epicsAssert.h" -#include "tsStamp.h" +#include "epicsAssert.h" +#include "tsStamp.h" /* * CA */ -#include "cadef.h" +#include "cadef.h" #include "caDiagnostics.h" -#define EVENT_ROUTINE null_event -#define CONN_ROUTINE conn - -#define NUM 1 - -int conn_cb_count; - #ifndef min #define min(A,B) ((A)>(B)?(B):(A)) #endif @@ -48,40 +41,36 @@ int conn_cb_count; #define NELEMENTS(A) ( sizeof (A) / sizeof (A[0]) ) #endif -int doacctst(char *pname); -void test_sync_groups(chid chix); -void multiple_sg_requests(chid chix, CA_SYNC_GID gid); -void null_event(struct event_handler_args args); -void write_event(struct event_handler_args args); -void conn(struct connection_handler_args args); -void get_cb(struct event_handler_args args); -void accessSecurity_cb(struct access_rights_handler_args args); -void pend_event_delay_test(dbr_double_t request); -void performMonitorUpdateTest (chid chan); -void performDeleteTest (chid chan); +typedef struct appChan { + char name[64]; + chid channel; + evid subscription; + unsigned char connected; + unsigned subscriptionUpdateCount; + unsigned accessUpdateCount; + unsigned connectionUpdateCount; + unsigned getCallbackCount; +} appChan; -void doubleTest( -chid chan, -dbr_double_t beginValue, -dbr_double_t increment, -dbr_double_t epsilon, -unsigned iterations); +unsigned subscriptionUpdateCount; +unsigned accessUpdateCount; +unsigned connectionUpdateCount; +unsigned getCallbackCount; -void floatTest( -chid chan, -dbr_float_t beginValue, -dbr_float_t increment, -dbr_float_t epsilon, -unsigned iterations); +void showProgress () +{ + printf ( "." ); + fflush (stdout ); +} -void performGrEnumTest (chid chan); - -void performCtrlDoubleTest (chid chan); - -void atLeastOneUpdateTester ( struct event_handler_args args ) +void nUpdatesTester ( struct event_handler_args args ) { unsigned *pCtr = (unsigned *) args.usr; - (*pCtr)++; + ( *pCtr ) ++; + if ( args.status != ECA_NORMAL ) { + printf("subscription update failed for \"%s\" because \"%s\"", + ca_name ( args.chid ), ca_message ( args.status ) ); + } } void monitorSubscriptionFirstUpdateTest ( chid chan ) @@ -95,7 +84,7 @@ void monitorSubscriptionFirstUpdateTest ( chid chan ) * verify that the first event arrives */ status = ca_add_event ( DBR_FLOAT, - chan, atLeastOneUpdateTester, &eventCount, &id ); + chan, nUpdatesTester, &eventCount, &id ); SEVCHK (status, 0); while ( eventCount < 1 && waitCount++ < 100 ) { ca_pend_event ( 0.01 ); @@ -103,1118 +92,370 @@ void monitorSubscriptionFirstUpdateTest ( chid chan ) assert ( eventCount > 0 ); status = ca_clear_event ( id ); SEVCHK (status, 0); + + showProgress (); } -int acctst (char *pname) +void accessRightsStateChange ( struct access_rights_handler_args args ) { - chid chix1; - chid chix2; - chid chix3; - chid chix4; - struct dbr_gr_float *pgrfloat = NULL; - dbr_float_t *pfloat = NULL; - dbr_double_t *pdouble = NULL; - long status; - long i, j; - evid monix; - char pstring[NUM][MAX_STRING_SIZE]; - unsigned monCount=0u; + appChan *pChan = (appChan *) ca_puser ( args.chid ); - SEVCHK ( ca_task_initialize(), "Unable to initialize" ); + assert ( pChan->channel == args.chid ); + assert ( args.ar.read_access == ca_read_access ( args.chid ) ); + assert ( args.ar.write_access == ca_write_access ( args.chid ) ); + accessUpdateCount++; + pChan->accessUpdateCount++; +} - conn_cb_count = 0; +void getCallbackStateChange ( struct event_handler_args args ) +{ + appChan *pChan = (appChan *) args.usr; - printf ( "begin\n" ); + assert ( pChan->channel == args.chid ); + assert ( pChan->connected ); + assert ( args.status == ECA_NORMAL ); - printf ( "CA Client V%s\n", ca_version () ); + getCallbackCount++; + pChan->getCallbackCount++; +} - /* - * verify that we dont print a disconnect message when - * we delete the last channel - * (this fails if we see a disconnect message) - */ - status = ca_search ( pname, &chix3 ); - SEVCHK ( status, NULL ); - status = ca_pend_io ( 1000.0 ); - SEVCHK ( status, NULL ); - status = ca_clear_channel ( chix3 ); - SEVCHK ( status, NULL ); +void connectionStateChange ( struct connection_handler_args args ) +{ + int status; - /* - * verify lots of disconnects - * verify channel connected state variables - */ - printf ( "Connect/disconnect test" ); - fflush ( stdout ); - for ( i = 0; i < 10; i++ ) { + appChan *pChan = (appChan *) ca_puser ( args.chid ); - status = ca_search ( pname, &chix3 ); - SEVCHK(status, NULL); + assert ( pChan->channel == args.chid ); - status = ca_search ( pname, &chix4 ); - SEVCHK(status, NULL); + if ( args.op == CA_OP_CONN_UP ) { + assert ( pChan->accessUpdateCount > 0u ); + assert ( ! pChan->connected ); + pChan->connected = 1; + status = ca_get_callback ( DBR_GR_STRING, args.chid, getCallbackStateChange, pChan ); + SEVCHK (status, 0); + status = ca_flush_io (); + SEVCHK (status, 0); + } + else if ( args.op == CA_OP_CONN_DOWN ) { + assert ( pChan->connected ); + pChan->connected = 0u; + assert ( ! ca_read_access ( args.chid ) ); + assert ( ! ca_write_access ( args.chid ) ); + } + else { + assert ( 0 ); + } + pChan->connectionUpdateCount++; + connectionUpdateCount++; +} - status = ca_search ( pname, &chix2 ); - SEVCHK(status, NULL); +void subscriptionStateChange ( struct event_handler_args args ) +{ + appChan *pChan = (appChan *) args.usr; - status = ca_search ( pname, &chix1 ); - SEVCHK(status, NULL); + assert ( pChan->channel == args.chid ); + assert ( pChan->connected ); + assert ( args.type == DBR_GR_STRING ); + pChan->subscriptionUpdateCount++; + subscriptionUpdateCount++; - if ( ca_test_io() == ECA_IOINPROGRESS ) { - assert(INVALID_DB_REQ(ca_field_type(chix1)) == TRUE); - assert(INVALID_DB_REQ(ca_field_type(chix2)) == TRUE); - assert(INVALID_DB_REQ(ca_field_type(chix3)) == TRUE); - assert(INVALID_DB_REQ(ca_field_type(chix4)) == TRUE); + if ( args.status != ECA_NORMAL ) { + printf("subscription update failed for \"%s\" because \"%s\"", + ca_name ( args.chid ), ca_message ( args.status ) ); + } - assert(ca_state(chix1) == cs_never_conn); - assert(ca_state(chix2) == cs_never_conn); - assert(ca_state(chix3) == cs_never_conn); - assert(ca_state(chix4) == cs_never_conn); + assert ( strlen ( (char *) args.dbr ) <= MAX_STRING_SIZE ); +} + +void noopSubscriptionStateChange ( struct event_handler_args args ) +{ + if ( args.status != ECA_NORMAL ) { + printf("subscription update failed for \"%s\" because \"%s\"", + ca_name ( args.chid ), ca_message ( args.status ) ); + } +} + +/* + * verifyConnectionHandlerConnect () + * + * 1) verify that connection handler runs during connect + * + * 2) verify that access rights handler runs during connect + * + * 3) verify that get call back runs from connection handler + * + * 4) verify that first event callback arrives after connect + * + * 5) verify subscription can be cleared before channel is cleared + * + * 6) verify subscription can be cleared by clearing the channel + */ +void verifyConnectionHandlerConnect ( appChan *pChans, unsigned chanCount, unsigned repetitionCount ) +{ + int status; + unsigned i, j; + + subscriptionUpdateCount = 0u; + accessUpdateCount = 0u; + connectionUpdateCount = 0u; + getCallbackCount = 0u; + + for ( i = 0; i < repetitionCount; i++ ) { + for ( j = 0u; j < chanCount; j++ ) { + + pChans[j].subscriptionUpdateCount = 0u; + pChans[j].accessUpdateCount = 0u; + pChans[j].connectionUpdateCount = 0u; + pChans[j].getCallbackCount = 0u; + pChans[j].connected = 0u; + + status = ca_search_and_connect ( pChans[j].name, + &pChans[j].channel, connectionStateChange, &pChans[j]); + SEVCHK ( status, NULL ); + + status = ca_replace_access_rights_event ( + pChans[j].channel, accessRightsStateChange ); + SEVCHK ( status, NULL ); + + status = ca_add_event ( DBR_GR_STRING, pChans[j].channel, + subscriptionStateChange, &pChans[j], &pChans[j].subscription ); + SEVCHK ( status, NULL ); + + assert ( ca_test_io () == ECA_IODONE ); } - status = ca_pend_io(1000.0); - SEVCHK(status, NULL); + while ( connectionUpdateCount < chanCount || + getCallbackCount < chanCount ) { + ca_pend_event ( 1.0 ); + } - printf("."); - fflush(stdout); + for ( j = 0u; j < chanCount; j++ ) { + assert ( pChans[j].getCallbackCount == 1u); + assert ( pChans[j].connectionUpdateCount > 0 ); + if ( pChans[j].connectionUpdateCount > 1u ) { + printf ("Unusual connection activity count = %u on channel %s?\n", + pChans[j].connectionUpdateCount, pChans[j].name ); + } + assert ( pChans[j].accessUpdateCount > 0 ); + if ( pChans[j].accessUpdateCount > 1u ) { + printf ("Unusual access rights activity count = %u on channel %s?\n", + pChans[j].connectionUpdateCount, pChans[j].name ); + } + } - assert(ca_test_io() == ECA_IODONE); + for ( j = 0u; j < chanCount; j += 2 ) { + status = ca_clear_event ( pChans[j].subscription ); + SEVCHK ( status, NULL ); + } - assert(ca_state(chix1) == cs_conn); - assert(ca_state(chix2) == cs_conn); - assert(ca_state(chix3) == cs_conn); - assert(ca_state(chix4) == cs_conn); + for ( j = 0u; j < chanCount; j++ ) { + status = ca_clear_channel ( pChans[j].channel ); + SEVCHK ( status, NULL ); + } + } + showProgress (); +} - SEVCHK(ca_clear_channel(chix4), NULL); - SEVCHK(ca_clear_channel(chix3), NULL); - SEVCHK(ca_clear_channel(chix2), NULL); - SEVCHK(ca_clear_channel(chix1), NULL); +/* + * verifyBlockingConnect () + * + * 1) verify that we dont print a disconnect message when + * we delete the last channel + * + * 2) verify that we delete the connection to the IOC + * when the last channel is deleted. + * + * 3) verify channel connection state variables + * + * 4) verify ca_test_io () and ca_pend_io () work with + * channels w/o connection handlers + * + * 5) verify that the pending IO count is properly + * maintained when we are add/removing a connection + * handler + * + * 6) verify that the pending IO count goes to zero + * if the channel is deleted before it connects. + */ +void verifyBlockingConnect ( appChan *pChans, unsigned chanCount, unsigned repetitionCount ) +{ + int status; + unsigned connections; + unsigned i, j; + + connections = ca_get_ioc_connection_count (); + assert ( connections == 0u ); + + for ( i = 0; i < repetitionCount; i++ ) { + + for ( j = 0u; j < chanCount; j++ ) { + pChans[j].subscriptionUpdateCount = 0u; + pChans[j].accessUpdateCount = 0u; + pChans[j].connectionUpdateCount = 0u; + pChans[j].getCallbackCount = 0u; + pChans[j].connected = 0u; + + status = ca_search_and_connect ( pChans[j].name, &pChans[j].channel, NULL, &pChans[j] ); + SEVCHK ( status, NULL ); + + if ( ca_state ( pChans[j].channel ) == cs_conn ) { + assert ( VALID_DB_REQ ( ca_field_type ( pChans[j].channel ) ) == TRUE ); + } + else { + assert ( INVALID_DB_REQ ( ca_field_type ( pChans[j].channel ) ) == TRUE ); + assert ( ca_test_io () == ECA_IOINPROGRESS ); + } + + status = ca_replace_access_rights_event ( + pChans[j].channel, accessRightsStateChange ); + SEVCHK ( status, NULL ); + } + + for ( j = 0u; j < chanCount; j += 2 ) { + status = ca_change_connection_event ( pChans[j].channel, connectionStateChange ); + SEVCHK ( status, NULL ); + } + + for ( j = 0u; j < chanCount; j += 2 ) { + status = ca_change_connection_event ( pChans[j].channel, NULL ); + SEVCHK ( status, NULL ); + } + + for ( j = 0u; j < chanCount; j += 2 ) { + status = ca_change_connection_event ( pChans[j].channel, connectionStateChange ); + SEVCHK ( status, NULL ); + } + + for ( j = 0u; j < chanCount; j += 2 ) { + status = ca_change_connection_event ( pChans[j].channel, NULL ); + SEVCHK ( status, NULL ); + } + + status = ca_pend_io ( 1000.0 ); + SEVCHK ( status, NULL ); + + assert ( ca_test_io () == ECA_IODONE ); + + connections = ca_get_ioc_connection_count (); + assert ( connections == 1u ); + + for ( j = 0u; j < chanCount; j++ ) { + assert ( VALID_DB_REQ ( ca_field_type ( pChans[j].channel ) ) == TRUE ); + assert ( ca_state ( pChans[j].channel ) == cs_conn ); + SEVCHK ( ca_clear_channel ( pChans[j].channel ), NULL ); + } /* * verify that connections to IOC's that are * not in use are dropped */ - j=0; - do { - ca_pend_event (0.1); - assert (++j<100); + if ( ca_get_ioc_connection_count() != 0u ) { + ca_pend_event ( 1.0 ); + j=0; + while ( ca_get_ioc_connection_count() != 0u ) { + printf ( "-" ); + ca_pend_event ( 1.0 ); + assert ( ++j < 100 ); + fflush ( stdout ); + } + printf ("\n"); } - while (ca_get_ioc_connection_count()>0u); - } - printf("\n"); - /* - * look for problems with ca_search(), ca_clear_channel(), - * ca_change_connection_event(), and ca_pend_io(() combo - */ - status = ca_search ( pname,& chix3 ); - SEVCHK ( status, NULL ); + for ( j = 0u; j < chanCount; j++ ) { + status = ca_search ( pChans[j].name, &pChans[j].channel ); + SEVCHK ( status, NULL ); + } - status = ca_replace_access_rights_event ( chix3, accessSecurity_cb ); - SEVCHK ( status, NULL ); + for ( j = 0u; j < chanCount; j++ ) { + status = ca_clear_channel ( pChans[j].channel ); + SEVCHK ( status, NULL ); + } - /* - * verify clear before connect - */ - status = ca_search ( pname, &chix4 ); - SEVCHK ( status, NULL ); - - /* - * verify that NULL - * evid does not cause failure - */ - status = ca_add_event ( DBR_FLOAT, - chix4, EVENT_ROUTINE, NULL, NULL ); - SEVCHK ( status, NULL ); - - status = ca_clear_channel ( chix4 ); - SEVCHK ( status, NULL ); - - status = ca_search ( pname, &chix4 ); - SEVCHK ( status, NULL ); - - status = ca_replace_access_rights_event ( chix4, accessSecurity_cb ); - SEVCHK ( status, NULL ); - - status = ca_search ( pname, &chix2 ); - SEVCHK (status, NULL); - - status = ca_replace_access_rights_event (chix2, accessSecurity_cb); - SEVCHK ( status, NULL ); - - status = ca_search ( pname, &chix1 ); - SEVCHK ( status, NULL ); - - status = ca_replace_access_rights_event ( chix1, accessSecurity_cb ); - SEVCHK ( status, NULL ); - - status = ca_change_connection_event ( chix1, conn ); - SEVCHK ( status, NULL ); - - status = ca_change_connection_event ( chix1, NULL ); - SEVCHK ( status, NULL ); - - status = ca_change_connection_event ( chix1, conn ); - SEVCHK ( status, NULL ); - - status = ca_change_connection_event ( chix1, NULL ); - SEVCHK ( status, NULL ); - - status = ca_pend_io ( 1000.0 ); - SEVCHK ( status, NULL ); - - assert ( ca_state (chix1) == cs_conn ); - assert ( ca_state (chix2) == cs_conn ); - assert ( ca_state (chix3) == cs_conn ); - assert ( ca_state (chix4) == cs_conn ); - - assert ( INVALID_DB_REQ (ca_field_type (chix1) ) == FALSE ); - assert ( INVALID_DB_REQ (ca_field_type (chix2) ) == FALSE ); - assert ( INVALID_DB_REQ (ca_field_type (chix3) ) == FALSE ); - assert ( INVALID_DB_REQ (ca_field_type (chix4) ) == FALSE ); - - printf("%s Read Access=%d Write Access=%d\n", - ca_name(chix1), ca_read_access(chix1), ca_write_access(chix1)); - - /* - * clear chans before starting another test - */ - status = ca_clear_channel(chix1); - SEVCHK(status, NULL); - status = ca_clear_channel(chix2); - SEVCHK(status, NULL); - status = ca_clear_channel(chix3); - SEVCHK(status, NULL); - status = ca_clear_channel(chix4); - SEVCHK(status, NULL); + assert ( ca_test_io () == ECA_IODONE ); /* * verify ca_pend_io() does not see old search requests * (that did not specify a connection handler) */ - status = ca_search_and_connect(pname, &chix1, NULL, NULL); - SEVCHK(status, NULL); - /* - * channel will connect synchronously if on the - * local host - */ - if (ca_state(chix1)==cs_never_conn) { - status = ca_pend_io(1e-16); - if (status==ECA_TIMEOUT) { - - printf ("waiting on pend io verify connect..."); - fflush (stdout); - while (ca_state(chix1)!=cs_conn) { - ca_pend_event(0.1); - } - printf ("done\n"); + status = ca_search_and_connect ( pChans[0].name, &pChans[0].channel, NULL, NULL); + SEVCHK ( status, NULL ); + if ( ca_state ( pChans[0].channel ) == cs_never_conn ) { + /* force an early timeout */ + status = ca_pend_io ( 1e-16 ); + if ( status == ECA_TIMEOUT ) { /* * we end up here if the channel isnt on the same host */ - status = ca_search_and_connect (pname, &chix2, NULL, NULL); - SEVCHK (status, NULL); - status = ca_pend_io(1e-16); - if (status!=ECA_TIMEOUT) { - assert(ca_state(chix2)==cs_conn); + ca_pend_event ( 0.1 ); + if ( ca_state( pChans[0].channel ) != cs_conn ) { + printf ( "waiting on pend io verify connect" ); + fflush ( stdout ); + while ( ca_state ( pChans[0].channel ) != cs_conn ) { + printf ( "." ); + fflush ( stdout ); + ca_pend_event ( 0.1 ); + } + printf ( "done\n" ); } - status = ca_clear_channel (chix2); - SEVCHK (status, NULL); - } - else { - assert (ca_state(chix1)==cs_conn); - } - } - status = ca_clear_channel(chix1); - SEVCHK (status, NULL); - /* - * verify connection handlers are working - */ - status = ca_search_and_connect(pname, &chix1, conn, NULL); - SEVCHK(status, NULL); - status = ca_search_and_connect(pname, &chix2, conn, NULL); - SEVCHK(status, NULL); - status = ca_search_and_connect(pname, &chix3, conn, NULL); - SEVCHK(status, NULL); - status = ca_search_and_connect(pname, &chix4, conn, NULL); - SEVCHK(status, NULL); - - printf("waiting on conn handler call back connect..."); - fflush(stdout); - while (conn_cb_count != 4) { - ca_pend_event(0.1); - } - printf("done\n"); - - monitorSubscriptionFirstUpdateTest ( chix4 ); - - performGrEnumTest (chix1); - - performCtrlDoubleTest (chix1); - - /* - * ca_pend_io() must block - */ - if(ca_read_access(chix4)){ - dbr_float_t req; - dbr_float_t resp; - - printf ("get TMO test ..."); - fflush(stdout); - req = 56.57f; - resp = -99.99f; - SEVCHK(ca_put(DBR_FLOAT, chix4, &req),NULL); - SEVCHK(ca_get(DBR_FLOAT, chix4, &resp),NULL); - status = ca_pend_io(1.0e-12); - if (status==ECA_NORMAL) { - if (resp != req) { - printf ( - "get block test failed - val written %f\n", req); - printf ( - "get block test failed - val read %f\n", resp); - assert(0); + status = ca_search_and_connect ( pChans[1].name, &pChans[1].channel, NULL, NULL ); + SEVCHK ( status, NULL ); + status = ca_pend_io ( 1e-16 ); + if ( status != ECA_TIMEOUT ) { + assert ( ca_state ( pChans[1].channel ) == cs_conn ); } - } - else if (resp != -99.99f) { - printf ( - "CA didnt block for get to return?\n"); - } - - req = 33.44f; - resp = -99.99f; - SEVCHK (ca_put(DBR_FLOAT, chix4, &req),NULL); - SEVCHK (ca_get(DBR_FLOAT, chix4, &resp),NULL); - SEVCHK (ca_pend_io(2000.0),NULL); - if (resp != req) { - printf ( - "get block test failed - val written %f\n", req); - printf ( - "get block test failed - val read %f\n", resp); - assert(0); - } - printf ("done\n"); - } - - /* - * Verify that we can do IO with the new types for ALH - */ -#if 0 - if(ca_read_access(chix4)&&ca_write_access(chix4)){ - { - dbr_put_ackt_t acktIn=1u; - dbr_put_acks_t acksIn=1u; - struct dbr_stsack_string stsackOut; - - SEVCHK (ca_put(DBR_PUT_ACKT, chix4, &acktIn),NULL); - SEVCHK (ca_put(DBR_PUT_ACKS, chix4, &acksIn),NULL); - SEVCHK (ca_get(DBR_STSACK_STRING, chix4, &stsackOut),NULL); - SEVCHK (ca_pend_io(2000.0),NULL); - } -#endif - - /* - * Verify that we can write and then read back - * the same analog value (DBR_FLOAT) - */ - if( (ca_field_type(chix1)==DBR_DOUBLE || - ca_field_type(chix1)==DBR_FLOAT) && - ca_read_access(chix1) && - ca_write_access(chix1)){ - - dbr_float_t incr; - dbr_float_t epsil; - dbr_float_t base; - unsigned long iter; - - printf ("dbr_float_t test "); - fflush (stdout); - epsil = FLT_EPSILON*4.0F; - base = FLT_MIN; - for (i=FLT_MIN_EXP; iFLT_MAX/10.0) { - iter = (unsigned long) (FLT_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - floatTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - base = FLT_MAX; - for (i=FLT_MIN_EXP; iFLT_MAX/10.0) { - iter = (unsigned long) (FLT_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - floatTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - base = - FLT_MAX; - for (i=FLT_MIN_EXP; iFLT_MAX/10.0) { - iter = (unsigned long) (FLT_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - floatTest (chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - printf ("done\n"); - } - - /* - * Verify that we can write and then read back - * the same analog value (DBR_DOUBLE) - */ - if( ca_field_type(chix1)==DBR_DOUBLE && - ca_read_access(chix1) && - ca_write_access(chix1)){ - - dbr_double_t incr; - dbr_double_t epsil; - dbr_double_t base; - unsigned long iter; - - printf ("dbr_double_t test "); - fflush(stdout); - epsil = DBL_EPSILON*4; - base = DBL_MIN; - for (i=DBL_MIN_EXP; iDBL_MAX/10.0) { - iter = (unsigned long) (DBL_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - doubleTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - base = DBL_MAX; - for (i=DBL_MIN_EXP; iDBL_MAX/10.0) { - iter = (unsigned long) (DBL_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - doubleTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - base = - DBL_MAX; - for (i=DBL_MIN_EXP; iDBL_MAX/10.0) { - iter = (unsigned long) (DBL_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - doubleTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - printf ("done\n"); - } - - /* - * Verify that we can write and then read back - * the same integer value (DBR_LONG) - */ - if (ca_read_access(chix1) && ca_write_access(chix1)) { - - dbr_long_t iter, rdbk, incr; - struct dbr_ctrl_long cl; - - status = ca_get (DBR_CTRL_LONG, chix1, &cl); - SEVCHK (status, "graphic long fetch failed\n"); - status = ca_pend_io (10.0); - SEVCHK (status, "graphic long pend failed\n"); - - incr = (cl.upper_ctrl_limit - cl.lower_ctrl_limit); - if (incr>=1) { - incr /= 1000; - if (incr==0) { - incr = 1; - } - printf ("dbr_long_t test "); - fflush (stdout); - for (iter=cl.lower_ctrl_limit; - iter<=cl.upper_ctrl_limit; iter+=incr) { - - status = ca_put (DBR_LONG, chix1, &iter); - status = ca_get (DBR_LONG, chix1, &rdbk); - status = ca_pend_io (10.0); - SEVCHK (status, "get pend failed\n"); - assert (iter == rdbk); - printf ("."); - fflush (stdout); - } - printf ("done\n"); - } - } - - /* - * verify we dont jam up on many uninterrupted - * solicitations - */ - if(ca_read_access(chix4)){ - dbr_float_t temp; - - printf("Performing multiple get test..."); - fflush(stdout); - for(i=0; i<10000; i++){ - SEVCHK(ca_get(DBR_FLOAT, chix4, &temp),NULL); - } - SEVCHK(ca_pend_io(2000.0), NULL); - printf("done.\n"); - } - else{ - printf("Skipped multiple get test - no read access\n"); - } - - /* - * verify we dont jam up on many uninterrupted requests - */ - if(ca_write_access(chix4)){ - printf("Performing multiple put test..."); - fflush(stdout); - for(i=0; i<10000; i++){ - dbr_double_t fval = 3.3; - status = ca_put(DBR_DOUBLE, chix4, &fval); - SEVCHK(status, NULL); - } - SEVCHK(ca_pend_io(2000.0), NULL); - printf("done.\n"); - } - else{ - printf("Skipped multiple put test - no write access\n"); - } - - /* - * verify we dont jam up on many uninterrupted - * solicitations - */ - if(ca_read_access(chix1)){ - unsigned count=0u; - printf("Performing multiple get callback test..."); - fflush(stdout); - for(i=0; i<10000; i++){ - status = ca_array_get_callback( - DBR_FLOAT, 1, chix1, null_event, &count); - - SEVCHK(status, NULL); - } - SEVCHK(ca_flush_io(), NULL); - while (count<10000u) { - ca_pend_event(1.0); - printf("waiting..."); - fflush(stdout); - } - printf("done.\n"); - } - else{ - printf("Skipped multiple get cb test - no read access\n"); - } - - /* - * verify we dont jam up on many uninterrupted - * put callback solicitations - */ - if ( ca_write_access (chix1) && ca_v42_ok (chix1) ) { - unsigned count = 0u; - printf ( "Performing multiple put callback test..." ); - fflush ( stdout ); - for ( i=0; i<10000; i++ ) { - dbr_float_t fval = 3.3F; - status = ca_array_put_callback ( - DBR_FLOAT, 1, chix1, &fval, - null_event, &count ); + status = ca_clear_channel ( pChans[1].channel ); SEVCHK ( status, NULL ); } - SEVCHK ( ca_flush_io (), NULL ); - while ( count < 10000u ) { - ca_pend_event ( 1.0 ); - printf ( "waiting..." ); - fflush ( stdout ); - } - - printf ( "done.\n" ); - } - else { - printf ( "Skipped multiple put cb test - no write access\n" ); - } - - /* - * verify that we detect that a large string has been written - */ - if ( ca_write_access (chix1) ) { - dbr_string_t stimStr; - dbr_string_t respStr; - memset(stimStr, 'a', sizeof(stimStr)); - status = ca_array_put(DBR_STRING, 1u, chix1, stimStr); - assert(status!=ECA_NORMAL); - sprintf(stimStr, "%u", 8u); - status = ca_array_put(DBR_STRING, 1u, chix1, stimStr); - assert(status==ECA_NORMAL); - status = ca_array_get(DBR_STRING, 1u, chix1, respStr); - assert(status==ECA_NORMAL); - status = ca_pend_io(0.0); - assert(status==ECA_NORMAL); - printf( -"Test fails if stim \"%s\" isnt roughly equiv to resp \"%s\"\n", - stimStr, respStr); - } - else{ - printf("Skipped bad string test - no write access\n"); - } - - if(ca_v42_ok(chix1)){ - test_sync_groups(chix1); - } - - /* performMonitorUpdateTest (chix4); */ - performDeleteTest (chix2); - - if (VALID_DB_REQ(ca_field_type(chix4))) { - status = ca_add_event( - DBR_FLOAT, - chix4, - EVENT_ROUTINE, - &monCount, - &monix); - SEVCHK(status, NULL); - SEVCHK(ca_clear_event(monix), NULL); - status = ca_add_event( - DBR_FLOAT, - chix4, - EVENT_ROUTINE, - &monCount, - &monix); - SEVCHK(status, NULL); - } - if (VALID_DB_REQ(ca_field_type(chix4))) { - status = ca_add_event( - DBR_FLOAT, - chix4, - EVENT_ROUTINE, - &monCount, - &monix); - SEVCHK(status, NULL); - SEVCHK(ca_clear_event(monix), NULL); - } - if (VALID_DB_REQ(ca_field_type(chix3))) { - status = ca_add_event( - DBR_FLOAT, - chix3, - EVENT_ROUTINE, - &monCount, - &monix); - SEVCHK(status, NULL); - status = ca_add_event( - DBR_FLOAT, - chix3, - write_event, - &monCount, - &monix); - SEVCHK(status, NULL); - } - - pfloat = (dbr_float_t *) calloc(sizeof(*pfloat),NUM); - assert (pfloat); - pdouble = (dbr_double_t *) calloc(sizeof(*pdouble),NUM); - assert (pdouble); - pgrfloat = (struct dbr_gr_float *) calloc(sizeof(*pgrfloat),NUM); - assert (pgrfloat); - - if (VALID_DB_REQ(ca_field_type(chix1))) { - for (i = 0; i < NUM; i++) { - for (j = 0; j < NUM; j++) - sprintf(&pstring[j][0], "%ld", j + 100l); - SEVCHK(ca_array_put( - DBR_STRING, - NUM, - chix1, - pstring), - NULL) - SEVCHK(ca_array_get( - DBR_FLOAT, - NUM, - chix1, - pfloat), - NULL) - SEVCHK(ca_array_get( - DBR_DOUBLE, - NUM, - chix1, - pdouble), - NULL) - SEVCHK(ca_array_get( - DBR_GR_FLOAT, - NUM, - chix1, - pgrfloat), - NULL) + else { + assert ( ca_state( pChans[0].channel ) == cs_conn ); } } - SEVCHK(ca_pend_io(4000.0), NULL); - - /* - * array test - * o verifies that we can at least write and read back the same array - * if multiple elements are present - */ - if (VALID_DB_REQ(ca_field_type(chix1))) { - if (ca_element_count(chix1)>1u && ca_read_access(chix1)) { - dbr_float_t *pRF, *pWF, *pEF, *pT1, *pT2; - - printf("Performing %lu element array test...", - ca_element_count(chix1) ); - fflush(stdout); - - pRF = (dbr_float_t *) calloc(ca_element_count(chix1), - sizeof(*pRF)); - assert(pRF!=NULL); - - pWF = (dbr_float_t *)calloc(ca_element_count(chix1), - sizeof(*pWF)); - assert(pWF!=NULL); - - /* - * write some random numbers into the array - */ - if (ca_write_access(chix1)) { - pT1 = pWF; - pEF = &pWF[ca_element_count(chix1)]; - while(pT1 epsilon) { - printf ("float test failed val written %f\n", fval); - printf ("float test failed val read %f\n", fretval); - assert(0); - } - - fval += increment; - } -} - -/* - * doubleTest () - */ -void doubleTest( -chid chan, -dbr_double_t beginValue, -dbr_double_t increment, -dbr_double_t epsilon, -unsigned iterations) -{ - unsigned i; - dbr_double_t fval; - dbr_double_t fretval; - int status; - - fval = beginValue; - for (i=0; i epsilon) { - printf ("float test failed val written %f\n", fval); - printf ("float test failed val read %f\n", fretval); - assert(0); - } - - fval += increment; - } -} - -/* - * null_event () - */ -void null_event (struct event_handler_args args) -{ - unsigned *pInc = (unsigned *) args.usr; - - /* - * no pend event in event call back test - */ -#if 0 - int status; - status = ca_pend_event (1e-6); - assert (status==ECA_EVDISALLOW); -#endif - - if (pInc) { - (*pInc)++; - } - -#if 0 - if (ca_state(args.chid)==cs_conn) { - status = ca_put (DBR_FLOAT, args.chid, &fval); - SEVCHK(status, "put failed in null_event()"); - } - else { - printf("null_event() called for disconnected %s\n", - ca_name(args.chid)); - } -#endif -} - -/* - * write_event () - */ -void write_event (struct event_handler_args args) -{ - static unsigned iterations = 100; - int status; - dbr_float_t *pFloat = (dbr_float_t *) args.dbr; - dbr_float_t a; - - if ( ! args.dbr ) { - return; - } - - if ( iterations > 0 ) { - iterations--; - - a = *pFloat; - a += 10.1F; - - status = ca_array_put ( DBR_FLOAT, 1, args.chid, &a); - SEVCHK ( status, NULL ); - SEVCHK ( ca_flush_io (), NULL ); - } -} - -void conn (struct connection_handler_args args) +void verifyClear ( appChan *pChans ) { int status; - if (args.op == CA_OP_CONN_UP) { -# if 0 - printf("Channel On Line [%s]\n", ca_name(args.chid)); -# endif - status = ca_get_callback (DBR_GR_FLOAT, args.chid, get_cb, NULL); - SEVCHK (status, "get call back in connection handler"); - status = ca_flush_io (); - SEVCHK (status, "get call back flush in connection handler"); - } - else if (args.op == CA_OP_CONN_DOWN) { -# if 0 - printf("Channel Off Line [%s]\n", ca_name(args.chid)); -# endif - } - else { - printf("Ukn conn ev\n"); - } + /* + * verify channel clear before connect + */ + status = ca_search ( pChans[0].name, &pChans[0].channel ); + SEVCHK ( status, NULL ); -} + status = ca_clear_channel ( pChans[0].channel ); + SEVCHK ( status, NULL ); -void get_cb (struct event_handler_args args) -{ - if ( ! ( args.status & CA_M_SUCCESS ) ) { - printf("Get cb failed because \"%s\"\n", - ca_message (args.status) ); - } - else { - conn_cb_count++; - } -} + /* + * verify subscription clear before connect + * and verify that NULL evid does not cause failure + */ + status = ca_search ( pChans[0].name, &pChans[0].channel ); + SEVCHK ( status, NULL ); -/* - * test_sync_groups() - */ -void test_sync_groups(chid chix) -{ - int status; - CA_SYNC_GID gid1=0; - CA_SYNC_GID gid2=0; + SEVCHK ( status, NULL ); + status = ca_add_event ( DBR_GR_DOUBLE, + pChans[0].channel, subscriptionStateChange, NULL, NULL ); + SEVCHK ( status, NULL ); - printf ("Performing sync group test..."); - fflush (stdout); - - status = ca_sg_create (&gid1); - SEVCHK (status, NULL); - - multiple_sg_requests (chix, gid1); - status = ca_sg_reset (gid1); - SEVCHK (status, NULL); - - status = ca_sg_create (&gid2); - SEVCHK (status, NULL); - - multiple_sg_requests (chix, gid2); - multiple_sg_requests (chix, gid1); - status = ca_sg_test (gid2); - SEVCHK (status, "SYNC GRP2"); - status = ca_sg_test (gid1); - SEVCHK (status, "SYNC GRP1"); - status = ca_sg_block (gid1, 500.0); - SEVCHK (status, "SYNC GRP1"); - status = ca_sg_block (gid2, 500.0); - SEVCHK (status, "SYNC GRP2"); - - status = ca_sg_delete (gid2); - SEVCHK (status, NULL); - status = ca_sg_create (&gid2); - SEVCHK (status, NULL); - - multiple_sg_requests (chix, gid1); - multiple_sg_requests (chix, gid2); - status = ca_sg_block (gid1, 15.0); - SEVCHK (status, "SYNC GRP1"); - status = ca_sg_block (gid2, 15.0); - SEVCHK (status, "SYNC GRP2"); - status = ca_sg_delete (gid1); - SEVCHK (status, NULL); - status = ca_sg_delete (gid2); - SEVCHK (status, NULL); - - printf ("done\n"); -} - -/* - * multiple_sg_requests() - */ -void multiple_sg_requests(chid chix, CA_SYNC_GID gid) -{ - int status; - unsigned i; - static dbr_float_t fvalput = 3.3F; - static dbr_float_t fvalget; - - for(i=0; i<1000; i++){ - if(ca_write_access(chix)){ - status = ca_sg_array_put( - gid, - DBR_FLOAT, - 1, - chix, - &fvalput); - SEVCHK(status, NULL); - } - - if(ca_read_access(chix)){ - status = ca_sg_array_get( - gid, - DBR_FLOAT, - 1, - chix, - &fvalget); - SEVCHK(status, NULL); - } - } -} - -/* - * accessSecurity_cb() - */ -void accessSecurity_cb(struct access_rights_handler_args args) -{ -# ifdef DEBUG - printf( "%s on %s has %s/%s access\n", - ca_name(args.chid), - ca_host_name(args.chid), - ca_read_access(args.chid)?"read":"noread", - ca_write_access(args.chid)?"write":"nowrite"); -# endif + status = ca_clear_channel ( pChans[0].channel ); + SEVCHK ( status, NULL ); + showProgress (); } /* * performGrEnumTest */ -void performGrEnumTest (chid chan) +void performGrEnumTest ( chid chan ) { struct dbr_gr_enum ge; unsigned count; @@ -1238,6 +479,7 @@ void performGrEnumTest (chid chan) } printf ("}\n"); } + showProgress (); } /* @@ -1254,10 +496,12 @@ void performCtrlDoubleTest (chid chan) unsigned i; if (!ca_write_access(chan)) { + printf ("skipped ctrl dbl test - no write access\n"); return; } if (dbr_value_class[ca_field_type(chan)]!=dbr_class_float) { + printf ("skipped ctrl dbl test - not an analog type\n"); return; } @@ -1304,8 +548,859 @@ void performCtrlDoubleTest (chid chan) free (pCtrlDbl); free (pDbl); + showProgress (); } +/* + * ca_pend_io() must block + */ +void verifyBlockInPendIO ( chid chan ) +{ + int status; + + if ( ca_read_access (chan) ) { + dbr_float_t req; + dbr_float_t resp; + + req = 56.57f; + resp = -99.99f; + SEVCHK ( ca_put (DBR_FLOAT, chan, &req), NULL ); + SEVCHK ( ca_get (DBR_FLOAT, chan, &resp), NULL ); + status = ca_pend_io (1.0e-12); + if ( status == ECA_NORMAL ) { + if ( resp != req ) { + printf ( + "get block test failed - val written %f\n", req ); + printf ( + "get block test failed - val read %f\n", resp ); + assert ( 0 ); + } + } + else if ( resp != -99.99f ) { + printf ( "CA didnt block for get to return?\n" ); + } + + req = 33.44f; + resp = -99.99f; + SEVCHK ( ca_put (DBR_FLOAT, chan, &req), NULL ); + SEVCHK ( ca_get (DBR_FLOAT, chan, &resp), NULL ); + SEVCHK ( ca_pend_io (2000.0) , NULL ); + if ( resp != req ) { + printf ( + "get block test failed - val written %f\n", req); + printf ( + "get block test failed - val read %f\n", resp); + assert(0); + } + } + else { + printf ("skipped pend IO block test - no read access\n"); + } + showProgress (); +} + +/* + * floatTest () + */ +void floatTest ( chid chan, dbr_float_t beginValue, dbr_float_t increment, + dbr_float_t epsilon, unsigned iterations ) +{ + unsigned i; + dbr_float_t fval; + dbr_float_t fretval; + int status; + + fval = beginValue; + for ( i=0; i < iterations; i++ ) { + fretval = FLT_MAX; + status = ca_put ( DBR_FLOAT, chan, &fval ); + SEVCHK ( status, NULL ); + status = ca_get ( DBR_FLOAT, chan, &fretval ); + SEVCHK ( status, NULL ); + status = ca_pend_io ( 10.0 ); + SEVCHK (status, NULL); + if ( fabs ( fval - fretval ) > epsilon ) { + printf ( "float test failed val written %f\n", fval ); + printf ( "float test failed val read %f\n", fretval ); + assert (0); + } + fval += increment; + } +} + +/* + * doubleTest () + */ +void doubleTest ( chid chan, dbr_double_t beginValue, + dbr_double_t increment, dbr_double_t epsilon, + unsigned iterations) +{ + unsigned i; + dbr_double_t fval; + dbr_double_t fretval; + int status; + + fval = beginValue; + for ( i = 0; i < iterations; i++ ) { + fretval = DBL_MAX; + status = ca_put ( DBR_DOUBLE, chan, &fval ); + SEVCHK ( status, NULL ); + status = ca_get ( DBR_DOUBLE, chan, &fretval ); + SEVCHK ( status, NULL ); + status = ca_pend_io ( 100.0 ); + SEVCHK ( status, NULL ); + if ( fabs ( fval - fretval ) > epsilon ) { + printf ( "float test failed val written %f\n", fval ); + printf ( "float test failed val read %f\n", fretval ); + assert ( 0 ); + } + fval += increment; + } +} + +/* + * Verify that we can write and then read back + * the same analog value + */ +void verifyAnalogIO ( chid chan, int dataType, double min, double max, + int minExp, int maxExp, double epsilon ) +{ + int i; + double incr; + double epsil; + double base; + unsigned iter; + + if ( ! ca_write_access ( chan ) ) { + printf ("skipped analog test - no write access\n"); + return; + } + + if ( ca_field_type ( chan ) != DBR_FLOAT && + ca_field_type ( chan ) != DBR_DOUBLE ) { + printf ("skipped analog test - not an analog type\n"); + return; + } + + epsil = epsilon * 4.0; + base = min; + for ( i = minExp; i <= maxExp; i += maxExp / 10 ) { + incr = ldexp ( 0.5, i ); + if ( fabs (incr) > max /10.0 ) { + iter = ( unsigned ) ( max / fabs (incr) ); + } + else { + iter = 10u; + } + if ( dataType == DBR_FLOAT ) { + floatTest ( chan, (dbr_float_t) base, (dbr_float_t) incr, + (dbr_float_t) epsil, iter ); + } + else if (dataType == DBR_DOUBLE ) { + doubleTest ( chan, (dbr_double_t) base, (dbr_double_t) incr, + (dbr_double_t) epsil, iter ); + } + else { + assert ( 0 ); + } + } + base = max; + for ( i = minExp; i <= maxExp; i += maxExp / 10 ) { + incr = - ldexp ( 0.5, i ); + if ( fabs (incr) > max / 10.0 ) { + iter = (unsigned) ( max / fabs (incr) ); + } + else { + iter = 10u; + } + if ( dataType == DBR_FLOAT ) { + floatTest ( chan, (dbr_float_t) base, (dbr_float_t) incr, + (dbr_float_t) epsil, iter ); + } + else if (dataType == DBR_DOUBLE ) { + doubleTest ( chan, (dbr_double_t) base, (dbr_double_t) incr, + (dbr_double_t) epsil, iter ); + } + else { + assert ( 0 ); + } + } + base = - max; + for ( i = minExp; i <= maxExp; i += maxExp / 10 ) { + incr = ldexp ( 0.5, i ); + if ( fabs (incr) > max / 10.0 ) { + iter = (unsigned) ( max / fabs ( incr ) ); + } + else { + iter = 10l; + } + if ( dataType == DBR_FLOAT ) { + floatTest ( chan, (dbr_float_t) base, (dbr_float_t) incr, + (dbr_float_t) epsil, iter ); + } + else if (dataType == DBR_DOUBLE ) { + doubleTest ( chan, (dbr_double_t) base, (dbr_double_t) incr, + (dbr_double_t) epsil, iter ); + } + else { + assert ( 0 ); + } + } + showProgress (); +} + +/* + * Verify that we can write and then read back + * the same DBR_LONG value + */ +void verifyLongIO ( chid chan ) +{ + int status; + + dbr_long_t iter, rdbk, incr; + struct dbr_ctrl_long cl; + + if ( ca_write_access ( chan ) ) { + return; + } + + status = ca_get ( DBR_CTRL_LONG, chan, &cl ); + SEVCHK ( status, "control long fetch failed\n" ); + status = ca_pend_io ( 10.0 ); + SEVCHK ( status, "control long pend failed\n" ); + + incr = ( cl.upper_ctrl_limit - cl.lower_ctrl_limit ); + if ( incr >= 1 ) { + incr /= 1000; + if ( incr == 0 ) { + incr = 1; + } + for ( iter = cl.lower_ctrl_limit; + iter <= cl.upper_ctrl_limit; iter+=incr ) { + + status = ca_put ( DBR_LONG, chan, &iter ); + status = ca_get ( DBR_LONG, chan, &rdbk ); + status = ca_pend_io ( 10.0 ); + SEVCHK ( status, "get pend failed\n" ); + assert ( iter == rdbk ); + } + showProgress (); + } + else { + printf ( "strange limits configured for channel \"%s\"\n", ca_name (chan) ); + } +} + +/* + * Verify that we can write and then read back + * the same DBR_SHORT value + */ +void verifyShortIO ( chid chan ) +{ + int status; + + dbr_short_t iter, rdbk, incr; + struct dbr_ctrl_short cl; + + if ( ca_write_access ( chan ) ) { + return; + } + + status = ca_get ( DBR_CTRL_SHORT, chan, &cl ); + SEVCHK ( status, "control short fetch failed\n" ); + status = ca_pend_io ( 10.0 ); + SEVCHK ( status, "control short pend failed\n" ); + + incr = ( cl.upper_ctrl_limit - cl.lower_ctrl_limit ); + if ( incr >= 1 ) { + incr /= 1000; + if ( incr == 0 ) { + incr = 1; + } + for ( iter = cl.lower_ctrl_limit; + iter <= cl.upper_ctrl_limit; iter+=incr ) { + + status = ca_put ( DBR_SHORT, chan, &iter ); + status = ca_get ( DBR_SHORT, chan, &rdbk ); + status = ca_pend_io ( 10.0 ); + SEVCHK ( status, "get pend failed\n" ); + assert ( iter == rdbk ); + } + showProgress (); + } + else { + printf ( "Strange limits configured for channel \"%s\"\n", ca_name (chan) ); + } +} + +void verifyHighThroughputRead ( chid chan ) +{ + int status; + unsigned i; + + /* + * verify we dont jam up on many uninterrupted + * solicitations + */ + if ( ca_read_access (chan) ) { + dbr_float_t temp; + for ( i=0; i<10000; i++ ) { + status = ca_get ( DBR_FLOAT, chan, &temp ); + SEVCHK ( status ,NULL ); + } + status = ca_pend_io (2000.0); + SEVCHK ( status, NULL ); + showProgress (); + } + else { + printf ( "Skipped highthroughput read test - no read access\n" ); + } +} + +void verifyHighThroughputWrite ( chid chan ) +{ + int status; + unsigned i; + + if (ca_write_access ( chan ) ) { + for ( i=0; i<10000; i++ ) { + dbr_double_t fval = 3.3; + status = ca_put ( DBR_DOUBLE, chan, &fval ); + SEVCHK ( status, NULL ); + } + SEVCHK ( ca_pend_io (2000.0), NULL ); + showProgress (); + } + else{ + printf("Skipped multiple put test - no write access\n"); + } +} + +/* + * verify we dont jam up on many uninterrupted + * get callback requests + */ +void verifyHighThroughputReadCallback ( chid chan ) +{ + unsigned i; + int status; + + if ( ca_read_access ( chan ) ) { + unsigned count = 0u; + for ( i=0; i<10000; i++ ) { + status = ca_array_get_callback ( + DBR_FLOAT, 1, chan, nUpdatesTester, &count ); + SEVCHK ( status, NULL ); + } + SEVCHK ( ca_flush_io (), NULL ); + while ( count < 10000u ) { + ca_pend_event ( 0.1 ); + } + showProgress (); + } + else { + printf ( "Skipped multiple get cb test - no read access\n" ); + } +} + +/* + * verify we dont jam up on many uninterrupted + * put callback request + */ +void verifyHighThroughputWriteCallback ( chid chan ) +{ + unsigned i; + int status; + + if ( ca_write_access (chan) && ca_v42_ok (chan) ) { + unsigned count = 0u; + for ( i=0; i<10000; i++ ) { + dbr_float_t fval = 3.3F; + status = ca_array_put_callback ( + DBR_FLOAT, 1, chan, &fval, + nUpdatesTester, &count ); + SEVCHK ( status, NULL ); + } + SEVCHK ( ca_flush_io (), NULL ); + while ( count < 10000u ) { + ca_pend_event ( 0.1 ); + } + showProgress (); + } + else { + printf ( "Skipped multiple put cb test - no write access\n" ); + } +} + +void verifyBadString ( chid chan ) +{ + int status; + + /* + * verify that we detect that a large string has been written + */ + if ( ca_write_access (chan) ) { + dbr_string_t stimStr; + dbr_string_t respStr; + memset (stimStr, 'a', sizeof (stimStr) ); + status = ca_array_put ( DBR_STRING, 1u, chan, stimStr ); + assert ( status != ECA_NORMAL ); + sprintf ( stimStr, "%u", 8u ); + status = ca_array_put ( DBR_STRING, 1u, chan, stimStr ); + assert ( status == ECA_NORMAL ); + status = ca_array_get ( DBR_STRING, 1u, chan, respStr ); + assert ( status == ECA_NORMAL ); + status = ca_pend_io ( 0.0 ); + assert ( status == ECA_NORMAL ); + printf ( +"Test fails if stim \"%s\" isnt roughly equiv to resp \"%s\"\n", + stimStr, respStr); + showProgress (); + } + else { + printf ( "Skipped bad string test - no write access\n" ); + } +} + +/* + * multiple_sg_requests() + */ +void multiple_sg_requests ( chid chix, CA_SYNC_GID gid ) +{ + int status; + unsigned i; + static dbr_float_t fvalput = 3.3F; + static dbr_float_t fvalget; + + for ( i=0; i < 1000; i++ ) { + if ( ca_write_access (chix) ){ + status = ca_sg_array_put ( gid, DBR_FLOAT, 1, + chix, &fvalput); + SEVCHK ( status, NULL ); + } + + if ( ca_read_access (chix) ) { + status = ca_sg_array_get ( gid, DBR_FLOAT, 1, + chix, &fvalget); + SEVCHK ( status, NULL ); + } + } +} + +/* + * test_sync_groups() + */ +void test_sync_groups ( chid chan ) +{ + int status; + CA_SYNC_GID gid1=0; + CA_SYNC_GID gid2=0; + + if ( ! ca_v42_ok ( chan ) ) { + printf ( "skipping sycnc group test - serveris on wron version\n" ); + } + + status = ca_sg_create ( &gid1 ); + SEVCHK ( status, NULL ); + + multiple_sg_requests ( chan, gid1 ); + status = ca_sg_reset ( gid1 ); + SEVCHK ( status, NULL ); + + status = ca_sg_create ( &gid2 ); + SEVCHK ( status, NULL ); + + multiple_sg_requests ( chan, gid2 ); + multiple_sg_requests ( chan, gid1 ); + status = ca_sg_test ( gid2 ); + SEVCHK ( status, "SYNC GRP2" ); + status = ca_sg_test ( gid1 ); + SEVCHK ( status, "SYNC GRP1" ); + status = ca_sg_block ( gid1, 500.0 ); + SEVCHK ( status, "SYNC GRP1" ); + status = ca_sg_block ( gid2, 500.0 ); + SEVCHK ( status, "SYNC GRP2" ); + + status = ca_sg_delete ( gid2 ); + SEVCHK (status, NULL); + status = ca_sg_create ( &gid2 ); + SEVCHK (status, NULL); + + multiple_sg_requests ( chan, gid1 ); + multiple_sg_requests ( chan, gid2 ); + status = ca_sg_block ( gid1, 15.0 ); + SEVCHK ( status, "SYNC GRP1" ); + status = ca_sg_block ( gid2, 15.0 ); + SEVCHK ( status, "SYNC GRP2" ); + status = ca_sg_delete ( gid1 ); + SEVCHK ( status, NULL ); + status = ca_sg_delete ( gid2 ); + SEVCHK ( status, NULL ); + + showProgress (); +} + +/* + * performDeleteTest + * + * 1) verify we can add many monitors at once + * 2) verify that under heavy load the last monitor + * returned is the last modification sent + */ +void performDeleteTest ( chid chan ) +{ + unsigned count = 0u; + evid mid[1000]; + dbr_float_t temp, getResp; + unsigned i; + + for ( i=0; i < NELEMENTS (mid); i++ ) { + SEVCHK ( ca_add_event ( DBR_GR_FLOAT, chan, noopSubscriptionStateChange, + &count, &mid[i]) , NULL ); + } + + showProgress (); + + /* + * force all of the monitors subscription requests to + * complete + * + * NOTE: this hopefully demonstrates that when the + * server is very busy with monitors the client + * is still able to punch through with a request. + */ + SEVCHK ( ca_get ( DBR_FLOAT,chan,&getResp ), NULL ); + SEVCHK ( ca_pend_io ( 1000.0 ), NULL ); + + showProgress (); + + /* + * attempt to generate heavy event traffic before initiating + * the monitor delete + */ + if ( ca_write_access (chan) ) { + for ( i=0; i < 10; i++ ) { + temp = (float) i; + SEVCHK ( ca_put (DBR_FLOAT, chan, &temp), NULL); + } + } + + showProgress (); + + /* + * without pausing begin deleting the event suvbscriptions + * while the queue is full + */ + for ( i=0; i < NELEMENTS (mid); i++ ) { + SEVCHK ( ca_clear_event ( mid[i]), NULL ); + } + + showProgress (); + + /* + * force all of the clear event requests to + * complete + */ + SEVCHK ( ca_get (DBR_FLOAT,chan,&temp), NULL ); + SEVCHK ( ca_pend_io (1000.0), NULL ); + + showProgress (); +} + +void eventClearTest ( chid chan ) +{ + int status; + evid monix1, monix2, monix3; + + status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, + NULL, &monix1 ); + SEVCHK ( status, NULL ); + + status = ca_clear_event ( monix1 ); + SEVCHK ( status, NULL ); + + status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, + NULL, &monix1 ); + SEVCHK ( status, NULL ); + + status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, + NULL, &monix2); + SEVCHK (status, NULL); + + status = ca_clear_event ( monix2 ); + SEVCHK ( status, NULL); + + status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, + NULL, &monix2); + SEVCHK ( status, NULL ); + + status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, + NULL, &monix3); + SEVCHK ( status, NULL ); + + status = ca_clear_event ( monix2 ); + SEVCHK ( status, NULL); + + status = ca_clear_event ( monix1 ); + SEVCHK ( status, NULL); + + status = ca_clear_event ( monix3 ); + SEVCHK ( status, NULL); +} + +/* + * array test + * + * verify that we can at least write and read back the same array + * if multiple elements are present + */ +void arrayTest ( chid chan ) +{ + dbr_double_t *pRF, *pWF; + unsigned i; + int status; + + if ( ! ca_write_access ( chan ) ) { + printf ( "skipping array test - no write access\n" ); + } + + pRF = (dbr_double_t *) calloc ( ca_element_count (chan), sizeof (*pRF) ); + assert ( pRF != NULL ); + + pWF = (dbr_double_t *) calloc ( ca_element_count (chan), sizeof (*pWF) ); + assert ( pWF != NULL ); + + /* + * 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 ( DBR_DOUBLE, ca_element_count ( chan ), + chan, pWF ); + SEVCHK ( status, "array write request failed" ); + + /* + * read back the array + */ + status = ca_array_get ( DBR_DOUBLE, ca_element_count (chan), + chan, pRF ); + SEVCHK ( status, "array read request failed" ); + status = ca_pend_io ( 30.0 ); + SEVCHK ( status, "array read failed" ); + + /* + * verify read response matches values written + */ + for ( i = 0; i < ca_element_count (chan); i++ ) { + assert ( pWF[i] == pRF[i] ); + } + + /* + * read back the array as strings + */ + { + /* clip to 16k message buffer limit */ + unsigned maxElem = ( ( 1 << 14 ) - 16 ) / MAX_STRING_SIZE; + unsigned nElem = min ( maxElem, ca_element_count (chan) ); + char *pRS = malloc ( nElem * MAX_STRING_SIZE ); + + assert (pRS); + status = ca_array_get ( DBR_STRING, nElem, chan, pRS ); + SEVCHK ( status, "array read request failed" ); + status = ca_pend_io ( 30.0 ); + SEVCHK ( status, "array read failed" ); + free ( pRS ); + } + + free ( pRF ); + free ( pWF ); + + showProgress (); +} + +/* + * pend_event_delay_test() + */ +void pend_event_delay_test(dbr_double_t request) +{ + int status; + TS_STAMP end_time; + TS_STAMP start_time; + dbr_double_t delay; + dbr_double_t accuracy; + + tsStampGetCurrent(&start_time); + status = ca_pend_event(request); + if (status != ECA_TIMEOUT) { + SEVCHK(status, NULL); + } + tsStampGetCurrent(&end_time); + delay = tsStampDiffInSeconds(&end_time,&start_time); + accuracy = 100.0*(delay-request)/request; + printf("CA pend event delay = %f sec results in error = %f %%\n", + request, accuracy); + assert (fabs(accuracy) < 10.0); +} + +caTaskExistTest () +{ + int status; + + TS_STAMP end_time; + TS_STAMP start_time; + dbr_double_t delay; + + tsStampGetCurrent ( &start_time ); + printf ( "entering ca_task_exit()\n" ); + status = ca_task_exit (); + SEVCHK ( status, NULL ); + tsStampGetCurrent ( &end_time ); + delay = tsStampDiffInSeconds ( &end_time, &start_time ); + printf ( "in ca_task_exit() for %f sec\n", delay ); +} + +int acctst ( char *pName, unsigned channelCount, unsigned repetitionCount ) +{ + chid chan; + int status; + unsigned i; + appChan *pChans; + + printf ( "CA Client V%s, channel name \"%s\"\n", ca_version (), pName ); + + pChans = calloc ( channelCount, sizeof ( *pChans ) ); + assert ( pChans ); + + for ( i = 0; i < channelCount; i++ ) { + strncpy ( pChans[ i ].name, pName, sizeof ( pChans[ i ].name ) ); + pChans[ i ].name[ sizeof ( pChans[i].name ) - 1 ] = '\0'; + } + + verifyBlockingConnect ( pChans, channelCount, repetitionCount ); + verifyConnectionHandlerConnect ( pChans, channelCount, repetitionCount ); + verifyClear ( pChans ); + + status = ca_search ( pName, &chan ); + SEVCHK ( status, NULL ); + status = ca_pend_io ( 100.0 ); + SEVCHK ( status, NULL ); + + monitorSubscriptionFirstUpdateTest ( chan ); + performGrEnumTest ( chan ); + performCtrlDoubleTest ( chan ); + verifyBlockInPendIO ( chan ); + verifyAnalogIO ( chan, DBR_FLOAT, FLT_MIN, FLT_MAX, + FLT_MIN_EXP, FLT_MAX_EXP, FLT_EPSILON ); + verifyAnalogIO ( chan, DBR_DOUBLE, DBL_MIN, DBL_MAX, + DBL_MIN_EXP, DBL_MAX_EXP, DBL_EPSILON ); + verifyLongIO ( chan ); + verifyShortIO ( chan ); + verifyHighThroughputRead ( chan ); + verifyHighThroughputWrite ( chan ); + verifyHighThroughputReadCallback ( chan ); + verifyHighThroughputWriteCallback ( chan ); + verifyBadString ( chan ); + test_sync_groups ( chan ); + /* performMonitorUpdateTest ( chan ); */ + performDeleteTest ( chan ); + eventClearTest ( chan ); + arrayTest ( chan ); + + /* + * Verify that we can do IO with the new types for ALH + */ +#if 0 + if ( ca_read_access (chan) && ca_write_access (chan) ) { + { + dbr_put_ackt_t acktIn = 1u; + dbr_put_acks_t acksIn = 1u; + struct dbr_stsack_string stsackOut; + + SEVCHK ( ca_put ( DBR_PUT_ACKT, chan, &acktIn ), NULL ); + SEVCHK ( ca_put ( DBR_PUT_ACKS, chan, &acksIn ), NULL ); + SEVCHK ( ca_get ( DBR_STSACK_STRING, chan, &stsackOut ), NULL ); + SEVCHK ( ca_pend_io ( 2000.0 ), NULL ); + } + + { + TS_STAMP end_time; + TS_STAMP start_time; + dbr_double_t delay; + dbr_double_t request = 15.0; + dbr_double_t accuracy; + + tsStampGetCurrent(&start_time); + printf ("waiting for events for %f sec\n", request); + status = ca_pend_event (request); + if ( status != ECA_TIMEOUT ) { + SEVCHK ( status, NULL ); + } + tsStampGetCurrent ( &end_time ); + delay = tsStampDiffInSeconds ( &end_time, &start_time ); + accuracy = 100.0 * ( delay - request ) / request; + printf ( "CA pend event delay accuracy = %f %%\n", accuracy ); + } +#endif + + /* + * CA pend event delay accuracy test + * (CA asssumes that search requests can be sent + * at least every 25 mS on all supported os) + */ + pend_event_delay_test ( 1.0 ); + pend_event_delay_test ( 0.1 ); + pend_event_delay_test ( 0.25 ); + + caTaskExistTest (); + + printf ( "\nTest Complete\n" ); + + return 0; +} + +/* + * write_event () + */ +void write_event (struct event_handler_args args) +{ + static unsigned iterations = 100; + int status; + dbr_float_t *pFloat = (dbr_float_t *) args.dbr; + dbr_float_t a; + + if ( ! args.dbr ) { + return; + } + + if ( iterations > 0 ) { + iterations--; + + a = *pFloat; + a += 10.1F; + + status = ca_array_put ( DBR_FLOAT, 1, args.chid, &a); + SEVCHK ( status, NULL ); + SEVCHK ( ca_flush_io (), NULL ); + } +} + +/* + * accessSecurity_cb() + */ +void accessSecurity_cb(struct access_rights_handler_args args) +{ +# ifdef DEBUG + printf( "%s on %s has %s/%s access\n", + ca_name(args.chid), + ca_host_name(args.chid), + ca_read_access(args.chid)?"read":"noread", + ca_write_access(args.chid)?"write":"nowrite"); +# endif +} + + typedef struct { evid id; dbr_float_t lastValue; @@ -1427,74 +1522,5 @@ void performMonitorUpdateTest ( chid chan ) printf ("flow control bypassed %u events\n", flowCtrlCount); } -/* - * performDeleteTest - * - * 1) verify we can add many monitors at once - * 2) verify that under heavy load the last monitor - * returned is the last modification sent - */ -void performDeleteTest (chid chan) -{ - unsigned count = 0u; - evid mid[1000]; - dbr_float_t temp, getResp; - unsigned i; - - printf ( "Performing event subscription delete test..." ); - fflush ( stdout ); - for ( i=0; i +#include #include "caDiagnostics.h" -int main(int argc, char **argv) +int main ( int argc, char **argv ) { - if(argc == 2){ - acctst(argv[1]); + unsigned channelCount; + unsigned repetitionCount; + + if ( argc < 2 || argc > 4 ) { + printf ( "usage: %s [channel count] [repetition count]\n", argv[0] ); } - else{ - printf("usage: %s \n", argv[0]); + + if ( argc >= 3 ) { + channelCount = atoi ( argv[2] ); } + else { + channelCount = 20000; + } + + if ( argc >= 4 ) { + repetitionCount = atoi ( argv[3] ); + } + else { + repetitionCount = 1; + } + + acctst ( argv[1], channelCount, repetitionCount ); + return 0; } diff --git a/src/ca/baseIIU_IL.h b/src/ca/baseIIU_IL.h new file mode 100644 index 000000000..e7bf4a251 --- /dev/null +++ b/src/ca/baseIIU_IL.h @@ -0,0 +1,22 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef baseIIU_ILh +#define baseIIU_ILh + + +#endif // baseIIU_ILh \ No newline at end of file diff --git a/src/ca/baseNMIU.cpp b/src/ca/baseNMIU.cpp index caa0cba2a..248b7cfa7 100644 --- a/src/ca/baseNMIU.cpp +++ b/src/ca/baseNMIU.cpp @@ -11,20 +11,21 @@ */ #include "iocinf.h" +#include "nciu_IL.h" -baseNMIU::baseNMIU ( nciu &chanIn ) : chan (chanIn) +baseNMIU::baseNMIU ( nciu &chanIn ) : chan ( chanIn ) { - this->chan.installIO ( *this ); -} - -void baseNMIU::destroy () -{ - delete this; + chanIn.ioInstall ( *this ); } baseNMIU::~baseNMIU () { - this->chan.uninstallIO ( *this ); + // private NOOP forces pool allocation +} + +void baseNMIU::destroy () +{ + this->chan.ioDestroy ( this->getId () ); } int baseNMIU::subscriptionMsg () diff --git a/src/ca/caDiagnostics.h b/src/ca/caDiagnostics.h index 19d6dbd2f..0623d35ed 100644 --- a/src/ca/caDiagnostics.h +++ b/src/ca/caDiagnostics.h @@ -1,9 +1,9 @@ enum appendNumberFlag {appendNumber, dontAppendNumber}; -int catime (char *channelName, unsigned channelCount, enum appendNumberFlag appNF); +int catime ( char *channelName, unsigned channelCount, enum appendNumberFlag appNF ); -int acctst (char *pname); +int acctst ( char *pname, unsigned channelCount, unsigned repititionCount ); #define CATIME_OK 0 #define CATIME_ERROR -1 diff --git a/src/ca/caProto.h b/src/ca/caProto.h index 79fcdaabf..e179af004 100644 --- a/src/ca/caProto.h +++ b/src/ca/caProto.h @@ -74,14 +74,18 @@ * 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) -/* 1492(min of ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP) joh 8-6-97 */ -#define ETHERNET_MAX_UDP (1482u-20u-8u) -#define MAX_UDP 1024u /* original MAX_UDP */ -#define MAX_TCP (MAX_UDP*16u) /* so waveforms fit */ -#define MAX_MSG_SIZE (MAX_TCP) /* the larger of tcp and udp max */ +#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) +/* + * 1500 (max of ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP) + * (the MTU of Ethernet is currently independent of speed varient) + */ +#define ETHERNET_MAX_UDP ( 1500u - 20u - 8u ) +#define MAX_UDP_RECV ( 0xffff + 16u ) /* allow large frames to be received in the future */ +#define MAX_UDP_SEND 1024u /* original MAX_UDP */ +#define MAX_TCP ( 1024 * 16u ) /* so waveforms fit */ +#define MAX_MSG_SIZE ( MAX_TCP ) /* the larger of tcp and udp max */ /* * architecture independent types @@ -118,7 +122,7 @@ typedef ca_uint32_t caResId; #define CA_PROTO_HOST_NAME 21u /* CA V4.1 identify client */ #define CA_PROTO_ACCESS_RIGHTS 22u /* CA V4.2 asynch access rights chg */ #define CA_PROTO_ECHO 23u /* CA V4.3 connection verify */ -#define REPEATER_REGISTER 24u /* registr for repeater fan out */ +#define REPEATER_REGISTER 24u /* register for repeater fan out */ #define CA_PROTO_SIGNAL 25u /* knock the server out of select */ #define CA_PROTO_CLAIM_CIU_FAILED 26u /* unable to create chan resource in server */ #define CA_PROTO_SERVER_DISCONN 27u /* server deletes PV (or channel) */ diff --git a/src/ca/caPutNotify.cpp b/src/ca/caPutNotify.cpp deleted file mode 100644 index 583c09938..000000000 --- a/src/ca/caPutNotify.cpp +++ /dev/null @@ -1,75 +0,0 @@ - - - - -/* - * caExtraEventLabor () - */ -LOCAL void caExtraEventLabor (void *pArg) -{ - cac *pcac = (cac *) pArg; - dbcaPutNotify *ppnb; - struct event_handler_args args; - - while (TRUE) { - semTakeStatus semStatus; - - /* - * independent lock used here in order to - * avoid any possibility of blocking - * the database (or indirectly blocking - * one client on another client). - */ - semStatus = semMutexTakeTimeout (pcac->localIIU.putNotifyLock, 60.0 /* sec */); - if (semStatus!=semTakeOK) { - ca_printf ("cac: put notify deadlock condition detected\n"); - (*pcac->localIIU.pdbAdapter->p_db_post_extra_labor) (pcac->localIIU.evctx); - break; - } - ppnb = (dbcaPutNotify *) ellGet (&pcac->localIIU.putNotifyQue); - semMutexGive (pcac->localIIU.putNotifyLock); - - /* - * break to loop exit - */ - if (!ppnb) { - break; - } - - /* - * setup arguments and call user's function - */ - args.usr = ppnb->caUserArg; - args.chid = ppnb->dbPutNotify.usrPvt; -#error type must be an external type - args.type = ppnb->dbPutNotify.dbrType; - args.count = ppnb->dbPutNotify.nRequest; - args.dbr = NULL; - if (ppnb->dbPutNotify.status) { - if (ppnb->dbPutNotify.status == S_db_Blocked) { - args.status = ECA_PUTCBINPROG; - } - else { - args.status = ECA_PUTFAIL; - } - } - else { - args.status = ECA_NORMAL; - } - - pcac->lock (); - (*ppnb->caUserCallback) (args); - pcac->unlock (); - - ppnb->busy = FALSE; - } - - /* - * wakeup the TCP thread if it is waiting for a cb to complete - */ - semBinaryGive (pcac->ca_blockSem); -} - - - - diff --git a/src/ca/cac.cpp b/src/ca/cac.cpp index 94c050f38..50e4029b8 100644 --- a/src/ca/cac.cpp +++ b/src/ca/cac.cpp @@ -39,11 +39,11 @@ static void cacInitRecursionLock ( void * dummy ) // cac::cac () // cac::cac ( bool enablePreemptiveCallbackIn ) : - beaconTable ( 1024 ), - endOfBCastList ( 0 ), + ipToAEngine ( "caIPAddrToAsciiEngine" ), ioTable ( 1024 ), chanTable ( 1024 ), sgTable ( 128 ), + beaconTable ( 1024 ), fdRegFunc ( 0 ), fdRegArg ( 0 ), pudpiiu ( 0 ), @@ -81,8 +81,7 @@ cac::cac ( bool enablePreemptiveCallbackIn ) : throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); } - ellInit (&this->putCvrtBuf); - this->ca_printf_func = errlogVprintf; + this->pVPrintfFunc = errlogVprintf; this->ca_exception_func = ca_default_exception_handler; this->ca_exception_arg = NULL; this->readSeq = 0u; @@ -103,44 +102,41 @@ cac::cac ( bool enablePreemptiveCallbackIn ) : if ( gunRet != osiGetUserNameSuccess ) { tmp[0] = '\0'; } - len = strlen (tmp) + 1; - this->ca_pUserName = (char *) malloc ( len ); - if ( ! this->ca_pUserName ) { + len = strlen ( tmp ) + 1; + this->pUserName = new char [len]; + if ( ! this->pUserName ) { semBinaryDestroy (this->ca_blockSem); throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); } - strncpy (this->ca_pUserName, tmp, len); + strncpy ( this->pUserName, tmp, len ); } this->programBeginTime = osiTime::getCurrent (); - status = envGetDoubleConfigParam (&EPICS_CA_CONN_TMO, &this->ca_connectTMO); - if (status) { - this->ca_connectTMO = CA_CONN_VERIFY_PERIOD; + status = envGetDoubleConfigParam ( &EPICS_CA_CONN_TMO, &this->connTMO ); + if ( status ) { + this->connTMO = CA_CONN_VERIFY_PERIOD; ca_printf ( - "EPICS \"%s\" float fetch failed\n", + "EPICS \"%s\" double fetch failed\n", EPICS_CA_CONN_TMO.name); ca_printf ( - "Setting \"%s\" = %f\n", + "Defaulting \"%s\" = %f\n", EPICS_CA_CONN_TMO.name, - this->ca_connectTMO); + this->connTMO); } - this->ca_server_port = - envGetInetPortConfigParam (&EPICS_CA_SERVER_PORT, CA_SERVER_PORT); - // - // unfortunately, this must be created her in the + // unfortunately, this must be created here in the // constructor, and not on demand (only when it is needed) // because the enable reference count must be // maintained whenever this object exists. // - this->pProcThread = new processThread ( this ); - if ( ! this->pProcThread ) { - throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + this->pRecvProcThread = new recvProcessThread ( this ); + if ( ! this->pRecvProcThread ) { + throwWithLocation ( caErrorCode ( ECA_ALLOCMEM ) ); } else if ( this->enablePreemptiveCallback ) { - // only after this->pProcThread is valid + // only after this->pRecvProcThread is valid this->enableCallbackPreemption (); } } @@ -170,19 +166,15 @@ cac::~cac () // make certain that process thread isnt deleting // tcpiiu objects at the same that this thread is // - delete this->pProcThread; + recvProcessThread *pTmp = this->pRecvProcThread; + this->pRecvProcThread = 0; + delete pTmp; // // shutdown all tcp connections and wait for threads to exit // this->iiuListMutex.lock (); - tsDLIterBD piiu ( this->iiuListIdle.first () ); - while ( piiu.valid () ) { - tsDLIterBD pnext = piiu.itemAfter (); - piiu->suicide (); - piiu = pnext; - } - piiu = this->iiuListRecvPending.first (); + tsDLIterBD piiu ( this->iiuList.first () ); while ( piiu.valid () ) { tsDLIterBD pnext = piiu.itemAfter (); piiu->suicide (); @@ -207,21 +199,17 @@ cac::~cac () delete this->pudpiiu; } - /* remove put convert block free list */ - ellFree ( &this->putCvrtBuf ); - - /* reclaim sync group resources */ - this->sgTable.destroyAllEntries (); - /* * free user name string */ - if ( this->ca_pUserName ) { - free ( this->ca_pUserName ); + if ( this->pUserName ) { + delete [] this->pUserName; } + this->sgTable.destroyAllEntries (); this->beaconTable.destroyAllEntries (); - + this->chanTable.destroyAllEntries (); + this->ioTable.destroyAllEntries (); semBinaryDestroy ( this->ca_blockSem ); osiSockRelease (); @@ -229,59 +217,18 @@ cac::~cac () delete this->pTimerQueue; } -void cac::safeDestroyNMIU (unsigned id) -{ - this->defaultMutex.lock (); - - baseNMIU *pIOBlock = this->ioTable.lookup (id); - if ( pIOBlock ) { - pIOBlock->destroy (); - } - - this->defaultMutex.unlock (); -} - void cac::processRecvBacklog () { - tcpiiu *piiu; + this->iiuListMutex.lock (); - while ( 1 ) { - int status; - unsigned bytesToProcess; + tsDLIterBD piiu ( this->iiuList.first () ); + while ( piiu.valid () ) { + tsDLIterBD pNext = piiu.itemAfter (); + piiu->processIncomingAndDestroySelfIfDisconnected (); + piiu = pNext; + } - this->iiuListMutex.lock (); - - piiu = this->iiuListRecvPending.get (); - if ( ! piiu ) { - this->iiuListMutex.unlock (); - break; - } - - piiu->recvPending = false; - this->iiuListIdle.add (*piiu); - - this->iiuListMutex.unlock (); - - if ( piiu->state == iiu_disconnected ) { - delete piiu; - continue; - } - - char *pProto = (char *) cacRingBufferReadReserveNoBlock - (&piiu->recv, &bytesToProcess); - while ( pProto ) { - status = piiu->post_msg (pProto, bytesToProcess); - if ( status == ECA_NORMAL ) { - cacRingBufferReadCommit (&piiu->recv, bytesToProcess); - cacRingBufferReadFlush (&piiu->recv); - } - else { - delete piiu; - } - pProto = (char *) cacRingBufferReadReserveNoBlock - (&piiu->recv, &bytesToProcess); - } - } + this->iiuListMutex.unlock (); } /* @@ -293,12 +240,7 @@ void cac::flush () * set the push pending flag on all virtual circuits */ this->iiuListMutex.lock (); - tsDLIterBD piiu ( this->iiuListIdle.first () ); - while ( piiu.valid () ) { - piiu->flush (); - piiu++; - } - piiu = this->iiuListRecvPending.first (); + tsDLIterBD piiu ( this->iiuList.first () ); while ( piiu.valid () ) { piiu->flush (); piiu++; @@ -317,69 +259,47 @@ void cac::flush () */ void cac::cleanUpPendIO () { - nciu *pchan; - this->defaultMutex.lock (); this->readSeq++; this->pndrecvcnt = 0u; - if ( this->pudpiiu ) { - tsDLIter iter ( this->pudpiiu->chidList ); - while ( ( pchan = iter () ) ) { - pchan->connectTimeoutNotify (); - } - } - this->defaultMutex.unlock (); + + if ( this->pudpiiu ) { + this->pudpiiu->connectTimeoutNotify (); + } } unsigned cac::connectionCount () const { - unsigned count; - - this->iiuListMutex.lock (); - count = this->iiuListIdle.count () + this->iiuListRecvPending.count (); - this->iiuListMutex.unlock (); - return count; + return this->iiuList.count (); } void cac::show (unsigned level) const { - this->defaultMutex.lock (); - if ( this->pudpiiu ) { this->pudpiiu->show (level); } this->iiuListMutex.lock (); - tsDLIterConstBD piiu ( this->iiuListIdle.first () ); - while ( piiu.valid () ) { - piiu->show (level); - piiu++; - } - - piiu = this->iiuListRecvPending.first (); + tsDLIterConstBD piiu ( this->iiuList.first () ); while ( piiu.valid () ) { piiu->show (level); piiu++; } this->iiuListMutex.unlock (); - - this->defaultMutex.unlock (); } void cac::installIIU ( tcpiiu &iiu ) { this->iiuListMutex.lock (); - iiu.recvPending = false; - this->iiuListIdle.add (iiu); + this->iiuList.add (iiu); this->iiuListMutex.unlock (); this->defaultMutex.lock (); - if ( ! this->enablePreemptiveCallback && this->fdRegFunc ) { ( * this->fdRegFunc ) ( (void *) this->fdRegArg, iiu.getSock (), TRUE ); @@ -387,33 +307,14 @@ void cac::installIIU ( tcpiiu &iiu ) this->defaultMutex.unlock (); } -void cac::signalRecvActivityIIU (tcpiiu &iiu) +void cac::signalRecvActivity () { - bool change; - - this->iiuListMutex.lock (); - - if ( iiu.recvPending ) { - change = false; - } - else { - this->iiuListIdle.remove (iiu); - this->iiuListRecvPending.add (iiu); - iiu.recvPending = true; - change = true; - } - - this->iiuListMutex.unlock (); - - // - // wakeup after unlock improves performance - // - if (change) { - this->recvActivity.signal (); + if ( this->pRecvProcThread ) { + this->pRecvProcThread->signalActivity (); } } -void cac::removeIIU (tcpiiu &iiu) +void cac::removeIIU ( tcpiiu &iiu ) { this->defaultMutex.lock (); osiSockAddr addr = iiu.address (); @@ -430,12 +331,7 @@ void cac::removeIIU (tcpiiu &iiu) this->iiuListMutex.lock (); - if ( iiu.recvPending ) { - this->iiuListRecvPending.remove (iiu); - } - else { - this->iiuListIdle.remove (iiu); - } + this->iiuList.remove (iiu); if ( ! this->enablePreemptiveCallback ) { if ( this->fdRegFunc ) { @@ -543,6 +439,7 @@ void cac::beaconNotify ( const inetAddrID &addr ) status = getsockname ( this->pudpiiu->getSock (), (struct sockaddr *) &saddr, &saddr_length ); if ( status < 0 ) { epicsPrintf ( "CAC: getsockname () error was \"%s\"\n", SOCKERRSTR (SOCKERRNO) ); + this->defaultMutex.unlock (); return; } port = ntohs ( saddr.sin_port ); @@ -560,22 +457,14 @@ void cac::beaconNotify ( const inetAddrID &addr ) } } - /* - * set retry count of all disconnected channels - * to zero - */ - tsDLIterBD iter ( this->pudpiiu->chidList.first () ); - while ( iter.valid () ) { - iter->retry = 0u; - iter++; - } - this->defaultMutex.unlock (); + this->pudpiiu->resetChannelRetryCounts (); + # if DEBUG { char buf[64]; - ipAddrToA (pnet_addr, buf, sizeof (buf) ); + ipAddrToA (pnet_addr, buf, sizeof ( buf ) ); printf ("new server available: %s\n", buf); } # endif @@ -596,28 +485,53 @@ void cac::removeBeaconInetAddr (const inetAddrID &ina) assert (pBHE); } -void cac::decrementOutstandingIO (unsigned seqNumber) +void cac::decrementOutstandingIO ( unsigned seqNumber ) { + bool signalNeeded; this->defaultMutex.lock (); if ( this->readSeq == seqNumber ) { if ( this->pndrecvcnt > 0u ) { this->pndrecvcnt--; + if ( this->pndrecvcnt == 0u ) { + signalNeeded = true; + } + else { + signalNeeded = false; + } + } + else { + signalNeeded = true; } } + else { + signalNeeded = true; + } this->defaultMutex.unlock (); - if ( this->pndrecvcnt == 0u ) { + + if ( signalNeeded ) { this->ioDone.signal (); } } void cac::decrementOutstandingIO () { + bool signalNeeded; this->defaultMutex.lock (); if ( this->pndrecvcnt > 0u ) { this->pndrecvcnt--; + if ( this->pndrecvcnt == 0u ) { + signalNeeded = true; + } + else { + signalNeeded = false; + } + } + else { + signalNeeded = true; } this->defaultMutex.unlock (); - if ( this->pndrecvcnt == 0u ) { + + if ( signalNeeded ) { this->ioDone.signal (); } } @@ -734,26 +648,150 @@ bool cac::ioComplete () const } } -void cac::installIO ( baseNMIU &io ) +void cac::ioInstall ( nciu &chan, baseNMIU &io ) { this->defaultMutex.lock (); this->ioTable.add ( io ); + chan.cacPrivate::eventq.add ( io ); this->defaultMutex.unlock (); } -void cac::uninstallIO ( baseNMIU &io ) +void cac::ioDestroy ( unsigned id ) { this->defaultMutex.lock (); - this->ioTable.remove ( io ); + baseNMIU * pmiu = this->ioTable.remove ( id ); + if ( pmiu ) { + pmiu->chan.cacPrivate::eventq.remove ( *pmiu ); + } this->defaultMutex.unlock (); + // care is taken to not destroy with the cac lock + // applied because we could potentially hold the + // cac lock while sending and deadlock with the + // recv thread, but we must uninstall the IO + // before accessing it with the lock released + if ( pmiu ) { + pmiu->destroy (); + } } -baseNMIU * cac::lookupIO (unsigned id) +void cac::ioCompletionNotify ( unsigned id ) { this->defaultMutex.lock (); baseNMIU * pmiu = this->ioTable.lookup ( id ); + if ( pmiu ) { + pmiu->completionNotify (); + } this->defaultMutex.unlock (); - return pmiu; +} + +void cac::ioCompletionNotify ( unsigned id, unsigned type, + unsigned long count, const void *pData ) +{ + this->defaultMutex.lock (); + baseNMIU * pmiu = this->ioTable.lookup ( id ); + if ( pmiu ) { + pmiu->completionNotify ( type, count, pData ); + } + this->defaultMutex.unlock (); +} + +void cac::ioExceptionNotify ( unsigned id, int status, const char *pContext ) +{ + this->defaultMutex.lock (); + baseNMIU * pmiu = this->ioTable.lookup ( id ); + if ( pmiu ) { + pmiu->exceptionNotify ( status, pContext ); + } + this->defaultMutex.unlock (); +} + +void cac::ioExceptionNotify ( unsigned id, int status, + const char *pContext, unsigned type, unsigned long count ) +{ + this->defaultMutex.lock (); + baseNMIU * pmiu = this->ioTable.lookup ( id ); + if ( pmiu ) { + pmiu->exceptionNotify ( status, pContext, type, count ); + } + this->defaultMutex.unlock (); +} + +void cac::ioCompletionNotifyAndDestroy ( unsigned id ) +{ + this->defaultMutex.lock (); + baseNMIU * pmiu = this->ioTable.remove ( id ); + if ( pmiu ) { + pmiu->chan.cacPrivate::eventq.remove ( *pmiu ); + } + this->defaultMutex.unlock (); + // care is taken to not destroy with the cac lock + // applied because we could potentially hold the + // cac lock while sending and deadlock with the + // recv thread, but we must uninstall the IO + // before accessing it with the lock released + if ( pmiu ) { + pmiu->completionNotify (); + pmiu->destroy (); + } +} + +void cac::ioCompletionNotifyAndDestroy ( unsigned id, + unsigned type, unsigned long count, const void *pData ) +{ + this->defaultMutex.lock (); + baseNMIU * pmiu = this->ioTable.remove ( id ); + if ( pmiu ) { + pmiu->chan.cacPrivate::eventq.remove ( *pmiu ); + } + this->defaultMutex.unlock (); + // care is taken to not destroy with the cac lock + // applied because we could potentially hold the + // cac lock while sending and deadlock with the + // recv thread, but we must uninstall the IO + // before accessing it with the lock released + if ( pmiu ) { + pmiu->completionNotify ( type, count, pData ); + pmiu->destroy (); + } +} + +void cac::ioExceptionNotifyAndDestroy ( unsigned id, int status, const char *pContext ) +{ + this->defaultMutex.lock (); + baseNMIU * pmiu = this->ioTable.remove ( id ); + if ( pmiu ) { + pmiu->chan.cacPrivate::eventq.remove ( *pmiu ); + } + this->defaultMutex.unlock (); + // care is taken to not destroy with the cac lock + // applied because we could potentially hold the + // cac lock while sending and deadlock with the + // recv thread, but we must uninstall the IO + // before accessing it with the lock released + if ( pmiu ) { + pmiu->exceptionNotify ( status, pContext ); + pmiu->destroy (); + } +} + +void cac::ioExceptionNotifyAndDestroy ( unsigned id, int status, + const char *pContext, unsigned type, unsigned long count ) +{ + this->defaultMutex.lock (); + baseNMIU * pmiu = this->ioTable.remove ( id ); + if ( pmiu ) { + pmiu->chan.cacPrivate::eventq.remove ( *pmiu ); + } + this->defaultMutex.unlock (); + // care is taken to not destroy with the cac lock + // applied because we could potentially hold the + // cac lock while sending and deadlock with the + // recv thread, but we must uninstall the IO + // before accessing it with the lock released + if ( pmiu ) { + pmiu->exceptionNotify ( status, pContext, type, count ); + pmiu->destroy (); + } } void cac::registerChannel (nciu &chan) @@ -763,19 +801,59 @@ void cac::registerChannel (nciu &chan) this->defaultMutex.unlock (); } -void cac::unregisterChannel (nciu &chan) +void cac::unregisterChannel ( nciu &chan ) { this->defaultMutex.lock (); this->chanTable.remove ( chan ); this->defaultMutex.unlock (); } -nciu * cac::lookupChan (unsigned id) +void cac::accessRightsNotify ( unsigned id, caar ar ) { this->defaultMutex.lock (); - nciu * pchan = this->chanTable.lookup ( id ); + nciu * pChan = this->chanTable.lookup ( id ); + if ( pChan ) { + pChan->accessRightsStateChange ( ar ); + } + this->defaultMutex.unlock (); +} + +void cac::connectChannel ( unsigned id, class tcpiiu &iiu, + unsigned nativeType, unsigned long nativeCount, unsigned sid ) +{ + this->defaultMutex.lock (); + nciu * pChan = this->chanTable.lookup ( id ); + if ( pChan ) { + unsigned sidTmp; + if ( iiu.ca_v44_ok () ) { + sidTmp = sid; + } + else { + sidTmp = pChan->getSID (); + } + pChan->connect ( iiu, nativeType, nativeCount, sidTmp ); + } + this->defaultMutex.unlock (); +} + +void cac::channelDestroy ( unsigned id ) +{ + this->defaultMutex.lock (); + nciu * pChan = this->chanTable.lookup ( id ); + if ( pChan ) { + pChan->destroy (); + } + this->defaultMutex.unlock (); +} + +void cac::disconnectChannel ( unsigned id ) +{ + this->defaultMutex.lock (); + nciu * pChan = this->chanTable.lookup ( id ); + if ( pChan ) { + pChan->disconnect (); + } this->defaultMutex.unlock (); - return pchan; } void cac::installCASG (CASG &sg) @@ -837,7 +915,7 @@ bool cac::createChannelIO (const char *pName, cacChannel &chan) return false; } } - nciu *pNetChan = new nciu ( this, chan, pName ); + nciu *pNetChan = new nciu ( *this, chan, pName ); if ( pNetChan ) { if ( ! pNetChan->fullyConstructed () ) { pNetChan->destroy (); @@ -867,7 +945,7 @@ bool cac::setupUDP () return true; } - this->pudpiiu = new udpiiu ( this ); + this->pudpiiu = new udpiiu ( *this ); if ( ! this->pudpiiu ) { this->defaultMutex.unlock (); return false; @@ -877,6 +955,7 @@ bool cac::setupUDP () if ( ! this->pSearchTmr ) { delete this->pudpiiu; this->pudpiiu = 0; + this->defaultMutex.unlock (); return false; } @@ -901,16 +980,6 @@ bool cac::setupUDP () return true; } -void cac::lock () const -{ - this->defaultMutex.lock (); -} - -void cac::unlock () const -{ - this->defaultMutex.unlock (); -} - void cac::registerForFileDescriptorCallBack ( CAFDHANDLER *pFunc, void *pArg ) { this->defaultMutex.lock (); @@ -921,12 +990,16 @@ void cac::registerForFileDescriptorCallBack ( CAFDHANDLER *pFunc, void *pArg ) void cac::enableCallbackPreemption () { - this->pProcThread->enable (); + if ( this->pRecvProcThread ) { + this->pRecvProcThread->enable (); + } } void cac::disableCallbackPreemption () { - this->pProcThread->disable (); + if ( this->pRecvProcThread ) { + this->pRecvProcThread->disable (); + } } void cac::changeExceptionEvent ( caExceptionHandler *pfunc, void *arg ) @@ -980,10 +1053,9 @@ void cac::installDisconnectedChannel ( nciu &chan ) assert ( this->pudpiiu && this->pSearchTmr ); - this->defaultMutex.lock (); - this->pudpiiu->addToChanList ( chan ); + chan.attachChanToIIU ( *this->pudpiiu ); + chan.resetRetryCount (); this->pSearchTmr->reset ( CA_RECAST_DELAY ); - this->defaultMutex.unlock (); } void cac::notifySearchResponse ( unsigned short retrySeqNo ) @@ -1004,10 +1076,123 @@ void cac::replaceErrLogHandler ( caPrintfFunc *ca_printf_func ) { this->defaultMutex.lock (); if ( ca_printf_func ) { - this->ca_printf_func = ca_printf_func; + this->pVPrintfFunc = ca_printf_func; } else { - this->ca_printf_func = epicsVprintf; + this->pVPrintfFunc = epicsVprintf; } this->defaultMutex.unlock (); -} \ No newline at end of file +} + +/* + * constructTCPIIU () + */ +tcpiiu * cac::constructTCPIIU ( const osiSockAddr &addr, unsigned minorVersion ) +{ + bhe *pBHE; + tcpiiu *piiu; + + if ( addr.sa.sa_family != AF_INET ) { + return 0u; + } + + /* + * look for an existing virtual circuit + */ + this->defaultMutex.lock (); + pBHE = this->lookupBeaconInetAddr ( addr.ia ); + if ( ! pBHE ) { + pBHE = this->createBeaconHashEntry ( addr.ia, osiTime () ); + if ( ! pBHE ) { + this->defaultMutex.unlock (); + return NULL; + } + } + + piiu = pBHE->getIIU (); + if ( piiu ) { + if ( piiu->alive () ) { + this->defaultMutex.unlock (); + return piiu; + } + else { + this->defaultMutex.unlock (); + return NULL; + } + } + this->defaultMutex.unlock (); + + piiu = new tcpiiu ( *this, addr, minorVersion, + *pBHE, this->connTMO, *this->pTimerQueue, + this->ipToAEngine ); + if ( ! piiu ) { + return NULL; + } + + if ( piiu->fullyConstructed () ) { + return piiu; + } + else { + delete piiu; + return NULL; + } +} + +void cac::lookupChannelAndTransferToTCP ( unsigned cid, unsigned sid, + unsigned typeCode, unsigned long count, + unsigned minorVersionNumber, const osiSockAddr &addr ) +{ + unsigned retrySeqNumber; + tcpiiu *allocpiiu; + + { + this->defaultMutex.lock (); + nciu *chan; + + /* + * ignore search replies for deleted channels + */ + chan = this->chanTable.lookup ( cid ); + if ( ! chan ) { + this->defaultMutex.unlock (); + return; + } + + retrySeqNumber = chan->getRetrySeqNo (); + + /* + * Ignore duplicate search replies + */ + if ( chan->connectionInProgress ( addr ) ) { + this->defaultMutex.unlock (); + return; + } + + allocpiiu = this->constructTCPIIU ( addr, minorVersionNumber ); + if ( ! allocpiiu ) { + this->defaultMutex.unlock (); + return; + } + + /* + * remove it from the broadcast niiu + */ + chan->searchReplySetUp ( sid, typeCode, count ); + allocpiiu->installChannelPendingClaim ( *chan ); + + this->defaultMutex.unlock (); + } + + this->notifySearchResponse ( retrySeqNumber ); + return; +} + +bool cac::currentThreadIsRecvProcessThread () +{ + if ( this->pRecvProcThread ) { + return this->pRecvProcThread->isCurrentThread (); + } + else { + return false; + } +} diff --git a/src/ca/cacChannel.cpp b/src/ca/cacChannel.cpp index 578962100..8623b3cb0 100644 --- a/src/ca/cacChannel.cpp +++ b/src/ca/cacChannel.cpp @@ -23,8 +23,9 @@ cacChannel::cacChannel () : pChannelIO (0) cacChannel::~cacChannel () { - if ( this->pChannelIO ) { - this->pChannelIO->destroy (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + pIO->destroy (); } } @@ -37,8 +38,9 @@ void cacChannel::attachIO (cacChannelIO &io) int cacChannel::read ( unsigned type, unsigned long count, cacNotify & notify ) { - if ( this->pChannelIO ) { - return pChannelIO->read (type, count, notify); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->read ( type, count, notify ); } else { return ECA_DISCONNCHID; @@ -47,8 +49,9 @@ int cacChannel::read ( unsigned type, unsigned long count, cacNotify & notify ) int cacChannel::read ( unsigned type, unsigned long count, void *pValue ) { - if ( this->pChannelIO ) { - return pChannelIO->read (type, count, pValue); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->read ( type, count, pValue ); } else { return ECA_DISCONNCHID; @@ -57,8 +60,9 @@ int cacChannel::read ( unsigned type, unsigned long count, void *pValue ) int cacChannel::write (unsigned type, unsigned long count, const void *pvalue ) { - if ( this->pChannelIO ) { - return pChannelIO->write (type, count, pvalue); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->write (type, count, pvalue); } else { return ECA_DISCONNCHID; @@ -68,8 +72,9 @@ int cacChannel::write (unsigned type, unsigned long count, const void *pvalue ) int cacChannel::write (unsigned type, unsigned long count, const void *pvalue, cacNotify & notify ) { - if ( this->pChannelIO ) { - return pChannelIO->write (type, count, pvalue, notify); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->write (type, count, pvalue, notify); } else { return ECA_DISCONNCHID; @@ -79,8 +84,9 @@ int cacChannel::write (unsigned type, unsigned long count, int cacChannel::subscribe ( unsigned type, unsigned long count, unsigned mask, cacNotify ¬ify ) { - if ( this->pChannelIO ) { - return pChannelIO->subscribe (type, count, mask, notify); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->subscribe (type, count, mask, notify); } else { return ECA_DISCONNCHID; @@ -90,8 +96,9 @@ int cacChannel::subscribe ( unsigned type, unsigned long count, void cacChannel::hostName ( char *pBuf, unsigned bufLength ) const { if ( bufLength ) { - if ( this->pChannelIO ) { - pChannelIO->hostName (pBuf, bufLength); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + pIO->hostName (pBuf, bufLength); } else { strncpy ( pBuf, "", bufLength ); @@ -102,8 +109,9 @@ void cacChannel::hostName ( char *pBuf, unsigned bufLength ) const const char * cacChannel::pHostName () const { - if ( this->pChannelIO ) { - return pChannelIO->pHostName (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->pHostName (); } else { return ""; @@ -112,8 +120,9 @@ const char * cacChannel::pHostName () const short cacChannel::nativeType () const { - if ( this->pChannelIO ) { - return pChannelIO->nativeType (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->nativeType (); } else { return TYPENOTCONN; @@ -122,8 +131,9 @@ short cacChannel::nativeType () const unsigned long cacChannel::nativeElementCount () const { - if ( this->pChannelIO ) { - return pChannelIO->nativeElementCount (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->nativeElementCount (); } else { return 0ul; @@ -132,8 +142,9 @@ unsigned long cacChannel::nativeElementCount () const channel_state cacChannel::state () const { - if ( this->pChannelIO ) { - return pChannelIO->state (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->state (); } else { return cs_never_conn; @@ -142,8 +153,9 @@ channel_state cacChannel::state () const bool cacChannel::readAccess () const { - if ( this->pChannelIO ) { - caar ar = pChannelIO->accessRights (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + caar ar = pIO->accessRights (); return ar.read_access; } else { @@ -153,7 +165,8 @@ bool cacChannel::readAccess () const bool cacChannel::writeAccess () const { - if ( this->pChannelIO ) { + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { caar ar = pChannelIO->accessRights (); return ar.write_access; } @@ -164,8 +177,9 @@ bool cacChannel::writeAccess () const const char *cacChannel::pName () const { - if ( this->pChannelIO ) { - return pChannelIO->pName (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->pName (); } else { return ""; @@ -174,8 +188,9 @@ const char *cacChannel::pName () const unsigned cacChannel::searchAttempts () const { - if ( this->pChannelIO ) { - return pChannelIO->searchAttempts (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->searchAttempts (); } else { return 0u; @@ -184,8 +199,9 @@ unsigned cacChannel::searchAttempts () const bool cacChannel::ca_v42_ok () const { - if ( this->pChannelIO ) { - return pChannelIO->ca_v42_ok (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->ca_v42_ok (); } else { return false; @@ -194,8 +210,9 @@ bool cacChannel::ca_v42_ok () const bool cacChannel::connected () const { - if ( this->pChannelIO ) { - return pChannelIO->connected (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->connected (); } else { return false; @@ -204,8 +221,9 @@ bool cacChannel::connected () const caar cacChannel::accessRights () const { - if ( this->pChannelIO ) { - return pChannelIO->accessRights (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->accessRights (); } else { caar ar; @@ -246,8 +264,9 @@ void cacChannel::connectTimeoutNotify () unsigned cacChannel::readSequence () const { - if ( this->pChannelIO ) { - return this->pChannelIO->readSequence (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + return pIO->readSequence (); } else { return 0u; @@ -256,28 +275,32 @@ unsigned cacChannel::readSequence () const void cacChannel::decrementOutstandingIO () { - if ( this->pChannelIO ) { - this->pChannelIO->decrementOutstandingIO (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + pIO->decrementOutstandingIO (); } } void cacChannel::incrementOutstandingIO () { - if ( this->pChannelIO ) { - this->pChannelIO->incrementOutstandingIO (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + pIO->incrementOutstandingIO (); } } -void cacChannel::lock () const +void cacChannel::lockOutstandingIO () const { - if ( this->pChannelIO ) { - this->pChannelIO->lock (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + pIO->lockOutstandingIO (); } } -void cacChannel::unlock () const +void cacChannel::unlockOutstandingIO () const { - if ( this->pChannelIO ) { - this->pChannelIO->unlock (); + cacChannelIO *pIO = this->pChannelIO; + if ( pIO ) { + pIO->unlockOutstandingIO (); } } diff --git a/src/ca/cacChannelIO.cpp b/src/ca/cacChannelIO.cpp index 0f6b6fa6a..a2d2d0b9c 100644 --- a/src/ca/cacChannelIO.cpp +++ b/src/ca/cacChannelIO.cpp @@ -107,4 +107,12 @@ void cacChannelIO::decrementOutstandingIO () { } +void cacChannelIO::lockOutstandingIO () const +{ +} + +void cacChannelIO::unlockOutstandingIO () const +{ +} + diff --git a/src/ca/cacIO.h b/src/ca/cacIO.h index 7269f9aa2..1ec2fee62 100644 --- a/src/ca/cacIO.h +++ b/src/ca/cacIO.h @@ -86,8 +86,8 @@ public: protected: class cacChannelIO *pChannelIO; - void lock () const; - void unlock () const; + void lockOutstandingIO () const; + void unlockOutstandingIO () const; private: virtual void ioAttachNotify (); @@ -116,8 +116,8 @@ public: virtual const char *pName () const = 0; - virtual void lock () const = 0; - virtual void unlock () const = 0; + virtual void lockOutstandingIO () const = 0; + virtual void unlockOutstandingIO () const = 0; private: virtual int read ( unsigned type, unsigned long count, void *pValue) = 0; @@ -143,11 +143,11 @@ private: friend class cacChannel; }; -class epicsShareClass cacLocalChannelIO : +class cacLocalChannelIO : public cacChannelIO, public tsDLNode { public: - cacLocalChannelIO ( cacChannel &chan ); - virtual ~cacLocalChannelIO () = 0; + epicsShareFunc cacLocalChannelIO ( cacChannel &chan ); + epicsShareFunc virtual ~cacLocalChannelIO () = 0; }; struct cacServiceIO : public tsDLNode { diff --git a/src/ca/cacPrivate.cpp b/src/ca/cacPrivate.cpp new file mode 100644 index 000000000..54400c09d --- /dev/null +++ b/src/ca/cacPrivate.cpp @@ -0,0 +1,80 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "iocinf.h" + +cacPrivate::cacPrivate ( cac &cacIn ) : + cacCtx ( cacIn ) +{ +} + +// Destroy all IO blocks attached. +// Care is taken here not to hold the lock while +// sending a subscription delete message (which +// would result in deadlocks) +void cacPrivate::destroyAllIO () +{ + while ( true ) { + unsigned id; + bool done; + + this->cacCtx.defaultMutex.lock (); + { + baseNMIU *pNMIU = this->eventq.first (); + if ( pNMIU ) { + id = pNMIU->getId (); + done = false; + } + else { + done = true; + } + } + this->cacCtx.defaultMutex.unlock (); + + if ( done ) { + break; + } + // care is taken to not hold a lock when + // executing this + this->cacCtx.ioDestroy ( id ); + } +} + +// resubscribe for monitors from this channel +void cacPrivate::subscribeAllIO () +{ + this->cacCtx.defaultMutex.lock (); + tsDLIterBD < baseNMIU > iter = this->eventq.first (); + while ( iter.valid () ) { + iter->subscriptionMsg (); + iter++; + } + this->cacCtx.defaultMutex.unlock (); +} + +// cancel IO operations and monitor subscriptions +void cacPrivate::disconnectAllIO ( const char *pHostName ) +{ + this->cacCtx.defaultMutex.lock (); + tsDLIterBD < baseNMIU > iter = this->eventq.first (); + while ( iter.valid () ) { + tsDLIterBD < baseNMIU > next = iter.itemAfter (); + iter->disconnect ( pHostName ); + iter = next; + } + this->cacCtx.defaultMutex.unlock (); +} \ No newline at end of file diff --git a/src/ca/cac_IL.h b/src/ca/cac_IL.h new file mode 100644 index 000000000..88153ac4d --- /dev/null +++ b/src/ca/cac_IL.h @@ -0,0 +1,55 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +inline int cac::vPrintf ( const char *pformat, va_list args ) +{ + return ( *this->pVPrintfFunc ) ( pformat, args ); +} + +inline int cac::printf ( const char *pformat, ... ) +{ + va_list theArgs; + int status; + + va_start ( theArgs, pformat ); + + status = this->vPrintf ( pformat, theArgs ); + + va_end ( theArgs ); + + return status; +} + +inline double cac::connectionTimeout () const +{ + return this->connTMO; +} + +inline const char * cac::userNamePointer () +{ + return this->pUserName; +} + +inline void cac::lockOutstandingIO () const +{ + this->defaultMutex.lock (); +} + +inline void cac::unlockOutstandingIO () const +{ + this->defaultMutex.unlock (); +} diff --git a/src/ca/cadef.h b/src/ca/cadef.h index 1ced77327..d4e93e980 100644 --- a/src/ca/cadef.h +++ b/src/ca/cadef.h @@ -37,7 +37,6 @@ # endif #endif -#include "ellLib.h" #include "osiThread.h" #include "shareLib.h" #include "caerr.h" @@ -719,6 +718,8 @@ epicsShareFunc void epicsShareAPI ca_signal_formated (long ca_status, const char * ca_host_name_function() * * channel R channel identifier + * + * !!!! this function is _not_ thread safe !!!! */ epicsShareFunc const char * epicsShareAPI ca_host_name (chid channel); /* thread safe version */ diff --git a/src/ca/caerr.h b/src/ca/caerr.h index 1a54c212d..6eeea9fb4 100644 --- a/src/ca/caerr.h +++ b/src/ca/caerr.h @@ -28,7 +28,7 @@ #define CA_M_MSG_NO 0x0000FFF8 #define CA_M_SEVERITY 0x00000007 -#define CA_M_LEVEL 0x00000003 +#define CA_M_LEVEL 0x00000003 #define CA_M_SUCCESS 0x00000001 #define CA_M_ERROR 0x00000002 #define CA_M_SEVERE 0x00000004 @@ -62,42 +62,42 @@ #define ECA_NORMAL DEFMSG(CA_K_SUCCESS, 0) -#define ECA_MAXIOC DEFMSG(CA_K_ERROR, 1) -#define ECA_UKNHOST DEFMSG(CA_K_ERROR, 2) -#define ECA_UKNSERV DEFMSG(CA_K_ERROR, 3) -#define ECA_SOCK DEFMSG(CA_K_ERROR, 4) +#define ECA_MAXIOC DEFMSG(CA_K_ERROR, 1) +#define ECA_UKNHOST DEFMSG(CA_K_ERROR, 2) +#define ECA_UKNSERV DEFMSG(CA_K_ERROR, 3) +#define ECA_SOCK DEFMSG(CA_K_ERROR, 4) #define ECA_CONN DEFMSG(CA_K_WARNING, 5) #define ECA_ALLOCMEM DEFMSG(CA_K_WARNING, 6) #define ECA_UKNCHAN DEFMSG(CA_K_WARNING, 7) #define ECA_UKNFIELD DEFMSG(CA_K_WARNING, 8) -#define ECA_TOLARGE DEFMSG(CA_K_ERROR, 9) -#define ECA_TIMEOUT DEFMSG(CA_K_WARNING, 10) -#define ECA_NOSUPPORT DEFMSG(CA_K_WARNING, 11) -#define ECA_STRTOBIG DEFMSG(CA_K_WARNING, 12) -#define ECA_DISCONNCHID DEFMSG(CA_K_ERROR, 13) -#define ECA_BADTYPE DEFMSG(CA_K_ERROR, 14) -#define ECA_CHIDNOTFND DEFMSG(CA_K_INFO, 15) -#define ECA_CHIDRETRY DEFMSG(CA_K_INFO, 16) -#define ECA_INTERNAL DEFMSG(CA_K_FATAL, 17) -#define ECA_DBLCLFAIL DEFMSG(CA_K_WARNING, 18) -#define ECA_GETFAIL DEFMSG(CA_K_WARNING, 19) -#define ECA_PUTFAIL DEFMSG(CA_K_WARNING, 20) -#define ECA_ADDFAIL DEFMSG(CA_K_WARNING, 21) -#define ECA_BADCOUNT DEFMSG(CA_K_WARNING, 22) -#define ECA_BADSTR DEFMSG(CA_K_ERROR, 23) -#define ECA_DISCONN DEFMSG(CA_K_WARNING, 24) -#define ECA_DBLCHNL DEFMSG(CA_K_WARNING, 25) -#define ECA_EVDISALLOW DEFMSG(CA_K_ERROR, 26) -#define ECA_BUILDGET DEFMSG(CA_K_WARNING, 27) -#define ECA_NEEDSFP DEFMSG(CA_K_WARNING, 28) -#define ECA_OVEVFAIL DEFMSG(CA_K_WARNING, 29) -#define ECA_BADMONID DEFMSG(CA_K_ERROR, 30) -#define ECA_NEWADDR DEFMSG(CA_K_WARNING, 31) -#define ECA_NEWCONN DEFMSG(CA_K_INFO, 32) -#define ECA_NOCACTX DEFMSG(CA_K_WARNING, 33) -#define ECA_DEFUNCT DEFMSG(CA_K_FATAL, 34) -#define ECA_EMPTYSTR DEFMSG(CA_K_WARNING, 35) -#define ECA_NOREPEATER DEFMSG(CA_K_WARNING, 36) +#define ECA_TOLARGE DEFMSG(CA_K_ERROR, 9) +#define ECA_TIMEOUT DEFMSG(CA_K_WARNING, 10) +#define ECA_NOSUPPORT DEFMSG(CA_K_WARNING, 11) +#define ECA_STRTOBIG DEFMSG(CA_K_WARNING, 12) +#define ECA_DISCONNCHID DEFMSG(CA_K_ERROR, 13) +#define ECA_BADTYPE DEFMSG(CA_K_ERROR, 14) +#define ECA_CHIDNOTFND DEFMSG(CA_K_INFO, 15) +#define ECA_CHIDRETRY DEFMSG(CA_K_INFO, 16) +#define ECA_INTERNAL DEFMSG(CA_K_FATAL, 17) +#define ECA_DBLCLFAIL DEFMSG(CA_K_WARNING, 18) +#define ECA_GETFAIL DEFMSG(CA_K_WARNING, 19) +#define ECA_PUTFAIL DEFMSG(CA_K_WARNING, 20) +#define ECA_ADDFAIL DEFMSG(CA_K_WARNING, 21) +#define ECA_BADCOUNT DEFMSG(CA_K_WARNING, 22) +#define ECA_BADSTR DEFMSG(CA_K_ERROR, 23) +#define ECA_DISCONN DEFMSG(CA_K_WARNING, 24) +#define ECA_DBLCHNL DEFMSG(CA_K_WARNING, 25) +#define ECA_EVDISALLOW DEFMSG(CA_K_ERROR, 26) +#define ECA_BUILDGET DEFMSG(CA_K_WARNING, 27) +#define ECA_NEEDSFP DEFMSG(CA_K_WARNING, 28) +#define ECA_OVEVFAIL DEFMSG(CA_K_WARNING, 29) +#define ECA_BADMONID DEFMSG(CA_K_ERROR, 30) +#define ECA_NEWADDR DEFMSG(CA_K_WARNING, 31) +#define ECA_NEWCONN DEFMSG(CA_K_INFO, 32) +#define ECA_NOCACTX DEFMSG(CA_K_WARNING, 33) +#define ECA_DEFUNCT DEFMSG(CA_K_FATAL, 34) +#define ECA_EMPTYSTR DEFMSG(CA_K_WARNING, 35) +#define ECA_NOREPEATER DEFMSG(CA_K_WARNING, 36) #define ECA_NOCHANMSG DEFMSG(CA_K_WARNING, 37) #define ECA_DLCKREST DEFMSG(CA_K_WARNING, 38) #define ECA_SERVBEHIND DEFMSG(CA_K_WARNING, 39) @@ -109,13 +109,12 @@ #define ECA_PUTCBINPROG DEFMSG(CA_K_ERROR, 45) #define ECA_NORDACCESS DEFMSG(CA_K_WARNING, 46) #define ECA_NOWTACCESS DEFMSG(CA_K_WARNING, 47) -#define ECA_ANACHRONISM DEFMSG(CA_K_ERROR, 48) +#define ECA_ANACHRONISM DEFMSG(CA_K_ERROR, 48) #define ECA_NOSEARCHADDR DEFMSG(CA_K_WARNING, 49) #define ECA_NOCONVERT DEFMSG(CA_K_WARNING, 50) -#define ECA_BADCHID DEFMSG(CA_K_ERROR, 51) -#define ECA_BADFUNCPTR DEFMSG(CA_K_ERROR, 52) -#define ECA_OPWILLBLOCK DEFMSG(CA_K_WARNING, 53) -#define ECA_ISATTACHED DEFMSG(CA_K_WARNING, 54) +#define ECA_BADCHID DEFMSG(CA_K_ERROR, 51) +#define ECA_BADFUNCPTR DEFMSG(CA_K_ERROR, 52) +#define ECA_ISATTACHED DEFMSG(CA_K_WARNING, 53) #ifndef CA_ERROR_GLBLSOURCE epicsShareExtern READONLY char *ca_message_text[]; @@ -148,7 +147,7 @@ READONLY char *ca_message_text[] "Count requested inappropriate for that channel", "The supplied string has improper format", "Network connection lost", -"Ambiguous channel host (multiple IOC's have a channel by that name)", +"Identical process variable name on multiple servers", "The CA routine called is inappropriate for use within an event handler", "Database value get for that channel failed during channel search", "Unable to initialize without the vxWorks VX_FP_TASK task option set", @@ -176,7 +175,6 @@ READONLY char *ca_message_text[] "Data conversion between client's type and the server's type failed", "Invalid channel identifier", "Invalid function pointer", -"Operation will block (this code is not returned to user)", "Thread is already attached to a client context" }; #endif diff --git a/src/ca/catime.c b/src/ca/catime.c index 47f405833..4f3d37517 100644 --- a/src/ca/catime.c +++ b/src/ca/catime.c @@ -83,7 +83,7 @@ unsigned *pInlineIter /* * test_search () */ -LOCAL void test_search( +LOCAL void test_search ( ti *pItems, unsigned iterations, unsigned *pInlineIter @@ -92,12 +92,12 @@ unsigned *pInlineIter unsigned i; int status; - for (i=0u; iclientId = chan.id; + this->serverId = chan.sid; + if ( this->v44 ) { + unsigned len = strlen ( chan.pNameStr ) + 1u; + if ( this->bufLen < len ) { + unsigned newBufLen = 2 * len; + char *pNewStr = new char [ newBufLen ]; + if ( pNewStr ) { + delete [] this->pStr; + this->pStr = pNewStr; + this->bufLen = newBufLen; + } + else { + return false; + } + } + strcpy ( this->pStr, chan.pNameStr ); + this->currentStrLen = len; + } + else { + this->currentStrLen = 0u; + } + return true; +} + diff --git a/src/ca/claimMsgCache_IL.h b/src/ca/claimMsgCache_IL.h new file mode 100644 index 000000000..3600c9758 --- /dev/null +++ b/src/ca/claimMsgCache_IL.h @@ -0,0 +1,39 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +inline claimMsgCache::claimMsgCache ( bool v44In ) : + pStr ( 0 ), clientId ( UINT_MAX ), serverId ( UINT_MAX ), currentStrLen ( 0u ), + bufLen ( 0u ), v44 ( v44In ) +{ +} + +inline claimMsgCache::~claimMsgCache () +{ + if ( this->pStr ) { + delete this->pStr; + } +} + +inline int claimMsgCache::deliverMsg ( tcpiiu &iiu ) +{ + if ( v44 ) { + return iiu.createChannelRequest ( this->clientId, this->pStr, this->currentStrLen ); + } + else { + return iiu.createChannelRequest ( this->serverId, 0u, 0u ); + } +} + +inline bool claimMsgCache::channelMatches ( class nciu &chan ) +{ + return chan.id == this->clientId; +} diff --git a/src/ca/claimsPendingIIU.cpp b/src/ca/claimsPendingIIU.cpp new file mode 100644 index 000000000..b64b2b289 --- /dev/null +++ b/src/ca/claimsPendingIIU.cpp @@ -0,0 +1,38 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" +#include "netiiu_IL.h" + +claimsPendingIIU::claimsPendingIIU ( tcpiiu &tcpIIUIn ) : + netiiu ( tcpIIUIn.clientCtx () ), tcpIIU ( tcpIIUIn ) +{ +} + +claimsPendingIIU::~claimsPendingIIU () +{ +} + +const char * claimsPendingIIU::pHostName () const +{ + return this->tcpIIU.pHostName (); +} + +void claimsPendingIIU::hostName ( char *pBuf, unsigned bufLength ) const +{ + this->tcpIIU.hostName ( pBuf, bufLength ); +} + +bool claimsPendingIIU::connectionInProgress ( const char *pChannelName, const osiSockAddr &addr ) const +{ + return this->tcpIIU.connectionInProgress ( pChannelName, addr ); +} diff --git a/src/ca/comBuf_IL.h b/src/ca/comBuf_IL.h new file mode 100644 index 000000000..4b92ae524 --- /dev/null +++ b/src/ca/comBuf_IL.h @@ -0,0 +1,239 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef comBuf_ILh +#define comBuf_ILh + +#include +#include "epicsTypes.h" +#include "osiWireFormat.h" + +inline comBuf::comBuf () : nextWriteIndex ( 0u ), nextReadIndex ( 0u ) +{ +} + +inline comBuf::~comBuf () +{ +} + +inline void comBuf::destroy () +{ + delete this; +} + +inline void * comBuf::operator new ( size_t size ) +{ + return comBuf::freeList.allocate ( size ); +} + +inline void comBuf::operator delete ( void *pCadaver, size_t size ) +{ + comBuf::freeList.release ( pCadaver, size ); +} + +inline unsigned comBuf::unoccupiedBytes () const +{ + return sizeof ( this->buf ) - this->nextWriteIndex; +} + +inline unsigned comBuf::occupiedBytes () const +{ + return this->nextWriteIndex - this->nextReadIndex; +} + +inline bool comBuf::copyInAllBytes ( const void *pBuf, unsigned nBytes ) +{ + if ( nBytes > this->unoccupiedBytes () ) { + return false; + } + memcpy ( &this->buf[this->nextWriteIndex], pBuf, nBytes); + this->nextWriteIndex += nBytes; + return true; +} + +inline unsigned comBuf::copyInBytes ( const void *pBuf, unsigned nBytes ) +{ + unsigned available = this->unoccupiedBytes (); + if ( nBytes > available ) { + nBytes = available; + } + memcpy ( &this->buf[this->nextWriteIndex], pBuf, nBytes); + this->nextWriteIndex += nBytes; + return nBytes; +} + +inline unsigned comBuf::copyIn ( comBuf &bufIn ) +{ + unsigned nBytes = this->copyInBytes ( &bufIn.buf[bufIn.nextReadIndex], + bufIn.nextWriteIndex - bufIn.nextReadIndex ); + bufIn.nextReadIndex += nBytes; + return nBytes; +} + +inline bool comBuf::copyOutAllBytes ( void *pBuf, unsigned nBytes ) +{ + if ( nBytes > this->occupiedBytes () ) { + return false; + } + memcpy ( pBuf, &this->buf[this->nextReadIndex], nBytes); + this->nextReadIndex += nBytes; + return true; +} + +inline unsigned comBuf::copyOutBytes ( void *pBuf, unsigned nBytes ) +{ + unsigned occupied = this->occupiedBytes (); + if ( nBytes > occupied ) { + nBytes = occupied; + } + memcpy ( pBuf, &this->buf[this->nextReadIndex], nBytes); + this->nextReadIndex += nBytes; + return nBytes; +} + +inline unsigned comBuf::removeBytes ( unsigned nBytes ) +{ + unsigned occupied = this->occupiedBytes (); + if ( nBytes > occupied ) { + nBytes = occupied; + } + this->nextReadIndex += nBytes; + return nBytes; +} + +inline unsigned comBuf::maxBytes () +{ + return comBufSize; +} + +inline bool comBuf::flushToWire ( class comQueSend &que ) +{ + unsigned occupied = this->occupiedBytes (); + + unsigned nSent = 0u; + while ( occupied ) { + unsigned nBytes = que.sendBytes ( &this->buf[this->nextReadIndex], occupied ); + if ( nBytes == 0u ) { + this->nextReadIndex = this->nextWriteIndex; + return false; + } + this->nextReadIndex += nBytes; + occupied = this->occupiedBytes (); + } + return true; +} + +inline unsigned comBuf::fillFromWire ( class comQueRecv &que ) +{ + unsigned nNewBytes = que.recvBytes ( &this->buf[this->nextWriteIndex], + sizeof ( this->buf ) - this->nextWriteIndex ); + this->nextWriteIndex += nNewBytes; + return nNewBytes; +} + +inline unsigned comBuf::clipNElem ( unsigned elemSize, unsigned nElem ) +{ + unsigned avail = this->unoccupiedBytes (); + if ( elemSize * nElem > avail ) { + return avail / elemSize; + } + else { + return nElem; + } +} + +inline unsigned comBuf::copyIn ( const epicsInt8 *pValue, unsigned nElem ) +{ + return copyInBytes ( pValue, nElem ); +} + +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 ); + for ( unsigned i = 0u; i < nElem; i++ ) { + this->buf[this->nextWriteIndex++] = pValue[i] >> 8u; + this->buf[this->nextWriteIndex++] = pValue[i] >> 0u; + } + return nElem; +} + +inline unsigned comBuf::copyIn ( const epicsUInt16 *pValue, unsigned nElem ) +{ + nElem = this->clipNElem ( sizeof (*pValue), nElem ); + for ( unsigned i = 0u; i < nElem; i++ ) { + this->buf[this->nextWriteIndex++] = pValue[i] >> 8u; + this->buf[this->nextWriteIndex++] = pValue[i] >> 0u; + } + return nElem; +} + +inline unsigned comBuf::copyIn ( const epicsInt32 *pValue, unsigned nElem ) +{ + nElem = this->clipNElem ( 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; + } + return nElem; +} + +inline unsigned comBuf::copyIn ( const epicsUInt32 *pValue, unsigned nElem ) +{ + nElem = this->clipNElem ( 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; + } + return nElem; +} + +inline unsigned comBuf::copyIn ( const epicsFloat32 *pValue, unsigned nElem ) +{ + nElem = this->clipNElem ( 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; + } + return nElem; +} + +inline unsigned comBuf::copyIn ( const epicsFloat64 *pValue, unsigned nElem ) +{ + nElem = this->clipNElem ( 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; + } + return nElem; +} + +#endif // comBuf_ILh diff --git a/src/ca/comQueRecv.cpp b/src/ca/comQueRecv.cpp new file mode 100644 index 000000000..340a48b7a --- /dev/null +++ b/src/ca/comQueRecv.cpp @@ -0,0 +1,113 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#include +#include + +comQueRecv::~comQueRecv () +{ + comBuf *pBuf; + + this->mutex.lock (); + while ( ( pBuf = this->bufs.get () ) ) { + pBuf->destroy (); + } + this->mutex.unlock (); +} + +unsigned comQueRecv::occupiedBytes () const +{ + this->mutex.lock (); + + unsigned count = this->bufs.count (); + unsigned nBytes; + if ( count >= 2u ) { + nBytes = this->bufs.first ()->occupiedBytes (); + nBytes += this->bufs.last ()->occupiedBytes (); + nBytes += ( count - 2u ) * comBuf::maxBytes (); + } + else if ( count == 1u ) { + nBytes = this->bufs.first ()->occupiedBytes (); + } + else { + nBytes = 0u; + } + + this->mutex.unlock (); + + return nBytes; +} + +bool comQueRecv::copyOutBytes ( void *pBuf, unsigned nBytes ) +{ + char *pCharBuf = static_cast < char * > ( pBuf ); + + this->mutex.lock (); + + // dont return partial message + if ( nBytes > this->occupiedBytes () ) { + this->mutex.unlock (); + return false; + } + + unsigned bytesLeft = nBytes; + while ( bytesLeft ) { + comBuf * pComBuf = this->bufs.first (); + assert ( pComBuf ); + bytesLeft -= pComBuf->copyOutBytes ( &pCharBuf[nBytes-bytesLeft], bytesLeft ); + if ( pComBuf->occupiedBytes () == 0u ) { + this->bufs.remove ( *pComBuf ); + pComBuf->destroy (); + } + } + + this->mutex.unlock (); + + return true; +} + +unsigned comQueRecv::fillFromWire () +{ + // this approach requires that only one thread performs fill + // but its advantage is that the lock is not held while filling + + comBuf *pComBuf = new comBuf; + if ( ! pComBuf ) { + // no way to be informed when memory is available + threadSleep ( 0.5 ); + return 0u; + } + + unsigned nNewBytes = pComBuf->fillFromWire ( *this ); + + this->mutex.lock (); + + comBuf *pLastBuf = this->bufs.last (); + if ( pLastBuf ) { + pLastBuf->copyIn ( *pComBuf ); + } + if ( pComBuf->occupiedBytes () ) { + this->bufs.add ( *pComBuf ); + } + else { + pComBuf->destroy (); + } + + this->mutex.unlock (); + + return nNewBytes; +} + diff --git a/src/ca/comQueSend.cpp b/src/ca/comQueSend.cpp new file mode 100644 index 000000000..29455139a --- /dev/null +++ b/src/ca/comQueSend.cpp @@ -0,0 +1,814 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +// +// Requirements: +// 1) Allow sufficent headroom so that users will be able to perform +// a reasonable amount of IO within CA callbacks without experiencing +// a push/pull deadlock. If a potential push/pull deadlock situation +// occurs then detect and avoid it and provide diagnotic to the user +// via special status. +// 2) Return status to the user when there is insufficent memory to +// queue a complete message. +// 3) return status to the user when a message cant be flushed because +// a connection dropped. +// 4) Do not allocate too much memory in exception situatons (such as +// after a circuit disconnect). +// 5) Avoid allocating more memory than is absolutely necessary to meet +// the above requirements. +// 6) Message fragments must never be sent to the IOC when there isnt +// enough memory to queue part of a message (we also must not force +// a disconnect because the client is starved for memory). +// 7) avoid the need to check status for each byte pushed into the +// protocol stream. +// +// Implementation: +// 1) When queuing a complete message, first test to see if a flush is +// required. If it is a receive thread schedual the flush with the +// send thread, and otherwise directly execute the system call. The +// send thread must run at a higher priority than the receive thread +// if we are to minimize memory consumption. +// 2) Preallocate space for the entire message prior to copying in the +// message so that message fragments are not flushed out just prior +// to detecting that memory is unavailable. +// 3) Return a special error constant when the following situations +// are detected when the user is attempting to queue a request +// from within a user callback executed by a receive thread: +// a) A user is queuing more requests that demand a response from a +// callback than are removed by the response that initiated the +// callback, and this situation persists for many callbacks until +// all buffering in the system is exausted. +// b) A user is queuing many requests that demand a response from one +// callback until all buffering in the system is exausted. +// c) Some combination of both (a) nad (b). +// +// + +#include +#include + +tsFreeList < class comBuf, 0x20, true > comBuf::freeList; + +// nill message pad bytes +static const char nillBytes[] = +{ + 0, 0, 0, 0, + 0, 0, 0, 0 +}; + +inline bufferReservoir::~bufferReservoir () +{ + comBuf *pBuf; + while ( pBuf = this->reservedBufs.get () ) { + pBuf->destroy (); + } +} + +inline comBuf *bufferReservoir::fetchOneBuffer () +{ + return this->reservedBufs.get (); +} + +inline bool bufferReservoir::addOneBuffer () +{ + comBuf *pBuf = new comBuf; + if ( pBuf ) { + this->reservedBufs.add ( *pBuf ); + return true; + } + else { + return false; + } +} + +inline unsigned bufferReservoir::nBytes () +{ + return ( this->reservedBufs.count () * comBuf::maxBytes () ); +} + +// o lock the comQueSend +// o 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) +// o unlock comQueSend if status is not ECA_NORMAL +inline int comQueSend::lockAndReserveSpace ( unsigned msgSize, bufferReservoir &reservoir ) +{ + unsigned bytesReserved = reservoir.nBytes (); + unsigned unoccupied; + + this->mutex.lock (); + + comBuf *pComBuf = this->bufs.last (); + if ( pComBuf ) { + unoccupied = pComBuf->unoccupiedBytes (); + } + else { + unoccupied = 0u; + } + + // flush if conditions indicate. second part of this guarantees + // that we will not flush out a buffer with almost nothing + // in it (this has a large impact on performance) + if ( this->bufs.count () <= 1u || unoccupied >= msgSize ) { + bytesReserved = unoccupied; + } + else { + this->mutex.unlock (); + if ( ! this->flushToWire () ) { + return ECA_DISCONNCHID; + } + if ( this->bufs.count () >= 32u ) { + return ECA_TOLARGE; + } + this->mutex.lock (); + } + + while ( bytesReserved < msgSize ) { + if ( reservoir.addOneBuffer() ) { + bytesReserved += comBuf::maxBytes (); + } + else { + this->mutex.unlock (); + return ECA_ALLOCMEM; + } + } + + return ECA_NORMAL; +} + +// 1) This routine is private because it assumes that the lock +// is applied +// +// 2) 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). +// +// 3) Arguments here are a bit verbose until compilers all implement +// member template functions. +// + +template < class T > +inline void comQueSend_copyIn ( tsDLList < comBuf > &comBufList, + bufferReservoir &reservoir, const T *pVal, unsigned nElem ) +{ + unsigned nCopied; + + comBuf *pComBuf = comBufList.last (); + if ( pComBuf ) { + nCopied = pComBuf->copyIn ( pVal, nElem ); + } + else { + nCopied = 0u; + } + + while ( nElem > nCopied ) { + 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 ( tsDLList < comBuf > &comBufList, bufferReservoir &reservoir, const T &val ) +{ + 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 ); +} + +void comQueSend::copy_dbr_string ( bufferReservoir &reservoir, const void *pValue, unsigned nElem ) +{ + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( pValue ), nElem ); +} + +void comQueSend::copy_dbr_short ( bufferReservoir &reservoir, const void *pValue, unsigned nElem ) +{ + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( pValue ), nElem ); +} + +void comQueSend::copy_dbr_float ( bufferReservoir &reservoir, const void *pValue, unsigned nElem ) +{ + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( pValue ), nElem ); +} + +void comQueSend::copy_dbr_char ( bufferReservoir &reservoir, const void *pValue, unsigned nElem ) +{ + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( pValue ), nElem ); +} + +void comQueSend::copy_dbr_long ( bufferReservoir &reservoir, const void *pValue, unsigned nElem ) +{ + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( pValue ), nElem ); +} + +void comQueSend::copy_dbr_double ( bufferReservoir &reservoir, const void *pValue, unsigned nElem ) +{ + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( pValue ), nElem ); +} + +const comQueSend::copyFunc_t comQueSend::dbrCopyVector [] = { + comQueSend::copy_dbr_string, + comQueSend::copy_dbr_short, + comQueSend::copy_dbr_float, + comQueSend::copy_dbr_short, // DBR_ENUM + comQueSend::copy_dbr_char, + comQueSend::copy_dbr_long, + comQueSend::copy_dbr_double, + 0, // DBR_STS_SHORT + 0, // DBR_STS_FLOAT + 0, // DBR_STS_ENUM + 0, // DBR_STS_CHAR + 0, // DBR_STS_LONG + 0, // DBR_STS_DOUBLE + 0, // DBR_TIME_STRING + 0, // DBR_TIME_INT + 0, // DBR_TIME_SHORT + 0, // DBR_TIME_FLOAT + 0, // DBR_TIME_ENUM + 0, // DBR_TIME_CHAR + 0, // DBR_TIME_LONG + 0, // DBR_TIME_DOUBLE + 0, // DBR_GR_STRING + 0, // DBR_GR_SHORT + 0, // DBR_GR_FLOAT + 0, // DBR_GR_ENUM + 0, // DBR_GR_CHAR + 0, // DBR_GR_LONG + 0, // DBR_GR_DOUBLE + 0, // DBR_CTRL_STRING + 0, // DBR_CTRL_SHORT + 0, // DBR_CTRL_FLOAT + 0, // DBR_CTRL_ENUM + 0, // DBR_CTRL_CHAR + 0, // DBR_CTRL_LONG + 0, // DBR_CTRL_DOUBLE + comQueSend::copy_dbr_short, // DBR_PUT_ACKT + comQueSend::copy_dbr_short, // DBR_PUT_ACKS + 0, // DBR_STSACK_STRING + 0 // DBR_CLASS_NAME +}; + +comQueSend::~comQueSend () +{ + comBuf *pBuf; + + this->mutex.lock (); + while ( ( pBuf = this->bufs.get () ) ) { + pBuf->destroy (); + } + this->mutex.unlock (); +} + +unsigned comQueSend::occupiedBytes () const +{ + this->mutex.lock (); + + unsigned count = this->bufs.count (); + unsigned nBytes; + if ( count >= 2u ) { + nBytes = this->bufs.first ()->occupiedBytes (); + nBytes += this->bufs.last ()->occupiedBytes (); + nBytes += ( count - 2u ) * comBuf::maxBytes (); + } + else if ( count == 1u ) { + nBytes = this->bufs.first ()->occupiedBytes (); + } + else { + nBytes = 0u; + } + + this->mutex.unlock (); + + return nBytes; +} + +bool comQueSend::flushToWire () +{ + bool success; + + // the recv thread is not permitted to flush as this + // can result in a push / pull deadlock on the TCP pipe, + // but in that case this does schedual the flush through + // the higher priority send thread + if ( ! this->flushToWirePermit () ) { + return true; + } + + // this approach requires that only one thread at a time + // performs flushes but its advantage is that the primary + // lock is not held while sending and this prevents deadlocks + this->flushMutex.lock (); + + while ( true ) { + this->mutex.lock (); + comBuf * pBuf = this->bufs.get (); + this->mutex.unlock (); + if ( ! pBuf ) { + success = true; + break; + } + bool success = pBuf->flushToWire ( *this ); + pBuf->destroy (); + if ( ! success ) { + comBuf *pBuf; + + this->mutex.lock (); + while ( ( pBuf = this->bufs.get () ) ) { + pBuf->destroy (); + } + this->mutex.unlock (); + + break; + } + } + + this->flushMutex.unlock (); + + return success; +} + +int comQueSend::writeRequest ( unsigned serverId, unsigned type, unsigned nElem, const void *pValue ) +{ + bufferReservoir reservoir; + unsigned size, postcnt; + bool stringOptim; + + if ( ! this->dbrCopyVector [type] ) { + return ECA_BADTYPE; + } + if ( nElem > 0xffff) { + return ECA_BADCOUNT; + } + + 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; + } + + postcnt = CA_MESSAGE_ALIGN ( size ); + if ( postcnt > 0xffff ) { + return ECA_BADCOUNT; + } + + assert ( serverId <= 0xffffffff ); + + int status = this->lockAndReserveSpace ( postcnt + 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_WRITE ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( postcnt ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( type ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( nElem ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( serverId ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( ~0UL ) ); // available + if ( stringOptim ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( pValue ), size ); + } + else { + ( this->*dbrCopyVector [type] ) ( reservoir, pValue, nElem ); + } + comQueSend_copyIn ( this->bufs, reservoir, nillBytes, postcnt - size ); + this->mutex.unlock (); + } + + return status; +} + +int comQueSend::writeNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, const void *pValue ) +{ + bufferReservoir reservoir; + ca_uint32_t size, postcnt; + + if ( ! this->dbrCopyVector [type] ) { + return ECA_BADTYPE; + } + if ( nElem > 0xffff) { + return ECA_BADCOUNT; + } + + if ( type == DBR_STRING && nElem == 1 ) { + char *pstr = (char *) pValue; + size = strlen ( pstr ) +1; + } + else { + size = dbr_size_n ( type, nElem ); + } + postcnt = CA_MESSAGE_ALIGN ( size ); + if ( postcnt > 0xffff ) { + return ECA_BADCOUNT; + } + + assert ( serverId <= 0xffffffff ); + + int status = this->lockAndReserveSpace ( postcnt + 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_WRITE_NOTIFY ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( postcnt ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( type ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( nElem ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( serverId ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( ioId ) ); // available + ( this->*dbrCopyVector [type] ) ( reservoir, pValue, nElem ); + comQueSend_copyIn ( this->bufs, reservoir, nillBytes, postcnt - size ); + this->mutex.unlock (); + } + + + return status; +} + +int comQueSend::readCopyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ) +{ + bufferReservoir reservoir; + + if ( nElem > 0xffff) { + return ECA_BADCOUNT; + } + if ( type > 0xffff) { + return ECA_BADTYPE; + } + + assert ( serverId <= 0xffffffff ); + + int status = this->lockAndReserveSpace ( 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_READ ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( type ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( nElem ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( serverId ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( ioId ) ); // available + this->mutex.unlock (); + } + + return status; +} + +int comQueSend::readNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ) +{ + bufferReservoir reservoir; + + if ( nElem > 0xffff) { + return ECA_BADCOUNT; + } + if ( type > 0xffff) { + return ECA_BADTYPE; + } + + assert ( serverId <= 0xffffffff ); + + int status = this->lockAndReserveSpace ( 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_READ_NOTIFY ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( type ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( nElem ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( serverId ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( ioId ) ); // available + this->mutex.unlock (); + } + + return status; +} + +int comQueSend::createChannelRequest ( unsigned id, const char *pName, unsigned nameLength ) +{ + bufferReservoir reservoir; + + unsigned postCnt = CA_MESSAGE_ALIGN ( nameLength ); + assert ( id <= 0xffffffff ); + assert ( postCnt <= 0xffff ); + + int status = this->lockAndReserveSpace ( postCnt + 16u, reservoir ); + if ( status == ECA_NORMAL ) { + + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_CLAIM_CIU ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( postCnt ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( id ) ); // cid + // + // The available field is used (abused) + // here to communicate the minor version number + // starting with CA 4.1. + // + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_MINOR_VERSION ) ); // available + if ( nameLength ) { + comQueSend_copyIn ( this->bufs, reservoir, pName, nameLength ); + } + if ( postCnt > nameLength ) { + comQueSend_copyIn ( this->bufs, reservoir, nillBytes, postCnt - nameLength ); + } + this->mutex.unlock (); + } + + return status; +} + +int comQueSend::clearChannelRequest ( unsigned clientId, unsigned serverId ) +{ + bufferReservoir reservoir; + + assert ( serverId <= 0xffffffff ); + assert ( clientId <= 0xffffffff ); + + int status = this->lockAndReserveSpace ( 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_CLEAR_CHANNEL ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( serverId ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( clientId ) ); // available + this->mutex.unlock (); + } + + return status; +} + +int comQueSend::subscriptionRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, unsigned mask ) +{ + bufferReservoir reservoir; + + if ( nElem > 0xffff) { + return ECA_BADCOUNT; + } + if ( type > 0xffff) { + return ECA_BADTYPE; + } + assert ( serverId <= 0xffffffff ); + assert ( ioId <= 0xffffffff ); + + int status = this->lockAndReserveSpace ( 32u, reservoir ); + if ( status == ECA_NORMAL ) { + + // header + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_EVENT_ADD ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 16u ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( type ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( nElem ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( serverId ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( ioId ) ); // available + + // extension + comQueSend_copyIn ( this->bufs, reservoir, static_cast < ca_float32_t > ( 0.0 ) ); // m_lval + comQueSend_copyIn ( this->bufs, reservoir, static_cast < ca_float32_t > ( 0.0 ) ); // m_hval + comQueSend_copyIn ( this->bufs, reservoir, static_cast < ca_float32_t > ( 0.0 ) ); // m_toval + comQueSend_copyIn ( this->bufs, reservoir, static_cast < ca_uint16_t > ( mask ) ); // m_mask + comQueSend_copyIn ( this->bufs, reservoir, static_cast < ca_uint16_t > ( 0u ) ); // m_pad + + this->mutex.unlock (); + } + + return status; +} + +int comQueSend::subscriptionCancelRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ) +{ + bufferReservoir reservoir; + + assert ( type <= 0xffff ); + assert ( nElem <= 0xffff ); + assert ( serverId <= 0xffffffff ); + assert ( ioId <= 0xffffffff ); + + int status = this->lockAndReserveSpace ( 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_EVENT_CANCEL ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( type ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( nElem ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( serverId ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( ioId ) ); // available + this->mutex.unlock (); + } + + return status; +} + +int comQueSend::disableFlowControlRequest () +{ + bufferReservoir reservoir; + + int status = this->lockAndReserveSpace ( 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_EVENTS_ON ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // available + this->mutex.unlock (); + } + return status; +} + +int comQueSend::enableFlowControlRequest () +{ + bufferReservoir reservoir; + + int status = this->lockAndReserveSpace ( 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_EVENTS_OFF ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // available + this->mutex.unlock (); + } + return status; +} + +int comQueSend::noopRequest () +{ + bufferReservoir reservoir; + + int status = this->lockAndReserveSpace ( 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_NOOP ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // available + this->mutex.unlock (); + } + return status; +} + +int comQueSend::echoRequest () +{ + bufferReservoir reservoir; + + int status = this->lockAndReserveSpace ( 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_ECHO ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // available + this->mutex.unlock (); + } + return status; +} + +int comQueSend::hostNameSetRequest ( const char *pName ) +{ + bufferReservoir reservoir; + unsigned size = strlen ( pName ) + 1u; + unsigned postSize = CA_MESSAGE_ALIGN ( size ); + assert ( postSize < 0xffff ); + + int status = this->lockAndReserveSpace ( postSize + 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_HOST_NAME ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( postSize ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // available + + comQueSend_copyIn ( this->bufs, reservoir, pName, size ); + comQueSend_copyIn ( this->bufs, reservoir, nillBytes, postSize - size ); + this->mutex.unlock (); + } + return status; +} + +int comQueSend::userNameSetRequest ( const char *pName ) +{ + bufferReservoir reservoir; + unsigned size = strlen ( pName ) + 1u; + unsigned postSize = CA_MESSAGE_ALIGN ( size ); + assert ( postSize < 0xffff ); + + int status = this->lockAndReserveSpace ( postSize + 16u, reservoir ); + if ( status == ECA_NORMAL ) { + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( CA_PROTO_CLIENT_NAME ) ); // cmd + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( postSize ) ); // postsize + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // dataType + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // count + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // cid + comQueSend_copyIn ( this->bufs, reservoir, static_cast ( 0u ) ); // available + + comQueSend_copyIn ( this->bufs, reservoir, pName, size ); + comQueSend_copyIn ( this->bufs, reservoir, nillBytes, postSize - size ); + this->mutex.unlock (); + } + return status; +} + +#if 0 +/* + * tcpiiu::pushStreamMsg () + */ +int tcpiiu::pushStreamMsg ( const caHdr &hdr, const void *pext, unsigned extsize ) +{ + unsigned alignedExtSize; + bool status; + msgDescriptor msgs[3]; + caHdr msgHdr = hdr; + + if ( extsize > 0xffff - 7 ) { + return ECA_TOLARGE; + } + + alignedExtSize = CA_MESSAGE_ALIGN ( extsize ); + msgHdr.m_postsize = htons ( alignedExtSize ); + + debugPrintf ( ( + "CAC: Request => cmmd=%x cid=0x%x type=%x count=%x postsize=%x\n", + hdr.m_cmmd, hdr.m_cid, hdr.m_dataType, + hdr.m_count, hdr.m_postsize ) ); + + msgs[0].pBuf = &msgHdr; + msgs[0].nBytes = sizeof ( msgHdr ); + msgs[1].pBuf = pext; + msgs[1].nBytes = extsize; + if ( alignedExtSize > extsize ) { + unsigned diff = alignedExtSize - extsize; + assert ( diff <= sizeof ( nullBuff ) ); + msgs[2].pBuf = nullBuff; + msgs[2].nBytes = diff; + status = this->copyInBytes ( msgs, 3u ); + } + else { + status = this->copyInBytes ( msgs, 2u ); + } + + if ( status ) { + return ECA_NORMAL; + } + else { + this->shutdown (); + return ECA_ALLOCMEM; + } +} + +/* + * tcpiiu::pushStreamMsg () + */ +int tcpiiu::pushStreamMsg ( const caHdr &hdr ) +{ + caHdr msgHdr = hdr; + msgHdr.m_postsize = htons ( 0 ); + + debugPrintf ( ( + "CAC: Request => cmmd=%x cid=0x%x type=%x count=%x postsize=%x\n", + hdr.m_cmmd, hdr.m_cid, hdr.m_dataType, + hdr.m_count, hdr.m_postsize ) ); + + bool status = this->copyIn ( msgHdr ); + if ( status ) { + return ECA_NORMAL; + } + else { + this->shutdown (); + return ECA_ALLOCMEM; + } +} +#endif \ No newline at end of file diff --git a/src/ca/comQueSend_IL.h b/src/ca/comQueSend_IL.h new file mode 100644 index 000000000..e1f0cad79 --- /dev/null +++ b/src/ca/comQueSend_IL.h @@ -0,0 +1,25 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 2000, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef comQueSend_ILh +#define comQueSend_ILh + + + + +#endif // comQueSend_ILh + diff --git a/src/ca/conn.cpp b/src/ca/conn.cpp deleted file mode 100644 index 8e94d9d23..000000000 --- a/src/ca/conn.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* $Id$ */ -/* - * - * L O S A L A M O S - * Los Alamos National Laboratory - * Los Alamos, New Mexico 87545 - * - * Copyright, 1986, The Regents of the University of California. - * - * Author: Jeff Hill - */ - -#include "iocinf.h" - -#ifdef DEBUG -#define LOGRETRYINTERVAL logRetryInterval(__FILE__, __LINE__); -LOCAL void logRetryInterval (pcac, char *pFN, unsigned lineno); -#else -#define LOGRETRYINTERVAL -#endif - diff --git a/src/ca/disconnectTimer.cpp b/src/ca/disconnectTimer.cpp deleted file mode 100644 index 8b1378917..000000000 --- a/src/ca/disconnectTimer.cpp +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/ca/hostNameCache.cpp b/src/ca/hostNameCache.cpp new file mode 100644 index 000000000..f051ab90e --- /dev/null +++ b/src/ca/hostNameCache.cpp @@ -0,0 +1,57 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#include "iocinf.h" + +hostNameCache::hostNameCache ( const osiSockAddr &addr, ipAddrToAsciiEngine &engine ) : + ipAddrToAsciiAsynchronous ( addr ), + pHostName ( 0u ) +{ + this->ioInitiate ( engine ); +} + +hostNameCache::~hostNameCache () +{ + if ( this->pHostName ) { + delete [] this->pHostName; + } +} + +void hostNameCache::ioCompletionNotify ( const char *pHostNameIn ) +{ + if ( ! this->pHostName ) { + unsigned size = strlen ( pHostNameIn ) + 1u; + char *pTmp = new char [size]; + if ( ! pTmp ) { + // we fail over to using the IP address for the name + return; + } + strcpy ( pTmp, pHostNameIn ); + this->pHostName = pTmp; + } +} + +void hostNameCache::hostName ( char *pBuf, unsigned bufSize ) const +{ + if ( this->pHostName ) { + strncpy ( pBuf, this->pHostName, bufSize); + } + else { + osiSockAddr addr = this->address(); + sockAddrToDottedA ( &addr.sa, pBuf, bufSize ); + } + pBuf [ bufSize - 1u ] = '\0'; +} diff --git a/src/ca/iocinf.cpp b/src/ca/iocinf.cpp index 386a0d9c2..782fd4cae 100644 --- a/src/ca/iocinf.cpp +++ b/src/ca/iocinf.cpp @@ -116,8 +116,8 @@ epicsShareFunc void epicsShareAPI setPortAndRemoveDuplicates if (pNode->addr.ia.sin_addr.s_addr == pTmpNode->addr.ia.sin_addr.s_addr && pNode->addr.ia.sin_port == pTmpNode->addr.ia.sin_port) { char buf[64]; - ipAddrToA (&pNode->addr.ia, buf, sizeof(buf)); - ca_printf ("Warning: Duplicate EPICS CA Address list entry \"%s\" discarded\n", buf); + ipAddrToA ( &pNode->addr.ia, buf, sizeof (buf) ); + ca_printf ( "Warning: Duplicate EPICS CA Address list entry \"%s\" discarded\n", buf ); free (pNode); pNode = NULL; break; @@ -139,7 +139,7 @@ epicsShareFunc void epicsShareAPI setPortAndRemoveDuplicates * configureChannelAccessAddressList () */ epicsShareFunc void epicsShareAPI configureChannelAccessAddressList - (ELLLIST *pList, SOCKET sock, unsigned short port) + ( ELLLIST *pList, SOCKET sock, unsigned short port ) { ELLLIST tmpList; char *pstr; @@ -149,7 +149,7 @@ epicsShareFunc void epicsShareAPI configureChannelAccessAddressList /* * dont load the list twice */ - assert ( ellCount(pList) == 0 ); + assert ( ellCount (pList) == 0 ); ellInit ( &tmpList ); @@ -202,16 +202,16 @@ epicsShareFunc void epicsShareAPI configureChannelAccessAddressList /* * printChannelAccessAddressList () */ -epicsShareFunc void epicsShareAPI printChannelAccessAddressList (const ELLLIST *pList) +epicsShareFunc void epicsShareAPI printChannelAccessAddressList ( const ELLLIST *pList ) { osiSockAddrNode *pNode; - printf ("Channel Access Address List\n"); - pNode = (osiSockAddrNode *) ellFirst (pList); + printf ( "Channel Access Address List\n" ); + pNode = (osiSockAddrNode *) ellFirst ( pList ); while (pNode) { char buf[64]; - ipAddrToA (&pNode->addr.ia, buf, sizeof(buf)); - printf ("%s\n", buf); - pNode = (osiSockAddrNode *) ellNext (&pNode->node); + ipAddrToA ( &pNode->addr.ia, buf, sizeof ( buf ) ); + printf ( "%s\n", buf ); + pNode = (osiSockAddrNode *) ellNext ( &pNode->node ); } } diff --git a/src/ca/iocinf.h b/src/ca/iocinf.h index 00174bd88..e5ffa56d2 100644 --- a/src/ca/iocinf.h +++ b/src/ca/iocinf.h @@ -1,4 +1,3 @@ - /* * $Id$ * @@ -35,7 +34,7 @@ #include #include -#if defined(epicsExportSharedSymbols) +#if defined (epicsExportSharedSymbols) #error suspect that libCom was not imported #endif @@ -45,7 +44,6 @@ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "epicsAssert.h" #include "bucketLib.h" -#include "ellLib.h" #include "envDefs.h" #include "epicsPrint.h" #include "tsStamp.h" @@ -59,9 +57,10 @@ #include "osiEvent.h" #include "resourceLib.h" #include "localHostName.h" +#include "ipAddrToAsciiAsynchronous.h" -#if defined(epicsExportSharedSymbols) -#error suspect that libCom was not imported +#if defined ( epicsExportSharedSymbols ) +# error suspect that libCom was not imported #endif /* @@ -76,16 +75,15 @@ */ #include "caProto.h" #include "net_convert.h" -#include "ringBuffer.h" #ifndef FALSE -# define FALSE 0 +# define FALSE 0 #elif FALSE # error FALSE isnt boolean false #endif #ifndef TRUE -# define TRUE 1 +# define TRUE 1 #elif !TRUE # error TRUE isnt boolean true #endif @@ -114,6 +112,121 @@ */ #define STRING_LIMIT 512 +static const unsigned comBufSize = 0x4000; + +class comBuf : public tsDLNode < comBuf > { +public: + comBuf (); + void destroy (); + unsigned unoccupiedBytes () const; + unsigned occupiedBytes () const; + static unsigned maxBytes (); + unsigned copyInBytes ( const void *pBuf, unsigned nBytes ); + unsigned copyIn ( comBuf & ); + unsigned copyIn ( const epicsInt8 *pValue, unsigned nElem ); + unsigned copyIn ( const epicsUInt8 *pValue, unsigned nElem ); + unsigned copyIn ( const epicsInt16 *pValue, unsigned nElem ); + unsigned copyIn ( const epicsUInt16 *pValue, unsigned nElem ); + unsigned copyIn ( const epicsInt32 *pValue, unsigned nElem ); + unsigned copyIn ( const epicsUInt32 *pValue, unsigned nElem ); + unsigned copyIn ( const epicsFloat32 *pValue, unsigned nElem ); + unsigned copyIn ( const epicsFloat64 *pValue, unsigned nElem ); + unsigned copyIn ( const epicsOldString *pValue, unsigned nElem ); + bool copyInAllBytes ( const void *pBuf, unsigned nBytes ); + unsigned copyOutBytes ( void *pBuf, unsigned nBytes ); + bool copyOutAllBytes ( void *pBuf, unsigned nBytes ); + unsigned removeBytes ( unsigned nBytes ); + void * operator new ( size_t size ); + void operator delete ( void *pCadaver, size_t size ); + bool flushToWire ( class comQueSend & ); + unsigned fillFromWire ( class comQueRecv & ); +private: + static tsFreeList < class comBuf, 0x20, true > freeList; + + ~comBuf (); + unsigned nextWriteIndex; + unsigned nextReadIndex; + unsigned char buf [ comBufSize ]; // optimal for 100 Mb Ethernet LAN MTU + + unsigned clipNElem ( unsigned elemSize, unsigned nElem ); +}; + +struct msgDescriptor { + const void *pBuf; + unsigned nBytes; +}; + +template < class T > +void comQueSend_copyIn ( comQueSend &que, const T *pVal, unsigned nElem ); + +template < class T > +void comQueSend_copyIn ( comQueSend &que, const T &val ); + +class bufferReservoir { +public: + ~bufferReservoir (); + bool addOneBuffer (); + comBuf *fetchOneBuffer (); + unsigned nBytes (); +private: + tsDLList < comBuf > reservedBufs; +}; + +class comQueSend { +public: + virtual ~comQueSend (); + unsigned occupiedBytes () const; + bool flushToWire (); + int writeRequest ( unsigned serverId, unsigned type, unsigned nElem, const void *pValue ); + int writeNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, const void *pValue ); + int readCopyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ); + int readNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ); + int createChannelRequest ( unsigned clientId, const char *pName, unsigned nameLength ); + int clearChannelRequest ( unsigned clientId, unsigned serverId ); + int subscriptionRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, unsigned mask ); + int subscriptionCancelRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ); + int disableFlowControlRequest (); + int enableFlowControlRequest (); + int echoRequest (); + int noopRequest (); + int hostNameSetRequest ( const char *pName ); + int userNameSetRequest ( const char *pName ); + + virtual unsigned sendBytes ( const void *pBuf, unsigned nBytesInBuf ) = 0; + +private: + int lockAndReserveSpace ( unsigned msgSize, bufferReservoir & ); + virtual bool flushToWirePermit () = 0; + virtual void shutdown () = 0; + + void copy_dbr_string ( bufferReservoir &, const void *pValue, unsigned nElem ); + void copy_dbr_short ( bufferReservoir &, const void *pValue, unsigned nElem ); + void copy_dbr_float ( bufferReservoir &, const void *pValue, unsigned nElem ); + void copy_dbr_char ( bufferReservoir &, const void *pValue, unsigned nElem ); + void copy_dbr_long ( bufferReservoir &, const void *pValue, unsigned nElem ); + void copy_dbr_double ( bufferReservoir &, const void *pValue, unsigned nElem ); + + tsDLList < comBuf > bufs; + osiMutex mutex; + osiMutex flushMutex; // only one thread flushes at a time + + typedef void ( comQueSend::*copyFunc_t ) ( bufferReservoir &, const void *pValue, unsigned nElem ); + + static const copyFunc_t dbrCopyVector []; +}; + +class comQueRecv { +public: + virtual ~comQueRecv (); + unsigned occupiedBytes () const; + bool copyOutBytes ( void *pBuf, unsigned nBytes ); + unsigned fillFromWire (); + virtual unsigned recvBytes ( void *pBuf, unsigned nBytesInBuf ) = 0; +private: + tsDLList < comBuf > bufs; + osiMutex mutex; +}; + class caClient { public: virtual void exceptionNotify (int status, const char *pContext, @@ -124,12 +237,56 @@ public: private: }; -class nciu : public cacChannelIO, public tsDLNode , - public chronIntIdRes { +/* + * nciu::claimMsg () + */ +class claimMsgCache { public: - nciu ( class cac *pcac, cacChannel &chan, const char *pNameIn ); + claimMsgCache ( bool v44 ); + ~claimMsgCache (); + bool set ( class nciu & chan ); + int deliverMsg ( class tcpiiu &iiu ); + bool channelMatches ( class nciu &chan ); +private: + char *pStr; + unsigned clientId; + unsigned serverId; + unsigned currentStrLen; + unsigned bufLen; + bool v44; +}; + +class netiiu; + +class nciuPrivate { +private: + osiMutex mutex; + osiEvent ptrLockReleaseWakeup; + friend class nciu; +}; + +class cac; + +class cacPrivate { +public: + cacPrivate ( cac & ); + void destroyAllIO (); + void subscribeAllIO (); + void disconnectAllIO ( const char *pHostName ); +protected: + cac &cacCtx; +private: + tsDLList < class baseNMIU > eventq; + friend class cac; +}; + +class nciu : public cacChannelIO, public tsDLNode < nciu >, + public chronIntIdRes < nciu >, public cacPrivate { +public: + nciu ( class cac &cac, cacChannel &chan, const char *pNameIn ); void destroy (); void connect ( class tcpiiu &iiu, unsigned nativeType, unsigned long nativeCount, unsigned sid ); + void connect ( tcpiiu &iiu ); void disconnect (); void searchReplySetUp ( unsigned sid, unsigned typeCode, unsigned long count ); int read ( unsigned type, unsigned long count, void *pValue ); @@ -150,52 +307,61 @@ public: void incrementOutstandingIO (); void decrementOutstandingIO (); void decrementOutstandingIO ( unsigned seqNumber ); - int searchMsg (); - bool claimMsg ( class tcpiiu *piiu ); + bool searchMsg ( unsigned short retrySeqNumber, unsigned &retryNoForThisChannel ); + void subscriptionCancelMsg ( ca_uint32_t clientId ); bool fullyConstructed () const; - void installIO ( class baseNMIU &io ); - void uninstallIO ( class baseNMIU &io ); + bool connectionInProgress ( const osiSockAddr & ); + bool identifierEquivelence ( unsigned idToMatch ); static void * operator new ( size_t size ); static void operator delete ( void *pCadaver, size_t size ); int subscriptionMsg ( unsigned subscriptionId, unsigned typeIn, unsigned long countIn, unsigned short maskIn ); + void resetRetryCount (); + unsigned getRetrySeqNo () const; + void accessRightsStateChange ( const caar &arIn ); + unsigned getSID () const; + void attachChanToIIU ( netiiu &iiu ); + void detachChanFromIIU (); + void ioInstall ( class baseNMIU & ); + void ioDestroy ( unsigned id ); - tsDLList - eventq; - class netiiu *piiu; - char *pNameStr; - unsigned sid; /* server id */ - unsigned retry; /* search retry number */ - unsigned short retrySeqNo; /* search retry seq number */ - unsigned short nameLength; /* channel name length */ - caar ar; /* access rights */ - unsigned claimPending:1; /* T if claim msg was sent */ - unsigned previousConn:1; /* T if connected in the past */ private: + caar ar; /* access rights */ + unsigned count; + char *pNameStr; + netiiu *piiu; + unsigned sid; /* server id */ + unsigned retry; /* search retry number */ + mutable unsigned short ptrLockCount; /* number of times IIU pointer was locked */ + mutable unsigned short ptrUnlockWaitCount; /* number of threads waiting for IIU pointer unlock */ + unsigned short retrySeqNo; /* search retry seq number */ + unsigned short nameLength; /* channel name length */ + unsigned short typeCode; + unsigned f_connected:1; + unsigned f_fullyConstructed:1; + unsigned previousConn:1; /* T if connected in the past */ - unsigned long count; - unsigned short typeCode; - unsigned f_connected:1; - unsigned f_fullyConstructed:1; static tsFreeList < class nciu, 1024 > freeList; ~nciu (); // force pool allocation - int issuePut ( ca_uint16_t cmd, unsigned id, chtype type, - unsigned long count, const void *pvalue ); + //int issuePut ( ca_uint16_t cmd, unsigned id, chtype type, + // unsigned long count, const void *pvalue ); void lock () const; void unlock () const; - + void lockPIIU () const; + void unlockPIIU () const; + void lockOutstandingIO () const; + void unlockOutstandingIO () const; const char * pHostName () const; // deprecated - please do not use + + friend class claimMsgCache; }; -class baseNMIU : public tsDLNode , - public chronIntIdRes { +class baseNMIU : public tsDLNode < baseNMIU >, public chronIntIdRes < baseNMIU > { public: baseNMIU ( nciu &chan ); - void destroy (); - unsigned identifier () const; class cacChannelIO & channelIO () const; virtual void completionNotify () = 0; virtual void completionNotify ( unsigned type, unsigned long count, const void *pData ) = 0; @@ -203,83 +369,92 @@ public: virtual void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ) = 0; virtual int subscriptionMsg (); virtual void disconnect ( const char *pHostName ) = 0; + void destroy (); protected: virtual ~baseNMIU (); // must be allocated from pool nciu &chan; + friend class cac; }; class netSubscription : public cacNotifyIO, public baseNMIU { public: - netSubscription ( nciu &chan, chtype type, unsigned long count, - unsigned short mask, cacNotify ¬ify ); - void destroy (); void completionNotify (); void completionNotify ( unsigned type, unsigned long count, const void *pData ); void exceptionNotify ( int status, const char *pContext ); void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); int subscriptionMsg (); void disconnect ( const char *pHostName ); + static bool factory ( nciu &chan, chtype type, unsigned long count, + unsigned short mask, cacNotify ¬ify, unsigned &id ); +private: + chtype type; + unsigned long count; + unsigned short mask; + + netSubscription ( nciu &chan, chtype type, unsigned long count, + unsigned short mask, cacNotify ¬ify ); + ~netSubscription (); + void destroy (); + static tsFreeList < class netSubscription, 1024 > freeList; static void * operator new ( size_t size ); static void operator delete ( void *pCadaver, size_t size ); -private: - chtype type; - unsigned long count; - unsigned short mask; - ~netSubscription (); - static tsFreeList < class netSubscription, 1024 > freeList; }; class netReadCopyIO : public baseNMIU { public: - netReadCopyIO ( nciu &chan, unsigned type, unsigned long count, - void *pValue, unsigned seqNumber ); void disconnect ( const char *pHostName ); - void destroy (); void completionNotify (); void completionNotify ( unsigned type, unsigned long count, const void *pData ); void exceptionNotify ( int status, const char *pContext ); void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); - static void * operator new ( size_t size ); - static void operator delete ( void *pCadaver, size_t size ); + static bool factory ( nciu &chan, unsigned type, unsigned long count, + void *pValue, unsigned seqNumber, ca_uint32_t &id ); private: unsigned type; unsigned long count; void *pValue; unsigned seqNumber; + netReadCopyIO ( nciu &chan, unsigned type, unsigned long count, + void *pValue, unsigned seqNumber ); ~netReadCopyIO (); // must be allocated from pool + void destroy (); static tsFreeList < class netReadCopyIO, 1024 > freeList; + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); }; class netReadNotifyIO : public cacNotifyIO, public baseNMIU { public: - netReadNotifyIO ( nciu &chan, cacNotify ¬ify ); - void destroy (); void disconnect ( const char *pHostName ); void completionNotify (); void completionNotify ( unsigned type, unsigned long count, const void *pData ); void exceptionNotify ( int status, const char *pContext ); void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); + static bool factory ( nciu &chan, cacNotify ¬ify, ca_uint32_t &id ); +private: + netReadNotifyIO ( nciu &chan, cacNotify ¬ify ); + ~netReadNotifyIO (); + void destroy (); + static tsFreeList < class netReadNotifyIO, 1024 > freeList; static void * operator new ( size_t size ); static void operator delete ( void *pCadaver, size_t size ); -private: - ~netReadNotifyIO (); - static tsFreeList < class netReadNotifyIO, 1024 > freeList; }; class netWriteNotifyIO : public cacNotifyIO, public baseNMIU { public: - netWriteNotifyIO ( nciu &chan, cacNotify ¬ify ); - void destroy (); void disconnect ( const char *pHostName ); void completionNotify (); void completionNotify ( unsigned type, unsigned long count, const void *pData ); void exceptionNotify ( int status, const char *pContext ); void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); + static bool factory ( nciu &chan, cacNotify ¬ify, ca_uint32_t &id ); +private: + netWriteNotifyIO ( nciu &chan, cacNotify ¬ify ); + ~netWriteNotifyIO (); + void destroy (); + static tsFreeList < class netWriteNotifyIO, 1024 > freeList; static void * operator new ( size_t size ); static void operator delete ( void *pCadaver, size_t size ); -private: - ~netWriteNotifyIO (); - static tsFreeList < class netWriteNotifyIO, 1024 > freeList; }; /* @@ -325,11 +500,11 @@ private: * without a delay in between before we go into * monitor flow control * - * turning this down effect maximum throughput + * turning this down effects maximum throughput * because we dont get an optimal number of bytes * per network frame */ -#define MAX_CONTIGUOUS_MSG_COUNT 10 +static const unsigned contiguousMsgCountWhichTriggersFlowControl = 10u; #define SEND_RETRY_COUNT_INIT 100 @@ -337,42 +512,56 @@ enum iiu_conn_state {iiu_connecting, iiu_connected, iiu_disconnected}; extern threadPrivateId cacRecursionLock; -class baseIIU { +class netiiu { public: - baseIIU (class cac *pcac) : pcas (pcac) {} - class cac *pcas; -}; - -class netiiu : public baseIIU { -public: - netiiu (class cac *pcac); + netiiu ( class cac &cac ); virtual ~netiiu (); - void show (unsigned level) const; - - virtual bool compareIfTCP (nciu &chan, const sockaddr_in &) const = 0; + void show ( unsigned level ) const; + unsigned channelCount () const; + cac & clientCtx () const; + void disconnectAllChan (); + void detachAllChan (); + void connectTimeoutNotify (); + void sendPendingClaims ( tcpiiu &iiu, bool v42Ok, claimMsgCache &cache ); + bool searchMsg ( unsigned short retrySeqNumber, unsigned &retryNoForThisChannel ); + void resetChannelRetryCounts (); virtual void hostName (char *pBuf, unsigned bufLength) const = 0; - virtual bool ca_v42_ok () const = 0; - virtual bool ca_v41_ok () const = 0; - virtual int pushStreamMsg (const caHdr *pmsg, const void *pext, bool BlockingOk) = 0; - virtual int pushDatagramMsg (const caHdr *pMsg, const void *pExt, ca_uint16_t extsize) = 0; - virtual void addToChanList ( nciu &chan ) = 0; - virtual void removeFromChanList ( nciu &chan ) = 0; - virtual void disconnect ( nciu &chan ) = 0; - virtual const char * pHostName () const = 0; // deprecated - please do not use + virtual bool connectionInProgress ( const char *pChannelName, const osiSockAddr &addr ) const; + virtual bool ca_v42_ok () const; + virtual bool ca_v41_ok () const; + virtual bool pushDatagramMsg (const caHdr &hdr, const void *pExt, ca_uint16_t extsize); - tsDLList chidList; + virtual int writeRequest ( unsigned serverId, unsigned type, unsigned nElem, const void *pValue); + virtual int writeNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, const void *pValue ); + virtual int readCopyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ); + virtual int readNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ); + virtual int subscriptionRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, unsigned mask ); + virtual int subscriptionCancelRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ); + virtual int createChannelRequest ( unsigned clientId, const char *pName, unsigned nameLength ); + virtual int clearChannelRequest ( unsigned clientId, unsigned serverId ); + +protected: + void lock () const; + void unlock () const; private: + tsDLList chidList; + class cac &cacRef; + osiMutex mutex; + + friend class nciu; + + virtual void lastChannelDetachNotify (); }; class udpiiu; -class searchTimer : public osiTimer { +class searchTimer : public osiTimer, private osiMutex { public: - searchTimer (udpiiu &iiu, osiTimerQueue &queue); + searchTimer ( udpiiu &iiu, osiTimerQueue &queue ); void notifySearchResponse ( unsigned short retrySeqNo ); - void reset (double delayToNextTry); + void reset ( double delayToNextTry ); private: virtual void expire (); @@ -387,12 +576,12 @@ private: udpiiu &iiu; unsigned framesPerTry; /* # of UDP frames per search try */ unsigned framesPerTryCongestThresh; /* one half N tries w congest */ - unsigned minRetry; /* min retry no so far */ + unsigned minRetry; /* min retry number so far */ unsigned retry; - unsigned searchTries; /* num search tries within seq # */ - unsigned searchResponses; /* num valid search resp within seq # */ + unsigned searchTriesWithinThisPass; /* num search tries within this pass through the list */ + unsigned searchResponsesWithinThisPass; /* num search resp within this pass through the list */ unsigned short retrySeqNo; /* search retry seq number */ - unsigned short retrySeqNoAtListBegin; /* search retry seq number at beg of list */ + unsigned short retrySeqAtPassBegin; /* search retry seq number at beg of pass through list */ double period; /* period between tries */ }; @@ -415,44 +604,76 @@ private: bool once; }; +extern "C" void cacRecvThreadUDP (void *pParam); + class udpiiu : public netiiu { public: - udpiiu (cac *pcac); + udpiiu ( cac &cac ); virtual ~udpiiu (); void shutdown (); - void hostName ( char *pBuf, unsigned bufLength ) const; - bool ca_v42_ok () const; - bool ca_v41_ok () const; - void addToChanList ( nciu &chan ); - void removeFromChanList ( nciu &chan ); - void disconnect ( nciu &chan ); void recvMsg (); - int post_msg (const struct sockaddr_in *pnet_addr, - char *pInBuf, unsigned long blockSize); - int pushStreamMsg ( const caHdr *pmsg, const void *pext, bool BlockingOk ); - int pushDatagramMsg (const caHdr *pMsg, const void *pExt, ca_uint16_t extsize); + int postMsg ( const osiSockAddr &net_addr, + char *pInBuf, unsigned long blockSize ); void repeaterRegistrationMessage ( unsigned attemptNumber ); void flush (); SOCKET getSock () const; - - osiTime recvTime; - char xmitBuf[MAX_UDP]; - char recvBuf[ETHERNET_MAX_UDP]; - semMutexId xmitBufLock; - ELLLIST dest; - semBinaryId recvThreadExitSignal; - unsigned nBytesInXmitBuf; - unsigned short repeaterPort; - bool shutdownCmd; + bool repeaterInstalled (); // exceptions class noSocket {}; class noMemory {}; private: - SOCKET sock; - bool compareIfTCP (nciu &chan, const sockaddr_in &) const; + osiTime recvTime; + char xmitBuf [MAX_UDP_SEND]; + char recvBuf [MAX_UDP_RECV]; + ELLLIST dest; + semBinaryId recvThreadExitSignal; + unsigned nBytesInXmitBuf; + SOCKET sock; + unsigned short repeaterPort; + unsigned short serverPort; + bool shutdownCmd; const char * pHostName () const; // deprecated - please do not use + void hostName ( char *pBuf, unsigned bufLength ) const; + + bool pushDatagramMsg ( const caHdr &msg, const void *pExt, ca_uint16_t extsize ); + + friend void cacRecvThreadUDP ( void *pParam ); + + typedef void (udpiiu::*pProtoStubUDP) ( const caHdr &, const osiSockAddr & ); + + // UDP protocol dispatch table + static const pProtoStubUDP udpJumpTableCAC[]; + + // UDP protocol stubs + void noopAction ( const caHdr &, const osiSockAddr & ); + void badUDPRespAction ( const caHdr &msg, const osiSockAddr &netAddr ); + void searchRespAction ( const caHdr &msg, const osiSockAddr &net_addr ); + void exceptionRespAction ( const caHdr &msg, const osiSockAddr &net_addr ); + void beaconAction ( const caHdr &msg, const osiSockAddr &net_addr ); + void notHereRespAction ( const caHdr &msg, const osiSockAddr &net_addr ); + void repeaterAckAction ( const caHdr &msg, const osiSockAddr &net_addr ); +}; + +class pendingClaimsTimer : public osiTimer { +public: + pendingClaimsTimer ( tcpiiu &iiuIn, osiTimerQueue & queueIn ); + + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); + +private: + void expire (); + void destroy (); + bool again () const; + double delay () const; + const char *name () const; + ~pendingClaimsTimer (); + + //tcpiiu &iiu; + + static tsFreeList < class pendingClaimsTimer, 1024 > freeList; }; class tcpRecvWatchdog : public osiTimer { @@ -472,8 +693,7 @@ private: double delay () const; const char *name () const; virtual void shutdown () = 0; - virtual void noopRequestMsg () = 0; - virtual void echoRequestMsg () = 0; + virtual void echoRequest () = 0; virtual void hostName ( char *pBuf, unsigned bufLength ) const = 0; const double period; @@ -501,77 +721,133 @@ private: const double period; }; -extern "C" void cacSendThreadTCP ( void *pParam ); +class tcpiiu; -class tcpiiu : public tcpRecvWatchdog, public tcpSendWatchdog, - public netiiu, public tsDLNode { +class claimsPendingIIU : public netiiu { public: - tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, class bhe &bhe); + claimsPendingIIU ( tcpiiu &tcpIIU ); + virtual ~claimsPendingIIU (); + + bool connectionInProgress ( const char *pChannelName, const osiSockAddr & ) const; + +private: + tcpiiu &tcpIIU; + void hostName (char *pBuf, unsigned bufLength) const; + const char * pHostName () const; // deprecated - please do not use +}; + +class hostNameCache : public ipAddrToAsciiAsynchronous { +public: + hostNameCache ( const osiSockAddr &addr, ipAddrToAsciiEngine &engine ); + ~hostNameCache (); + void ioCompletionNotify ( const char *pHostName ); + void hostName (char *pBuf, unsigned bufLength) const; +private: + char *pHostName; +}; + +extern "C" void cacSendThreadTCP ( void *pParam ); +extern "C" void cacRecvThreadTCP ( void *pParam ); + +class tcpiiu : + public tcpRecvWatchdog, public tcpSendWatchdog, + public netiiu, public tsDLNode , + private comQueSend, private comQueRecv { +public: + tcpiiu ( cac &cac, const osiSockAddr &addrIn, + unsigned minorVersion, class bhe &bhe, + double connectionTimeout, osiTimerQueue &timerQueue, + ipAddrToAsciiEngine & ); ~tcpiiu (); void suicide (); void shutdown (); static void * operator new (size_t size); static void operator delete (void *pCadaver, size_t size); - void hostName (char *pBuf, unsigned bufLength) const; - bool ca_v42_ok () const; - bool ca_v41_ok () const; - int pushStreamMsg ( const caHdr *pmsg, const void *pext, bool BlockingOk ); - int post_msg (char *pInBuf, unsigned long blockSize); - void addToChanList ( nciu &chan ); - void removeFromChanList ( nciu &chan ); - void connect (); - void disconnect ( nciu &chan ); bool fullyConstructed () const; - void recvMsg (); void flush (); - virtual void show (unsigned level) const; + virtual void show ( unsigned level ) const; osiSockAddr address () const; SOCKET getSock () const; + void echoRequest (); - void noopRequestMsg (); void echoRequestMsg (); - int busyRequestMsg (); - int readyRequestMsg (); + void enableFlowControlMsg (); + void disableFlowControlMsg (); void hostNameSetMsg (); void userNameSetMsg (); + void processIncomingAndDestroySelfIfDisconnected (); + void installChannelPendingClaim ( nciu & ); + bool ca_v44_ok () const; + bool ca_v41_ok () const; + void connect (); + int writeRequest ( unsigned serverId, unsigned type, unsigned nElem, const void *pValue ); + int writeNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, const void *pValue ); + int readCopyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ); + int readNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ); + int createChannelRequest ( unsigned clientId, const char *pName, unsigned nameLength ); + int clearChannelRequest ( unsigned clientId, unsigned serverId ); + int subscriptionRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, unsigned mask ); + int subscriptionCancelRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ); - ringBuffer send; - ringBuffer recv; - char host_name_str[64]; - osiSockAddr dest; - caHdr curMsg; - unsigned long curDataMax; - unsigned long curDataBytes; - class bhe &bhe; - void *pCurData; - semBinaryId sendThreadExitSignal; - semBinaryId recvThreadExitSignal; - unsigned minor_version_number; - unsigned contiguous_msg_count; - unsigned curMsgBytes; - iiu_conn_state state; - bool client_busy; - bool echoRequestPending; - bool claimRequestsPending; - bool sendPending; - bool recvPending; - bool pushPending; - + void hostName (char *pBuf, unsigned bufLength) const; + const char * pHostName () const; // deprecated - please do not use + bool connectionInProgress ( const char *pChannelName, const osiSockAddr &addr ) const; + bool alive () const; private: - bool compareIfTCP (nciu &chan, const sockaddr_in &) const; - int pushDatagramMsg (const caHdr *pMsg, const void *pExt, ca_uint16_t extsize); - int pushStreamMsgPrivate ( const caHdr *pmsg, const void *pext, - unsigned extsize, unsigned actualextsize ); - void flowControlOn (); - void flowControlOff (); + hostNameCache ipToA; + caHdr curMsg; + unsigned long curDataMax; + class bhe &bhe; + void *pCurData; + unsigned minor_version_number; + iiu_conn_state state; + bool ca_v42_ok () const; + void postMsg (); + unsigned sendBytes ( const void *pBuf, unsigned nBytesInBuf ); + unsigned recvBytes ( void *pBuf, unsigned nBytesInBuf ); + bool flushToWirePermit (); + + claimsPendingIIU *pClaimsPendingIIU; + semBinaryId sendThreadFlushSignal; + semBinaryId recvThreadRingBufferSpaceAvailableSignal; + semBinaryId sendThreadExitSignal; + semBinaryId recvThreadExitSignal; SOCKET sock; - bool fc; + unsigned contigRecvMsgCount; + bool fullyConstructedFlag; + bool busyStateDetected; // only modified by the recv thread + bool flowControlActive; // only modified by the send process thread + bool flowControlStateChange; + bool echoRequestPending; + bool recvPending; + bool flushPending; + bool msgHeaderAvailable; static tsFreeList < class tcpiiu, 16 > freeList; friend void cacSendThreadTCP ( void *pParam ); - const char * pHostName () const; // deprecated - please do not use + friend void cacRecvThreadTCP ( void *pParam ); + + void lastChannelDetachNotify (); + int requestStubStatus (); + + // protocol stubs + void noopAction (); + void echoRespAction (); + void writeNotifyRespAction (); + void readNotifyRespAction (); + void eventRespAction (); + void readRespAction (); + void clearChannelRespAction (); + void exceptionRespAction (); + void accessRightsRespAction (); + void claimCIURespAction (); + void verifyAndDisconnectChan (); + void badTCPRespAction (); + + typedef void ( tcpiiu::*pProtoStubTCP ) (); + static const pProtoStubTCP tcpJumpTableCAC []; }; class inetAddrID { @@ -613,24 +889,15 @@ private: int code; }; -/* - * This struct allocated off of a free list - * so that the select() ctx is thread safe - */ -struct caFDInfo { - ELLNODE node; - fd_set readMask; - fd_set writeMask; -}; - -class processThread : public osiThread { +class recvProcessThread : public osiThread { public: - processThread (class cac *pcacIn); - ~processThread (); + recvProcessThread (class cac *pcacIn); + ~recvProcessThread (); void entryPoint (); void signalShutDown (); void enable (); void disable (); + void signalActivity (); private: // // The additional complexity associated with @@ -639,6 +906,7 @@ private: // and therefore reduces the chance of creating // a deadlock window during code maintenance. // + osiEvent recvActivity; class cac *pcac; osiEvent exit; osiEvent processingDone; @@ -649,9 +917,23 @@ private: bool shutDown; }; +class sendProcessThread : public osiThread { +public: + sendProcessThread ( class cac &cacIn ); + ~sendProcessThread (); + void entryPoint (); + void signalShutDown (); + void signalActivity (); +private: + osiEvent sendActivity; + class cac &cacRef; + osiEvent exit; + bool shutDown; +}; + #define CASG_MAGIC 0xFAB4CAFE -class syncGroupNotify : public cacNotify, public tsDLNode { +class syncGroupNotify : public cacNotify, public tsDLNode < syncGroupNotify > { public: syncGroupNotify ( struct CASG &sgIn, void *pValue ); void destroy (); @@ -680,7 +962,7 @@ private: /* * one per synch group */ -struct CASG : public chronIntIdRes { +struct CASG : public chronIntIdRes < CASG > { public: CASG (cac &cacIn); void destroy (); @@ -710,84 +992,105 @@ private: friend class syncGroupNotify; }; -class cac : public caClient { +class cac : public caClient, public nciuPrivate { public: cac ( bool enablePreemptiveCallback = false ); virtual ~cac (); - void safeDestroyNMIU (unsigned id); - void processRecvBacklog (); void flush (); - void cleanUpPendIO (); - unsigned connectionCount () const; - void show (unsigned level) const; - void installIIU (tcpiiu &iiu); - void removeIIU (tcpiiu &iiu); - void signalRecvActivityIIU (tcpiiu &iiu); + int pend (double timeout, int early); + + // beacon management void beaconNotify (const inetAddrID &addr); bhe *lookupBeaconInetAddr (const inetAddrID &ina); bhe *createBeaconHashEntry (const inetAddrID &ina, const osiTime &initialTimeStamp); + void repeaterSubscribeConfirmNotify (); + + // outstanding IO count maintenance void removeBeaconInetAddr (const inetAddrID &ina); void decrementOutstandingIO (); void incrementOutstandingIO (); void decrementOutstandingIO ( unsigned seqNumber ); + void lockOutstandingIO () const; + void unlockOutstandingIO () const; unsigned readSequence () const; - int pend (double timeout, int early); - bool ioComplete () const; + void cleanUpPendIO (); + + // IIU routines + void installIIU ( tcpiiu &iiu ); + void removeIIU ( tcpiiu &iiu ); + void signalRecvActivity (); + void processRecvBacklog (); + tcpiiu * constructTCPIIU ( const osiSockAddr &, unsigned minorVersion ); + double connectionTimeout () const; + + // exception routines void exceptionNotify (int status, const char *pContext, const char *pFileName, unsigned lineNo); void exceptionNotify (int status, const char *pContext, unsigned type, unsigned long count, const char *pFileName, unsigned lineNo); - baseNMIU * lookupIO (unsigned id); - void installIO (baseNMIU &io); - void uninstallIO (baseNMIU &io); - nciu * lookupChan (unsigned id); + void changeExceptionEvent ( caExceptionHandler *pfunc, void *arg ); + void genLocalExcepWFL (long stat, const char *ctx, const char *pFile, unsigned lineNo); + + // IO management routines + bool ioComplete () const; + void ioDestroy ( unsigned id ); + void ioInstall ( nciu &chan, baseNMIU &io ); + void ioCompletionNotify ( unsigned id ); + void ioCompletionNotify ( unsigned id, unsigned type, + unsigned long count, const void *pData ); + void ioExceptionNotify ( unsigned id, + int status, const char *pContext ); + void ioExceptionNotify ( unsigned id, int status, + const char *pContext, unsigned type, unsigned long count ); + void ioCompletionNotifyAndDestroy ( unsigned id ); + void ioCompletionNotifyAndDestroy ( unsigned id, + unsigned type, unsigned long count, const void *pData ); + void ioExceptionNotifyAndDestroy ( unsigned id, + int status, const char *pContext ); + void ioExceptionNotifyAndDestroy ( unsigned id, + int status, const char *pContext, unsigned type, unsigned long count ); + + // channel routines + void connectChannel ( unsigned id, class tcpiiu &iiu, + unsigned nativeType, unsigned long nativeCount, unsigned sid ); + void channelDestroy ( unsigned id ); + void disconnectChannel ( unsigned id ); void registerChannel (nciu &chan); - void unregisterChannel (nciu &chan); + void unregisterChannel ( nciu &chan ); + bool createChannelIO (const char *name_str, cacChannel &chan); + void lookupChannelAndTransferToTCP ( unsigned cid, unsigned sid, + unsigned typeCode, unsigned long count, unsigned minorVersionNumber, + const osiSockAddr & ); + void installDisconnectedChannel ( nciu &chan ); + void accessRightsNotify ( unsigned id, caar ); + + // sync group routines CASG * lookupCASG (unsigned id); void installCASG (CASG &); void uninstallCASG (CASG &); + void registerService ( cacServiceIO &service ); - bool createChannelIO (const char *name_str, cacChannel &chan); void registerForFileDescriptorCallBack ( CAFDHANDLER *pFunc, void *pArg ); - void lock () const; - void unlock () const; void enableCallbackPreemption (); void disableCallbackPreemption (); - void changeExceptionEvent ( caExceptionHandler *pfunc, void *arg ); - void genLocalExcepWFL (long stat, const char *ctx, const char *pFile, unsigned lineNo); - void installDisconnectedChannel ( nciu &chan ); void notifySearchResponse ( unsigned short retrySeqNo ); - void repeaterSubscribeConfirmNotify (); + bool currentThreadIsRecvProcessThread (); + const char * userNamePointer (); + + // diagnostics + unsigned connectionCount () const; + void show (unsigned level) const; + int printf ( const char *pformat, ... ); + int vPrintf ( const char *pformat, va_list args ); void replaceErrLogHandler ( caPrintfFunc *ca_printf_func ); - osiTimerQueue *pTimerQueue; - ELLLIST activeCASGOP; - ELLLIST putCvrtBuf; - osiTime programBeginTime; - ca_real ca_connectTMO; - caExceptionHandler *ca_exception_func; - void *ca_exception_arg; - caPrintfFunc *ca_printf_func; - char *ca_pUserName; - resTable - < bhe, inetAddrID > beaconTable; - tsDLIterBD endOfBCastList; - osiEvent recvActivity; - semBinaryId ca_blockSem; - unsigned readSeq; - unsigned ca_nextSlowBucketId; - unsigned short ca_server_port; - char ca_new_err_code_msg_buf[128u]; - private: + ipAddrToAsciiEngine ipToAEngine; + char ca_new_err_code_msg_buf[128u]; cacServiceList services; - osiMutex defaultMutex; - osiMutex iiuListMutex; - osiEvent ioDone; - tsDLList iiuListIdle; - tsDLList iiuListRecvPending; + tsDLList iiuList; tsDLList localChanList; chronIntIdResTable @@ -796,17 +1099,34 @@ private: < nciu > chanTable; chronIntIdResTable < CASG > sgTable; - processThread *pProcThread; + resTable + < bhe, inetAddrID > beaconTable; + osiTime programBeginTime; + double connTMO; + osiTimerQueue *pTimerQueue; + caExceptionHandler *ca_exception_func; + void *ca_exception_arg; + caPrintfFunc *pVPrintfFunc; + char *pUserName; + osiEvent ioDone; + recvProcessThread *pRecvProcThread; CAFDHANDLER *fdRegFunc; void *fdRegArg; udpiiu *pudpiiu; searchTimer *pSearchTmr; repeaterSubscribeTimer *pRepeaterSubscribeTmr; + semBinaryId ca_blockSem; + osiMutex defaultMutex; + osiMutex iiuListMutex; unsigned pndrecvcnt; + unsigned readSeq; + unsigned ca_nextSlowBucketId; bool enablePreemptiveCallback; - int pendPrivate (double timeout, int early); + int pendPrivate ( double timeout, int early ); bool setupUDP (); + + friend class cacPrivate; }; extern const caHdr cacnullmsg; @@ -823,12 +1143,11 @@ epicsShareFunc void epicsShareAPI ca_repeater (void); bhe *lookupBeaconInetAddr(cac *pcac, const struct sockaddr_in *pnet_addr); -#define genLocalExcep( PCAC, STAT, PCTX ) \ -(PCAC)->genLocalExcepWFL ( STAT, PCTX, __FILE__, __LINE__ ) +#define genLocalExcep( CAC, STAT, PCTX ) \ +(CAC).genLocalExcepWFL ( STAT, PCTX, __FILE__, __LINE__ ) double cac_fetch_poll_period (cac *pcac); -tcpiiu * constructTCPIIU (cac *pcac, const struct sockaddr_in *pina, - unsigned minorVersion); + void cac_destroy (cac *pcac); int fetchClientContext (cac **ppcac); extern "C" void caRepeaterThread (void *pDummy); diff --git a/src/ca/localHostName.cpp b/src/ca/localHostName.cpp index 5c4e95d56..82e4dc038 100644 --- a/src/ca/localHostName.cpp +++ b/src/ca/localHostName.cpp @@ -20,10 +20,25 @@ localHostName localHostNameAtLoadTime; localHostName::localHostName () { - int status = gethostname ( this->cache, sizeof ( this->cache ) ); + int status = osiSockAttach (); if ( status ) { - strncpy ( this->cache, "", sizeof ( this->cache ) ); - localHostName::cache [ sizeof ( this->cache ) - 1u ] = '\0'; + this->attachedToSockLib = true; + int status = gethostname ( this->cache, sizeof ( this->cache ) ); + if ( status ) { + strncpy ( this->cache, "", sizeof ( this->cache ) ); + localHostName::cache [ sizeof ( this->cache ) - 1u ] = '\0'; + } + this->length = strlen ( this->cache ); + } + else { + this->attachedToSockLib = false; + strncpy ( this->cache, "", sizeof ( this->cache ) ); + } +} + +localHostName::~localHostName () +{ + if ( this->attachedToSockLib ) { + osiSockRelease (); } - this->length = strlen ( this->cache ); } diff --git a/src/ca/localHostName.h b/src/ca/localHostName.h index 53e76d91a..b679aaa5a 100644 --- a/src/ca/localHostName.h +++ b/src/ca/localHostName.h @@ -13,10 +13,12 @@ class localHostName { public: localHostName (); + ~localHostName (); const char * pointer () const; void copy ( char *pBuf, unsigned bufLength ) const; unsigned stringLength () const; private: + bool attachedToSockLib; unsigned length; char cache [128]; }; diff --git a/src/ca/nciu.cpp b/src/ca/nciu.cpp index 37bd413d0..c4466c1d9 100644 --- a/src/ca/nciu.cpp +++ b/src/ca/nciu.cpp @@ -8,43 +8,48 @@ * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill + * + * Notes: + * 1) This class has a pointer to the IIU. Since it is possible + * for the channel to exist when no IIU exists (because the user + * created the channel), and because an IIU can disconnect and be + * destroyed at any time, then it is necessary to hold a mutex while + * the IIU pointer is in use. This mutex can not be the IIU's mutex + * because the IIU's lock must not be held while waiting for a + * message to be sent (otherwise a push pull deadlock can occur). */ #include "iocinf.h" #include "nciu_IL.h" +#include "netReadCopyIO_IL.h" #include "netReadNotifyIO_IL.h" #include "netWriteNotifyIO_IL.h" #include "netSubscription_IL.h" +#include "cac_IL.h" tsFreeList < class nciu, 1024 > nciu::freeList; -struct putCvrtBuf { - ELLNODE node; - unsigned long size; - void *pBuf; -}; - -/* - * nciu::nciu () - */ -nciu::nciu ( cac *pcac, cacChannel &chan, const char *pNameIn ) : - cacChannelIO ( chan ) +nciu::nciu ( cac &cacIn, cacChannel &chan, const char *pNameIn ) : + cacChannelIO ( chan ), cacPrivate ( cacIn ) { static const caar defaultAccessRights = { false, false }; size_t strcnt; strcnt = strlen ( pNameIn ) + 1; - if ( strcnt > MAX_UDP - sizeof ( caHdr ) ) { + // second constraint is imposed by size field in protocol header + if ( strcnt > MAX_UDP_SEND - sizeof ( caHdr ) || strcnt > 0xffff ) { throwWithLocation ( caErrorCode ( ECA_STRTOBIG ) ); } - this->pNameStr = reinterpret_cast ( malloc ( strcnt ) ); + + this->pNameStr = new char [ strcnt ]; if ( ! this->pNameStr ) { this->f_fullyConstructed = false; return; } strcpy ( this->pNameStr, pNameIn ); + this->piiu = 0u; this->typeCode = USHRT_MAX; /* invalid initial type */ this->count = 0; /* invalid initial count */ this->sid = UINT_MAX; /* invalid initial server id */ @@ -53,25 +58,25 @@ nciu::nciu ( cac *pcac, cacChannel &chan, const char *pNameIn ) : this->previousConn = 0; this->f_connected = false; this->f_fullyConstructed = true; + this->retry = 0u; + this->retrySeqNo = 0u; + this->ptrLockCount = 0u; + this->ptrUnlockWaitCount = 0u; - pcac->lock (); + this->cacCtx.registerChannel ( *this ); - pcac->registerChannel ( *this ); - - pcac->installDisconnectedChannel ( *this ); + this->cacCtx.installDisconnectedChannel ( *this ); chan.attachIO ( *this ); - - pcac->unlock (); } -/* - * nciu::~nciu () - */ +void nciu::destroy () +{ + delete this; +} + nciu::~nciu () { - netiiu *piiuCopy = this->piiu; - if ( ! this->fullyConstructed () ) { return; } @@ -80,98 +85,60 @@ nciu::~nciu () // this calls virtual functions in the cacChannelIO base this->ioReleaseNotify (); - if ( this->f_connected ) { - caHdr hdr; + this->destroyAllIO (); - hdr.m_cmmd = htons ( CA_PROTO_CLEAR_CHANNEL ); - hdr.m_available = this->getId (); - hdr.m_cid = this->sid; - hdr.m_dataType = htons ( 0 ); - hdr.m_count = htons ( 0 ); - hdr.m_postsize = 0; + this->lockPIIU (); - this->piiu->pushStreamMsg (&hdr, NULL, true); + if ( this->f_connected && this->piiu ) { + this->piiu->clearChannelRequest ( this->getId (), this->sid ); } + + this->detachChanFromIIU (); - this->piiu->pcas->lock (); + this->unlockPIIU (); - /* - * remove any IO blocks still attached to this channel - */ - tsDLIterBD iter = this->eventq.first (); - while ( iter.valid () ) { - tsDLIterBD next = iter.itemAfter (); - iter->destroy (); - iter = next; - } + this->cacCtx.unregisterChannel ( *this ); - this->piiu->pcas->unregisterChannel ( *this ); - - this->piiu->removeFromChanList ( *this ); - - free ( reinterpret_cast ( this->pNameStr ) ); - - piiuCopy->pcas->unlock (); // remove clears this->piiu + delete [] this->pNameStr; } int nciu::read ( unsigned type, unsigned long countIn, cacNotify ¬ify ) { int status; - caHdr hdr; - ca_uint16_t type_u16; - ca_uint16_t count_u16; + unsigned id; - /* - * fail out if channel isnt connected or arguments are - * otherwise invalid - */ - if ( ! this->f_connected ) { - return ECA_DISCONNCHID; - } + // + // fail out if their arguments are invalid + // if ( INVALID_DB_REQ (type) ) { return ECA_BADTYPE; } if ( ! this->ar.read_access ) { return ECA_NORDACCESS; } - if ( countIn > this->count || countIn > 0xffff ) { + if ( countIn > UINT_MAX ) { return ECA_BADCOUNT; } if ( countIn == 0 ) { countIn = this->count; } - - /* - * only after range checking type and count cast - * them down to a smaller size - */ - type_u16 = (ca_uint16_t) type; - count_u16 = (ca_uint16_t) countIn; - - this->piiu->pcas->lock (); - { - netReadNotifyIO *monix = new netReadNotifyIO ( *this, notify ); - if ( ! monix ) { - this->piiu->pcas->unlock (); - return ECA_ALLOCMEM; - } - - hdr.m_cmmd = htons (CA_PROTO_READ_NOTIFY); - hdr.m_dataType = htons (type_u16); - hdr.m_count = htons (count_u16); - hdr.m_available = monix->getId (); - hdr.m_postsize = 0; - hdr.m_cid = this->sid; + + bool success = netReadNotifyIO::factory ( *this, notify, id ); + if ( ! success ) { + return ECA_ALLOCMEM; } - this->piiu->pcas->unlock (); - status = this->piiu->pushStreamMsg (&hdr, NULL, true); + this->lockPIIU (); + if ( this->piiu ) { + status = this->piiu->readNotifyRequest ( id, this->sid, type, countIn ); + } + else { + status = ECA_DISCONNCHID; + } + this->unlockPIIU (); + if ( status != ECA_NORMAL ) { - /* - * we need to be careful about touching the monix - * pointer after the lock has been released - */ - this->piiu->pcas->safeDestroyNMIU (hdr.m_available); + this->cacCtx.ioDestroy ( id ); } return status; @@ -179,10 +146,9 @@ int nciu::read ( unsigned type, unsigned long countIn, cacNotify ¬ify ) int nciu::read ( unsigned type, unsigned long countIn, void *pValue ) { + unsigned id; + bool success; int status; - caHdr hdr; - ca_uint16_t type_u16; - ca_uint16_t count_u16; /* * fail out if channel isnt connected or arguments are @@ -204,67 +170,32 @@ int nciu::read ( unsigned type, unsigned long countIn, void *pValue ) countIn = this->count; } - /* - * only after range checking type and count cast - * them down to a smaller size - */ - type_u16 = ( ca_uint16_t ) type; - count_u16 = ( ca_uint16_t ) countIn; - - this->piiu->pcas->lock (); - { - netReadCopyIO *monix = new netReadCopyIO ( *this, - type, countIn, pValue, this->readSequence () ); - if ( ! monix ) { - this->piiu->pcas->unlock (); - return ECA_ALLOCMEM; - } - - hdr.m_cmmd = htons ( CA_PROTO_READ ); - hdr.m_dataType = htons ( type_u16 ); - hdr.m_count = htons ( count_u16 ); - hdr.m_available = monix->getId (); - hdr.m_postsize = 0; - hdr.m_cid = this->sid; + success = netReadCopyIO::factory ( *this, type, countIn, pValue, + this->readSequence (), id ); + if ( ! success ) { + return ECA_ALLOCMEM; } - this->piiu->pcas->unlock (); - status = this->piiu->pushStreamMsg ( &hdr, NULL, true ); + this->lockPIIU (); + if ( this->piiu ) { + status = this->piiu->readCopyRequest ( id, this->sid, type, countIn ); + } + else { + status = ECA_DISCONNCHID; + } + this->unlockPIIU (); + if ( status != ECA_NORMAL ) { - /* - * we need to be careful about touching the monix - * pointer after the lock has been released - */ - this->piiu->pcas->safeDestroyNMIU ( hdr.m_available ); + this->cacCtx.ioDestroy ( id ); } return status; } -/* - * free_put_convert() - */ -#ifdef CONVERSION_REQUIRED -LOCAL void free_put_convert (cac *pcac, void *pBuf) -{ - struct putCvrtBuf *pBufHdr; - - pBufHdr = (struct putCvrtBuf *)pBuf; - pBufHdr -= 1; - assert ( pBufHdr->pBuf == (void *) ( pBufHdr + 1 ) ); - - pcac->lock (); - ellAdd (&pcac->putCvrtBuf, &pBufHdr->node); - pcac->unlock (); - - return; -} -#endif /* CONVERSION_REQUIRED */ - /* * check_a_dbr_string() */ -LOCAL int check_a_dbr_string (const char *pStr, const unsigned count) +LOCAL int check_a_dbr_string ( const char *pStr, const unsigned count ) { unsigned i; @@ -285,37 +216,7 @@ LOCAL int check_a_dbr_string (const char *pStr, const unsigned count) return ECA_NORMAL; } -/* - * malloc_put_convert() - */ -#ifdef CONVERSION_REQUIRED -LOCAL void *malloc_put_convert (cac *pcac, unsigned long size) -{ - struct putCvrtBuf *pBuf; - - pcac->lock (); - while ( (pBuf = (struct putCvrtBuf *) ellGet(&pcac->putCvrtBuf)) ) { - if ( pBuf->size >= size ) { - break; - } - else { - free ( pBuf ); - } - } - pcac->unlock (); - - if ( ! pBuf ) { - pBuf = (struct putCvrtBuf *) malloc ( sizeof (*pBuf) + size ); - if (!pBuf) { - return NULL; - } - pBuf->size = size; - pBuf->pBuf = (void *) ( pBuf + 1 ); - } - - return pBuf->pBuf; -} -#endif /* CONVERSION_REQUIRED */ +#ifdef JUNKYARD /* * nciu::issuePut () @@ -326,8 +227,6 @@ int nciu::issuePut ( ca_uint16_t cmd, unsigned idIn, chtype type, int status; caHdr hdr; unsigned postcnt; - ca_uint16_t type_u16; - ca_uint16_t count_u16; # ifdef CONVERSION_REQUIRED void *pCvrtBuf; # endif /*CONVERSION_REQUIRED*/ @@ -364,17 +263,12 @@ int nciu::issuePut ( ca_uint16_t cmd, unsigned idIn, chtype type, return ECA_TOLARGE; } - /* - * only after range checking type and count cast - * them down to a smaller size - */ - type_u16 = (ca_uint16_t) type; - count_u16 = (ca_uint16_t) countIn; - - if (type == DBR_STRING && countIn == 1) { - char *pstr = (char *)pvalue; - - postcnt = strlen(pstr)+1; + if ( type == DBR_STRING && countIn == 1 ) { + char *pstr = (char *) pvalue; + postcnt = strlen (pstr) +1; + } + else { + postcnt = dbr_size_n ( type, countIn ); } # ifdef CONVERSION_REQUIRED @@ -385,8 +279,10 @@ int nciu::issuePut ( ca_uint16_t cmd, unsigned idIn, chtype type, size_of_one = dbr_size[type]; - pCvrtBuf = pdest = malloc_put_convert (this->piiu->pcas, postcnt); - if (!pdest) { +#error can we eliminate this? + pCvrtBuf = pdest = this->cacCtx.mallocPutConvert ( postcnt ); + if ( ! pdest ) { + this->unlockPIIU (); return ECA_ALLOCMEM; } @@ -399,14 +295,15 @@ int nciu::issuePut ( ca_uint16_t cmd, unsigned idIn, chtype type, * update when it is a single element. */ i=0; - while (TRUE) { - switch (type) { + while ( TRUE ) { + switch ( type ) { case DBR_LONG: - *(dbr_long_t *)pdest = htonl (*(dbr_long_t *)pvalue); + * (dbr_long_t *) pdest = + htonl ( ( * reinterpret_cast < const dbr_long_t * > (pvalue) ) ); break; case DBR_CHAR: - *(dbr_char_t *)pdest = *(dbr_char_t *)pvalue; + * (dbr_char_t *) pdest = * (dbr_char_t *) pvalue; break; case DBR_ENUM: @@ -416,15 +313,16 @@ int nciu::issuePut ( ca_uint16_t cmd, unsigned idIn, chtype type, # if DBR_INT != DBR_SHORT # error DBR_INT != DBR_SHORT ? # endif /*DBR_INT != DBR_SHORT*/ - *(dbr_short_t *)pdest = htons (*(dbr_short_t *)pvalue); + * (dbr_short_t *) pdest = + htons ( ( * reinterpret_cast < const dbr_short_t * > (pvalue) ) ); break; case DBR_FLOAT: - dbr_htonf ((dbr_float_t *)pvalue, (dbr_float_t *)pdest); + dbr_htonf ( (dbr_float_t *) pvalue, (dbr_float_t *) pdest ); break; case DBR_DOUBLE: - dbr_htond ((dbr_double_t *)pvalue, (dbr_double_t *)pdest); + dbr_htond ( (dbr_double_t *) pvalue, (dbr_double_t *) pdest ); break; case DBR_STRING: @@ -442,151 +340,431 @@ int nciu::issuePut ( ca_uint16_t cmd, unsigned idIn, chtype type, break; } - pdest = ((char *)pdest) + size_of_one; - pvalue = ((char *)pvalue) + size_of_one; + pdest = ( (char *) pdest ) + size_of_one; + pvalue = ( (char *) pvalue ) + size_of_one; } pvalue = pCvrtBuf; } # endif /*CONVERSION_REQUIRED*/ - hdr.m_cmmd = htons (cmd); - hdr.m_dataType = htons (type_u16); - hdr.m_count = htons (count_u16); + hdr.m_cmmd = htons ( cmd ); + hdr.m_dataType = htons ( static_cast ( type ) ); + hdr.m_count = htons ( static_cast ( countIn ) ); hdr.m_cid = this->sid; hdr.m_available = idIn; - hdr.m_postsize = (ca_uint16_t) postcnt; - status = this->piiu->pushStreamMsg (&hdr, pvalue, true); + this->lockPIIU (); + if ( this->piiu ) { + status = this->piiu->pushStreamMsg ( &hdr, pvalue, postcnt ); + } + else { + status = ECA_DISCONNCHID; + } + this->unlockPIIU (); # ifdef CONVERSION_REQUIRED - free_put_convert (this->piiu->pcas, pCvrtBuf); + this->cacCtx.freePutConvert ( pCvrtBuf ); # endif /*CONVERSION_REQUIRED*/ return status; } -int nciu::write (unsigned type, unsigned long countIn, const void *pValue) -{ - return this->issuePut (CA_PROTO_WRITE, ~0U, type, countIn, pValue); -} +#endif -int nciu::write (unsigned type, unsigned long countIn, const void *pValue, cacNotify ¬ify) +int nciu::write ( unsigned type, unsigned long countIn, const void *pValue ) { - netWriteNotifyIO *monix; - unsigned newId; int status; - if ( ! this->f_connected ) { - return ECA_DISCONNCHID; + if ( ! this->ar.write_access ) { + return ECA_NOWTACCESS; } - if ( ! this->piiu->ca_v41_ok () ) { - return ECA_NOSUPPORT; + if ( countIn > this->count || countIn == 0 ) { + return ECA_BADCOUNT; } - /* - * lock around io block create and list add - * so that we are not deleted without - * reclaiming the resource - */ - this->piiu->pcas->lock (); + if ( type == DBR_STRING ) { + status = check_a_dbr_string ( (char *) pValue, countIn ); + if ( status != ECA_NORMAL ) { + return status; + } + } - monix = new netWriteNotifyIO (*this, notify); - if ( ! monix ) { - this->piiu->pcas->unlock (); + this->lockPIIU (); + if ( this->piiu ) { + status = this->piiu->writeRequest ( this->sid, type, countIn, pValue ); + } + else { + status = ECA_DISCONNCHID; + } + this->lockPIIU (); + return status; +} + +int nciu::write ( unsigned type, unsigned long countIn, const void *pValue, cacNotify ¬ify ) +{ + ca_uint32_t newId; + int status; + + if ( ! this->ar.write_access ) { + return ECA_NOWTACCESS; + } + + if ( countIn > this->count || countIn == 0 ) { + return ECA_BADCOUNT; + } + + if ( type == DBR_STRING ) { + status = check_a_dbr_string ( (char *) pValue, countIn ); + if ( status != ECA_NORMAL ) { + return status; + } + } + + // dont use monix pointer because monix could be deleted + // when the channel disconnects or when the IO completes + bool success = netWriteNotifyIO::factory ( *this, notify, newId ); + if ( ! success ) { return ECA_ALLOCMEM; } - newId = monix->getId (); + this->lockPIIU (); - this->piiu->pcas->unlock (); + if ( this->piiu ) { + status = this->piiu->writeNotifyRequest ( newId, this->sid, type, countIn, pValue ); + } + else { + status = ECA_DISCONNCHID; + } + + this->unlockPIIU (); - status = this->issuePut (CA_PROTO_WRITE_NOTIFY, newId, - type, countIn, pValue); if ( status != ECA_NORMAL ) { /* * we need to be careful about touching the monix * pointer after the lock has been released */ - this->piiu->pcas->safeDestroyNMIU ( newId ); + this->cacCtx.ioDestroy ( newId ); } + return status; } -int nciu::subscribe (unsigned type, unsigned long countIn, - unsigned mask, cacNotify ¬ify) +void nciu::connect ( tcpiiu &iiu, unsigned nativeType, + unsigned long nativeCount, unsigned sidIn ) { - netSubscription *pNetMon; - this->piiu->pcas->lock (); - - pNetMon = new netSubscription (*this, type, countIn, - static_cast (mask), notify); - if ( ! pNetMon ) { - this->piiu->pcas->unlock (); - return ECA_ALLOCMEM; + if ( this->f_connected ) { + ca_printf ( + "CAC: Ignored conn resp to conn chan CID=%u SID=%u?\n", + this->getId (), sidIn ); + return; } - this->piiu->pcas->unlock (); + this->lock (); - pNetMon->subscriptionMsg (); + this->typeCode = nativeType; + this->count = nativeCount; + this->sid = sidIn; + this->f_connected = true; + this->previousConn = true; - return ECA_NORMAL; + /* + * if less than v4.1 then the server will never + * send access rights and we know that there + * will always be access + */ + if ( ! iiu.ca_v41_ok () ) { + this->ar.read_access = true; + this->ar.write_access = true; + } + + this->unlock (); + + // resubscribe for monitors from this channel + this->subscribeAllIO (); + + this->connectNotify (); + + /* + * if less than v4.1 then the server will never + * send access rights and we know that there + * will always be access and also need to call + * their call back here + */ + if ( ! iiu.ca_v41_ok () ) { + this->accessRightsNotify ( this->ar ); + } } -void nciu::destroy () +void nciu::disconnect () { - delete this; + char hostNameBuf[64]; + this->hostName ( hostNameBuf, sizeof (hostNameBuf) ); + + this->lock (); + + if ( ! this->f_connected ) { + this->unlock (); + return; + } + + this->retry = 0u; + this->typeCode = USHRT_MAX; + this->count = 0u; + this->sid = UINT_MAX; + this->ar.read_access = false; + this->ar.write_access = false; + this->f_connected = false; + + this->unlock (); + + /* + * look for events that have an event cancel in progress + */ + disconnectAllIO ( hostNameBuf ); + + this->disconnectNotify (); + this->accessRightsNotify ( this->ar ); + + this->cacCtx.installDisconnectedChannel ( *this ); +} + +/* + * nciu::searchMsg () + */ +bool nciu::searchMsg ( unsigned short retrySeqNumber, unsigned &retryNoForThisChannel ) +{ + caHdr msg; + bool status; + + 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_cid = this->getId (); + + this->lockPIIU (); + + if ( this->piiu ) { + status = this->piiu->pushDatagramMsg ( msg, + this->pNameStr, this->nameLength ); + } + else { + status = false; + } + + this->unlockPIIU (); + + if ( status ) { + // + // increment the number of times we have tried + // to find this channel + // + this->lock (); + + if ( this->retry < MAXCONNTRIES ) { + this->retry++; + } + + this->retrySeqNo = retrySeqNumber; + retryNoForThisChannel = this->retry; + + this->unlock (); + } + + return status; +} + +int nciu::subscriptionMsg ( unsigned subscriptionId, unsigned typeIn, + unsigned long countIn, unsigned short maskIn) +{ + int status; + + /* + * clip to the native count and set to the native count if they + * specify zero + */ + if ( countIn > this->count ){ + countIn = this->count; + } + + if ( this->f_connected ) { + this->lockPIIU (); + if ( this->piiu ) { + status = this->piiu->subscriptionRequest ( subscriptionId, this->sid, typeIn, countIn, maskIn ); + } + else { + status = ECA_NORMAL; + } + this->unlockPIIU (); + } + else { + status = ECA_NORMAL; + } + + return status; +} + +void nciu::attachChanToIIU ( netiiu &iiu ) +{ + this->lockPIIU (); + + if ( this->piiu ) { + this->piiu->mutex.lock (); + this->piiu->chidList.remove ( *this ); + if ( this->piiu->chidList.count () == 0u ) { + this->piiu->lastChannelDetachNotify (); + } + this->piiu->mutex.unlock (); + } + + iiu.mutex.lock (); + + // add to the front of the list so that + // search requests for new channels will be sent first + iiu.chidList.push ( *this ); + this->piiu = &iiu; + + iiu.mutex.unlock (); + + this->unlockPIIU (); +} + +void nciu::detachChanFromIIU () +{ + this->lockPIIU (); + if ( this->piiu ) { + this->piiu->mutex.lock (); + this->piiu->chidList.remove ( *this ); + if ( this->piiu->chidList.count () == 0u ) { + this->piiu->lastChannelDetachNotify (); + } + this->piiu->mutex.unlock (); + this->piiu = 0u; + } + this->unlockPIIU (); +} + +void nciu::incrementOutstandingIO () +{ + this->cacCtx.incrementOutstandingIO (); +} + +void nciu::decrementOutstandingIO () +{ + this->cacCtx.decrementOutstandingIO (); +} + +void nciu::decrementOutstandingIO ( unsigned seqNumber ) +{ + this->cacCtx.decrementOutstandingIO ( seqNumber ); +} + +void nciu::lockOutstandingIO () const +{ + this->cacCtx.lockOutstandingIO (); +} + +void nciu::unlockOutstandingIO () const +{ + this->cacCtx.unlockOutstandingIO (); +} + +unsigned nciu::readSequence () const +{ + return this->cacCtx.readSequence (); } void nciu::hostName ( char *pBuf, unsigned bufLength ) const { - this->piiu->hostName ( pBuf, bufLength ); + this->lockPIIU (); + if ( this->piiu ) { + this->piiu->hostName ( pBuf, bufLength ); + } + else { + strncpy (pBuf, "", bufLength); + pBuf[bufLength-1] = '\0'; + } + this->unlockPIIU (); } -// deprecated - please do not use +// deprecated - please do not use, this is _not_ thread safe const char * nciu::pHostName () const { - return this->piiu->pHostName (); + this->lockPIIU (); + const char *pName = this->piiu->pHostName (); + this->unlockPIIU (); + return pName; // ouch ! } bool nciu::ca_v42_ok () const { - return this->piiu->ca_v42_ok (); + bool status; + + this->lockPIIU (); + if ( this->piiu ) { + status = this->piiu->ca_v42_ok (); + } + else { + status = false; + } + this->unlockPIIU (); + return status; } short nciu::nativeType () const { + short type; + + this->lock (); if ( this->f_connected ) { - return static_cast (this->typeCode); + type = static_cast ( this->typeCode ); } else { - return TYPENOTCONN; + type = TYPENOTCONN; } + this->unlock (); + + return type; } unsigned long nciu::nativeElementCount () const { + unsigned long countOut; + + this->lock (); if ( this->f_connected ) { - return this->count; + countOut = this->count; } else { - return 0ul; + countOut = 0ul; } + this->unlock (); + + return countOut; } channel_state nciu::state () const { + channel_state stateOut; + + this->lock (); + if ( this->f_connected ) { - return cs_conn; + stateOut = cs_conn; } else if ( this->previousConn ) { - return cs_prev_conn; + stateOut = cs_prev_conn; } else { - return cs_never_conn; + stateOut = cs_never_conn; } + + this->unlock (); + + return stateOut; } caar nciu::accessRights () const @@ -604,310 +782,16 @@ unsigned nciu::searchAttempts () const return this->retry; } -void nciu::connect ( tcpiiu &iiu, unsigned nativeType, - unsigned long nativeCount, unsigned sidIn ) +int nciu::subscribe ( unsigned type, unsigned long countIn, + unsigned mask, cacNotify ¬ify ) { - iiu.pcas->lock (); - - if ( this->connected () ) { - ca_printf ( - "CAC: Ignored conn resp to conn chan CID=%u SID=%u?\n", - this->getId (), sidIn ); - iiu.pcas->unlock (); - return; + unsigned id; + bool success = netSubscription::factory ( *this, type, countIn, + static_cast (mask), notify, id ); + if ( success ) { + return ECA_NORMAL; } - - this->typeCode = nativeType; - this->count = nativeCount; - this->sid = sidIn; - this->f_connected = true; - this->previousConn = true; - - /* - * if less than v4.1 then the server will never - * send access rights and we know that there - * will always be access and call their call back - * here - */ - if ( ! CA_V41 ( CA_PROTOCOL_VERSION, iiu.minor_version_number ) ) { - this->ar.read_access = true; - this->ar.write_access = true; - this->accessRightsNotify ( this->ar ); - } - - this->connectNotify (); - - // resubscribe for monitors from this channel - tsDLIterBD iter = this->eventq.first (); - while ( iter.valid () ) { - iter->subscriptionMsg (); - iter++; - } - - iiu.pcas->unlock (); -} - -void nciu::disconnect () -{ - this->piiu->pcas->lock (); - - this->typeCode = USHRT_MAX; - this->count = 0u; - this->sid = UINT_MAX; - this->ar.read_access = false; - this->ar.write_access = false; - this->f_connected = false; - - char hostNameBuf[64]; - this->piiu->hostName ( hostNameBuf, sizeof (hostNameBuf) ); - - /* - * look for events that have an event cancel in progress - */ - tsDLIterBD iter = this->eventq.first (); - while ( iter.valid () ) { - tsDLIterBD next = iter.itemAfter (); - iter->disconnect ( hostNameBuf ); - iter = next; - } - - this->disconnectNotify (); - this->accessRightsNotify (this->ar); - - this->piiu->pcas->unlock (); - - this->piiu->disconnect ( *this ); -} - -/* - * nciu::searchMsg () - */ -int nciu::searchMsg () -{ - int status; - caHdr msg; - - if ( this->nameLength > 0xffff ) { - return ECA_STRTOBIG; - } - - 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_cid = this->getId (); - - status = this->piiu->pushDatagramMsg (&msg, this->pNameStr, this->nameLength); - if (status != ECA_NORMAL) { - return status; - } - - /* - * increment the number of times we have tried to find this thisnel - */ - if ( this->retry < MAXCONNTRIES ) { - this->retry++; - } - - /* - * move the channel to the end of the list so - * that all channels get a equal chance - */ - this->piiu->pcas->lock (); - this->piiu->chidList.remove (*this); - this->piiu->chidList.add (*this); - this->piiu->pcas->unlock (); - - return ECA_NORMAL; -} - -void nciu::searchReplySetUp ( unsigned sidIn, - unsigned typeIn, unsigned long countIn ) -{ - this->typeCode = typeIn; - this->count = countIn; - this->sid = sidIn; -} - -/* - * nciu::claimMsg () - */ -bool nciu::claimMsg ( tcpiiu *piiuIn ) -{ - caHdr hdr; - unsigned size; - const char *pStr; - int status; - - this->piiu->pcas->lock (); - - if ( ! this->claimPending ) { - this->piiu->pcas->unlock (); - return false; - } - - if ( this->f_connected ) { - this->piiu->pcas->unlock (); - return false; - } - - hdr = cacnullmsg; - hdr.m_cmmd = htons (CA_PROTO_CLAIM_CIU); - - if ( CA_V44 (CA_PROTOCOL_VERSION, piiuIn->minor_version_number) ) { - hdr.m_cid = this->getId (); - pStr = this->pNameStr; - size = this->nameLength; - } - else { - hdr.m_cid = this->sid; - pStr = NULL; - size = 0u; - } - - hdr.m_postsize = size; - - /* - * The available field is used (abused) - * here to communicate the minor version number - * starting with CA 4.1. - */ - hdr.m_available = htonl (CA_MINOR_VERSION); - - /* - * If we are out of buffer space then postpone this - * operation until later. This avoids any possibility - * of a push pull deadlock (since this is sent when - * parsing the UDP input buffer). - */ - status = piiuIn->pushStreamMsg (&hdr, pStr, false); - if ( status == ECA_NORMAL ) { - - /* - * move to the end of the list once the claim has been sent - */ - this->claimPending = FALSE; - piiuIn->chidList.remove (*this); - piiuIn->chidList.add (*this); - - if ( ! CA_V42 (CA_PROTOCOL_VERSION, piiuIn->minor_version_number) ) { - this->connect (*piiuIn, this->typeCode, this->count, this->sid); - } - } - else { - piiuIn->claimRequestsPending = true; - } - this->piiu->pcas->unlock (); - - if ( status == ECA_NORMAL ) { - return true; - } - else { - return false; + else { + return ECA_ALLOCMEM; } } - -void nciu::installIO ( baseNMIU &io ) -{ - this->piiu->pcas->lock (); - this->piiu->pcas->installIO ( io ); - this->eventq.add ( io ); - this->piiu->pcas->unlock (); -} - -void nciu::uninstallIO ( baseNMIU &io ) -{ - this->piiu->pcas->lock (); - this->eventq.remove ( io ); - this->piiu->pcas->uninstallIO ( io ); - this->piiu->pcas->unlock (); -} - -bool nciu::connected () const -{ - return this->f_connected; -} - -unsigned nciu::readSequence () const -{ - return this->piiu->pcas->readSequence (); -} - -void nciu::incrementOutstandingIO () -{ - this->piiu->pcas->incrementOutstandingIO (); -} - -void nciu::decrementOutstandingIO () -{ - this->piiu->pcas->decrementOutstandingIO (); -} - -void nciu::decrementOutstandingIO ( unsigned seqNumber ) -{ - this->piiu->pcas->decrementOutstandingIO ( seqNumber ); -} - -int nciu::subscriptionMsg ( unsigned subscriptionId, unsigned typeIn, - unsigned long countIn, unsigned short maskIn) -{ - int status; - struct monops msg; - ca_float32_t p_delta; - ca_float32_t n_delta; - ca_float32_t tmo; - - /* - * clip to the native count and set to the native count if they - * specify zero - */ - if ( countIn > this->count ){ - countIn = this->count; - } - - /* - * dont allow overflow when converting to ca_uint16_t - */ - if ( countIn > 0xffff ) { - countIn = 0xffff; - } - - /* msg header */ - msg.m_header.m_cmmd = htons ( CA_PROTO_EVENT_ADD ); - msg.m_header.m_available = subscriptionId; - msg.m_header.m_dataType = - htons ( static_cast ( typeIn ) ); - msg.m_header.m_count = - htons ( static_cast ( countIn ) ); - msg.m_header.m_cid = this->sid; - msg.m_header.m_postsize = sizeof ( msg.m_info ); - - /* msg body */ - p_delta = 0.0; - n_delta = 0.0; - tmo = 0.0; - dbr_htonf ( &p_delta, &msg.m_info.m_hval ); - dbr_htonf ( &n_delta, &msg.m_info.m_lval ); - dbr_htonf ( &tmo, &msg.m_info.m_toval ); - msg.m_info.m_mask = htons ( maskIn ); - msg.m_info.m_pad = 0; /* allow future use */ - - if ( this->f_connected ) { - status = this->piiu->pushStreamMsg ( &msg.m_header, &msg.m_info, true ); - } - else { - status = ECA_NORMAL; - } - - return status; -} - -void nciu::lock () const -{ - this->piiu->pcas->lock (); -} - -void nciu::unlock () const -{ - this->piiu->pcas->unlock (); -} \ No newline at end of file diff --git a/src/ca/nciu_IL.h b/src/ca/nciu_IL.h index 817270848..e0545fc31 100644 --- a/src/ca/nciu_IL.h +++ b/src/ca/nciu_IL.h @@ -30,7 +30,136 @@ inline void nciu::operator delete ( void *pCadaver, size_t size ) nciu::freeList.release ( pCadaver, size ); } +inline void nciu::lock () const +{ + this->cacCtx.nciuPrivate::mutex.lock (); +} + +inline void nciu::unlock () const +{ + this->cacCtx.nciuPrivate::mutex.unlock (); +} + inline bool nciu::fullyConstructed () const { return this->f_fullyConstructed; } + +inline bool nciu::identifierEquivelence ( unsigned idToMatch ) +{ + return idToMatch == this->id; +} + +inline void nciu::resetRetryCount () +{ + this->retry = 0u; +} + +inline void nciu::accessRightsStateChange ( const caar &arIn ) +{ + this->ar = ar; + this->accessRightsNotify ( this->ar ); +} + +inline unsigned nciu::getSID () const +{ + return this->sid; +} + +inline unsigned nciu::getRetrySeqNo () const +{ + return this->retrySeqNo; +} + +inline void nciu::lockPIIU () const +{ + this->lock (); + assert ( this->ptrLockCount < USHRT_MAX ); + this->ptrLockCount++; + this->unlock (); +} + +inline void nciu::unlockPIIU () const +{ + bool signalNeeded; + + this->lock (); + assert ( this->ptrLockCount > 0 ); + this->ptrLockCount--; + if ( this->ptrLockCount == 0u && this->ptrUnlockWaitCount > 0u ) { + this->ptrUnlockWaitCount--; + signalNeeded = true; + } + else { + signalNeeded = false; + } + this->unlock (); + + if ( signalNeeded ) { + this->cacCtx.nciuPrivate::ptrLockReleaseWakeup.signal (); + } +} + +inline void nciu::subscriptionCancelMsg ( ca_uint32_t clientId ) +{ + this->lockPIIU (); + + if ( this->piiu ) { + this->piiu->subscriptionCancelRequest ( clientId, this->sid, this->typeCode, this->count ); + } + + this->unlockPIIU (); +} + +inline void nciu::connect ( tcpiiu &iiu ) +{ + this->connect ( iiu, this->typeCode, this->count, this->sid ); +} + +inline void nciu::searchReplySetUp ( unsigned sidIn, + unsigned typeIn, unsigned long countIn ) +{ + this->lock (); + + this->typeCode = typeIn; + this->count = countIn; + this->sid = sidIn; + this->ar.read_access = true; + this->ar.write_access = true; + + this->unlock (); +} + +inline bool nciu::connected () const +{ + return this->f_connected; +} + +inline bool nciu::connectionInProgress ( const osiSockAddr &addrIn ) +{ + bool status; + + this->lockPIIU (); + + if ( this->piiu ) { + status = this->piiu->connectionInProgress ( this->pNameStr, addrIn ); + } + else { + status = false; + } + + this->unlockPIIU (); + + return status; +} + +inline void nciu::ioInstall ( class baseNMIU &nmiu ) +{ + this->cacCtx.ioInstall ( *this, nmiu ); +} + +inline void nciu::ioDestroy ( unsigned id ) +{ + this->cacCtx.ioDestroy ( id ); +} + diff --git a/src/ca/netReadCopyIO.cpp b/src/ca/netReadCopyIO.cpp index e52b11c63..8ff8d3d00 100644 --- a/src/ca/netReadCopyIO.cpp +++ b/src/ca/netReadCopyIO.cpp @@ -11,6 +11,8 @@ */ #include "iocinf.h" +#include "netReadCopyIO_IL.h" +#include "nciu_IL.h" tsFreeList < class netReadCopyIO, 1024 > netReadCopyIO::freeList; @@ -24,12 +26,13 @@ netReadCopyIO::netReadCopyIO ( nciu &chanIn, unsigned typeIn, unsigned long coun netReadCopyIO::~netReadCopyIO () { + } void netReadCopyIO::disconnect ( const char *pHostName ) { this->exceptionNotify ( ECA_DISCONN, pHostName ); - delete this; + this->baseNMIU::destroy (); } void netReadCopyIO::completionNotify () @@ -48,7 +51,7 @@ void netReadCopyIO::completionNotify ( unsigned typeIn, memcpy ( this->pValue, pDataIn, dbr_size_n ( typeIn, countIn ) ); # endif - chan.decrementOutstandingIO (this->seqNumber); + this->chan.decrementOutstandingIO (this->seqNumber); } else { this->exceptionNotify ( ECA_INTERNAL, "bad data type in message" ); @@ -67,13 +70,3 @@ void netReadCopyIO::exceptionNotify ( int status, "%s type=%d count=%ld\n", pContextIn, typeIn, countIn); } - -void * netReadCopyIO::operator new ( size_t size ) -{ - return netReadCopyIO::freeList.allocate ( size ); -} - -void netReadCopyIO::operator delete ( void *pCadaver, size_t size ) -{ - netReadCopyIO::freeList.release ( pCadaver, size ); -} diff --git a/src/ca/netReadCopyIO_IL.h b/src/ca/netReadCopyIO_IL.h new file mode 100644 index 000000000..7d28e2cca --- /dev/null +++ b/src/ca/netReadCopyIO_IL.h @@ -0,0 +1,52 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef netReadCopyIO_ILh +#define netReadCopyIO_ILh + +// +// we need to be careful about exporting a raw IO +// pointer because the IO object may be deleted +// at any time when the channel disconnects or the +// IO completes +// +inline bool netReadCopyIO::factory ( nciu &chan, unsigned type, + unsigned long count, void *pValue, unsigned seqNumber, unsigned &id ) +{ + netReadCopyIO *pIO = new netReadCopyIO ( chan, + type, count, pValue, seqNumber ); + if ( pIO ) { + id = pIO->getId (); + return true; + } + else { + return false; + } +} + +inline void * netReadCopyIO::operator new ( size_t size ) +{ + return netReadCopyIO::freeList.allocate ( size ); +} + +inline void netReadCopyIO::operator delete ( void *pCadaver, size_t size ) +{ + netReadCopyIO::freeList.release ( pCadaver, size ); +} + + +#endif // netReadCopyIO_ILh diff --git a/src/ca/netReadNotifyIO.cpp b/src/ca/netReadNotifyIO.cpp index d6635c1c3..21c5edaaf 100644 --- a/src/ca/netReadNotifyIO.cpp +++ b/src/ca/netReadNotifyIO.cpp @@ -22,17 +22,18 @@ netReadNotifyIO::netReadNotifyIO ( nciu &chan, cacNotify ¬ifyIn ) : netReadNotifyIO::~netReadNotifyIO () { + // private NOOP forces pool allocation } void netReadNotifyIO::destroy () { - delete this; + this->baseNMIU::destroy (); } void netReadNotifyIO::disconnect ( const char *pHostName ) { this->cacNotifyIO::exceptionNotify ( ECA_DISCONN, pHostName ); - delete this; + this->baseNMIU::destroy (); } void netReadNotifyIO::completionNotify () diff --git a/src/ca/netReadNotifyIO_IL.h b/src/ca/netReadNotifyIO_IL.h index dbba308fb..63d6580a9 100644 --- a/src/ca/netReadNotifyIO_IL.h +++ b/src/ca/netReadNotifyIO_IL.h @@ -15,13 +15,35 @@ * 505 665 1831 */ -inline void * netReadNotifyIO::operator new (size_t size) +#ifndef netReadNotifyIO_ILh +#define netReadNotifyIO_ILh + +inline void * netReadNotifyIO::operator new ( size_t size ) { - return netReadNotifyIO::freeList.allocate (size); + return netReadNotifyIO::freeList.allocate ( size ); } -inline void netReadNotifyIO::operator delete (void *pCadaver, size_t size) +inline void netReadNotifyIO::operator delete ( void *pCadaver, size_t size ) { - netReadNotifyIO::freeList.release (pCadaver,size); + netReadNotifyIO::freeList.release ( pCadaver, size ); } +// +// we need to be careful about exporting a raw IO +// pointer because the IO object may be deleted +// at any time when the channel disconnects or the +// IO completes +// +inline bool netReadNotifyIO::factory ( nciu &chan, cacNotify ¬ify, ca_uint32_t &id ) +{ + netReadNotifyIO *pIO = new netReadNotifyIO ( chan, notify ); + if ( pIO ) { + id = pIO->getId (); + return true; + } + else { + return false; + } +} + +#endif // netReadNotifyIO_ILh diff --git a/src/ca/netSubscription.cpp b/src/ca/netSubscription.cpp index 4b6295f38..df8212fd7 100644 --- a/src/ca/netSubscription.cpp +++ b/src/ca/netSubscription.cpp @@ -12,6 +12,7 @@ #include "iocinf.h" #include "netSubscription_IL.h" +#include "nciu_IL.h" tsFreeList < class netSubscription, 1024 > netSubscription::freeList; @@ -20,41 +21,22 @@ netSubscription::netSubscription ( nciu &chan, chtype typeIn, unsigned long coun cacNotifyIO (notifyIn), baseNMIU (chan), type (typeIn), count (countIn), mask (maskIn) { + this->subscriptionMsg (); } netSubscription::~netSubscription () { - if ( this->chan.connected () ) { - caHdr hdr; - ca_uint16_t type_16, count_16; - - type_16 = (ca_uint16_t) this->chan.nativeType (); - if ( this->chan.nativeElementCount () > 0xffff ) { - count_16 = 0xffff; - } - else { - count_16 = (ca_uint16_t) this->chan.nativeElementCount (); - } - - hdr.m_cmmd = htons (CA_PROTO_EVENT_CANCEL); - hdr.m_available = this->id; - hdr.m_dataType = htons ( type_16 ); - hdr.m_count = htons ( count_16 ); - hdr.m_cid = this->chan.sid; - hdr.m_postsize = 0; - - this->chan.piiu->pushStreamMsg (&hdr, NULL, true); - } + this->chan.subscriptionCancelMsg ( this->getId () ); } void netSubscription::destroy() { - delete this; + this->baseNMIU::destroy (); } int netSubscription::subscriptionMsg () { - return this->chan.subscriptionMsg ( this->id, this->type, + return this->chan.subscriptionMsg ( this->getId (), this->type, this->count, this->mask ); } diff --git a/src/ca/netSubscription_IL.h b/src/ca/netSubscription_IL.h index f28cc89fc..337460b8b 100644 --- a/src/ca/netSubscription_IL.h +++ b/src/ca/netSubscription_IL.h @@ -15,6 +15,9 @@ * 505 665 1831 */ +#ifndef netSubscription_ILh +#define netSubscription_ILh + inline void * netSubscription::operator new (size_t size) { @@ -25,3 +28,26 @@ inline void netSubscription::operator delete (void *pCadaver, size_t size) { netSubscription::freeList.release (pCadaver,size); } + +// +// we need to be careful about exporting a raw IO +// pointer because the IO object may be deleted +// at any time when the channel disconnects or the +// IO completes +// +inline bool netSubscription::factory ( nciu &chan, chtype type, unsigned long count, + unsigned short mask, cacNotify ¬ify, unsigned &id ) +{ + netSubscription *pIO = new netSubscription ( chan, type, count, mask, notify ); + if ( pIO ) { + id = pIO->getId (); + return true; + } + else { + return false; + } +} + +#endif // netSubscription_ILh + + diff --git a/src/ca/netWriteNotifyIO.cpp b/src/ca/netWriteNotifyIO.cpp index 1f2b80493..fe7251ce7 100644 --- a/src/ca/netWriteNotifyIO.cpp +++ b/src/ca/netWriteNotifyIO.cpp @@ -22,17 +22,18 @@ netWriteNotifyIO::netWriteNotifyIO (nciu &chan, cacNotify ¬ifyIn) : netWriteNotifyIO::~netWriteNotifyIO () { + // private NOOP forces pool allocation } void netWriteNotifyIO::destroy () { - delete this; + this->baseNMIU::destroy (); } void netWriteNotifyIO::disconnect ( const char *pHostName ) { this->exceptionNotify (ECA_DISCONN, pHostName); - delete this; + this->baseNMIU::destroy (); } void netWriteNotifyIO::completionNotify () diff --git a/src/ca/netWriteNotifyIO_IL.h b/src/ca/netWriteNotifyIO_IL.h index d2bdcc931..254eba003 100644 --- a/src/ca/netWriteNotifyIO_IL.h +++ b/src/ca/netWriteNotifyIO_IL.h @@ -28,3 +28,22 @@ inline void netWriteNotifyIO::operator delete ( void *pCadaver, size_t size ) netWriteNotifyIO::freeList.release ( pCadaver, size ); } +// +// we need to be careful about exporting a raw IO +// pointer because the IO object may be deleted +// at any time when the channel disconnects or the +// IO completes +// +inline bool netWriteNotifyIO::factory ( nciu &chan, cacNotify ¬ify, ca_uint32_t &id ) +{ + netWriteNotifyIO *pIO = new netWriteNotifyIO ( chan, notify ); + if ( pIO ) { + id = pIO->getId (); + return true; + } + else { + return false; + } +} + + diff --git a/src/ca/netiiu.cpp b/src/ca/netiiu.cpp index 9fe4945a9..600f069b6 100644 --- a/src/ca/netiiu.cpp +++ b/src/ca/netiiu.cpp @@ -11,25 +11,13 @@ */ #include "iocinf.h" +#include "nciu_IL.h" +#include "claimMsgCache_IL.h" -/* - * constructNIIU () - */ -netiiu::netiiu (cac *pcac) : baseIIU (pcac) -{ - ellInit (&this->chidList); -} - -/* - * netiiu::~netiiu () - */ -netiiu::~netiiu () -{ -} void netiiu::show ( unsigned /* level */ ) const { - this->pcas->lock (); + this->lock (); tsDLIterConstBD pChan ( this->chidList.first () ); while ( pChan.valid () ) { @@ -41,25 +29,217 @@ void netiiu::show ( unsigned /* level */ ) const pChan->nativeElementCount (), hostName ); switch ( pChan->state () ) { case cs_never_conn: - printf ("never connected to an IOC"); + printf ( "never connected to an IOC" ); break; case cs_prev_conn: - printf ("disconnected from IOC"); + printf ( "disconnected from IOC" ); break; case cs_conn: - printf ("connected to an IOC"); + printf ( "connected to an IOC" ); break; case cs_closed: - printf ("invalid channel"); + printf ( "invalid channel" ); break; default: break; } - printf("\n"); + printf ( "\n" ); } - this->pcas->unlock (); + this->unlock (); } +unsigned netiiu::channelCount () const +{ + return this->chidList.count (); +} +void netiiu::lock () const +{ + this->mutex.lock (); +} + +void netiiu::unlock () const +{ + this->mutex.unlock (); +} + +void netiiu::detachAllChan () +{ + this->lock (); + tsDLIterBD chan ( this->chidList.first () ); + while ( chan.valid () ) { + tsDLIterBD next = chan.itemAfter (); + chan->detachChanFromIIU (); + chan = next; + } + this->unlock (); +} + +void netiiu::disconnectAllChan () +{ + this->lock (); + tsDLIterBD chan ( this->chidList.first () ); + while ( chan.valid () ) { + tsDLIterBD next = chan.itemAfter (); + chan->disconnect (); + chan = next; + } + this->unlock (); +} + +void netiiu::connectTimeoutNotify () +{ + this->lock (); + tsDLIterBD chan ( this->chidList.first () ); + while ( chan.valid () ) { + chan->connectTimeoutNotify (); + chan++; + } + this->unlock (); +} + +void netiiu::resetChannelRetryCounts () +{ + this->lock (); + tsDLIterBD chan ( this->chidList.first () ); + while ( chan.valid () ) { + chan->resetRetryCount (); + chan++; + } + this->unlock (); +} + +bool netiiu::searchMsg ( unsigned short retrySeqNumber, unsigned &retryNoForThisChannel ) +{ + bool status; + + this->lock (); + + tsDLIterBD chan = this->chidList.first (); + if ( chan.valid () ) { + status = chan->searchMsg ( retrySeqNumber, retryNoForThisChannel ); + if ( status ) { + this->chidList.remove ( *chan ); + this->chidList.add ( *chan ); + } + } + else { + status = false; + } + + this->unlock (); + + return status; +} + +// +// considerable extra effort is taken in this routine to +// guarantee that the lock is not held while blocking +// in ::send () for buffer space. +// +void netiiu::sendPendingClaims ( tcpiiu &iiu, bool v42Ok, claimMsgCache &cache ) +{ + while ( 1 ) { + while (1) { + this->lock (); + tsDLIterBD < nciu > chan ( this->chidList.last () ); + if ( ! chan.valid () ) { + this->unlock (); + return; + } + + bool status = cache.set ( *chan ); + if ( status ) { + this->unlock (); + break; + } + + this->unlock (); + threadSleep ( 1.0 ); + } + + int status = cache.deliverMsg ( iiu ); + if ( status != ECA_NORMAL ) { + break; + } + + this->lock (); + // if the channel was not deleted while the lock was off + tsDLIterBD < nciu > chan ( this->chidList.last () ); + if ( chan.valid () ) { + if ( cache.channelMatches ( *chan ) ) { + if ( ! v42Ok ) { + chan->connect ( iiu ); + } + chan->attachChanToIIU ( iiu ); + } + } + this->unlock (); + } +} + +bool netiiu::ca_v42_ok () const +{ + return false; +} + +bool netiiu::ca_v41_ok () const +{ + return false; +} + +bool netiiu::pushDatagramMsg ( const caHdr &, const void *, ca_uint16_t ) +{ + return false; +} + +bool netiiu::connectionInProgress ( const char *pChannelName, const osiSockAddr &addr ) const +{ + return false; +} + +void netiiu::lastChannelDetachNotify () +{ +} + +int netiiu::writeRequest ( unsigned, unsigned, unsigned, const void * ) +{ + return ECA_DISCONNCHID; +} + +int netiiu::writeNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, const void *pValue ) +{ + return ECA_DISCONNCHID; +} + +int netiiu::readCopyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ) +{ + return ECA_DISCONNCHID; +} + +int netiiu::readNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ) +{ + return ECA_DISCONNCHID; +} + +int netiiu::createChannelRequest ( unsigned clientId, const char *pName, unsigned nameLength ) +{ + return ECA_DISCONNCHID; +} + +int netiiu::clearChannelRequest ( unsigned clientId, unsigned serverId ) +{ + return ECA_DISCONNCHID; +} + +int netiiu::subscriptionRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, unsigned mask ) +{ + return ECA_DISCONNCHID; +} + +int netiiu::subscriptionCancelRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ) +{ + return ECA_DISCONNCHID; +} \ No newline at end of file diff --git a/src/ca/netiiuPtr_IL.h b/src/ca/netiiuPtr_IL.h new file mode 100644 index 000000000..2aaf3a050 --- /dev/null +++ b/src/ca/netiiuPtr_IL.h @@ -0,0 +1,24 @@ + +#ifndef netiiuPtr_ILh +#define netiiuPtr_ILh + +inline netiiuPtr::netiiuPtr () : piiu ( 0 ) +{ +} + +inline netiiuPtr::~netiiuPtr () +{ +} + +inline netiiu *netiiuPtr::pIIU () const +{ + return this->piiu; +} + +inline void netiiuPtr::setPointer ( netiiu *p ) +{ + this->piiu = p; +} + + +#endif // netiiuPtr_ILh \ No newline at end of file diff --git a/src/ca/netiiu_IL.h b/src/ca/netiiu_IL.h new file mode 100644 index 000000000..0b2a1118e --- /dev/null +++ b/src/ca/netiiu_IL.h @@ -0,0 +1,35 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef netiiu_ILh +#define netiiu_ILh + +inline netiiu::netiiu ( cac &cacIn ) : + cacRef ( cacIn ) +{ +} + +inline netiiu::~netiiu () +{ +} + +inline cac & netiiu::clientCtx () const +{ + return this->cacRef; +} + +#endif // netiiu_ILh \ No newline at end of file diff --git a/src/ca/oldAccess.h b/src/ca/oldAccess.h index e7ef7134c..6fa7296af 100644 --- a/src/ca/oldAccess.h +++ b/src/ca/oldAccess.h @@ -15,6 +15,8 @@ * 505 665 1831 */ +extern "C" void cacNoConnHandler ( struct connection_handler_args args ); + struct oldChannel : public cacChannel { public: oldChannel (caCh *pConnCallBack, void *pPrivate); @@ -42,6 +44,7 @@ private: static tsFreeList < struct oldChannel, 1024 > freeList; friend int epicsShareAPI ca_array_get (chtype type, unsigned long count, chid pChan, void *pValue); + friend void cacNoConnHandler ( struct connection_handler_args args ); }; class getCallback : public cacNotify { diff --git a/src/ca/oldChannel.cpp b/src/ca/oldChannel.cpp index a5e612fc8..2e9e00378 100644 --- a/src/ca/oldChannel.cpp +++ b/src/ca/oldChannel.cpp @@ -20,53 +20,6 @@ tsFreeList < struct oldChannel, 1024 > oldChannel::freeList; -oldChannel::oldChannel (caCh *pConnCallBackIn, void *pPrivateIn) : - pConnCallBack (pConnCallBackIn), pPrivate (pPrivateIn), pAccessRightsFunc (0) -{ -} - -oldChannel::~oldChannel () -{ - this->lock (); - if ( ! this->pConnCallBack ) { - this->decrementOutstandingIO (); - } - this->unlock (); -} - -void oldChannel::destroy () -{ - delete this; -} - -void oldChannel::ioAttachNotify () -{ - this->lock (); - if ( ! this->pConnCallBack ) { - this->incrementOutstandingIO (); - } - this->unlock (); -} - -void oldChannel::ioReleaseNotify () -{ - this->lock (); - if ( ! this->pConnCallBack ) { - this->decrementOutstandingIO (); - } - this->unlock (); -} - -void oldChannel::setPrivatePointer (void *pPrivateIn) -{ - this->pPrivate = pPrivateIn; -} - -void * oldChannel::privatePointer () const -{ - return this->pPrivate; -} - /* * cacAlreadyConnHandler () * This is installed into channels which dont have @@ -78,89 +31,156 @@ extern "C" void cacAlreadyConnHandler (struct connection_handler_args) { } +/* + * cacNoConnHandler () + * This is installed into channels which dont have + * a connection handler before ca_pend_io() times + * out so that we will properly decrement the pending + * recv count in the future. + */ +extern "C" void cacNoConnHandler ( struct connection_handler_args args ) +{ + args.chid->lockOutstandingIO (); + if ( args.chid->pConnCallBack == cacNoConnHandler ) { + args.chid->pConnCallBack = cacAlreadyConnHandler; + if ( args.op == CA_OP_CONN_UP ) { + args.chid->decrementOutstandingIO (); + } + } + args.chid->unlockOutstandingIO (); +} + +extern "C" void cacNoopAccesRightsHandler ( struct access_rights_handler_args ) +{ +} + +oldChannel::oldChannel (caCh *pConnCallBackIn, void *pPrivateIn) : + pPrivate ( pPrivateIn ), pAccessRightsFunc ( cacNoopAccesRightsHandler ) +{ + if ( ! pConnCallBackIn ) { + this->pConnCallBack = cacNoConnHandler; + } + else { + this->pConnCallBack = pConnCallBackIn; + } +} + +oldChannel::~oldChannel () +{ + if ( this->pConnCallBack == cacNoConnHandler ) { + this->decrementOutstandingIO (); + } +} + +void oldChannel::destroy () +{ + delete this; +} + +void oldChannel::ioAttachNotify () +{ + this->lockOutstandingIO (); + if ( this->pConnCallBack == cacNoConnHandler ) { + this->incrementOutstandingIO (); + } + this->unlockOutstandingIO (); +} + +void oldChannel::ioReleaseNotify () +{ + this->lockOutstandingIO (); + if ( this->pConnCallBack == cacNoConnHandler ) { + this->decrementOutstandingIO (); + } + this->unlockOutstandingIO (); +} + +void oldChannel::setPrivatePointer ( void *pPrivateIn ) +{ + this->pPrivate = pPrivateIn; +} + +void * oldChannel::privatePointer () const +{ + return this->pPrivate; +} + void oldChannel::connectTimeoutNotify () { - this->lock (); - if ( ! this->pConnCallBack ) { + this->lockOutstandingIO (); + if ( this->pConnCallBack == cacNoConnHandler ) { this->pConnCallBack = cacAlreadyConnHandler; } - this->unlock (); + this->unlockOutstandingIO (); } void oldChannel::connectNotify () { - this->lock (); - if ( this->pConnCallBack ) { - caCh *pCCB = this->pConnCallBack; - struct connection_handler_args args; - args.chid = this; - args.op = CA_OP_CONN_UP; - (*pCCB) (args); - } - else { - this->pConnCallBack = cacAlreadyConnHandler; - this->decrementOutstandingIO (); - } - this->unlock (); + this->lockOutstandingIO (); + struct connection_handler_args args; + args.chid = this; + args.op = CA_OP_CONN_UP; + (*this->pConnCallBack) (args); + this->unlockOutstandingIO (); } void oldChannel::disconnectNotify () { - this->lock (); - if ( this->pConnCallBack ) { - struct connection_handler_args args; - args.chid = this; - args.op = CA_OP_CONN_DOWN; - (*this->pConnCallBack) (args); - } - this->unlock (); + this->lockOutstandingIO (); + struct connection_handler_args args; + args.chid = this; + args.op = CA_OP_CONN_DOWN; + (*this->pConnCallBack) ( args ); + this->unlockOutstandingIO (); } -void oldChannel::accessRightsNotify (caar ar) +int oldChannel::changeConnCallBack ( caCh *pfunc ) { - this->lock (); - if ( this->pAccessRightsFunc ) { - struct access_rights_handler_args args; - args.chid = this; - args.ar = ar; - ( *this->pAccessRightsFunc ) ( args ); - } - this->unlock (); -} - -int oldChannel::changeConnCallBack (caCh *pfunc) -{ - this->lock (); - if ( pfunc == 0) { - if ( this->pConnCallBack != 0 ) { - if ( this->pConnCallBack != cacAlreadyConnHandler ) { + this->lockOutstandingIO (); + if ( ! pfunc ) { + if ( this->pConnCallBack != cacNoConnHandler && + this->pConnCallBack != cacAlreadyConnHandler ) { + if ( this->state () == cs_never_conn ) { this->incrementOutstandingIO (); - this->pConnCallBack = 0; + this->pConnCallBack = cacNoConnHandler; + } + else { + this->pConnCallBack = cacAlreadyConnHandler; } } } else { - if ( this->pConnCallBack == 0 ) { + if ( this->pConnCallBack == cacNoConnHandler ) { this->decrementOutstandingIO (); } this->pConnCallBack = pfunc; } - this->unlock (); + this->unlockOutstandingIO (); return ECA_NORMAL; } -int oldChannel::replaceAccessRightsEvent (caArh *pfunc) +void oldChannel::accessRightsNotify ( caar ar ) { - this->lock (); + struct access_rights_handler_args args; + args.chid = this; + args.ar = ar; + ( *this->pAccessRightsFunc ) ( args ); +} + +int oldChannel::replaceAccessRightsEvent ( caArh *pfunc ) +{ + if ( ! pfunc ) { + pfunc = cacNoopAccesRightsHandler; + } + this->pAccessRightsFunc = pfunc; - if ( pfunc && this->connected () ) { + if ( this->connected () ) { struct access_rights_handler_args args; args.chid = this; args.ar = this->accessRights (); (*pfunc) (args); } - this->unlock (); return ECA_NORMAL; } diff --git a/src/ca/processThread.cpp b/src/ca/recvProcessThread.cpp similarity index 73% rename from src/ca/processThread.cpp rename to src/ca/recvProcessThread.cpp index 5853c0908..43eb16032 100644 --- a/src/ca/processThread.cpp +++ b/src/ca/recvProcessThread.cpp @@ -18,8 +18,8 @@ #include -processThread::processThread (cac *pcacIn) : - osiThread ( "CAC-process", threadGetStackSize (threadStackSmall), threadPriorityMedium ), +recvProcessThread::recvProcessThread (cac *pcacIn) : + osiThread ( "CAC-recv-process", threadGetStackSize (threadStackSmall), threadPriorityMedium ), pcac ( pcacIn ), enableRefCount ( 0u ), blockingForCompletion ( 0u ), @@ -29,18 +29,18 @@ processThread::processThread (cac *pcacIn) : this->start (); } -processThread::~processThread () +recvProcessThread::~recvProcessThread () { this->signalShutDown (); while ( ! this->exit.wait ( 10.0 ) ) { - printf ("processThread::~processThread (): Warning, thread object destroyed before thread exit \n"); + errlogPrintf ("recvProcessThread::~recvProcessThread (): Warning, thread object destroyed before thread exit \n"); } } -void processThread::entryPoint () +void recvProcessThread::entryPoint () { int status = ca_attach_context ( this->pcac ); - SEVCHK ( status, "attaching to client context in process thread" ); + SEVCHK ( status, "attaching to client context in recv process thread" ); while ( ! this->shutDown ) { this->mutex.lock (); @@ -58,18 +58,18 @@ void processThread::entryPoint () this->processingDone.signal (); } - this->pcac->recvActivity.wait (); + this->recvActivity.wait (); } this->exit.signal (); } -void processThread::signalShutDown () +void recvProcessThread::signalShutDown () { this->shutDown = true; - this->pcac->recvActivity.signal (); + this->recvActivity.signal (); } -void processThread::enable () +void recvProcessThread::enable () { unsigned copy; @@ -80,11 +80,11 @@ void processThread::enable () this->mutex.unlock (); if ( copy == 0u ) { - this->pcac->recvActivity.signal (); + this->recvActivity.signal (); } } -void processThread::disable () +void recvProcessThread::disable () { bool waitNeeded; @@ -111,4 +111,9 @@ void processThread::disable () this->processingDone.signal (); } } +} + +void recvProcessThread::signalActivity () +{ + this->recvActivity.signal (); } \ No newline at end of file diff --git a/src/ca/repeater.cpp b/src/ca/repeater.cpp index 3620083ea..5f5467f23 100644 --- a/src/ca/repeater.cpp +++ b/src/ca/repeater.cpp @@ -57,23 +57,44 @@ #include "iocinf.h" #include "taskwd.h" +#ifdef DEBUG +# define debugPrintf(argsInParen) printf argsInParen +#else +# define debugPrintf(argsInParen) +#endif + /* * one socket per client so we will get the ECONNREFUSED * error code (and then delete the client) */ -struct one_client { - ELLNODE node; - struct sockaddr_in from; - SOCKET sock; +class repeaterClient : public tsDLNode < repeaterClient > { +public: + repeaterClient ( const osiSockAddr &from ); + bool connect (); + bool sendConfirm (); + bool sendMessage ( const void *pBuf, unsigned bufSize ); + void destroy (); + bool verify (); + bool identicalAddress ( const osiSockAddr &from ); + bool identicalPort ( const osiSockAddr &from ); + void * operator new ( size_t size ); + void operator delete ( void *pCadaver, size_t size ); +private: + osiSockAddr from; + SOCKET sock; + static tsFreeList < class repeaterClient, 0x20 > freeList; + ~repeaterClient (); + unsigned port () const; }; /* * these can be external since there is only one instance * per machine so we dont care about reentrancy */ -static ELLLIST client_list; +static tsDLList < repeaterClient > client_list; +tsFreeList < repeaterClient, 0x20 > repeaterClient::freeList; -static char buf[ETHERNET_MAX_UDP]; +static char buf [MAX_UDP_RECV]; static const unsigned short PORT_ANY = 0u; @@ -86,14 +107,14 @@ typedef struct { /* * makeSocket() */ -LOCAL makeSocketReturn makeSocket (unsigned short port, int reuseAddr) +LOCAL makeSocketReturn makeSocket ( unsigned short port, bool reuseAddr ) { int status; struct sockaddr_in bd; makeSocketReturn msr; int flag; - msr.sock = socket (AF_INET, SOCK_DGRAM, 0); + msr.sock = socket ( AF_INET, SOCK_DGRAM, 0 ); if ( msr.sock == INVALID_SOCKET ) { msr.errNumber = SOCKERRNO; msr.pErrStr = SOCKERRSTR (msr.errNumber); @@ -110,18 +131,18 @@ LOCAL makeSocketReturn makeSocket (unsigned short port, int reuseAddr) bd.sin_addr.s_addr = htonl (INADDR_ANY); bd.sin_port = htons (port); status = bind (msr.sock, (struct sockaddr *)&bd, (int)sizeof(bd)); - if (status<0) { + if ( status < 0 ) { msr.errNumber = SOCKERRNO; - msr.pErrStr = SOCKERRSTR (msr.errNumber); + msr.pErrStr = SOCKERRSTR ( msr.errNumber ); socket_close (msr.sock); msr.sock = INVALID_SOCKET; return msr; } if (reuseAddr) { - flag = TRUE; + flag = true; status = setsockopt ( msr.sock, SOL_SOCKET, SO_REUSEADDR, - (char *)&flag, sizeof (flag) ); - if (status<0) { + (char *) &flag, sizeof (flag) ); + if ( status < 0 ) { int errnoCpy = SOCKERRNO; ca_printf ( "%s: set socket option failed because \"%s\"\n", @@ -135,131 +156,233 @@ LOCAL makeSocketReturn makeSocket (unsigned short port, int reuseAddr) return msr; } +repeaterClient::repeaterClient ( const osiSockAddr &fromIn ) : + from ( fromIn ), sock ( INVALID_SOCKET ) +{ + debugPrintf ( ( "new client %u\n", ntohs ( from.ia.sin_port ) ) ); +} + +bool repeaterClient::connect () +{ + int status; + makeSocketReturn msr; + + msr = makeSocket ( PORT_ANY, false ); + if ( msr.sock == INVALID_SOCKET ) { + ca_printf ( "%s: no client sock because %d=\"%s\"\n", + __FILE__, msr.errNumber, msr.pErrStr ); + return false; + } + + this->sock = msr.sock; + + status = ::connect ( this->sock, &this->from.sa, sizeof ( this->from.sa ) ); + if ( status < 0 ) { + int errnoCpy = SOCKERRNO; + + ca_printf ( + "%s: unable to connect client sock because \"%s\"\n", + __FILE__, SOCKERRSTR ( errnoCpy ) ); + return false; + } + + return true; +} + +bool repeaterClient::sendConfirm () +{ + int status; + + caHdr confirm; + memset ( (char *) &confirm, '\0', sizeof (confirm) ); + confirm.m_cmmd = htons ( REPEATER_CONFIRM ); + confirm.m_available = this->from.ia.sin_addr.s_addr; + status = send ( this->sock, (char *) &confirm, + sizeof (confirm), 0 ); + if ( status >= 0 ) { + assert ( status == sizeof ( confirm ) ); + return true; + } + else if ( SOCKERRNO == SOCK_ECONNREFUSED ) { + return false; + } + else { + ca_printf ( "CA Repeater: confirm err was \"%s\"\n", + SOCKERRSTR (SOCKERRNO) ); + return false; + } +} + +bool repeaterClient::sendMessage ( const void *pBuf, unsigned bufSize ) +{ + int status; + + status = send ( this->sock, (char *) pBuf, bufSize, 0 ); + if ( status >= 0 ) { + assert ( status == bufSize ); + debugPrintf ( ("Sent to %u\n", ntohs ( this->from.ia.sin_port ) ) ); + return true; + } + else { + int errnoCpy = SOCKERRNO; + if ( errnoCpy == SOCK_ECONNREFUSED ) { + debugPrintf ( ("Client refused message %u\n", ntohs ( this->from.ia.sin_port ) ) ); + } + else { + debugPrintf ( ( "CA Repeater: UDP send err was \"%s\"\n", SOCKERRSTR (errnoCpy) ) ); + } + return false; + } +} + +repeaterClient::~repeaterClient () +{ + if ( this->sock != INVALID_SOCKET ) { + socket_close ( this->sock ); + } + debugPrintf ( ( "Deleted client %u\n", ntohs ( this->from.ia.sin_port ) ) ); +} + +inline void * repeaterClient::operator new ( size_t size ) +{ + return repeaterClient::freeList.allocate ( size ); +} + +inline void repeaterClient::operator delete ( void *pCadaver, size_t size ) +{ + repeaterClient::freeList.release ( pCadaver, size ); +} + +inline void repeaterClient::destroy () +{ + delete this; +} + +inline unsigned repeaterClient::port () const +{ + return ntohs ( this->from.ia.sin_port ); +} + +inline bool repeaterClient::identicalAddress ( const osiSockAddr &from ) +{ + if ( from.sa.sa_family == this->from.sa.sa_family ) { + if ( from.ia.sin_port == this->from.ia.sin_port) { + if ( from.ia.sin_addr.s_addr == this->from.ia.sin_addr.s_addr ) { + return true; + } + } + } + return false; +} + +inline bool repeaterClient::identicalPort ( const osiSockAddr &from ) +{ + if ( from.sa.sa_family == this->from.sa.sa_family ) { + if ( from.ia.sin_port == this->from.ia.sin_port) { + return true; + } + } + return false; +} + +bool repeaterClient::verify () +{ + makeSocketReturn msr; + msr = makeSocket ( this->port (), false ); + if ( msr.sock != INVALID_SOCKET ) { + socket_close ( msr.sock ); + return false; + } + else { + /* + * win sock does not set SOCKERRNO when this fails + */ + if ( msr.errNumber != SOCK_EADDRINUSE ) { + ca_printf ( +"CA Repeater: bind test err was %d=\"%s\"\n", + msr.errNumber, msr.pErrStr ); + } + return true; + } +} + + /* * verifyClients() * (this is required because solaris has a half baked version of sockets) */ LOCAL void verifyClients() { - ELLLIST theClients; - struct one_client *pclient; - makeSocketReturn msr; + static tsDLList < repeaterClient > theClients; + repeaterClient *pclient; - ellInit(&theClients); - while ( (pclient=(struct one_client *)ellGet(&client_list)) ) { - ellAdd (&theClients, &pclient->node); - - msr = makeSocket ( ntohs (pclient->from.sin_port), FALSE ); - if ( msr.sock != INVALID_SOCKET ) { -#ifdef DEBUG - ca_printf ("Deleted client %d\n", - ntohs (pclient->from.sin_port) ); -#endif - ellDelete (&theClients, &pclient->node); - socket_close (msr.sock); - socket_close (pclient->sock); - free (pclient); + while ( pclient = client_list.get () ) { + if ( pclient->verify () ) { + theClients.add ( *pclient ); } else { - /* - * win sock does not set SOCKERRNO when this fails - */ - if ( msr.errNumber != SOCK_EADDRINUSE ) { - ca_printf ( - "CA Repeater: bind test err was %d=\"%s\"\n", - msr.errNumber, msr.pErrStr); - } + pclient->destroy (); } } - ellConcat (&client_list, &theClients); + client_list.add ( theClients ); } /* * fanOut() */ -LOCAL void fanOut (struct sockaddr_in *pFrom, const char *pMsg, unsigned msgSize) +LOCAL void fanOut ( const osiSockAddr &from, const void *pMsg, unsigned msgSize ) { - ELLLIST theClients; - struct one_client *pclient; - int status; - int verify = FALSE; + static tsDLList < repeaterClient > theClients; + repeaterClient *pclient; - ellInit(&theClients); - while ( ( pclient = (struct one_client *) ellGet (&client_list) ) ) { - ellAdd(&theClients, &pclient->node); - - /* - * Dont reflect back to sender - */ - if(pFrom->sin_port == pclient->from.sin_port && - pFrom->sin_addr.s_addr == pclient->from.sin_addr.s_addr){ + while ( pclient = client_list.get () ) { + theClients.add ( *pclient ); + /* Dont reflect back to sender */ + if ( pclient->identicalAddress ( from ) ) { continue; } - status = send ( pclient->sock, (char *)pMsg, msgSize, 0 ); - if ( status >= 0 ) { -#ifdef DEBUG - ca_printf ("Sent to %d\n", - ntohs (pclient->from.sin_port)); -#endif - } - if ( status < 0 ) { - int errnoCpy = SOCKERRNO; - if ( errnoCpy == SOCK_ECONNREFUSED ) { -#ifdef DEBUG - ca_printf ("Deleted client %d\n", - ntohs (pclient->from.sin_port)); -#endif - verify = TRUE; - } - else { - ca_printf ( -"CA Repeater: UDP fan out err was \"%s\"\n", - SOCKERRSTR(errnoCpy)); + if ( ! pclient->sendMessage ( pMsg, msgSize ) ) { + if ( ! pclient->verify () ) { + theClients.remove ( *pclient ); + pclient->destroy (); } } } - ellConcat(&client_list, &theClients); - if (verify) { - verifyClients (); - } + client_list.add ( theClients ); } /* * register_new_client() */ -LOCAL void register_new_client (struct sockaddr_in *pFrom) +LOCAL void register_new_client ( osiSockAddr &from ) { int status; - struct one_client *pclient; - caHdr confirm; - caHdr noop; - int newClient = FALSE; + bool newClient = false; makeSocketReturn msr; - if (pFrom->sin_family != AF_INET) { + if ( from.sa.sa_family != AF_INET ) { return; } /* * the repeater and its clients must be on the same host */ - if ( htonl(INADDR_LOOPBACK) != pFrom->sin_addr.s_addr ) { + if ( htonl ( INADDR_LOOPBACK ) != from.ia.sin_addr.s_addr ) { static SOCKET testSock = INVALID_SOCKET; - static int init; - struct sockaddr_in ina; + static bool init = false; if ( ! init ) { - msr = makeSocket (PORT_ANY, TRUE); + msr = makeSocket ( PORT_ANY, true ); if ( msr.sock == INVALID_SOCKET ) { - ca_printf("%s: Unable to create repeater bind test socket because %d=\"%s\"\n", - __FILE__, msr.errNumber, msr.pErrStr); + ca_printf ( "%s: Unable to create repeater bind test socket because %d=\"%s\"\n", + __FILE__, msr.errNumber, msr.pErrStr ); } else { testSock = msr.sock; } - init = TRUE; + init = true; } /* @@ -272,12 +395,14 @@ LOCAL void register_new_client (struct sockaddr_in *pFrom) * to current code. */ if ( testSock != INVALID_SOCKET ) { - ina = *pFrom; - ina.sin_port = PORT_ANY; + osiSockAddr addr; + + addr = from; + addr.ia.sin_port = PORT_ANY; /* we can only bind to a local address */ - status = bind ( testSock, (struct sockaddr *) &ina, (int) sizeof (ina) ); - if (status) { + status = bind ( testSock, &addr.sa, sizeof ( addr ) ); + if ( status ) { return; } } @@ -286,84 +411,45 @@ LOCAL void register_new_client (struct sockaddr_in *pFrom) } } - for ( pclient = (struct one_client *) ellFirst (&client_list); - pclient; pclient = (struct one_client *) ellNext (&pclient->node) ){ - - if ( pFrom->sin_port == pclient->from.sin_port ) { + tsDLIterBD < repeaterClient > pclient = client_list.first (); + while ( pclient.valid () ) { + if ( pclient->identicalPort ( from ) ) { break; } + pclient = pclient.itemAfter (); } - if ( ! pclient ) { - pclient = (struct one_client *) calloc ( 1, sizeof (*pclient) ); - if ( ! pclient ) { + if ( ! pclient.valid () ) { + pclient = new repeaterClient ( from ); + if ( ! pclient.valid () ) { ca_printf ( "%s: no memory for new client\n", __FILE__ ); return; } - msr = makeSocket (PORT_ANY, FALSE); - if ( msr.sock==INVALID_SOCKET ) { - free ( pclient ); - ca_printf ( "%s: no client sock because %d=\"%s\"\n", - __FILE__, msr.errNumber, msr.pErrStr ); + if ( ! pclient->connect () ) { + pclient->destroy (); return; } - pclient->sock = msr.sock; - - status = connect ( pclient->sock, - (struct sockaddr *) pFrom, - sizeof ( *pFrom ) ); - if ( status < 0 ) { - int errnoCpy = SOCKERRNO; - - ca_printf ( - "%s: unable to connect client sock because \"%s\"\n", - __FILE__, SOCKERRSTR (errnoCpy) ); - - socket_close ( pclient->sock ); - free ( pclient ); - return; - } - - pclient->from = *pFrom; - - ellAdd ( &client_list, &pclient->node ); - newClient = TRUE; -#ifdef DEBUG - ca_printf ( "Added %d\n", ntohs (pFrom->sin_port) ); -#endif + client_list.add ( *pclient ); + newClient = true; } - memset ( (char *) &confirm, '\0', sizeof (confirm) ); - confirm.m_cmmd = htons (REPEATER_CONFIRM); - confirm.m_available = pFrom->sin_addr.s_addr; - status = send ( pclient->sock, (char *) &confirm, - sizeof (confirm), 0 ); - if ( status >= 0 ) { - assert ( status == sizeof (confirm) ); - } - else if ( SOCKERRNO == SOCK_ECONNREFUSED ){ -#ifdef DEBUG - ca_printf ( "Deleted repeater client=%d sending ack\n", - ntohs (pFrom->sin_port) ); -#endif - ellDelete ( &client_list, &pclient->node ); - socket_close ( pclient->sock ); - free ( pclient ); - } - else { - ca_printf ( "CA Repeater: confirm err was \"%s\"\n", - SOCKERRSTR (SOCKERRNO) ); + if ( ! pclient->sendConfirm () ) { + client_list.remove (*pclient ); + pclient->destroy (); + debugPrintf ( ( "Deleted repeater client=%u (error while sending ack)\n", + ntohs (from.ia.sin_port) ) ); } /* * send a noop message to all other clients so that we dont * accumulate sockets when there are no beacons */ - memset ( (char *) &noop, '\0', sizeof (noop) ); - confirm.m_cmmd = htons (CA_PROTO_NOOP); - fanOut ( pFrom, (char *)&noop, sizeof (noop) ); + caHdr noop; + memset ( (char *) &noop, '\0', sizeof ( noop ) ); + noop.m_cmmd = htons ( CA_PROTO_NOOP ); + fanOut ( from, &noop, sizeof ( noop ) ); if ( newClient ) { /* @@ -389,8 +475,8 @@ void epicsShareAPI ca_repeater () { int size; SOCKET sock; - struct sockaddr_in from; - int from_size = sizeof from; + osiSockAddr from; + int from_size = sizeof ( from ); unsigned short port; makeSocketReturn msr; @@ -398,9 +484,7 @@ void epicsShareAPI ca_repeater () port = envGetInetPortConfigParam ( &EPICS_CA_REPEATER_PORT, CA_REPEATER_PORT ); - ellInit(&client_list); - - msr = makeSocket (port, TRUE); + msr = makeSocket ( port, true ); if ( msr.sock == INVALID_SOCKET ) { /* * test for server was already started @@ -417,15 +501,13 @@ void epicsShareAPI ca_repeater () sock = msr.sock; -#ifdef DEBUG - ca_printf ("CA Repeater: Attached and initialized\n"); -#endif + debugPrintf ( ( "CA Repeater: Attached and initialized\n" ) ); - while (TRUE) { + while ( true ) { caHdr *pMsg; size = recvfrom ( sock, buf, sizeof (buf), 0, - (struct sockaddr *)&from, &from_size ); + &from.sa, &from_size ); if ( size < 0 ) { int errnoCpy = SOCKERRNO; # ifdef linux @@ -449,8 +531,8 @@ void epicsShareAPI ca_repeater () * will register a new client */ if ( ( (size_t) size) >= sizeof (*pMsg) ) { - if ( ntohs(pMsg->m_cmmd) == REPEATER_REGISTER ) { - register_new_client (&from); + if ( ntohs ( pMsg->m_cmmd ) == REPEATER_REGISTER ) { + register_new_client ( from ); /* * strip register client message @@ -463,21 +545,21 @@ void epicsShareAPI ca_repeater () } } else if (size == 0) { - register_new_client (&from); + register_new_client ( from ); continue; } - fanOut (&from, (char *) pMsg, size); + fanOut ( from, pMsg, size ); } } /* * caRepeaterThread () */ -void caRepeaterThread (void * /* pDummy */ ) +void caRepeaterThread ( void * /* pDummy */ ) { - taskwdInsert (threadGetIdSelf(), NULL, NULL); - ca_repeater(); + taskwdInsert ( threadGetIdSelf(), NULL, NULL ); + ca_repeater (); } diff --git a/src/ca/ringBuffer.cpp b/src/ca/ringBuffer.cpp index 3a4ed8f40..b8b823cbf 100644 --- a/src/ca/ringBuffer.cpp +++ b/src/ca/ringBuffer.cpp @@ -13,50 +13,40 @@ * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 + * + * Notes: + * 1) when rdix is equal to wtix it indicates that the entire buffer is + * available to be read, and therefore nothing can be written. + * 2) the byte at index rdix + 1 is the next byte to read. + * 3) the byte at index wtix is the next byte to write. */ #include #include "ringBuffer.h" -static const unsigned ringIndexMask = nElementsInRing-1; +static const unsigned ringIndexMask = nElementsInRing - 1; /* * cacRingBufferConstruct () */ -int cacRingBufferConstruct (ringBuffer *pBuf) +bool cacRingBufferConstruct ( ringBuffer *pBuf ) { - pBuf->shutDown = 0u; pBuf->rdix = 0u; pBuf->wtix = 1u; - pBuf->readSignal = semBinaryCreate (semEmpty); - if (!pBuf->readSignal) { - return -1; - } - - pBuf->writeSignal = semBinaryCreate (semEmpty); - if (!pBuf->writeSignal) { - semBinaryDestroy (pBuf->readSignal); - return -1; - } - pBuf->readLock = semMutexCreate (); - if (!pBuf->readLock) { - semBinaryDestroy (pBuf->readSignal); - semBinaryDestroy (pBuf->writeSignal); - return -1; + if ( ! pBuf->readLock ) { + return false; } pBuf->writeLock = semMutexCreate (); - if (!pBuf->writeLock) { - semBinaryDestroy (pBuf->readSignal); - semBinaryDestroy (pBuf->writeSignal); - semMutexDestroy (pBuf->readLock); - return -1; + if ( ! pBuf->writeLock ) { + semMutexDestroy ( pBuf->readLock ); + return false; } - return 0; + return true; } /* @@ -64,35 +54,23 @@ int cacRingBufferConstruct (ringBuffer *pBuf) */ void cacRingBufferDestroy (ringBuffer *pBuf) { - semBinaryDestroy (pBuf->readSignal); - semBinaryDestroy (pBuf->writeSignal); - semMutexDestroy (pBuf->readLock); - semMutexDestroy (pBuf->writeLock); -} - -/* - * cacRingBufferShutDown (); - */ -void cacRingBufferShutDown (ringBuffer *pBuf) -{ - pBuf->shutDown = 1u; - semBinaryGive (pBuf->readSignal); - semBinaryGive (pBuf->writeSignal); + semMutexDestroy ( pBuf->readLock ); + semMutexDestroy ( pBuf->writeLock ); } /* * cacRingBufferReadSize () */ -static inline unsigned cacRingBufferReadSize (ringBuffer *pBuf) +static inline unsigned cacRingBufferReadSize ( ringBuffer *pBuf ) { unsigned long count; if ( pBuf->wtix <= pBuf->rdix ) { - static const unsigned bufSizeM1 = sizeof (pBuf->buf) - 1u; + static const unsigned bufSizeM1 = sizeof ( pBuf->buf ) - 1u; count = ( bufSizeM1 - pBuf->rdix ) + pBuf->wtix; } else { - count = (pBuf->wtix - pBuf->rdix) - 1u; + count = ( pBuf->wtix - pBuf->rdix ) - 1u; } return count; } @@ -102,11 +80,11 @@ static inline unsigned cacRingBufferReadSize (ringBuffer *pBuf) */ static inline unsigned cacRingBufferContiguousReadSize (ringBuffer *pBuf) { - static const unsigned bufSizeM1 = sizeof (pBuf->buf) - 1u; - unsigned long count; + static const unsigned bufSizeM1 = sizeof ( pBuf->buf ) - 1u; + unsigned long count; if ( pBuf->wtix <= pBuf->rdix ) { - if (pBuf->rdix==bufSizeM1) { + if ( pBuf->rdix == bufSizeM1 ) { count = pBuf->wtix; } else { @@ -114,7 +92,7 @@ static inline unsigned cacRingBufferContiguousReadSize (ringBuffer *pBuf) } } else { - count = (pBuf->wtix - pBuf->rdix) - 1u; + count = ( pBuf->wtix - pBuf->rdix ) - 1u; } return count; } @@ -122,11 +100,11 @@ static inline unsigned cacRingBufferContiguousReadSize (ringBuffer *pBuf) /* * cacRingBufferWriteSize () */ -static inline unsigned cacRingBufferWriteSize (ringBuffer *pBuf) +static inline unsigned cacRingBufferWriteSize ( ringBuffer *pBuf ) { unsigned long count; - if (pBuf->wtix <= pBuf->rdix) { + if ( pBuf->wtix <= pBuf->rdix ) { count = pBuf->rdix - pBuf->wtix; } else { @@ -138,15 +116,15 @@ static inline unsigned cacRingBufferWriteSize (ringBuffer *pBuf) /* * cacRingBufferContiguousWriteSize () */ -static inline unsigned cacRingBufferContiguousWriteSize (ringBuffer *pBuf) +static inline unsigned cacRingBufferContiguousWriteSize ( ringBuffer *pBuf ) { unsigned long count; - if (pBuf->wtix <= pBuf->rdix) { + if ( pBuf->wtix <= pBuf->rdix ) { count = pBuf->rdix - pBuf->wtix; } else { - count = sizeof (pBuf->buf) - pBuf->wtix; + count = sizeof ( pBuf->buf ) - pBuf->wtix; } return count; } @@ -158,17 +136,23 @@ static inline unsigned cacRingBufferContiguousWriteSize (ringBuffer *pBuf) * returns the number of bytes read which may be less than * the number requested. */ -static unsigned cacRingBufferReadPartial (ringBuffer *pRing, void *pBuf, - unsigned nBytes) +static unsigned cacRingBufferReadPartial ( ringBuffer *pRing, void *pBuf, + unsigned nBytes ) { unsigned totalBytes; - if ( pRing->wtix < pRing->rdix ) { - static const unsigned bufSizeM1 = sizeof (pRing->buf) - 1u; + if ( pRing->wtix <= pRing->rdix ) { + static const unsigned bufSizeM1 = sizeof ( pRing->buf ) - 1u; unsigned nBytesAvail1stBlock, nBytesAvail2ndBlock; - nBytesAvail1stBlock = bufSizeM1 - pRing->rdix; - nBytesAvail2ndBlock = pRing->wtix; + if ( pRing->rdix == bufSizeM1 ) { + nBytesAvail1stBlock = pRing->wtix; + nBytesAvail2ndBlock = 0u; + } + else { + nBytesAvail1stBlock = bufSizeM1 - pRing->rdix; + nBytesAvail2ndBlock = pRing->wtix; + } if ( nBytesAvail1stBlock >= nBytes ) { totalBytes = nBytes; memcpy ( pBuf, pRing->buf + pRing->rdix + 1u, totalBytes ); @@ -186,53 +170,15 @@ static unsigned cacRingBufferReadPartial (ringBuffer *pRing, void *pBuf, pRing->rdix += totalBytes; pRing->rdix &= ringIndexMask; } - else if ( pRing->wtix > pRing->rdix ) { - totalBytes = (pRing->wtix - pRing->rdix) - 1; + else { + totalBytes = ( pRing->wtix - pRing->rdix ) - 1; if ( totalBytes > nBytes ) { totalBytes = nBytes; } - memcpy (pBuf, pRing->buf+pRing->rdix+1, totalBytes); + memcpy ( pBuf, pRing->buf+pRing->rdix+1, totalBytes ); pRing->rdix += totalBytes; pRing->rdix &= ringIndexMask; } - else { - totalBytes = 0; - } - - return totalBytes; -} - -/* - * cacRingBufferRead () - * - * returns the number of bytes read which may be less than - * the number requested. - */ -unsigned cacRingBufferRead (ringBuffer *pRing, void *pBuf, - unsigned nBytes) -{ - unsigned char *pBufTmp = (unsigned char *) pBuf; - unsigned totalBytes = 0; - unsigned curBytes; - - semMutexMustTake (pRing->readLock); - - while (totalBytesreadSignal); - if (pRing->shutDown) { - semMutexGive (pRing->readLock); - return totalBytes; - } - } - else { - totalBytes += curBytes; - } - } - - semMutexGive (pRing->readLock); return totalBytes; } @@ -243,8 +189,8 @@ unsigned cacRingBufferRead (ringBuffer *pRing, void *pBuf, * returns the number of bytes written which may be less than * the number requested. */ -static unsigned cacRingBufferWritePartial (ringBuffer *pRing, - const void *pBuf, unsigned nBytes) +static unsigned cacRingBufferWritePartial ( ringBuffer *pRing, + const void *pBuf, unsigned nBytes ) { unsigned totalBytes; @@ -287,96 +233,37 @@ static unsigned cacRingBufferWritePartial (ringBuffer *pRing, return totalBytes; } -/* - * cacRingBufferWrite () - * - * returns the number of bytes written which may be less than - * the number requested. - */ -unsigned cacRingBufferWrite (ringBuffer *pRing, const void *pBuf, - unsigned nBytes) +void cacRingBufferWriteLock ( ringBuffer *pBuf ) { - unsigned char *pBufTmp = (unsigned char *) pBuf; - unsigned totalBytes = 0; - unsigned curBytes; - - semMutexMustTake ( pRing->writeLock ); - - while ( totalBytes < nBytes ) { - curBytes = cacRingBufferWritePartial ( pRing, - pBufTmp+totalBytes, nBytes-totalBytes ); - if ( curBytes == 0 ) { - semBinaryGive ( pRing->readSignal ); - semBinaryMustTake ( pRing->writeSignal ); - if ( pRing->shutDown ) { - semMutexGive ( pRing->writeLock ); - return totalBytes; - } - } - else { - totalBytes += curBytes; - } - } - - semMutexGive ( pRing->writeLock ); - - return totalBytes; + semMutexMustTake ( pBuf->writeLock ); } -void cacRingBufferWriteLock (ringBuffer *pBuf) +bool cacRingBufferWriteLockIfBytesAvailable ( ringBuffer *pBuf, unsigned bytesRequired ) { - semMutexMustTake (pBuf->writeLock); -} - -bool cacRingBufferWriteLockNoBlock (ringBuffer *pBuf, unsigned bytesRequired) -{ - semMutexMustTake (pBuf->writeLock); + semMutexMustTake ( pBuf->writeLock ); if ( cacRingBufferWriteSize (pBuf) < bytesRequired ) { - semMutexGive (pBuf->writeLock); + semMutexGive ( pBuf->writeLock ); return false; } return true; } -void cacRingBufferWriteUnlock (ringBuffer *pBuf) +void cacRingBufferWriteUnlock ( ringBuffer *pBuf ) { - semMutexGive (pBuf->writeLock); + semMutexGive ( pBuf->writeLock ); } -void *cacRingBufferWriteReserve (ringBuffer *pRing, unsigned *pBytesAvail) +void *cacRingBufferWriteReserve ( ringBuffer *pRing, unsigned *pBytesAvail ) { unsigned avail; - semMutexMustTake (pRing->writeLock); + semMutexMustTake ( pRing->writeLock ); - avail = cacRingBufferContiguousWriteSize (pRing); - while (avail==0) { - semBinaryGive (pRing->readSignal); - semBinaryMustTake (pRing->writeSignal); - if (pRing->shutDown) { - semMutexGive (pRing->writeLock); - *pBytesAvail = 0u; - return 0; - } - avail = cacRingBufferContiguousWriteSize (pRing); - } + avail = cacRingBufferContiguousWriteSize ( pRing ); - *pBytesAvail = avail; - - return (void *) &pRing->buf[pRing->wtix]; -} - -void *cacRingBufferWriteReserveNoBlock (ringBuffer *pRing, unsigned *pBytesAvail) -{ - unsigned avail; - - semMutexMustTake (pRing->writeLock); - - avail = cacRingBufferContiguousWriteSize (pRing); - - if ( avail==0 || pRing->shutDown ) { + if ( avail == 0 ) { *pBytesAvail = 0u; - semMutexGive (pRing->writeLock); + semMutexGive ( pRing->writeLock ); return NULL; } @@ -385,76 +272,113 @@ void *cacRingBufferWriteReserveNoBlock (ringBuffer *pRing, unsigned *pBytesAvail return (void *) &pRing->buf[pRing->wtix]; } -void cacRingBufferWriteCommit (ringBuffer *pRing, unsigned delta) +void cacRingBufferWriteCommit ( ringBuffer *pRing, unsigned delta ) { pRing->wtix += delta; pRing->wtix &= ringIndexMask; - semMutexGive (pRing->writeLock); + semMutexGive ( pRing->writeLock ); } -void *cacRingBufferReadReserve (ringBuffer *pRing, unsigned *pBytesAvail) +bool cacRingBufferWriteNoBlock ( ringBuffer *pBuf, const void *pMsg, unsigned bytesRequired ) { - unsigned avail; + unsigned nBytesWritten; - semMutexMustTake (pRing->readLock); - - avail = cacRingBufferContiguousReadSize (pRing); - while (avail==0) { - semBinaryMustTake (pRing->readSignal); - if (pRing->shutDown) { - semMutexGive (pRing->readLock); - *pBytesAvail = 0u; - return NULL; - } - avail = cacRingBufferContiguousReadSize (pRing); + semMutexMustTake ( pBuf->writeLock ); + if ( cacRingBufferWriteSize ( pBuf ) < bytesRequired ) { + semMutexGive ( pBuf->writeLock ); + return false; } - *pBytesAvail = avail; - - return (void *) &pRing->buf[(pRing->rdix+1) & ringIndexMask]; + nBytesWritten = cacRingBufferWritePartial ( pBuf, pMsg, bytesRequired ); + semMutexGive ( pBuf->writeLock ); + return nBytesWritten == bytesRequired; } -void *cacRingBufferReadReserveNoBlock (ringBuffer *pRing, unsigned *pBytesAvail) +bool cacRingBufferWriteMultipartMessageNoBlock ( ringBuffer *pBuf, + const msgDescriptor *pMsgs, unsigned nMsgs ) +{ + unsigned i; + unsigned totalBytes = 0u; + unsigned nBytesWritten; + + for ( i = 0u; i < nMsgs; i++ ) { + totalBytes += pMsgs[i].length; + } + + semMutexMustTake ( pBuf->writeLock ); + + if ( cacRingBufferWriteSize ( pBuf ) < totalBytes ) { + semMutexGive ( pBuf->writeLock ); + return false; + } + + for ( i = 0u; i < nMsgs; i++ ) { + nBytesWritten = cacRingBufferWritePartial ( pBuf, + pMsgs[i].pMsg, pMsgs[i].length ); + if ( nBytesWritten != pMsgs[i].length ) { + semMutexGive ( pBuf->writeLock ); + return false; + } + } + + semMutexGive ( pBuf->writeLock ); + return true; +} + +unsigned cacRingBufferWrite ( ringBuffer *pBuf, const void *pMsg, unsigned nBytes ) +{ + unsigned nBytesWritten; + + semMutexMustTake ( pBuf->writeLock ); + nBytesWritten = cacRingBufferWritePartial ( pBuf, pMsg, nBytes ); + semMutexGive ( pBuf->writeLock ); + + return nBytesWritten; +} + +void *cacRingBufferReadReserve ( ringBuffer *pRing, unsigned *pBytesAvail ) { unsigned avail; - semMutexMustTake (pRing->readLock); + semMutexMustTake ( pRing->readLock ); - avail = cacRingBufferContiguousReadSize (pRing); + avail = cacRingBufferContiguousReadSize ( pRing ); - if ( avail==0 || pRing->shutDown ) { + if ( avail == 0 ) { *pBytesAvail = 0u; - semMutexGive (pRing->readLock); + semMutexGive ( pRing->readLock ); return NULL; } *pBytesAvail = avail; - return (void *) &pRing->buf[(pRing->rdix+1) & ringIndexMask]; + return (void *) &pRing->buf[ ( pRing->rdix + 1 ) & ringIndexMask ]; } -void cacRingBufferReadCommit (ringBuffer *pRing, unsigned delta) +void cacRingBufferReadCommit ( ringBuffer *pRing, unsigned delta ) { pRing->rdix += delta; pRing->rdix &= ringIndexMask; - semMutexGive (pRing->readLock); + semMutexGive ( pRing->readLock ); } -bool cacRingBufferWriteFlush (ringBuffer *pRing) +bool cacRingBufferReadNoBlock ( ringBuffer *pBuf, void *pDest, unsigned nBytesRequired ) { - if ( cacRingBufferReadSize (pRing) ) { - semBinaryGive (pRing->readSignal); - return true; + semMutexMustTake ( pBuf->readLock ); + unsigned available = cacRingBufferReadSize ( pBuf ); + if ( available < nBytesRequired) { + semMutexGive ( pBuf->readLock ); + return false; } - return false; + char *pCurrent = static_cast ( pDest ); + unsigned totalBytes = cacRingBufferReadPartial ( pBuf, pCurrent, nBytesRequired ); + unsigned diff = nBytesRequired - totalBytes; + if ( diff ) { + totalBytes += cacRingBufferReadPartial ( pBuf, &pCurrent[totalBytes], diff ); + assert ( totalBytes == nBytesRequired ); + } + semMutexGive ( pBuf->readLock ); + return true; } -bool cacRingBufferReadFlush (ringBuffer *pRing) -{ - if ( cacRingBufferWriteSize (pRing) ) { - semBinaryGive (pRing->writeSignal); - return true; - } - return false; -} diff --git a/src/ca/ringBuffer.h b/src/ca/ringBuffer.h index 79e250a44..7b6630590 100644 --- a/src/ca/ringBuffer.h +++ b/src/ca/ringBuffer.h @@ -25,45 +25,43 @@ #define nElementsInRing (1<iiu.pcas->lock (); + this->lock (); this->retry = 0; if ( this->period > delayToNextTry ) { reschedule = true; @@ -56,7 +56,7 @@ void searchTimer::reset ( double delayToNextTry ) reschedule = false; } this->period = CA_RECAST_DELAY; - this->iiu.pcas->unlock (); + this->unlock (); if ( reschedule ) { this->reschedule ( delayToNextTry ); @@ -76,7 +76,7 @@ void searchTimer::setRetryInterval (unsigned retryNo) unsigned idelay; double delay; - this->iiu.pcas->lock (); + this->lock (); /* * set the retry number @@ -93,7 +93,7 @@ void searchTimer::setRetryInterval (unsigned retryNo) */ this->period = min (CA_RECAST_PERIOD, delay); - this->iiu.pcas->unlock (); + this->unlock (); debugPrintf ( ("new CA search period is %f sec\n", this->period) ); } @@ -107,17 +107,21 @@ void searchTimer::setRetryInterval (unsigned retryNo) // void searchTimer::notifySearchResponse ( unsigned short retrySeqNo ) { - this->iiu.pcas->lock (); + bool reschedualNeeded; - if ( this->retrySeqNoAtListBegin <= retrySeqNo ) { - if ( this->searchResponses < ULONG_MAX ) { - this->searchResponses++; + this->lock (); + + if ( this->retrySeqAtPassBegin <= retrySeqNo ) { + if ( this->searchResponsesWithinThisPass < UINT_MAX ) { + this->searchResponsesWithinThisPass++; } } - - this->iiu.pcas->unlock (); - if ( retrySeqNo == this->retrySeqNo ) { + reschedualNeeded = ( retrySeqNo == this->retrySeqNo ); + + this->unlock (); + + if ( reschedualNeeded ) { this->reschedule (0.0); } } @@ -127,19 +131,17 @@ void searchTimer::notifySearchResponse ( unsigned short retrySeqNo ) // void searchTimer::expire () { - tsDLIterBD chan(0); - tsDLIterBD firstChan(0); - int status; - unsigned nSent=0u; - + unsigned nFrameSent = 0u; + unsigned nChanSent = 0u; + /* * check to see if there is nothing to do here */ - if ( ellCount (&this->iiu.chidList) ==0 ) { + if ( this->iiu.channelCount () == 0 ) { return; } - this->iiu.pcas->lock (); + this->lock (); /* * increment the retry sequence number @@ -173,8 +175,8 @@ void 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->searchResponses > - (this->searchTries-(this->searchTries/16u)) ) { + if (this->searchResponsesWithinThisPass > + (this->searchTriesWithinThisPass-(this->searchTriesWithinThisPass/16u)) ) { /* * increase UDP frames per try if we have a good score */ @@ -189,33 +191,26 @@ void searchTimer::expire () this->framesPerTry += (this->framesPerTry/8) + 1; } debugPrintf ( ("Increasing frame count to %u t=%u r=%u\n", - this->framesPerTry, this->searchTries, this->searchResponses) ); + this->framesPerTry, this->searchTriesWithinThisPass, this->searchResponsesWithinThisPass) ); } } /* * if we detect congestion because we have less than a 87.5% success * rate then gradually reduce the frames per try */ - else if ( this->searchResponses < - (this->searchTries-(this->searchTries/8u)) ) { + 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->searchTries, - this->searchResponses) ); + this->framesPerTry, this->searchTriesWithinThisPass, + this->searchResponsesWithinThisPass) ); } - /* - * a successful chan->searchMsg() sends channel to - * the end of the list - */ - firstChan = chan = this->iiu.chidList.first (); - while ( chan.valid () ) { - - this->minRetry = min (this->minRetry, chan->retry); - + while ( 1 ) { + /* * clear counter when we reach the end of the list * @@ -223,10 +218,10 @@ void searchTimer::expire () * dont increase the delay between search * requests */ - if ( this->iiu.pcas->endOfBCastList == chan ) { - if ( this->searchResponses == 0u ) { + if ( this->searchTriesWithinThisPass >= this->iiu.channelCount () ) { + if ( this->searchResponsesWithinThisPass == 0u ) { debugPrintf ( ("increasing search try interval\n") ); - this->setRetryInterval (this->minRetry + 1u); + this->setRetryInterval ( this->minRetry + 1u ); } this->minRetry = UINT_MAX; @@ -243,83 +238,67 @@ void searchTimer::expire () /* * so that old search tries will not update the counters */ - this->retrySeqNoAtListBegin = this->retrySeqNo; + this->retrySeqAtPassBegin = this->retrySeqNo; - /* - * reset the search try/response counters at the end of the list - * (sequence number) so that we dont overflow, but dont subtract - * out tries that dont have a matching response yet in case they - * are delayed - */ - if ( this->searchTries > this->searchResponses ) { - this->searchTries -= this->searchResponses; - } - else { - this->searchTries = 0; - } - this->searchResponses = 0; + this->searchTriesWithinThisPass = 0; + this->searchResponsesWithinThisPass = 0; debugPrintf ( ("saw end of list\n") ); } - /* - * this moves the channel to the end of the - * list (if successful) - */ - status = chan->searchMsg (); - if ( status != ECA_NORMAL ) { - nSent++; + unsigned retryNoForThisChannel; + if ( ! this->iiu.searchMsg ( this->retrySeqNo, retryNoForThisChannel ) ) { + nFrameSent++; - if ( nSent >= this->framesPerTry ) { + if ( nFrameSent >= this->framesPerTry ) { break; } - // flush out the search request buffer this->iiu.flush (); - // try again - status = chan->searchMsg (); - if (status != ECA_NORMAL) { + if ( ! this->iiu.searchMsg ( this->retrySeqNo, retryNoForThisChannel ) ) { break; } } - if ( this->searchTries < ULONG_MAX ) { - this->searchTries++; - } + this->minRetry = min ( this->minRetry, retryNoForThisChannel ); - chan->retrySeqNo = this->retrySeqNo; - chan = this->iiu.chidList.first (); + if ( this->searchTriesWithinThisPass < UINT_MAX ) { + this->searchTriesWithinThisPass++; + } + if ( nChanSent < UINT_MAX ) { + nChanSent++; + } /* * dont send any of the channels twice within one try */ - if ( chan == firstChan ) { + if ( nChanSent >= this->iiu.channelCount () ) { /* - * add one to nSent because there may be + * add one to nFrameSent because there may be * one more partial frame to be sent */ - nSent++; + nFrameSent++; /* * cap this->framesPerTry to * the number of frames required for all of * the unresolved channels */ - if (this->framesPerTry>nSent) { - this->framesPerTry = nSent; + if ( this->framesPerTry > nFrameSent ) { + this->framesPerTry = nFrameSent; } break; } } - this->iiu.pcas->unlock (); + this->unlock (); // flush out the search request buffer this->iiu.flush (); - debugPrintf ( ("sent %u delay sec=%f\n", nSent, this->period) ); + debugPrintf ( ("sent %u delay sec=%f\n", nFrameSent, this->period) ); } void searchTimer::destroy () @@ -328,7 +307,7 @@ void searchTimer::destroy () bool searchTimer::again () const { - if ( ellCount (&this->iiu.chidList) == 0 ) { + if ( this->iiu.channelCount () == 0 ) { return false; } else { diff --git a/src/ca/service.cpp b/src/ca/service.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/ca/tcpRecvWatchdog.cpp b/src/ca/tcpRecvWatchdog.cpp index a196726bd..00b703902 100644 --- a/src/ca/tcpRecvWatchdog.cpp +++ b/src/ca/tcpRecvWatchdog.cpp @@ -29,14 +29,7 @@ tcpRecvWatchdog::~tcpRecvWatchdog () void tcpRecvWatchdog::expire () { - /* - * remain backwards compatible with old servers - * ( this isnt an echo request ) - */ - if ( ! this->echoProtocolAccepted ) { - this->noopRequestMsg (); - } - else if ( this->responsePending ) { + if ( this->responsePending ) { char hostName[128]; this->hostName ( hostName, sizeof (hostName) ); ca_printf ( "CA server %s unresponsive for %g sec. Disconnecting.\n", @@ -44,8 +37,10 @@ void tcpRecvWatchdog::expire () this->shutdown (); } else { - this->echoRequestMsg (); - this->responsePending = true; + this->echoRequest (); + if ( this->echoProtocolAccepted ) { + this->responsePending = true; + } } } diff --git a/src/ca/tcpiiu.cpp b/src/ca/tcpiiu.cpp index eb6425bd2..b47c0f4d0 100644 --- a/src/ca/tcpiiu.cpp +++ b/src/ca/tcpiiu.cpp @@ -16,11 +16,48 @@ #include "inetAddrID_IL.h" #include "bhe_IL.h" #include "tcpiiu_IL.h" +#include "claimMsgCache_IL.h" +#include "cac_IL.h" +#include "comQueSend_IL.h" +#include "netiiu_IL.h" const caHdr cacnullmsg = { 0,0,0,0,0,0 }; +// TCP protocol jump table +const tcpiiu::pProtoStubTCP tcpiiu::tcpJumpTableCAC [] = +{ + tcpiiu::noopAction, + tcpiiu::eventRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::readRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::exceptionRespAction, + tcpiiu::clearChannelRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::readNotifyRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::claimCIURespAction, + tcpiiu::writeNotifyRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::accessRightsRespAction, + tcpiiu::echoRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::badTCPRespAction, + tcpiiu::verifyAndDisconnectChan, + tcpiiu::verifyAndDisconnectChan +}; + tsFreeList < class tcpiiu, 16 > tcpiiu::freeList; #ifdef DEBUG @@ -33,8 +70,6 @@ tsFreeList < class tcpiiu, 16 > tcpiiu::freeList; extern CACVRTFUNC *cac_dbr_cvrt[]; #endif /*CONVERSION_REQUIRED*/ -typedef void (*pProtoStubTCP) (tcpiiu *piiu); - const static char nullBuff[32] = { 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, @@ -42,53 +77,6 @@ const static char nullBuff[32] = { 0,0 }; -/* - * constructTCPIIU () - * (lock must be applied by caller) - */ -tcpiiu * constructTCPIIU (cac *pcac, const struct sockaddr_in *pina, - unsigned minorVersion) -{ - bhe *pBHE; - tcpiiu *piiu; - - /* - * look for an existing virtual circuit - */ - pBHE = pcac->lookupBeaconInetAddr ( *pina ); - if ( ! pBHE ) { - pBHE = pcac->createBeaconHashEntry ( *pina, osiTime () ); - if ( ! pBHE ) { - return NULL; - } - } - - piiu = pBHE->getIIU (); - if ( piiu ) { - if ( piiu->state == iiu_connecting || - piiu->state == iiu_connected ) { - return piiu; - } - else { - return NULL; - } - } - - piiu = new tcpiiu ( pcac, *pina, minorVersion, *pBHE ); - if (!piiu) { - return NULL; - } - - if ( piiu->fullyConstructed () ) { - return piiu; - } - else { - delete piiu; - return NULL; - } -} - - /* * tcpiiu::connect () */ @@ -103,8 +91,9 @@ void tcpiiu::connect () while (1) { int errnoCpy; - status = ::connect ( this->sock, &this->dest.sa, - sizeof ( this->dest.sa ) ); + osiSockAddr addr = this->ipToA.address (); + status = ::connect ( this->sock, &addr.sa, + sizeof ( addr.sa ) ); if ( status == 0 ) { this->cancelSendWatchdog (); // put the iiu into the connected state @@ -130,157 +119,137 @@ void tcpiiu::connect () else { this->cancelSendWatchdog (); ca_printf ( "Unable to connect because %d=\"%s\"\n", - errnoCpy, SOCKERRSTR (errnoCpy) ); + errnoCpy, SOCKERRSTR ( errnoCpy ) ); this->shutdown (); return; } } } -/* - * retryPendingClaims() - * - * This assumes that all channels with claims pending are at the - * front of the list (and that the channel is moved to the end of - * the list when a claim message has been sent for it) - * - * We send claim messages here until the outgoing message buffer - * will not accept any more messages - */ -LOCAL void retryPendingClaims (tcpiiu *piiu) -{ - bool success; - - piiu->pcas->lock (); - tsDLIterBD chan ( piiu->chidList.first () ); - while ( chan.valid () ) { - if ( ! chan->claimPending ) { - piiu->claimRequestsPending = false; - piiu->flush (); - break; - } - // this moves the channel to the end of the list - success = chan->claimMsg ( piiu ); - if ( ! success ) { - piiu->flush (); - break; - } - chan = piiu->chidList.first (); - } - piiu->pcas->unlock (); -} - /* * cacSendThreadTCP () */ -extern "C" void cacSendThreadTCP (void *pParam) +extern "C" void cacSendThreadTCP ( void *pParam ) { - tcpiiu *piiu = (tcpiiu *) pParam; + tcpiiu *piiu = ( tcpiiu * ) pParam; + claimMsgCache cache ( CA_V44 ( CA_PROTOCOL_VERSION, piiu->minor_version_number ) ); while ( true ) { - unsigned sendCnt; - char *pOutBuf; - int status; - pOutBuf = static_cast ( cacRingBufferReadReserveNoBlock (&piiu->send, &sendCnt) ); - while ( ! pOutBuf ) { - piiu->cancelSendWatchdog (); - if ( piiu->state != iiu_connected ) { - semBinaryGive ( piiu->sendThreadExitSignal ); - return; - } - pOutBuf = (char *) cacRingBufferReadReserve (&piiu->send, &sendCnt); - } + semBinaryMustTake ( piiu->sendThreadFlushSignal ); - assert ( sendCnt <= INT_MAX ); - status = send ( piiu->sock, pOutBuf, (int) sendCnt, 0 ); - if ( status > 0 ) { - cacRingBufferReadCommit ( &piiu->send, (unsigned long) status ); - cacRingBufferReadFlush ( &piiu->send ); - if ( piiu->claimRequestsPending ) { - retryPendingClaims ( piiu ); - } - if ( piiu->echoRequestPending ) { - piiu->echoRequestMsg (); - } - } - else { - int localError = SOCKERRNO; - - cacRingBufferReadCommit (&piiu->send, 0); - - if ( status == 0 ) { - piiu->shutdown (); - break; - } - - if ( localError == SOCK_SHUTDOWN ) { - break; - } - - if ( localError == SOCK_EINTR ) { - if ( piiu->state == iiu_disconnected ) { - break; - } - else { - continue; - } - } - - if ( localError != SOCK_EPIPE && localError != SOCK_ECONNRESET && - localError != SOCK_ETIMEDOUT) { - ca_printf ("CAC: unexpected TCP send error: %s\n", SOCKERRSTR (localError) ); - } - - piiu->shutdown (); + if ( piiu->state != iiu_connected ) { break; } + + if ( piiu->pClaimsPendingIIU->channelCount () > 0u ) { + piiu->pClaimsPendingIIU->sendPendingClaims ( *piiu, + CA_V42 ( CA_PROTOCOL_VERSION, piiu->minor_version_number ), cache ); + piiu->flushPending = true; + } + + if ( piiu->busyStateDetected != piiu->flowControlActive ) { + if ( piiu->flowControlActive ) { + piiu->enableFlowControlMsg (); + } + else { + piiu->disableFlowControlMsg (); + } + piiu->flushPending = true; + } + + if ( piiu->echoRequestPending ) { + piiu->echoRequestMsg (); + piiu->flushPending = true; + } + + if ( piiu->flushPending ) { + if ( ! piiu->flushToWire () ) { + break; + } + piiu->flushPending = false; + } } semBinaryGive ( piiu->sendThreadExitSignal ); } -/* - * tcpiiu::recvMsg () - */ -void tcpiiu::recvMsg () +unsigned tcpiiu::sendBytes ( const void *pBuf, unsigned nBytesInBuf ) +{ + int status; + + assert ( nBytesInBuf <= INT_MAX ); + + while ( true ) { + this->clientCtx ().enableCallbackPreemption (); + this->armSendWatchdog (); + status = ::send ( this->sock, + static_cast < const char * > (pBuf), (int) nBytesInBuf, 0 ); + this->clientCtx ().disableCallbackPreemption (); + if ( status > 0 ) { + this->cancelSendWatchdog (); + return ( unsigned ) status; + } + else { + int localError = SOCKERRNO; + + if ( status == 0 ) { + this->cancelSendWatchdog (); + this->shutdown (); + return 0u; + } + + if ( localError == SOCK_SHUTDOWN ) { + this->cancelSendWatchdog (); + return 0u; + } + + if ( localError == SOCK_EINTR ) { + continue; + } + + if ( localError != SOCK_EPIPE && localError != SOCK_ECONNRESET && + localError != SOCK_ETIMEDOUT && localError != SOCK_ECONNABORTED ) { + ca_printf ("CAC: unexpected TCP send error: %s\n", SOCKERRSTR (localError) ); + } + + this->cancelSendWatchdog (); + this->shutdown (); + return 0u; + } + } +} + +unsigned tcpiiu::recvBytes ( void *pBuf, unsigned nBytesInBuf ) { - char *pProto; - unsigned writeSpace; unsigned totalBytes; int status; if ( this->state != iiu_connected ) { - return; - } - - pProto = (char *) cacRingBufferWriteReserve ( &this->recv, &writeSpace ); - if ( ! pProto ) { - return; + return 0u; } - assert ( writeSpace <= INT_MAX ); - status = ::recv ( this->sock, pProto, (int) writeSpace, 0); + assert ( nBytesInBuf <= INT_MAX ); + status = ::recv ( this->sock, static_cast ( pBuf ), + static_cast ( nBytesInBuf ), 0); if ( status <= 0 ) { int localErrno = SOCKERRNO; - cacRingBufferWriteCommit ( &this->recv, 0 ); - if ( status == 0 ) { this->shutdown (); - return; + return 0u; } if ( localErrno == SOCK_SHUTDOWN ) { - return; + return 0u; } if ( localErrno == SOCK_EINTR ) { - return; + return 0u; } if ( localErrno == SOCK_ECONNABORTED ) { - return; + return 0u; } { @@ -292,28 +261,34 @@ void tcpiiu::recvMsg () this->shutdown (); - return; + return 0u; } - assert ( ( (unsigned) status ) <= writeSpace ); - totalBytes = (unsigned) status; + assert ( static_cast ( status ) <= nBytesInBuf ); + totalBytes = static_cast ( status ); - if ( writeSpace == totalBytes ) { - this->flowControlOn (); + if ( nBytesInBuf == totalBytes ) { + if ( this->contigRecvMsgCount >= contiguousMsgCountWhichTriggersFlowControl ) { + this->busyStateDetected = true; + } + else { + this->contigRecvMsgCount++; + } } else { - this->flowControlOff (); + this->contigRecvMsgCount = 0u; + this->busyStateDetected = false; } - - cacRingBufferWriteCommit ( &this->recv, totalBytes ); - // cacRingBufferWriteFlush (&this->recv); + // care is taken not to post the busy/ready message to the send buffer + // here so that we avoid push/pull deadlocks + this->flowControlStateChange = this->busyStateDetected != this->flowControlActive; + this->messageArrivalNotify (); // reschedule connection activity watchdog - return; + return totalBytes; } - /* * cacRecvThreadTCP () */ @@ -328,40 +303,56 @@ extern "C" void cacRecvThreadTCP (void *pParam) threadBoolStatus tbs; threadId tid; - tbs = threadHighestPriorityLevelBelow (priorityOfSelf, &priorityOfSend); + tbs = threadLowestPriorityLevelAbove ( priorityOfSelf, &priorityOfSend ); if ( tbs != tbsSuccess ) { priorityOfSend = priorityOfSelf; } - tid = threadCreate ("CAC-TCP-send", priorityOfSend, - threadGetStackSize (threadStackMedium), cacSendThreadTCP, piiu); + tid = threadCreate ( "CAC-TCP-send", priorityOfSend, + threadGetStackSize ( threadStackMedium ), cacSendThreadTCP, piiu ); if ( tid ) { - while (1) { - piiu->recvMsg (); - if ( piiu->state != iiu_connected ) { - break; + while ( piiu->state == iiu_connected ) { + if ( piiu->comQueRecv::occupiedBytes () >= 0x4000 ) { + semBinaryMustTake ( piiu->recvThreadRingBufferSpaceAvailableSignal ); + } + else { + unsigned nBytesIn = piiu->fillFromWire (); + if ( nBytesIn ) { + piiu->recvPending = true; + piiu->clientCtx ().signalRecvActivity (); + } } - piiu->pcas->signalRecvActivityIIU (*piiu); } } else { - semBinaryGive (piiu->sendThreadExitSignal); + semBinaryGive ( piiu->sendThreadExitSignal ); piiu->shutdown (); } } else { - semBinaryGive (piiu->sendThreadExitSignal); + semBinaryGive ( piiu->sendThreadExitSignal ); } - semBinaryGive (piiu->recvThreadExitSignal); + semBinaryGive ( piiu->recvThreadExitSignal ); } // // tcpiiu::tcpiiu () // -tcpiiu::tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, class bhe &bheIn) : - tcpRecvWatchdog (pcac->ca_connectTMO, *pcac->pTimerQueue, CA_V43 (CA_PROTOCOL_VERSION, minorVersion) ), - tcpSendWatchdog (pcac->ca_connectTMO, *pcac->pTimerQueue), - netiiu (pcac), - bhe (bheIn) +tcpiiu::tcpiiu ( cac &cac, const osiSockAddr &addrIn, + unsigned minorVersion, class bhe &bheIn, + double connectionTimeout, osiTimerQueue &timerQueue, + ipAddrToAsciiEngine &engineIn ) : + ipToA ( addrIn, engineIn ), + tcpRecvWatchdog ( connectionTimeout, timerQueue, CA_V43 (CA_PROTOCOL_VERSION, minorVersion ) ), + tcpSendWatchdog ( connectionTimeout, timerQueue ), + netiiu ( cac ), + bhe ( bheIn ), + contigRecvMsgCount ( 0u ), + busyStateDetected ( false ), + flowControlActive ( false ), + flowControlStateChange ( false ), + recvPending ( false ), + flushPending ( false ), + msgHeaderAvailable ( false ) { SOCKET newSocket; int status; @@ -371,7 +362,7 @@ tcpiiu::tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, if ( newSocket == INVALID_SOCKET ) { ca_printf ("CAC: unable to create virtual circuit because \"%s\"\n", SOCKERRSTR (SOCKERRNO)); - this->fc = false; + this->fullyConstructedFlag = false; return; } @@ -422,42 +413,11 @@ tcpiiu::tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, this->sock = newSocket; this->minor_version_number = minorVersion; - this->dest.ia = ina; - this->contiguous_msg_count = 0u; - this->client_busy = false; - this->claimRequestsPending = false; this->echoRequestPending = false; - this->sendPending = false; - this->pushPending = false; this->curDataMax = 0ul; - this->curMsgBytes = 0ul; - this->curDataBytes = 0ul; memset ( (void *) &this->curMsg, '\0', sizeof (this->curMsg) ); this->pCurData = 0; - status = cacRingBufferConstruct (&this->recv); - if (status) { - ca_printf ("CA: unable to create recv ring buffer\n"); - socket_close ( newSocket ); - this->fc = false; - return; - } - - status = cacRingBufferConstruct (&this->send); - if (status) { - ca_printf ("CA: unable to create send ring buffer\n"); - cacRingBufferDestroy (&this->recv); - socket_close ( newSocket ); - this->fc = false; - return; - } - - /* - * Save the Host name for efficient access in the - * future. - */ - ipAddrToA ( &this->dest.ia, this->host_name_str, sizeof (this->host_name_str) ); - /* * TCP starts out in the connecting state and later transitions * to the connected state @@ -467,10 +427,8 @@ tcpiiu::tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, this->sendThreadExitSignal = semBinaryCreate (semEmpty); if ( ! this->sendThreadExitSignal ) { ca_printf ("CA: unable to create CA client send thread exit semaphore\n"); - cacRingBufferDestroy (&this->recv); - cacRingBufferDestroy (&this->send); socket_close ( newSocket ); - this->fc = false; + this->fullyConstructedFlag = false; return; } @@ -478,18 +436,50 @@ tcpiiu::tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, if ( ! this->recvThreadExitSignal ) { ca_printf ("CA: unable to create CA client send thread exit semaphore\n"); semBinaryDestroy (this->sendThreadExitSignal); - cacRingBufferDestroy (&this->recv); - cacRingBufferDestroy (&this->send); socket_close ( newSocket ); - this->fc = false; + this->fullyConstructedFlag = false; return; } + this->pClaimsPendingIIU = new claimsPendingIIU ( *this ); + if ( ! this->pClaimsPendingIIU ) { + ca_printf ("CA: unable to create claimsPendingIIU object\n"); + semBinaryDestroy (this->sendThreadExitSignal); + socket_close ( newSocket ); + this->fullyConstructedFlag = false; + return; + } + + this->sendThreadFlushSignal = semBinaryCreate ( semEmpty ); + if ( ! this->sendThreadFlushSignal ) { + ca_printf ("CA: unable to create sendThreadFlushSignal object\n"); + semBinaryDestroy (this->sendThreadExitSignal); + socket_close ( newSocket ); + delete this->pClaimsPendingIIU; + this->fullyConstructedFlag = false; + return; + } + + this->recvThreadRingBufferSpaceAvailableSignal = semBinaryCreate ( semEmpty ); + if ( ! this->recvThreadRingBufferSpaceAvailableSignal ) { + ca_printf ("CA: unable to create recvThreadRingBufferSpaceAvailableSignal object\n"); + semBinaryDestroy (this->sendThreadExitSignal); + semBinaryDestroy (this->sendThreadFlushSignal); + socket_close ( newSocket ); + delete this->pClaimsPendingIIU; + this->fullyConstructedFlag = false; + return; + } + + this->userNameSetMsg (); + + this->hostNameSetMsg (); + { unsigned priorityOfSelf = threadGetPrioritySelf (); unsigned priorityOfRecv; - threadId tid; threadBoolStatus tbs; + threadId tid; tbs = threadHighestPriorityLevelBelow (priorityOfSelf, &priorityOfRecv); if ( tbs != tbsSuccess ) { @@ -498,22 +488,23 @@ tcpiiu::tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, tid = threadCreate ("CAC-TCP-recv", priorityOfRecv, threadGetStackSize (threadStackMedium), cacRecvThreadTCP, this); - if (tid==0) { + if ( tid == 0 ) { ca_printf ("CA: unable to create CA client receive thread\n"); semBinaryDestroy (this->recvThreadExitSignal); semBinaryDestroy (this->sendThreadExitSignal); - cacRingBufferDestroy (&this->recv); - cacRingBufferDestroy (&this->send); socket_close ( newSocket ); - this->fc = false; + delete this->pClaimsPendingIIU; + semBinaryDestroy (this->sendThreadFlushSignal); + semBinaryDestroy (this->recvThreadRingBufferSpaceAvailableSignal); + this->fullyConstructedFlag = false; return; } } bhe.bindToIIU ( *this ); - pcac->installIIU ( *this ); + cac.installIIU ( *this ); - this->fc = true; + this->fullyConstructedFlag = true; } /* @@ -521,21 +512,29 @@ tcpiiu::tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, */ void tcpiiu::shutdown () { - this->pcas->lock (); - if ( this->state != iiu_disconnected ) { - int status; + bool laborNeeded; + this->lock (); + if ( this->state != iiu_disconnected ) { this->state = iiu_disconnected; - status = ::shutdown ( this->sock, SD_BOTH ); + laborNeeded = true; + } + else { + laborNeeded = false; + } + this->unlock (); + + if ( laborNeeded ) { + int status = ::shutdown ( this->sock, SD_BOTH ); if ( status ) { errlogPrintf ("CAC TCP shutdown error was %s\n", SOCKERRSTR (SOCKERRNO) ); } - cacRingBufferShutDown ( &this->send ); - cacRingBufferShutDown ( &this->recv ); - this->pcas->signalRecvActivityIIU ( *this ); + semBinaryGive ( this->sendThreadFlushSignal ); + semBinaryGive ( this->recvThreadRingBufferSpaceAvailableSignal ); + this->recvPending = true; + this->clientCtx ().signalRecvActivity (); } - this->pcas->unlock (); } // @@ -543,50 +542,44 @@ void tcpiiu::shutdown () // tcpiiu::~tcpiiu () { - unsigned chanDisconnectCount; - - if ( ! this->fc ) { + if ( ! this->fullyConstructedFlag ) { return; } - this->fc = false; + this->fullyConstructedFlag = false; this->shutdown (); - this->pcas->lock (); - - chanDisconnectCount = ellCount (&this->chidList); - if ( chanDisconnectCount ) { - genLocalExcep ( this->pcas, ECA_DISCONN, this->host_name_str ); + if ( this->channelCount () ) { + char hostName[64]; + this->ipToA.hostName ( hostName, sizeof ( hostName ) ); + genLocalExcep ( this->clientCtx (), ECA_DISCONN, hostName ); } - tsDLIterBD iter ( this->chidList.first () ); - while ( iter.valid () ) { - tsDLIterBD next = iter.itemAfter (); - iter->disconnect (); - iter = next; - } - - this->pcas->unlock (); + this->disconnectAllChan (); // wait for send and recv threads to exit semBinaryMustTake ( this->sendThreadExitSignal ); semBinaryMustTake ( this->recvThreadExitSignal ); - semBinaryDestroy (this->sendThreadExitSignal); - semBinaryDestroy (this->recvThreadExitSignal); - this->pcas->removeIIU ( *this ); + if ( this->pClaimsPendingIIU ) { + delete this->pClaimsPendingIIU; + } - socket_close (this->sock); + semBinaryDestroy ( this->sendThreadExitSignal ); + semBinaryDestroy ( this->recvThreadExitSignal ); + semBinaryDestroy ( this->sendThreadFlushSignal ); + semBinaryDestroy ( this->recvThreadRingBufferSpaceAvailableSignal ); - cacRingBufferDestroy (&this->recv); - cacRingBufferDestroy (&this->send); + this->clientCtx ().removeIIU ( *this ); + + socket_close ( this->sock ); /* * free message body cache */ - if (this->pCurData) { - free (this->pCurData); + if ( this->pCurData ) { + free ( this->pCurData ); } } @@ -595,17 +588,18 @@ void tcpiiu::suicide () delete this; } -bool tcpiiu::compareIfTCP ( nciu &chan, const sockaddr_in &addr ) const +bool tcpiiu::connectionInProgress ( const char *pChannelName, const osiSockAddr &addr ) const { - if ( this->dest.ia.sin_addr.s_addr != addr.sin_addr.s_addr || - this->dest.ia.sin_port != addr.sin_port ) { + if ( ! this->ipToA.identicalAddress ( addr ) ) { + char acc[64]; char rej[64]; char buf[256]; - ipAddrToA ( &addr, rej, sizeof (rej) ); - sprintf ( buf, "Channel: %64s Accepted: %64s Rejected: %64s", - chan.pName (), this->host_name_str, rej ); - genLocalExcep ( this->pcas, ECA_DBLCHNL, buf ); + this->ipToA.hostName ( acc, sizeof (acc) ); + sockAddrToA ( &addr.sa, rej, sizeof (rej) ); + sprintf ( buf, "Channel: \"%.64s\", Connecting to: %.64s, Ignored: %.64s", + pChannelName, acc, rej ); + genLocalExcep ( this->clientCtx (), ECA_DBLCHNL, buf ); } return true; } @@ -614,25 +608,10 @@ void tcpiiu::show ( unsigned /* level */ ) const { } -/* - * tcpiiu::noopRequestMsg () - */ -void tcpiiu::noopRequestMsg () +void tcpiiu::echoRequest () { - caHdr hdr; - int status; - - hdr.m_cmmd = htons (CA_PROTO_NOOP); - hdr.m_dataType = htons (0); - hdr.m_count = htons (0); - hdr.m_cid = htons (0); - hdr.m_available = htons (0); - hdr.m_postsize = 0; - - status = this->pushStreamMsg (&hdr, NULL, true); - if ( status == ECA_NORMAL ) { - this->flush (); - } + this->echoRequestPending = true; + this->flush (); } /* @@ -640,64 +619,38 @@ void tcpiiu::noopRequestMsg () */ void tcpiiu::echoRequestMsg () { - caHdr hdr; - - hdr.m_cmmd = htons (CA_PROTO_ECHO); - hdr.m_dataType = htons (0); - hdr.m_count = htons (0); - hdr.m_cid = htons (0); - hdr.m_available = htons (0); - hdr.m_postsize = 0u; - - /* - * If we are out of buffer space then postpone this - * operation until later. This avoids any possibility - * of a push pull deadlock (since this can be sent when - * parsing the UDP input buffer). - */ - if ( this->pushStreamMsg (&hdr, NULL, false) == ECA_NORMAL ) { - this->flush (); - this->echoRequestPending = false; + if ( CA_V43 ( CA_PROTOCOL_VERSION, this->minor_version_number ) ) { + this->comQueSend::echoRequest (); } else { - this->echoRequestPending = true; + this->comQueSend::noopRequest (); } + + this->echoRequestPending = false; } /* - * tcpiiu::busyRequestMsg () + * tcpiiu::enableFlowControlMsg () */ -int tcpiiu::busyRequestMsg () +void tcpiiu::enableFlowControlMsg () { - caHdr hdr; - int status; - - hdr = cacnullmsg; - hdr.m_cmmd = htons ( CA_PROTO_EVENTS_OFF ); - - status = this->pushStreamMsg ( &hdr, NULL, true ); - if ( status == ECA_NORMAL ) { - this->flush (); - } - return status; + this->comQueSend::enableFlowControlRequest (); + this->flowControlActive = true; +# if defined ( DEBUG ) || 1 + printf( "fc on\n" ); +# endif } /* - * tcpiiu::readyRequestMsg () + * tcpiiu::disableFlowControlMsg () */ -int tcpiiu::readyRequestMsg () +void tcpiiu::disableFlowControlMsg () { - caHdr hdr; - int status; - - hdr = cacnullmsg; - hdr.m_cmmd = htons (CA_PROTO_EVENTS_ON); - - status = this->pushStreamMsg (&hdr, NULL, true); - if ( status == ECA_NORMAL ) { - this->flush (); - } - return status; + this->comQueSend::disableFlowControlRequest (); + this->flowControlActive = false; +# if defined ( DEBUG ) || 1 + printf ( "fc off\n" ); +# endif } /* @@ -705,19 +658,11 @@ int tcpiiu::readyRequestMsg () */ void tcpiiu::hostNameSetMsg () { - caHdr hdr; - if ( ! CA_V41 ( CA_PROTOCOL_VERSION, this->minor_version_number ) ) { return; } - hdr = cacnullmsg; - hdr.m_cmmd = htons ( CA_PROTO_HOST_NAME ); - hdr.m_postsize = localHostNameAtLoadTime.stringLength () + 1u; - - this->pushStreamMsg ( &hdr, localHostNameAtLoadTime.pointer (), true ); - - return; + this->comQueSend::hostNameSetRequest ( localHostNameAtLoadTime.pointer () ); } /* @@ -725,498 +670,251 @@ void tcpiiu::hostNameSetMsg () */ void tcpiiu::userNameSetMsg () { - unsigned size; - caHdr hdr; - char *pName; + if ( ! CA_V41 ( CA_PROTOCOL_VERSION, this->minor_version_number ) ) { + return; + } - if ( ! CA_V41(CA_PROTOCOL_VERSION, this->minor_version_number) ) { + this->comQueSend::userNameSetRequest ( this->clientCtx ().userNamePointer () ); +} + +void tcpiiu::noopAction () +{ + return; +} + +void tcpiiu::echoRespAction () +{ + return; +} + +void tcpiiu::writeNotifyRespAction () +{ + int status = this->curMsg.m_cid; + if ( status == ECA_NORMAL ) { + this->clientCtx ().ioCompletionNotifyAndDestroy ( this->curMsg.m_available ); + } + else { + this->clientCtx ().ioExceptionNotifyAndDestroy ( this->curMsg.m_available, + status, "write notify request rejected" ); + } +} + +void tcpiiu::readNotifyRespAction () +{ + int v41; + int status; + + /* + * convert the data buffer from net + * format to host format + */ +# ifdef CONVERSION_REQUIRED + if ( this->curMsg.m_dataType < NELEMENTS ( cac_dbr_cvrt ) ) { + ( *cac_dbr_cvrt[ this->curMsg.m_dataType ] ) ( + this->pCurData, this->pCurData, FALSE, this->curMsg.m_count); + } + else { + this->curMsg.m_cid = htonl ( ECA_BADTYPE ); + } +# endif + + /* + * the channel id field is abused for + * read notify status starting + * with CA V4.1 + */ + v41 = CA_V41 ( CA_PROTOCOL_VERSION, this->minor_version_number ); + if (v41) { + status = this->curMsg.m_cid; + } + else{ + status = ECA_NORMAL; + } + + if ( status == ECA_NORMAL ) { + this->clientCtx ().ioCompletionNotifyAndDestroy ( this->curMsg.m_available, + this->curMsg.m_dataType, this->curMsg.m_count, this->pCurData ); + } + else { + this->clientCtx ().ioExceptionNotifyAndDestroy ( this->curMsg.m_available, + status, "read failed", this->curMsg.m_dataType, this->curMsg.m_count ); + } +} + +void tcpiiu::eventRespAction () +{ + int v41; + int status; + + /* + * m_postsize = 0 used to be a confirmation, but is + * now a noop because the above hash lookup will + * not find a matching IO block + */ + if ( ! this->curMsg.m_postsize ) { + this->clientCtx ().ioDestroy ( this->curMsg.m_available ); return; } /* - * allocate space in the outgoing buffer + * convert the data buffer from net + * format to host format */ - pName = this->pcas->ca_pUserName, - size = strlen (pName) + 1; - hdr = cacnullmsg; - hdr.m_cmmd = htons ( CA_PROTO_CLIENT_NAME ); - hdr.m_postsize = size; - - this->pushStreamMsg ( &hdr, pName, true ); - - return; -} - - -/* - * tcp_noop_action () - */ -LOCAL void tcp_noop_action (tcpiiu * /* piiu */) -{ - return; -} - -/* - * echo_resp_action () - */ -LOCAL void echo_resp_action (tcpiiu *piiu) -{ - return; -} - -/* - * write_notify_resp_action () - */ -LOCAL void write_notify_resp_action (tcpiiu *piiu) -{ - baseNMIU *monix; - - piiu->pcas->lock (); - monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); - if (monix) { - int status = ntohl (piiu->curMsg.m_cid); - if ( status == ECA_NORMAL ) { - monix->completionNotify (); +# ifdef CONVERSION_REQUIRED + if ( this->curMsg.m_dataType < NELEMENTS ( cac_dbr_cvrt ) ) { + ( *cac_dbr_cvrt [ this->curMsg.m_dataType ] )( + this->pCurData, this->pCurData, FALSE, + this->curMsg.m_count); } else { - monix->exceptionNotify ( status, "write notify request rejected" ); + this->curMsg.m_cid = htonl ( ECA_BADTYPE ); } - monix->destroy (); - } - piiu->pcas->unlock (); -} - -/* - * read_notify_resp_action () - */ -LOCAL void read_notify_resp_action ( tcpiiu *piiu ) -{ - baseNMIU *monix; - - piiu->pcas->lock (); - monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); - - if (monix) { - int v41; - int status; - - /* - * convert the data buffer from net - * format to host format - */ -# ifdef CONVERSION_REQUIRED - if (piiu->curMsg.m_dataTypecurMsg.m_dataType])( - piiu->pCurData, - piiu->pCurData, - FALSE, - piiu->curMsg.m_count); - } - else { - piiu->curMsg.m_cid = htonl(ECA_BADTYPE); - } -# endif - - /* - * the channel id field is abused for - * read notify status starting - * with CA V4.1 - */ - v41 = CA_V41 (CA_PROTOCOL_VERSION, piiu->minor_version_number); - if (v41) { - status = ntohl (piiu->curMsg.m_cid); - } - else{ - status = ECA_NORMAL; - } - - if ( status == ECA_NORMAL ) { - monix->completionNotify (piiu->curMsg.m_dataType, piiu->curMsg.m_count, piiu->pCurData); - } - else { - monix->exceptionNotify (status, "read failed", piiu->curMsg.m_dataType, piiu->curMsg.m_count); - } - monix->destroy (); - } - - piiu->pcas->unlock (); - - return; -} - -/* - * event_resp_action () - */ -LOCAL void event_resp_action (tcpiiu *piiu) -{ - baseNMIU *monix; +# endif /* - * run the user's event handler + * the channel id field is abused for + * read notify status starting + * with CA V4.1 */ - piiu->pcas->lock (); - monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); - if ( monix ) { - int v41; - int status; - - /* - * m_postsize = 0 used to be a confirmation, but is - * now a noop because the above hash lookup will - * not find a matching IO block - */ - if ( ! piiu->curMsg.m_postsize ) { - monix->destroy (); - piiu->pcas->unlock (); - return; - } - - /* - * convert the data buffer from net - * format to host format - */ -# ifdef CONVERSION_REQUIRED - if (piiu->curMsg.m_dataTypecurMsg.m_dataType])( - piiu->pCurData, - piiu->pCurData, - FALSE, - piiu->curMsg.m_count); - } - else { - piiu->curMsg.m_cid = htonl(ECA_BADTYPE); - } -# endif - - /* - * the channel id field is abused for - * read notify status starting - * with CA V4.1 - */ - v41 = CA_V41 (CA_PROTOCOL_VERSION, piiu->minor_version_number); - if (v41) { - status = ntohl (piiu->curMsg.m_cid); - } - else { - status = ECA_NORMAL; - } - if ( status == ECA_NORMAL ) { - monix->completionNotify ( piiu->curMsg.m_dataType, - piiu->curMsg.m_count, piiu->pCurData ); - } - else { - monix->exceptionNotify ( status, "subscription update failed", - piiu->curMsg.m_dataType, piiu->curMsg.m_count ); - } + v41 = CA_V41 ( CA_PROTOCOL_VERSION, this->minor_version_number ); + if (v41) { + status = this->curMsg.m_cid; } - - piiu->pcas->unlock (); - - return; -} - -/* - * read_resp_action () - */ -LOCAL void read_resp_action (tcpiiu *piiu) -{ - baseNMIU *pIOBlock; - - piiu->pcas->lock (); - - /* - * verify the event id - */ - pIOBlock = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); - if ( pIOBlock ) { - - /* - * convert the data buffer from net - * format to host format - */ - pIOBlock->completionNotify (piiu->curMsg.m_dataType, piiu->curMsg.m_count, piiu->pCurData); - pIOBlock->destroy (); + else { + status = ECA_NORMAL; + } + if ( status == ECA_NORMAL ) { + this->clientCtx ().ioCompletionNotify ( this->curMsg.m_available, + this->curMsg.m_dataType, this->curMsg.m_count, this->pCurData ); + } + else { + this->clientCtx ().ioExceptionNotify ( this->curMsg.m_available, + status, "subscription update failed", + this->curMsg.m_dataType, this->curMsg.m_count ); } - - piiu->pcas->unlock (); - - return; } -/* - * clear_channel_resp_action () - */ -LOCAL void clear_channel_resp_action (tcpiiu * /* piiu */) +void tcpiiu::readRespAction () { - /* presently a noop */ - return; + this->clientCtx ().ioCompletionNotifyAndDestroy ( this->curMsg.m_available, + this->curMsg.m_dataType, this->curMsg.m_count, this->pCurData ); } -/* - * exception_resp_action () - */ -LOCAL void exception_resp_action (tcpiiu *piiu) +void tcpiiu::clearChannelRespAction () +{ + this->clientCtx ().channelDestroy ( this->curMsg.m_available ); +} + +void tcpiiu::exceptionRespAction () { - baseNMIU *monix; char context[255]; - caHdr *req = (caHdr *) piiu->pCurData; + char hostName[64]; + caHdr *req = (caHdr *) this->pCurData; - if ( piiu->curMsg.m_postsize > sizeof (caHdr) ){ - sprintf (context, "detected by: %s for: %s", - piiu->host_name_str, (char *)(req+1)); + this->ipToA.hostName ( hostName, sizeof ( hostName ) ); + if ( this->curMsg.m_postsize > sizeof (caHdr) ) { + sprintf ( context, "detected by: %s for: %s", + hostName, (char *)(req+1) ); } else{ - sprintf (context, "detected by: %s", piiu->host_name_str); + sprintf ( context, "detected by: %s", hostName ); } - piiu->pcas->lock (); - switch ( ntohs (req->m_cmmd) ) { + switch ( ntohs ( req->m_cmmd ) ) { case CA_PROTO_READ_NOTIFY: - monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); - if (monix) { - monix->exceptionNotify ( ntohl (piiu->curMsg.m_available), context, - ntohs (req->m_dataType), ntohs (req->m_count) ); - monix->destroy (); - } - else { - piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), - context, __FILE__, __LINE__); - } + this->clientCtx ().ioExceptionNotifyAndDestroy ( ntohl (req->m_available), + ntohl (this->curMsg.m_available), context, + ntohs (req->m_dataType), ntohs (req->m_count) ); break; case CA_PROTO_READ: - monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); - if (monix) { - monix->exceptionNotify ( ntohl (piiu->curMsg.m_available), context, - ntohs (req->m_dataType), ntohs (req->m_count) ); - monix->destroy (); - } - else { - piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), - context, __FILE__, __LINE__); - } + this->clientCtx ().ioExceptionNotifyAndDestroy ( ntohl (req->m_available), + ntohl (this->curMsg.m_available), context, + ntohs (req->m_dataType), ntohs (req->m_count) ); break; case CA_PROTO_WRITE_NOTIFY: - monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); - if (monix) { - monix->exceptionNotify (ntohl (piiu->curMsg.m_available), context); - monix->destroy (); - } - else { - piiu->pcas->exceptionNotify (ntohl ( piiu->curMsg.m_available), - context, __FILE__, __LINE__); - } + this->clientCtx ().ioExceptionNotifyAndDestroy ( ntohl (req->m_available), + ntohl (this->curMsg.m_available), context, + ntohs (req->m_dataType), ntohs (req->m_count) ); break; case CA_PROTO_WRITE: - piiu->pcas->exceptionNotify (ntohl ( piiu->curMsg.m_available), + this->clientCtx ().exceptionNotify ( ntohl ( this->curMsg.m_available), context, ntohs (req->m_dataType), ntohs (req->m_count), __FILE__, __LINE__); break; case CA_PROTO_EVENT_ADD: - monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); - if (monix) { - monix->exceptionNotify (ntohl (piiu->curMsg.m_available), context); - monix->destroy (); - } - else { - piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), - context, __FILE__, __LINE__); - } + this->clientCtx ().ioExceptionNotify ( ntohl (req->m_available), + ntohl (this->curMsg.m_available), context, + ntohs (req->m_dataType), ntohs (req->m_count) ); break; case CA_PROTO_EVENT_CANCEL: - monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); - if (monix) { - monix->exceptionNotify (ntohl (piiu->curMsg.m_available), context); - monix->destroy (); - } - else { - piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), - context, __FILE__, __LINE__); - } + this->clientCtx ().ioExceptionNotifyAndDestroy ( ntohl (req->m_available), + ntohl (this->curMsg.m_available), context ); break; default: - piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), + this->clientCtx ().exceptionNotify (ntohl (this->curMsg.m_available), context, __FILE__, __LINE__); break; } - - piiu->pcas->unlock (); - - return; } -/* - * access_rights_resp_action () - */ -LOCAL void access_rights_resp_action (tcpiiu *piiu) +void tcpiiu::accessRightsRespAction () { - int ar; - nciu *chan; + static caar init; + caar arBitField = init; // shut up bounds checker + unsigned ar; - piiu->pcas->lock (); - chan = piiu->pcas->lookupChan (piiu->curMsg.m_cid); - if ( ! chan ) { - /* - * end up here if they delete the channel - * prior to connecting - */ - piiu->pcas->unlock (); - return; - } + ar = this->curMsg.m_available; + arBitField.read_access = ( ar & CA_PROTO_ACCESS_RIGHT_READ ) ? 1 : 0; + arBitField.write_access = ( ar & CA_PROTO_ACCESS_RIGHT_WRITE ) ? 1 : 0; - ar = ntohl (piiu->curMsg.m_available); - chan->ar.read_access = (ar&CA_PROTO_ACCESS_RIGHT_READ)?1:0; - chan->ar.write_access = (ar&CA_PROTO_ACCESS_RIGHT_WRITE)?1:0; - - chan->accessRightsNotify (chan->ar); - - piiu->pcas->unlock (); - return; + this->clientCtx ().accessRightsNotify ( this->curMsg.m_cid, arBitField ); } -/* - * claim_ciu_resp_action () - */ -LOCAL void claim_ciu_resp_action (tcpiiu *piiu) +void tcpiiu::claimCIURespAction () { - unsigned sid; - nciu *chan; - - piiu->pcas->lock (); - - chan = piiu->pcas->lookupChan (piiu->curMsg.m_cid); - if ( ! chan ) { - piiu->pcas->unlock (); - return; - } - - if ( CA_V44 (CA_PROTOCOL_VERSION, piiu->minor_version_number) ) { - sid = piiu->curMsg.m_available; - } - else { - sid = chan->sid; - } - - chan->connect (*piiu, piiu->curMsg.m_dataType, piiu->curMsg.m_count, sid); - - piiu->pcas->unlock (); - - return; + this->clientCtx ().connectChannel ( this->curMsg.m_cid, *this, + this->curMsg.m_dataType, this->curMsg.m_count, this->curMsg.m_available ); } -/* - * verifyAndDisconnectChan () - */ -LOCAL void verifyAndDisconnectChan (tcpiiu *piiu) +void tcpiiu::verifyAndDisconnectChan () { - nciu *chan; - - piiu->pcas->lock (); - chan = piiu->pcas->lookupChan (piiu->curMsg.m_cid); - if (!chan) { - /* - * end up here if they delete the channel - * prior to this response - */ - piiu->pcas->unlock (); - return; - } - - /* - * need to move the channel back to the cast niiu - * (so we will be able to reconnect) - * - * this marks the niiu for disconnect if the channel - * count goes to zero - */ - chan->disconnect (); - piiu->pcas->unlock (); - return; + this->clientCtx ().disconnectChannel ( this->curMsg.m_cid ); } -/* - * bad_tcp_resp_action () - */ -LOCAL void bad_tcp_resp_action (tcpiiu *piiu) +void tcpiiu::badTCPRespAction () { - ca_printf ("CAC: Bad response code in TCP message from %s was %u\n", - piiu->host_name_str, piiu->curMsg.m_cmmd); + char hostName[64]; + this->ipToA.hostName ( hostName, sizeof ( hostName ) ); + ca_printf ( "CAC: Bad response code in TCP message from %s was %u\n", + hostName, this->curMsg.m_cmmd); } -/* - * TCP protocol jump table - */ -LOCAL const pProtoStubTCP tcpJumpTableCAC[] = -{ - tcp_noop_action, - event_resp_action, - bad_tcp_resp_action, - read_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - exception_resp_action, - clear_channel_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - read_notify_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - claim_ciu_resp_action, - write_notify_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - access_rights_resp_action, - echo_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - verifyAndDisconnectChan, - verifyAndDisconnectChan -}; - - /* * post_tcp_msg () */ -int tcpiiu::post_msg (char *pInBuf, unsigned long blockSize) +void tcpiiu::postMsg () { - unsigned long size; + while ( 1 ) { - while ( blockSize ) { - - /* - * fetch a complete message header - */ - if ( this->curMsgBytes < sizeof (this->curMsg) ) { - char *pHdr; - - size = sizeof (this->curMsg) - this->curMsgBytes; - size = min (size, blockSize); - - pHdr = (char *) &this->curMsg; - memcpy ( pHdr + this->curMsgBytes, pInBuf, size); - - this->curMsgBytes += size; - if ( this->curMsgBytes < sizeof (this->curMsg) ) { -#if 0 - printf ("waiting for %d msg hdr bytes\n", - sizeof(this->curMsg) - this->curMsgBytes); -#endif - return ECA_NORMAL; + // + // fetch a complete message header + // + if ( ! this->msgHeaderAvailable ) { + this->msgHeaderAvailable = this->copyOutBytes ( &this->curMsg, sizeof ( this->curMsg ) ); + if ( ! this->msgHeaderAvailable ) { + return; } + + semBinaryGive ( this->recvThreadRingBufferSpaceAvailableSignal ); - pInBuf += size; - blockSize -= size; - - /* - * fix endian of bytes - */ - this->curMsg.m_postsize = ntohs (this->curMsg.m_postsize); - this->curMsg.m_cmmd = ntohs (this->curMsg.m_cmmd); - this->curMsg.m_dataType = ntohs (this->curMsg.m_dataType); - this->curMsg.m_count = ntohs (this->curMsg.m_count); + // + // 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 ); #if 0 ca_printf ( "%s Cmd=%3d Type=%3d Count=%4d Size=%4d", @@ -1232,19 +930,20 @@ int tcpiiu::post_msg (char *pInBuf, unsigned long blockSize) #endif } - /* - * dont allow huge msg body until - * the server supports it - */ + // + // dont allow huge msg body until + // the client library supports it + // if ( this->curMsg.m_postsize > (unsigned) MAX_TCP ) { - this->curMsgBytes = 0; - this->curDataBytes = 0; - return ECA_TOLARGE; + this->msgHeaderAvailable = false; + ca_printf ("CAC: message body was too large (disconnecting)\n"); + this->shutdown (); + return; } - /* - * make sure we have a large enough message body cache - */ + // + // make sure we have a large enough message body cache + // if ( this->curMsg.m_postsize > this->curDataMax ) { void *pData; size_t cacheSize; @@ -1256,10 +955,12 @@ int tcpiiu::post_msg (char *pInBuf, unsigned long blockSize) * not page fault if they read MAX_STRING_SIZE * bytes (instead of the actual string size). */ - cacheSize = max ( this->curMsg.m_postsize, MAX_STRING_SIZE ); + cacheSize = max ( this->curMsg.m_postsize * 2u, MAX_STRING_SIZE ); pData = (void *) calloc (1u, cacheSize); if ( ! pData ) { - return ECA_ALLOCMEM; + ca_printf ("CAC: not enough memory for message body cache (disconnecting)\n"); + this->shutdown (); + return; } if (this->pCurData) { free (this->pCurData); @@ -1268,229 +969,122 @@ int tcpiiu::post_msg (char *pInBuf, unsigned long blockSize) this->curDataMax = this->curMsg.m_postsize; } - /* - * Fetch a complete message body - * (allows for arrays larger than than the - * ring buffer size) - */ - if (this->curMsg.m_postsize > this->curDataBytes ) { - char *pBdy; + bool msgBodyAvailable = this->copyOutBytes ( this->pCurData, this->curMsg.m_postsize ); + if ( ! msgBodyAvailable ) { + return; + } - size = this->curMsg.m_postsize - this->curDataBytes; - size = min (size, blockSize); - pBdy = (char *) this->pCurData; - memcpy ( pBdy + this->curDataBytes, pInBuf, size); - this->curDataBytes += size; - if (this->curDataBytes < this->curMsg.m_postsize) { -#if 0 - printf ("waiting for %d msg bdy bytes\n", - this->curMsg.m_postsize - this->curDataBytes); -#endif - return ECA_NORMAL; - } - pInBuf += size; - blockSize -= size; - } + semBinaryGive ( this->recvThreadRingBufferSpaceAvailableSignal ); /* * execute the response message */ - pProtoStubTCP pStub; - if ( this->curMsg.m_cmmd >= NELEMENTS (tcpJumpTableCAC) ) { - pStub = bad_tcp_resp_action; + pProtoStubTCP pStub; + if ( this->curMsg.m_cmmd >= NELEMENTS ( tcpJumpTableCAC ) ) { + pStub = badTCPRespAction; } else { pStub = tcpJumpTableCAC [this->curMsg.m_cmmd]; } - (*pStub) (this); + ( this->*pStub ) (); - this->curMsgBytes = 0; - this->curDataBytes = 0; - } - return ECA_NORMAL; -} - -/* - * tcpiiu::pushStreamMsg () - */ -int tcpiiu::pushStreamMsg ( const caHdr *pmsg, - const void *pext, bool BlockingOk ) -{ - caHdr msg; - ca_uint16_t actualextsize; - ca_uint16_t extsize; - unsigned msgsize; - int status; - - if ( pext == NULL ) { - extsize = actualextsize = 0; - } - else { - if ( pmsg->m_postsize > 0xffff-7 ) { - return ECA_TOLARGE; - } - actualextsize = pmsg->m_postsize; - extsize = CA_MESSAGE_ALIGN (actualextsize); - } - - msg = *pmsg; - msg.m_postsize = htons ( extsize ); - msgsize = extsize + sizeof ( msg ); - - if ( cacRingBufferWriteLockNoBlock ( &this->send, msgsize ) ) { - status = this->pushStreamMsgPrivate ( &msg, pext, extsize, actualextsize ); - cacRingBufferWriteUnlock ( &this->send ); - return status; - } - else if ( BlockingOk ) { - this->armSendWatchdog (); - this->pcas->enableCallbackPreemption (); - cacRingBufferWriteLock ( &this->send ); - status = this->pushStreamMsgPrivate ( &msg, pext, extsize, actualextsize ); - cacRingBufferWriteUnlock ( &this->send ); - this->pcas->disableCallbackPreemption (); - return status; - } - else { - return ECA_OPWILLBLOCK; + this->msgHeaderAvailable = false; } } -int tcpiiu::pushStreamMsgPrivate ( const caHdr *pmsg, const void *pext, - unsigned extsize, unsigned actualextsize ) +inline int tcpiiu::requestStubStatus () { - unsigned bytesSent; - - /* - * push the header onto the ring - */ - bytesSent = cacRingBufferWrite ( &this->send, pmsg, sizeof ( *pmsg ) ); - if ( bytesSent != sizeof ( *pmsg ) ) { + if ( this->state == iiu_connected ) { + return ECA_NORMAL; + } + else { return ECA_DISCONNCHID; } - - /* - * push message body onto the ring - * - * (optionally encode in network format as we send) - */ - if ( extsize > 0u ) { - bytesSent = cacRingBufferWrite ( &this->send, pext, actualextsize ); - if ( bytesSent != actualextsize ) { - return ECA_DISCONNCHID; - } - /* - * force pad bytes at the end of the message to nill - * if present (this avoids messages from purify) - */ - { - unsigned long n; - - n = extsize-actualextsize; - if ( n ) { - assert ( n <= sizeof (nullBuff) ); - bytesSent = cacRingBufferWrite ( &this->send, nullBuff, n ); - if ( bytesSent != n ) { - return ECA_DISCONNCHID; - } - } - } - } - return ECA_NORMAL; } -int tcpiiu::pushDatagramMsg (const caHdr * /* pMsg */, - const void * /* pExt */, ca_uint16_t /* extsize */) +int tcpiiu::writeRequest ( unsigned serverId, unsigned type, unsigned nElem, const void *pValue ) { - return ECA_INTERNAL; + return this->comQueSend::writeRequest ( serverId, type, nElem, pValue ); } -/* - * add to the beginning of the list until we - * have sent the claim message (after which we - * move it to the end of the list) - */ -void tcpiiu::addToChanList ( nciu &chan ) +int tcpiiu::writeNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, const void *pValue ) { - this->pcas->lock (); - chan.claimPending = TRUE; - this->chidList.push ( chan ); - chan.piiu = this; - this->pcas->unlock (); -} - -void tcpiiu::removeFromChanList ( nciu &chan ) -{ - this->pcas->lock (); - this->chidList.remove ( chan ); - chan.piiu = NULL; - this->pcas->unlock (); - - if ( this->chidList.count () == 0 ) { - this->shutdown (); - } -} - -void tcpiiu::disconnect ( nciu &chan ) -{ - this->pcas->lock (); - this->removeFromChanList (chan); - this->pcas->installDisconnectedChannel ( chan ); - this->pcas->unlock (); -} - -/* - * FLOW CONTROL - * - * Keep track of how many times messages have - * come with out a break in between and - * suppress monitors if we are behind - * (an update is sent when we catch up) - */ -void tcpiiu::flowControlOn () -{ - int status; - - this->pcas->lock (); - - /* - * I prefer to avoid going into flow control - * as this impacts the performance of batched fetches - */ - if ( this->contiguous_msg_count >= MAX_CONTIGUOUS_MSG_COUNT ) { - if ( ! this->client_busy ) { - status = this->busyRequestMsg (); - if ( status == ECA_NORMAL ) { - this->client_busy = TRUE; -# if defined(DEBUG) - printf("fc on\n"); -# endif - } - } + if ( this->ca_v41_ok () ) { + return this->comQueSend::writeNotifyRequest ( ioId, serverId, type, nElem, pValue ); } else { - this->contiguous_msg_count++; + return ECA_NOSUPPORT; } - - this->pcas->unlock (); } -void tcpiiu::flowControlOff () +int tcpiiu::readCopyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ) { - int status; - - this->pcas->lock (); - - this->contiguous_msg_count = 0; - if ( this->client_busy ) { - status = this->readyRequestMsg (); - if ( status == ECA_NORMAL ) { - this->client_busy = FALSE; -# if defined (DEBUG) - printf("fc off\n"); -# endif - } - } - - this->pcas->unlock (); + return this->comQueSend::readCopyRequest ( ioId, serverId, type, nElem ); } + +int tcpiiu::readNotifyRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ) +{ + return this->comQueSend::readNotifyRequest ( ioId, serverId, type, nElem ); +} + +int tcpiiu::createChannelRequest ( unsigned clientId, const char *pName, unsigned nameLength ) +{ + return this->comQueSend::createChannelRequest ( clientId, pName, nameLength ); +} + +int tcpiiu::clearChannelRequest ( unsigned clientId, unsigned serverId ) +{ + return this->comQueSend::clearChannelRequest ( clientId, serverId ); +} + +int tcpiiu::subscriptionRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem, unsigned mask ) +{ + return this->comQueSend::subscriptionRequest ( ioId, serverId, type, nElem, mask ); +} + +int tcpiiu::subscriptionCancelRequest ( unsigned ioId, unsigned serverId, unsigned type, unsigned nElem ) +{ + return this->comQueSend::subscriptionCancelRequest ( ioId, serverId, type, nElem ); +} + +void tcpiiu::processIncomingAndDestroySelfIfDisconnected () +{ + if ( this->state == iiu_disconnected ) { + this->suicide (); + } + else if ( this->recvPending ) { + this->recvPending = false; + this->postMsg (); + } +} + +void tcpiiu::lastChannelDetachNotify () +{ + this->shutdown (); +} + +void tcpiiu::installChannelPendingClaim ( nciu &chan ) +{ + chan.attachChanToIIU ( *this->pClaimsPendingIIU ); + // wake up send thread which sends claim message + semBinaryGive ( this->sendThreadFlushSignal ); +} + +// the recv thread is not permitted to flush as this +// can result in a push / pull deadlock on the TCP pipe. +// Instead the recv thread scheduals the flush with the +// send thread which runs at a higher priority than the +// send thread. +bool tcpiiu::flushToWirePermit () +{ + if ( this->clientCtx ().currentThreadIsRecvProcessThread () ) { + this->flushPending = true; + semBinaryGive ( this->sendThreadFlushSignal ); + return false; + } + else { + return true; + } +} + + diff --git a/src/ca/tcpiiu_IL.h b/src/ca/tcpiiu_IL.h index 21dcdb36c..92f976eed 100644 --- a/src/ca/tcpiiu_IL.h +++ b/src/ca/tcpiiu_IL.h @@ -18,7 +18,7 @@ inline osiSockAddr tcpiiu::address () const { - return this->dest; + return this->ipToA.address (); } inline void * tcpiiu::operator new (size_t size) @@ -33,41 +33,55 @@ inline void tcpiiu::operator delete (void *pCadaver, size_t size) inline bool tcpiiu::fullyConstructed () const { - return this->fc; -} - -inline void tcpiiu::flush () -{ - if ( cacRingBufferWriteFlush ( &this->send ) ) { - this->armSendWatchdog (); - } + return this->fullyConstructedFlag; } inline void tcpiiu::hostName ( char *pBuf, unsigned bufLength ) const { - if ( bufLength ) { - strncpy ( pBuf, this->host_name_str, bufLength ); - pBuf[bufLength - 1u] = '\0'; - } + this->ipToA.hostName ( pBuf, bufLength ); } // deprecated - please dont use inline const char * tcpiiu::pHostName () const { - return this->host_name_str; -} - -inline bool tcpiiu::ca_v42_ok () const -{ - return CA_V42 (CA_PROTOCOL_VERSION, this->minor_version_number); -} - -inline bool tcpiiu::ca_v41_ok () const -{ - return CA_V41 (CA_PROTOCOL_VERSION, this->minor_version_number); + static char nameBuf [128]; + this->ipToA.hostName ( nameBuf, sizeof ( nameBuf ) ); + return nameBuf; // ouch !! } inline SOCKET tcpiiu::getSock () const { return this->sock; } + +inline void tcpiiu::flush () +{ + this->flushPending = true; + semBinaryGive ( this->sendThreadFlushSignal ); +} + +inline bool tcpiiu::ca_v44_ok () const +{ + return CA_V44 ( CA_PROTOCOL_VERSION, this->minor_version_number ); +} + +inline bool tcpiiu::ca_v42_ok () const +{ + return CA_V42 ( CA_PROTOCOL_VERSION, this->minor_version_number ); +} + +inline bool tcpiiu::ca_v41_ok () const +{ + return CA_V41 ( CA_PROTOCOL_VERSION, this->minor_version_number ); +} + +inline bool tcpiiu::alive () const +{ + if ( this->state == iiu_connecting || + this->state == iiu_connected ) { + return true; + } + else { + return false; + } +} diff --git a/src/ca/udpiiu.cpp b/src/ca/udpiiu.cpp index bd845b97b..875d357ff 100644 --- a/src/ca/udpiiu.cpp +++ b/src/ca/udpiiu.cpp @@ -15,9 +15,43 @@ #include "iocinf.h" #include "addrList.h" #include "inetAddrID_IL.h" +#include "netiiu_IL.h" typedef void (*pProtoStubUDP) (udpiiu *piiu, caHdr *pMsg, const struct sockaddr_in *pnet_addr); +// UDP protocol dispatch table +const udpiiu::pProtoStubUDP udpiiu::udpJumpTableCAC[] = +{ + udpiiu::noopAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::searchRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::exceptionRespAction, + udpiiu::badUDPRespAction, + udpiiu::beaconAction, + udpiiu::notHereRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::repeaterAckAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction, + udpiiu::badUDPRespAction +}; + // // udpiiu::recvMsg () // @@ -62,12 +96,12 @@ void udpiiu::recvMsg () SOCKERRSTR (errnoCpy) ); } else if (status > 0) { - status = this->post_msg ( &src.ia, + status = this->postMsg ( src, this->recvBuf, (unsigned long) status ); if ( status != ECA_NORMAL ) { char buf[64]; - ipAddrToA (&src.ia, buf, sizeof(buf)); + sockAddrToA ( &src.sa, buf, sizeof (buf) ); ca_printf ( "%s: bad UDP msg from %s because \"%s\"\n", __FILE__, @@ -210,27 +244,27 @@ void udpiiu::repeaterRegistrationMessage ( unsigned attemptNumber ) * * 072392 - problem solved by using SO_REUSEADDR */ -int repeater_installed (udpiiu *piiu) +bool udpiiu::repeaterInstalled () { - int installed = FALSE; + bool installed = false; int status; SOCKET sock; struct sockaddr_in bd; int flag; - sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock == INVALID_SOCKET) { + sock = socket ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if ( sock == INVALID_SOCKET ) { return installed; } - memset ( (char *) &bd, 0, sizeof (bd) ); + memset ( (char *) &bd, 0, sizeof ( bd ) ); bd.sin_family = AF_INET; - bd.sin_addr.s_addr = htonl (INADDR_ANY); - bd.sin_port = htons (piiu->repeaterPort); - status = bind (sock, (struct sockaddr *) &bd, sizeof(bd) ); - if (status<0) { - if (SOCKERRNO == SOCK_EADDRINUSE) { - installed = TRUE; + bd.sin_addr.s_addr = htonl ( INADDR_ANY ); + bd.sin_port = htons ( this->repeaterPort ); + status = bind ( sock, (struct sockaddr *) &bd, sizeof ( bd ) ); + if ( status < 0 ) { + if ( SOCKERRNO == SOCK_EADDRINUSE ) { + installed = true; } } @@ -240,12 +274,12 @@ int repeater_installed (udpiiu *piiu) */ flag = TRUE; status = setsockopt ( sock, SOL_SOCKET, SO_REUSEADDR, - (char *)&flag, sizeof (flag) ); + (char *)&flag, sizeof ( flag ) ); if (status<0) { ca_printf ( "CAC: set socket option reuseaddr set failed\n"); } - socket_close (sock); + socket_close ( sock ); return installed; } @@ -253,9 +287,8 @@ int repeater_installed (udpiiu *piiu) // // udpiiu::udpiiu () // -udpiiu::udpiiu ( cac *pcac ) : - netiiu ( pcac ), - shutdownCmd ( false ) +udpiiu::udpiiu ( cac &cac ) : + netiiu ( cac ), shutdownCmd ( false ) { static const unsigned short PORT_ANY = 0u; osiSockAddr addr; @@ -265,8 +298,11 @@ udpiiu::udpiiu ( cac *pcac ) : this->repeaterPort = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT, CA_REPEATER_PORT); - this->sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (this->sock == INVALID_SOCKET) { + this->serverPort = + envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, CA_SERVER_PORT ); + + this->sock = socket ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if ( this->sock == INVALID_SOCKET ) { ca_printf ("CAC: unable to create datagram socket because = \"%s\"\n", SOCKERRSTR (SOCKERRNO)); throwWithLocation ( noSocket () ); @@ -306,7 +342,7 @@ udpiiu::udpiiu ( cac *pcac ) : addr.ia.sin_addr.s_addr = htonl (INADDR_ANY); addr.ia.sin_port = htons (PORT_ANY); status = bind (this->sock, &addr.sa, sizeof (addr) ); - if (status<0) { + if ( status < 0 ) { socket_close (this->sock); ca_printf ("CAC: unable to bind to an unconstrained address because = \"%s\"\n", SOCKERRSTR (SOCKERRNO)); @@ -315,16 +351,9 @@ udpiiu::udpiiu ( cac *pcac ) : this->nBytesInXmitBuf = 0u; - this->xmitBufLock = semMutexCreate (); - if (!this->xmitBufLock) { - socket_close (this->sock); - throwWithLocation ( noMemory () ); - } - - this->recvThreadExitSignal = semBinaryCreate (semEmpty); + this->recvThreadExitSignal = semBinaryCreate ( semEmpty ); if ( ! this->recvThreadExitSignal ) { - semMutexDestroy (this->xmitBufLock); - socket_close (this->sock); + socket_close ( this->sock ); throwWithLocation ( noMemory () ); } @@ -333,9 +362,9 @@ udpiiu::udpiiu ( cac *pcac ) : * broadcast address list */ ellInit ( &this->dest ); - configureChannelAccessAddressList (&this->dest, this->sock, pcac->ca_server_port); + configureChannelAccessAddressList ( &this->dest, this->sock, this->serverPort ); if ( ellCount ( &this->dest ) == 0 ) { - genLocalExcep ( this->pcas, ECA_NOSEARCHADDR, NULL ); + genLocalExcep ( this->clientCtx (), ECA_NOSEARCHADDR, NULL ); } { @@ -349,18 +378,17 @@ udpiiu::udpiiu ( cac *pcac ) : priorityOfRecv = priorityOfSelf; } - tid = threadCreate ("CAC-UDP", priorityOfRecv, - threadGetStackSize (threadStackMedium), cacRecvThreadUDP, this); + tid = threadCreate ( "CAC-UDP", priorityOfRecv, + threadGetStackSize (threadStackMedium), cacRecvThreadUDP, this ); if (tid==0) { ca_printf ("CA: unable to create UDP receive thread\n"); semBinaryDestroy (this->recvThreadExitSignal); - semMutexDestroy (this->xmitBufLock); socket_close (this->sock); throwWithLocation ( noMemory () ); } } - if ( ! repeater_installed (this) ) { + if ( ! this->repeaterInstalled () ) { osiSpawnDetachedProcessReturn osptr; /* @@ -369,7 +397,7 @@ udpiiu::udpiiu ( cac *pcac ) : * the 2nd repeater exits when unable to attach to the * repeater's port) */ - osptr = osiSpawnDetachedProcess ("CA Repeater", "caRepeater"); + osptr = osiSpawnDetachedProcess ( "CA Repeater", "caRepeater" ); if ( osptr == osiSpawnDetachedProcessNoSupport ) { threadId tid; @@ -390,28 +418,22 @@ udpiiu::udpiiu ( cac *pcac ) : */ udpiiu::~udpiiu () { - nciu *pChan, *pNext; // closes the udp socket this->shutdown (); - this->pcas->lock (); - tsDLIter iter (this->chidList); - pChan = iter (); - while (pChan) { - pNext = iter (); - pChan->destroy (); - pChan = pNext; - } - this->pcas->unlock (); + this->detachAllChan (); // wait for recv threads to exit - semBinaryMustTake (this->recvThreadExitSignal); + semBinaryMustTake ( this->recvThreadExitSignal ); - semMutexDestroy (this->xmitBufLock); - semBinaryDestroy (this->recvThreadExitSignal); + semBinaryDestroy ( this->recvThreadExitSignal ); - ellFree (&this->dest); + ellFree ( &this->dest ); + + if ( this->sock != INVALID_SOCKET ) { + socket_close ( this->sock ); + } } /* @@ -419,62 +441,72 @@ udpiiu::~udpiiu () */ void udpiiu::shutdown () { - this->pcas->lock (); - if ( ! this->shutdownCmd ) { - int status; + bool laborRequired; - // this knocks the UDP input thread out of recv () + this->lock (); + if ( ! this->shutdownCmd ) { this->shutdownCmd = true; - status = socket_close ( this->sock ); - if ( status ) { - errlogPrintf ( "CAC UDP socket close error was \"%s\"\n", - SOCKERRSTR (SOCKERRNO) ); + laborRequired = true; + } + else { + laborRequired = false; + } + this->unlock (); + + if ( laborRequired ) { + int status; + osiSockAddr addr; + int size = sizeof ( addr.sa ); + + status = getsockname ( this->sock, &addr.sa, &size ); + if ( status < 0 ) { + // this knocks the UDP input thread out of recv () + // on all os except linux + socket_close ( this->sock ); + this->sock = INVALID_SOCKET; + } + else { + caHdr msg; + msg.m_cmmd = htons ( CA_PROTO_NOOP ); + msg.m_available = htonl ( 0u ); + msg.m_dataType = htons ( 0u ); + msg.m_count = htons ( 0u ); + msg.m_cid = htonl ( 0u ); + msg.m_postsize = htons ( 0u ); + + // send a wakeup msg so the UDP recv thread will exit + status = sendto ( this->sock, reinterpret_cast < const char * > ( &msg ), sizeof (msg), 0, + &addr.sa, sizeof ( addr.sa ) ); + if ( status < 0 ) { + // this knocks the UDP input thread out of recv () + // on all os except linux + socket_close ( this->sock ); + this->sock = INVALID_SOCKET; + } } } - this->pcas->unlock (); } -/* - * bad_udp_resp_action () - */ -LOCAL void bad_udp_resp_action (udpiiu * /* piiu */, - caHdr *pMsg, const struct sockaddr_in *pnet_addr) +void udpiiu::badUDPRespAction ( const caHdr &msg, const osiSockAddr &netAddr ) { char buf[256]; - ipAddrToA ( pnet_addr, buf, sizeof (buf) ); + sockAddrToA ( &netAddr.sa, buf, sizeof ( buf ) ); ca_printf ( "CAC: Bad response code in UDP message from %s was %u\n", - buf, pMsg->m_cmmd); + buf, msg.m_cmmd); } -/* - * udp_noop_action () - */ -LOCAL void udp_noop_action (udpiiu * /* piiu */, caHdr * /* pMsg */, - const struct sockaddr_in * /* pnet_addr */) +void udpiiu::noopAction ( const caHdr &, const osiSockAddr & ) { return; } -/* - * search_resp_action () - */ -LOCAL void search_resp_action (udpiiu *piiu, caHdr *pMsg, const struct sockaddr_in *pnet_addr) +void udpiiu::searchRespAction ( const caHdr &msg, const osiSockAddr &addr ) { - struct sockaddr_in ina; - nciu *chan; - tcpiiu *allocpiiu; - unsigned short *pMinorVersion; - unsigned minorVersion; + osiSockAddr serverAddr; + unsigned minorVersion; + ca_uint16_t *pMinorVersion; - /* - * ignore broadcast replies for deleted channels - * - * lock required around use of the sprintf buffer - */ - piiu->pcas->lock (); - chan = piiu->pcas->lookupChan (pMsg->m_available); - if ( ! chan ) { - piiu->pcas->unlock (); + if ( addr.sa.sa_family != AF_INET ) { return; } @@ -483,9 +515,9 @@ LOCAL void search_resp_action (udpiiu *piiu, caHdr *pMsg, const struct sockaddr_ * is appended to the end of each search reply. * This value is ignored by earlier clients. */ - if ( pMsg->m_postsize >= sizeof (*pMinorVersion) ){ - pMinorVersion = (unsigned short *) (pMsg+1); - minorVersion = ntohs (*pMinorVersion); + if ( msg.m_postsize >= sizeof (*pMinorVersion) ){ + pMinorVersion = (ca_uint16_t *) ( &msg + 1 ); + minorVersion = ntohs ( *pMinorVersion ); } else { minorVersion = CA_UKN_MINOR_VERSION; @@ -495,109 +527,49 @@ LOCAL void search_resp_action (udpiiu *piiu, caHdr *pMsg, const struct sockaddr_ * the type field is abused to carry the port number * so that we can have multiple servers on one host */ - ina.sin_family = AF_INET; + serverAddr.ia.sin_family = AF_INET; if ( CA_V48 (CA_PROTOCOL_VERSION,minorVersion) ) { - if ( pMsg->m_cid != INADDR_BROADCAST ) { + if ( msg.m_cid != INADDR_BROADCAST ) { /* * Leave address in network byte order (m_cid has not been * converted to the local byte order) */ - ina.sin_addr.s_addr = pMsg->m_cid; + serverAddr.ia.sin_addr.s_addr = msg.m_cid; } else { - ina.sin_addr = pnet_addr->sin_addr; + serverAddr.ia.sin_addr = addr.ia.sin_addr; } - ina.sin_port = htons (pMsg->m_dataType); + serverAddr.ia.sin_port = htons (msg.m_dataType); } else if ( CA_V45 (CA_PROTOCOL_VERSION,minorVersion) ) { - ina.sin_port = htons (pMsg->m_dataType); - ina.sin_addr = pnet_addr->sin_addr; + serverAddr.ia.sin_port = htons ( msg.m_dataType ); + serverAddr.ia.sin_addr = addr.ia.sin_addr; } else { - ina.sin_port = htons (piiu->pcas->ca_server_port); - ina.sin_addr = pnet_addr->sin_addr; + serverAddr.ia.sin_port = htons ( this->serverPort ); + serverAddr.ia.sin_addr = addr.ia.sin_addr; } - /* - * Ignore duplicate search replies - */ - if ( chan->piiu->compareIfTCP (*chan, *pnet_addr) ) { - piiu->pcas->unlock (); - return; - } - - allocpiiu = constructTCPIIU (piiu->pcas, &ina, minorVersion); - if ( ! allocpiiu ) { - piiu->pcas->unlock (); - return; - } - - /* - * If this is the first channel to be - * added to this niiu then communicate - * the client's name to the server. - * (CA V4.1 or higher) - */ - if ( ellCount ( &allocpiiu->chidList ) == 0 ) { - allocpiiu->userNameSetMsg (); - allocpiiu->hostNameSetMsg (); - } - - piiu->pcas->notifySearchResponse ( chan->retrySeqNo ); - - /* - * Assume that we have access once connected briefly - * until the server is able to tell us the correct - * state for backwards compatibility. - * - * Their access rights call back does not get - * called for the first time until the information - * arrives however. - */ - chan->ar.read_access = TRUE; - chan->ar.write_access = TRUE; - - /* - * remove it from the broadcast niiu - */ - chan->piiu->removeFromChanList ( *chan ); - - /* - * chan->piiu must be correctly set prior to issuing the - * claim request - * - * add to the beginning of the list until we - * have sent the claim message (after which we - * move it to the end of the list) - * - * claim pending flag is set here - */ - allocpiiu->addToChanList ( *chan ); - if ( CA_V42 ( CA_PROTOCOL_VERSION, minorVersion ) ) { - chan->searchReplySetUp ( pMsg->m_cid, USHRT_MAX, 0 ); + this->clientCtx ().lookupChannelAndTransferToTCP + ( msg.m_available, msg.m_cid, USHRT_MAX, 0, + minorVersion, serverAddr ); } else { - chan->searchReplySetUp ( pMsg->m_cid, pMsg->m_dataType, pMsg->m_count ); + this->clientCtx ().lookupChannelAndTransferToTCP + ( msg.m_available, msg.m_cid, msg.m_dataType, + minorVersion, msg.m_count, serverAddr ); } - - chan->claimMsg ( allocpiiu ); - cacRingBufferWriteFlush ( &allocpiiu->send ); - - piiu->pcas->unlock (); } - -/* - * beacon_action () - */ -LOCAL void beacon_action ( udpiiu * piiu, - caHdr *pMsg, const struct sockaddr_in *pnet_addr) +void udpiiu::beaconAction ( const caHdr &msg, const osiSockAddr &net_addr ) { struct sockaddr_in ina; - piiu->pcas->lock (); - + if ( net_addr.sa.sa_family != AF_INET ) { + return; + } + /* * this allows a fan-out server to potentially * insert the true address of the CA server @@ -613,143 +585,96 @@ LOCAL void beacon_action ( udpiiu * piiu, * then it is the overriding IP address of the server. */ ina.sin_family = AF_INET; - if ( pMsg->m_available != htonl (INADDR_ANY) ) { - ina.sin_addr.s_addr = pMsg->m_available; + if ( msg.m_available != htonl (INADDR_ANY) ) { + ina.sin_addr.s_addr = msg.m_available; } else { - ina.sin_addr = pnet_addr->sin_addr; + ina.sin_addr = net_addr.ia.sin_addr; } - if ( pMsg->m_count != 0 ) { - ina.sin_port = htons ( pMsg->m_count ); + if ( msg.m_count != 0 ) { + ina.sin_port = htons ( msg.m_count ); } else { /* * old servers dont supply this and the * default port must be assumed */ - ina.sin_port = htons (piiu->pcas->ca_server_port); + ina.sin_port = htons ( this->serverPort ); } - piiu->pcas->beaconNotify (ina); - piiu->pcas->unlock (); + this->clientCtx ().beaconNotify ( ina ); return; } -/* - * repeater_ack_action () - */ -LOCAL void repeater_ack_action (udpiiu * piiu, - caHdr * /* pMsg */, const struct sockaddr_in * /* pnet_addr */) +void udpiiu::repeaterAckAction ( const caHdr &, const osiSockAddr &) { - piiu->pcas->repeaterSubscribeConfirmNotify (); + this->clientCtx ().repeaterSubscribeConfirmNotify (); } -/* - * not_here_resp_action () - */ -LOCAL void not_here_resp_action (udpiiu * /* piiu */, caHdr * /* pMsg */, const struct sockaddr_in * /* pnet_addr */) +void udpiiu::notHereRespAction ( const caHdr &, const osiSockAddr &) { return; } -/* - * udp_exception_resp_action () - */ -LOCAL void udp_exception_resp_action ( udpiiu *piiu, - caHdr *pMsg, const struct sockaddr_in *pnet_addr ) +void udpiiu::exceptionRespAction ( const caHdr &msg, const osiSockAddr &net_addr ) { - caHdr *pReqMsg = pMsg + 1; + const caHdr &reqMsg = * ( &msg + 1 ); char name[64]; - ipAddrToA ( pnet_addr, name, sizeof ( name ) ); + sockAddrToA ( &net_addr.sa, name, sizeof ( name ) ); - if ( pMsg->m_postsize > sizeof (caHdr) ){ + if ( msg.m_postsize > sizeof ( caHdr ) ){ errlogPrintf ( "error condition \"%s\" detected by %s with context \"%s\"\n", - ca_message ( ntohl ( pMsg->m_available ) ), - name, reinterpret_cast ( pReqMsg + 1 ) ); + ca_message ( msg.m_available ), + name, reinterpret_cast ( &reqMsg + 1 ) ); } else{ errlogPrintf ( "error condition \"%s\" detected by %s\n", - ca_message ( ntohl ( pMsg->m_available ) ), name ); + ca_message ( msg.m_available ), name ); } return; } -/* - * UDP protocol jump table - */ -LOCAL const pProtoStubUDP udpJumpTableCAC[] = -{ - udp_noop_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - search_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - udp_exception_resp_action, - bad_udp_resp_action, - beacon_action, - not_here_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - repeater_ack_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action -}; - /* * post_udp_msg () */ -int udpiiu::post_msg (const struct sockaddr_in *pnet_addr, - char *pInBuf, unsigned long blockSize) +int udpiiu::postMsg ( const osiSockAddr &net_addr, + char *pInBuf, unsigned long blockSize ) { caHdr *pCurMsg; while ( blockSize ) { unsigned long size; - if ( blockSize < sizeof (*pCurMsg) ) { + if ( blockSize < sizeof ( *pCurMsg ) ) { return ECA_TOLARGE; } - pCurMsg = reinterpret_cast (pInBuf); + pCurMsg = reinterpret_cast ( pInBuf ); /* * fix endian of bytes */ - pCurMsg->m_postsize = ntohs (pCurMsg->m_postsize); - pCurMsg->m_cmmd = ntohs (pCurMsg->m_cmmd); - pCurMsg->m_dataType = ntohs (pCurMsg->m_dataType); - pCurMsg->m_count = ntohs (pCurMsg->m_count); + pCurMsg->m_postsize = ntohs ( pCurMsg->m_postsize ); + pCurMsg->m_cmmd = ntohs ( pCurMsg->m_cmmd ); + pCurMsg->m_dataType = ntohs ( pCurMsg->m_dataType ); + pCurMsg->m_count = ntohs ( pCurMsg->m_count ); #if 0 - printf ("UDP Cmd=%3d Type=%3d Count=%4d Size=%4d", + printf ( "UDP Cmd=%3d Type=%3d Count=%4d Size=%4d", pCurMsg->m_cmmd, pCurMsg->m_dataType, pCurMsg->m_count, - pCurMsg->m_postsize); + pCurMsg->m_postsize ); printf (" Avail=%8x Cid=%6d\n", pCurMsg->m_available, - pCurMsg->m_cid); + pCurMsg->m_cid ); #endif - size = pCurMsg->m_postsize + sizeof (*pCurMsg); + size = pCurMsg->m_postsize + sizeof ( *pCurMsg ); /* * dont allow msg body extending beyond frame boundary @@ -762,13 +687,13 @@ int udpiiu::post_msg (const struct sockaddr_in *pnet_addr, * execute the response message */ pProtoStubUDP pStub; - if ( pCurMsg->m_cmmd>=NELEMENTS (udpJumpTableCAC) ) { - pStub = bad_udp_resp_action; + if ( pCurMsg->m_cmmd >= NELEMENTS ( udpJumpTableCAC ) ) { + pStub = badUDPRespAction; } else { pStub = udpJumpTableCAC [pCurMsg->m_cmmd]; } - (*pStub) (this, pCurMsg, pnet_addr); + (this->*pStub) ( *pCurMsg, net_addr); blockSize -= size; pInBuf += size;; @@ -789,110 +714,44 @@ const char * udpiiu::pHostName () const return ""; } -bool udpiiu::ca_v42_ok () const -{ - return false; -} - -bool udpiiu::ca_v41_ok () const -{ - return false; -} - -bool udpiiu::compareIfTCP (nciu &, const sockaddr_in &) const -{ - return false; -} - -/* - * Add chan to iiu and guarantee that - * one chan on the B cast iiu list is pointed to by - * ca_pEndOfBCastList - */ -void udpiiu::addToChanList ( nciu &chan ) -{ - this->pcas->lock (); - - /* - * add to the beginning of the list so that search requests for - * this channel will be sent first (since the retry count is zero) - */ - if ( ellCount ( &this->chidList ) == 0 ) { - this->pcas->endOfBCastList = tsDLIterBD ( &chan ); - } - /* - * add to the front of the list so that - * search requests for new channels will be sent first - */ - chan.retry = 0u; - this->chidList.push ( chan ); - chan.piiu = this; - - this->pcas->unlock (); -} - -void udpiiu::removeFromChanList ( nciu &chan ) -{ - tsDLIterBD iter ( &chan ); - - this->pcas->lock (); - if ( chan.piiu->pcas->endOfBCastList == iter ) { - if ( iter.itemBefore ().valid () ) { - chan.piiu->pcas->endOfBCastList = iter.itemBefore (); - } - else { - chan.piiu->pcas->endOfBCastList = - tsDLIterBD ( chan.piiu->chidList.last () ); - } - } - chan.piiu->chidList.remove ( chan ); - chan.piiu = NULL; - this->pcas->unlock (); -} - -void udpiiu::disconnect ( nciu & /* chan */ ) -{ - // NOOP -} - /* * udpiiu::pushDatagramMsg () */ -int udpiiu::pushDatagramMsg (const caHdr *pMsg, const void *pExt, ca_uint16_t extsize) +bool udpiiu::pushDatagramMsg ( const caHdr &msg, const void *pExt, ca_uint16_t extsize ) { unsigned long msgsize; ca_uint16_t allignedExtSize; caHdr *pbufmsg; - allignedExtSize = CA_MESSAGE_ALIGN (extsize); - msgsize = sizeof (caHdr) + allignedExtSize; + allignedExtSize = CA_MESSAGE_ALIGN ( extsize ); + msgsize = sizeof ( caHdr ) + allignedExtSize; /* fail out if max message size exceeded */ - if ( msgsize >= sizeof (this->xmitBuf)-7 ) { - return ECA_TOLARGE; + if ( msgsize >= sizeof ( this->xmitBuf ) - 7 ) { + return false; } - semMutexMustTake (this->xmitBufLock); + this->lock (); if ( msgsize + this->nBytesInXmitBuf > sizeof ( this->xmitBuf ) ) { - semMutexGive (this->xmitBufLock); - return ECA_TOLARGE; + this->unlock (); + return false; } pbufmsg = (caHdr *) &this->xmitBuf[this->nBytesInXmitBuf]; - *pbufmsg = *pMsg; - memcpy (pbufmsg+1, pExt, extsize); + *pbufmsg = msg; + memcpy ( pbufmsg+1, pExt, extsize ); if ( extsize != allignedExtSize ) { - char *pDest = (char *) (pbufmsg+1); - memset (pDest + extsize, '\0', allignedExtSize - extsize); + char *pDest = (char *) ( pbufmsg + 1 ); + memset ( pDest + extsize, '\0', allignedExtSize - extsize ); } - pbufmsg->m_postsize = htons (allignedExtSize); + pbufmsg->m_postsize = htons ( allignedExtSize ); this->nBytesInXmitBuf += msgsize; - semMutexGive (this->xmitBufLock); + this->unlock (); - return ECA_NORMAL; + return true; } // @@ -902,7 +761,12 @@ void udpiiu::flush () { osiSockAddrNode *pNode; - semMutexMustTake (this->xmitBufLock); + this->lock (); + + if ( this->nBytesInXmitBuf == 0u ) { + this->unlock (); + return; + } pNode = (osiSockAddrNode *) ellFirst ( &this->dest ); while ( pNode ) { @@ -940,7 +804,7 @@ void udpiiu::flush () else { char buf[64]; - ipAddrToA ( &pNode->addr.ia, buf, sizeof ( buf ) ); + sockAddrToA ( &pNode->addr.sa, buf, sizeof ( buf ) ); ca_printf ( "CAC: error = \"%s\" sending UDP msg to %s\n", @@ -954,17 +818,12 @@ void udpiiu::flush () this->nBytesInXmitBuf = 0u; - semMutexGive ( this->xmitBufLock ); -} - -int udpiiu::pushStreamMsg ( const caHdr * /* pmsg */, - const void * /* pext */, bool /* blockingOk */ ) -{ - ca_printf ("in pushStreamMsg () for a udp iiu?\n"); - return ECA_DISCONNCHID; + this->unlock (); } SOCKET udpiiu::getSock () const { return this->sock; -} \ No newline at end of file +} + +