From f04fa5fdf315cc6f703f17266f86de535ceaa209 Mon Sep 17 00:00:00 2001 From: Jeff Hill Date: Fri, 9 Jan 2004 00:42:15 +0000 Subject: [PATCH] many changes associated will disconnecting the channel but not disconnecting the circuit --- src/ca/CASG.cpp | 162 ++-- src/ca/Makefile | 15 +- src/ca/access.cpp | 83 +- src/ca/autoPtrRecycle.h | 23 +- src/ca/bhe.cpp | 44 +- src/ca/bhe.h | 17 +- src/ca/ca_client_context.cpp | 403 +++++--- src/ca/cac.cpp | 867 ++++++------------ src/ca/cac.h | 186 ++-- src/ca/cacChannel.cpp | 3 + .../{cacNotify.cpp => cacContextNotify.cpp} | 6 +- src/ca/cacIO.h | 139 +-- src/ca/cacServiceList.cpp | 71 -- src/ca/casw.cpp | 12 +- src/ca/comQueSend.cpp | 17 +- src/ca/comQueSend.h | 6 +- src/ca/disconnectGovernorTimer.cpp | 57 ++ src/ca/disconnectGovernorTimer.h | 60 ++ src/ca/getCallback.cpp | 24 +- src/ca/getCopy.cpp | 28 +- src/ca/hostNameCache.cpp | 3 - src/ca/nciu.cpp | 359 ++++---- src/ca/nciu.h | 160 ++-- src/ca/netIO.h | 154 ++-- src/ca/netReadNotifyIO.cpp | 66 +- src/ca/netSubscription.cpp | 100 +- src/ca/netWriteNotifyIO.cpp | 65 +- src/ca/netiiu.cpp | 39 +- src/ca/netiiu.h | 40 +- src/ca/oldAccess.h | 303 +++--- src/ca/oldChannelNotify.cpp | 132 ++- src/ca/oldSubscription.cpp | 25 +- src/ca/putCallback.cpp | 24 +- src/ca/repeater.cpp | 2 +- src/ca/repeaterSubscribeTimer.cpp | 5 + src/ca/repeaterSubscribeTimer.h | 1 + src/ca/searchTimer.cpp | 89 +- src/ca/searchTimer.h | 6 +- src/ca/sgAutoPtr.h | 12 +- src/ca/syncGroup.h | 147 ++- src/ca/syncGroupNotify.cpp | 16 +- src/ca/syncGroupReadNotify.cpp | 48 +- src/ca/syncGroupWriteNotify.cpp | 41 +- src/ca/syncgrp.cpp | 57 +- src/ca/tcpRecvWatchdog.cpp | 149 ++- src/ca/tcpRecvWatchdog.h | 23 +- src/ca/tcpSendWatchdog.cpp | 14 +- src/ca/tcpSendWatchdog.h | 5 +- src/ca/tcpiiu.cpp | 747 ++++++++++----- src/ca/templateInstances.cpp | 11 +- src/ca/udpiiu.cpp | 157 +++- src/ca/udpiiu.h | 58 +- src/ca/virtualCircuit.h | 127 +-- src/db/Makefile | 2 +- src/db/dbCAC.h | 147 +-- src/db/dbChannelIO.cpp | 78 +- src/db/dbChannelIO.h | 58 +- ...Cache.cpp => dbContextReadNotifyCache.cpp} | 38 +- src/db/dbPutNotifyBlocker.cpp | 81 +- src/db/dbPutNotifyBlocker.h | 22 +- src/db/dbServiceIO.cpp | 274 +++--- src/db/dbSubscriptionIO.cpp | 69 +- 62 files changed, 3591 insertions(+), 2586 deletions(-) rename src/ca/{cacNotify.cpp => cacContextNotify.cpp} (88%) delete mode 100644 src/ca/cacServiceList.cpp create mode 100644 src/ca/disconnectGovernorTimer.cpp create mode 100644 src/ca/disconnectGovernorTimer.h rename src/db/{dbServiceIOReadNotifyCache.cpp => dbContextReadNotifyCache.cpp} (64%) diff --git a/src/ca/CASG.cpp b/src/ca/CASG.cpp index e6471b95b..0c26d87f0 100644 --- a/src/ca/CASG.cpp +++ b/src/ca/CASG.cpp @@ -23,31 +23,35 @@ #include "iocinf.h" #include "syncGroup.h" #include "oldAccess.h" -#include "autoPtrDestroy.h" #include "cac.h" #include "sgAutoPtr.h" casgRecycle::~casgRecycle () {} -CASG::CASG ( ca_client_context &cacIn ) : +CASG::CASG ( epicsGuard < epicsMutex > & guard, ca_client_context & cacIn ) : client ( cacIn ), magic ( CASG_MAGIC ) { - client.installCASG ( *this ); + client.installCASG ( guard, *this ); } CASG::~CASG () { - if ( this->verify () ) { - this->reset (); - this->client.uninstallCASG ( *this ); +} + +void CASG::destructor ( epicsGuard < epicsMutex > & guard ) +{ + if ( this->verify ( guard ) ) { + this->reset ( guard ); + this->client.uninstallCASG ( guard, *this ); this->magic = 0; } else { - this->printf ("cac: attempt to destroy invalid sync group ignored\n"); + this->printf ( "cac: attempt to destroy invalid sync group ignored\n" ); } + this->~CASG (); } -bool CASG::verify () const +bool CASG::verify ( epicsGuard < epicsMutex > & ) const { return ( this->magic == CASG_MAGIC ); } @@ -55,7 +59,7 @@ bool CASG::verify () const /* * CASG::block () */ -int CASG::block ( double timeout ) +int CASG::block ( epicsGuard < epicsMutex > & guard, double timeout ) { epicsTime cur_time; epicsTime beg_time; @@ -75,7 +79,7 @@ int CASG::block ( double timeout ) cur_time = epicsTime::getCurrent (); - this->client.flushRequest (); + this->client.flush ( guard ); beg_time = cur_time; delay = 0.0; @@ -96,7 +100,11 @@ int CASG::block ( double timeout ) break; } - this->client.blockForEventAndEnableCallbacks ( this->sem, remaining ); + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->client.blockForEventAndEnableCallbacks ( + this->sem, remaining ); + } /* * force a time update @@ -106,87 +114,79 @@ int CASG::block ( double timeout ) delay = cur_time - beg_time; } - this->reset (); + this->reset ( guard ); return status; } -void CASG::reset () +void CASG::reset ( + epicsGuard < epicsMutex > & guard ) { - epicsGuard < casgMutex > locker ( this->mutex ); - this->destroyCompletedIO (); - this->destroyPendingIO (); + this->destroyCompletedIO ( guard ); + this->destroyPendingIO ( guard ); } // lock must be applied -void CASG::destroyCompletedIO () +void CASG::destroyCompletedIO ( + epicsGuard < epicsMutex > & guard ) { - tsDLList < syncGroupNotify > userStillRequestingList; - syncGroupNotify *pNotify; + syncGroupNotify * pNotify; while ( ( pNotify = this->ioCompletedList.get () ) ) { - if ( pNotify->ioInitiated() ) { - pNotify->destroy ( * this ); - } - else { - userStillRequestingList.add ( *pNotify ); - } + pNotify->destroy ( guard, * this ); } - this->ioCompletedList.add ( userStillRequestingList ); } // lock must be applied -void CASG::destroyPendingIO () +void CASG::destroyPendingIO ( + epicsGuard < epicsMutex > & guard ) { - tsDLList < syncGroupNotify > userStillRequestingList; syncGroupNotify *pNotify; while ( ( pNotify = this->ioPendingList.get () ) ) { - if ( pNotify->ioInitiated() ) { - pNotify->destroy ( * this ); - } - else { - userStillRequestingList.add ( *pNotify ); - } + pNotify->destroy ( guard, * this ); } - this->ioPendingList.add ( userStillRequestingList ); } void CASG::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->client.mutexRef () ); + this->show ( guard, level ); +} + +void CASG::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const { ::printf ( "Sync Group: id=%u, magic=%u, opPend=%u\n", this->getId (), this->magic, this->ioPendingList.count () ); if ( level ) { - epicsGuard < casgMutex > locker ( this->mutex ); ::printf ( "\tPending" ); - tsDLIterConst < syncGroupNotify > notifyPending = this->ioPendingList.firstIter (); + tsDLIterConst < syncGroupNotify > notifyPending = + this->ioPendingList.firstIter (); while ( notifyPending.valid () ) { - notifyPending->show ( level - 1u ); + notifyPending->show ( guard, level - 1u ); notifyPending++; } ::printf ( "\tCompleted" ); - tsDLIterConst < syncGroupNotify > notifyCompleted = this->ioCompletedList.firstIter (); + tsDLIterConst < syncGroupNotify > notifyCompleted = + this->ioCompletedList.firstIter (); while ( notifyCompleted.valid () ) { - notifyCompleted->show ( level - 1u ); + notifyCompleted->show ( guard, level - 1u ); notifyCompleted++; } } } -bool CASG::ioComplete () +bool CASG::ioComplete ( + epicsGuard < epicsMutex > & guard ) { - bool isCompleted; - { - epicsGuard < casgMutex > locker ( this->mutex ); - this->destroyCompletedIO (); - isCompleted = ( this->ioPendingList.count () == 0u ); - } - return isCompleted; + this->destroyCompletedIO ( guard ); + return this->ioPendingList.count () == 0u; } -void CASG::put ( chid pChan, unsigned type, arrayElementCount count, const void * pValue ) +void CASG::put ( epicsGuard < epicsMutex > & guard, chid pChan, + unsigned type, arrayElementCount count, const void * pValue ) { - sgAutoPtr < syncGroupWriteNotify > pNotify ( *this ); + sgAutoPtr < syncGroupWriteNotify > pNotify ( guard, *this ); { - epicsGuard < casgMutex > locker ( this->mutex ); pNotify = syncGroupWriteNotify::factory ( this->freeListWriteOP, *this, pChan ); if ( pNotify.get () ) { @@ -196,15 +196,15 @@ void CASG::put ( chid pChan, unsigned type, arrayElementCount count, const void return; } } - pNotify->begin ( type, count, pValue ); + pNotify->begin ( guard, type, count, pValue ); pNotify.release (); } -void CASG::get ( chid pChan, unsigned type, arrayElementCount count, void *pValue ) +void CASG::get ( epicsGuard < epicsMutex > & guard, chid pChan, + unsigned type, arrayElementCount count, void *pValue ) { - sgAutoPtr < syncGroupReadNotify > pNotify ( *this ); + sgAutoPtr < syncGroupReadNotify > pNotify ( guard, *this ); { - epicsGuard < casgMutex > locker ( this->mutex ); pNotify = syncGroupReadNotify::factory ( this->freeListReadOP, *this, pChan, pValue ); if ( pNotify.get () ) { @@ -214,39 +214,37 @@ void CASG::get ( chid pChan, unsigned type, arrayElementCount count, void *pValu return; } } - pNotify->begin ( type, count ); + pNotify->begin ( guard, type, count ); pNotify.release (); } -void CASG::destroyPendingIO ( syncGroupNotify * pNotify ) +void CASG::destroyPendingIO ( + epicsGuard < epicsMutex > & guard, syncGroupNotify * pNotify ) { if ( pNotify ) { - epicsGuard < casgMutex > locker ( this->mutex ); this->ioPendingList.remove ( *pNotify ); - pNotify->destroy ( *this ); + pNotify->destroy ( guard, *this ); } } -void CASG::completionNotify ( syncGroupNotify & notify ) +void CASG::completionNotify ( + epicsGuard < epicsMutex > & guard, syncGroupNotify & notify ) { - unsigned requestsIncomplete; - { - epicsGuard < casgMutex > locker ( this->mutex ); - this->ioPendingList.remove ( notify ); - this->ioCompletedList.add ( notify ); - requestsIncomplete = this->ioPendingList.count (); - } - if ( requestsIncomplete == 0u ) { + this->ioPendingList.remove ( notify ); + this->ioCompletedList.add ( notify ); + if ( this->ioPendingList.count () == 0u ) { this->sem.signal (); } } -void CASG::recycleSyncGroupWriteNotify ( syncGroupWriteNotify & io ) +void CASG::recycleSyncGroupWriteNotify ( + epicsGuard < epicsMutex > & guard, syncGroupWriteNotify & io ) { this->freeListWriteOP.release ( & io ); } -void CASG::recycleSyncGroupReadNotify ( syncGroupReadNotify & io ) +void CASG::recycleSyncGroupReadNotify ( + epicsGuard < epicsMutex > & guard, syncGroupReadNotify & io ) { this->freeListReadOP.release ( & io ); } @@ -265,18 +263,28 @@ int CASG::printf ( const char *pformat, ... ) return status; } -void CASG::exception ( int status, const char *pContext, - const char *pFileName, unsigned lineNo ) +void CASG::exception ( + epicsGuard < epicsMutex > & guard, + int status, const char * pContext, + const char * pFileName, unsigned lineNo ) { - this->client.exception ( status, pContext, pFileName, lineNo ); + if ( status != ECA_CHANDESTROY ) { + this->client.exception ( + guard, status, pContext, pFileName, lineNo ); + } } -void CASG::exception ( int status, const char *pContext, - const char *pFileName, unsigned lineNo, oldChannelNotify &chan, +void CASG::exception ( + epicsGuard < epicsMutex > & guard, + int status, const char * pContext, + const char * pFileName, unsigned lineNo, oldChannelNotify & chan, unsigned type, arrayElementCount count, unsigned op ) { - this->client.exception ( status, pContext, pFileName, - lineNo, chan, type, count, op ); + if ( status != ECA_CHANDESTROY ) { + this->client.exception ( + guard, status, pContext, pFileName, + lineNo, chan, type, count, op ); + } } void * CASG::operator new ( size_t ) // X aCC 361 diff --git a/src/ca/Makefile b/src/ca/Makefile index ed840de8e..64a216382 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -29,23 +29,23 @@ INC += caDiagnostics.h LIBSRCS += cac.cpp LIBSRCS += cacChannel.cpp LIBSRCS += cacChannelNotify.cpp -LIBSRCS += cacNotify.cpp +LIBSRCS += cacContextNotify.cpp LIBSRCS += cacReadNotify.cpp LIBSRCS += cacWriteNotify.cpp LIBSRCS += cacStateNotify.cpp -LIBSRCS += cacServiceList.cpp LIBSRCS += access.cpp LIBSRCS += iocinf.cpp LIBSRCS += convert.cpp LIBSRCS += test_event.cpp LIBSRCS += repeater.cpp LIBSRCS += searchTimer.cpp +LIBSRCS += disconnectGovernorTimer.cpp LIBSRCS += repeaterSubscribeTimer.cpp -LIBSRCS += tcpiiu.cpp -LIBSRCS += udpiiu.cpp -LIBSRCS += netiiu.cpp -LIBSRCS += nciu.cpp LIBSRCS += baseNMIU.cpp +LIBSRCS += nciu.cpp +LIBSRCS += netiiu.cpp +LIBSRCS += udpiiu.cpp +LIBSRCS += tcpiiu.cpp LIBSRCS += netReadNotifyIO.cpp LIBSRCS += netWriteNotifyIO.cpp LIBSRCS += netSubscription.cpp @@ -89,7 +89,8 @@ PROD_LIBS = ca Com PROD_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 PROD_HOST += caRepeater catime acctst caConnTest casw caEventRate -OBJS_IOC_vxWorks += catime acctst caConnTest casw caEventRate +OBJS_IOC_vxWorks += +OBJS_IOC += catime acctst caConnTest casw caEventRate caRepeater_SRCS = caRepeater.cpp catime_SRCS = catimeMain.c catime.c acctst_SRCS = acctstMain.c acctst.c diff --git a/src/ca/access.cpp b/src/ca/access.cpp index 982e9c433..6b956a89e 100644 --- a/src/ca/access.cpp +++ b/src/ca/access.cpp @@ -41,7 +41,6 @@ #define epicsExportSharedSymbols #include "iocinf.h" #include "oldAccess.h" -#include "autoPtrDestroy.h" #include "cac.h" epicsThreadPrivateId caClientContextId; @@ -187,7 +186,7 @@ int epicsShareAPI ca_context_create ( ca_client_context *pcac; try { - epicsThreadOnce ( &caClientContextIdOnce, ca_init_client_context, 0); + epicsThreadOnce ( & caClientContextIdOnce, ca_init_client_context, 0); if ( caClientContextId == 0 ) { return ECA_ALLOCMEM; } @@ -216,22 +215,6 @@ int epicsShareAPI ca_context_create ( return ECA_NORMAL; } -// -// ca_register_service () -// -int epicsShareAPI ca_register_service ( cacService *pService ) -{ - ca_client_context *pcac; - int caStatus = fetchClientContext (&pcac); - if ( caStatus != ECA_NORMAL ) { - return caStatus; - } - if ( pService ) { - pcac->registerService ( *pService ); - } - return ECA_NORMAL; -} - // // ca_modify_host_name () // @@ -328,8 +311,8 @@ int epicsShareAPI ca_create_channel ( CAFDHANDLER * pFunc = 0; void * pArg = 0; { - epicsGuard < ca_client_context_mutex > - autoMutex ( pcac->mutex ); + epicsGuard < epicsMutex > + guard ( pcac->mutex ); if ( pcac->fdRegFuncNeedsToBeCalled ) { pFunc = pcac->fdRegFunc; pArg = pcac->fdRegArg; @@ -342,14 +325,18 @@ int epicsShareAPI ca_create_channel ( } try { + epicsGuard < epicsMutex > guard ( pcac->mutex ); oldChannelNotify * pChanNotify = new ( pcac->oldChannelNotifyFreeList ) - oldChannelNotify ( *pcac, name_str, + oldChannelNotify ( guard, *pcac, name_str, conn_func, puser, priority ); // make sure that their chan pointer is set prior to // calling connection call backs *chanptr = pChanNotify; - pChanNotify->initiateConnect (); + pChanNotify->initiateConnect ( guard ); + // no need to worry about a connect preempting here because + // the connect sequence will not start untill initiateConnect() + // is called } catch ( cacChannel::badString & ) { return ECA_BADSTR; @@ -360,6 +347,9 @@ int epicsShareAPI ca_create_channel ( catch ( cacChannel::badPriority & ) { return ECA_BADPRIORITY; } + catch ( cacChannel::unsupportedByService & ) { + return ECA_UNAVAILINSERV; + } catch ( ... ) { return ECA_INTERNAL; } @@ -392,12 +382,13 @@ int epicsShareAPI ca_array_get ( chtype type, return ECA_BADTYPE; } unsigned tmpType = static_cast < unsigned > ( type ); - autoPtrFreeList < getCopy > pNotify + epicsGuard < epicsMutex > guard ( pChan->getClientCtx().mutex ); + autoPtrFreeList < getCopy, 0x400, epicsMutexNOOP > pNotify ( pChan->getClientCtx().getCopyFreeList, new ( pChan->getClientCtx().getCopyFreeList ) - getCopy ( pChan->getClientCtx(), *pChan, + getCopy ( guard, pChan->getClientCtx(), *pChan, tmpType, count, pValue ) ); - pChan->read ( type, count, *pNotify ); + pChan->read ( guard, type, count, *pNotify ); pNotify.release (); caStatus = ECA_NORMAL; } @@ -458,11 +449,12 @@ int epicsShareAPI ca_array_get_callback ( chtype type, } unsigned tmpType = static_cast < unsigned > ( type ); - autoPtrFreeList < getCallback > pNotify + epicsGuard < epicsMutex > guard ( pChan->getClientCtx().mutex ); + autoPtrFreeList < getCallback, 0x400, epicsMutexNOOP > pNotify ( pChan->getClientCtx().getCallbackFreeList, new ( pChan->getClientCtx().getCallbackFreeList ) getCallback ( *pChan, pfunc, arg ) ); - pChan->read ( tmpType, count, *pNotify ); + pChan->read ( guard, tmpType, count, *pNotify ); pNotify.release (); caStatus = ECA_NORMAL; } @@ -520,12 +512,13 @@ int epicsShareAPI ca_array_put_callback ( chtype type, arrayElementCount count, if ( type < 0 ) { return ECA_BADTYPE; } + epicsGuard < epicsMutex > guard ( pChan->getClientCtx().mutex ); unsigned tmpType = static_cast < unsigned > ( type ); - autoPtrFreeList < putCallback > pNotify + autoPtrFreeList < putCallback, 0x400, epicsMutexNOOP > pNotify ( pChan->getClientCtx().putCallbackFreeList, new ( pChan->getClientCtx().putCallbackFreeList ) putCallback ( *pChan, pfunc, usrarg ) ); - pChan->write ( tmpType, count, pValue, *pNotify ); + pChan->write ( guard, tmpType, count, pValue, *pNotify ); pNotify.release (); caStatus = ECA_NORMAL; } @@ -573,7 +566,7 @@ int epicsShareAPI ca_array_put_callback ( chtype type, arrayElementCount count, */ // extern "C" int epicsShareAPI ca_array_put ( chtype type, arrayElementCount count, - chid pChan, const void *pValue ) + chid pChan, const void * pValue ) { if ( type < 0 ) { return ECA_BADTYPE; @@ -582,7 +575,8 @@ int epicsShareAPI ca_array_put ( chtype type, arrayElementCount count, int caStatus; try { - pChan->write ( tmpType, count, pValue ); + epicsGuard < epicsMutex > guard ( pChan->getClientCtx().mutex ); + pChan->write ( guard, tmpType, count, pValue ); caStatus = ECA_NORMAL; } catch ( cacChannel::badString & ) @@ -687,7 +681,8 @@ int epicsShareAPI ca_create_subscription ( } try { - autoPtrFreeList < oldSubscription > pSubsr + epicsGuard < epicsMutex > guard ( pChan->getClientCtx().mutex ); + autoPtrFreeList < oldSubscription, 0x400, epicsMutexNOOP > pSubsr ( pChan->getClientCtx().subscriptionFreeList, new ( pChan->getClientCtx().subscriptionFreeList ) oldSubscription ( *pChan, @@ -696,7 +691,7 @@ int epicsShareAPI ca_create_subscription ( if ( monixptr ) { *monixptr = pTmp; } - pTmp->begin ( tmpType, count, mask ); + pTmp->begin ( guard, tmpType, count, mask ); // dont touch pTmp after this because // the first callback might have canceled it return ECA_NORMAL; @@ -751,8 +746,8 @@ epicsShareFunc int epicsShareAPI ca_clear_subscription ( evid pMon ) { oldChannelNotify & chan = pMon->channel (); ca_client_context & cac = chan.getClientCtx (); - pMon->ioCancel (); - cac.destroySubscription ( *pMon ); + epicsGuard < epicsMutex > guard ( cac.mutex ); + pMon->ioCancel ( guard ); return ECA_NORMAL; } @@ -837,16 +832,16 @@ int epicsShareAPI ca_pend_io ( ca_real timeout ) /* * ca_flush_io () */ -// extern "C" int epicsShareAPI ca_flush_io () { - ca_client_context *pcac; + ca_client_context * pcac; int caStatus = fetchClientContext (&pcac); if ( caStatus != ECA_NORMAL ) { return caStatus; } - pcac->flushRequest (); + epicsGuard < epicsMutex > guard ( pcac->mutex ); + pcac->flush ( guard ); return ECA_NORMAL; } @@ -854,7 +849,6 @@ int epicsShareAPI ca_flush_io () /* * CA_TEST_IO () */ -// extern "C" int epicsShareAPI ca_test_io () // X aCC 361 { ca_client_context *pcac; @@ -934,9 +928,11 @@ void epicsShareAPI ca_signal_formated ( long ca_status, const char *pfilenm, pcac->vSignal ( ca_status, pfilenm, lineno, pFormat, theArgs ); } else { - fprintf ( stderr, "file=%s line=%d: CA exception delivered to a thread w/o ca context\n", - pfilenm, lineno ); - vfprintf ( stderr, pFormat, theArgs ); + fprintf ( stderr, "CA exception in thread w/o CA ctx: status=%s file=%s line=%d: \n", + ca_message ( ca_status ), pfilenm, lineno ); + if ( pFormat ) { + vfprintf ( stderr, pFormat, theArgs ); + } } va_end ( theArgs ); } @@ -1109,7 +1105,6 @@ double epicsShareAPI ca_beacon_period ( chid pChan ) return pChan->beaconPeriod (); } -// extern "C" double epicsShareAPI ca_receive_watchdog_delay ( chid pChan ) { return pChan->receiveWatchdogDelay (); @@ -1130,7 +1125,7 @@ unsigned epicsShareAPI ca_get_ioc_connection_count () return 0u; } - return pcac->connectionCount (); + return pcac->circuitCount (); } unsigned epicsShareAPI ca_beacon_anomaly_count () diff --git a/src/ca/autoPtrRecycle.h b/src/ca/autoPtrRecycle.h index 51d4b7f36..4cbc219e7 100644 --- a/src/ca/autoPtrRecycle.h +++ b/src/ca/autoPtrRecycle.h @@ -30,27 +30,29 @@ template < class T > class autoPtrRecycle { public: - autoPtrRecycle ( chronIntIdResTable < baseNMIU > &, - tsDLList < class baseNMIU > &, cacRecycle &, T * ); + autoPtrRecycle ( + epicsGuard < epicsMutex > &, chronIntIdResTable < baseNMIU > &, + cacRecycle &, T * ); ~autoPtrRecycle (); T & operator * () const; T * operator -> () const; T * get () const; T * release (); private: - T *p; - cacRecycle &r; - tsDLList < class baseNMIU > &eventq; - chronIntIdResTable < baseNMIU > &ioTable; + T * p; + cacRecycle & r; + chronIntIdResTable < baseNMIU > & ioTable; + epicsGuard < epicsMutex > & guard; // not implemented autoPtrRecycle ( const autoPtrRecycle & ); autoPtrRecycle & operator = ( const autoPtrRecycle & ); }; template < class T > -inline autoPtrRecycle::autoPtrRecycle ( chronIntIdResTable < baseNMIU > &tbl, - tsDLList < class baseNMIU > &list, cacRecycle &rIn, T *pIn ) : - p ( pIn ), r ( rIn ), eventq ( list ), ioTable ( tbl ) {} +inline autoPtrRecycle::autoPtrRecycle ( + epicsGuard < epicsMutex > & guardIn, chronIntIdResTable < baseNMIU > & tbl, + cacRecycle & rIn, T * pIn ) : + p ( pIn ), r ( rIn ), ioTable ( tbl ), guard ( guardIn ) {} template < class T > inline autoPtrRecycle::~autoPtrRecycle () @@ -58,8 +60,7 @@ inline autoPtrRecycle::~autoPtrRecycle () if ( this->p ) { baseNMIU *pb = this->p; this->ioTable.remove ( *pb ); - this->eventq.remove ( *pb ); - pb->destroy ( this->r ); + pb->destroy ( this->guard, this->r ); } } diff --git a/src/ca/bhe.cpp b/src/ca/bhe.cpp index fe9e09bf7..bf804ebf9 100644 --- a/src/ca/bhe.cpp +++ b/src/ca/bhe.cpp @@ -24,11 +24,6 @@ #include #include -#if 0 -#define DEBUG -#define DEBUG_ALL 0 -#endif - #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #define epicsExportSharedSymbols @@ -50,10 +45,10 @@ * zero (so we can correctly compute the period * between the 1st and 2nd beacons) */ -bhe::bhe ( const epicsTime & initialTimeStamp, +bhe::bhe ( epicsMutex & mutexIn, const epicsTime & initialTimeStamp, unsigned initialBeaconNumber, const inetAddrID & addr ) : inetAddrID ( addr ), timeStamp ( initialTimeStamp ), averagePeriod ( - DBL_MAX ), - pIIU ( 0 ), lastBeaconNumber ( initialBeaconNumber ) + mutex ( mutexIn ), pIIU ( 0 ), lastBeaconNumber ( initialBeaconNumber ) { # ifdef DEBUG { @@ -68,10 +63,11 @@ bhe::~bhe () { } -void bhe::beaconAnomalyNotify () +void bhe::beaconAnomalyNotify ( epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->mutex ); if ( this->pIIU ) { - this->pIIU->beaconAnomalyNotify (); + this->pIIU->beaconAnomalyNotify ( guard ); } } @@ -125,10 +121,13 @@ void bhe::logBeaconDiscard ( unsigned /* beaconAdvance */, * * updates beacon period, and looks for beacon anomalies */ -bool bhe::updatePeriod ( const epicsTime & programBeginTime, +bool bhe::updatePeriod ( + epicsGuard < epicsMutex > & guard, const epicsTime & programBeginTime, const epicsTime & currentTime, ca_uint32_t beaconNumber, unsigned protocolRevision ) { + guard.assertIdenticalMutex ( this->mutex ); + // // this block is enetered if the beacon was created as a side effect of // creating a connection and so we dont yet know the first beacon time @@ -139,7 +138,7 @@ bool bhe::updatePeriod ( const epicsTime & programBeginTime, this->lastBeaconNumber = beaconNumber; } - this->beaconAnomalyNotify (); + this->beaconAnomalyNotify ( guard ); /* * this is the 1st beacon seen - the beacon time stamp @@ -189,7 +188,7 @@ bool bhe::updatePeriod ( const epicsTime & programBeginTime, if ( this->averagePeriod < 0.0 ) { double totalRunningTime; - this->beaconAnomalyNotify (); + this->beaconAnomalyNotify ( guard ); /* * this is the 2nd beacon seen. We cant tell about @@ -229,7 +228,7 @@ bool bhe::updatePeriod ( const epicsTime & programBeginTime, * trigger on any missing beacon * if connected to this server */ - this->beaconAnomalyNotify (); + this->beaconAnomalyNotify ( guard ); if ( currentPeriod >= this->averagePeriod * 3.25 ) { /* @@ -253,14 +252,14 @@ bool bhe::updatePeriod ( const epicsTime & programBeginTime, * that the server is available */ else if ( currentPeriod <= this->averagePeriod * 0.80 ) { - this->beaconAnomalyNotify (); + this->beaconAnomalyNotify ( guard ); netChange = true; logBeacon ( "bal", currentPeriod, currentTime ); } else if ( this->pIIU ) { // update state of health for active virtual circuits // if the beacon looks ok - this->pIIU->beaconArrivalNotify ( currentTime ); + this->pIIU->beaconArrivalNotify ( guard, currentTime ); logBeacon ( "vb", currentPeriod, currentTime ); } @@ -276,27 +275,34 @@ bool bhe::updatePeriod ( const epicsTime & programBeginTime, void bhe::show ( unsigned /* level */ ) const { + epicsGuard < epicsMutex > guard ( this->mutex ); ::printf ( "CA beacon hash entry at %p with average period %f\n", static_cast ( this ), this->averagePeriod ); } -double bhe::period () const +double bhe::period ( epicsGuard < epicsMutex > & guard ) const { + guard.assertIdenticalMutex ( this->mutex ); return this->averagePeriod; } -epicsTime bhe::updateTime () const +epicsTime bhe::updateTime ( epicsGuard < epicsMutex > & guard ) const { + guard.assertIdenticalMutex ( this->mutex ); return this->timeStamp; } -void bhe::registerIIU ( tcpiiu & iiu ) +void bhe::registerIIU ( + epicsGuard < epicsMutex > & guard, tcpiiu & iiu ) { + guard.assertIdenticalMutex ( this->mutex ); this->pIIU = & iiu; } -void bhe::unregisterIIU ( tcpiiu & iiu ) +void bhe::unregisterIIU ( + epicsGuard < epicsMutex > & guard, tcpiiu & iiu ) { + guard.assertIdenticalMutex ( this->mutex ); if ( this->pIIU == & iiu ) { this->pIIU = 0; this->timeStamp = epicsTime(); diff --git a/src/ca/bhe.h b/src/ca/bhe.h index 862639d42..ad5d51244 100644 --- a/src/ca/bhe.h +++ b/src/ca/bhe.h @@ -55,18 +55,20 @@ public: class bhe : public tsSLNode < bhe >, public inetAddrID { public: - epicsShareFunc bhe ( const epicsTime & initialTimeStamp, + epicsShareFunc bhe ( + epicsMutex &, const epicsTime & initialTimeStamp, unsigned initialBeaconNumber, const inetAddrID & addr ); epicsShareFunc ~bhe (); epicsShareFunc bool updatePeriod ( + epicsGuard < epicsMutex > &, const epicsTime & programBeginTime, const epicsTime & currentTime, ca_uint32_t beaconNumber, unsigned protocolRevision ); - epicsShareFunc double period () const; - epicsShareFunc epicsTime updateTime () const; - epicsShareFunc void show ( unsigned level) const; - epicsShareFunc void registerIIU ( tcpiiu & ); - epicsShareFunc void unregisterIIU ( tcpiiu & ); + epicsShareFunc double period ( epicsGuard < epicsMutex > & ) const; + epicsShareFunc epicsTime updateTime ( epicsGuard < epicsMutex > & ) const; + epicsShareFunc void show ( unsigned level ) const; + epicsShareFunc void registerIIU ( epicsGuard < epicsMutex > &, tcpiiu & ); + epicsShareFunc void unregisterIIU ( epicsGuard < epicsMutex > &, tcpiiu & ); epicsShareFunc void * operator new ( size_t size, bheMemoryManager & ); #ifdef CXX_PLACEMENT_DELETE epicsShareFunc void operator delete ( void *, bheMemoryManager & ); @@ -74,9 +76,10 @@ public: private: epicsTime timeStamp; double averagePeriod; + epicsMutex & mutex; tcpiiu * pIIU; ca_uint32_t lastBeaconNumber; - void beaconAnomalyNotify (); + void beaconAnomalyNotify ( epicsGuard < epicsMutex > & ); void logBeacon ( const char * pDiagnostic, const double & currentPeriod, const epicsTime & currentTime ); diff --git a/src/ca/ca_client_context.cpp b/src/ca/ca_client_context.cpp index 1c8860360..f2d6bf3bf 100644 --- a/src/ca/ca_client_context.cpp +++ b/src/ca/ca_client_context.cpp @@ -38,6 +38,9 @@ extern epicsThreadPrivateId caClientContextId; +cacService * ca_client_context::pDefaultService = 0; +epicsMutex ca_client_context::defaultServiceInstallMutex; + ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) : ca_exception_func ( 0 ), ca_exception_arg ( 0 ), pVPrintfFunc ( errlogVprintf ), fdRegFunc ( 0 ), fdRegArg ( 0 ), @@ -47,6 +50,22 @@ ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) : { static const unsigned short PORT_ANY = 0u; + if ( ! osiSockAttach () ) { + throwWithLocation ( noSocket () ); + } + + { + // this wont consistently work if called from file scope constructor + epicsGuard < epicsMutex > guard ( ca_client_context::defaultServiceInstallMutex ); + if ( ca_client_context::pDefaultService ) { + this->pServiceContext.reset ( + & ca_client_context::pDefaultService->contextCreate ( this->mutex, *this ) ); + } + else { + this->pServiceContext.reset ( new cac ( this->mutex, *this ) ); + } + } + this->sock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( this->sock == INVALID_SOCKET ) { char sockErrBuf[64]; @@ -113,9 +132,6 @@ ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) : this->localPort = epicsNTOH16 ( tmpAddr.ia.sin_port ); } - epics_auto_ptr < cac > pCAC ( - new cac ( *this ) ); - epics_auto_ptr < epicsGuard < epicsMutex > > pCBGuard; if ( ! enablePreemptiveCallback ) { pCBGuard.reset ( new epicsGuard < epicsMutex > ( this->callbackMutex ) ); @@ -123,7 +139,6 @@ ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) : // multiple steps ensure exception safety this->pCallbackGuard = pCBGuard; - this->pClientCtx = pCAC; } ca_client_context::~ca_client_context () @@ -133,49 +148,75 @@ ca_client_context::~ca_client_context () ( this->fdRegArg, this->sock, false ); } epicsSocketDestroy ( this->sock ); + + osiSockRelease (); + + // force a logical shutdown order + // so that the cac class does not hang its + // receive threads during their shutdown sequence + // and so that classes using this classes mutex + // are destroyed before the mutex is destroyed + if ( this->pCallbackGuard.get() ) { + epicsGuardRelease < epicsMutex > unguard ( *this->pCallbackGuard ); + this->pServiceContext.reset ( 0 ); + } + else { + this->pServiceContext.reset ( 0 ); + } } void ca_client_context::destroyChannel ( oldChannelNotify & chan ) { - chan.~oldChannelNotify (); + epicsGuard < epicsMutex > guard ( this->mutex ); + chan.destructor ( guard ); this->oldChannelNotifyFreeList.release ( & chan ); } -void ca_client_context::destroyGetCopy ( getCopy & gc ) +void ca_client_context::destroyGetCopy ( + epicsGuard < epicsMutex > & guard, getCopy & gc ) { + guard.assertIdenticalMutex ( this->mutex ); gc.~getCopy (); this->getCopyFreeList.release ( & gc ); } -void ca_client_context::destroyGetCallback ( getCallback & gcb ) +void ca_client_context::destroyGetCallback ( + epicsGuard < epicsMutex > & guard, getCallback & gcb ) { + guard.assertIdenticalMutex ( this->mutex ); gcb.~getCallback (); this->getCallbackFreeList.release ( & gcb ); } -void ca_client_context::destroyPutCallback ( putCallback & pcb ) +void ca_client_context::destroyPutCallback ( + epicsGuard < epicsMutex > & guard, putCallback & pcb ) { + guard.assertIdenticalMutex ( this->mutex ); pcb.~putCallback (); this->putCallbackFreeList.release ( & pcb ); } -void ca_client_context::destroySubscription ( oldSubscription & os ) +void ca_client_context::destroySubscription ( + epicsGuard < epicsMutex > & guard, oldSubscription & os ) { + guard.assertIdenticalMutex ( this->mutex ); os.~oldSubscription (); this->subscriptionFreeList.release ( & os ); } -void ca_client_context::changeExceptionEvent ( caExceptionHandler *pfunc, void *arg ) +void ca_client_context::changeExceptionEvent ( + caExceptionHandler * pfunc, void * arg ) { - epicsGuard < ca_client_context_mutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); this->ca_exception_func = pfunc; this->ca_exception_arg = arg; // should block here until releated callback in progress completes } -void ca_client_context::replaceErrLogHandler ( caPrintfFunc *ca_printf_func ) +void ca_client_context::replaceErrLogHandler ( + caPrintfFunc * ca_printf_func ) { - epicsGuard < ca_client_context_mutex > autoMutex ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); if ( ca_printf_func ) { this->pVPrintfFunc = ca_printf_func; } @@ -185,16 +226,18 @@ void ca_client_context::replaceErrLogHandler ( caPrintfFunc *ca_printf_func ) // should block here until releated callback in progress completes } -void ca_client_context::registerForFileDescriptorCallBack ( CAFDHANDLER *pFunc, void *pArg ) +void ca_client_context::registerForFileDescriptorCallBack ( + CAFDHANDLER *pFunc, void *pArg ) { - epicsGuard < ca_client_context_mutex > autoMutex ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); this->fdRegFunc = pFunc; this->fdRegArg = pArg; this->fdRegFuncNeedsToBeCalled = true; // should block here until releated callback in progress completes } -int ca_client_context::printf ( const char *pformat, ... ) const +int ca_client_context::printf ( + const char *pformat, ... ) const { va_list theArgs; int status; @@ -208,11 +251,12 @@ int ca_client_context::printf ( const char *pformat, ... ) const return status; } -int ca_client_context::vPrintf ( const char *pformat, va_list args ) const // X aCC 361 +int ca_client_context::vPrintf ( + const char *pformat, va_list args ) const // X aCC 361 { - caPrintfFunc *pFunc; + caPrintfFunc * pFunc; { - epicsGuard < ca_client_context_mutex > autoMutex ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); pFunc = this->pVPrintfFunc; } if ( pFunc ) { @@ -223,81 +267,137 @@ int ca_client_context::vPrintf ( const char *pformat, va_list args ) const // X } } -void ca_client_context::exception ( int stat, const char *pCtx, - const char *pFile, unsigned lineNo ) +void ca_client_context::exception ( + epicsGuard < epicsMutex > & guard, int stat, const char * pCtx, + const char * pFile, unsigned lineNo ) { struct exception_handler_args args; - caExceptionHandler *pFunc; - void *pArg; + caExceptionHandler * pFunc = this->ca_exception_func; + void * pArg = this->ca_exception_arg; { - epicsGuard < ca_client_context_mutex > autoMutex ( this->mutex ); - pFunc = this->ca_exception_func; - pArg = this->ca_exception_arg; - } - - // NOOP if they disable exceptions - if ( pFunc ) { - args.chid = NULL; - args.type = TYPENOTCONN; - args.count = 0; - args.addr = NULL; - args.stat = stat; - args.op = CA_OP_OTHER; - args.ctx = pCtx; - args.pFile = pFile; - args.lineNo = lineNo; - args.usr = pArg; - ( *pFunc ) ( args ); - } - else { - this->pClientCtx->signal ( stat, pFile, lineNo, pCtx ); + epicsGuardRelease < epicsMutex > unguard ( guard ); + // NOOP if they disable exceptions + if ( pFunc ) { + args.chid = NULL; + args.type = TYPENOTCONN; + args.count = 0; + args.addr = NULL; + args.stat = stat; + args.op = CA_OP_OTHER; + args.ctx = pCtx; + args.pFile = pFile; + args.lineNo = lineNo; + args.usr = pArg; + ( *pFunc ) ( args ); + } + else { + this->signal ( stat, pFile, lineNo, pCtx ); + } } } -void ca_client_context::exception ( int status, const char *pContext, - const char *pFileName, unsigned lineNo, oldChannelNotify &chan, +void ca_client_context::exception ( + epicsGuard < epicsMutex > & guard, int status, const char * pContext, + const char * pFileName, unsigned lineNo, oldChannelNotify & chan, unsigned type, arrayElementCount count, unsigned op ) { struct exception_handler_args args; - caExceptionHandler *pFunc; - void *pArg; + caExceptionHandler * pFunc = this->ca_exception_func; + void * pArg = this->ca_exception_arg; { - epicsGuard < ca_client_context_mutex > autoMutex ( this->mutex ); - pFunc = this->ca_exception_func; - pArg = this->ca_exception_arg; + epicsGuardRelease < epicsMutex > unguard ( guard ); + // NOOP if they disable exceptions + if ( pFunc ) { + args.chid = &chan; + args.type = type; + args.count = count; + args.addr = NULL; + args.stat = status; + args.op = op; + args.ctx = pContext; + args.pFile = pFileName; + args.lineNo = lineNo; + args.usr = pArg; + ( *pFunc ) ( args ); + } + else { + this->signal ( status, pFileName, lineNo, + "op=%u, channel=%s, type=%s, count=%lu, ctx=\"%s\"", + op, ca_name ( &chan ), + dbr_type_to_text ( static_cast ( type ) ), + count, pContext ); + } } +} - // NOOP if they disable exceptions - if ( pFunc ) { - args.chid = &chan; - args.type = type; - args.count = count; - args.addr = NULL; - args.stat = status; - args.op = op; - args.ctx = pContext; - args.pFile = pFileName; - args.lineNo = lineNo; - args.usr = pArg; - ( *pFunc ) ( args ); +void ca_client_context::signal ( int ca_status, const char * pfilenm, + int lineno, const char * pFormat, ... ) +{ + va_list theArgs; + va_start ( theArgs, pFormat ); + this->vSignal ( ca_status, pfilenm, lineno, pFormat, theArgs); + va_end ( theArgs ); +} + +void ca_client_context::vSignal ( int ca_status, const char *pfilenm, + int lineno, const char *pFormat, va_list args ) +{ + static const char *severity[] = + { + "Warning", + "Success", + "Error", + "Info", + "Fatal", + "Fatal", + "Fatal", + "Fatal" + }; + + this->printf ( "CA.Client.Exception...............................................\n" ); + + this->printf ( " %s: \"%s\"\n", + severity[ CA_EXTRACT_SEVERITY ( ca_status ) ], + ca_message ( ca_status ) ); + + if ( pFormat ) { + this->printf ( " Context: \"" ); + this->vPrintf ( pFormat, args ); + this->printf ( "\"\n" ); } - else { - this->pClientCtx->signal ( status, pFileName, lineNo, - "op=%u, channel=%s, type=%s, count=%lu, ctx=\"%s\"", - op, ca_name ( &chan ), - dbr_type_to_text ( static_cast ( type ) ), - count, pContext ); + + if ( pfilenm ) { + this->printf ( " Source File: %s line %d\n", + pfilenm, lineno ); + } + + epicsTime current = epicsTime::getCurrent (); + char date[64]; + current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); + this->printf ( " Current Time: %s\n", date ); + + /* + * Terminate execution if unsuccessful + */ + if( ! ( ca_status & CA_M_SUCCESS ) && + CA_EXTRACT_SEVERITY ( ca_status ) != CA_K_WARNING ){ + errlogFlush (); + abort (); } + + this->printf ( "..................................................................\n" ); } void ca_client_context::show ( unsigned level ) const { + epicsGuard < epicsMutex > guard ( this->mutex ); + ::printf ( "ca_client_context at %p pndRecvCnt=%u ioSeqNo=%u\n", static_cast ( this ), this->pndRecvCnt, this->ioSeqNo ); + if ( level > 0u ) { - this->mutex.show ( level - 1u ); - this->pClientCtx->show ( level - 1u ); + this->pServiceContext->show ( guard, level - 1u ); ::printf ( "\tpreemptive callback is %s\n", this->pCallbackGuard.get() ? "disabled" : "enabled" ); ::printf ( "\tthere are %u unsatisfied IO operations blocking ca_pend_io()\n", @@ -306,6 +406,8 @@ void ca_client_context::show ( unsigned level ) const this->ioSeqNo ); ::printf ( "IO done event:\n"); this->ioDone.show ( level - 1u ); + ::printf ( "Synchronous group identifier hash table:\n" ); + this->sgTable.show ( level - 1u ); } } @@ -315,37 +417,26 @@ void ca_client_context::attachToClientCtx () epicsThreadPrivateSet ( caClientContextId, this ); } -void ca_client_context::incrementOutstandingIO ( unsigned ioSeqNoIn ) +void ca_client_context::incrementOutstandingIO ( + epicsGuard < epicsMutex > & guard, unsigned ioSeqNoIn ) { - epicsGuard < ca_client_context_mutex > guard ( this->mutex ); + guard.assertIdenticalMutex ( this->mutex ); if ( this->ioSeqNo == ioSeqNoIn ) { assert ( this->pndRecvCnt < UINT_MAX ); this->pndRecvCnt++; } } -void ca_client_context::decrementOutstandingIO ( unsigned ioSeqNoIn ) +void ca_client_context::decrementOutstandingIO ( + epicsGuard < epicsMutex > & guard, unsigned ioSeqNoIn ) { - bool signalNeeded; - { - epicsGuard < ca_client_context_mutex > guard ( this->mutex ); - if ( this->ioSeqNo == ioSeqNoIn ) { - assert ( this->pndRecvCnt > 0u ); - this->pndRecvCnt--; - if ( this->pndRecvCnt == 0u ) { - signalNeeded = true; - } - else { - signalNeeded = false; - } + guard.assertIdenticalMutex ( this->mutex ); + if ( this->ioSeqNo == ioSeqNoIn ) { + assert ( this->pndRecvCnt > 0u ); + this->pndRecvCnt--; + if ( this->pndRecvCnt == 0u ) { + this->ioDone.signal (); } - else { - signalNeeded = false; - } - } - - if ( signalNeeded ) { - this->ioDone.signal (); } } @@ -367,7 +458,9 @@ int ca_client_context::pendIO ( const double & timeout ) epicsTime beg_time = epicsTime::getCurrent (); double remaining = timeout; - this->flushRequest (); + epicsGuard < epicsMutex > guard ( this->mutex ); + + this->flush ( guard ); while ( this->pndRecvCnt > 0 ) { if ( remaining < CAC_SIGNIFICANT_DELAY ) { @@ -375,7 +468,10 @@ int ca_client_context::pendIO ( const double & timeout ) break; } - this->blockForEventAndEnableCallbacks ( this->ioDone, remaining ); + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->blockForEventAndEnableCallbacks ( this->ioDone, remaining ); + } double delay = epicsTime::getCurrent () - beg_time; if ( delay < timeout ) { @@ -386,11 +482,8 @@ int ca_client_context::pendIO ( const double & timeout ) } } - { - epicsGuard < ca_client_context_mutex > guard ( this->mutex ); - this->ioSeqNo++; - this->pndRecvCnt = 0u; - } + this->ioSeqNo++; + this->pndRecvCnt = 0u; return status; } @@ -411,10 +504,16 @@ int ca_client_context::pendEvent ( const double & timeout ) epicsTime current = epicsTime::getCurrent (); - this->flushRequest (); + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->flush ( guard ); + } // process at least once if preemptive callback is disabled if ( this->pCallbackGuard.get() ) { + epicsGuardRelease < epicsMutex > unguard ( *this->pCallbackGuard ); + epicsGuard < epicsMutex > guard ( this->mutex ); + // // This is needed because in non-preemptive callback mode // legacy applications that use file descriptor managers @@ -423,11 +522,11 @@ int ca_client_context::pendEvent ( const double & timeout ) // been read. We must guarantee that other threads get a // chance to run if there is data in any of the sockets. // - epicsGuardRelease < epicsMutex > unguardcb ( *this->pCallbackGuard ); - epicsGuard < ca_client_context_mutex > guard ( this->mutex ); if ( this->fdRegFunc ) { - epicsGuardRelease < ca_client_context_mutex > unguard ( guard ); - // remove short udp message sent to wake up a file descriptor manager + epicsGuardRelease < epicsMutex > unguard ( guard ); + + // remove short udp message sent to wake + // up a file descriptor manager osiSockAddr tmpAddr; osiSocklen_t addrSize = sizeof ( tmpAddr.sa ); char buf = 0; @@ -439,7 +538,7 @@ int ca_client_context::pendEvent ( const double & timeout ) } this->noWakeupSincePend = true; while ( this->callbackThreadsPending > 0 ) { - epicsGuardRelease < ca_client_context_mutex > unguard ( guard ); + epicsGuardRelease < epicsMutex > unguard ( guard ); this->callbackThreadActivityComplete.wait ( 30.0 ); } } @@ -486,7 +585,7 @@ void ca_client_context::callbackLock () if ( this->pCallbackGuard.get() ) { bool sendNeeded = false; { - epicsGuard < ca_client_context_mutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); this->callbackThreadsPending++; if ( this->fdRegFunc && this->noWakeupSincePend ) { this->noWakeupSincePend = false; @@ -517,7 +616,7 @@ void ca_client_context::callbackUnlock () if ( this->pCallbackGuard.get() ) { bool signalNeeded = false; { - epicsGuard < ca_client_context_mutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); if ( this->callbackThreadsPending <= 1 ) { if ( this->callbackThreadsPending == 1 ) { this->callbackThreadsPending = 0; @@ -537,71 +636,101 @@ void ca_client_context::callbackUnlock () void ca_client_context::changeConnCallBack ( caCh * pfunc, caCh * & pConnCallBack, const bool & currentlyConnected ) { - epicsGuard < epicsMutex > callbackGuard ( this->callbackMutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); if ( ! currentlyConnected ) { if ( pfunc ) { if ( ! pConnCallBack ) { - this->decrementOutstandingIO ( this->ioSeqNo ); + this->decrementOutstandingIO ( guard, this->ioSeqNo ); } } else { if ( pConnCallBack ) { - this->incrementOutstandingIO ( this->ioSeqNo ); + this->incrementOutstandingIO ( guard, this->ioSeqNo ); } } } pConnCallBack = pfunc; } -void ca_client_context::registerService ( cacService &service ) +cacChannel & ca_client_context::createChannel ( + epicsGuard < epicsMutex > & guard, const char * pChannelName, + oldChannelNotify & chan, cacChannel::priLev pri ) { - this->pClientCtx->registerService ( service ); + guard.assertIdenticalMutex ( this->mutex ); + return this->pServiceContext->createChannel ( + guard, pChannelName, chan, pri ); } -cacChannel & ca_client_context::createChannel ( const char * name_str, - oldChannelNotify & chan, cacChannel::priLev pri ) +void ca_client_context::flush ( epicsGuard < epicsMutex > & guard ) { - return this->pClientCtx->createChannel ( name_str, chan, pri ); + this->pServiceContext->flush ( guard ); } -void ca_client_context::flushRequest () +unsigned ca_client_context::circuitCount () const { - this->pClientCtx->flushRequest (); -} - -unsigned ca_client_context::connectionCount () const -{ - return this->pClientCtx->connectionCount (); + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->pServiceContext->circuitCount ( guard ); } unsigned ca_client_context::beaconAnomaliesSinceProgramStart () const { - return this->pClientCtx->beaconAnomaliesSinceProgramStart (); + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->pServiceContext->beaconAnomaliesSinceProgramStart ( guard ); } -CASG * ca_client_context::lookupCASG ( unsigned id ) +void ca_client_context::installCASG ( + epicsGuard < epicsMutex > & guard, CASG & sg ) { - return this->pClientCtx->lookupCASG ( id ); + guard.assertIdenticalMutex ( this->mutex ); + this->sgTable.add ( sg ); } -void ca_client_context::installCASG ( CASG &sg ) +void ca_client_context::uninstallCASG ( + epicsGuard < epicsMutex > & guard, CASG & sg ) { - this->pClientCtx->installCASG ( sg ); + guard.assertIdenticalMutex ( this->mutex ); + this->sgTable.remove ( sg ); } -void ca_client_context::uninstallCASG ( CASG &sg ) +CASG * ca_client_context::lookupCASG ( + epicsGuard < epicsMutex > & guard, unsigned idIn ) { - this->pClientCtx->uninstallCASG ( sg ); + guard.assertIdenticalMutex ( this->mutex ); + CASG * psg = this->sgTable.lookup ( idIn ); + if ( psg ) { + if ( ! psg->verify ( guard ) ) { + psg = 0; + } + } + return psg; } -void ca_client_context::vSignal ( int ca_status, const char *pfilenm, - int lineno, const char *pFormat, va_list args ) +void ca_client_context::selfTest () const { - this->pClientCtx->vSignal ( ca_status, pfilenm, - lineno, pFormat, args ); + epicsGuard < epicsMutex > guard ( this->mutex ); + this->sgTable.verify (); + this->pServiceContext->selfTest ( guard ); } -void ca_client_context::selfTest () +epicsMutex & ca_client_context::mutexRef () const { - this->pClientCtx->selfTest (); + return this->mutex; } + +cacContext & ca_client_context::createNetworkContext ( epicsMutex & mutex ) +{ + return * new cac ( mutex, *this ); +} + + +void epicsShareAPI caInstallDefaultService ( cacService & service ) +{ + // this wont consistently work if called from file scope constructor + epicsGuard < epicsMutex > guard ( ca_client_context::defaultServiceInstallMutex ); + if ( ca_client_context::pDefaultService ) { + throw std::logic_error ( "CA in-memory service already installed and can't be replaced"); + } + ca_client_context::pDefaultService = & service; +} + + diff --git a/src/ca/cac.cpp b/src/ca/cac.cpp index 1953fe1ee..d8aeaeb18 100644 --- a/src/ca/cac.cpp +++ b/src/ca/cac.cpp @@ -42,7 +42,6 @@ #include "udpiiu.h" #include "bhe.h" #include "net_convert.h" -#include "autoPtrDestroy.h" #include "autoPtrFreeList.h" static const char *pVersionCAC = @@ -70,7 +69,7 @@ const cac::pProtoStubTCP cac::tcpJumpTableCAC [] = &cac::readNotifyRespAction, &cac::badTCPRespAction, &cac::badTCPRespAction, - &cac::claimCIURespAction, + &cac::createChannelRespAction, &cac::writeNotifyRespAction, &cac::badTCPRespAction, &cac::badTCPRespAction, @@ -135,11 +134,11 @@ extern "C" void cacOnceFunc ( void * ) // // cac::cac () // -cac::cac ( cacNotify & notifyIn ) : +cac::cac ( epicsMutex & mutexIn, cacContextNotify & notifyIn ) : programBeginTime ( epicsTime::getCurrent() ), connTMO ( CA_CONN_VERIFY_PERIOD ), cbMutex ( notifyIn ), - globalServiceList ( globalServiceListCAC.getReference () ), + mutex ( mutexIn ), ipToAEngine ( ipAddrToAsciiEngine::allocate () ), timerQueue ( epicsTimerQueueActive::allocate ( false, lowestPriorityLevelAbove(epicsThreadGetPrioritySelf()) ) ), @@ -248,18 +247,16 @@ cac::~cac () // get the lock. if ( this->pudpiiu ) { this->pudpiiu->shutdown (); - } - // - // shutdown all tcp circuits - // - { + // + // shutdown all tcp circuits + // epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); tsDLIter < tcpiiu > iter = this->serverList.firstIter (); while ( iter.valid() ) { // this causes a clean shutdown to occur - iter->removeAllChannels ( cbGuard, guard, *this ); + iter->removeAllChannels ( cbGuard, guard, *this->pudpiiu ); iter++; } } @@ -271,9 +268,9 @@ cac::~cac () // hold a lock while waiting // { - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); while ( this->serverList.count() ) { - epicsGuardRelease < cacMutex > unguard ( guard ); + epicsGuardRelease < epicsMutex > unguard ( guard ); this->iiuUninstall.wait (); } } @@ -335,25 +332,27 @@ unsigned cac::highestPriorityLevelBelow ( unsigned priority ) // // set the push pending flag on all virtual circuits // -void cac::flushRequest () +void cac::flush ( epicsGuard < epicsMutex > & guard ) { - epicsGuard < cacMutex > guard ( this->mutex ); + guard.assertIdenticalMutex ( this->mutex ); tsDLIter < tcpiiu > iter = this->serverList.firstIter (); - while ( iter.valid() ) { - iter->flushRequest (); + while ( iter.valid () ) { + iter->flushRequest ( guard ); iter++; } } -unsigned cac::connectionCount () const +unsigned cac::circuitCount ( + epicsGuard < epicsMutex > & guard ) const { - epicsGuard < cacMutex > guard ( this->mutex ); + guard.assertIdenticalMutex ( this->mutex ); return this->serverList.count (); } -void cac::show ( unsigned level ) const +void cac::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const { - epicsGuard < cacMutex > autoMutex2 ( this->mutex ); + guard.assertIdenticalMutex ( this->mutex ); ::printf ( "Channel Access Client Context at %p for user %s\n", static_cast ( this ), this->pUserName ); @@ -364,8 +363,6 @@ void cac::show ( unsigned level ) const if ( level > 0u ) { this->serverTable.show ( level - 1u ); ::printf ( "\tconnection time out watchdog period %f\n", this->connTMO ); - ::printf ( "list of installed services:\n" ); - this->services.show ( level - 1u ); } if ( level > 1u ) { @@ -381,8 +378,6 @@ void cac::show ( unsigned level ) const this->chanTable.show ( level - 3u ); ::printf ( "IO identifier hash table:\n" ); this->ioTable.show ( level - 3u ); - ::printf ( "Synchronous group identifier hash table:\n" ); - this->sgTable.show ( level - 3u ); ::printf ( "Beacon source identifier hash table:\n" ); this->beaconTable.show ( level - 3u ); ::printf ( "Timer queue:\n" ); @@ -405,7 +400,7 @@ void cac::show ( unsigned level ) const void cac::beaconNotify ( const inetAddrID & addr, const epicsTime & currentTime, ca_uint32_t beaconNumber, unsigned protocolRevision ) { - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); if ( ! this->pudpiiu ) { return; @@ -419,8 +414,8 @@ void cac::beaconNotify ( const inetAddrID & addr, const epicsTime & currentTime, /* * return if the beacon period has not changed significantly */ - if ( ! pBHE->updatePeriod ( this->programBeginTime, currentTime, - beaconNumber, protocolRevision ) ) { + if ( ! pBHE->updatePeriod ( guard, this->programBeginTime, + currentTime, beaconNumber, protocolRevision ) ) { return; } } @@ -433,7 +428,7 @@ void cac::beaconNotify ( const inetAddrID & addr, const epicsTime & currentTime, * shortly after the program started up) */ pBHE = new ( this->bheFreeList ) - bhe ( currentTime, beaconNumber, addr ); + bhe ( this->mutex, currentTime, beaconNumber, addr ); if ( pBHE ) { if ( this->beaconTable.add ( *pBHE ) < 0 ) { pBHE->~bhe (); @@ -456,38 +451,12 @@ void cac::beaconNotify ( const inetAddrID & addr, const epicsTime & currentTime, # endif } -void cac::installCASG ( CASG &sg ) -{ - epicsGuard < cacMutex > guard ( this->mutex ); - this->sgTable.add ( sg ); -} - -void cac::uninstallCASG ( CASG & sg ) -{ - epicsGuard < cacMutex > guard ( this->mutex ); - this->sgTable.remove ( sg ); -} - -CASG * cac::lookupCASG ( unsigned idIn ) -{ - epicsGuard < cacMutex > guard ( this->mutex ); - CASG * psg = this->sgTable.lookup ( idIn ); - if ( psg ) { - if ( ! psg->verify () ) { - psg = 0; - } - } - return psg; -} - -void cac::registerService ( cacService & service ) -{ - this->services.registerService ( service ); -} - -cacChannel & cac::createChannel ( const char * pName, +cacChannel & cac::createChannel ( + epicsGuard < epicsMutex > & guard, const char * pName, cacChannelNotify & chan, cacChannel::priLev pri ) { + guard.assertIdenticalMutex ( this->mutex ); + if ( pri > cacChannel::priorityMax ) { throw cacChannel::badPriority (); } @@ -496,23 +465,15 @@ cacChannel & cac::createChannel ( const char * pName, throw cacChannel::badString (); } - autoPtrDestroy < cacChannel > - pIO ( this->services.createChannel ( pName, chan, pri ) ); - if ( pIO.get() == 0 ) { - pIO = this->globalServiceList->createChannel ( pName, chan, pri ); - if ( pIO.get() == 0 ) { - epicsGuard < cacMutex > guard ( this->mutex ); - if ( ! this->pudpiiu ) { - this->pudpiiu = new udpiiu ( this->timerQueue, this->cbMutex, *this ); - } - autoPtrDestroy < nciu > pNetChan - ( new ( this->channelFreeList ) - nciu ( *this, *this->pudpiiu, chan, pName, pri ) ); - this->chanTable.add ( *pNetChan ); - return * pNetChan.release (); - } + if ( ! this->pudpiiu ) { + this->pudpiiu = new udpiiu ( + this->timerQueue, this->cbMutex, *this ); } - return * pIO.release (); + + nciu * pNetChan = new ( this->channelFreeList ) + nciu ( *this, *this->pudpiiu, chan, pName, pri ); + this->chanTable.add ( *pNetChan ); + return *pNetChan; } void cac::repeaterSubscribeConfirmNotify () @@ -535,7 +496,7 @@ bool cac::transferChanToVirtCircuit ( } { - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); /* * ignore search replies for deleted channels @@ -582,14 +543,14 @@ bool cac::transferChanToVirtCircuit ( bhe * pBHE = this->beaconTable.lookup ( addr.ia ); if ( ! pBHE ) { pBHE = new ( this->bheFreeList ) - bhe ( epicsTime (), 0u, addr.ia ); + bhe ( this->mutex, epicsTime (), 0u, addr.ia ); if ( this->beaconTable.add ( *pBHE ) < 0 ) { return false; } } this->serverTable.add ( *pnewiiu ); this->serverList.add ( *pnewiiu ); - pBHE->registerIIU ( *pnewiiu ); + pBHE->registerIIU ( guard, *pnewiiu ); piiu = pnewiiu.release (); newIIU = true; } @@ -602,7 +563,7 @@ bool cac::transferChanToVirtCircuit ( } } - this->pudpiiu->uninstallChan ( cbGuard, guard, *pChan ); + this->pudpiiu->uninstallChan ( guard, *pChan ); piiu->installChannel ( cbGuard, guard, *pChan, sid, typeCode, count ); if ( ! piiu->ca_v42_ok () ) { @@ -618,94 +579,70 @@ bool cac::transferChanToVirtCircuit ( return true; } -void cac::destroyChannel ( nciu & chan ) +void cac::destroyChannel ( + epicsGuard < epicsMutex > & guard, nciu & chan ) { - tsDLList < baseNMIU > tmpList; + guard.assertIdenticalMutex ( this->mutex ); - // uninstall channel and any subsiderary IO so that recv threads - // will not start a new callback for this channel's IO. Send any - // side effect IO requests w/o holding the callback lock so that - // we do not dead lock - { - epicsGuard < cacMutex > guard ( this->mutex ); + // uninstall channel so that recv threads + // will not start a new callback for this channel's IO. + if ( this->chanTable.remove ( chan ) != & chan ) { + errlogPrintf ( + "CAC: Attemt to uninstall unregisterred channel ID=%u ignored.\n", + chan.getId () ); + return; + } - // if the send backlog is too high send some frames before we get entagled - // in the channel shutdown sequence below. There is special protection in - // this routine that releases the callback lock if we are already holding it - // when this is the tcp receive thread or if this is the main thread and - // preemptive callback is disabled. - this->flushIfRequired ( guard, *chan.getPIIU() ); - - // unregister the channel - if ( this->chanTable.remove ( chan ) != &chan ) { - errlogPrintf ( - "CAC: Attemt to uninstall unregisterred channel ID=%u ignored.\n", - chan.getId () ); - return; - } - - // for each outstanding IO - // - // IO must be removed from the list and also uninstalled while holding the - // lock so that ioCancel() does not break in while postponing the destroy - // waiting for outstanding callbacks to complete - // - while ( baseNMIU *pIO = chan.cacPrivateListOfIO::eventq.get() ) { - // unregister IO class - if ( pIO != this->ioTable.remove ( *pIO ) ) { - errlogPrintf ( - "CAC: Unregister IO ID=%u found when uninstalling channel?\n", - pIO->getId () ); - continue; - } - // connected subscriptions must be canceled in the server - class netSubscription *pSubscr = pIO->isSubscription (); - if ( pSubscr && chan.connected() ) { - // we will deadlock if we hold the callback lock here - chan.getPIIU()->subscriptionCancelRequest ( guard, chan, *pSubscr ); - } - tmpList.add ( *pIO ); - } - - // if the claim reply has not returned yet then we will issue - // the clear channel request to the server when the claim reply - // arrives and there is no matching nciu in the client - if ( chan.connected() ) { - chan.getPIIU()->clearChannelRequest ( guard, chan.getSID(), chan.getCID() ); - } + // if the claim reply has not returned yet then we will issue + // the clear channel request to the server when the claim reply + // arrives and there is no matching nciu in the client + if ( chan.connected ( guard ) ) { + chan.getPIIU()->clearChannelRequest ( + guard, chan.getSID(), chan.getCID() ); } { - // taking this mutex prior to deleting the IO and channel guarantees - // that we will not delete a channel out from under a callback - epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); - - // destroy subsiderary IO now that it is safe to do so - while ( baseNMIU *pIO = tmpList.get() ) { - // If they call ioCancel() here it will be ignored - // because the IO has been unregistered above. - // This must be done after outstanding callbacks - // for this channel have completed. - pIO->exception ( ECA_CHANDESTROY, chan.pName() ); - pIO->destroy ( *this ); - } - - // this must be done after the following - // o subscription cancel requests - // o clear channel request - // o outstanding callbacks using this channel have completed - // o chan destroy exception has been delivered + // reverse the lock order so that we dont botch the lock hierarchy + epicsGuardRelease < epicsMutex > unguard ( guard ); { - epicsGuard < cacMutex > guard ( this->mutex ); - chan.getPIIU()->uninstallChan ( cbGuard, guard, chan ); + // taking the callback mutex prior to deleting the channel + // guarantees that we will not delete a channel out from under a callback + epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); } } - + // run channel's destructor and return it to the free list - chan.~nciu (); + chan.destructor ( guard ); + + // IIU must be valid until after IO is destroyed in the destructor + chan.getPIIU()->uninstallChan ( guard, chan ); + this->channelFreeList.release ( & chan ); } +void cac::disconnectAllIO ( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < epicsMutex > & guard, + nciu & chan, tsDLList < baseNMIU > & ioList ) +{ + cbGuard.assertIdenticalMutex ( this->cbMutex ); + guard.assertIdenticalMutex ( this->mutex ); + + char buf[128]; + sprintf ( buf, "host = %.100s", chan.pHostName() ); + + tsDLIter < baseNMIU > pNetIO = ioList.firstIter(); + while ( pNetIO.valid () ) { + tsDLIter < baseNMIU > pNext = pNetIO; + pNext++; + if ( ! pNetIO->isSubscription() ) { + this->ioTable.remove ( pNetIO->getId () ); + } + pNetIO->exception ( guard, *this, ECA_DISCONN, buf ); + pNetIO = pNext; + } +} + int cac::printf ( const char *pformat, ... ) const { va_list theArgs; @@ -720,11 +657,12 @@ int cac::printf ( const char *pformat, ... ) const return status; } -// lock must be applied before calling this cac private routine -void cac::flushIfRequired ( epicsGuard < cacMutex > & guard, netiiu & iiu ) +void cac::flushIfRequired ( epicsGuard < epicsMutex > & guard, netiiu & iiu ) { + guard.assertIdenticalMutex ( this->mutex ); + if ( iiu.flushBlockThreshold ( guard ) ) { - iiu.flushRequest (); + iiu.flushRequest ( guard ); // the process thread is not permitted to flush as this // can result in a push / pull deadlock on the TCP pipe. // Instead, the process thread scheduals the flush with the @@ -743,335 +681,147 @@ void cac::flushIfRequired ( epicsGuard < cacMutex > & guard, netiiu & iiu ) } } -void cac::writeRequest ( nciu & chan, unsigned type, arrayElementCount nElem, const void * pValue ) +void cac::writeRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, unsigned type, + arrayElementCount nElem, const void * pValue ) { - epicsGuard < cacMutex > guard ( this->mutex ); + guard.assertIdenticalMutex ( this->mutex ); this->flushIfRequired ( guard, *chan.getPIIU() ); chan.getPIIU()->writeRequest ( guard, chan, type, nElem, pValue ); } -cacChannel::ioid -cac::writeNotifyRequest ( nciu & chan, unsigned type, // X aCC 361 - arrayElementCount nElem, const void * pValue, cacWriteNotify & notifyIn ) +netWriteNotifyIO & cac::writeNotifyRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, privateInterfaceForIO & icni, + unsigned type, arrayElementCount nElem, const void * pValue, cacWriteNotify & notifyIn ) { - epicsGuard < cacMutex > guard ( this->mutex ); - autoPtrRecycle < netWriteNotifyIO > pIO ( this->ioTable, chan.cacPrivateListOfIO::eventq, - *this, netWriteNotifyIO::factory ( this->freeListWriteNotifyIO, chan, notifyIn ) ); + guard.assertIdenticalMutex ( this->mutex ); + autoPtrRecycle < netWriteNotifyIO > pIO ( + guard, this->ioTable, *this, + netWriteNotifyIO::factory ( this->freeListWriteNotifyIO, icni, notifyIn ) ); this->ioTable.add ( *pIO ); - chan.cacPrivateListOfIO::eventq.add ( *pIO ); this->flushIfRequired ( guard, *chan.getPIIU() ); chan.getPIIU()->writeNotifyRequest ( guard, chan, *pIO, type, nElem, pValue ); - return pIO.release()->getId (); + return *pIO.release(); } -cacChannel::ioid -cac::readNotifyRequest ( nciu & chan, unsigned type, // X aCC 361 - arrayElementCount nElem, cacReadNotify & notifyIn ) +netReadNotifyIO & cac::readNotifyRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, privateInterfaceForIO & icni, + unsigned type, arrayElementCount nElem, cacReadNotify & notifyIn ) { - epicsGuard < cacMutex > guard ( this->mutex ); - autoPtrRecycle < netReadNotifyIO > pIO ( this->ioTable, - chan.cacPrivateListOfIO::eventq, *this, - netReadNotifyIO::factory ( this->freeListReadNotifyIO, chan, notifyIn ) ); + guard.assertIdenticalMutex ( this->mutex ); + autoPtrRecycle < netReadNotifyIO > pIO ( + guard, this->ioTable, *this, + netReadNotifyIO::factory ( this->freeListReadNotifyIO, icni, notifyIn ) ); this->ioTable.add ( *pIO ); - chan.cacPrivateListOfIO::eventq.add ( *pIO ); this->flushIfRequired ( guard, *chan.getPIIU() ); chan.getPIIU()->readNotifyRequest ( guard, chan, *pIO, type, nElem ); - return pIO.release()->getId (); + return *pIO.release(); } -void cac::ioCancel ( nciu & chan, const cacChannel::ioid & idIn ) +baseNMIU * cac::destroyIO ( + epicsGuard < epicsMutex > & guard, + const cacChannel::ioid & idIn, nciu & chan ) { - baseNMIU * pmiu; - + guard.assertIdenticalMutex ( this->mutex ); // unistall the IO object so that a receive thread will not find it, // but do _not_ hold the callback lock here because this could result // in deadlock - { - epicsGuard < cacMutex > guard ( this->mutex ); - pmiu = this->ioTable.remove ( idIn ); - if ( ! pmiu ) { - return; + baseNMIU * pIO = this->ioTable.remove ( idIn ); + if ( pIO ) { + class netSubscription * pSubscr = pIO->isSubscription (); + if ( pSubscr && chan.connected ( guard ) ) { + chan.getPIIU()->subscriptionCancelRequest ( + guard, chan, *pSubscr ); } - class netSubscription *pSubscr = pmiu->isSubscription (); - if ( pSubscr ) { - this->flushIfRequired ( guard, *chan.getPIIU() ); - if ( chan.connected() ) { - chan.getPIIU()->subscriptionCancelRequest ( guard, chan, *pSubscr ); + + { + // reverse the lock order so that we dont botch the lock hierarchy + epicsGuardRelease < epicsMutex > unguard ( guard ); + { + // taking the callback mutex prior to deleting the IO and channel + // guarantees that we will not delete a channel out from under a callback + epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); + epicsGuard < epicsMutex > tmpGuard ( this->mutexRef () ); + // this uninstalls from the list and destroys the IO + pIO->exception ( tmpGuard, *this, + ECA_CHANDESTROY, chan.pName() ); } } - // must be uninstalled and also removed from the table - // while holding the lock to prevent a channel delete - // from destroying this IO object after we release the lock - chan.cacPrivateListOfIO::eventq.remove ( *pmiu ); - } - // wait for any IO callbacks in progress to complete - // prior to destroying the IO object - { - epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); - } - // now it is safe to destroy the IO object - { - epicsGuard < cacMutex > guard ( this->mutex ); - pmiu->destroy ( *this ); - } + } + return pIO; } void cac::ioShow ( const cacChannel::ioid & idIn, unsigned level ) const { - epicsGuard < cacMutex > autoMutex ( this->mutex ); + epicsGuard < epicsMutex > autoMutex ( this->mutex ); baseNMIU * pmiu = this->ioTable.lookup ( idIn ); if ( pmiu ) { pmiu->show ( level ); } } -void cac::ioCompletionNotify ( unsigned idIn, unsigned type, - arrayElementCount count, const void *pData ) +void cac::ioExceptionNotify ( + unsigned idIn, int status, const char * pContext, + unsigned type, arrayElementCount count ) { - baseNMIU * pmiu; - - { - epicsGuard < cacMutex > autoMutex ( this->mutex ); - pmiu = this->ioTable.lookup ( idIn ); - if ( ! pmiu ) { - return; - } + epicsGuard < epicsMutex > guard ( this->mutex ); + baseNMIU * pmiu = this->ioTable.lookup ( idIn ); + if ( pmiu ) { + pmiu->exception ( guard, *this, status, pContext, type, count ); } - - // - // The IO destroy routines take the call back mutex - // when uninstalling and deleting the baseNMIU so there is - // no need to worry here about the baseNMIU being deleted while - // it is in use here. - // - pmiu->completion ( type, count, pData ); } -void cac::ioExceptionNotify ( unsigned idIn, int status, const char *pContext ) +void cac::ioExceptionNotifyAndUninstall ( + unsigned idIn, int status, const char * pContext, + unsigned type, arrayElementCount count ) { - baseNMIU * pmiu; - { - epicsGuard < cacMutex > autoMutex ( this->mutex ); - pmiu = this->ioTable.lookup ( idIn ); - } - - if ( ! pmiu ) { - return; - } - - // - // The IO destroy routines take the call back mutex - // when uninstalling and deleting the baseNMIU so there is - // no need to worry here about the baseNMIU being deleted while - // it is in use here. - // - - pmiu->exception ( status, pContext ); -} - -void cac::ioExceptionNotify ( unsigned idIn, int status, - const char *pContext, unsigned type, arrayElementCount count ) -{ - baseNMIU * pmiu; - - { - epicsGuard < cacMutex > autoMutex ( this->mutex ); - pmiu = this->ioTable.lookup ( idIn ); - if ( ! pmiu ) { - return; - } - } - - // - // The IO destroy routines take the call back mutex - // when uninstalling and deleting the baseNMIU so there is - // no need to worry here about the baseNMIU being deleted while - // it is in use here. - // - pmiu->exception ( status, pContext, type, count ); -} - -void cac::ioCompletionNotifyAndDestroy ( unsigned idIn ) -{ - epicsGuard < cacMutex > autoMutex ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); baseNMIU * pmiu = this->ioTable.remove ( idIn ); - if ( ! pmiu ) { - return; - } - - pmiu->channel().cacPrivateListOfIO::eventq.remove ( *pmiu ); - - // - // The IO destroy routines take the call back mutex - // when uninstalling and deleting the baseNMIU so there is - // no need to worry here about the baseNMIU being deleted while - // it is in use here. - // - { - epicsGuardRelease < cacMutex > autoMutexRelease ( autoMutex ); - pmiu->completion (); - } - - pmiu->destroy ( *this ); -} - -void cac::ioCompletionNotifyAndDestroy ( unsigned idIn, - unsigned type, arrayElementCount count, const void *pData ) -{ - epicsGuard < cacMutex > autoMutex ( this->mutex ); - baseNMIU * pmiu = this->ioTable.remove ( idIn ); - if ( ! pmiu ) { - return; - } - pmiu->channel().cacPrivateListOfIO::eventq.remove ( *pmiu ); - - // - // The IO destroy routines take the call back mutex - // when uninstalling and deleting the baseNMIU so there is - // no need to worry here about the baseNMIU being deleted while - // it is in use here. - // - { - epicsGuardRelease < cacMutex > autoMutexRelease ( autoMutex ); - pmiu->completion ( type, count, pData ); - } - - pmiu->destroy ( *this ); -} - -void cac::ioExceptionNotifyAndDestroy ( unsigned idIn, int status, - const char *pContext ) -{ - epicsGuard < cacMutex > autoMutex ( this->mutex ); - baseNMIU * pmiu = this->ioTable.remove ( idIn ); - if ( ! pmiu ) { - return; - } - pmiu->channel().cacPrivateListOfIO::eventq.remove ( *pmiu ); - - // - // The IO destroy routines take the call back mutex - // when uninstalling and deleting the baseNMIU so there is - // no need to worry here about the baseNMIU being deleted while - // it is in use here. - // - { - epicsGuardRelease < cacMutex > autoMutexRelease ( autoMutex ); - pmiu->exception ( status, pContext ); - } - - pmiu->destroy ( *this ); -} - -void cac::ioExceptionNotifyAndDestroy ( unsigned idIn, int status, - const char *pContext, unsigned type, arrayElementCount count ) -{ - epicsGuard < cacMutex > autoMutex ( this->mutex ); - baseNMIU * pmiu = this->ioTable.remove ( idIn ); - if ( ! pmiu ) { - return; - } - pmiu->channel().cacPrivateListOfIO::eventq.remove ( *pmiu ); - - // - // The IO destroy routines take the call back mutex - // when uninstalling and deleting the baseNMIU so there is - // no need to worry here about the baseNMIU being deleted while - // it is in use here. - // - - { - epicsGuardRelease < cacMutex > autoMutexRelease ( autoMutex ); - pmiu->exception ( status, pContext, type, count ); - } - - pmiu->destroy ( *this ); -} - -// resubscribe for monitors from this channel -// (always called from a udp thread) -void cac::connectAllIO ( epicsGuard < cacMutex > & guard, nciu & chan ) -{ - tsDLIter < baseNMIU > pNetIO = - chan.cacPrivateListOfIO::eventq.firstIter (); - while ( pNetIO.valid () ) { - tsDLIter < baseNMIU > next = pNetIO; - next++; - class netSubscription * pSubscr = pNetIO->isSubscription (); - // disconnected channels should have only subscription IO attached - assert ( pSubscr ); - try { - chan.getPIIU()->subscriptionRequest ( guard, chan, *pSubscr ); - } - catch ( ... ) { - this->printf ( "CAC: failed to send subscription request during channel connect\n" ); - } - pNetIO = next; - } - chan.getPIIU()->requestRecvProcessPostponedFlush (); -} - -// cancel IO operations and monitor subscriptions -// -- callback lock and cac lock must be applied here -void cac::disconnectAllIO ( epicsGuard < cacMutex > &locker, nciu & chan, bool enableCallbacks ) -{ - tsDLIter pNetIO = chan.cacPrivateListOfIO::eventq.firstIter(); - while ( pNetIO.valid() ) { - tsDLIter pNext = pNetIO; - pNext++; - if ( ! pNetIO->isSubscription() ) { - // no use after disconnected - so uninstall it - this->ioTable.remove ( *pNetIO ); - chan.cacPrivateListOfIO::eventq.remove ( *pNetIO ); - } - if ( enableCallbacks ) { - char buf[128]; - sprintf ( buf, "host = %.100s", chan.pHostName() ); - epicsGuardRelease < cacMutex > unlocker ( locker ); - pNetIO->exception ( ECA_DISCONN, buf ); - } - if ( ! pNetIO->isSubscription() ) { - pNetIO->destroy ( *this ); - } - pNetIO = pNext; + if ( pmiu ) { + pmiu->exception ( guard, *this, status, pContext, type, count ); } } -void cac::recycleReadNotifyIO ( netReadNotifyIO &io ) +void cac::recycleReadNotifyIO ( + epicsGuard < epicsMutex > & guard, netReadNotifyIO & io ) { + guard.assertIdenticalMutex ( this->mutex ); this->freeListReadNotifyIO.release ( & io ); } -void cac::recycleWriteNotifyIO ( netWriteNotifyIO &io ) +void cac::recycleWriteNotifyIO ( + epicsGuard < epicsMutex > & guard, netWriteNotifyIO & io ) { + guard.assertIdenticalMutex ( this->mutex ); this->freeListWriteNotifyIO.release ( & io ); } -void cac::recycleSubscription ( netSubscription &io ) +void cac::recycleSubscription ( + epicsGuard < epicsMutex > & guard, netSubscription & io ) { + guard.assertIdenticalMutex ( this->mutex ); this->freeListSubscription.release ( & io ); } -cacChannel::ioid -cac::subscriptionRequest ( nciu & chan, unsigned type, // X aCC 361 - arrayElementCount nElem, unsigned mask, - cacStateNotify & notifyIn ) +netSubscription & cac::subscriptionRequest ( + epicsGuard < epicsMutex > & guard, + nciu & chan, privateInterfaceForIO & privChan, + unsigned type, // X aCC 361 + arrayElementCount nElem, unsigned mask, + cacStateNotify & notifyIn ) { - epicsGuard < cacMutex > guard ( this->mutex ); - autoPtrRecycle < netSubscription > pIO ( this->ioTable, - chan.cacPrivateListOfIO::eventq, *this, + guard.assertIdenticalMutex ( this->mutex ); + autoPtrRecycle < netSubscription > pIO ( + guard, this->ioTable, *this, netSubscription::factory ( this->freeListSubscription, - chan, type, nElem, mask, notifyIn ) ); + privChan, type, nElem, mask, notifyIn ) ); this->ioTable.add ( *pIO ); - chan.cacPrivateListOfIO::eventq.add ( *pIO ); - if ( chan.connected () ) { + if ( chan.connected ( guard ) ) { this->flushIfRequired ( guard, *chan.getPIIU() ); chan.getPIIU()->subscriptionRequest ( guard, chan, *pIO ); } - cacChannel::ioid idOut = pIO->getId (); - pIO.release (); - return idOut; + return *pIO.release (); } bool cac::versionAction ( epicsGuard < callbackMutex > &, tcpiiu &, @@ -1080,27 +830,33 @@ bool cac::versionAction ( epicsGuard < callbackMutex > &, tcpiiu &, return true; } -bool cac::echoRespAction ( epicsGuard < callbackMutex > &, tcpiiu &, - const epicsTime &, const caHdrLargeArray &, void * ) +bool cac::echoRespAction ( + epicsGuard < callbackMutex > & cbGuard, tcpiiu & iiu, + const epicsTime & current, const caHdrLargeArray &, void * ) { + iiu.probeResponseNotify ( cbGuard, current ); return true; } -bool cac::writeNotifyRespAction ( epicsGuard < callbackMutex > &, tcpiiu &, - const epicsTime &, const caHdrLargeArray &hdr, void * ) +bool cac::writeNotifyRespAction ( + epicsGuard < callbackMutex > &, tcpiiu &, + const epicsTime &, const caHdrLargeArray & hdr, void * ) { - int caStatus = hdr.m_cid; - if ( caStatus == ECA_NORMAL ) { - this->ioCompletionNotifyAndDestroy ( hdr.m_available ); - } - else { - this->ioExceptionNotifyAndDestroy ( hdr.m_available, - caStatus, "write notify request rejected" ); + epicsGuard < epicsMutex > guard ( this->mutex ); + baseNMIU * pmiu = this->ioTable.remove ( hdr.m_available ); + if ( pmiu ) { + if ( hdr.m_cid == ECA_NORMAL ) { + pmiu->completion ( guard, *this ); + } + else { + pmiu->exception ( guard, *this, + hdr.m_cid, "write notify request rejected" ); + } } return true; } -bool cac::readNotifyRespAction ( epicsGuard < callbackMutex > &, tcpiiu &iiu, +bool cac::readNotifyRespAction ( epicsGuard < callbackMutex > &, tcpiiu & iiu, const epicsTime &, const caHdrLargeArray & hdr, void * pMsgBdy ) { /* @@ -1129,13 +885,30 @@ bool cac::readNotifyRespAction ( epicsGuard < callbackMutex > &, tcpiiu &iiu, } # endif - if ( caStatus == ECA_NORMAL ) { - this->ioCompletionNotifyAndDestroy ( hdr.m_available, - hdr.m_dataType, hdr.m_count, pMsgBdy ); - } - else { - this->ioExceptionNotifyAndDestroy ( hdr.m_available, - caStatus, "read failed", hdr.m_dataType, hdr.m_count ); + epicsGuard < epicsMutex > guard ( this->mutex ); + baseNMIU * pmiu = this->ioTable.remove ( hdr.m_available ); + // + // The IO destroy routines take the call back mutex + // when uninstalling and deleting the baseNMIU so there is + // no need to worry here about the baseNMIU being deleted while + // it is in use here. + // + if ( pmiu ) { + // if its a circuit-becomes-responsive subscription update + // then we need to reinstall the IO into the table + netSubscription * pSubscr = pmiu->isSubscription (); + if ( pSubscr ) { + this->ioTable.add ( *pmiu ); + } + if ( caStatus == ECA_NORMAL ) { + pmiu->completion ( guard, *this, + hdr.m_dataType, hdr.m_count, pMsgBdy ); + } + else { + pmiu->exception ( guard, *this, + caStatus, "read failed", + hdr.m_dataType, hdr.m_count ); + } } return true; } @@ -1177,14 +950,24 @@ bool cac::eventRespAction ( epicsGuard < callbackMutex > &, tcpiiu &iiu, } # endif - if ( caStatus == ECA_NORMAL ) { - this->ioCompletionNotify ( hdr.m_available, - hdr.m_dataType, hdr.m_count, pMsgBdy ); - } - else { - this->ioExceptionNotify ( hdr.m_available, - caStatus, "subscription update failed", + // + // The IO destroy routines take the call back mutex + // when uninstalling and deleting the baseNMIU so there is + // no need to worry here about the baseNMIU being deleted while + // it is in use here. + // + epicsGuard < epicsMutex > guard ( this->mutex ); + baseNMIU * pmiu = this->ioTable.lookup ( hdr.m_available ); + if ( pmiu ) { + if ( caStatus == ECA_NORMAL ) { + pmiu->completion ( guard, *this, + hdr.m_dataType, hdr.m_count, pMsgBdy ); + } + else { + pmiu->exception ( guard, *this, caStatus, + "subscription update read failed", hdr.m_dataType, hdr.m_count ); + } } return true; } @@ -1192,8 +975,18 @@ bool cac::eventRespAction ( epicsGuard < callbackMutex > &, tcpiiu &iiu, bool cac::readRespAction ( epicsGuard < callbackMutex > &, tcpiiu &, const epicsTime &, const caHdrLargeArray & hdr, void * pMsgBdy ) { - this->ioCompletionNotifyAndDestroy ( hdr.m_available, - hdr.m_dataType, hdr.m_count, pMsgBdy ); + epicsGuard < epicsMutex > guard ( this->mutex ); + baseNMIU * pmiu = this->ioTable.remove ( hdr.m_available ); + // + // The IO destroy routines take the call back mutex + // when uninstalling and deleting the baseNMIU so there is + // no need to worry here about the baseNMIU being deleted while + // it is in use here. + // + if ( pmiu ) { + pmiu->completion ( guard, *this, + hdr.m_dataType, hdr.m_count, pMsgBdy ); + } return true; } @@ -1203,21 +996,34 @@ bool cac::clearChannelRespAction ( epicsGuard < callbackMutex > &, tcpiiu &, return true; // currently a noop } -bool cac::defaultExcep ( epicsGuard < callbackMutex > &, tcpiiu &iiu, - const caHdrLargeArray &, - const char *pCtx, unsigned status ) +bool cac::defaultExcep ( + epicsGuard < callbackMutex > &, tcpiiu & iiu, + const caHdrLargeArray &, const char * pCtx, unsigned status ) { char buf[512]; char hostName[64]; iiu.hostName ( hostName, sizeof ( hostName ) ); sprintf ( buf, "host=%s ctx=%.400s", hostName, pCtx ); - this->notify.exception ( status, buf, 0, 0u ); + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->notify.exception ( guard, status, buf, 0, 0u ); + } return true; } -bool cac::eventAddExcep ( epicsGuard < callbackMutex > &, tcpiiu & /* iiu */, - const caHdrLargeArray &hdr, - const char *pCtx, unsigned status ) +void cac::exception ( + epicsGuard < callbackMutex > & cbGuard, int status, + const char *pContext, const char *pFileName, unsigned lineNo ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->notify.exception ( guard, status, pContext, + pFileName, lineNo ); +} + +bool cac::eventAddExcep ( + epicsGuard < callbackMutex > &, tcpiiu & /* iiu */, + const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ) { this->ioExceptionNotify ( hdr.m_available, status, pCtx, hdr.m_dataType, hdr.m_count ); @@ -1225,22 +1031,23 @@ bool cac::eventAddExcep ( epicsGuard < callbackMutex > &, tcpiiu & /* iiu */, } bool cac::readExcep ( epicsGuard < callbackMutex > &, tcpiiu &, - const caHdrLargeArray &hdr, - const char *pCtx, unsigned status ) + const caHdrLargeArray & hdr, + const char * pCtx, unsigned status ) { - this->ioExceptionNotifyAndDestroy ( hdr.m_available, + this->ioExceptionNotifyAndUninstall ( hdr.m_available, status, pCtx, hdr.m_dataType, hdr.m_count ); return true; } -bool cac::writeExcep ( epicsGuard < callbackMutex > &cbLocker, // X aCC 431 - tcpiiu &, - const caHdrLargeArray &hdr, - const char *pCtx, unsigned status ) +bool cac::writeExcep ( + epicsGuard < callbackMutex > & cbGuard, // X aCC 431 + tcpiiu &, const caHdrLargeArray & hdr, + const char * pCtx, unsigned status ) { + epicsGuard < epicsMutex > guard ( this->mutex ); nciu * pChan = this->chanTable.lookup ( hdr.m_available ); if ( pChan ) { - pChan->writeException ( cbLocker, status, pCtx, + pChan->writeException ( cbGuard, guard, status, pCtx, hdr.m_dataType, hdr.m_count ); } return true; @@ -1250,7 +1057,7 @@ bool cac::readNotifyExcep ( epicsGuard < callbackMutex > &, tcpiiu &, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ) { - this->ioExceptionNotifyAndDestroy ( hdr.m_available, + this->ioExceptionNotifyAndUninstall ( hdr.m_available, status, pCtx, hdr.m_dataType, hdr.m_count ); return true; } @@ -1259,7 +1066,7 @@ bool cac::writeNotifyExcep ( epicsGuard < callbackMutex > &, tcpiiu &, const caHdrLargeArray &hdr, const char *pCtx, unsigned status ) { - this->ioExceptionNotifyAndDestroy ( hdr.m_available, + this->ioExceptionNotifyAndUninstall ( hdr.m_available, status, pCtx, hdr.m_dataType, hdr.m_count ); return true; } @@ -1308,7 +1115,7 @@ bool cac::accessRightsRespAction ( epicsGuard < callbackMutex > & cbGuard, tcpiiu &, // X aCC 431 const epicsTime &, const caHdrLargeArray & hdr, void * /* pMsgBdy */ ) { - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); if ( pChan ) { unsigned ar = hdr.m_available; @@ -1321,11 +1128,11 @@ bool cac::accessRightsRespAction ( return true; } -bool cac::claimCIURespAction ( +bool cac::createChannelRespAction ( epicsGuard < callbackMutex > &cbGuard, tcpiiu & iiu, // X aCC 431 const epicsTime &, const caHdrLargeArray & hdr, void * /* pMsgBdy */ ) { - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); if ( pChan ) { unsigned sidTmp; @@ -1335,9 +1142,7 @@ bool cac::claimCIURespAction ( else { sidTmp = pChan->getSID (); } - - // the callback lock is taken when a channel is unistalled or when - // is disconnected to prevent race conditions here + iiu.connectNotify ( guard, *pChan ); pChan->connect ( hdr.m_dataType, hdr.m_count, sidTmp, cbGuard, guard ); } @@ -1354,7 +1159,7 @@ bool cac::verifyAndDisconnectChan ( epicsGuard < callbackMutex > & cbGuard, tcpiiu & /* iiu */, const epicsTime & currentTime, const caHdrLargeArray & hdr, void * /* pMsgBdy */ ) { - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); if ( ! pChan ) { return true; @@ -1366,13 +1171,15 @@ bool cac::verifyAndDisconnectChan ( void cac::disconnectChannel ( const epicsTime & currentTime, epicsGuard < callbackMutex > & cbGuard, // X aCC 431 - epicsGuard < cacMutex > & guard, nciu & chan ) + epicsGuard < epicsMutex > & guard, nciu & chan ) { + guard.assertIdenticalMutex ( this->mutex ); assert ( this->pudpiiu ); - this->disconnectAllIO ( guard, chan, true ); - chan.getPIIU()->uninstallChan ( cbGuard, guard, chan ); - this->pudpiiu->installDisconnectedChannel ( currentTime, chan ); - chan.circuitHangupNotify ( *this->pudpiiu, cbGuard, guard ); + chan.disconnectAllIO ( cbGuard, guard ); + chan.getPIIU()->uninstallChan ( guard, chan ); + this->pudpiiu->installDisconnectedChannel ( chan ); + chan.setServerAddressUnknown ( *this->pudpiiu, guard ); + chan.unresponsiveCircuitNotify ( cbGuard, guard ); } bool cac::badTCPRespAction ( epicsGuard < callbackMutex > &, tcpiiu & iiu, @@ -1399,86 +1206,15 @@ bool cac::executeResponse ( epicsGuard < callbackMutex > & cbLocker, tcpiiu & ii return ( this->*pStub ) ( cbLocker, iiu, currentTime, hdr, pMshBody ); } -void cac::signal ( int ca_status, const char *pfilenm, - int lineno, const char *pFormat, ... ) +void cac::selfTest ( + epicsGuard < epicsMutex > & guard ) const { - va_list theArgs; - va_start ( theArgs, pFormat ); - this->vSignal ( ca_status, pfilenm, lineno, pFormat, theArgs); - va_end ( theArgs ); -} - -void cac::vSignal ( int ca_status, const char *pfilenm, - int lineno, const char *pFormat, va_list args ) -{ - static const char *severity[] = - { - "Warning", - "Success", - "Error", - "Info", - "Fatal", - "Fatal", - "Fatal", - "Fatal" - }; - - this->printf ( "CA.Client.Exception...............................................\n" ); - - this->printf ( " %s: \"%s\"\n", - severity[ CA_EXTRACT_SEVERITY ( ca_status ) ], - ca_message ( ca_status ) ); - - if ( pFormat ) { - this->printf ( " Context: \"" ); - this->vPrintf ( pFormat, args ); - this->printf ( "\"\n" ); - } - - if ( pfilenm ) { - this->printf ( " Source File: %s line %d\n", - pfilenm, lineno ); - } - - epicsTime current = epicsTime::getCurrent (); - char date[64]; - current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); - this->printf ( " Current Time: %s\n", date ); - - /* - * Terminate execution if unsuccessful - */ - if( ! ( ca_status & CA_M_SUCCESS ) && - CA_EXTRACT_SEVERITY ( ca_status ) != CA_K_WARNING ){ - errlogFlush (); - abort (); - } - - this->printf ( "..................................................................\n" ); -} - -void cac::selfTest () const -{ - epicsGuard < cacMutex > guard ( this->mutex ); + guard.assertIdenticalMutex ( this->mutex ); this->chanTable.verify (); this->ioTable.verify (); - this->sgTable.verify (); this->beaconTable.verify (); } -void cac::disconnectNotify ( tcpiiu & iiu ) -{ - epicsGuard < cacMutex > guard ( this->mutex ); - iiu.disconnectNotify ( guard ); -} - -void cac::unresponsiveCircuitNotify ( tcpiiu & iiu ) -{ - epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); - epicsGuard < cacMutex > guard ( this->mutex ); - iiu.unresponsiveCircuitNotify ( cbGuard, guard ); -} - void cac::initiateAbortShutdown ( tcpiiu & iiu ) { int exception = ECA_DISCONN; @@ -1486,7 +1222,7 @@ void cac::initiateAbortShutdown ( tcpiiu & iiu ) bool exceptionNeeded = false; epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); { - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); if ( iiu.channelCount( cbGuard ) ) { iiu.hostName ( hostNameTmp, sizeof ( hostNameTmp ) ); @@ -1502,7 +1238,7 @@ void cac::initiateAbortShutdown ( tcpiiu & iiu ) // because on certain OS such as HPUX it's difficult to // unblock a blocking send() call, and we need immediate // disconnect notification. - iiu.removeAllChannels ( cbGuard, guard, *this ); + iiu.removeAllChannels ( cbGuard, guard, *this->pudpiiu ); } if ( exceptionNeeded ) { @@ -1514,7 +1250,7 @@ void cac::destroyIIU ( tcpiiu & iiu ) { { epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); if ( iiu.channelCount ( cbGuard ) ) { char hostNameTmp[64]; iiu.hostName ( hostNameTmp, sizeof ( hostNameTmp ) ); @@ -1525,12 +1261,12 @@ void cac::destroyIIU ( tcpiiu & iiu ) inetAddrID tmp ( addr.ia ); bhe * pBHE = this->beaconTable.lookup ( tmp ); if ( pBHE ) { - pBHE->unregisterIIU ( iiu ); + pBHE->unregisterIIU ( guard, iiu ); } } assert ( this->pudpiiu ); - iiu.removeAllChannels ( cbGuard, guard, *this ); + iiu.removeAllChannels ( cbGuard, guard, *this->pudpiiu ); } { @@ -1540,7 +1276,7 @@ void cac::destroyIIU ( tcpiiu & iiu ) // access any part of the cac (including the // callback lock) because ~cac is allowed to // complete. - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); this->serverTable.remove ( iiu ); this->serverList.remove ( iiu ); @@ -1554,7 +1290,7 @@ void cac::destroyIIU ( tcpiiu & iiu ) double cac::beaconPeriod ( const nciu & chan ) const { - epicsGuard < cacMutex > guard ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); const netiiu * pIIU = chan.getConstPIIU (); if ( pIIU ) { osiSockAddr addr = pIIU->getNetworkAddress (); @@ -1562,15 +1298,17 @@ double cac::beaconPeriod ( const nciu & chan ) const inetAddrID tmp ( addr.ia ); bhe *pBHE = this->beaconTable.lookup ( tmp ); if ( pBHE ) { - return pBHE->period (); + return pBHE->period ( guard ); } } } return - DBL_MAX; } -void cac::initiateConnect ( nciu & chan ) +void cac::initiateConnect ( + epicsGuard < epicsMutex > & guard, nciu & chan ) { + guard.assertIdenticalMutex ( this->mutex ); assert ( this->pudpiiu ); this->pudpiiu->installNewChannel ( epicsTime::getCurrent(), chan ); @@ -1599,3 +1337,4 @@ void cac::pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv, mfmdpv.~msgForMultiplyDefinedPV (); this->mdpvFreeList.release ( & mfmdpv ); } + diff --git a/src/ca/cac.h b/src/ca/cac.h index fbcbd6254..f55262080 100644 --- a/src/ca/cac.h +++ b/src/ca/cac.h @@ -60,9 +60,12 @@ class netSubscription; // is applied class cacRecycle { // X aCC 655 public: - virtual void recycleReadNotifyIO ( netReadNotifyIO &io ) = 0; - virtual void recycleWriteNotifyIO ( netWriteNotifyIO &io ) = 0; - virtual void recycleSubscription ( netSubscription &io ) = 0; + virtual void recycleReadNotifyIO ( + epicsGuard < epicsMutex > &, netReadNotifyIO &io ) = 0; + virtual void recycleWriteNotifyIO ( + epicsGuard < epicsMutex > &, netWriteNotifyIO &io ) = 0; + virtual void recycleSubscription ( + epicsGuard < epicsMutex > &, netSubscription &io ) = 0; }; struct CASG; @@ -72,15 +75,6 @@ struct caHdrLargeArray; extern epicsThreadPrivateId caClientCallbackThreadId; -class cacMutex { -public: - void lock (); - void unlock (); - void show ( unsigned level ) const; -private: - epicsMutex mutex; -}; - class cacComBufMemoryManager : public comBufMemoryManager { public: @@ -95,82 +89,98 @@ public: virtual void disconnectChannel ( const epicsTime & currentTime, epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > &, nciu & chan ) = 0; + epicsGuard < epicsMutex > &, nciu & chan ) = 0; }; class callbackMutex { public: - callbackMutex ( cacNotify & ); + callbackMutex ( cacContextNotify & ); ~callbackMutex (); void lock (); void unlock (); private: - cacNotify & notify; + cacContextNotify & notify; callbackMutex ( callbackMutex & ); callbackMutex & operator = ( callbackMutex & ); }; -class cac : private cacRecycle, private cacDisconnectChannelPrivate, +class cac : + public cacContext, + private cacRecycle, + private cacDisconnectChannelPrivate, private callbackForMultiplyDefinedPV { public: - cac ( cacNotify & ); + cac ( epicsMutex &, cacContextNotify & ); virtual ~cac (); // beacon management void beaconNotify ( const inetAddrID & addr, const epicsTime & currentTime, ca_uint32_t beaconNumber, unsigned protocolRevision ); void repeaterSubscribeConfirmNotify (); - unsigned beaconAnomaliesSinceProgramStart () const; + unsigned beaconAnomaliesSinceProgramStart ( + epicsGuard < epicsMutex > & ) const; // IO management - void flushRequest (); + void flush ( epicsGuard < epicsMutex > & guard ); bool executeResponse ( epicsGuard < callbackMutex > &, tcpiiu &, const epicsTime & currentTime, caHdrLargeArray &, char *pMsgBody ); // channel routines bool transferChanToVirtCircuit ( - epicsGuard < callbackMutex > &, - unsigned cid, unsigned sid, - ca_uint16_t typeCode, arrayElementCount count, - unsigned minorVersionNumber, const osiSockAddr & ); - void connectAllIO ( epicsGuard < cacMutex > &, nciu & chan ); - void destroyChannel ( nciu & ); - cacChannel & createChannel ( const char *name_str, - cacChannelNotify &chan, cacChannel::priLev pri ); - void registerService ( cacService &service ); - void initiateConnect ( nciu & ); + epicsGuard < callbackMutex > &, + unsigned cid, unsigned sid, + ca_uint16_t typeCode, arrayElementCount count, + unsigned minorVersionNumber, const osiSockAddr & ); + cacChannel & createChannel ( + epicsGuard < epicsMutex > & guard, const char * pChannelName, + cacChannelNotify &, cacChannel::priLev ); + void destroyChannel ( + epicsGuard < epicsMutex > &, nciu & ); + void initiateConnect ( + epicsGuard < epicsMutex > &, nciu & ); // IO requests - void writeRequest ( nciu &, unsigned type, + void writeRequest ( epicsGuard < epicsMutex > &, nciu &, unsigned type, arrayElementCount nElem, const void * pValue ); - cacChannel::ioid writeNotifyRequest ( nciu &, unsigned type, - arrayElementCount nElem, const void *pValue, cacWriteNotify & ); - cacChannel::ioid readNotifyRequest ( nciu &, unsigned type, - arrayElementCount nElem, cacReadNotify & ); - cacChannel::ioid subscriptionRequest ( nciu &, unsigned type, - arrayElementCount nElem, unsigned mask, cacStateNotify & ); - void ioCancel ( nciu & chan, const cacChannel::ioid & id ); + netWriteNotifyIO & writeNotifyRequest ( + epicsGuard < epicsMutex > &, nciu &, privateInterfaceForIO &, + unsigned type, arrayElementCount nElem, const void * pValue, + cacWriteNotify & ); + netReadNotifyIO & readNotifyRequest ( + epicsGuard < epicsMutex > &, nciu &, privateInterfaceForIO &, + unsigned type, arrayElementCount nElem, + cacReadNotify & ); + netSubscription & subscriptionRequest ( + epicsGuard < epicsMutex > &, nciu &, privateInterfaceForIO &, + unsigned type, arrayElementCount nElem, unsigned mask, + cacStateNotify & ); + baseNMIU * destroyIO ( + epicsGuard < epicsMutex > & guard, + const cacChannel::ioid & idIn, + nciu & chan ); + void disconnectAllIO ( + epicsGuard < callbackMutex > &, + epicsGuard < epicsMutex > &, + nciu &, tsDLList < baseNMIU > & ioList ); + void ioShow ( const cacChannel::ioid &id, unsigned level ) const; // sync group routines - CASG * lookupCASG ( unsigned id ); - void installCASG ( CASG & ); - void uninstallCASG ( CASG & ); + CASG * lookupCASG ( epicsGuard < epicsMutex > &, unsigned id ); + void installCASG ( epicsGuard < epicsMutex > &, CASG & ); + void uninstallCASG ( epicsGuard < epicsMutex > &, CASG & ); // exception generation - void exception ( epicsGuard < callbackMutex > &, int status, const char * pContext, + void exception ( epicsGuard < callbackMutex > &, + int status, const char * pContext, const char * pFileName, unsigned lineNo ); // diagnostics - unsigned connectionCount () const; - void show ( unsigned level ) const; + unsigned circuitCount ( epicsGuard < epicsMutex > & ) const; + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; int printf ( const char *pformat, ... ) const; int vPrintf ( const char *pformat, va_list args ) const; - void signal ( int ca_status, const char *pfilenm, - int lineno, const char *pFormat, ... ); - void vSignal ( int ca_status, const char *pfilenm, - int lineno, const char *pFormat, va_list args ); // buffer management char * allocateSmallBufferTCP (); @@ -182,20 +192,18 @@ public: // misc const char * userNamePointer () const; unsigned getInitializingThreadsPriority () const; - cacMutex & mutexRef (); + epicsMutex & mutexRef (); void attachToClientCtx (); - void selfTest () const; + void selfTest ( epicsGuard < epicsMutex > & ) const; double beaconPeriod ( const nciu & chan ) const; static unsigned lowestPriorityLevelAbove ( unsigned priority ); static unsigned highestPriorityLevelBelow ( unsigned priority ); void initiateAbortShutdown ( tcpiiu & ); - void unresponsiveCircuitNotify ( tcpiiu & ); - void disconnectNotify ( tcpiiu & ); void destroyIIU ( tcpiiu & iiu ); + void flushIfRequired ( epicsGuard < epicsMutex > &, netiiu & ); private: localHostName hostNameCache; - cacServiceList services; chronIntIdResTable < nciu > chanTable; // // !!!! There is at this point no good reason @@ -210,7 +218,6 @@ private: // !!!! terms of detecting damaged protocol. // chronIntIdResTable < baseNMIU > ioTable; - chronIntIdResTable < CASG > sgTable; resTable < bhe, inetAddrID > beaconTable; resTable < tcpiiu, caServerID > serverTable; tsDLList < tcpiiu > serverList; @@ -226,7 +233,9 @@ private: tsFreeList < class netSubscription, 1024, epicsMutexNOOP > freeListSubscription; - tsFreeList < nciu, 1024 > channelFreeList; + tsFreeList + < class nciu, 1024, epicsMutexNOOP > + channelFreeList; tsFreeList < class msgForMultiplyDefinedPV, 16 > mdpvFreeList; @@ -238,53 +247,41 @@ private: // callback lock must always be acquired before // the primary mutex if both locks are needed callbackMutex cbMutex; - mutable cacMutex mutex; + mutable epicsMutex & mutex; epicsEvent iiuUninstall; - epicsSingleton - < cacServiceList >::reference - globalServiceList; - ipAddrToAsciiEngine & ipToAEngine; + ipAddrToAsciiEngine & ipToAEngine; epicsTimerQueueActive & timerQueue; char * pUserName; class udpiiu * pudpiiu; void * tcpSmallRecvBufFreeList; void * tcpLargeRecvBufFreeList; - cacNotify & notify; + cacContextNotify & notify; epicsThreadId initializingThreadsId; unsigned initializingThreadsPriority; unsigned maxRecvBytesTCP; unsigned beaconAnomalyCount; void run (); - void disconnectAllIO ( epicsGuard < cacMutex > & locker, nciu & chan, bool enableCallbacks ); - void flushIfRequired ( epicsGuard < cacMutex > &, netiiu & ); - void recycleReadNotifyIO ( netReadNotifyIO &io ); - void recycleWriteNotifyIO ( netWriteNotifyIO &io ); - void recycleSubscription ( netSubscription &io ); + void recycleReadNotifyIO ( + epicsGuard < epicsMutex > &, netReadNotifyIO &io ); + void recycleWriteNotifyIO ( + epicsGuard < epicsMutex > &, netWriteNotifyIO &io ); + void recycleSubscription ( + epicsGuard < epicsMutex > &, netSubscription &io ); void disconnectChannel ( const epicsTime & currentTime, epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > &, nciu & chan ); + epicsGuard < epicsMutex > &, nciu & chan ); - void ioCompletionNotify ( unsigned id, unsigned type, - arrayElementCount count, const void *pData ); - void ioExceptionNotify ( unsigned id, - int status, const char *pContext ); void ioExceptionNotify ( unsigned id, int status, - const char *pContext, unsigned type, arrayElementCount count ); + const char * pContext, unsigned type, arrayElementCount count ); + void ioExceptionNotifyAndUninstall ( unsigned id, int status, + const char * pContext, unsigned type, arrayElementCount count ); void pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv, const char * pChannelName, const char * pAcc, const char * pRej ); - void ioCompletionNotifyAndDestroy ( unsigned id ); - void ioCompletionNotifyAndDestroy ( unsigned id, - unsigned type, arrayElementCount count, const void *pData ); - void ioExceptionNotifyAndDestroy ( unsigned id, - int status, const char *pContext ); - void ioExceptionNotifyAndDestroy ( unsigned id, - int status, const char *pContext, unsigned type, arrayElementCount count ); - // recv protocol stubs bool versionAction ( epicsGuard < callbackMutex > &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); @@ -304,7 +301,7 @@ private: const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool accessRightsRespAction ( epicsGuard < callbackMutex > &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); - bool claimCIURespAction ( epicsGuard < callbackMutex > &, tcpiiu &, + bool createChannelRespAction ( epicsGuard < callbackMutex > &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); bool verifyAndDisconnectChan ( epicsGuard < callbackMutex > &, tcpiiu &, const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); @@ -349,17 +346,11 @@ inline unsigned cac::getInitializingThreadsPriority () const return this->initializingThreadsPriority; } -inline cacMutex & cac::mutexRef () +inline epicsMutex & cac::mutexRef () { return this->mutex; } -inline void cac::exception ( epicsGuard < callbackMutex > &, int status, - const char *pContext, const char *pFileName, unsigned lineNo ) -{ - this->notify.exception ( status, pContext, pFileName, lineNo ); -} - inline int cac::vPrintf ( const char *pformat, va_list args ) const { return this->notify.vPrintf ( pformat, args ); @@ -399,27 +390,14 @@ inline void cac::releaseLargeBufferTCP ( char *pBuf ) freeListFree ( this->tcpLargeRecvBufFreeList, pBuf ); } -inline unsigned cac::beaconAnomaliesSinceProgramStart () const +inline unsigned cac::beaconAnomaliesSinceProgramStart ( + epicsGuard < epicsMutex > & guard ) const { + guard.assertIdenticalMutex ( this->mutex ); return this->beaconAnomalyCount; } -inline void cacMutex::lock () -{ - this->mutex.lock (); -} - -inline void cacMutex::unlock () -{ - this->mutex.unlock (); -} - -inline void cacMutex::show ( unsigned level ) const -{ - this->mutex.show ( level ); -} - -inline callbackMutex::callbackMutex ( cacNotify & notifyIn ) : +inline callbackMutex::callbackMutex ( cacContextNotify & notifyIn ) : notify ( notifyIn ) { } diff --git a/src/ca/cacChannel.cpp b/src/ca/cacChannel.cpp index b50211ae5..a724a14d3 100644 --- a/src/ca/cacChannel.cpp +++ b/src/ca/cacChannel.cpp @@ -104,5 +104,8 @@ void cacChannel::operator delete ( void * ) __FILE__, __LINE__ ); } +cacContext::~cacContext () {} + +cacService::~cacService () {} diff --git a/src/ca/cacNotify.cpp b/src/ca/cacContextNotify.cpp similarity index 88% rename from src/ca/cacNotify.cpp rename to src/ca/cacContextNotify.cpp index c624665b1..8a31416c6 100644 --- a/src/ca/cacNotify.cpp +++ b/src/ca/cacContextNotify.cpp @@ -27,15 +27,15 @@ #include "cacIO.h" #undef epicsExportSharedSymbols -cacNotify::~cacNotify () +cacContextNotify::~cacContextNotify () { } -void cacNotify::callbackLock () +void cacContextNotify::callbackLock () { } -void cacNotify::callbackUnlock () +void cacContextNotify::callbackUnlock () { } diff --git a/src/ca/cacIO.h b/src/ca/cacIO.h index 33cb8d019..6819449ce 100644 --- a/src/ca/cacIO.h +++ b/src/ca/cacIO.h @@ -55,6 +55,7 @@ #include "tsDLList.h" #include "epicsMutex.h" +#include "epicsGuard.h" #include "epicsSingleton.h" #ifdef cacIOh_restore_epicsExportSharedSymbols @@ -72,9 +73,11 @@ typedef unsigned long arrayElementCount; class epicsShareClass cacWriteNotify { // X aCC 655 public: virtual ~cacWriteNotify () = 0; - virtual void completion () = 0; + virtual void completion ( epicsGuard < epicsMutex > & ) = 0; // we should probably have a different vf for each type of exception ???? - virtual void exception ( int status, const char *pContext, + virtual void exception ( + epicsGuard < epicsMutex > &, + int status, const char * pContext, unsigned type, arrayElementCount count ) = 0; }; @@ -83,11 +86,14 @@ public: class epicsShareClass cacReadNotify { // X aCC 655 public: virtual ~cacReadNotify () = 0; - virtual void completion ( unsigned type, - arrayElementCount count, const void *pData ) = 0; + virtual void completion ( + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void * pData ) = 0; // we should probably have a different vf for each type of exception ???? - virtual void exception ( int status, - const char *pContext, unsigned type, arrayElementCount count ) = 0; + virtual void exception ( + epicsGuard < epicsMutex > &, int status, + const char * pContext, unsigned type, + arrayElementCount count ) = 0; }; // 1) this should not be passing caerr.h status to the exception callback @@ -95,11 +101,14 @@ public: class epicsShareClass cacStateNotify { // X aCC 655 public: virtual ~cacStateNotify () = 0; - virtual void current ( unsigned type, - arrayElementCount count, const void *pData ) = 0; + virtual void current ( + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void * pData ) = 0; // we should probably have a different vf for each type of exception ???? - virtual void exception ( int status, - const char *pContext, unsigned type, arrayElementCount count ) = 0; + virtual void exception ( + epicsGuard < epicsMutex > &, int status, + const char *pContext, unsigned type, + arrayElementCount count ) = 0; }; class caAccessRights { @@ -126,16 +135,20 @@ private: class epicsShareClass cacChannelNotify { // X aCC 655 public: virtual ~cacChannelNotify () = 0; - virtual void connectNotify () = 0; - virtual void disconnectNotify () = 0; + virtual void connectNotify ( epicsGuard < epicsMutex > & ) = 0; + virtual void disconnectNotify ( epicsGuard < epicsMutex > & ) = 0; virtual void serviceShutdownNotify () = 0; - virtual void accessRightsNotify ( const caAccessRights & ) = 0; - virtual void exception ( int status, const char *pContext ) = 0; + virtual void accessRightsNotify ( + epicsGuard < epicsMutex > &, const caAccessRights & ) = 0; + virtual void exception ( + epicsGuard < epicsMutex > &, int status, const char *pContext ) = 0; // we should probably have a different vf for each type of exception ???? - virtual void readException ( int status, const char *pContext, + virtual void readException ( + epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count, void *pValue ) = 0; // we should probably have a different vf for each type of exception ???? - virtual void writeException ( int status, const char *pContext, + virtual void writeException ( + epicsGuard < epicsMutex > &, int status, const char * pContext, unsigned type, arrayElementCount count ) = 0; }; @@ -161,20 +174,33 @@ public: cacChannel ( cacChannelNotify & ); cacChannelNotify & notify () const; - virtual void destroy () = 0; + virtual void destroy ( + epicsGuard < epicsMutex > & ) = 0; virtual const char * pName () const = 0; // not thread safe - virtual void show ( unsigned level ) const = 0; - virtual void initiateConnect () = 0; - virtual ioStatus read ( unsigned type, arrayElementCount count, + virtual void show ( + unsigned level ) const = 0; + virtual void initiateConnect ( + epicsGuard < epicsMutex > & ) = 0; + virtual ioStatus read ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, cacReadNotify &, ioid * = 0 ) = 0; - virtual void write ( unsigned type, arrayElementCount count, + virtual void write ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, const void *pValue ) = 0; - virtual ioStatus write ( unsigned type, arrayElementCount count, + virtual ioStatus write ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, const void *pValue, cacWriteNotify &, ioid * = 0 ) = 0; - virtual void subscribe ( unsigned type, arrayElementCount count, - unsigned mask, cacStateNotify &, ioid * = 0 ) = 0; - virtual void ioCancel ( const ioid & ) = 0; - virtual void ioShow ( const ioid &, unsigned level ) const = 0; + virtual void subscribe ( + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, unsigned mask, cacStateNotify &, + ioid * = 0 ) = 0; + virtual void ioCancel ( + epicsGuard < epicsMutex > &, const ioid & ) = 0; + virtual void ioShow ( + const ioid &, unsigned level ) const = 0; virtual short nativeType () const = 0; virtual arrayElementCount nativeElementCount () const = 0; virtual caAccessRights accessRights () const; // defaults to unrestricted access @@ -211,15 +237,36 @@ private: void operator delete ( void * ); }; -class cacNotify { // X aCC 655 +class epicsShareClass cacContext { // X aCC 655 public: - virtual ~cacNotify () = 0; + virtual ~cacContext (); + virtual cacChannel & createChannel ( + epicsGuard < epicsMutex > &, + const char * pChannelName, cacChannelNotify &, + cacChannel::priLev = cacChannel::priorityDefault ) = 0; + virtual void flush ( + epicsGuard < epicsMutex > & ) = 0; + virtual unsigned circuitCount ( + epicsGuard < epicsMutex > & ) const = 0; + virtual void selfTest ( + epicsGuard < epicsMutex > & ) const = 0; + virtual unsigned beaconAnomaliesSinceProgramStart ( + epicsGuard < epicsMutex > & ) const = 0; + virtual void show ( + epicsGuard < epicsMutex > &, unsigned level ) const = 0; +}; + +class epicsShareClass cacContextNotify { // X aCC 655 +public: + virtual ~cacContextNotify () = 0; + virtual cacContext & createNetworkContext ( epicsMutex & ) = 0; // we should probably have a different vf for each type of exception ???? - virtual void exception ( int status, const char * pContext, + virtual void exception ( + epicsGuard < epicsMutex > &, int status, const char * pContext, const char * pFileName, unsigned lineNo ) = 0; // perhaps this should be phased out in deference to the exception mechanism - virtual int vPrintf ( const char *pformat, va_list args ) const = 0; -// backwards compatibility + virtual int vPrintf ( const char * pformat, va_list args ) const = 0; +// backwards compatibility (from here down) virtual void attachToClientCtx () = 0; virtual void blockForEventAndEnableCallbacks ( class epicsEvent & event, const double & timeout ) = 0; @@ -227,34 +274,14 @@ public: virtual void callbackUnlock () = 0; }; -class cacService : public tsDLNode < cacService > { // X aCC 655 +class epicsShareClass cacService { public: - virtual cacChannel * createChannel ( - const char *pName, cacChannelNotify &, - cacChannel::priLev = cacChannel::priorityDefault ) = 0; - virtual void show ( unsigned level ) const = 0; + virtual ~cacService () = 0; + virtual cacContext & contextCreate ( + epicsMutex &, cacContextNotify & ) = 0; }; -class cacServiceList { -public: - epicsShareFunc cacServiceList (); - epicsShareFunc void registerService ( cacService &service ); - epicsShareFunc cacChannel * createChannel ( - const char *pName, cacChannelNotify &, - cacChannel::priLev = cacChannel::priorityDefault ); - epicsShareFunc void show ( unsigned level ) const; -private: - tsDLList < cacService > services; - mutable epicsMutex mutex; - cacServiceList ( const cacServiceList & ); - cacServiceList & operator = ( const cacServiceList & ); -}; - -template < class T > class epicsSingleton; - -epicsShareExtern epicsSingleton < cacServiceList > globalServiceListCAC; - -epicsShareFunc int epicsShareAPI ca_register_service ( cacService *pService ); +epicsShareFunc void epicsShareAPI caInstallDefaultService ( cacService & service ); inline cacChannel::cacChannel ( cacChannelNotify & notify ) : callback ( notify ) diff --git a/src/ca/cacServiceList.cpp b/src/ca/cacServiceList.cpp deleted file mode 100644 index 90a57a308..000000000 --- a/src/ca/cacServiceList.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ -/* - * $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 "epicsSingleton.h" - -#define epicsExportSharedSymbols -#include "iocinf.h" -#include "epicsGuard.h" -#include "cacIO.h" - -epicsShareDef epicsSingleton < cacServiceList > globalServiceListCAC; - -cacServiceList::cacServiceList () -{ -} - -void cacServiceList::registerService ( cacService &service ) -{ - epicsGuard < epicsMutex > locker ( this->mutex ); - this->services.add ( service ); -} - -cacChannel * cacServiceList::createChannel ( - const char * pName, cacChannelNotify & chan, cacChannel::priLev pri ) -{ - cacChannel *pChanIO = 0; - - epicsGuard < epicsMutex > locker ( this->mutex ); - tsDLIter < cacService > iter = this->services.firstIter (); - while ( iter.valid () ) { - pChanIO = iter->createChannel ( pName, chan, pri ); - if ( pChanIO ) { - break; - } - iter++; - } - - return pChanIO; -} - -void cacServiceList::show ( unsigned level ) const -{ - epicsGuard < epicsMutex > locker ( this->mutex ); - tsDLIterConst < cacService > iter = this->services.firstIter (); - while ( iter.valid () ) { - iter->show ( level ); - iter++; - } -} diff --git a/src/ca/casw.cpp b/src/ca/casw.cpp index 1df479aa7..3725c55cc 100644 --- a/src/ca/casw.cpp +++ b/src/ca/casw.cpp @@ -53,6 +53,8 @@ void bheFreeStoreMgr::release ( void * pCadaver ) int main ( int argc, char ** argv ) { + epicsMutex mutex; + epicsGuard < epicsMutex > guard ( mutex ); bheFreeStoreMgr bheFreeList; epicsTime programBeginTime = epicsTime::getCurrent (); bool validCommandLine = false; @@ -241,8 +243,9 @@ int main ( int argc, char ** argv ) */ bhe *pBHE = beaconTable.lookup ( ina ); if ( pBHE ) { - epicsTime previousTime = pBHE->updateTime (); - bool anomaly = pBHE->updatePeriod ( programBeginTime, + epicsTime previousTime = pBHE->updateTime ( guard ); + bool anomaly = pBHE->updatePeriod ( + guard, programBeginTime, currentTime, beaconNumber, protocolRevision ); if ( anomaly ) { char date[64]; @@ -254,7 +257,8 @@ int main ( int argc, char ** argv ) host, date ); if ( interest > 0 ) { printf ( "\testimate=%f current=%f\n", - pBHE->period (), currentTime - previousTime ); + pBHE->period ( guard ), + currentTime - previousTime ); } fflush(stdout); } @@ -268,7 +272,7 @@ int main ( int argc, char ** argv ) * shortly after the program started up) */ pBHE = new ( bheFreeList ) - bhe ( currentTime, beaconNumber, ina ); + bhe ( mutex, currentTime, beaconNumber, ina ); if ( pBHE ) { if ( beaconTable.add ( *pBHE ) < 0 ) { pBHE->~bhe (); diff --git a/src/ca/comQueSend.cpp b/src/ca/comQueSend.cpp index 9dc0be714..63283937a 100644 --- a/src/ca/comQueSend.cpp +++ b/src/ca/comQueSend.cpp @@ -321,10 +321,13 @@ void comQueSend::insertRequestHeader ( } void comQueSend::insertRequestWithPayLoad ( - ca_uint16_t request, unsigned dataType, ca_uint32_t nElem, + ca_uint16_t request, unsigned dataType, arrayElementCount nElem, ca_uint32_t cid, ca_uint32_t requestDependent, const void * pPayload, bool v49Ok ) { + if ( INVALID_DB_REQ ( dataType ) ) { + throw cacChannel::badType (); + } if ( dataType >= comQueSendCopyDispatchSize ) { throw cacChannel::badType(); } @@ -353,24 +356,28 @@ void comQueSend::insertRequestWithPayLoad ( } } else { - unsigned maxBytes; + arrayElementCount maxBytes; if ( v49Ok ) { maxBytes = 0xffffffff; } else { maxBytes = MAX_TCP - sizeof ( caHdr ); } - unsigned maxElem = + arrayElementCount maxElem = ( maxBytes - sizeof (dbr_double_t) - dbr_size[dataType] ) / dbr_value_size[dataType]; if ( nElem >= maxElem ) { throw cacChannel::outOfBounds(); } - size = dbr_size_n ( dataType, nElem ); + // the above checks verify that the total size + // is lest that 0xffffffff + size = static_cast < ca_uint32_t > + ( dbr_size_n ( dataType, nElem ) ); payloadSize = CA_MESSAGE_ALIGN ( size ); this->insertRequestHeader ( request, payloadSize, static_cast ( dataType ), - nElem, cid, requestDependent, v49Ok ); + static_cast < ca_uint32_t > ( nElem ), + cid, requestDependent, v49Ok ); ( this->*dbrCopyVector [dataType] ) ( pPayload, nElem ); } // set pad bytes to nill diff --git a/src/ca/comQueSend.h b/src/ca/comQueSend.h index b34349b57..05c11ce45 100644 --- a/src/ca/comQueSend.h +++ b/src/ca/comQueSend.h @@ -41,7 +41,7 @@ template < class T > class epicsGuard; class comQueSendMsgMinder { public: comQueSendMsgMinder ( - class comQueSend &, epicsGuard < cacMutex > & ); + class comQueSend &, epicsGuard < epicsMutex > & ); ~comQueSendMsgMinder (); void commit (); private: @@ -69,7 +69,7 @@ public: ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, ca_uint32_t requestDependent, bool v49Ok ); void insertRequestWithPayLoad ( - ca_uint16_t request, unsigned dataType, ca_uint32_t nElem, + ca_uint16_t request, unsigned dataType, arrayElementCount nElem, ca_uint32_t cid, ca_uint32_t requestDependent, const void * pPayload, bool v49Ok ); comBuf * popNextComBufToSend (); @@ -157,7 +157,7 @@ private: extern const char cacNillBytes[]; inline comQueSendMsgMinder::comQueSendMsgMinder ( - class comQueSend & sendQueIn, epicsGuard < cacMutex > & ) : + class comQueSend & sendQueIn, epicsGuard < epicsMutex > & ) : pSendQue ( & sendQueIn ) { sendQueIn.beginMsg (); diff --git a/src/ca/disconnectGovernorTimer.cpp b/src/ca/disconnectGovernorTimer.cpp new file mode 100644 index 000000000..54bf37d57 --- /dev/null +++ b/src/ca/disconnectGovernorTimer.cpp @@ -0,0 +1,57 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// +// $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 +// + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#define epicsExportSharedSymbols +#include "disconnectGovernorTimer.h" +#include "udpiiu.h" + +static const double period = 10.0; // sec + +disconnectGovernorTimer::disconnectGovernorTimer ( + udpiiu & iiuIn, epicsTimerQueue & queueIn ) : + timer ( queueIn.createTimer () ), + iiu ( iiuIn ) +{ + this->timer.start ( *this, period ); +} + +disconnectGovernorTimer::~disconnectGovernorTimer () +{ + this->timer.destroy (); +} + +void disconnectGovernorTimer::shutdown () +{ + this->timer.cancel (); +} + +epicsTimerNotify::expireStatus disconnectGovernorTimer::expire ( const epicsTime & currentTime ) // X aCC 361 +{ + this->iiu.govExpireNotify ( currentTime ); + return expireStatus ( restart, period ); +} + +void disconnectGovernorTimer::show ( unsigned /* level */ ) const +{ +} + diff --git a/src/ca/disconnectGovernorTimer.h b/src/ca/disconnectGovernorTimer.h new file mode 100644 index 000000000..b4608d59a --- /dev/null +++ b/src/ca/disconnectGovernorTimer.h @@ -0,0 +1,60 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +// +// $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 disconnectGovernorTimerh +#define disconnectGovernorTimerh + +#ifdef epicsExportSharedSymbols +# define searchTimerh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "epicsMutex.h" +#include "epicsGuard.h" +#include "epicsTimer.h" + +#ifdef searchTimerh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "caProto.h" + +class disconnectGovernorTimer : private epicsTimerNotify { +public: + disconnectGovernorTimer ( class udpiiu &, epicsTimerQueue & ); + virtual ~disconnectGovernorTimer (); + void show ( unsigned level ) const; + void shutdown (); +private: + epicsTimer & timer; + class udpiiu & iiu; + epicsTimerNotify::expireStatus expire ( const epicsTime & currentTime ); + disconnectGovernorTimer ( const disconnectGovernorTimer & ); + disconnectGovernorTimer & operator = ( const disconnectGovernorTimer & ); +}; + +#endif // ifdef disconnectGovernorTimerh diff --git a/src/ca/getCallback.cpp b/src/ca/getCallback.cpp index c7efe7f3c..2c0c8a101 100644 --- a/src/ca/getCallback.cpp +++ b/src/ca/getCallback.cpp @@ -31,7 +31,7 @@ #include "iocinf.h" #include "oldAccess.h" -getCallback::getCallback ( oldChannelNotify &chanIn, +getCallback::getCallback ( oldChannelNotify & chanIn, caEventCallBackFunc *pFuncIn, void *pPrivateIn ) : chan ( chanIn ), pFunc ( pFuncIn ), pPrivate ( pPrivateIn ) { @@ -42,34 +42,44 @@ getCallback::~getCallback () } void getCallback::completion ( + epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count, const void *pData ) { struct event_handler_args args; args.usr = this->pPrivate; - args.chid = &this->chan; + args.chid = & this->chan; args.type = type; args.count = count; args.status = ECA_NORMAL; args.dbr = pData; - ( *this->pFunc ) ( args ); - this->chan.getClientCtx().destroyGetCallback ( *this ); + caEventCallBackFunc * pFuncTmp = this->pFunc; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } + this->chan.getClientCtx().destroyGetCallback ( guard, *this ); } void getCallback::exception ( + epicsGuard < epicsMutex > & guard, int status, const char * /* pContext */, unsigned type, arrayElementCount count ) { if ( status != ECA_CHANDESTROY ) { struct event_handler_args args; args.usr = this->pPrivate; - args.chid = &this->chan; + args.chid = & this->chan; args.type = type; args.count = count; args.status = status; args.dbr = 0; - ( *this->pFunc ) ( args ); + caEventCallBackFunc * pFuncTmp = this->pFunc; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } } - this->chan.getClientCtx().destroyGetCallback ( *this ); + this->chan.getClientCtx().destroyGetCallback ( guard, *this ); } void * getCallback::operator new ( size_t ) // X aCC 361 diff --git a/src/ca/getCopy.cpp b/src/ca/getCopy.cpp index 43860309c..4e1e8ba19 100644 --- a/src/ca/getCopy.cpp +++ b/src/ca/getCopy.cpp @@ -34,12 +34,15 @@ #include "oldAccess.h" #include "cac.h" -getCopy::getCopy ( ca_client_context &cacCtxIn, oldChannelNotify &chanIn, - unsigned typeIn, arrayElementCount countIn, void *pValueIn ) : +getCopy::getCopy ( + epicsGuard < epicsMutex > & guard, ca_client_context & cacCtxIn, + oldChannelNotify & chanIn, unsigned typeIn, + arrayElementCount countIn, void * pValueIn ) : count ( countIn ), cacCtx ( cacCtxIn ), chan ( chanIn ), pValue ( pValueIn ), - ioSeqNo ( cacCtxIn.sequenceNumberOfOutstandingIO () ), type ( typeIn ) + ioSeqNo ( 0 ), type ( typeIn ) { - cacCtxIn.incrementOutstandingIO ( cacCtxIn.sequenceNumberOfOutstandingIO () ); + this->ioSeqNo = cacCtxIn.sequenceNumberOfOutstandingIO ( guard ); + cacCtxIn.incrementOutstandingIO ( guard, this->ioSeqNo ); } getCopy::~getCopy () @@ -48,34 +51,37 @@ getCopy::~getCopy () void getCopy::cancel () { - this->cacCtx.decrementOutstandingIO ( this->ioSeqNo ); + epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef () ); + this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); } -void getCopy::completion ( unsigned typeIn, +void getCopy::completion ( + epicsGuard < epicsMutex > & guard, unsigned typeIn, arrayElementCount countIn, const void *pDataIn ) { if ( this->type == typeIn ) { unsigned size = dbr_size_n ( typeIn, countIn ); memcpy ( this->pValue, pDataIn, size ); - this->cacCtx.decrementOutstandingIO ( this->ioSeqNo ); + this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); } else { - this->exception ( ECA_INTERNAL, + this->exception ( guard, ECA_INTERNAL, "bad data type match in get copy back response", typeIn, countIn); } - this->cacCtx.destroyGetCopy ( *this ); + this->cacCtx.destroyGetCopy ( guard, *this ); } void getCopy::exception ( + epicsGuard < epicsMutex > & guard, int status, const char *pContext, unsigned /* typeIn */, arrayElementCount /* countIn */ ) { if ( status != ECA_CHANDESTROY ) { - this->cacCtx.exception ( status, pContext, + this->cacCtx.exception ( guard, status, pContext, __FILE__, __LINE__, this->chan, this->type, this->count, CA_OP_GET ); } - this->cacCtx.destroyGetCopy ( *this ); + this->cacCtx.destroyGetCopy ( guard, *this ); } void getCopy::show ( unsigned level ) const diff --git a/src/ca/hostNameCache.cpp b/src/ca/hostNameCache.cpp index 0399efeea..6755ca932 100644 --- a/src/ca/hostNameCache.cpp +++ b/src/ca/hostNameCache.cpp @@ -27,9 +27,6 @@ #include -#if 0 -#include "iocinf.h" -#endif #include "hostNameCache.h" #include "epicsGuard.h" diff --git a/src/ca/nciu.cpp b/src/ca/nciu.cpp index 5f4525641..b6dcf79cb 100644 --- a/src/ca/nciu.cpp +++ b/src/ca/nciu.cpp @@ -47,16 +47,13 @@ nciu::nciu ( cac & cacIn, netiiu & iiuIn, cacChannelNotify & chanIn, const char *pNameIn, cacChannel::priLev pri ) : cacChannel ( chanIn ), cacCtx ( cacIn ), - piiu ( &iiuIn ), + piiu ( & iiuIn ), sid ( UINT_MAX ), count ( 0 ), retry ( 0u ), nameLength ( 0u ), typeCode ( USHRT_MAX ), - priority ( static_cast ( pri ) ), - f_connected ( false ), - f_createChanReqSent ( false ), - f_createChanRespReceived ( false ) + priority ( static_cast ( pri ) ) { size_t nameLengthTmp = strlen ( pNameIn ) + 1; @@ -77,13 +74,31 @@ nciu::nciu ( cac & cacIn, netiiu & iiuIn, cacChannelNotify & chanIn, nciu::~nciu () { - delete [] this->pNameStr; } -void nciu::destroy () +void nciu::destructor ( epicsGuard < epicsMutex > & guard ) { - // care is taken so that a lock is not applied during this phase - this->cacCtx.destroyChannel ( *this ); + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + // Send any side effect IO requests w/o holding the callback lock so that + // we do not dead lock. + // There is special protection in this routine that prevents blocking if + // this is the tcp receive thread. + // must not hold callback lock here + this->cacCtx.flushIfRequired ( guard, *this->piiu ); + while ( baseNMIU * pNetIO = this->eventq.first () ) { + assert ( this->cacCtx.destroyIO ( guard, + pNetIO->getId (), *this ) ); + } + delete [] this->pNameStr; + this->~nciu (); +} + +#pragma message ("audit all callback mutex locks to verify that they dont occur at inappropriate times in the main thread?" ) +// called virtually +void nciu::destroy ( epicsGuard < epicsMutex > & guard ) +{ + // must not hold callback lock here + this->cacCtx.destroyChannel ( guard, *this ); } void * nciu::operator new ( size_t ) // X aCC 361 @@ -104,30 +119,18 @@ void nciu::operator delete ( void * ) __FILE__, __LINE__ ); } -void nciu::initiateConnect () +void nciu::initiateConnect ( + epicsGuard < epicsMutex > & guard ) { - this->cacCtx.initiateConnect ( *this ); + this->cacCtx.initiateConnect ( guard, *this ); } void nciu::connect ( unsigned nativeType, unsigned nativeCount, unsigned sidIn, epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ) + epicsGuard < epicsMutex > & guard ) { - if ( ! this->f_createChanReqSent ) { - this->cacCtx.printf ( - "CAC: Ignored conn resp to chan lacking virtual circuit CID=%u SID=%u?\n", - this->getId (), sidIn ); - return; - } - - if ( this->f_createChanRespReceived ) { - this->cacCtx.printf ( - "CAC: Ignored create channel resp to conn chan CID=%u SID=%u?\n", - this->getId (), sidIn ); - return; - } - + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); if ( ! dbf_type_is_valid ( nativeType ) ) { this->cacCtx.printf ( "CAC: Ignored conn resp with bad native data type CID=%u SID=%u?\n", @@ -138,8 +141,6 @@ void nciu::connect ( unsigned nativeType, this->typeCode = static_cast < unsigned short > ( nativeType ); this->count = nativeCount; this->sid = sidIn; - this->f_connected = true; - this->f_createChanRespReceived = true; /* * if less than v4.1 then the server will never @@ -151,63 +152,44 @@ void nciu::connect ( unsigned nativeType, this->accessRightState.setWritePermit(); } - // this installs any subscriptions that - // might still be attached - this->cacCtx.connectAllIO ( guard, *this ); - - { - epicsGuardRelease < cacMutex > unguard ( guard ); - - /* - * 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 ( ! v41Ok ) { - this->notify().accessRightsNotify ( this->accessRightState ); - } - - // channel uninstal routine grabs the callback lock so - // a channel will not be deleted while a call back is - // in progress - // - // the callback lock is also taken when a channel - // disconnects to prevent a race condition with the - // code below - ie we hold the callback lock here - // so a chanel cant be destroyed out from under us. - this->notify().connectNotify (); + /* + * 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 ( ! v41Ok ) { + this->notify().accessRightsNotify ( + guard, this->accessRightState ); } + + // channel uninstal routine grabs the callback lock so + // a channel will not be deleted while a call back is + // in progress + // + // the callback lock is also taken when a channel + // disconnects to prevent a race condition with the + // code below - ie we hold the callback lock here + // so a chanel cant be destroyed out from under us. + this->notify().connectNotify ( guard ); } void nciu::unresponsiveCircuitNotify ( epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ) + epicsGuard < epicsMutex > & guard ) { - if ( this->f_connected ) { - this->f_connected = false; - epicsGuardRelease < cacMutex > autoMutexRelease ( guard ); - this->notify().disconnectNotify (); + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + if ( this->channelNode::isConnected ( guard ) ) { + this->notify().disconnectNotify ( guard ); caAccessRights noRights; - this->notify().accessRightsNotify ( noRights ); + this->notify().accessRightsNotify ( guard, noRights ); } } -void nciu::responsiveCircuitNotify ( - epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ) -{ - if ( ! this->f_connected ) { - this->f_connected = true; - epicsGuardRelease < cacMutex > autoMutexRelease ( guard ); - this->notify().connectNotify (); - this->notify().accessRightsNotify ( this->accessRightState ); - } -} - -void nciu::circuitHangupNotify ( class udpiiu & newiiu, - epicsGuard < callbackMutex > & cbGuard, epicsGuard < cacMutex > & guard ) +void nciu::setServerAddressUnknown ( udpiiu & newiiu, + epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); this->piiu = & newiiu; this->retry = disconnectRetrySetpoint; this->typeCode = USHRT_MAX; @@ -215,20 +197,13 @@ void nciu::circuitHangupNotify ( class udpiiu & newiiu, this->sid = UINT_MAX; this->accessRightState.clrReadPermit(); this->accessRightState.clrWritePermit(); - this->f_createChanReqSent = false; - this->f_createChanRespReceived = false; - if ( this->f_connected ) { - this->f_connected = false; - epicsGuardRelease < cacMutex > autoMutexRelease ( guard ); - this->notify().disconnectNotify (); - this->notify().accessRightsNotify ( this->accessRightState ); - } } void nciu::accessRightsStateChange ( const caAccessRights & arIn, epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > & guard ) + epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); this->accessRightState = arIn; // @@ -236,8 +211,7 @@ void nciu::accessRightsStateChange ( // that this will not be called when the channel is being // deleted. // - epicsGuardRelease < cacMutex > unguard ( guard ); - this->notify().accessRightsNotify ( this->accessRightState ); + this->notify().accessRightsNotify ( guard, this->accessRightState ); } /* @@ -281,41 +255,37 @@ unsigned nciu::nameLen () const return this->nameLength; } -void nciu::createChannelRequest ( - tcpiiu & iiu, epicsGuard < cacMutex > & guard ) -{ - iiu.createChannelRequest ( *this, guard ); - this->f_createChanReqSent = true; -} - cacChannel::ioStatus nciu::read ( + epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount countIn, cacReadNotify ¬ify, ioid *pId ) { - // - // fail out if their arguments are invalid - // - if ( ! this->f_connected ) { - throw notConnected (); + if ( ! this->isConnected ( guard ) ) { + throw cacChannel::notConnected (); } - if ( INVALID_DB_REQ (type) ) { - throw badType (); + if ( ! this->accessRightState.readPermit () ) { + throw cacChannel::noReadAccess (); } - if ( ! this->accessRightState.readPermit() ) { - throw noReadAccess (); + if ( countIn > this->count ) { + throw cacChannel::outOfBounds (); } - if ( countIn > UINT_MAX ) { - throw outOfBounds (); - } - if ( countIn == 0 ) { countIn = this->count; } - - ioid tmpId = this->cacCtx.readNotifyRequest ( *this, type, countIn, notify ); - if ( pId ) { - *pId = tmpId; + + // + // fail out if their arguments are invalid + // + if ( INVALID_DB_REQ ( type ) ) { + throw cacChannel::badType (); } + + netReadNotifyIO & io = this->cacCtx.readNotifyRequest ( + guard, *this, *this, type, countIn, notify ); + if ( pId ) { + *pId = io.getId (); + } + this->eventq.add ( io ); return cacChannel::iosAsynch; } @@ -332,79 +302,73 @@ void nciu::stringVerify ( const char *pStr, const unsigned count ) } } -void nciu::write ( unsigned type, - arrayElementCount countIn, const void *pValue ) +void nciu::write ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount countIn, const void * pValue ) { // make sure that they get this and not "no write access" // if disconnected - if ( ! this->f_connected ) { - throw notConnected(); + if ( ! this->connected ( guard ) ) { + throw cacChannel::notConnected(); } - if ( ! this->accessRightState.writePermit() ) { - throw noWriteAccess(); + throw cacChannel::noWriteAccess(); } - if ( countIn > this->count || countIn == 0 ) { - throw outOfBounds(); + throw cacChannel::outOfBounds(); } - if ( type == DBR_STRING ) { nciu::stringVerify ( (char *) pValue, countIn ); } - this->cacCtx.writeRequest ( *this, type, countIn, pValue ); + this->cacCtx.writeRequest ( guard, *this, type, countIn, pValue ); } -cacChannel::ioStatus nciu::write ( unsigned type, arrayElementCount countIn, - const void *pValue, cacWriteNotify ¬ify, ioid *pId ) +cacChannel::ioStatus nciu::write ( + epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount countIn, + const void * pValue, cacWriteNotify & notify, ioid * pId ) { // make sure that they get this and not "no write access" // if disconnected - if ( ! this->f_connected ) { - throw notConnected(); + if ( ! this->connected ( guard ) ) { + throw cacChannel::notConnected(); } - if ( ! this->accessRightState.writePermit() ) { - throw noWriteAccess(); + throw cacChannel::noWriteAccess(); } - if ( countIn > this->count || countIn == 0 ) { - throw outOfBounds(); + throw cacChannel::outOfBounds(); } - if ( type == DBR_STRING ) { nciu::stringVerify ( (char *) pValue, countIn ); } - ioid tmpId = this->cacCtx.writeNotifyRequest ( *this, type, countIn, pValue, notify ); + netWriteNotifyIO & io = this->cacCtx.writeNotifyRequest ( + guard, *this, *this, type, countIn, pValue, notify ); if ( pId ) { - *pId = tmpId; + *pId = io.getId (); } + this->eventq.add ( io ); return cacChannel::iosAsynch; } -void nciu::subscribe ( unsigned type, arrayElementCount nElem, - unsigned mask, cacStateNotify ¬ify, ioid *pId ) +void nciu::subscribe ( + epicsGuard < epicsMutex > & guard, unsigned type, + arrayElementCount nElem, unsigned mask, + cacStateNotify & notify, ioid *pId ) { - if ( INVALID_DB_REQ(type) ) { - throw badType(); - } - - if ( mask > 0xffff || mask == 0u ) { - throw badEventSelection(); - } - - ioid tmpId = this->cacCtx.subscriptionRequest ( - *this, type, nElem, mask, notify ); + netSubscription & io = this->cacCtx.subscriptionRequest ( + guard, *this, *this, type, nElem, mask, notify ); if ( pId ) { - *pId = tmpId; + *pId = io.getId (); } + this->eventq.add ( io ); } -void nciu::ioCancel ( const ioid &idIn ) +void nciu::ioCancel ( + epicsGuard < epicsMutex > & guard, const ioid & idIn ) { - this->cacCtx.ioCancel ( *this, idIn ); + this->cacCtx.destroyIO ( guard, idIn, *this ); } void nciu::ioShow ( const ioid &idIn, unsigned level ) const @@ -414,7 +378,7 @@ void nciu::ioShow ( const ioid &idIn, unsigned level ) const void nciu::hostName ( char *pBuf, unsigned bufLength ) const { - epicsGuard < cacMutex > locker ( this->cacCtx.mutexRef() ); + epicsGuard < epicsMutex > locker ( this->cacCtx.mutexRef() ); this->piiu->hostName ( pBuf, bufLength ); } @@ -426,15 +390,15 @@ const char * nciu::pHostName () const bool nciu::ca_v42_ok () const { - epicsGuard < cacMutex > locker ( this->cacCtx.mutexRef() ); + epicsGuard < epicsMutex > locker ( this->cacCtx.mutexRef() ); return this->piiu->ca_v42_ok (); } short nciu::nativeType () const { - epicsGuard < cacMutex > locker ( this->cacCtx.mutexRef() ); + epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef() ); short type; - if ( this->f_connected ) { + if ( this->channelNode::isConnected ( guard ) ) { if ( this->typeCode < SHRT_MAX ) { type = static_cast ( this->typeCode ); } @@ -450,27 +414,20 @@ short nciu::nativeType () const arrayElementCount nciu::nativeElementCount () const { - epicsGuard < cacMutex > locker ( this->cacCtx.mutexRef() ); - arrayElementCount countOut; - if ( this->f_connected ) { - countOut = this->count; - } - else { - countOut = 0ul; - } - return countOut; + epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef() ); + return this->nativeElementCount ( guard ); } caAccessRights nciu::accessRights () const { - epicsGuard < cacMutex > locker ( this->cacCtx.mutexRef() ); + epicsGuard < epicsMutex > locker ( this->cacCtx.mutexRef() ); caAccessRights tmp = this->accessRightState; return tmp; } unsigned nciu::searchAttempts () const { - epicsGuard < cacMutex > locker ( this->cacCtx.mutexRef() ); + epicsGuard < epicsMutex > locker ( this->cacCtx.mutexRef() ); return this->retry; } @@ -481,14 +438,20 @@ double nciu::beaconPeriod () const double nciu::receiveWatchdogDelay () const { - epicsGuard < cacMutex > locker ( this->cacCtx.mutexRef() ); + epicsGuard < epicsMutex > locker ( this->cacCtx.mutexRef() ); return this->piiu->receiveWatchdogDelay (); } +bool nciu::connected ( epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + return this->channelNode::isConnected ( guard ); +} + void nciu::show ( unsigned level ) const { - epicsGuard < cacMutex > locker ( this->cacCtx.mutexRef() ); - if ( this->f_connected ) { + epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef() ); + if ( this->channelNode::isConnected ( guard ) ) { char hostNameTmp [256]; this->hostName ( hostNameTmp, sizeof ( hostNameTmp ) ); ::printf ( "Channel \"%s\", connected to server %s", @@ -536,3 +499,73 @@ void nciu::beaconAnomalyNotify () } } +void nciu::ioCompletionNotify ( + epicsGuard < epicsMutex > &, class baseNMIU & io ) +{ + this->eventq.remove ( io ); +} + +void nciu::resubscribe ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + tsDLIter < baseNMIU > pNetIO = this->eventq.firstIter (); + while ( pNetIO.valid () ) { + tsDLIter < baseNMIU > next = pNetIO; + next++; + class netSubscription * pSubscr = pNetIO->isSubscription (); + // Its normal for other types of IO to exist after the channel connects, + // but before all of the resubscription requests go out. We must ignore + // them here. + if ( pSubscr ) { + try { + this->getPIIU()->subscriptionRequest ( guard, *this, *pSubscr ); + } + catch ( ... ) { + this->printf ( "CAC: failed to send subscription request during channel connect\n" ); + } + } + pNetIO = next; + } +} + +void nciu::sendSubscriptionUpdateRequests ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + tsDLIter < baseNMIU > pNetIO = this->eventq.firstIter (); + while ( pNetIO.valid () ) { + tsDLIter < baseNMIU > next = pNetIO; + next++; + class netSubscription * pSubscr = pNetIO->isSubscription (); + // disconnected channels should have only subscription IO attached + assert ( pSubscr ); + try { + pSubscr->subscriptionUpdateIfRequired ( guard, *this ); + } + catch ( ... ) { + this->printf ( "CAC: failed to send subscription request during channel connect\n" ); + } + pNetIO = next; + } +} + +void nciu::disconnectAllIO ( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + this->cacCtx.disconnectAllIO ( cbGuard, guard, + *this, this->eventq ); +} + +arrayElementCount nciu::nativeElementCount ( + epicsGuard < epicsMutex > & guard ) const +{ + arrayElementCount countOut; + if ( this->channelNode::isConnected ( guard ) ) { + countOut = this->count; + } + else { + countOut = 0ul; + } + return countOut; +} + diff --git a/src/ca/nciu.h b/src/ca/nciu.h index 0673de0b0..3027922f6 100644 --- a/src/ca/nciu.h +++ b/src/ca/nciu.h @@ -50,44 +50,71 @@ class cac; class netiiu; class callbackMutex; -class cacMutex; -class cacPrivateListOfIO { +// The node and the state which tracks the list membership +// are in the channel, but belong to the circuit. +// Protected by the callback mutex +class channelNode : public tsDLNode < class nciu > +{ public: - cacPrivateListOfIO (); + channelNode (); + bool isConnected ( epicsGuard < epicsMutex > & ) const; private: - tsDLList < class baseNMIU > eventq; - friend class cac; - cacPrivateListOfIO ( const cacPrivateListOfIO & ); - cacPrivateListOfIO & operator = ( const cacPrivateListOfIO & ); + enum channelState { + cs_none, + cs_disconnGov, + cs_serverAddrResPend, + cs_createReqPend, + cs_createRespPend, + cs_subscripReqPend, + cs_connected, + cs_unrespCircuit, + cs_subscripUpdateReqPend + } listMember; + friend class tcpiiu; + friend class tcpSendThread; + friend class udpiiu; }; -class nciu : public cacChannel, public tsDLNode < nciu >, - public chronIntIdRes < nciu >, public cacPrivateListOfIO { +class privateInterfaceForIO { +public: + virtual void ioCompletionNotify ( + epicsGuard < epicsMutex > &, class baseNMIU & ) = 0; + virtual arrayElementCount nativeElementCount ( + epicsGuard < epicsMutex > & ) const = 0; + virtual int printf ( const char *pFormat, ... ) = 0; + virtual bool connected ( epicsGuard < epicsMutex > & ) const = 0; +}; + +class nciu : + public cacChannel, + public chronIntIdRes < nciu >, + public channelNode, + private privateInterfaceForIO { public: nciu ( cac &, netiiu &, cacChannelNotify &, - const char *pNameIn, cacChannel::priLev ); - ~nciu (); - void destroy (); + const char * pNameIn, cacChannel::priLev ); + void destructor ( + epicsGuard < epicsMutex > & ); void connect ( unsigned nativeType, unsigned nativeCount, unsigned sid, epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ); + epicsGuard < epicsMutex > & guard ); void connect ( epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ); - void responsiveCircuitNotify ( + epicsGuard < epicsMutex > & guard ); + void unresponsiveCircuitNotify ( epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ); - void unresponsiveCircuitNotify ( epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ); + epicsGuard < epicsMutex > & guard ); void circuitHangupNotify ( class udpiiu &, - epicsGuard < callbackMutex > & cbGuard, epicsGuard < cacMutex > & guard ); + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void setServerAddressUnknown ( + udpiiu & newiiu, epicsGuard < epicsMutex > & guard ); bool searchMsg ( class udpiiu & iiu, unsigned & retryNoForThisChannel ); - void createChannelRequest ( class tcpiiu & iiu, epicsGuard < cacMutex > & ); void beaconAnomalyNotify (); void serviceShutdownNotify (); void accessRightsStateChange ( const caAccessRights &, - epicsGuard < callbackMutex > &, epicsGuard < cacMutex > & ); + epicsGuard < callbackMutex > &, epicsGuard < epicsMutex > & ); ca_uint32_t getSID () const; ca_uint32_t getCID () const; netiiu * getPIIU (); @@ -96,19 +123,28 @@ public: int printf ( const char *pFormat, ... ); void searchReplySetUp ( netiiu &iiu, unsigned sidIn, ca_uint16_t typeIn, arrayElementCount countIn, - epicsGuard < cacMutex > & ); + epicsGuard < epicsMutex > & ); void show ( unsigned level ) const; - const char *pName () const; + const char * pName () const; unsigned nameLen () const; const char * pHostName () const; // deprecated - please do not use - arrayElementCount nativeElementCount () const; - bool connected () const; - void writeException ( epicsGuard < callbackMutex > &, + void writeException ( + epicsGuard < callbackMutex > &, epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); cacChannel::priLev getPriority () const; - void * operator new ( size_t size, tsFreeList < class nciu, 1024 > & ); - epicsPlacementDeleteOperator (( void *, tsFreeList < class nciu, 1024 > & )) + void * operator new ( + size_t size, tsFreeList < class nciu, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator ( + ( void *, tsFreeList < class nciu, 1024, epicsMutexNOOP > & )) + arrayElementCount nativeElementCount ( epicsGuard < epicsMutex > & ) const; + void resubscribe ( epicsGuard < epicsMutex > & ); + void sendSubscriptionUpdateRequests ( epicsGuard < epicsMutex > & ); + void disconnectAllIO ( + epicsGuard < callbackMutex > &, epicsGuard < epicsMutex > & ); + bool connected ( epicsGuard < epicsMutex > & ) const; + private: + tsDLList < class baseNMIU > eventq; caAccessRights accessRightState; cac & cacCtx; char * pNameStr; @@ -119,20 +155,31 @@ private: unsigned short nameLength; // channel name length ca_uint16_t typeCode; ca_uint8_t priority; - bool f_connected:1; - bool f_createChanReqSent:1; - bool f_createChanRespReceived:1; - void initiateConnect (); - ioStatus read ( unsigned type, arrayElementCount count, + ~nciu (); + void destroy ( + epicsGuard < epicsMutex > & ); + void initiateConnect ( + epicsGuard < epicsMutex > & ); + ioStatus read ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, cacReadNotify &, ioid * ); - void write ( unsigned type, arrayElementCount count, + void write ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, const void *pValue ); - ioStatus write ( unsigned type, arrayElementCount count, + ioStatus write ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, const void *pValue, cacWriteNotify &, ioid * ); - void subscribe ( unsigned type, arrayElementCount nElem, + void subscribe ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount nElem, unsigned mask, cacStateNotify ¬ify, ioid * ); - void ioCancel ( const ioid & ); - void ioShow ( const ioid &, unsigned level ) const; + void ioCancel ( + epicsGuard < epicsMutex > &, const ioid & ); + void ioShow ( + const ioid &, unsigned level ) const; short nativeType () const; caAccessRights accessRights () const; unsigned searchAttempts () const; @@ -140,7 +187,10 @@ private: double receiveWatchdogDelay () const; bool ca_v42_ok () const; void hostName ( char *pBuf, unsigned bufLength ) const; + arrayElementCount nativeElementCount () const; static void stringVerify ( const char *pStr, const unsigned count ); + virtual void ioCompletionNotify ( + epicsGuard < epicsMutex > &, class baseNMIU & ); nciu ( const nciu & ); nciu & operator = ( const nciu & ); void * operator new ( size_t ); @@ -148,14 +198,14 @@ private: }; inline void * nciu::operator new ( size_t size, - tsFreeList < class nciu, 1024 > & freeList ) + tsFreeList < class nciu, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void nciu::operator delete ( void * pCadaver, - tsFreeList < class nciu, 1024 > & freeList ) + tsFreeList < class nciu, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver, sizeof ( nciu ) ); } @@ -173,7 +223,7 @@ inline ca_uint32_t nciu::getCID () const // this is to only be used by early protocol revisions inline void nciu::connect ( epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ) + epicsGuard < epicsMutex > & guard ) { this->connect ( this->typeCode, this->count, this->sid, cbGuard, guard ); @@ -181,7 +231,7 @@ inline void nciu::connect ( epicsGuard < callbackMutex > & cbGuard, inline void nciu::searchReplySetUp ( netiiu &iiu, unsigned sidIn, ca_uint16_t typeIn, arrayElementCount countIn, - epicsGuard < cacMutex > & ) + epicsGuard < epicsMutex > & ) { this->piiu = & iiu; this->typeCode = typeIn; @@ -189,20 +239,19 @@ inline void nciu::searchReplySetUp ( netiiu &iiu, unsigned sidIn, this->sid = sidIn; } -inline bool nciu::connected () const -{ - return this->f_connected; -} - inline netiiu * nciu::getPIIU () { return this->piiu; } -inline void nciu::writeException ( epicsGuard < callbackMutex > &, int status, - const char *pContext, unsigned typeIn, arrayElementCount countIn ) +inline void nciu::writeException ( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < epicsMutex > & guard, + int status, const char * pContext, + unsigned typeIn, arrayElementCount countIn ) { - this->notify().writeException ( status, pContext, typeIn, countIn ); + this->notify().writeException ( guard, + status, pContext, typeIn, countIn ); } inline const netiiu * nciu::getConstPIIU () const @@ -225,8 +274,17 @@ inline cacChannel::priLev nciu::getPriority () const return this->priority; } -inline cacPrivateListOfIO::cacPrivateListOfIO () +inline channelNode::channelNode () : + listMember ( cs_none ) { } +inline bool channelNode::isConnected ( epicsGuard < epicsMutex > & ) const +{ + return + this->listMember == cs_connected || + this->listMember == cs_subscripReqPend || + this->listMember == cs_subscripUpdateReqPend; +} + #endif // ifdef nciuh diff --git a/src/ca/netIO.h b/src/ca/netIO.h index f9eaa3ca3..e345ea64e 100644 --- a/src/ca/netIO.h +++ b/src/ca/netIO.h @@ -41,24 +41,28 @@ # define NETIO_VIRTUAL_DESTRUCTOR virtual #endif +class privateInterfaceForIO; + class baseNMIU : public tsDLNode < baseNMIU >, // X aCC 655 public chronIntIdRes < baseNMIU > { public: - virtual void destroy ( class cacRecycle & ) = 0; // only called by cac - virtual void completion () = 0; - virtual void exception ( int status, - const char * pContext ) = 0; - virtual void exception ( int status, - const char * pContext, unsigned type, + virtual void destroy ( + epicsGuard < epicsMutex > &, class cacRecycle & ) = 0; // only called by cac + virtual void completion ( + epicsGuard < epicsMutex > &, cacRecycle & ) = 0; + virtual void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext ) = 0; + virtual void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext, unsigned type, arrayElementCount count ) = 0; - virtual void completion ( unsigned type, - arrayElementCount count, const void * pData ) = 0; + virtual void completion ( + epicsGuard < epicsMutex > &, cacRecycle &, + unsigned type, arrayElementCount count, + const void * pData ) = 0; virtual class netSubscription * isSubscription () = 0; virtual void show ( unsigned level ) const = 0; -// -// not fond of the vf overhead to fetch this -// - virtual nciu & channel () const = 0; protected: NETIO_VIRTUAL_DESTRUCTOR ~baseNMIU (); }; @@ -67,20 +71,26 @@ class netSubscription : public baseNMIU { public: static netSubscription * factory ( tsFreeList < class netSubscription, 1024, epicsMutexNOOP > &, - nciu & chan, unsigned type, arrayElementCount count, - unsigned mask, cacStateNotify ¬ify ); + class privateInterfaceForIO &, unsigned type, arrayElementCount count, + unsigned mask, cacStateNotify & ); void show ( unsigned level ) const; - arrayElementCount getCount () const; + arrayElementCount getCount ( + epicsGuard < epicsMutex > & ) const; unsigned getType () const; unsigned getMask () const; + void subscriptionUpdateIfRequired ( + epicsGuard < epicsMutex > &, nciu & ); private: const arrayElementCount count; - nciu & chan; + class privateInterfaceForIO & privateChanForIO; cacStateNotify & notify; const unsigned type; const unsigned mask; - netSubscription ( nciu & chan, unsigned type, arrayElementCount count, - unsigned mask, cacStateNotify ¬ify ); + bool updateWhileDisconnected; + netSubscription ( + class privateInterfaceForIO &, unsigned type, + arrayElementCount count, + unsigned mask, cacStateNotify & ); class netSubscription * isSubscription (); void * operator new ( size_t ); void operator delete ( void * ); @@ -88,16 +98,20 @@ private: tsFreeList < class netSubscription, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class netSubscription, 1024, epicsMutexNOOP > & )) - void destroy ( class cacRecycle & ); - void completion (); - void exception ( int status, - const char *pContext ); - void completion ( unsigned type, - arrayElementCount count, const void *pData ); - void exception ( int status, - const char *pContext, unsigned type, + void destroy ( + epicsGuard < epicsMutex > &, class cacRecycle & ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle & ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle &, + unsigned type, arrayElementCount count, const void * pData ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext, unsigned type, arrayElementCount count ); - nciu & channel () const; netSubscription ( const netSubscription & ); netSubscription & operator = ( const netSubscription & ); ~netSubscription (); @@ -107,26 +121,33 @@ class netReadNotifyIO : public baseNMIU { public: static netReadNotifyIO * factory ( tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > &, - nciu &chan, cacReadNotify ¬ify ); + privateInterfaceForIO &, cacReadNotify & ); void show ( unsigned level ) const; private: cacReadNotify & notify; - nciu & chan; - netReadNotifyIO ( nciu & chan, cacReadNotify & notify ); + class privateInterfaceForIO & privateChanForIO; + netReadNotifyIO ( privateInterfaceForIO &, cacReadNotify & ); void * operator new ( size_t ); void operator delete ( void * ); void * operator new ( size_t, tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & )) - void destroy ( class cacRecycle & ); - void completion (); - void exception ( int status, const char *pContext ); - void completion ( unsigned type, - arrayElementCount count, const void *pData ); - void exception ( int status, const char *pContext, + void destroy ( + epicsGuard < epicsMutex > &, class cacRecycle & ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle & ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle &, + unsigned type, arrayElementCount count, + const void * pData ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext, unsigned type, arrayElementCount count ); - nciu & channel () const; ~netReadNotifyIO (); class netSubscription * isSubscription (); netReadNotifyIO ( const netReadNotifyIO & ); @@ -137,12 +158,12 @@ class netWriteNotifyIO : public baseNMIU { public: static netWriteNotifyIO * factory ( tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > &, - nciu &chan, cacWriteNotify ¬ify ); + privateInterfaceForIO &, cacWriteNotify & ); void show ( unsigned level ) const; private: cacWriteNotify & notify; - nciu & chan; - netWriteNotifyIO ( nciu &chan, cacWriteNotify ¬ify ); + privateInterfaceForIO & privateChanForIO; + netWriteNotifyIO ( privateInterfaceForIO &, cacWriteNotify & ); void * operator new ( size_t ); void operator delete ( void * ); void * operator new ( size_t, @@ -150,14 +171,21 @@ private: epicsPlacementDeleteOperator (( void *, tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & )) class netSubscription * isSubscription (); - void destroy ( class cacRecycle & ); - void completion (); - void exception ( int status, const char *pContext ); - void completion ( unsigned type, - arrayElementCount count, const void *pData ); - void exception ( int status, const char *pContext, - unsigned type, arrayElementCount count ); - nciu & channel () const; + void destroy ( + epicsGuard < epicsMutex > &, class cacRecycle & ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle & ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle &, + unsigned type, arrayElementCount count, + const void * pData ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext, unsigned type, + arrayElementCount count ); netWriteNotifyIO ( const netWriteNotifyIO & ); netWriteNotifyIO & operator = ( const netWriteNotifyIO & ); ~netWriteNotifyIO (); @@ -178,17 +206,19 @@ inline void * netSubscription::operator new ( size_t size, #endif inline netSubscription * netSubscription::factory ( - tsFreeList < class netSubscription, 1024, epicsMutexNOOP > &freeList, - nciu &chan, unsigned type, arrayElementCount count, + tsFreeList < class netSubscription, 1024, epicsMutexNOOP > & freeList, + class privateInterfaceForIO & chan, unsigned type, arrayElementCount count, unsigned mask, cacStateNotify ¬ify ) { return new ( freeList ) netSubscription ( chan, type, // X aCC 930 count, mask, notify ); } -inline arrayElementCount netSubscription::getCount () const // X aCC 361 +inline arrayElementCount netSubscription::getCount ( + epicsGuard < epicsMutex > & guard ) const // X aCC 361 { - arrayElementCount nativeCount = this->chan.nativeElementCount (); + //guard.assertIdenticalMutex ( this->mutex ); + arrayElementCount nativeCount = this->privateChanForIO.nativeElementCount ( guard ); if ( this->count == 0u || this->count > nativeCount ) { return nativeCount; } @@ -208,42 +238,42 @@ inline unsigned netSubscription::getMask () const } inline netReadNotifyIO * netReadNotifyIO::factory ( - tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > &freeList, - nciu &chan, cacReadNotify ¬ify ) + tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & freeList, + privateInterfaceForIO & ioComplNotifIntf, cacReadNotify & notify ) { - return new ( freeList ) netReadNotifyIO ( chan, notify ); // X aCC 930 + return new ( freeList ) netReadNotifyIO ( ioComplNotifIntf, notify ); // X aCC 930 } inline void * netReadNotifyIO::operator new ( size_t size, - tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > &freeList ) + tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #if defined ( CXX_PLACEMENT_DELETE ) inline void netReadNotifyIO::operator delete ( void *pCadaver, - tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > &freeList ) + tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline netWriteNotifyIO * netWriteNotifyIO::factory ( - tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > &freeList, - nciu &chan, cacWriteNotify ¬ify ) + tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & freeList, + privateInterfaceForIO & ioComplNotifyIntf, cacWriteNotify & notify ) { - return new ( freeList ) netWriteNotifyIO ( chan, notify ); // X aCC 930 + return new ( freeList ) netWriteNotifyIO ( ioComplNotifyIntf, notify ); // X aCC 930 } inline void * netWriteNotifyIO::operator new ( size_t size, - tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > &freeList ) + tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #if defined ( CXX_PLACEMENT_DELETE ) inline void netWriteNotifyIO::operator delete ( void *pCadaver, - tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > &freeList ) + tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } diff --git a/src/ca/netReadNotifyIO.cpp b/src/ca/netReadNotifyIO.cpp index a344cd119..a0a4169c2 100644 --- a/src/ca/netReadNotifyIO.cpp +++ b/src/ca/netReadNotifyIO.cpp @@ -28,8 +28,10 @@ #include "nciu.h" #include "cac.h" -netReadNotifyIO::netReadNotifyIO ( nciu & chanIn, cacReadNotify & notify ) : - notify ( notify ), chan ( chanIn ) +netReadNotifyIO::netReadNotifyIO ( + privateInterfaceForIO & ioComplIntfIn, + cacReadNotify & notify ) : + notify ( notify ), privateChanForIO ( ioComplIntfIn ) { } @@ -43,32 +45,61 @@ void netReadNotifyIO::show ( unsigned /* level */ ) const static_cast < const void * > ( this ) ); } -void netReadNotifyIO::destroy ( cacRecycle & recycle ) +void netReadNotifyIO::destroy ( + epicsGuard < epicsMutex > & guard, cacRecycle & recycle ) { - this->~netReadNotifyIO(); - recycle.recycleReadNotifyIO ( *this ); + this->~netReadNotifyIO (); + recycle.recycleReadNotifyIO ( guard, *this ); } -void netReadNotifyIO::completion () +void netReadNotifyIO::completion ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, unsigned type, + arrayElementCount count, const void * pData ) { - this->chan.getClient().printf ( "Read response w/o data ?\n" ); + //guard.assertIdenticalMutex ( this->mutex ); + this->notify.completion ( guard, type, count, pData ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netReadNotifyIO (); + recycle.recycleReadNotifyIO ( guard, *this ); } -void netReadNotifyIO::exception ( int status, const char *pContext ) +void netReadNotifyIO::completion ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle ) { - this->notify.exception ( status, pContext, UINT_MAX, 0u ); + //guard.assertIdenticalMutex ( this->mutex ); + //this->chan.getClient().printf ( "Read response w/o data ?\n" ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netReadNotifyIO (); + recycle.recycleReadNotifyIO ( guard, *this ); } -void netReadNotifyIO::exception ( int status, const char *pContext, - unsigned type, arrayElementCount count ) +void netReadNotifyIO::exception ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, + int status, const char *pContext ) { - this->notify.exception ( status, pContext, type, count ); + //guard.assertIdenticalMutex ( this->mutex ); + this->notify.exception ( + guard, status, pContext, UINT_MAX, 0u ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netReadNotifyIO (); + recycle.recycleReadNotifyIO ( guard, *this ); } -void netReadNotifyIO::completion ( unsigned type, - arrayElementCount count, const void *pData ) +void netReadNotifyIO::exception ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, + int status, const char *pContext, + unsigned type, arrayElementCount count ) { - this->notify.completion ( type, count, pData ); + //guard.assertIdenticalMutex ( this->mutex ) + this->notify.exception ( + guard, status, pContext, type, count ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netReadNotifyIO (); + recycle.recycleReadNotifyIO ( guard, *this ); } class netSubscription * netReadNotifyIO::isSubscription () @@ -76,11 +107,6 @@ class netSubscription * netReadNotifyIO::isSubscription () return 0; } -nciu & netReadNotifyIO::channel () const -{ - return this->chan; -} - void * netReadNotifyIO::operator new ( size_t ) // X aCC 361 { // The HPUX compiler seems to require this even though no code diff --git a/src/ca/netSubscription.cpp b/src/ca/netSubscription.cpp index 3f5c46708..355a3c5a3 100644 --- a/src/ca/netSubscription.cpp +++ b/src/ca/netSubscription.cpp @@ -29,12 +29,15 @@ #include "nciu.h" #include "cac.h" #include "db_access.h" // for dbf_type_to_text +#include "caerr.h" -netSubscription::netSubscription ( nciu & chanIn, +netSubscription::netSubscription ( + privateInterfaceForIO & chanIn, unsigned typeIn, arrayElementCount countIn, - unsigned maskIn, cacStateNotify ¬ifyIn ) : - count ( countIn ), chan ( chanIn ), - notify ( notifyIn ), type ( typeIn ), mask ( maskIn ) + unsigned maskIn, cacStateNotify & notifyIn ) : + count ( countIn ), privateChanForIO ( chanIn ), + notify ( notifyIn ), type ( typeIn ), mask ( maskIn ), + updateWhileDisconnected ( false ) { if ( ! dbr_type_is_valid ( typeIn ) ) { throw cacChannel::badType (); @@ -48,10 +51,11 @@ netSubscription::~netSubscription () { } -void netSubscription::destroy ( cacRecycle &recycle ) +void netSubscription::destroy ( + epicsGuard < epicsMutex > & guard, cacRecycle & recycle ) { this->~netSubscription (); - recycle.recycleSubscription ( *this ); + recycle.recycleSubscription ( guard, *this ); } class netSubscription * netSubscription::isSubscription () @@ -67,31 +71,91 @@ void netSubscription::show ( unsigned /* level */ ) const this->count, this->mask ); } -void netSubscription::completion () +void netSubscription::completion ( + epicsGuard < epicsMutex > & guard, cacRecycle & ) { - this->chan.printf ( "subscription update w/o data ?\n" ); + if ( this->privateChanForIO.connected ( guard ) ) { + this->updateWhileDisconnected = false; + } + else { + this->updateWhileDisconnected = true; + } + this->privateChanForIO.printf ( "subscription update w/o data ?\n" ); } -void netSubscription::exception ( int status, const char *pContext ) +void netSubscription::exception ( + epicsGuard < epicsMutex > & guard, cacRecycle & recycle, + int status, const char * pContext ) { - this->notify.exception ( status, pContext, UINT_MAX, 0 ); + if ( status == ECA_CHANDESTROY ) { + this->notify.exception ( + guard, status, pContext, UINT_MAX, 0 ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netSubscription (); + recycle.recycleSubscription ( guard, *this ); + } + else { + // guard.assertIdenticalMutex ( this->mutex ); + if ( this->privateChanForIO.connected ( guard ) ) { + this->updateWhileDisconnected = false; + this->notify.exception ( + guard, status, pContext, UINT_MAX, 0 ); + } + else { + this->updateWhileDisconnected = true; + } + } } -void netSubscription::exception ( int status, const char *pContext, - unsigned typeIn, arrayElementCount countIn ) +void netSubscription::exception ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, int status, const char * pContext, + unsigned typeIn, arrayElementCount countIn ) { - this->notify.exception ( status, pContext, typeIn, countIn ); + if ( status == ECA_CHANDESTROY ) { + this->notify.exception ( + guard, status, pContext, UINT_MAX, 0 ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netSubscription (); + recycle.recycleSubscription ( guard, *this ); + } + else { + //guard.assertIdenticalMutex ( this->mutex ); + if ( this->privateChanForIO.connected ( guard ) ) { + this->updateWhileDisconnected = false; + this->notify.exception ( + guard, status, pContext, typeIn, countIn ); + } + else { + this->updateWhileDisconnected = true; + } + } } -void netSubscription::completion ( unsigned typeIn, - arrayElementCount countIn, const void *pDataIn ) +void netSubscription::completion ( + epicsGuard < epicsMutex > & guard, cacRecycle &, + unsigned typeIn, arrayElementCount countIn, + const void * pDataIn ) { - this->notify.current ( typeIn, countIn, pDataIn ); + // guard.assertIdenticalMutex ( this->mutex ); + if ( this->privateChanForIO.connected ( guard ) ) { + this->updateWhileDisconnected = false; + this->notify.current ( + guard, typeIn, countIn, pDataIn ); + } + else { + this->updateWhileDisconnected = true; + } } -nciu & netSubscription::channel () const +void netSubscription::subscriptionUpdateIfRequired ( + epicsGuard < epicsMutex > & guard, nciu & chan ) { - return this->chan; + if ( this->updateWhileDisconnected ) { + chan.getPIIU()->subscriptionUpdateRequest ( + guard, chan, *this ); + this->updateWhileDisconnected = false; + } } void * netSubscription::operator new ( size_t ) // X aCC 361 diff --git a/src/ca/netWriteNotifyIO.cpp b/src/ca/netWriteNotifyIO.cpp index 87c889b44..4696555b6 100644 --- a/src/ca/netWriteNotifyIO.cpp +++ b/src/ca/netWriteNotifyIO.cpp @@ -28,8 +28,9 @@ #include "nciu.h" #include "cac.h" -netWriteNotifyIO::netWriteNotifyIO ( nciu & chanIn, cacWriteNotify & notifyIn ) : - notify ( notifyIn ), chan ( chanIn ) +netWriteNotifyIO::netWriteNotifyIO ( + privateInterfaceForIO & ioComplIntf, cacWriteNotify & notifyIn ) : + notify ( notifyIn ), privateChanForIO ( ioComplIntf ) { } @@ -43,33 +44,59 @@ void netWriteNotifyIO::show ( unsigned /* level */ ) const static_cast < const void * > ( this ) ); } -void netWriteNotifyIO::destroy ( cacRecycle & recycle ) +void netWriteNotifyIO::destroy ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle ) { this->~netWriteNotifyIO (); - recycle.recycleWriteNotifyIO ( *this ); + recycle.recycleWriteNotifyIO ( guard, *this ); } -void netWriteNotifyIO::completion () +void netWriteNotifyIO::completion ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle ) { - this->notify.completion (); + this->notify.completion ( guard ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netWriteNotifyIO (); + recycle.recycleWriteNotifyIO ( guard, *this ); } -void netWriteNotifyIO::exception ( int status, const char *pContext ) +void netWriteNotifyIO::completion ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, + unsigned /* type */, arrayElementCount /* count */, + const void * /* pData */ ) { - this->notify.exception ( status, pContext, UINT_MAX, 0u ); + //this->chan.getClient().printf ( "Write response with data ?\n" ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netWriteNotifyIO (); + recycle.recycleWriteNotifyIO ( guard, *this ); } -void netWriteNotifyIO::exception ( int status, const char *pContext, - unsigned type, arrayElementCount count ) +void netWriteNotifyIO::exception ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, + int status, const char * pContext ) { - this->notify.exception ( status, pContext, type, count ); + this->notify.exception ( + guard, status, pContext, UINT_MAX, 0u ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netWriteNotifyIO (); + recycle.recycleWriteNotifyIO ( guard, *this ); } - -void netWriteNotifyIO::completion ( unsigned /* type */, - arrayElementCount /* count */, const void * /* pData */ ) +void netWriteNotifyIO::exception ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, + int status, const char *pContext, + unsigned type, arrayElementCount count ) { - this->chan.getClient().printf ( "Write response with data ?\n" ); + this->notify.exception ( + guard, status, pContext, type, count ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netWriteNotifyIO (); + recycle.recycleWriteNotifyIO ( guard, *this ); } class netSubscription * netWriteNotifyIO::isSubscription () @@ -77,16 +104,12 @@ class netSubscription * netWriteNotifyIO::isSubscription () return 0; } -nciu & netWriteNotifyIO::channel () const -{ - return this->chan; -} - void * netWriteNotifyIO::operator new ( size_t ) // X aCC 361 { // The HPUX compiler seems to require this even though no code // calls it directly - throw std::logic_error ( "why is the compiler calling private operator new" ); + throw std::logic_error ( + "why is the compiler calling private operator new" ); } void netWriteNotifyIO::operator delete ( void * ) diff --git a/src/ca/netiiu.cpp b/src/ca/netiiu.cpp index 3781d6048..3d862d0d5 100644 --- a/src/ca/netiiu.cpp +++ b/src/ca/netiiu.cpp @@ -42,35 +42,41 @@ bool netiiu::ca_v41_ok () const return false; } -void netiiu::writeRequest ( epicsGuard < cacMutex > &, nciu &, - unsigned, unsigned, const void * ) +void netiiu::writeRequest ( epicsGuard < epicsMutex > &, nciu &, + unsigned, arrayElementCount, const void * ) { throw cacChannel::notConnected(); } -void netiiu::writeNotifyRequest ( epicsGuard < cacMutex > &, - nciu &, netWriteNotifyIO &, unsigned, unsigned, const void * ) +void netiiu::writeNotifyRequest ( epicsGuard < epicsMutex > &, + nciu &, netWriteNotifyIO &, unsigned, arrayElementCount, const void * ) { throw cacChannel::notConnected(); } -void netiiu::readNotifyRequest ( epicsGuard < cacMutex > &, - nciu &, netReadNotifyIO &, unsigned, unsigned ) +void netiiu::readNotifyRequest ( epicsGuard < epicsMutex > &, + nciu &, netReadNotifyIO &, unsigned, arrayElementCount ) { throw cacChannel::notConnected(); } -void netiiu::clearChannelRequest ( epicsGuard < cacMutex > &, ca_uint32_t, ca_uint32_t ) +void netiiu::clearChannelRequest ( + epicsGuard < epicsMutex > &, ca_uint32_t, ca_uint32_t ) { } -void netiiu::subscriptionRequest ( epicsGuard < cacMutex > &, - nciu &, netSubscription & ) +void netiiu::subscriptionRequest ( + epicsGuard < epicsMutex > &, nciu &, netSubscription & ) { } -void netiiu::subscriptionCancelRequest ( epicsGuard < cacMutex > &, - nciu &, netSubscription & ) +void netiiu::subscriptionCancelRequest ( + epicsGuard < epicsMutex > &, nciu &, netSubscription & ) +{ +} + +void netiiu::subscriptionUpdateRequest ( + epicsGuard < epicsMutex > &, nciu &, netSubscription & ) { } @@ -94,21 +100,21 @@ osiSockAddr netiiu::getNetworkAddress () const return addr; } -void netiiu::flushRequest () +void netiiu::flushRequest ( epicsGuard < epicsMutex > & ) { } -bool netiiu::flushBlockThreshold ( epicsGuard < cacMutex > & ) const +bool netiiu::flushBlockThreshold ( epicsGuard < epicsMutex > & ) const { return false; } -void netiiu::flushRequestIfAboveEarlyThreshold ( epicsGuard < cacMutex > & ) +void netiiu::flushRequestIfAboveEarlyThreshold ( epicsGuard < epicsMutex > & ) { } void netiiu::blockUntilSendBacklogIsReasonable - ( cacNotify &, epicsGuard < cacMutex > & ) + ( cacContextNotify &, epicsGuard < epicsMutex > & ) { } @@ -117,8 +123,7 @@ void netiiu::requestRecvProcessPostponedFlush () return; } -void netiiu::uninstallChan ( - epicsGuard < callbackMutex > &, epicsGuard < cacMutex > &, nciu & ) +void netiiu::uninstallChan ( epicsGuard < epicsMutex > &, nciu & ) { throw cacChannel::notConnected(); } diff --git a/src/ca/netiiu.h b/src/ca/netiiu.h index 531c27a33..655d6357c 100644 --- a/src/ca/netiiu.h +++ b/src/ca/netiiu.h @@ -33,7 +33,6 @@ class netWriteNotifyIO; class netReadNotifyIO; class netSubscription; -class cacMutex; union osiSockAddr; class cac; @@ -46,28 +45,29 @@ public: virtual const char * pHostName () const = 0; // deprecated - please do not use virtual bool ca_v41_ok () const = 0; virtual bool ca_v42_ok () const = 0; - virtual void writeRequest ( epicsGuard < cacMutex > &, nciu &, - unsigned type, unsigned nElem, const void *pValue ) = 0; - virtual void writeNotifyRequest ( epicsGuard < cacMutex > &, - nciu &, netWriteNotifyIO &, - unsigned type, unsigned nElem, const void *pValue ) = 0; - virtual void readNotifyRequest ( epicsGuard < cacMutex > &, nciu &, - netReadNotifyIO &, unsigned type, unsigned nElem ) = 0; - virtual void clearChannelRequest ( epicsGuard < cacMutex > &, - ca_uint32_t sid, ca_uint32_t cid ) = 0; - virtual void subscriptionRequest ( epicsGuard < cacMutex > &, - nciu &, netSubscription &subscr ) = 0; - virtual void subscriptionCancelRequest ( epicsGuard < cacMutex > &, - nciu & chan, netSubscription & subscr ) = 0; - virtual void flushRequest () = 0; - virtual bool flushBlockThreshold ( epicsGuard < cacMutex > & ) const = 0; - virtual void flushRequestIfAboveEarlyThreshold ( epicsGuard < cacMutex > & ) = 0; + virtual void writeRequest ( epicsGuard < epicsMutex > &, nciu &, + unsigned type, arrayElementCount nElem, const void *pValue ) = 0; + virtual void writeNotifyRequest ( epicsGuard < epicsMutex > &, + nciu &, netWriteNotifyIO &, + unsigned type, arrayElementCount nElem, const void *pValue ) = 0; + virtual void readNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, + netReadNotifyIO &, unsigned type, arrayElementCount nElem ) = 0; + virtual void clearChannelRequest ( epicsGuard < epicsMutex > &, + ca_uint32_t sid, ca_uint32_t cid ) = 0; + virtual void subscriptionRequest ( epicsGuard < epicsMutex > &, + nciu &, netSubscription & ) = 0; + virtual void subscriptionUpdateRequest ( + epicsGuard < epicsMutex > &, nciu &, netSubscription & ) = 0; + virtual void subscriptionCancelRequest ( epicsGuard < epicsMutex > &, + nciu & chan, netSubscription & subscr ) = 0; + virtual void flushRequest ( epicsGuard < epicsMutex > & ) = 0; + virtual bool flushBlockThreshold ( epicsGuard < epicsMutex > & ) const = 0; + virtual void flushRequestIfAboveEarlyThreshold ( epicsGuard < epicsMutex > & ) = 0; virtual void blockUntilSendBacklogIsReasonable - ( cacNotify &, epicsGuard < cacMutex > & ) = 0; + ( cacContextNotify &, epicsGuard < epicsMutex > & ) = 0; virtual void requestRecvProcessPostponedFlush () = 0; virtual osiSockAddr getNetworkAddress () const = 0; - virtual void uninstallChan ( epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > &, nciu & ) = 0; + virtual void uninstallChan ( epicsGuard < epicsMutex > &, nciu & ) = 0; virtual double receiveWatchdogDelay () const = 0; }; diff --git a/src/ca/oldAccess.h b/src/ca/oldAccess.h index b62f82725..d99645ade 100644 --- a/src/ca/oldAccess.h +++ b/src/ca/oldAccess.h @@ -49,32 +49,42 @@ struct oldChannelNotify : public cacChannelNotify { public: - oldChannelNotify ( struct ca_client_context &, const char * pName, - caCh * pConnCallBackIn, void * pPrivateIn, capri priority ); - ~oldChannelNotify (); + oldChannelNotify ( + epicsGuard < epicsMutex > &, struct ca_client_context &, + const char * pName, caCh * pConnCallBackIn, + void * pPrivateIn, capri priority ); + void destructor ( + epicsGuard < epicsMutex > & ); void setPrivatePointer ( void * ); void * privatePointer () const; int changeConnCallBack ( caCh *pfunc ); int replaceAccessRightsEvent ( caArh *pfunc ); const char *pName () const; void show ( unsigned level ) const; - void initiateConnect (); + void initiateConnect ( + epicsGuard < epicsMutex > & ); void read ( + epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, cacReadNotify ¬ify, cacChannel::ioid *pId = 0 ); void read ( + epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, void *pValue ); void write ( + epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pValue ); void write ( + epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, const void *pValue, cacWriteNotify &, cacChannel::ioid *pId = 0 ); void subscribe ( + epicsGuard < epicsMutex > &, unsigned type, arrayElementCount count, unsigned mask, cacStateNotify &, cacChannel::ioid & ); - void ioCancel ( const cacChannel::ioid & ); + void ioCancel ( + epicsGuard < epicsMutex > &, const cacChannel::ioid & ); void ioShow ( const cacChannel::ioid &, unsigned level ) const; short nativeType () const; arrayElementCount nativeElementCount () const; @@ -89,9 +99,9 @@ public: const char * pHostName () const; // deprecated - please do not use ca_client_context & getClientCtx (); void * operator new ( size_t size, - tsFreeList < struct oldChannelNotify, 1024 > & ); + tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void * , - tsFreeList < struct oldChannelNotify, 1024 > & )) + tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & )) private: ca_client_context & cacCtx; cacChannel & io; @@ -101,14 +111,19 @@ private: unsigned ioSeqNo; bool currentlyConnected; bool prevConnected; - void connectNotify (); - void disconnectNotify (); + ~oldChannelNotify (); + void connectNotify ( epicsGuard < epicsMutex > & ); + void disconnectNotify ( epicsGuard < epicsMutex > & ); void serviceShutdownNotify (); - void accessRightsNotify ( const caAccessRights & ); - void exception ( int status, const char *pContext ); - void readException ( int status, const char *pContext, + void accessRightsNotify ( + epicsGuard < epicsMutex > &, const caAccessRights & ); + void exception ( epicsGuard < epicsMutex > &, + int status, const char * pContext ); + void readException ( epicsGuard < epicsMutex > &, + int status, const char * pContext, unsigned type, arrayElementCount count, void *pValue ); - void writeException ( int status, const char *pContext, + void writeException ( epicsGuard < epicsMutex > &, + int status, const char * pContext, unsigned type, arrayElementCount count ); oldChannelNotify ( const oldChannelNotify & ); oldChannelNotify & operator = ( const oldChannelNotify & ); @@ -118,25 +133,30 @@ private: class getCopy : public cacReadNotify { public: - getCopy ( ca_client_context &cacCtx, oldChannelNotify &, unsigned type, + getCopy ( + epicsGuard < epicsMutex > & guard, + ca_client_context & cacCtx, + oldChannelNotify &, unsigned type, arrayElementCount count, void *pValue ); ~getCopy (); void show ( unsigned level ) const; void cancel (); void * operator new ( size_t size, - tsFreeList < class getCopy, 1024 > & ); + tsFreeList < class getCopy, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, - tsFreeList < class getCopy, 1024 > & )) + tsFreeList < class getCopy, 1024, epicsMutexNOOP > & )) private: arrayElementCount count; - ca_client_context &cacCtx; - oldChannelNotify &chan; + ca_client_context & cacCtx; + oldChannelNotify & chan; void *pValue; unsigned ioSeqNo; unsigned type; void completion ( - unsigned type, arrayElementCount count, const void *pData); - void exception ( int status, + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void *pData ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); getCopy ( const getCopy & ); getCopy & operator = ( const getCopy & ); @@ -146,21 +166,24 @@ private: class getCallback : public cacReadNotify { public: - getCallback ( oldChannelNotify &chanIn, + getCallback ( + oldChannelNotify & chanIn, caEventCallBackFunc *pFunc, void *pPrivate ); ~getCallback (); void * operator new ( size_t size, - tsFreeList < class getCallback, 1024 > & ); + tsFreeList < class getCallback, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, - tsFreeList < class getCallback, 1024 > & )) + tsFreeList < class getCallback, 1024, epicsMutexNOOP > & )) private: oldChannelNotify & chan; caEventCallBackFunc * pFunc; void * pPrivate; void completion ( - unsigned type, arrayElementCount count, const void *pData); - void exception ( int status, - const char *pContext, unsigned type, arrayElementCount count ); + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void *pData); + void exception ( + epicsGuard < epicsMutex > &, int status, + const char * pContext, unsigned type, arrayElementCount count ); getCallback ( const getCallback & ); getCallback & operator = ( const getCallback & ); void * operator new ( size_t size ); @@ -169,19 +192,21 @@ private: class putCallback : public cacWriteNotify { public: - putCallback ( oldChannelNotify &, + putCallback ( + oldChannelNotify &, caEventCallBackFunc *pFunc, void *pPrivate ); ~putCallback (); void * operator new ( size_t size, - tsFreeList < class putCallback, 1024 > & ); + tsFreeList < class putCallback, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, - tsFreeList < class putCallback, 1024 > & )) + tsFreeList < class putCallback, 1024, epicsMutexNOOP > & )) private: oldChannelNotify & chan; caEventCallBackFunc * pFunc; void *pPrivate; - void completion (); - void exception ( int status, const char *pContext, + void completion ( epicsGuard < epicsMutex > & ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); putCallback ( const putCallback & ); putCallback & operator = ( const putCallback & ); @@ -194,13 +219,14 @@ public: oldSubscription ( oldChannelNotify &, caEventCallBackFunc *pFunc, void *pPrivate ); ~oldSubscription (); - void begin ( unsigned type, arrayElementCount nElem, unsigned mask ); + void begin ( epicsGuard < epicsMutex > & guard, unsigned type, + arrayElementCount nElem, unsigned mask ); oldChannelNotify & channel () const; void * operator new ( size_t size, - tsFreeList < struct oldSubscription, 1024 > & ); + tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, - tsFreeList < struct oldSubscription, 1024 > & )) - void ioCancel (); + tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & )) + void ioCancel ( epicsGuard < epicsMutex > & ); private: oldChannelNotify & chan; cacChannel::ioid id; @@ -208,8 +234,10 @@ private: void * pPrivate; bool subscribed; void current ( - unsigned type, arrayElementCount count, const void *pData ); - void exception ( int status, + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void *pData ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); oldSubscription ( const oldSubscription & ); oldSubscription & operator = ( const oldSubscription & ); @@ -217,16 +245,7 @@ private: void operator delete ( void * ); }; -class ca_client_context_mutex { -public: - void lock (); - void unlock (); - void show ( unsigned level ) const; -private: - epicsMutex mutex; -}; - -struct ca_client_context : public cacNotify +struct ca_client_context : public cacContextNotify { public: ca_client_context ( bool enablePreemptiveCallback = false ); @@ -234,59 +253,68 @@ public: void changeExceptionEvent ( caExceptionHandler * pfunc, void * arg ); void registerForFileDescriptorCallBack ( CAFDHANDLER * pFunc, void * pArg ); void replaceErrLogHandler ( caPrintfFunc * ca_printf_func ); - void registerService ( cacService & service ); - cacChannel & createChannel ( const char * name_str, - oldChannelNotify & chan, cacChannel::priLev pri ); - void flushRequest (); + cacChannel & createChannel ( + epicsGuard < epicsMutex > &, const char * pChannelName, + oldChannelNotify &, cacChannel::priLev pri ); + void flush ( epicsGuard < epicsMutex > & ); int pendIO ( const double & timeout ); int pendEvent ( const double & timeout ); bool ioComplete () const; void show ( unsigned level ) const; - unsigned connectionCount () const; - unsigned sequenceNumberOfOutstandingIO () const; + unsigned circuitCount () const; + unsigned sequenceNumberOfOutstandingIO ( + epicsGuard < epicsMutex > & ) const; unsigned beaconAnomaliesSinceProgramStart () const; - void incrementOutstandingIO ( unsigned ioSeqNo ); - void decrementOutstandingIO ( unsigned ioSeqNo ); - void exception ( int status, const char *pContext, - const char *pFileName, unsigned lineNo ); - void exception ( int status, const char *pContext, - const char *pFileName, unsigned lineNo, oldChannelNotify &chan, + void incrementOutstandingIO ( + epicsGuard < epicsMutex > &, unsigned ioSeqNo ); + void decrementOutstandingIO ( + epicsGuard < epicsMutex > &, unsigned ioSeqNo ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char * pContext, + const char * pFileName, unsigned lineNo ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char * pContext, + const char * pFileName, unsigned lineNo, oldChannelNotify & chan, unsigned type, arrayElementCount count, unsigned op ); void blockForEventAndEnableCallbacks ( epicsEvent & event, const double & timeout ); - CASG * lookupCASG ( unsigned id ); - void installCASG ( CASG & ); - void uninstallCASG ( CASG & ); - void selfTest (); + CASG * lookupCASG ( epicsGuard < epicsMutex > &, unsigned id ); + void installCASG ( epicsGuard < epicsMutex > &, CASG & ); + void uninstallCASG ( epicsGuard < epicsMutex > &, CASG & ); + void selfTest () const; // perhaps these should be eliminated in deference to the exception mechanism - int printf ( const char *pformat, ... ) const; - int vPrintf ( const char *pformat, va_list args ) const; - void vSignal ( int ca_status, const char *pfilenm, - int lineno, const char *pFormat, va_list args ); + int printf ( const char * pformat, ... ) const; + int vPrintf ( const char * pformat, va_list args ) const; + void signal ( int ca_status, const char * pfilenm, + int lineno, const char * pFormat, ... ); + void vSignal ( int ca_status, const char * pfilenm, + int lineno, const char *pFormat, va_list args ); bool preemptiveCallbakIsEnabled () const; void destroyChannel ( oldChannelNotify & chan ); - void destroyGetCopy ( getCopy & ); - void destroyGetCallback ( getCallback & ); - void destroyPutCallback ( putCallback & ); - void destroySubscription ( oldSubscription & ); + void destroyGetCopy ( epicsGuard < epicsMutex > &, getCopy & ); + void destroyGetCallback ( epicsGuard < epicsMutex > &, getCallback & ); + void destroyPutCallback ( epicsGuard < epicsMutex > &, putCallback & ); + void destroySubscription ( epicsGuard < epicsMutex > &, oldSubscription & ); void changeConnCallBack ( caCh * pfunc, caCh * & pConnCallBack, const bool & currentlyConnected ); + epicsMutex & mutexRef () const; // exceptions class noSocket {}; private: - tsFreeList < struct oldChannelNotify, 1024 > oldChannelNotifyFreeList; - tsFreeList < class getCopy, 1024 > getCopyFreeList; - tsFreeList < class getCallback, 1024 > getCallbackFreeList; - tsFreeList < class putCallback, 1024 > putCallbackFreeList; - tsFreeList < struct oldSubscription, 1024 > subscriptionFreeList; - tsFreeList < struct CASG, 128 > casgFreeList; - mutable ca_client_context_mutex mutex; + chronIntIdResTable < CASG > sgTable; + tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > oldChannelNotifyFreeList; + tsFreeList < class getCopy, 1024, epicsMutexNOOP > getCopyFreeList; + tsFreeList < class getCallback, 1024, epicsMutexNOOP > getCallbackFreeList; + tsFreeList < class putCallback, 1024, epicsMutexNOOP > putCallbackFreeList; + tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > subscriptionFreeList; + tsFreeList < struct CASG, 128, epicsMutexNOOP > casgFreeList; + mutable epicsMutex mutex; epicsMutex callbackMutex; epicsEvent ioDone; epicsEvent callbackThreadActivityComplete; - epics_auto_ptr < class cac > pClientCtx; epics_auto_ptr < epicsGuard < epicsMutex > > pCallbackGuard; + epics_auto_ptr < cacContext > pServiceContext; caExceptionHandler * ca_exception_func; void * ca_exception_arg; caPrintfFunc * pVPrintfFunc; @@ -303,29 +331,38 @@ private: void callbackLock (); void callbackUnlock (); void attachToClientCtx (); + cacContext & createNetworkContext ( epicsMutex & mutex ); ca_client_context ( const ca_client_context & ); ca_client_context & operator = ( const ca_client_context & ); + static cacService * pDefaultService; + static epicsMutex defaultServiceInstallMutex; + friend int epicsShareAPI ca_create_channel ( const char * name_str, caCh * conn_func, void * puser, capri priority, chid * chanptr ); friend int epicsShareAPI ca_array_get ( chtype type, - arrayElementCount count, chid pChan, void *pValue ); + arrayElementCount count, chid pChan, void * pValue ); friend int epicsShareAPI ca_array_get_callback ( chtype type, - arrayElementCount count, chid pChan, - caEventCallBackFunc *pfunc, void *arg ); + arrayElementCount count, chid pChan, + caEventCallBackFunc *pfunc, void *arg ); + friend int epicsShareAPI ca_array_put ( chtype type, + arrayElementCount count, chid pChan, const void * pValue ); friend int epicsShareAPI ca_array_put_callback ( chtype type, - arrayElementCount count, chid pChan, const void *pValue, - caEventCallBackFunc *pfunc, void *usrarg ); + arrayElementCount count, chid pChan, const void * pValue, + caEventCallBackFunc *pfunc, void *usrarg ); friend int epicsShareAPI ca_create_subscription ( - chtype type, arrayElementCount count, chid pChan, - long mask, caEventCallBackFunc *pCallBack, void *pCallBackArg, - evid *monixptr ); + chtype type, arrayElementCount count, chid pChan, + long mask, caEventCallBackFunc * pCallBack, void * pCallBackArg, + evid *monixptr ); + friend int epicsShareAPI ca_clear_subscription ( evid pMon ); + friend int epicsShareAPI ca_flush_io (); friend int epicsShareAPI ca_sg_create ( CA_SYNC_GID * pgid ); friend int epicsShareAPI ca_sg_delete ( const CA_SYNC_GID gid ); + friend void epicsShareAPI caInstallDefaultService ( cacService & ); }; -int fetchClientContext ( ca_client_context **ppcac ); +int fetchClientContext ( ca_client_context * * ppcac ); inline ca_client_context & oldChannelNotify::getClientCtx () { @@ -342,42 +379,20 @@ inline void oldChannelNotify::show ( unsigned level ) const this->io.show ( level ); } -inline void oldChannelNotify::initiateConnect () +inline void oldChannelNotify::initiateConnect ( + epicsGuard < epicsMutex > & guard ) { - this->io.initiateConnect (); + this->io.initiateConnect ( guard ); } -inline void oldChannelNotify::read ( unsigned type, arrayElementCount count, - cacReadNotify ¬ify, cacChannel::ioid * pId ) +inline void oldChannelNotify::ioCancel ( + epicsGuard < epicsMutex > & guard, const cacChannel::ioid & id ) { - this->io.read ( type, count, notify, pId ); + this->io.ioCancel ( guard, id ); } -inline void oldChannelNotify::write ( unsigned type, - arrayElementCount count, const void * pValue ) -{ - this->io.write ( type, count, pValue ); -} - -inline void oldChannelNotify::write ( unsigned type, arrayElementCount count, - const void * pValue, cacWriteNotify & notify, cacChannel::ioid * pId ) -{ - this->io.write ( type, count, pValue, notify, pId ); -} - -inline void oldChannelNotify::subscribe ( unsigned type, - arrayElementCount count, unsigned mask, cacStateNotify & notify, - cacChannel::ioid & idOut) -{ - this->io.subscribe ( type, count, mask, notify, &idOut ); -} - -inline void oldChannelNotify::ioCancel ( const cacChannel::ioid &id ) -{ - this->io.ioCancel ( id ); -} - -inline void oldChannelNotify::ioShow ( const cacChannel::ioid &id, unsigned level ) const +inline void oldChannelNotify::ioShow ( + const cacChannel::ioid & id, unsigned level ) const { this->io.ioShow ( id, level ); } @@ -438,45 +453,46 @@ inline const char * oldChannelNotify::pHostName () const } inline void * oldChannelNotify::operator new ( size_t size, - tsFreeList < struct oldChannelNotify, 1024 > & freeList ) + tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void oldChannelNotify::operator delete ( void *pCadaver, - tsFreeList < struct oldChannelNotify, 1024 > & freeList ) + tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline oldSubscription::oldSubscription ( - oldChannelNotify & chanIn, - caEventCallBackFunc * pFuncIn, void * pPrivateIn ) : + oldChannelNotify & chanIn, caEventCallBackFunc * pFuncIn, + void * pPrivateIn ) : chan ( chanIn ), id ( UINT_MAX ), pFunc ( pFuncIn ), pPrivate ( pPrivateIn ), subscribed ( false ) { } -inline void oldSubscription::begin ( unsigned type, - arrayElementCount nElem, unsigned mask ) +inline void oldSubscription::begin ( + epicsGuard < epicsMutex > & guard, unsigned type, + arrayElementCount nElem, unsigned mask ) { this->subscribed = true; - this->chan.subscribe ( type, nElem, mask, *this, this->id ); + this->chan.subscribe ( guard, type, nElem, mask, *this, this->id ); // dont touch this pointer after this point because the // 1st update callback might cancel the subscription } inline void * oldSubscription::operator new ( size_t size, - tsFreeList < struct oldSubscription, 1024 > & freeList ) + tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void oldSubscription::operator delete ( void *pCadaver, - tsFreeList < struct oldSubscription, 1024 > & freeList ) + tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } @@ -489,49 +505,48 @@ inline oldChannelNotify & oldSubscription::channel () const inline int oldChannelNotify::changeConnCallBack ( caCh * pfunc ) { - // operation protected by call back lock in ca_client_context this->cacCtx.changeConnCallBack ( pfunc, this->pConnCallBack, this->currentlyConnected ); return ECA_NORMAL; } inline void * getCopy::operator new ( size_t size, - tsFreeList < class getCopy, 1024 > & freeList ) + tsFreeList < class getCopy, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void getCopy::operator delete ( void *pCadaver, - tsFreeList < class getCopy, 1024 > & freeList ) + tsFreeList < class getCopy, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline void * putCallback::operator new ( size_t size, - tsFreeList < class putCallback, 1024 > & freeList ) + tsFreeList < class putCallback, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE inline void putCallback::operator delete ( void * pCadaver, - tsFreeList < class putCallback, 1024 > & freeList ) + tsFreeList < class putCallback, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif inline void * getCallback::operator new ( size_t size, - tsFreeList < class getCallback, 1024 > & freeList ) + tsFreeList < class getCallback, 1024, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE -inline void getCallback::operator delete ( void *pCadaver, - tsFreeList < class getCallback, 1024 > & freeList ) +inline void getCallback::operator delete ( void * pCadaver, + tsFreeList < class getCallback, 1024, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } @@ -547,25 +562,11 @@ inline bool ca_client_context::ioComplete () const return ( this->pndRecvCnt == 0u ); } -inline unsigned ca_client_context::sequenceNumberOfOutstandingIO () const +inline unsigned ca_client_context::sequenceNumberOfOutstandingIO ( + epicsGuard < epicsMutex > & ) const { // perhaps on SMP systems THERE should be lock/unlock around this return this->ioSeqNo; } -inline void ca_client_context_mutex::lock () -{ - this->mutex.lock (); -} - -inline void ca_client_context_mutex::unlock () -{ - this->mutex.unlock (); -} - -inline void ca_client_context_mutex::show ( unsigned level ) const -{ - this->mutex.show ( level ); -} - #endif // ifndef oldAccessh diff --git a/src/ca/oldChannelNotify.cpp b/src/ca/oldChannelNotify.cpp index 762c7bac8..32ffc7669 100644 --- a/src/ca/oldChannelNotify.cpp +++ b/src/ca/oldChannelNotify.cpp @@ -41,56 +41,70 @@ extern "C" void cacNoopAccesRightsHandler ( struct access_rights_handler_args ) { } -oldChannelNotify::oldChannelNotify ( ca_client_context & cacIn, const char *pName, - caCh * pConnCallBackIn, void * pPrivateIn, capri priority ) : +oldChannelNotify::oldChannelNotify ( + epicsGuard < epicsMutex > & guard, ca_client_context & cacIn, + const char *pName, caCh * pConnCallBackIn, + void * pPrivateIn, capri priority ) : cacCtx ( cacIn ), - io ( cacIn.createChannel ( pName, *this, priority ) ), + io ( cacIn.createChannel ( guard, pName, *this, priority ) ), pConnCallBack ( pConnCallBackIn ), pPrivate ( pPrivateIn ), pAccessRightsFunc ( cacNoopAccesRightsHandler ), - ioSeqNo ( cacIn.sequenceNumberOfOutstandingIO () ), - currentlyConnected ( false ), prevConnected ( false ) + ioSeqNo ( 0 ), currentlyConnected ( false ), prevConnected ( false ) { - // no need to worry about a connect preempting here because - // the connect sequence will not start untill initiateConnect() - // is called + guard.assertIdenticalMutex ( cacIn.mutexRef () ); + this->ioSeqNo = cacIn.sequenceNumberOfOutstandingIO ( guard ); if ( pConnCallBackIn == 0 ) { - this->cacCtx.incrementOutstandingIO ( cacIn.sequenceNumberOfOutstandingIO () ); + cacIn.incrementOutstandingIO ( guard, this->ioSeqNo ); } } oldChannelNotify::~oldChannelNotify () { - this->io.destroy (); +} +void oldChannelNotify::destructor ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + this->io.destroy ( guard ); // no need to worry about a connect preempting here because // the io (the nciu) has been destroyed above if ( this->pConnCallBack == 0 && ! this->currentlyConnected ) { - this->cacCtx.decrementOutstandingIO ( this->ioSeqNo ); + this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); } + this->~oldChannelNotify (); } void oldChannelNotify::setPrivatePointer ( void *pPrivateIn ) { + epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef () ); this->pPrivate = pPrivateIn; } void * oldChannelNotify::privatePointer () const { + epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef () ); return this->pPrivate; } int oldChannelNotify::replaceAccessRightsEvent ( caArh *pfunc ) { - // The order of the following is significant to guarantee that the - // access rights handler is always gets called even if the channel connects - // while this is running. There is some very small chance that the - // handler could be called twice here with the same access rights state, but - // that will not upset the application. - this->pAccessRightsFunc = pfunc ? pfunc : cacNoopAccesRightsHandler; - if ( this->currentlyConnected ) { + bool isConnected; + caAccessRights tmp; + { + epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef () ); + // The order of the following is significant to guarantee that the + // access rights handler is always gets called even if the channel connects + // while this is running. There is some very small chance that the + // handler could be called twice here with the same access rights state, but + // that will not upset the application. + this->pAccessRightsFunc = pfunc ? pfunc : cacNoopAccesRightsHandler; + isConnected = this->currentlyConnected; + tmp = this->io.accessRights (); + } + if ( isConnected ) { struct access_rights_handler_args args; args.chid = this; - caAccessRights tmp = this->io.accessRights (); args.ar.read_access = tmp.readPermit (); args.ar.write_access = tmp.writePermit (); ( *pfunc ) ( args ); @@ -98,7 +112,8 @@ int oldChannelNotify::replaceAccessRightsEvent ( caArh *pfunc ) return ECA_NORMAL; } -void oldChannelNotify::connectNotify () +void oldChannelNotify::connectNotify ( + epicsGuard < epicsMutex > & guard ) { this->currentlyConnected = true; this->prevConnected = true; @@ -106,26 +121,34 @@ void oldChannelNotify::connectNotify () struct connection_handler_args args; args.chid = this; args.op = CA_OP_CONN_UP; - ( *this->pConnCallBack ) ( args ); - + caCh * pFunc = this->pConnCallBack; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFunc ) ( args ); + } } else { - this->cacCtx.decrementOutstandingIO ( this->ioSeqNo ); + this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); } - } -void oldChannelNotify::disconnectNotify () +void oldChannelNotify::disconnectNotify ( + epicsGuard < epicsMutex > & guard ) { this->currentlyConnected = false; if ( this->pConnCallBack ) { struct connection_handler_args args; args.chid = this; args.op = CA_OP_CONN_DOWN; - ( *this->pConnCallBack ) ( args ); + caCh * pFunc = this->pConnCallBack; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFunc ) ( args ); + } } else { - this->cacCtx.incrementOutstandingIO ( this->ioSeqNo ); + this->cacCtx.incrementOutstandingIO ( + guard, this->ioSeqNo ); } } @@ -134,31 +157,39 @@ void oldChannelNotify::serviceShutdownNotify () this->cacCtx.destroyChannel ( *this ); } -void oldChannelNotify::accessRightsNotify ( const caAccessRights &ar ) +void oldChannelNotify::accessRightsNotify ( + epicsGuard < epicsMutex > & guard, const caAccessRights & ar ) { struct access_rights_handler_args args; args.chid = this; args.ar.read_access = ar.readPermit(); args.ar.write_access = ar.writePermit(); - ( *this->pAccessRightsFunc ) ( args ); + caArh * pFunc = this->pAccessRightsFunc; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFunc ) ( args ); + } } -void oldChannelNotify::exception ( int status, const char *pContext ) +void oldChannelNotify::exception ( + epicsGuard < epicsMutex > & guard, int status, const char * pContext ) { - this->cacCtx.exception ( status, pContext, __FILE__, __LINE__ ); + this->cacCtx.exception ( guard, status, pContext, __FILE__, __LINE__ ); } -void oldChannelNotify::readException ( int status, const char *pContext, +void oldChannelNotify::readException ( + epicsGuard < epicsMutex > & guard, int status, const char *pContext, unsigned type, arrayElementCount count, void * /* pValue */ ) { - this->cacCtx.exception ( status, pContext, + this->cacCtx.exception ( guard, status, pContext, __FILE__, __LINE__, *this, type, count, CA_OP_GET ); } -void oldChannelNotify::writeException ( int status, const char *pContext, +void oldChannelNotify::writeException ( + epicsGuard < epicsMutex > & guard, int status, const char *pContext, unsigned type, arrayElementCount count ) { - this->cacCtx.exception ( status, pContext, + this->cacCtx.exception ( guard, status, pContext, __FILE__, __LINE__, *this, type, count, CA_OP_PUT ); } @@ -180,3 +211,34 @@ void oldChannelNotify::operator delete ( void * ) __FILE__, __LINE__ ); } +void oldChannelNotify::read ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount count, + cacReadNotify & notify, cacChannel::ioid * pId ) +{ + this->io.read ( guard, type, count, notify, pId ); +} + +void oldChannelNotify::write ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount count, const void * pValue ) +{ + this->io.write ( guard, type, count, pValue ); +} + +void oldChannelNotify::write ( + epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count, + const void * pValue, cacWriteNotify & notify, cacChannel::ioid * pId ) +{ + this->io.write ( guard, type, count, pValue, notify, pId ); +} + +void oldChannelNotify::subscribe ( + epicsGuard < epicsMutex > & guard, unsigned type, + arrayElementCount count, unsigned mask, cacStateNotify & notify, + cacChannel::ioid & idOut) +{ + this->io.subscribe ( guard, type, count, mask, notify, &idOut ); +} + + diff --git a/src/ca/oldSubscription.cpp b/src/ca/oldSubscription.cpp index 50697872a..790acc4f2 100644 --- a/src/ca/oldSubscription.cpp +++ b/src/ca/oldSubscription.cpp @@ -29,44 +29,53 @@ oldSubscription::~oldSubscription () { } -void oldSubscription::ioCancel () +void oldSubscription::ioCancel ( epicsGuard < epicsMutex > & guard ) { if ( this->subscribed ) { - this->chan.ioCancel ( this->id ); + this->chan.ioCancel ( guard, this->id ); } } -void oldSubscription::current ( +void oldSubscription::current ( + epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count, const void * pData ) { struct event_handler_args args; - args.usr = this->pPrivate; args.chid = & this->chan; args.type = static_cast < long > ( type ); args.count = static_cast < long > ( count ); args.status = ECA_NORMAL; args.dbr = pData; - ( *this->pFunc ) ( args ); + caEventCallBackFunc * pFuncTmp = this->pFunc; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } } void oldSubscription::exception ( + epicsGuard < epicsMutex > & guard, int status, const char * /* pContext */, unsigned type, arrayElementCount count ) { if ( status == ECA_CHANDESTROY ) { ca_client_context & cac = this->chan.getClientCtx (); - cac.destroySubscription ( *this ); + cac.destroySubscription ( guard, *this ); } else if ( status != ECA_DISCONN ) { struct event_handler_args args; args.usr = this->pPrivate; - args.chid = &this->chan; + args.chid = & this->chan; args.type = type; args.count = count; args.status = status; args.dbr = 0; - ( *this->pFunc ) ( args ); + caEventCallBackFunc * pFuncTmp = this->pFunc; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } } } diff --git a/src/ca/putCallback.cpp b/src/ca/putCallback.cpp index 641284590..5aa340a59 100644 --- a/src/ca/putCallback.cpp +++ b/src/ca/putCallback.cpp @@ -31,8 +31,9 @@ #include "iocinf.h" #include "oldAccess.h" -putCallback::putCallback ( oldChannelNotify &chanIn, - caEventCallBackFunc *pFuncIn, void *pPrivateIn ) : +putCallback::putCallback ( + oldChannelNotify & chanIn, caEventCallBackFunc * pFuncIn, + void * pPrivateIn ) : chan ( chanIn ), pFunc ( pFuncIn ), pPrivate ( pPrivateIn ) { } @@ -41,7 +42,7 @@ putCallback::~putCallback () { } -void putCallback::completion () +void putCallback::completion ( epicsGuard < epicsMutex > & guard ) { struct event_handler_args args; @@ -51,11 +52,16 @@ void putCallback::completion () args.count = 0; args.status = ECA_NORMAL; args.dbr = 0; - ( *this->pFunc ) (args); - this->chan.getClientCtx().destroyPutCallback ( *this ); + caEventCallBackFunc * pFuncTmp = this->pFunc; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } + this->chan.getClientCtx().destroyPutCallback ( guard, *this ); } void putCallback::exception ( + epicsGuard < epicsMutex > & guard, int status, const char * /* pContext */, unsigned type, arrayElementCount count ) { @@ -67,9 +73,13 @@ void putCallback::exception ( args.count = count; args.status = status; args.dbr = 0; - ( *this->pFunc ) (args); + caEventCallBackFunc * pFuncTmp = this->pFunc; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) (args); + } } - this->chan.getClientCtx().destroyPutCallback ( *this ); + this->chan.getClientCtx().destroyPutCallback ( guard, *this ); } void * putCallback::operator new ( size_t ) // X aCC 361 diff --git a/src/ca/repeater.cpp b/src/ca/repeater.cpp index 03dc5b431..b732f066e 100644 --- a/src/ca/repeater.cpp +++ b/src/ca/repeater.cpp @@ -87,11 +87,11 @@ #include "envDefs.h" #include "tsFreeList.h" #include "osiWireFormat.h" +#include "taskwd.h" #define epicsExportSharedSymbols #include "iocinf.h" #include "caProto.h" -#include "taskwd.h" #include "udpiiu.h" #include "repeaterClient.h" diff --git a/src/ca/repeaterSubscribeTimer.cpp b/src/ca/repeaterSubscribeTimer.cpp index 399d19a85..065467b17 100644 --- a/src/ca/repeaterSubscribeTimer.cpp +++ b/src/ca/repeaterSubscribeTimer.cpp @@ -41,6 +41,11 @@ repeaterSubscribeTimer::~repeaterSubscribeTimer () this->timer.destroy (); } +void repeaterSubscribeTimer::shutdown () +{ + this->timer.cancel (); +} + epicsTimerNotify::expireStatus repeaterSubscribeTimer:: expire ( const epicsTime & /* currentTime */ ) // X aCC 361 { diff --git a/src/ca/repeaterSubscribeTimer.h b/src/ca/repeaterSubscribeTimer.h index 8476f2179..bf6679cf6 100644 --- a/src/ca/repeaterSubscribeTimer.h +++ b/src/ca/repeaterSubscribeTimer.h @@ -35,6 +35,7 @@ class repeaterSubscribeTimer : private epicsTimerNotify { public: repeaterSubscribeTimer ( udpiiu &, epicsTimerQueue & ); virtual ~repeaterSubscribeTimer (); + void shutdown (); void confirmNotify (); void show ( unsigned level ) const; private: diff --git a/src/ca/searchTimer.cpp b/src/ca/searchTimer.cpp index e8fe8e996..0966f0e52 100644 --- a/src/ca/searchTimer.cpp +++ b/src/ca/searchTimer.cpp @@ -23,6 +23,10 @@ #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" +#if 0 +#define DEBUG +#endif + #include "tsMinMax.h" #define epicsExportSharedSymbols @@ -47,7 +51,7 @@ searchTimer::searchTimer ( udpiiu & iiuIn, iiu ( iiuIn ), mutex ( mutexIn ), framesPerTry ( initialTriesPerFrame ), - framesPerTryCongestThresh ( UINT_MAX ), + framesPerTryCongestThresh ( DBL_MAX ), minRetry ( 0 ), minRetryThisPass ( UINT_MAX ), searchAttempts ( 0u ), @@ -55,7 +59,8 @@ searchTimer::searchTimer ( udpiiu & iiuIn, searchAttemptsThisPass ( 0u ), searchResponsesThisPass ( 0u ), dgSeqNoAtTimerExpireBegin ( 0u ), - dgSeqNoAtTimerExpireEnd ( 0u ) + dgSeqNoAtTimerExpireEnd ( 0u ), + stopped ( false ) { } @@ -64,22 +69,30 @@ searchTimer::~searchTimer () this->timer.destroy (); } +void searchTimer::shutdown () +{ + this->stopped = true; + this->timer.cancel (); +} + void searchTimer::newChannelNotify ( epicsGuard < udpMutex > & guard, const epicsTime & currentTime, bool firstChannel, unsigned minRetryNo ) { - if ( firstChannel ) { - this->recomputeTimerPeriod ( guard, minRetryNo ); - double newPeriod = this->period; - { - // avoid timer cancel block deadlock - epicsGuardRelease < udpMutex > unguard ( guard ); - this->timer.start ( *this, currentTime + newPeriod ); + if ( ! this->stopped ) { + if ( firstChannel ) { + this->recomputeTimerPeriod ( guard, minRetryNo ); + double newPeriod = this->period; + { + // avoid timer cancel block deadlock + epicsGuardRelease < udpMutex > unguard ( guard ); + this->timer.start ( *this, currentTime + newPeriod ); + } + } + else { + this->recomputeTimerPeriodAndStartTimer ( guard, + currentTime, minRetryNo, 0.0 ); } - } - else { - this->recomputeTimerPeriodAndStartTimer ( guard, - currentTime, minRetryNo, 0.0 ); } } @@ -110,7 +123,7 @@ void searchTimer::recomputeTimerPeriod ( void searchTimer::recomputeTimerPeriodAndStartTimer ( epicsGuard < udpMutex > & guard, const epicsTime & currentTime, unsigned minRetryNew, const double & initialDelay ) { - if ( this->iiu.channelCount ( guard ) == 0 ) { + if ( this->iiu.unresolvedChannelCount ( guard ) == 0 || this->stopped ) { return; } @@ -159,7 +172,7 @@ void searchTimer::recomputeTimerPeriodAndStartTimer ( epicsGuard < udpMutex > & void searchTimer::notifySearchResponse ( epicsGuard < udpMutex > & guard, ca_uint32_t respDatagramSeqNo, bool seqNumberIsValid, const epicsTime & currentTime ) { - if ( this->iiu.channelCount ( guard ) == 0 ) { + if ( this->iiu.unresolvedChannelCount ( guard ) == 0 || this->stopped ) { return; } @@ -186,14 +199,7 @@ void searchTimer::notifySearchResponse ( epicsGuard < udpMutex > & guard, } if ( reschedualNeeded ) { -# if defined(DEBUG) && 0 - char buf[64]; - epicsTime ts = currentTime; - ts.strftime ( buf, sizeof(buf), "%M:%S.%09f"); -# endif - // debugPrintf ( ( "Response set timer delay to zero. ts=%s\n", - // buf ) ); - + debugPrintf ( ( "Response set timer delay to zero\n" ) ); // avoid timer cancel block deadlock epicsGuardRelease < udpMutex > unguard ( guard ); this->timer.start ( *this, currentTime ); @@ -208,12 +214,13 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi epicsGuard < udpMutex > guard ( this->mutex ); // check to see if there is nothing to do here - if ( this->iiu.channelCount ( guard ) == 0 ) { + if ( this->iiu.unresolvedChannelCount ( guard ) == 0 ) { debugPrintf ( ( "all channels located - search timer terminating\n" ) ); this->period = DBL_MAX; return noRestart; } +#if 0 // // dynamically adjust the number of UDP frames per // try depending how many search requests are not @@ -260,9 +267,37 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi this->framesPerTry--; } this->framesPerTryCongestThresh = this->framesPerTry/2 + 1; + debugPrintf ( ("Congestion detected - set frames per try to %f t=%u r=%u\n", + this->framesPerTry, this->searchAttempts, this->searchResponses) ); + } +#else + if ( this->searchResponses == this->searchAttempts ) { + // increase UDP frames per try if we have a good score + if ( this->framesPerTry < maxTriesPerFrame ) { + // a congestion avoidance threshold similar to TCP is now used + if ( this->framesPerTry < this->framesPerTryCongestThresh ) { + double doubled = 2 * this->framesPerTry; + if ( doubled > this->framesPerTryCongestThresh ) { + this->framesPerTry = this->framesPerTryCongestThresh; + } + else { + this->framesPerTry = doubled; + } + } + else { + this->framesPerTry += 1.0 / this->framesPerTry; + } + debugPrintf ( ("Increasing frame count to %u t=%u r=%u\n", + this->framesPerTry, this->searchAttempts, this->searchResponses) ); + } + } + else { + this->framesPerTryCongestThresh = this->framesPerTry / 2.0; + this->framesPerTry = 1u; debugPrintf ( ("Congestion detected - set frames per try to %u t=%u r=%u\n", this->framesPerTry, this->searchAttempts, this->searchResponses) ); } +#endif if ( this->searchAttemptsThisPass <= UINT_MAX - this->searchAttempts ) { this->searchAttemptsThisPass += this->searchAttempts; @@ -287,7 +322,7 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi while ( true ) { // check to see if we have reached the end of the list - if ( this->searchAttemptsThisPass >= this->iiu.channelCount ( guard ) ) { + if ( this->searchAttemptsThisPass >= this->iiu.unresolvedChannelCount ( guard ) ) { // if we are making some progress then dont increase the // delay between search requests if ( this->searchResponsesThisPass == 0u ) { @@ -330,7 +365,7 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi // // dont send any of the channels twice within one try // - if ( nChanSent >= this->iiu.channelCount ( guard ) ) { + if ( nChanSent >= this->iiu.unresolvedChannelCount ( guard ) ) { // // add one to nFrameSent because there may be // one more partial frame to be sent @@ -363,7 +398,7 @@ epicsTimerNotify::expireStatus searchTimer::expire ( const epicsTime & currentTi nFrameSent, this->period, buf ) ); # endif - if ( this->iiu.channelCount ( guard ) == 0 ) { + if ( this->iiu.unresolvedChannelCount ( guard ) == 0 ) { debugPrintf ( ( "all channels connected\n" ) ); this->period = DBL_MAX; return noRestart; diff --git a/src/ca/searchTimer.h b/src/ca/searchTimer.h index 01d3be8f7..513a6dd0b 100644 --- a/src/ca/searchTimer.h +++ b/src/ca/searchTimer.h @@ -57,14 +57,15 @@ public: unsigned minRetryNo ); void beaconAnomalyNotify ( epicsGuard < udpMutex > &, const epicsTime & currentTime, const double & delay ); + void shutdown (); void show ( unsigned level ) const; private: double period; /* period between tries */ epicsTimer & timer; class udpiiu & iiu; udpMutex & mutex; - unsigned framesPerTry; /* # of UDP frames per search try */ - unsigned framesPerTryCongestThresh; /* one half N tries w congest */ + double framesPerTry; /* # of UDP frames per search try */ + double framesPerTryCongestThresh; /* one half N tries w congest */ unsigned minRetry; /* min retry number so far */ unsigned minRetryThisPass; unsigned searchAttempts; /* num search tries within this timer experation */ @@ -73,6 +74,7 @@ private: unsigned searchResponsesThisPass; /* num search resp within this pass */ ca_uint32_t dgSeqNoAtTimerExpireBegin; ca_uint32_t dgSeqNoAtTimerExpireEnd; + bool stopped; expireStatus expire ( const epicsTime & currentTime ); void recomputeTimerPeriod ( epicsGuard < udpMutex > &, unsigned minRetryNew ); void recomputeTimerPeriodAndStartTimer ( epicsGuard < udpMutex > &, diff --git a/src/ca/sgAutoPtr.h b/src/ca/sgAutoPtr.h index 910ca8480..4d98172c3 100644 --- a/src/ca/sgAutoPtr.h +++ b/src/ca/sgAutoPtr.h @@ -30,7 +30,7 @@ template < class T > class sgAutoPtr { public: - sgAutoPtr ( struct CASG & ); + sgAutoPtr ( epicsGuard < epicsMutex > &, struct CASG & ); ~sgAutoPtr (); sgAutoPtr < T > & operator = ( T * ); T * operator -> (); @@ -40,25 +40,27 @@ public: private: T * pNotify; struct CASG & sg; + epicsGuard < epicsMutex > & guard; }; template < class T > -inline sgAutoPtr < T > :: sgAutoPtr ( struct CASG & sgIn ) : - pNotify ( 0 ), sg ( sgIn ) +inline sgAutoPtr < T > :: sgAutoPtr ( + epicsGuard < epicsMutex > & guardIn, struct CASG & sgIn ) : + pNotify ( 0 ), sg ( sgIn ), guard ( guardIn ) { } template < class T > inline sgAutoPtr < T > :: ~sgAutoPtr () { - this->sg.destroyPendingIO ( this->pNotify ); + this->sg.destroyPendingIO ( this->guard, this->pNotify ); } template < class T > inline sgAutoPtr < T > & sgAutoPtr < T > :: operator = ( T * pNotifyIn ) { if ( this->pNotify ) { - this->sg.destroyPendingIO ( this->pNotify ); + this->sg.destroyPendingIO ( this->guard, this->pNotify ); } this->pNotify = pNotifyIn; return *this; diff --git a/src/ca/syncGroup.h b/src/ca/syncGroup.h index d480a07ea..87fe16d83 100644 --- a/src/ca/syncGroup.h +++ b/src/ca/syncGroup.h @@ -52,24 +52,22 @@ static const unsigned CASG_MAGIC = 0xFAB4CAFE; // is applied class casgRecycle { // X aCC 655 public: - virtual void recycleSyncGroupWriteNotify ( class syncGroupWriteNotify & io ) = 0; - virtual void recycleSyncGroupReadNotify ( class syncGroupReadNotify & io ) = 0; + virtual void recycleSyncGroupWriteNotify ( + epicsGuard < epicsMutex > &, class syncGroupWriteNotify & io ) = 0; + virtual void recycleSyncGroupReadNotify ( + epicsGuard < epicsMutex > &, class syncGroupReadNotify & io ) = 0; protected: virtual ~casgRecycle (); }; class syncGroupNotify : public tsDLNode < syncGroupNotify > { public: - syncGroupNotify ( struct CASG &sgIn, chid ); - virtual void destroy ( casgRecycle & ) = 0; - void show ( unsigned level ) const; - bool ioInitiated () const; + syncGroupNotify (); + virtual void destroy ( + epicsGuard < epicsMutex > &, casgRecycle & ) = 0; + virtual void show ( + epicsGuard < epicsMutex > &, unsigned level ) const = 0; protected: - chid chan; - struct CASG & sg; - const unsigned magic; - cacChannel::ioid id; - bool idIsValid; virtual ~syncGroupNotify (); syncGroupNotify ( const syncGroupNotify & ); syncGroupNotify & operator = ( const syncGroupNotify & ); @@ -80,14 +78,20 @@ public: static syncGroupReadNotify * factory ( tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > &, struct CASG &, chid, void *pValueIn ); - void begin ( unsigned type, arrayElementCount count ); - void destroy ( casgRecycle & ); - void show ( unsigned level ) const; + void begin ( epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count ); + void destroy ( epicsGuard < epicsMutex > &, casgRecycle & ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; protected: virtual ~syncGroupReadNotify (); private: - void *pValue; - syncGroupReadNotify ( struct CASG &sgIn, chid, void *pValueIn ); + chid chan; + struct CASG & sg; + const unsigned magic; + cacChannel::ioid id; + bool idIsValid; + void * pValue; + syncGroupReadNotify ( struct CASG & sgIn, chid, void * pValueIn ); void * operator new ( size_t ); void operator delete ( void * ); void * operator new ( size_t, @@ -95,9 +99,11 @@ private: epicsPlacementDeleteOperator (( void *, tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > & )) void completion ( - unsigned type, arrayElementCount count, const void *pData ); + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void * pData ); void exception ( - int status, const char *pContext, unsigned type, arrayElementCount count ); + epicsGuard < epicsMutex > &, int status, + const char * pContext, unsigned type, arrayElementCount count ); syncGroupReadNotify ( const syncGroupReadNotify & ); syncGroupReadNotify & operator = ( const syncGroupReadNotify & ); }; @@ -107,14 +113,18 @@ public: static syncGroupWriteNotify * factory ( tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > &, struct CASG &, chid ); - void begin ( unsigned type, - arrayElementCount count, const void * pValueIn ); - void destroy ( casgRecycle & ); - void show ( unsigned level ) const; + void begin ( epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void * pValueIn ); + void destroy ( epicsGuard < epicsMutex > &, casgRecycle & ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; protected: virtual ~syncGroupWriteNotify (); // allocate only from pool private: - void *pValue; + chid chan; + struct CASG & sg; + const unsigned magic; + cacChannel::ioid id; + bool idIsValid; syncGroupWriteNotify ( struct CASG &, chid ); void * operator new ( size_t ); void operator delete ( void * ); @@ -122,8 +132,9 @@ private: tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > & )) - void completion (); - void exception ( int status, const char *pContext, + void completion ( epicsGuard < epicsMutex > & ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); syncGroupWriteNotify ( const syncGroupWriteNotify & ); syncGroupWriteNotify & operator = ( const syncGroupWriteNotify & ); @@ -131,52 +142,52 @@ private: struct ca_client_context; -class casgMutex { -public: - void lock (); - void unlock (); - void show ( unsigned level ) const; -private: - epicsMutex mutex; -}; - template < class T > class sgAutoPtr; struct CASG : public chronIntIdRes < CASG >, private casgRecycle { public: - CASG ( ca_client_context & cacIn ); - ~CASG (); - bool ioComplete (); - bool verify () const; - int block ( double timeout ); - void reset (); + CASG ( epicsGuard < epicsMutex > &, ca_client_context & cacIn ); + void destructor ( epicsGuard < epicsMutex > & ); + bool ioComplete ( epicsGuard < epicsMutex > & ); + bool verify ( epicsGuard < epicsMutex > & ) const; + int block ( epicsGuard < epicsMutex > &, double timeout ); + void reset ( epicsGuard < epicsMutex > & ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void show ( unsigned level ) const; - void get ( chid pChan, unsigned type, arrayElementCount count, void * pValue ); - void put ( chid pChan, unsigned type, arrayElementCount count, const void * pValue ); - void completionNotify ( syncGroupNotify & ); + void get ( epicsGuard < epicsMutex > &, chid pChan, + unsigned type, arrayElementCount count, void * pValue ); + void put ( epicsGuard < epicsMutex > &, chid pChan, + unsigned type, arrayElementCount count, const void * pValue ); + void completionNotify ( + epicsGuard < epicsMutex > &, syncGroupNotify & ); int printf ( const char * pFormat, ... ); - void exception ( int status, const char *pContext, - const char *pFileName, unsigned lineNo ); - void exception ( int status, const char *pContext, - const char *pFileName, unsigned lineNo, oldChannelNotify &chan, + void exception ( + epicsGuard < epicsMutex > &, int status, const char * pContext, + const char * pFileName, unsigned lineNo ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char * pContext, + const char * pFileName, unsigned lineNo, oldChannelNotify & chan, unsigned type, arrayElementCount count, unsigned op ); - void * operator new ( size_t size, tsFreeList < struct CASG, 128 > & ); - epicsPlacementDeleteOperator (( void *, tsFreeList < struct CASG, 128 > & )) + void * operator new ( size_t size, + tsFreeList < struct CASG, 128, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < struct CASG, 128, epicsMutexNOOP > & )) private: tsDLList < syncGroupNotify > ioPendingList; tsDLList < syncGroupNotify > ioCompletedList; - casgMutex mutable mutex; epicsEvent sem; ca_client_context & client; unsigned magic; tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > freeListReadOP; tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > freeListWriteOP; - void recycleSyncGroupWriteNotify ( syncGroupWriteNotify &io ); - void recycleSyncGroupReadNotify ( syncGroupReadNotify &io ); + void recycleSyncGroupWriteNotify ( + epicsGuard < epicsMutex > &, syncGroupWriteNotify & io ); + void recycleSyncGroupReadNotify ( + epicsGuard < epicsMutex > &, syncGroupReadNotify & io ); - void destroyPendingIO ( syncGroupNotify * ); - void destroyCompletedIO (); - void destroyPendingIO (); + void destroyPendingIO ( epicsGuard < epicsMutex > &, syncGroupNotify * ); + void destroyCompletedIO ( epicsGuard < epicsMutex > & ); + void destroyPendingIO ( epicsGuard < epicsMutex > & ); CASG ( const CASG & ); CASG & operator = ( const CASG & ); @@ -184,42 +195,24 @@ private: void * operator new ( size_t size ); void operator delete ( void * ); + ~CASG (); + friend class sgAutoPtr < syncGroupWriteNotify >; friend class sgAutoPtr < syncGroupReadNotify >; }; inline void * CASG::operator new ( size_t size, - tsFreeList < struct CASG, 128 > & freeList ) + tsFreeList < struct CASG, 128, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } # if defined ( CXX_PLACEMENT_DELETE ) inline void CASG::operator delete ( void * pCadaver, - tsFreeList < struct CASG, 128 > & freeList ) + tsFreeList < struct CASG, 128, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif -inline bool syncGroupNotify::ioInitiated () const -{ - return this->idIsValid; -} - -inline void casgMutex::lock () -{ - this->mutex.lock (); -} - -inline void casgMutex::unlock () -{ - this->mutex.unlock (); -} - -inline void casgMutex::show ( unsigned level ) const -{ - this->mutex.show ( level ); -} - #endif // ifdef syncGrouph diff --git a/src/ca/syncGroupNotify.cpp b/src/ca/syncGroupNotify.cpp index 2a77471b0..8068bdae4 100644 --- a/src/ca/syncGroupNotify.cpp +++ b/src/ca/syncGroupNotify.cpp @@ -22,24 +22,14 @@ #include "syncGroup.h" #include "oldAccess.h" -syncGroupNotify::syncGroupNotify ( CASG &sgIn, chid chanIn ) : - chan ( chanIn ), sg ( sgIn ), - magic ( CASG_MAGIC ), id ( 0u ), idIsValid ( false ) +syncGroupNotify::syncGroupNotify () { } syncGroupNotify::~syncGroupNotify () { - if ( this->idIsValid ) { - this->chan->ioCancel ( this->id ); - } -} - -void syncGroupNotify::show ( unsigned /* level */ ) const -{ - ::printf ( "pending sg op: chan=%s magic=%u sg=%p\n", - this->chan->pName(), this->magic, - static_cast ( &this->sg ) ); } + + diff --git a/src/ca/syncGroupReadNotify.cpp b/src/ca/syncGroupReadNotify.cpp index 4b0cfbdc4..394e2f622 100644 --- a/src/ca/syncGroupReadNotify.cpp +++ b/src/ca/syncGroupReadNotify.cpp @@ -25,43 +25,50 @@ #include "syncGroup.h" #include "oldAccess.h" -syncGroupReadNotify::syncGroupReadNotify ( CASG &sgIn, chid pChan, void *pValueIn ) : - syncGroupNotify ( sgIn, pChan ), pValue ( pValueIn ) +syncGroupReadNotify::syncGroupReadNotify ( + CASG & sgIn, chid pChan, void * pValueIn ) : + chan ( pChan ), sg ( sgIn ), magic ( CASG_MAGIC ), + id ( 0u ), idIsValid ( false ), pValue ( pValueIn ) { } -void syncGroupReadNotify::begin ( unsigned type, arrayElementCount count ) +void syncGroupReadNotify::begin ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount count ) { - this->chan->read ( type, count, *this, &this->id ); + this->chan->read ( guard, type, count, *this, &this->id ); this->idIsValid = true; } syncGroupReadNotify * syncGroupReadNotify::factory ( - tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > &freeList, - struct CASG &sg, chid chan, void *pValueIn ) + tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > & freeList, + struct CASG & sg, chid chan, void * pValueIn ) { return new ( freeList ) // X aCC 930 syncGroupReadNotify ( sg, chan, pValueIn ); } -void syncGroupReadNotify::destroy ( casgRecycle &recycle ) +void syncGroupReadNotify::destroy ( + epicsGuard < epicsMutex > & guard, casgRecycle & recycle ) { + if ( this->idIsValid ) { + this->chan->ioCancel ( guard, this->id ); + } this->~syncGroupReadNotify (); - recycle.recycleSyncGroupReadNotify ( * this ); + recycle.recycleSyncGroupReadNotify ( guard, *this ); } syncGroupReadNotify::~syncGroupReadNotify () { - if ( this->idIsValid ) { - this->chan->ioCancel ( this-> id ); - } } void syncGroupReadNotify::completion ( - unsigned type, arrayElementCount count, const void *pData ) + epicsGuard < epicsMutex > & guard, unsigned type, + arrayElementCount count, const void * pData ) { if ( this->magic != CASG_MAGIC ) { - this->sg.printf ( "cac: sync group io_complete(): bad sync grp op magic number?\n" ); + this->sg.printf ( + "cac: sync group io_complete(): bad sync grp op magic number?\n" ); return; } @@ -70,13 +77,15 @@ void syncGroupReadNotify::completion ( memcpy ( this->pValue, pData, size ); } this->idIsValid = false; - this->sg.completionNotify ( *this ); + this->sg.completionNotify ( guard, *this ); } void syncGroupReadNotify::exception ( - int status, const char *pContext, unsigned type, arrayElementCount count ) + epicsGuard < epicsMutex > & guard, int status, const char * pContext, + unsigned type, arrayElementCount count ) { - this->sg.exception ( status, pContext, + this->idIsValid = false; + this->sg.exception ( guard, status, pContext, __FILE__, __LINE__, *this->chan, type, count, CA_OP_GET ); // // This notify is left installed at this point as a place holder indicating that @@ -85,11 +94,14 @@ void syncGroupReadNotify::exception ( // } -void syncGroupReadNotify::show ( unsigned level ) const +void syncGroupReadNotify::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const { ::printf ( "pending sg read op: pVal=%p\n", this->pValue ); if ( level > 0u ) { - this->syncGroupNotify::show ( level - 1u ); + ::printf ( "pending sg op: chan=%s magic=%u sg=%p\n", + this->chan->pName(), this->magic, + static_cast < void * > ( & this->sg ) ); } } diff --git a/src/ca/syncGroupWriteNotify.cpp b/src/ca/syncGroupWriteNotify.cpp index 74d7d789c..9ab2cbae0 100644 --- a/src/ca/syncGroupWriteNotify.cpp +++ b/src/ca/syncGroupWriteNotify.cpp @@ -26,52 +26,60 @@ #include "oldAccess.h" syncGroupWriteNotify::syncGroupWriteNotify ( CASG & sgIn, chid pChan ) : - syncGroupNotify ( sgIn, pChan ) + chan ( pChan ), sg ( sgIn ), magic ( CASG_MAGIC ), + id ( 0u ), idIsValid ( false ) { } -void syncGroupWriteNotify::begin ( unsigned type, - arrayElementCount count, const void * pValueIn ) +void syncGroupWriteNotify::begin ( + epicsGuard < epicsMutex > & guard, unsigned type, + arrayElementCount count, const void * pValueIn ) { - this->chan->write ( type, count, pValueIn, *this, &this->id ); + this->chan->write ( guard, type, count, + pValueIn, *this, &this->id ); this->idIsValid = true; } syncGroupWriteNotify * syncGroupWriteNotify::factory ( tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > &freeList, - struct CASG &sg, chid chan ) + struct CASG & sg, chid chan ) { return new ( freeList ) syncGroupWriteNotify ( sg, chan ); } -void syncGroupWriteNotify::destroy ( casgRecycle & recycle ) +void syncGroupWriteNotify::destroy ( + epicsGuard < epicsMutex > & guard, casgRecycle & recycle ) { + if ( this->idIsValid ) { + this->chan->ioCancel ( guard, this->id ); + } this->~syncGroupWriteNotify (); - recycle.recycleSyncGroupWriteNotify ( * this ); + recycle.recycleSyncGroupWriteNotify ( guard, *this ); } syncGroupWriteNotify::~syncGroupWriteNotify () { - if ( this->idIsValid ) { - this->chan->ioCancel ( this->id ); - } } -void syncGroupWriteNotify::completion () +void syncGroupWriteNotify::completion ( + epicsGuard < epicsMutex > & guard ) { if ( this->magic != CASG_MAGIC ) { this->sg.printf ( "cac: sync group io_complete(): bad sync grp op magic number?\n" ); return; } this->idIsValid = false; - this->sg.completionNotify ( *this ); + this->sg.completionNotify ( guard, *this ); } void syncGroupWriteNotify::exception ( + epicsGuard < epicsMutex > & guard, int status, const char *pContext, unsigned type, arrayElementCount count ) { - this->sg.exception ( status, pContext, + this->sg.exception ( guard, status, pContext, __FILE__, __LINE__, *this->chan, type, count, CA_OP_PUT ); + this->idIsValid = false; + // // This notify is left installed at this point as a place holder indicating that // all requests have not been completed. This notify is not uninstalled until @@ -79,11 +87,14 @@ void syncGroupWriteNotify::exception ( // } -void syncGroupWriteNotify::show ( unsigned level ) const +void syncGroupWriteNotify::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const { ::printf ( "pending write sg op\n" ); if ( level > 0u ) { - this->syncGroupNotify::show ( level - 1u ); + ::printf ( "pending sg op: chan=%s magic=%u sg=%p\n", + this->chan->pName(), this->magic, + static_cast < void * > ( &this->sg ) ); } } diff --git a/src/ca/syncgrp.cpp b/src/ca/syncgrp.cpp index 141097234..de7b1ef41 100644 --- a/src/ca/syncgrp.cpp +++ b/src/ca/syncgrp.cpp @@ -26,9 +26,9 @@ */ extern "C" int epicsShareAPI ca_sg_create ( CA_SYNC_GID * pgid ) // X aCC 361 { - ca_client_context *pcac; + ca_client_context * pcac; int caStatus; - CASG *pcasg; + CASG * pcasg; caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { @@ -36,7 +36,8 @@ extern "C" int epicsShareAPI ca_sg_create ( CA_SYNC_GID * pgid ) // X aCC 361 } try { - pcasg = new ( pcac->casgFreeList ) CASG ( *pcac ); + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + pcasg = new ( pcac->casgFreeList ) CASG ( guard, *pcac ); *pgid = pcasg->getId (); return ECA_NORMAL; } @@ -59,12 +60,14 @@ extern "C" int epicsShareAPI ca_sg_delete ( const CA_SYNC_GID gid ) return caStatus; } - CASG * pcasg = pcac->lookupCASG ( gid ); + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + + CASG * pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { return ECA_BADSYNCGRP; } - pcasg->~CASG (); + pcasg->destructor ( guard ); pcac->casgFreeList.release ( pcasg ); return ECA_NORMAL; @@ -84,12 +87,14 @@ extern "C" int epicsShareAPI ca_sg_block ( const CA_SYNC_GID gid, ca_real timeou return status; } - pcasg = pcac->lookupCASG ( gid ); + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + + pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { status = ECA_BADSYNCGRP; } else { - status = pcasg->block ( timeout ); + status = pcasg->block ( guard, timeout ); } return status; @@ -109,13 +114,15 @@ extern "C" int epicsShareAPI ca_sg_reset ( const CA_SYNC_GID gid ) return caStatus; } - pcasg = pcac->lookupCASG ( gid ); + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + + pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { caStatus = ECA_BADSYNCGRP; } else { caStatus = ECA_NORMAL; - pcasg->reset (); + pcasg->reset ( guard ); } return caStatus; @@ -126,21 +133,20 @@ extern "C" int epicsShareAPI ca_sg_reset ( const CA_SYNC_GID gid ) */ extern "C" int epicsShareAPI ca_sg_stat ( const CA_SYNC_GID gid ) { - ca_client_context *pcac; - CASG *pcasg; - - int caStatus = fetchClientContext (&pcac); + ca_client_context * pcac; + int caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } - pcasg = pcac->lookupCASG ( gid ); + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + + CASG * pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { ::printf ( "Bad Sync Group Id\n"); return ECA_BADSYNCGRP; } - - pcasg->show ( 1000u ); + pcasg->show ( guard, 1000u ); return ECA_NORMAL; } @@ -159,12 +165,13 @@ extern "C" int epicsShareAPI ca_sg_test ( const CA_SYNC_GID gid ) // X aCC 361 return caStatus; } - pcasg = pcac->lookupCASG ( gid ); + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + + pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { return ECA_BADSYNCGRP; } - - if ( pcasg->ioComplete () ) { + if ( pcasg->ioComplete ( guard ) ) { return ECA_IODONE; } else{ @@ -187,13 +194,15 @@ extern "C" int epicsShareAPI ca_sg_array_put ( const CA_SYNC_GID gid, chtype typ return caStatus; } - pcasg = pcac->lookupCASG ( gid ); + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + + pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { return ECA_BADSYNCGRP; } try { - pcasg->put ( pChan, type, + pcasg->put ( guard, pChan, type, static_cast < unsigned > ( count ), pValue ); return ECA_NORMAL; } @@ -250,13 +259,15 @@ extern "C" int epicsShareAPI ca_sg_array_get ( const CA_SYNC_GID gid, chtype typ return caStatus; } - pcasg = pcac->lookupCASG ( gid ); + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + + pcasg = pcac->lookupCASG ( guard, gid ); if ( ! pcasg ) { return ECA_BADSYNCGRP; } try { - pcasg->get ( pChan, type, + pcasg->get ( guard, pChan, type, static_cast < unsigned > ( count ), pValue ); return ECA_NORMAL; } diff --git a/src/ca/tcpRecvWatchdog.cpp b/src/ca/tcpRecvWatchdog.cpp index 6c84d6716..b6d73f8c1 100644 --- a/src/ca/tcpRecvWatchdog.cpp +++ b/src/ca/tcpRecvWatchdog.cpp @@ -29,10 +29,12 @@ // the recv watchdog timer is active when this object is created // tcpRecvWatchdog::tcpRecvWatchdog - ( cac & cacIn, tcpiiu & iiuIn, double periodIn, epicsTimerQueue & queueIn ) : + ( callbackMutex & cbMutexIn, epicsMutex & mutexIn, tcpiiu & iiuIn, + double periodIn, epicsTimerQueue & queueIn ) : period ( periodIn ), timer ( queueIn.createTimer () ), - iiu ( iiuIn ), cacRef ( cacIn ), responsePending ( false ), - beaconAnomaly ( true ) + iiu ( iiuIn ), cbMutex ( cbMutexIn ), mutex ( mutexIn ), + probeResponsePending ( false ), beaconAnomaly ( true ), + probeTimeoutDetected ( false ) { } @@ -44,21 +46,20 @@ tcpRecvWatchdog::~tcpRecvWatchdog () epicsTimerNotify::expireStatus tcpRecvWatchdog::expire ( const epicsTime & /* currentTime */ ) // X aCC 361 { - if ( this->responsePending ) { + if ( this->probeResponsePending ) { if ( this->iiu.bytesArePendingInOS() ) { - this->cacRef.printf ( + this->iiu.printf ( "The CA client library's server inactivity timer initiated server disconnect\n" ); - this->cacRef.printf ( + this->iiu.printf ( "despite the fact that messages from this server are pending for processing in\n" ); - this->cacRef.printf ( + this->iiu.printf ( "the client library. Here are some possible causes of the unnecessary disconnect:\n" ); - this->cacRef.printf ( + this->iiu.printf ( "o ca_pend_event() or ca_poll() have not been called for %f seconds\n", this->period ); - this->cacRef.printf ( + this->iiu.printf ( "o application is blocked in a callback from the client library\n" ); } - this->cancel (); # ifdef DEBUG char hostName[128]; this->iiu.hostName ( hostName, sizeof (hostName) ); @@ -66,19 +67,31 @@ tcpRecvWatchdog::expire ( const epicsTime & /* currentTime */ ) // X aCC 361 "- disconnecting.\n", hostName, this->period ) ); # endif - this->cacRef.unresponsiveCircuitNotify ( this->iiu ); + { + epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); + this->iiu.receiveTimeoutNotify ( cbGuard, guard ); + this->probeTimeoutDetected = true; + } return noRestart; } else { - this->responsePending = this->iiu.setEchoRequestPending (); + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->probeTimeoutDetected = false; + this->probeResponsePending = this->iiu.setEchoRequestPending ( guard ); + } debugPrintf ( ("TCP connection timed out - sending echo request\n") ); return expireStatus ( restart, CA_ECHO_TIMEOUT ); } } -void tcpRecvWatchdog::beaconArrivalNotify ( const epicsTime & currentTime ) +void tcpRecvWatchdog::beaconArrivalNotify ( + epicsGuard < epicsMutex > & guard, const epicsTime & currentTime ) { - if ( ! this->beaconAnomaly && ! this->responsePending ) { + guard.assertIdenticalMutex ( this->mutex ); + if ( ! this->beaconAnomaly && ! this->probeResponsePending ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); this->timer.start ( *this, currentTime + this->period ); debugPrintf ( ("Saw a normal beacon - reseting TCP recv watchdog\n") ); } @@ -91,18 +104,65 @@ void tcpRecvWatchdog::beaconArrivalNotify ( const epicsTime & currentTime ) // faster when the server is rebooted twice in rapid // succession before a 1st or 2nd beacon has been received) // -void tcpRecvWatchdog::beaconAnomalyNotify () +void tcpRecvWatchdog::beaconAnomalyNotify ( + epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->mutex ); this->beaconAnomaly = true; debugPrintf ( ("Saw an abnormal beacon\n") ); } -void tcpRecvWatchdog::messageArrivalNotify ( const epicsTime & currentTime ) +void tcpRecvWatchdog::messageArrivalNotify ( + const epicsTime & currentTime ) { - this->beaconAnomaly = false; - this->responsePending = false; - this->timer.start ( *this, currentTime + this->period ); - debugPrintf ( ("received a message - reseting TCP recv watchdog\n") ); + bool restartNeeded = false; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( ! this->probeResponsePending ) { + this->beaconAnomaly = false; + restartNeeded = true; + } + } + // dont hold the lock for fear of deadlocking + // because cancel is blocking for the completion + // of the recvDog expire which takes the lock + // - it take also the callback lock + if ( restartNeeded ) { + this->timer.start ( *this, currentTime + this->period ); + debugPrintf ( ("received a message - reseting TCP recv watchdog\n") ); + } +} + +void tcpRecvWatchdog::probeResponseNotify ( + epicsGuard < callbackMutex > & cbGuard, + const epicsTime & currentTime ) +{ + bool restartNeeded = false; + double restartDelay = DBL_MAX; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->probeResponsePending ) { + restartNeeded = true; + if ( this->probeTimeoutDetected ) { + this->probeTimeoutDetected = false; + this->probeResponsePending = this->iiu.setEchoRequestPending ( guard ); + restartDelay = CA_ECHO_TIMEOUT; + debugPrintf ( ("late probe response - sending another probe request\n") ); + } + else { + this->probeResponsePending = false; + restartDelay = this->period; + this->iiu.responsiveCircuitNotify ( cbGuard, guard ); + debugPrintf ( ("probe response on time - setting circuit to reponsive state\n") ); + } + } + } + if ( restartNeeded ) { + // timer callback takes the callback mutex and the cac mutex + epicsGuardRelease < callbackMutex > cbGuardRelease ( cbGuard ); + epicsTime expireTime = currentTime + restartDelay; + this->timer.start ( *this, expireTime ); + } } // @@ -119,15 +179,25 @@ void tcpRecvWatchdog::messageArrivalNotify ( const epicsTime & currentTime ) // The send watchdog will be responsible for detecting // dead connections in this case. // -void tcpRecvWatchdog::sendBacklogProgressNotify ( const epicsTime & currentTime ) +void tcpRecvWatchdog::sendBacklogProgressNotify ( + epicsGuard < epicsMutex > & guard, + const epicsTime & currentTime ) { + guard.assertIdenticalMutex ( this->mutex ); + // We dont set "beaconAnomaly" to be false here because, after we see a // beacon anomaly (which could be transiently detecting a reboot) we will // not trust the beacon as an indicator of a healthy server until we // receive at least one message from the server. - this->responsePending = false; - this->timer.start ( *this, currentTime + this->period ); - debugPrintf ( ("saw heavy send backlog - reseting TCP recv watchdog\n") ); + if ( this->probeResponsePending ) { + // we avoid calling this with the lock applied because + // it restarts the recv wd timer, this might block + // until a recv wd timer expire callback completes, and + // this callback takes the lock + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->timer.start ( *this, currentTime + CA_ECHO_TIMEOUT ); + debugPrintf ( ("saw heavy send backlog - reseting TCP recv watchdog\n") ); + } } void tcpRecvWatchdog::connectNotify () @@ -136,6 +206,28 @@ void tcpRecvWatchdog::connectNotify () debugPrintf ( ("connected to the server - reseting TCP recv watchdog\n") ); } +void tcpRecvWatchdog::sendTimeoutNotify ( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < epicsMutex > & guard, + const epicsTime & currentTime ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + bool restartNeeded = false; + if ( ! this->probeResponsePending ) { + this->probeResponsePending = this->iiu.setEchoRequestPending ( guard ); + restartNeeded = true; + } + if ( restartNeeded ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + { + epicsGuardRelease < callbackMutex > cbUnguard ( cbGuard ); + this->timer.start ( *this, currentTime + CA_ECHO_TIMEOUT ); + } + } + debugPrintf ( ("TCP send timed out - sending echo request\n") ); +} + void tcpRecvWatchdog::cancel () { this->timer.cancel (); @@ -149,10 +241,15 @@ double tcpRecvWatchdog::delay () const void tcpRecvWatchdog::show ( unsigned level ) const { + epicsGuard < epicsMutex > guard ( this->mutex ); + ::printf ( "Receive virtual circuit watchdog at %p, period %f\n", static_cast ( this ), this->period ); if ( level > 0u ) { - ::printf ( "\tresponse pending boolean %u, beacon anomaly boolean %u\n", - this->responsePending, this->beaconAnomaly ); + ::printf ( "\t%s %s %s\n", + this->probeResponsePending ? "probe-response-pending" : "", + this->beaconAnomaly ? "beacon-anomaly-detected" : "", + this->probeTimeoutDetected ? "probe-response-timeout" : "" ); } } + diff --git a/src/ca/tcpRecvWatchdog.h b/src/ca/tcpRecvWatchdog.h index e576431fd..0759a829f 100644 --- a/src/ca/tcpRecvWatchdog.h +++ b/src/ca/tcpRecvWatchdog.h @@ -33,27 +33,40 @@ class tcpiiu; class tcpRecvWatchdog : private epicsTimerNotify { public: - tcpRecvWatchdog ( cac &, tcpiiu &, + tcpRecvWatchdog ( callbackMutex &, + epicsMutex &, tcpiiu &, double periodIn, epicsTimerQueue & ); virtual ~tcpRecvWatchdog (); void sendBacklogProgressNotify ( + epicsGuard < epicsMutex > &, const epicsTime & currentTime ); void messageArrivalNotify ( const epicsTime & currentTime ); - void beaconArrivalNotify ( + void probeResponseNotify ( + epicsGuard < callbackMutex > &, const epicsTime & currentTime ); - void beaconAnomalyNotify (); + void beaconArrivalNotify ( + epicsGuard < epicsMutex > &, + const epicsTime & currentTime ); + void beaconAnomalyNotify ( epicsGuard < epicsMutex > & ); void connectNotify (); + void sendTimeoutNotify ( + epicsGuard < callbackMutex > &, + epicsGuard < epicsMutex > &, + const epicsTime & currentTime ); +#pragma message ("too low level?") void cancel (); void show ( unsigned level ) const; double delay () const; private: const double period; epicsTimer & timer; + callbackMutex & cbMutex; + epicsMutex & mutex; tcpiiu & iiu; - cac & cacRef; - bool responsePending; + bool probeResponsePending; bool beaconAnomaly; + bool probeTimeoutDetected; expireStatus expire ( const epicsTime & currentTime ); tcpRecvWatchdog ( const tcpRecvWatchdog & ); tcpRecvWatchdog & operator = ( const tcpRecvWatchdog & ); diff --git a/src/ca/tcpSendWatchdog.cpp b/src/ca/tcpSendWatchdog.cpp index 7e0850fd3..c5fe1ce38 100644 --- a/src/ca/tcpSendWatchdog.cpp +++ b/src/ca/tcpSendWatchdog.cpp @@ -26,9 +26,10 @@ #include "virtualCircuit.h" tcpSendWatchdog::tcpSendWatchdog - ( cac & cacIn, tcpiiu & iiuIn, double periodIn, epicsTimerQueue & queueIn ) : + ( callbackMutex & cbMutexIn, tcpiiu & iiuIn, + double periodIn, epicsTimerQueue & queueIn ) : period ( periodIn ), timer ( queueIn.createTimer () ), - cacRef ( cacIn ), iiu ( iiuIn ) + cbMutex ( cbMutexIn ), iiu ( iiuIn ) { } @@ -38,10 +39,10 @@ tcpSendWatchdog::~tcpSendWatchdog () } epicsTimerNotify::expireStatus tcpSendWatchdog::expire ( - const epicsTime & /* currentTime */ ) + const epicsTime & currentTime ) { if ( this->iiu.bytesArePendingInOS() ) { - this->cacRef.printf ( + this->iiu.printf ( "The CA client library is disconnecting after a flush request " "timed out, but receive data is pending, probably because of an " "application schedualing problem\n" ); @@ -52,7 +53,10 @@ epicsTimerNotify::expireStatus tcpSendWatchdog::expire ( debugPrintf ( ( "Request not accepted by CA server %s for %g sec. Disconnecting.\n", hostName, this->period ) ); # endif - this->cacRef.unresponsiveCircuitNotify ( this->iiu ); + { + epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); + this->iiu.sendTimeoutNotify ( currentTime, cbGuard ); + } return noRestart; } diff --git a/src/ca/tcpSendWatchdog.h b/src/ca/tcpSendWatchdog.h index 6e217fc4d..0e6b5c748 100644 --- a/src/ca/tcpSendWatchdog.h +++ b/src/ca/tcpSendWatchdog.h @@ -31,14 +31,15 @@ class tcpSendWatchdog : private epicsTimerNotify { public: - tcpSendWatchdog ( cac &, tcpiiu &, double periodIn, epicsTimerQueue & queueIn ); + tcpSendWatchdog ( callbackMutex &, tcpiiu &, + double periodIn, epicsTimerQueue & queueIn ); virtual ~tcpSendWatchdog (); void start ( const epicsTime & ); void cancel (); private: const double period; epicsTimer & timer; - cac & cacRef; + callbackMutex & cbMutex; tcpiiu & iiu; expireStatus expire ( const epicsTime & currentTime ); tcpSendWatchdog ( const tcpSendWatchdog & ); diff --git a/src/ca/tcpiiu.cpp b/src/ca/tcpiiu.cpp index d613609e2..f9defde12 100644 --- a/src/ca/tcpiiu.cpp +++ b/src/ca/tcpiiu.cpp @@ -38,6 +38,7 @@ #include "bhe.h" #include "epicsSignal.h" #include "caerr.h" +#include "udpiiu.h" const unsigned mSecPerSec = 1000u; const unsigned uSecPerSec = 1000u * mSecPerSec; @@ -67,53 +68,91 @@ void tcpSendThread::exitWait () void tcpSendThread::run () { try { + epicsGuard < epicsMutex > guard ( this->iiu.cacRef.mutexRef() ); + + bool laborPending = false; while ( true ) { - bool flowControlLaborNeeded; - bool echoLaborNeeded; - this->iiu.sendThreadFlushEvent.wait (); + // dont wait if there is still labor to be done below + if ( ! laborPending ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->iiu.sendThreadFlushEvent.wait (); + } if ( this->iiu.state != tcpiiu::iiucs_connected ) { break; } - { - epicsGuard < cacMutex > guard ( this->iiu.cacRef.mutexRef() ); - flowControlLaborNeeded = - this->iiu.busyStateDetected != this->iiu.flowControlActive; - echoLaborNeeded = this->iiu.echoRequestPending; - this->iiu.echoRequestPending = false; - - if ( flowControlLaborNeeded ) { - if ( this->iiu.flowControlActive ) { - this->iiu.disableFlowControlRequest ( guard ); - this->iiu.flowControlActive = false; - debugPrintf ( ( "fc off\n" ) ); - } - else { - this->iiu.enableFlowControlRequest ( guard ); - this->iiu.flowControlActive = true; - debugPrintf ( ( "fc on\n" ) ); - } - } + laborPending = false; + bool flowControlLaborNeeded = + this->iiu.busyStateDetected != this->iiu.flowControlActive; + bool echoLaborNeeded = this->iiu.echoRequestPending; + this->iiu.echoRequestPending = false; - if ( echoLaborNeeded ) { - if ( CA_V43 ( this->iiu.minorProtocolVersion ) ) { - this->iiu.echoRequest ( guard ); - } - else { - this->iiu.versionMessage ( guard, this->iiu.priority() ); - } + if ( flowControlLaborNeeded ) { + if ( this->iiu.flowControlActive ) { + this->iiu.disableFlowControlRequest ( guard ); + this->iiu.flowControlActive = false; + debugPrintf ( ( "fc off\n" ) ); + } + else { + this->iiu.enableFlowControlRequest ( guard ); + this->iiu.flowControlActive = true; + debugPrintf ( ( "fc on\n" ) ); } } - if ( ! this->iiu.flush () ) { + if ( echoLaborNeeded ) { + if ( CA_V43 ( this->iiu.minorProtocolVersion ) ) { + this->iiu.echoRequest ( guard ); + } + else { + this->iiu.versionMessage ( guard, this->iiu.priority() ); + } + } + + while ( nciu * pChan = this->iiu.createReqPend.get () ) { + this->iiu.createChannelRequest ( *pChan, guard ); + this->iiu.createRespPend.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_createRespPend; + if ( this->iiu.sendQue.flushBlockThreshold ( 0u ) ) { + laborPending = true; + break; + } + } + + while ( nciu * pChan = this->iiu.subscripReqPend.get () ) { + // this installs any subscriptions as needed + pChan->resubscribe ( guard ); + this->iiu.connectedList.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_connected; + if ( this->iiu.sendQue.flushBlockThreshold ( 0u ) ) { + laborPending = true; + break; + } + } + + while ( nciu * pChan = this->iiu.subscripUpdateReqPend.get () ) { + // this updates any subscriptions as needed + pChan->sendSubscriptionUpdateRequests ( guard ); + this->iiu.connectedList.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_connected; + if ( this->iiu.sendQue.flushBlockThreshold ( 0u ) ) { + laborPending = true; + break; + } + } + + if ( ! this->iiu.flush ( guard ) ) { break; } } if ( this->iiu.state == tcpiiu::iiucs_clean_shutdown ) { - this->iiu.flush (); + this->iiu.flush ( guard ); // this should cause the server to disconnect from // the client int status = ::shutdown ( this->iiu.sock, SHUT_WR ); @@ -180,7 +219,7 @@ unsigned tcpiiu::sendBytes ( const void *pBuf, // winsock indicates disconnect by returning zero here if ( status == 0 ) { - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); break; } @@ -201,7 +240,7 @@ unsigned tcpiiu::sendBytes ( const void *pBuf, sockErrBuf ); } - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); break; } } @@ -232,7 +271,7 @@ unsigned tcpiiu::recvBytes ( void * pBuf, unsigned nBytesInBuf ) int localErrno = SOCKERRNO; if ( status == 0 ) { - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); return 0u; } @@ -244,7 +283,7 @@ unsigned tcpiiu::recvBytes ( void * pBuf, unsigned nBytesInBuf ) } if ( localErrno == SOCK_SHUTDOWN ) { - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); return 0u; } @@ -253,12 +292,12 @@ unsigned tcpiiu::recvBytes ( void * pBuf, unsigned nBytesInBuf ) } if ( localErrno == SOCK_ECONNABORTED ) { - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); return 0u; } if ( localErrno == SOCK_ECONNRESET ) { - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); return 0u; } @@ -313,13 +352,28 @@ void tcpRecvThread::run () this->iiu.sendThread.start (); if ( this->iiu.state != tcpiiu::iiucs_connected ) { - this->iiu.cacRef.disconnectNotify ( this->iiu ); + this->iiu.disconnectNotify (); return; } comBuf * pComBuf = new ( this->iiu.comBufMemMgr ) comBuf; while ( this->iiu.state == tcpiiu::iiucs_connected || this->iiu.state == tcpiiu::iiucs_clean_shutdown ) { + // + // if this thread has connected channels with subscriptions + // that need to be sent then wakeup the send thread + { + bool wakeupNeeded = false; + { + epicsGuard < epicsMutex > cacGuard ( this->iiu.cacRef.mutexRef() ); + if ( this->iiu.subscripReqPend.count() ) { + wakeupNeeded = true; + } + } + if ( wakeupNeeded ) { + this->iiu.sendThreadFlushEvent.signal (); + } + } // // We leave the bytes pending and fetch them after @@ -336,10 +390,6 @@ void tcpRecvThread::run () epicsTime currentTime = epicsTime::getCurrent (); // reschedule connection activity watchdog - // but dont hold the lock for fear of deadlocking - // because cancel is blocking for the completion - // of the recvDog expire which takes the lock - // - it take also the callback lock this->iiu.recvDog.messageArrivalNotify ( currentTime ); // only one recv thread at a time may call callbacks @@ -347,11 +397,6 @@ void tcpRecvThread::run () // this lock get a chance to run epicsGuard < callbackMutex > cbGuard ( this->cbMutex ); - if ( this->iiu.softDisconnect ) { - epicsGuard < cacMutex > cacGuard ( this->iiu.cacRef.mutexRef() ); - this->iiu.responsiveCircuitNotify ( cbGuard, cacGuard ); - } - // force the receive watchdog to be reset every 5 frames unsigned contiguousFrameCount = 0; while ( nBytesIn ) { @@ -429,10 +474,9 @@ tcpiiu::tcpiiu ( cac & cac, callbackMutex & cbMutex, double connectionTimeout, sendThread ( *this, cbMutex, "CAC-TCP-send", epicsThreadGetStackSize ( epicsThreadStackMedium ), cac::lowestPriorityLevelAbove ( - cac::lowestPriorityLevelAbove ( - cac.getInitializingThreadsPriority() ) ) ), - recvDog ( cac, *this, connectionTimeout, timerQueue ), - sendDog ( cac, *this, connectionTimeout, timerQueue ), + cac.getInitializingThreadsPriority() ) ), + recvDog ( cbMutex, cac.mutexRef (), *this, connectionTimeout, timerQueue ), + sendDog ( cbMutex, *this, connectionTimeout, timerQueue ), sendQue ( *this, comBufMemMgrIn ), recvQue ( comBufMemMgrIn ), curDataMax ( MAX_TCP ), @@ -447,6 +491,7 @@ tcpiiu::tcpiiu ( cac & cac, callbackMutex & cbMutex, double connectionTimeout, blockingForFlush ( 0u ), socketLibrarySendBufferSize ( 0x1000 ), unacknowledgedSendBytes ( 0u ), + channelCountTot ( 0u ), busyStateDetected ( false ), flowControlActive ( false ), echoRequestPending ( false ), @@ -456,7 +501,7 @@ tcpiiu::tcpiiu ( cac & cac, callbackMutex & cbMutex, double connectionTimeout, recvProcessPostponedFlush ( false ), discardingPendingData ( false ), socketHasBeenClosed ( false ), - softDisconnect ( false ) + unresponsiveCircuit ( false ) { this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( this->sock == INVALID_SOCKET ) { @@ -494,7 +539,7 @@ tcpiiu::tcpiiu ( cac & cac, callbackMutex & cbMutex, double connectionTimeout, // load message queue with messages informing server // of version, user, and host name of client { - epicsGuard < cacMutex > guard ( this->cacRef.mutexRef() ); + epicsGuard < epicsMutex > guard ( this->cacRef.mutexRef() ); this->versionMessage ( guard, this->priority() ); this->userNameSetRequest ( guard ); this->hostNameSetRequest ( guard ); @@ -584,27 +629,26 @@ void tcpiiu::start () */ void tcpiiu::connect () { - /* - * attempt to connect to a CA server - */ - this->sendDog.start ( epicsTime::getCurrent() ); - + // attempt to connect to a CA server while ( this->state == iiucs_connecting ) { osiSockAddr tmp = this->address (); int status = ::connect ( this->sock, &tmp.sa, sizeof ( tmp.sa ) ); if ( status == 0 ) { + bool enteredConnectedState = false; + { + epicsGuard < epicsMutex > autoMutex ( this->cacRef.mutexRef() ); - epicsGuard < cacMutex > autoMutex ( this->cacRef.mutexRef() ); - - if ( this->state == iiucs_connecting ) { - // put the iiu into the connected state - this->state = iiucs_connected; - + if ( this->state == iiucs_connecting ) { + // put the iiu into the connected state + this->state = iiucs_connected; + enteredConnectedState = true; + } + } + if ( enteredConnectedState ) { // start connection activity watchdog this->recvDog.connectNotify (); } - break; } @@ -622,77 +666,105 @@ void tcpiiu::connect () sockErrBuf, sizeof ( sockErrBuf ) ); this->printf ( "Unable to connect because \"%s\"\n", sockErrBuf ); - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); break; } } - this->sendDog.cancel (); return; } -void tcpiiu::initiateCleanShutdown ( epicsGuard < cacMutex > & ) +void tcpiiu::initiateCleanShutdown ( epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); if ( this->state == iiucs_connected ) { this->state = iiucs_clean_shutdown; this->sendThreadFlushEvent.signal (); } } -void tcpiiu::disconnectNotify ( epicsGuard < cacMutex > & ) +void tcpiiu::disconnectNotify () { - this->state = iiucs_disconnected; + { + epicsGuard < epicsMutex > guard ( this->cacRef.mutexRef () ); + this->state = iiucs_disconnected; + } this->sendThreadFlushEvent.signal (); } void tcpiiu::responsiveCircuitNotify ( - epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ) + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) { - this->softDisconnect = false; - tsDLIter < nciu > pChan = this->channelList.firstIter (); - while ( pChan.valid() ) { - // The cac lock is released herein so there is concern that - // the list could be changed while we are traversing it. - // However, this occurs only if a circuit disconnects, - // a user deletes a channel, or a server disconnects a - // channel. The callback lock must be taken in all of - // these situations so this code is protected. - pChan->responsiveCircuitNotify ( cbGuard, guard ); - pChan++; + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + while ( nciu * pChan = this->unrespCircuit.get() ) { + this->subscripUpdateReqPend.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_subscripUpdateReqPend; + pChan->connect ( cbGuard, guard ); } + this->sendThreadFlushEvent.signal (); +} + +void tcpiiu::sendTimeoutNotify ( + const epicsTime & currentTime, + epicsGuard < callbackMutex > & cbGuard ) +{ + epicsGuard < epicsMutex > guard ( this->cacRef.mutexRef() ); + this->unresponsiveCircuitNotify ( cbGuard, guard ); + // setup circuit probe sequence + this->recvDog.sendTimeoutNotify ( cbGuard, guard, currentTime ); +} + +void tcpiiu::receiveTimeoutNotify ( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + this->unresponsiveCircuitNotify ( cbGuard, guard ); } void tcpiiu::unresponsiveCircuitNotify ( epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ) + epicsGuard < epicsMutex > & guard ) { - this->recvDog.cancel(); - this->sendDog.cancel(); - this->softDisconnect = true; - if ( this->channelList.count() ) { - char hostNameTmp[128]; - this->hostName ( hostNameTmp, sizeof ( hostNameTmp ) ); - { - epicsGuardRelease < cacMutex > guardRelease ( guard ); - genLocalExcep ( cbGuard, this->cacRef, ECA_UNRESPTMO, hostNameTmp ); - } - tsDLIter < nciu > pChan = this->channelList.firstIter (); - while ( pChan.valid() ) { - // The cac lock is released herein so there is concern that - // the list could be changed while we are traversing it. - // However, this occurs only if a circuit disconnects, - // a user deletes a channel, or a server disconnects a - // channel. The callback lock must be taken in all of - // these situations so this code is protected. - pChan->unresponsiveCircuitNotify ( cbGuard, guard ); - pChan++; + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + if ( ! this->unresponsiveCircuit ) { + this->unresponsiveCircuit = true; + this->echoRequestPending = true; + this->sendThreadFlushEvent.signal (); + +#pragma message ( "cancel is too low level" ) + this->recvDog.cancel (); + this->sendDog.cancel (); + if ( this->connectedList.count() ) { + char hostNameTmp[128]; + this->hostName ( hostNameTmp, sizeof ( hostNameTmp ) ); + { + epicsGuardRelease < epicsMutex > guardRelease ( guard ); + genLocalExcep ( cbGuard, this->cacRef, ECA_UNRESPTMO, hostNameTmp ); + } + while ( nciu * pChan = this->connectedList.get () ) { + // The cac lock is released herein so there is concern that + // the list could be changed while we are traversing it. + // However, this occurs only if a circuit disconnects, + // a user deletes a channel, or a server disconnects a + // channel. The callback lock must be taken in all of + // these situations so this code is protected. + this->unrespCircuit.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_unrespCircuit; + pChan->unresponsiveCircuitNotify ( cbGuard, guard ); + } } } } void tcpiiu::initiateAbortShutdown ( epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > & guard ) + epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + if ( ! this->discardingPendingData ) { // force abortive shutdown sequence // (discard outstanding sends and receives) @@ -744,7 +816,6 @@ void tcpiiu::initiateAbortShutdown ( epicsGuard < callbackMutex > &, this->recvThread.interruptSocketRecv (); this->sendThread.interruptSocketSend (); break; - case esscimqi_shuechanismImplemenedHerein: default: break; }; @@ -781,7 +852,7 @@ tcpiiu::~tcpiiu () void tcpiiu::show ( unsigned level ) const { - epicsGuard < cacMutex > locker ( this->cacRef.mutexRef() ); + epicsGuard < epicsMutex > locker ( this->cacRef.mutexRef() ); char buf[256]; this->hostNameCacheInstance.hostName ( buf, sizeof ( buf ) ); ::printf ( "Virtual circuit to \"%s\" at version V%u.%u state %u\n", @@ -804,20 +875,54 @@ void tcpiiu::show ( unsigned level ) const ::printf ("\techo pending bool = %u\n", this->echoRequestPending ); ::printf ( "IO identifier hash table:\n" ); - tsDLIterConst < nciu > pChan = this->channelList.firstIter (); - while ( pChan.valid () ) { - pChan->show ( level - 2u ); - pChan++; + if ( this->createReqPend.count () ) { + ::printf ( "Create request pending channels\n" ); + tsDLIterConst < nciu > pChan = this->createReqPend.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + if ( this->createRespPend.count () ) { + ::printf ( "Create response pending channels\n" ); + tsDLIterConst < nciu > pChan = this->createRespPend.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + if ( this->subscripReqPend.count () ) { + ::printf ( "Subscription request pending channels\n" ); + tsDLIterConst < nciu > pChan = this->subscripReqPend.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + if ( this->connectedList.count () ) { + ::printf ( "Connected channels\n" ); + tsDLIterConst < nciu > pChan = this->connectedList.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + if ( this->unrespCircuit.count () ) { + ::printf ( "Unresponsive circuit channels\n" ); + tsDLIterConst < nciu > pChan = this->unrespCircuit.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } } } } -bool tcpiiu::setEchoRequestPending () // X aCC 361 +bool tcpiiu::setEchoRequestPending ( epicsGuard < epicsMutex > & guard ) // X aCC 361 { - { - epicsGuard < cacMutex > locker ( this->cacRef.mutexRef() ); - this->echoRequestPending = true; - } + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + this->echoRequestPending = true; this->sendThreadFlushEvent.signal (); if ( CA_V43 ( this->minorProtocolVersion ) ) { // we send an echo @@ -829,9 +934,15 @@ bool tcpiiu::setEchoRequestPending () // X aCC 361 } } -// -// tcpiiu::processIncoming() -// +void tcpiiu::flushIfRecvProcessRequested () +{ + epicsGuard < epicsMutex > guard ( this->cacRef.mutexRef () ); + if ( this->recvProcessPostponedFlush ) { + this->flushRequest ( guard ); + this->recvProcessPostponedFlush = false; + } +} + bool tcpiiu::processIncoming ( const epicsTime & currentTime, epicsGuard < callbackMutex > & guard ) { @@ -933,11 +1044,10 @@ bool tcpiiu::processIncoming ( # endif } -/* - * tcpiiu::hostNameSetRequest () - */ -void tcpiiu::hostNameSetRequest ( epicsGuard < cacMutex > & locker ) // X aCC 431 +void tcpiiu::hostNameSetRequest ( epicsGuard < epicsMutex > & guard ) // X aCC 431 { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + if ( ! CA_V41 ( this->minorProtocolVersion ) ) { return; } @@ -950,10 +1060,10 @@ void tcpiiu::hostNameSetRequest ( epicsGuard < cacMutex > & locker ) // X aCC 43 assert ( postSize < 0xffff ); if ( this->sendQue.flushEarlyThreshold ( postSize + 16u ) ) { - this->flushRequest (); + this->flushRequest ( guard ); } - comQueSendMsgMinder minder ( this->sendQue, locker ); + comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_HOST_NAME, postSize, 0u, 0u, 0u, 0u, @@ -966,8 +1076,10 @@ void tcpiiu::hostNameSetRequest ( epicsGuard < cacMutex > & locker ) // X aCC 43 /* * tcpiiu::userNameSetRequest () */ -void tcpiiu::userNameSetRequest ( epicsGuard < cacMutex > & locker ) // X aCC 431 +void tcpiiu::userNameSetRequest ( epicsGuard < epicsMutex > & guard ) // X aCC 431 { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + if ( ! CA_V41 ( this->minorProtocolVersion ) ) { return; } @@ -978,10 +1090,10 @@ void tcpiiu::userNameSetRequest ( epicsGuard < cacMutex > & locker ) // X aCC 43 assert ( postSize < 0xffff ); if ( this->sendQue.flushEarlyThreshold ( postSize + 16u ) ) { - this->flushRequest (); + this->flushRequest ( guard ); } - comQueSendMsgMinder minder ( this->sendQue, locker ); + comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_CLIENT_NAME, postSize, 0u, 0u, 0u, 0u, @@ -991,12 +1103,15 @@ void tcpiiu::userNameSetRequest ( epicsGuard < cacMutex > & locker ) // X aCC 43 minder.commit (); } -void tcpiiu::disableFlowControlRequest ( epicsGuard < cacMutex > & locker ) // X aCC 431 +void tcpiiu::disableFlowControlRequest ( + epicsGuard < epicsMutex > & guard ) // X aCC 431 { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { - this->flushRequest (); + this->flushRequest ( guard ); } - comQueSendMsgMinder minder ( this->sendQue, locker ); + comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_EVENTS_ON, 0u, 0u, 0u, 0u, 0u, @@ -1004,12 +1119,15 @@ void tcpiiu::disableFlowControlRequest ( epicsGuard < cacMutex > & locker ) // X minder.commit (); } -void tcpiiu::enableFlowControlRequest ( epicsGuard < cacMutex > & locker ) // X aCC 431 +void tcpiiu::enableFlowControlRequest ( + epicsGuard < epicsMutex > & guard ) // X aCC 431 { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { - this->flushRequest (); + this->flushRequest ( guard ); } - comQueSendMsgMinder minder ( this->sendQue, locker ); + comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_EVENTS_OFF, 0u, 0u, 0u, 0u, 0u, @@ -1017,16 +1135,18 @@ void tcpiiu::enableFlowControlRequest ( epicsGuard < cacMutex > & locker ) // X minder.commit (); } -void tcpiiu::versionMessage ( epicsGuard < cacMutex > & locker, // X aCC 431 +void tcpiiu::versionMessage ( epicsGuard < epicsMutex > & guard, // X aCC 431 const cacChannel::priLev & priority ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + assert ( priority <= 0xffff ); if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { - this->flushRequest (); + this->flushRequest ( guard ); } - comQueSendMsgMinder minder ( this->sendQue, locker ); + comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_VERSION, 0u, priority, CA_MINOR_PROTOCOL_REVISION, 0u, 0u, @@ -1034,12 +1154,14 @@ void tcpiiu::versionMessage ( epicsGuard < cacMutex > & locker, // X aCC 431 minder.commit (); } -void tcpiiu::echoRequest ( epicsGuard < cacMutex > & locker ) // X aCC 431 +void tcpiiu::echoRequest ( epicsGuard < epicsMutex > & guard ) // X aCC 431 { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { - this->flushRequest (); + this->flushRequest ( guard ); } - comQueSendMsgMinder minder ( this->sendQue, locker ); + comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_ECHO, 0u, 0u, 0u, 0u, 0u, @@ -1047,12 +1169,11 @@ void tcpiiu::echoRequest ( epicsGuard < cacMutex > & locker ) // X aCC 431 minder.commit (); } -void tcpiiu::writeRequest ( epicsGuard < cacMutex > & guard, // X aCC 431 - nciu &chan, unsigned type, unsigned nElem, const void *pValue ) +void tcpiiu::writeRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 + nciu &chan, unsigned type, arrayElementCount nElem, const void *pValue ) { - if ( ! chan.connected () ) { - throw cacChannel::notConnected(); - } + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestWithPayLoad ( CA_PROTO_WRITE, type, nElem, chan.getSID(), chan.getCID(), pValue, @@ -1061,13 +1182,12 @@ void tcpiiu::writeRequest ( epicsGuard < cacMutex > & guard, // X aCC 431 } -void tcpiiu::writeNotifyRequest ( epicsGuard < cacMutex > & guard, // X aCC 431 +void tcpiiu::writeNotifyRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 nciu &chan, netWriteNotifyIO &io, unsigned type, - unsigned nElem, const void *pValue ) + arrayElementCount nElem, const void *pValue ) { - if ( ! chan.connected () ) { - throw cacChannel::notConnected(); - } + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + if ( ! this->ca_v41_ok () ) { throw cacChannel::unsupportedByService(); } @@ -1078,42 +1198,42 @@ void tcpiiu::writeNotifyRequest ( epicsGuard < cacMutex > & guard, // X aCC 431 minder.commit (); } -void tcpiiu::readNotifyRequest ( epicsGuard < cacMutex > & locker, // X aCC 431 +void tcpiiu::readNotifyRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 nciu & chan, netReadNotifyIO & io, - unsigned dataType, unsigned nElem ) + unsigned dataType, arrayElementCount nElem ) { - if ( ! chan.connected () ) { - throw cacChannel::notConnected (); + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + if ( INVALID_DB_REQ ( dataType ) ) { + throw cacChannel::badType (); } - if ( ! dbr_type_is_valid ( dataType ) ) { - throw cacChannel::badType(); - } - if ( nElem > chan.nativeElementCount() ) { - throw cacChannel::outOfBounds (); - } - unsigned maxBytes; + arrayElementCount maxBytes; if ( CA_V49 ( this->minorProtocolVersion ) ) { maxBytes = this->cacRef.largeBufferSizeTCP (); } else { maxBytes = MAX_TCP; } - unsigned maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; + arrayElementCount maxElem = + ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; if ( nElem > maxElem ) { throw cacChannel::msgBodyCacheTooSmall (); } - comQueSendMsgMinder minder ( this->sendQue, locker ); + comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_READ_NOTIFY, 0u, static_cast < ca_uint16_t > ( dataType ), - nElem, chan.getSID(), io.getId(), + static_cast < ca_uint32_t > ( nElem ), + chan.getSID(), io.getId(), CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } void tcpiiu::createChannelRequest ( - nciu & chan, epicsGuard < cacMutex > & guard ) // X aCC 431 + nciu & chan, epicsGuard < epicsMutex > & guard ) // X aCC 431 { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + const char *pName; unsigned nameLength; ca_uint32_t identity; @@ -1153,10 +1273,12 @@ void tcpiiu::createChannelRequest ( minder.commit (); } -void tcpiiu::clearChannelRequest ( epicsGuard < cacMutex > & locker, // X aCC 431 +void tcpiiu::clearChannelRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 ca_uint32_t sid, ca_uint32_t cid ) { - comQueSendMsgMinder minder ( this->sendQue, locker ); + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_CLEAR_CHANNEL, 0u, 0u, 0u, sid, cid, @@ -1168,10 +1290,12 @@ void tcpiiu::clearChannelRequest ( epicsGuard < cacMutex > & locker, // X aCC 43 // this routine return void because if this internally fails the best response // is to try again the next time that we reconnect // -void tcpiiu::subscriptionRequest ( epicsGuard < cacMutex > & locker, // X aCC 431 +void tcpiiu::subscriptionRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 nciu &chan, netSubscription & subscr ) { - if ( ! chan.connected() ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + if ( ! chan.isConnected ( guard ) ) { return; } unsigned mask = subscr.getMask(); @@ -1179,8 +1303,8 @@ void tcpiiu::subscriptionRequest ( epicsGuard < cacMutex > & locker, // X aCC 43 mask &= 0xffff; this->cacRef.printf ( "CAC: subscriptionRequest() truncated unusual event select mask\n" ); } - arrayElementCount nElem = subscr.getCount (); - unsigned maxBytes; + arrayElementCount nElem = subscr.getCount ( guard ); + arrayElementCount maxBytes; if ( CA_V49 ( this->minorProtocolVersion ) ) { maxBytes = this->cacRef.largeBufferSizeTCP (); } @@ -1188,15 +1312,18 @@ void tcpiiu::subscriptionRequest ( epicsGuard < cacMutex > & locker, // X aCC 43 maxBytes = MAX_TCP; } unsigned dataType = subscr.getType (); - unsigned maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; + // data type bounds checked when sunscription created + arrayElementCount maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; if ( nElem > maxElem ) { throw cacChannel::msgBodyCacheTooSmall (); } - comQueSendMsgMinder minder ( this->sendQue, locker ); + comQueSendMsgMinder minder ( this->sendQue, guard ); + // nElement bounds checked above this->sendQue.insertRequestHeader ( CA_PROTO_EVENT_ADD, 16u, static_cast < ca_uint16_t > ( dataType ), - nElem, chan.getSID(), subscr.getId(), + static_cast < ca_uint32_t > ( nElem ), + chan.getSID(), subscr.getId(), CA_V49 ( this->minorProtocolVersion ) ); // extension @@ -1208,85 +1335,112 @@ void tcpiiu::subscriptionRequest ( epicsGuard < cacMutex > & locker, // X aCC 43 minder.commit (); } -void tcpiiu::subscriptionCancelRequest ( epicsGuard < cacMutex > & locker, // X aCC 431 +// +// this routine return void because if this internally fails the best response +// is to try again the next time that we reconnect +// +void tcpiiu::subscriptionUpdateRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 + nciu & chan, netSubscription & subscr ) +{ + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + if ( ! chan.isConnected ( guard ) ) { + return; + } + arrayElementCount nElem = subscr.getCount ( guard ); + arrayElementCount maxBytes; + if ( CA_V49 ( this->minorProtocolVersion ) ) { + maxBytes = this->cacRef.largeBufferSizeTCP (); + } + else { + maxBytes = MAX_TCP; + } + unsigned dataType = subscr.getType (); + // data type bounds checked when subscription constructed + arrayElementCount maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; + if ( nElem > maxElem ) { + throw cacChannel::msgBodyCacheTooSmall (); + } + comQueSendMsgMinder minder ( this->sendQue, guard ); + // nElem boounds checked above + this->sendQue.insertRequestHeader ( + CA_PROTO_READ_NOTIFY, 0u, + static_cast < ca_uint16_t > ( dataType ), + static_cast < ca_uint32_t > ( nElem ), + chan.getSID (), subscr.getId (), + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + +void tcpiiu::subscriptionCancelRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 nciu & chan, netSubscription & subscr ) { - comQueSendMsgMinder minder ( this->sendQue, locker ); + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + comQueSendMsgMinder minder ( this->sendQue, guard ); this->sendQue.insertRequestHeader ( CA_PROTO_EVENT_CANCEL, 0u, - static_cast < ca_uint16_t > ( subscr.getType() ), - static_cast < ca_uint16_t > ( subscr.getCount() ), + static_cast < ca_uint16_t > ( subscr.getType () ), + static_cast < ca_uint16_t > ( subscr.getCount ( guard ) ), chan.getSID(), subscr.getId(), CA_V49 ( this->minorProtocolVersion ) ); minder.commit (); } -bool tcpiiu::flush () +bool tcpiiu::flush ( epicsGuard < epicsMutex > & guard ) { - if ( this->sendQue.occupiedBytes() == 0 ) { - return true; - } + guard.assertIdenticalMutex ( this->cacRef.mutexRef() ); - bool success = true; - unsigned bytesToBeSent = 0u; - epicsTime current = epicsTime::getCurrent (); - while ( true ) { - comBuf * pBuf; - { - epicsGuard < cacMutex > autoMutex ( this->cacRef.mutexRef() ); - // set it here with this odd order because we must have - // the lock and we miust have already sent the bytes - if ( bytesToBeSent ) { - this->unacknowledgedSendBytes += bytesToBeSent; - } - pBuf = this->sendQue.popNextComBufToSend (); - if ( ! pBuf ) { - break; - } - bytesToBeSent = pBuf->occupiedBytes (); - } + if ( this->sendQue.occupiedBytes() > 0 ) { + while ( comBuf * pBuf = this->sendQue.popNextComBufToSend () ) { + epicsTime current = epicsTime::getCurrent (); - success = pBuf->flushToWire ( *this, current ); - pBuf->~comBuf (); - this->comBufMemMgr.release ( pBuf ); - - if ( ! success ) { - epicsGuard < cacMutex > autoMutex ( this->cacRef.mutexRef() ); - while ( ( pBuf = this->sendQue.popNextComBufToSend () ) ) { + unsigned bytesToBeSent = pBuf->occupiedBytes (); + bool success = false; + { + // no lock while blocking to send + epicsGuardRelease < epicsMutex > unguard ( guard ); + success = pBuf->flushToWire ( *this, current ); pBuf->~comBuf (); this->comBufMemMgr.release ( pBuf ); } - break; - } - current = epicsTime::getCurrent (); + if ( ! success ) { + while ( ( pBuf = this->sendQue.popNextComBufToSend () ) ) { + pBuf->~comBuf (); + this->comBufMemMgr.release ( pBuf ); + } + return false; + } - // - // we avoid calling this with the lock applied because - // it restarts the recv wd timer, this might block - // until a recv wd timer expire callback completes, and - // this callback takes the lock - // - if ( this->unacknowledgedSendBytes > - this->socketLibrarySendBufferSize ) { - this->recvDog.sendBacklogProgressNotify ( current ); + // set it here with this odd order because we must have + // the lock and we must have already sent the bytes + this->unacknowledgedSendBytes += bytesToBeSent; + if ( this->unacknowledgedSendBytes > + this->socketLibrarySendBufferSize ) { + this->recvDog.sendBacklogProgressNotify ( guard, current ); + } } } + + this->earlyFlush = false; if ( this->blockingForFlush ) { this->flushBlockEvent.signal (); } - this->earlyFlush = false; - return success; + + return true; } // ~tcpiiu() will not return while this->blockingForFlush is greater than zero void tcpiiu::blockUntilSendBacklogIsReasonable ( - cacNotify & notify, epicsGuard < cacMutex > & primaryLocker ) + cacContextNotify & notify, epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + assert ( this->blockingForFlush < UINT_MAX ); this->blockingForFlush++; while ( this->sendQue.flushBlockThreshold(0u) && this->state == iiucs_connected ) { - epicsGuardRelease < cacMutex > autoRelease ( primaryLocker ); + epicsGuardRelease < epicsMutex > autoRelease ( guard ); notify.blockForEventAndEnableCallbacks ( this->flushBlockEvent, 30.0 ); } assert ( this->blockingForFlush > 0u ); @@ -1296,16 +1450,22 @@ void tcpiiu::blockUntilSendBacklogIsReasonable ( } } -void tcpiiu::flushRequestIfAboveEarlyThreshold ( epicsGuard < cacMutex > & ) +void tcpiiu::flushRequestIfAboveEarlyThreshold ( + epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + if ( ! this->earlyFlush && this->sendQue.flushEarlyThreshold(0u) ) { this->earlyFlush = true; this->sendThreadFlushEvent.signal (); } } -bool tcpiiu::flushBlockThreshold ( epicsGuard < cacMutex > & ) const +bool tcpiiu::flushBlockThreshold ( + epicsGuard < epicsMutex > & guard ) const { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + return this->sendQue.flushBlockThreshold ( 0u ); } @@ -1340,39 +1500,121 @@ const char * tcpiiu::pHostName () const void tcpiiu::removeAllChannels ( epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard, - cacDisconnectChannelPrivate & dcp ) + epicsGuard < epicsMutex > & guard, + udpiiu & discIIU ) { - epicsTime currentTime = epicsTime::getCurrent (); - while ( nciu *pChan = this->channelList.first() ) { - // if the claim reply has not returned then we will issue - // the clear channel request to the server when the claim reply - // arrives and there is no matching nciu in the client - if ( pChan->connected() ) { - this->clearChannelRequest ( guard, pChan->getSID(), pChan->getCID() ); - } - dcp.disconnectChannel ( currentTime, cbGuard, guard, *pChan ); + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + while ( nciu * pChan = this->createReqPend.get () ) { + discIIU.installDisconnectedChannel ( *pChan ); + pChan->setServerAddressUnknown ( discIIU, guard ); } + + while ( nciu * pChan = this->createRespPend.get () ) { + this->clearChannelRequest ( guard, pChan->getSID(), pChan->getCID() ); + discIIU.installDisconnectedChannel ( *pChan ); + pChan->setServerAddressUnknown ( discIIU, guard ); + } + + while ( nciu * pChan = this->subscripReqPend.get () ) { + pChan->disconnectAllIO ( cbGuard, guard ); + discIIU.installDisconnectedChannel ( *pChan ); + pChan->setServerAddressUnknown ( discIIU, guard ); + pChan->unresponsiveCircuitNotify ( cbGuard, guard ); + } + + while ( nciu * pChan = this->connectedList.get () ) { + pChan->disconnectAllIO ( cbGuard, guard ); + discIIU.installDisconnectedChannel ( *pChan ); + pChan->setServerAddressUnknown ( discIIU, guard ); + pChan->unresponsiveCircuitNotify ( cbGuard, guard ); + } + + while ( nciu * pChan = this->unrespCircuit.get () ) { + pChan->disconnectAllIO ( cbGuard, guard ); + discIIU.installDisconnectedChannel ( *pChan ); + pChan->setServerAddressUnknown ( discIIU, guard ); + } + + this->channelCountTot = 0u; + + this->initiateCleanShutdown ( guard ); } void tcpiiu::installChannel ( epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > & guard, + epicsGuard < epicsMutex > & guard, nciu & chan, unsigned sidIn, ca_uint16_t typeIn, arrayElementCount countIn ) { - this->channelList.add ( chan ); + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + this->createReqPend.add ( chan ); + this->channelCountTot++; + chan.channelNode::listMember = channelNode::cs_createReqPend; chan.searchReplySetUp ( *this, sidIn, typeIn, countIn, guard ); - chan.createChannelRequest ( *this, guard ); - this->flushRequest (); + // The tcp send thread runs at apriority below the udp thread + // so that this will not send small packets + this->sendThreadFlushEvent.signal (); +} + +void tcpiiu::nameResolutionMsgEndNotify () +{ + bool wakeupNeeded = false; + { + epicsGuard < epicsMutex > autoMutex ( this->cacRef.mutexRef() ); + if ( this->createReqPend.count () ) { + wakeupNeeded = true; + } + } + if ( wakeupNeeded ) { + this->sendThreadFlushEvent.signal (); + } +} + +void tcpiiu::connectNotify ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + this->createRespPend.remove ( chan ); + this->subscripReqPend.add ( chan ); + chan.channelNode::listMember = channelNode::cs_subscripReqPend; + // the TCP send thread is awakened by its receive thread whenever the receive thread + // is about to block if this->subscripReqPend has items in it } void tcpiiu::uninstallChan ( - epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > & guard, nciu & chan ) + epicsGuard < epicsMutex > & guard, nciu & chan ) { - this->channelList.remove ( chan ); - if ( channelList.count() == 0 ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + + switch ( chan.channelNode::listMember ) { + case channelNode::cs_createReqPend: + this->createReqPend.remove ( chan ); + break; + case channelNode::cs_createRespPend: + this->createRespPend.remove ( chan ); + break; + case channelNode::cs_subscripReqPend: + this->subscripReqPend.remove ( chan ); + break; + case channelNode::cs_connected: + this->connectedList.remove ( chan ); + break; + case channelNode::cs_unrespCircuit: + this->unrespCircuit.remove ( chan ); + break; + case channelNode::cs_subscripUpdateReqPend: + this->subscripUpdateReqPend.remove ( chan ); + break; + default: + this->cacRef.printf ( + "cac: attempt to uninstall channel from tcp iiu, but it inst installed there?" ); + } + chan.channelNode::listMember = channelNode::cs_none; + this->channelCountTot--; + if ( this->channelCountTot == 0 ) { this->initiateCleanShutdown ( guard ); } } @@ -1392,9 +1634,9 @@ int tcpiiu::printf ( const char *pformat, ... ) } // this is called virtually -void tcpiiu::flushRequest () +void tcpiiu::flushRequest ( epicsGuard < epicsMutex > & ) { - if ( this->sendQue.occupiedBytes() > 0 ) { + if ( this->sendQue.occupiedBytes () > 0 ) { this->sendThreadFlushEvent.signal (); } } @@ -1442,14 +1684,14 @@ void tcpiiu::blockUntilBytesArePendingInOS () break; } else if ( status == 0 ) { - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); return; } else { int localErrno = SOCKERRNO; if ( localErrno == SOCK_SHUTDOWN ) { - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); return; } @@ -1458,12 +1700,12 @@ void tcpiiu::blockUntilBytesArePendingInOS () } if ( localErrno == SOCK_ECONNABORTED ) { - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); return; } if ( localErrno == SOCK_ECONNRESET ) { - this->cacRef.disconnectNotify ( *this ); + this->disconnectNotify (); return; } @@ -1551,6 +1793,11 @@ void tcpiiu::operator delete ( void * /* pCadaver */ ) __FILE__, __LINE__ ); } +// protected by callback lock +unsigned tcpiiu::channelCount ( epicsGuard < callbackMutex > & ) +{ + return this->channelCountTot; +} diff --git a/src/ca/templateInstances.cpp b/src/ca/templateInstances.cpp index b61f795c1..d648b4a26 100644 --- a/src/ca/templateInstances.cpp +++ b/src/ca/templateInstances.cpp @@ -58,17 +58,16 @@ template class tsFreeList < CASG, 128 >; template class tsFreeList < syncGroupReadNotify, 128, epicsMutexNOOP >; template class tsFreeList < syncGroupWriteNotify, 128, epicsMutexNOOP >; template class tsFreeList < comBuf, 0x20 >; -template class tsFreeList < getCallback, 1024 >; -template class tsFreeList < getCopy, 1024 >; +template class tsFreeList < getCallback, 1024, epicsMutexNOOP >; +template class tsFreeList < getCopy, 1024, epicsMutexNOOP >; template class tsFreeList < hostNameCache, 16 >; template class tsFreeList < msgForMultiplyDefinedPV, 16 >; template class tsFreeList < nciu, 1024 >; template class tsFreeList < oldChannelNotify, 1024 >; -template class tsFreeList < oldSubscription, 1024 >; -template class tsFreeList < putCallback, 1024 >; +template class tsFreeList < oldSubscription, 1024, epicsMutexNOOP >; +template class tsFreeList < putCallback, 1024, epicsMutexNOOP >; template class tsFreeList < repeaterClient, 0x20 >; -template class epicsSingleton ; -template class epicsSingleton ; +template class epicsSingleton < localHostName >; #ifdef _MSC_VER # pragma warning ( pop ) diff --git a/src/ca/udpiiu.cpp b/src/ca/udpiiu.cpp index 6d00fb621..c31e63f7e 100644 --- a/src/ca/udpiiu.cpp +++ b/src/ca/udpiiu.cpp @@ -36,8 +36,9 @@ #include "iocinf.h" #include "inetAddrID.h" #include "cac.h" -#include "repeaterSubscribeTimer.h" #include "searchTimer.h" +#include "repeaterSubscribeTimer.h" +#include "disconnectGovernorTimer.h" // UDP protocol dispatch table const udpiiu::pProtoStubUDP udpiiu::udpJumpTableCAC [] = @@ -69,8 +70,9 @@ udpiiu::udpiiu ( epicsTimerQueueActive & timerQueue, callbackMutex & cbMutex, ca recvThread ( *this, cbMutex, "CAC-UDP", epicsThreadGetStackSize ( epicsThreadStackMedium ), - cac::lowestPriorityLevelAbove - ( cac.getInitializingThreadsPriority () ) ), + cac::lowestPriorityLevelAbove ( + cac::lowestPriorityLevelAbove ( + cac.getInitializingThreadsPriority () ) ) ), rtteMean ( 5.0e-3 ), // seconds cacRef ( cac ), nBytesInXmitBuf ( 0 ), @@ -78,6 +80,7 @@ udpiiu::udpiiu ( epicsTimerQueueActive & timerQueue, callbackMutex & cbMutex, ca rtteSequenceNumber ( 0 ), lastReceivedSeqNo ( 0 ), sock ( 0 ), + pGovTmr ( new disconnectGovernorTimer ( *this, timerQueue ) ), // The udpiiu and the search timer share the same lock because // this is much more efficent with recursive locks. Also, access // to the udp's netiiu base list is protected. @@ -203,7 +206,14 @@ udpiiu::~udpiiu () // no need to own CAC lock here because the CA context // is being decomissioned - tsDLIter < nciu > chan = this->channelList.firstIter (); + tsDLIter < nciu > chan = this->disconnGovernor.firstIter (); + while ( chan.valid () ) { + tsDLIter < nciu > next = chan; + next++; + chan->serviceShutdownNotify (); + chan = next; + } + chan = this->serverAddrRes.firstIter (); while ( chan.valid () ) { tsDLIter < nciu > next = chan; next++; @@ -226,6 +236,11 @@ udpiiu::~udpiiu () void udpiiu::shutdown () { + // stop all of the timers + this->pGovTmr->shutdown (); + this->pSearchTmr->shutdown (); + this->pRepeaterSubscribeTmr->shutdown (); + if ( ! this->recvThread.exitWait ( 0.0 ) ) { unsigned tries = 0u; @@ -353,8 +368,8 @@ void udpiiu::repeaterRegistrationMessage ( unsigned attemptNumber ) void epicsShareAPI caRepeaterRegistrationMessage ( SOCKET sock, unsigned repeaterPort, unsigned attemptNumber ) { - caHdr msg; osiSockAddr saddr; + caHdr msg; int status; int len; @@ -943,11 +958,18 @@ void udpiiu::show ( unsigned level ) const ::printf ("\tshut down command bool %u\n", this->shutdownCmd ); ::printf ( "\trecv thread exit signal:\n" ); this->recvThread.show ( level - 2u ); - ::printf ( "search message timer:\n" ); - this->pSearchTmr->show ( level - 2u ); ::printf ( "repeater subscribee timer:\n" ); this->pRepeaterSubscribeTmr->show ( level - 2u ); - tsDLIterConst < nciu > pChan = this->channelList.firstIter (); + ::printf ( "disconnect governor subscribee timer:\n" ); + this->pGovTmr->show ( level - 2u ); + tsDLIterConst < nciu > pChan = this->disconnGovernor.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + ::printf ( "search message timer:\n" ); + this->pSearchTmr->show ( level - 2u ); + pChan = this->serverAddrRes.firstIter (); while ( pChan.valid () ) { pChan->show ( level - 2u ); pChan++; @@ -990,7 +1012,7 @@ void udpiiu::beaconAnomalyNotify ( const epicsTime & currentTime ) { epicsGuard guard ( this->mutex ); - tsDLIter < nciu > chan = this->channelList.firstIter (); + tsDLIter < nciu > chan = this->serverAddrRes.firstIter (); while ( chan.valid () ) { chan->beaconAnomalyNotify (); chan++; @@ -1022,13 +1044,13 @@ bool udpiiu::searchMsg ( epicsGuard < udpMutex > & /* guard */, { bool success; - if ( nciu *pChan = this->channelList.get () ) { + if ( nciu *pChan = this->serverAddrRes.get () ) { success = pChan->searchMsg ( *this, retryNoForThisChannel ); if ( success ) { - this->channelList.add ( *pChan ); + this->serverAddrRes.add ( *pChan ); } else { - this->channelList.push ( *pChan ); + this->serverAddrRes.push ( *pChan ); } } else { @@ -1038,23 +1060,55 @@ bool udpiiu::searchMsg ( epicsGuard < udpMutex > & /* guard */, return success; } -void udpiiu::installChannel ( const epicsTime & currentTime, - nciu & chan, unsigned minRetryNo ) +void udpiiu::installNewChannel ( const epicsTime & currentTime, nciu & chan ) { bool firstChannel = false; - epicsGuard < udpMutex > guard ( this->mutex ); - // add it to the front of the list so that + if ( this->serverAddrRes.count() == 0 ) { + firstChannel = true; + } + // push them to the front of the list so that // a search request is sent immediately, and // so that the new channel's retry count is // seen when calculating the minimum retry // which is used to compute the search interval - this->channelList.push ( chan ); - if ( this->channelList.count() == 1 ) { - firstChannel = true; - } + this->serverAddrRes.push ( chan ); + chan.channelNode::listMember = + channelNode::cs_serverAddrResPend; + this->pSearchTmr->newChannelNotify ( guard, currentTime, - firstChannel, minRetryNo ); + firstChannel, 0 ); +} + +void udpiiu::installDisconnectedChannel ( nciu & chan ) +{ + epicsGuard < udpMutex > guard ( this->mutex ); + this->disconnGovernor.add ( chan ); + chan.channelNode::listMember = + channelNode::cs_disconnGov; +} + +void udpiiu::govExpireNotify ( const epicsTime & currentTime ) +{ + epicsGuard < udpMutex > guard ( this->mutex ); + if ( this->disconnGovernor.count () ) { + bool firstChannel = false; + if ( this->serverAddrRes.count() == 0 ) { + firstChannel = true; + } + // push them to the front of the list so that + // a search request is sent immediately, and + // so that the new channel's retry count is + // seen when calculating the minimum retry + // which is used to compute the search interval + while ( nciu * pChan = this->disconnGovernor.get () ) { + this->serverAddrRes.push ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_serverAddrResPend; + } + this->pSearchTmr->newChannelNotify ( guard, currentTime, + firstChannel, disconnectRetrySetpoint ); + } } int udpiiu::printf ( const char *pformat, ... ) @@ -1071,12 +1125,23 @@ int udpiiu::printf ( const char *pformat, ... ) return status; } -void udpiiu::uninstallChan ( - epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > &, nciu & chan ) +void udpiiu::uninstallChan ( + epicsGuard < epicsMutex > & guardIn, nciu & chan ) { + guardIn.assertIdenticalMutex ( this->cacRef.mutexRef () ); + epicsGuard < udpMutex > guard ( this->mutex ); - this->channelList.remove ( chan ); + if ( chan.channelNode::listMember == channelNode::cs_disconnGov ) { + this->disconnGovernor.remove ( chan ); + } + else if ( chan.channelNode::listMember == channelNode::cs_serverAddrResPend ) { + this->serverAddrRes.remove ( chan ); + } + else { + this->cacRef.printf ( + "cac: attempt to uninstall channel from udp iiu, but it inst installed there?" ); + } + chan.channelNode::listMember = channelNode::cs_none; } void udpiiu::hostName ( char *pBuf, unsigned bufLength ) const @@ -1099,60 +1164,74 @@ bool udpiiu::ca_v41_ok () const return netiiu::ca_v41_ok (); } -void udpiiu::writeRequest ( epicsGuard < cacMutex > & guard, nciu & chan, unsigned type, - unsigned nElem, const void * pValue ) +void udpiiu::writeRequest ( epicsGuard < epicsMutex > & guard, + nciu & chan, unsigned type, arrayElementCount nElem, const void * pValue ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); netiiu::writeRequest ( guard, chan, type, nElem, pValue ); } -void udpiiu::writeNotifyRequest ( epicsGuard < cacMutex > & guard, nciu & chan, - netWriteNotifyIO & io, unsigned type, unsigned nElem, const void *pValue ) +void udpiiu::writeNotifyRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, + netWriteNotifyIO & io, unsigned type, arrayElementCount nElem, const void *pValue ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); netiiu::writeNotifyRequest ( guard, chan, io, type, nElem, pValue ); } -void udpiiu::readNotifyRequest ( epicsGuard < cacMutex > & guard, nciu & chan, - netReadNotifyIO & io, unsigned type, unsigned nElem ) +void udpiiu::readNotifyRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, + netReadNotifyIO & io, unsigned type, arrayElementCount nElem ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); netiiu::readNotifyRequest ( guard, chan, io, type, nElem ); } -void udpiiu::clearChannelRequest ( epicsGuard < cacMutex > & guard, +void udpiiu::clearChannelRequest ( epicsGuard < epicsMutex > & guard, ca_uint32_t sid, ca_uint32_t cid ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); netiiu::clearChannelRequest ( guard, sid, cid ); } -void udpiiu::subscriptionRequest ( epicsGuard < cacMutex > & guard, nciu & chan, +void udpiiu::subscriptionRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); netiiu::subscriptionRequest ( guard, chan, subscr ); } -void udpiiu::subscriptionCancelRequest ( epicsGuard < cacMutex > & guard, +void udpiiu::subscriptionUpdateRequest ( epicsGuard < epicsMutex > &, nciu &, + netSubscription & ) +{ +} + +void udpiiu::subscriptionCancelRequest ( epicsGuard < epicsMutex > & guard, nciu & chan, netSubscription & subscr ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); netiiu::subscriptionCancelRequest ( guard, chan, subscr ); } -void udpiiu::flushRequest () +void udpiiu::flushRequest ( epicsGuard < epicsMutex > & guard ) { - netiiu::flushRequest (); + netiiu::flushRequest ( guard ); } -bool udpiiu::flushBlockThreshold ( epicsGuard < cacMutex > & guard ) const +bool udpiiu::flushBlockThreshold ( epicsGuard < epicsMutex > & guard ) const { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); return netiiu::flushBlockThreshold ( guard ); } -void udpiiu::flushRequestIfAboveEarlyThreshold ( epicsGuard < cacMutex > & guard ) +void udpiiu::flushRequestIfAboveEarlyThreshold ( epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); netiiu::flushRequestIfAboveEarlyThreshold ( guard ); } void udpiiu::blockUntilSendBacklogIsReasonable - ( cacNotify & notify, epicsGuard < cacMutex > & guard ) + ( cacContextNotify & notify, epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); netiiu::blockUntilSendBacklogIsReasonable ( notify, guard ); } diff --git a/src/ca/udpiiu.h b/src/ca/udpiiu.h index f647df4b8..60b677a1b 100644 --- a/src/ca/udpiiu.h +++ b/src/ca/udpiiu.h @@ -85,7 +85,7 @@ public: udpiiu ( class epicsTimerQueueActive &, callbackMutex &, class cac & ); virtual ~udpiiu (); void installNewChannel ( const epicsTime & currentTime, nciu & ); - void installDisconnectedChannel ( const epicsTime & currentTime, nciu & ); + void installDisconnectedChannel ( nciu & ); void repeaterRegistrationMessage ( unsigned attemptNumber ); bool searchMsg ( epicsGuard < udpMutex > &, unsigned & retryNoForThisChannel ); void datagramFlush ( epicsGuard < udpMutex > &, const epicsTime & currentTime ); @@ -94,10 +94,10 @@ public: bool wakeupMsg (); void repeaterConfirmNotify (); void beaconAnomalyNotify ( const epicsTime & currentTime ); + void govExpireNotify ( const epicsTime & currentTime ); int printf ( const char *pformat, ... ); - unsigned channelCount ( epicsGuard < udpMutex > & ) const; - void uninstallChan ( - epicsGuard < callbackMutex > &, epicsGuard < cacMutex > &, nciu & ); + unsigned unresolvedChannelCount ( epicsGuard < udpMutex > & ) const; + void uninstallChan ( epicsGuard < epicsMutex > &, nciu & ); bool pushDatagramMsg ( const caHdr & hdr, const void * pExt, ca_uint16_t extsize); void shutdown (); @@ -111,7 +111,9 @@ private: char recvBuf [MAX_UDP_RECV]; mutable udpMutex mutex; udpRecvThread recvThread; - tsDLList < nciu > channelList; + // nciu state field tells us which list + tsDLList < nciu > disconnGovernor; + tsDLList < nciu > serverAddrRes; ELLLIST dest; epicsTime rtteTimeStamp; double rtteMean; @@ -121,6 +123,7 @@ private: ca_uint32_t rtteSequenceNumber; ca_uint32_t lastReceivedSeqNo; SOCKET sock; + epics_auto_ptr < class disconnectGovernorTimer > pGovTmr; epics_auto_ptr < class searchTimer > pSearchTmr; epics_auto_ptr < class repeaterSubscribeTimer > pRepeaterSubscribeTmr; ca_uint16_t repeaterPort; @@ -165,28 +168,29 @@ private: const char * pHostName () const; // deprecated - please do not use bool ca_v42_ok () const; bool ca_v41_ok () const; - void writeRequest ( epicsGuard < cacMutex > &, nciu &, unsigned type, - unsigned nElem, const void *pValue ); - void writeNotifyRequest ( epicsGuard < cacMutex > &, nciu &, netWriteNotifyIO &, - unsigned type, unsigned nElem, const void *pValue ); - void readNotifyRequest ( epicsGuard < cacMutex > &, nciu &, netReadNotifyIO &, - unsigned type, unsigned nElem ); - void clearChannelRequest ( epicsGuard < cacMutex > &, + void writeRequest ( epicsGuard < epicsMutex > &, nciu &, unsigned type, + arrayElementCount nElem, const void *pValue ); + void writeNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netWriteNotifyIO &, + unsigned type, arrayElementCount nElem, const void *pValue ); + void readNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, netReadNotifyIO &, + unsigned type, arrayElementCount nElem ); + void clearChannelRequest ( epicsGuard < epicsMutex > &, ca_uint32_t sid, ca_uint32_t cid ); - void subscriptionRequest ( epicsGuard < cacMutex > &, nciu &, - netSubscription &subscr ); + void subscriptionRequest ( epicsGuard < epicsMutex > &, nciu &, + netSubscription & ); + void subscriptionUpdateRequest ( epicsGuard < epicsMutex > &, nciu &, + netSubscription & ); bool pushVersionMsg (); - void subscriptionCancelRequest ( epicsGuard < cacMutex > &, + void subscriptionCancelRequest ( epicsGuard < epicsMutex > &, nciu & chan, netSubscription & subscr ); - void flushRequest (); - bool flushBlockThreshold ( epicsGuard < cacMutex > & ) const; - void flushRequestIfAboveEarlyThreshold ( epicsGuard < cacMutex > & ); + void flushRequest ( epicsGuard < epicsMutex > & ); + bool flushBlockThreshold ( epicsGuard < epicsMutex > & ) const; + void flushRequestIfAboveEarlyThreshold ( epicsGuard < epicsMutex > & ); void blockUntilSendBacklogIsReasonable - ( cacNotify &, epicsGuard < cacMutex > & ); + ( cacContextNotify &, epicsGuard < epicsMutex > & ); void requestRecvProcessPostponedFlush (); osiSockAddr getNetworkAddress () const; double receiveWatchdogDelay () const; - void installChannel ( const epicsTime & currentTime, nciu &, unsigned minRetryNo ); udpiiu ( const udpiiu & ); udpiiu & operator = ( const udpiiu & ); @@ -215,10 +219,10 @@ inline void udpMutex::show ( unsigned level ) const this->mutex.show ( level ); } -inline unsigned udpiiu::channelCount ( +inline unsigned udpiiu::unresolvedChannelCount ( epicsGuard < udpMutex > & ) const { - return this->channelList.count (); + return this->serverAddrRes.count (); } inline ca_uint32_t udpiiu::datagramSeqNumber ( @@ -233,15 +237,5 @@ inline double udpiiu::roundTripDelayEstimate ( return this->rtteMean; } -inline void udpiiu::installNewChannel ( const epicsTime & currentTime, nciu & chan ) -{ - this->installChannel ( currentTime, chan, 0 ); -} - -inline void udpiiu::installDisconnectedChannel ( const epicsTime & currentTime, nciu & chan ) -{ - this->installChannel ( currentTime, chan, disconnectRetrySetpoint ); -} - #endif // udpiiuh diff --git a/src/ca/virtualCircuit.h b/src/ca/virtualCircuit.h index 46321f3d2..e2d70e328 100644 --- a/src/ca/virtualCircuit.h +++ b/src/ca/virtualCircuit.h @@ -41,7 +41,7 @@ #include "compilerDependencies.h" // a modified ca header with capacity for large arrays -struct caHdrLargeArray { +struct caHdrLargeArray { ca_uint32_t m_postsize; // size of message extension ca_uint32_t m_count; // operation data count ca_uint32_t m_cid; // channel identifier @@ -97,29 +97,34 @@ public: const cacChannel::priLev & priorityIn ); ~tcpiiu (); void start (); - void initiateCleanShutdown ( epicsGuard < cacMutex > & ); + void initiateCleanShutdown ( epicsGuard < epicsMutex > & ); void initiateAbortShutdown ( epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > & ); - void unresponsiveCircuitNotify ( epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > & ); - void tcpiiu::responsiveCircuitNotify ( - epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard ); - void disconnectNotify ( epicsGuard < cacMutex > & ); - void beaconAnomalyNotify (); + epicsGuard < epicsMutex > & ); + void responsiveCircuitNotify ( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void sendTimeoutNotify ( const epicsTime & currentTime, + epicsGuard < callbackMutex > & cbGuard ); + void tcpiiu::receiveTimeoutNotify( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < epicsMutex > & ); + void beaconAnomalyNotify ( epicsGuard < epicsMutex > & ); void beaconArrivalNotify ( + epicsGuard < epicsMutex > &, + const epicsTime & currentTime ); + void probeResponseNotify ( + epicsGuard < callbackMutex > &, const epicsTime & currentTime ); - void flushRequest (); - bool flushBlockThreshold ( epicsGuard < cacMutex > & ) const; - void flushRequestIfAboveEarlyThreshold ( epicsGuard < cacMutex > & ); + void flushRequest ( epicsGuard < epicsMutex > & ); + bool flushBlockThreshold ( epicsGuard < epicsMutex > & ) const; + void flushRequestIfAboveEarlyThreshold ( epicsGuard < epicsMutex > & ); void blockUntilSendBacklogIsReasonable - ( cacNotify &, epicsGuard < cacMutex > & ); + ( cacContextNotify &, epicsGuard < epicsMutex > & ); virtual void show ( unsigned level ) const; - bool setEchoRequestPending (); - void createChannelRequest ( nciu &, epicsGuard < cacMutex > & ); + bool setEchoRequestPending ( epicsGuard < epicsMutex > & ); void requestRecvProcessPostponedFlush (); - void clearChannelRequest ( epicsGuard < cacMutex > &, + void clearChannelRequest ( epicsGuard < epicsMutex > &, ca_uint32_t sid, ca_uint32_t cid ); bool ca_v41_ok () const; @@ -135,13 +140,13 @@ public: unsigned channelCount ( epicsGuard < callbackMutex > & ); void removeAllChannels ( epicsGuard < callbackMutex > & cbGuard, - epicsGuard < cacMutex > & guard, - class cacDisconnectChannelPrivate & ); + epicsGuard < epicsMutex > & guard, udpiiu & ); void installChannel ( epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > &, nciu & chan, + epicsGuard < epicsMutex > &, nciu & chan, unsigned sidIn, ca_uint16_t typeIn, arrayElementCount countIn ); - void uninstallChan ( epicsGuard < callbackMutex > &, - epicsGuard < cacMutex > &, nciu & chan ); + void uninstallChan ( epicsGuard < epicsMutex > &, nciu & chan ); + void connectNotify ( epicsGuard < epicsMutex > &, nciu & chan ); + void nameResolutionMsgEndNotify (); bool bytesArePendingInOS () const; @@ -158,7 +163,14 @@ private: tcpSendWatchdog sendDog; comQueSend sendQue; comQueRecv recvQue; - tsDLList < nciu > channelList; + // nciu state field tells us which list + // protected by the callback mutex + tsDLList < nciu > createReqPend; + tsDLList < nciu > createRespPend; + tsDLList < nciu > subscripReqPend; + tsDLList < nciu > connectedList; + tsDLList < nciu > unrespCircuit; + tsDLList < nciu > subscripUpdateReqPend; caHdrLargeArray curMsg; arrayElementCount curDataMax; arrayElementCount curDataBytes; @@ -180,6 +192,7 @@ private: unsigned blockingForFlush; unsigned socketLibrarySendBufferSize; unsigned unacknowledgedSendBytes; + unsigned channelCountTot; bool busyStateDetected; // only modified by the recv thread bool flowControlActive; // only modified by the send process thread bool echoRequestPending; @@ -189,7 +202,7 @@ private: bool recvProcessPostponedFlush; bool discardingPendingData; bool socketHasBeenClosed; - bool softDisconnect; + bool unresponsiveCircuit; bool processIncoming ( const epicsTime & currentTime, epicsGuard < callbackMutex > & ); @@ -200,21 +213,34 @@ private: const char * pHostName () const; void blockUntilBytesArePendingInOS (); double receiveWatchdogDelay () const; + void unresponsiveCircuitNotify ( + epicsGuard < callbackMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void disconnectNotify (); // send protocol stubs - void echoRequest ( epicsGuard < cacMutex > & ); - void versionMessage ( epicsGuard < cacMutex > &, const cacChannel::priLev & priority ); - void disableFlowControlRequest (epicsGuard < cacMutex > & ); - void enableFlowControlRequest (epicsGuard < cacMutex > & ); - void hostNameSetRequest ( epicsGuard < cacMutex > & ); - void userNameSetRequest ( epicsGuard < cacMutex > & ); - void writeRequest ( epicsGuard < cacMutex > &, nciu &, unsigned type, unsigned nElem, const void *pValue ); - void writeNotifyRequest ( epicsGuard < cacMutex > &, nciu &, netWriteNotifyIO &, unsigned type, unsigned nElem, const void *pValue ); - void readNotifyRequest ( epicsGuard < cacMutex > &, nciu &, netReadNotifyIO &, unsigned type, unsigned nElem ); - void subscriptionRequest ( epicsGuard < cacMutex > &, nciu &, netSubscription & subscr ); - void subscriptionCancelRequest ( epicsGuard < cacMutex > &, nciu & chan, netSubscription & subscr ); + void echoRequest ( epicsGuard < epicsMutex > & ); + void versionMessage ( epicsGuard < epicsMutex > &, const cacChannel::priLev & priority ); + void disableFlowControlRequest (epicsGuard < epicsMutex > & ); + void enableFlowControlRequest (epicsGuard < epicsMutex > & ); + void hostNameSetRequest ( epicsGuard < epicsMutex > & ); + void userNameSetRequest ( epicsGuard < epicsMutex > & ); + void createChannelRequest ( nciu &, epicsGuard < epicsMutex > & ); + void writeRequest ( epicsGuard < epicsMutex > &, nciu &, + unsigned type, arrayElementCount nElem, const void *pValue ); + void writeNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, + netWriteNotifyIO &, unsigned type, + arrayElementCount nElem, const void *pValue ); + void readNotifyRequest ( epicsGuard < epicsMutex > &, nciu &, + netReadNotifyIO &, unsigned type, arrayElementCount nElem ); + void subscriptionRequest ( epicsGuard < epicsMutex > &, + nciu &, netSubscription & subscr ); + void subscriptionUpdateRequest ( epicsGuard < epicsMutex > &, + nciu & chan, netSubscription & subscr ); + void subscriptionCancelRequest ( epicsGuard < epicsMutex > &, + nciu & chan, netSubscription & subscr ); void flushIfRecvProcessRequested (); - bool flush (); // only to be called by the send thread + bool flush ( epicsGuard < epicsMutex > & ); // only to be called by the send thread friend void tcpRecvThread::run (); friend void tcpSendThread::run (); @@ -265,29 +291,24 @@ inline bool tcpiiu::connecting () const return ( this->state == iiucs_connecting ); } -inline void tcpiiu::beaconAnomalyNotify () +inline void tcpiiu::beaconAnomalyNotify ( epicsGuard < epicsMutex > & guard ) { - this->recvDog.beaconAnomalyNotify (); + //guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + this->recvDog.beaconAnomalyNotify ( guard ); } inline void tcpiiu::beaconArrivalNotify ( + epicsGuard < epicsMutex > & guard, const epicsTime & currentTime ) +{ + //guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + this->recvDog.beaconArrivalNotify ( guard, currentTime ); +} + +inline void tcpiiu::probeResponseNotify ( + epicsGuard < callbackMutex > & cbGuard, const epicsTime & currentTime ) { - this->recvDog.beaconArrivalNotify ( currentTime ); -} - -inline void tcpiiu::flushIfRecvProcessRequested () -{ - if ( this->recvProcessPostponedFlush ) { - this->flushRequest (); - this->recvProcessPostponedFlush = false; - } -} - -inline unsigned tcpiiu::channelCount ( epicsGuard < callbackMutex > & ) -{ - // protected by callback lock - return this->channelList.count (); + this->recvDog.probeResponseNotify ( cbGuard, currentTime ); } #endif // ifdef virtualCircuith diff --git a/src/db/Makefile b/src/db/Makefile index ee0e2b1c2..70cf81426 100644 --- a/src/db/Makefile +++ b/src/db/Makefile @@ -71,7 +71,7 @@ LIBSRCS += dbServiceIO.cpp LIBSRCS += dbChannelIO.cpp LIBSRCS += dbSubscriptionIO.cpp LIBSRCS += dbPutNotifyBlocker.cpp -LIBSRCS += dbServiceIOReadNotifyCache.cpp +LIBSRCS += dbContextReadNotifyCache.cpp LIBSRCS += templateInstances.cpp LIBRARY_IOC = dbIoc diff --git a/src/db/dbCAC.h b/src/db/dbCAC.h index 9f2785465..7dbb1e8b3 100644 --- a/src/db/dbCAC.h +++ b/src/db/dbCAC.h @@ -39,6 +39,7 @@ #include "resourceLib.h" #include "cacIO.h" #include "compilerDependencies.h" +#include "epicsMemory.h" #ifdef dbCACh_restore_epicsExportSharedSymbols # define epicsExportSharedSymbols @@ -56,7 +57,7 @@ extern "C" void putNotifyCompletion ( putNotify *ppn ); -class dbServiceIO; +class dbContext; class dbChannelIO; class dbPutNotifyBlocker; class dbSubscriptionIO; @@ -65,6 +66,7 @@ class dbBaseIO // X aCC 655 : public chronIntIdRes < dbBaseIO > { public: virtual dbSubscriptionIO * isSubscription () = 0; + virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0; virtual void show ( unsigned level ) const = 0; dbBaseIO (); dbBaseIO ( const dbBaseIO & ); @@ -74,109 +76,144 @@ public: extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr *paddr, int eventsRemaining, struct db_field_log *pfl ); -class dbSubscriptionIO : public tsDLNode , public dbBaseIO { +class dbSubscriptionIO : + public tsDLNode < dbSubscriptionIO >, + public dbBaseIO { public: - dbSubscriptionIO ( dbServiceIO &, dbChannelIO &, struct dbAddr &, cacStateNotify &, + dbSubscriptionIO ( + epicsGuard < epicsMutex > &, epicsMutex &, + dbContext &, dbChannelIO &, struct dbAddr &, cacStateNotify &, unsigned type, unsigned long count, unsigned mask, dbEventCtx ); - virtual ~dbSubscriptionIO (); - void unsubscribe (); - void channelDeleteException (); + void destructor ( epicsGuard < epicsMutex > & ); + void unsubscribe ( epicsGuard < epicsMutex > & ); + void channelDeleteException ( epicsGuard < epicsMutex > & ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void show ( unsigned level ) const; - void * operator new ( size_t size, tsFreeList < dbSubscriptionIO > & ); - epicsPlacementDeleteOperator (( void *, tsFreeList < dbSubscriptionIO > & )) + void * operator new ( size_t size, + tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & )) private: + epicsMutex & mutex; + unsigned long count; cacStateNotify & notify; dbChannelIO & chan; dbEventSubscription es; unsigned type; - unsigned long count; unsigned id; dbSubscriptionIO * isSubscription (); - friend void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr *paddr, - int eventsRemaining, struct db_field_log *pfl ); + friend void dbSubscriptionEventCallback ( + void * pPrivate, struct dbAddr * paddr, + int eventsRemaining, struct db_field_log * pfl ); dbSubscriptionIO ( const dbSubscriptionIO & ); dbSubscriptionIO & operator = ( const dbSubscriptionIO & ); + virtual ~dbSubscriptionIO (); void * operator new ( size_t size ); void operator delete ( void * ); }; -class dbServiceIO; +class dbContext; -class dbServicePrivateListOfIO { +class dbContextPrivateListOfIO { public: - dbServicePrivateListOfIO (); + dbContextPrivateListOfIO (); private: tsDLList < dbSubscriptionIO > eventq; dbPutNotifyBlocker * pBlocker; - friend class dbServiceIO; - dbServicePrivateListOfIO ( const dbServicePrivateListOfIO & ); - dbServicePrivateListOfIO & operator = ( const dbServicePrivateListOfIO & ); + friend class dbContext; + dbContextPrivateListOfIO ( const dbContextPrivateListOfIO & ); + dbContextPrivateListOfIO & operator = ( const dbContextPrivateListOfIO & ); }; +#pragma message ( "name change reflecting dbContext indicated?" ) // allow only one thread at a time to use the cache, but do not hold // lock when calling the callback -class dbServiceIOReadNotifyCache { +class dbContextReadNotifyCache { public: - dbServiceIOReadNotifyCache (); - ~dbServiceIOReadNotifyCache (); - void callReadNotify ( struct dbAddr &addr, unsigned type, unsigned long count, - cacReadNotify ¬ify ); - void show ( unsigned level ) const; + dbContextReadNotifyCache ( epicsMutex & ); + ~dbContextReadNotifyCache (); + void callReadNotify ( epicsGuard < epicsMutex > &, + struct dbAddr & addr, unsigned type, unsigned long count, + cacReadNotify & notify ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; private: - epicsMutex mutex; unsigned long readNotifyCacheSize; - char *pReadNotifyCache; - dbServiceIOReadNotifyCache ( const dbServiceIOReadNotifyCache & ); - dbServiceIOReadNotifyCache & operator = ( const dbServiceIOReadNotifyCache & ); + epicsMutex & mutex; + char * pReadNotifyCache; + dbContextReadNotifyCache ( const dbContextReadNotifyCache & ); + dbContextReadNotifyCache & operator = ( const dbContextReadNotifyCache & ); }; -class dbServiceIO : public cacService { +class dbContext : public cacContext { public: - dbServiceIO (); - virtual ~dbServiceIO (); - cacChannel * createChannel ( const char *pName, - cacChannelNotify &, cacChannel::priLev ); - void destroyChannel ( dbChannelIO & ); - void callReadNotify ( struct dbAddr &addr, unsigned type, unsigned long count, - cacReadNotify ¬ify ); + dbContext ( epicsMutex & mutex, cacContextNotify & notify ); + virtual ~dbContext (); + void destroyChannel ( epicsGuard < epicsMutex > &, dbChannelIO & ); + void callReadNotify ( epicsGuard < epicsMutex > &, + struct dbAddr & addr, unsigned type, unsigned long count, + cacReadNotify & notify ); void callStateNotify ( struct dbAddr &addr, unsigned type, unsigned long count, const struct db_field_log * pfl, cacStateNotify & notify ); void subscribe ( + epicsGuard < epicsMutex > &, struct dbAddr & addr, dbChannelIO & chan, unsigned type, unsigned long count, unsigned mask, cacStateNotify & notify, cacChannel::ioid * pId ); - void initiatePutNotify ( dbChannelIO &, struct dbAddr &, unsigned type, - unsigned long count, const void *pValue, cacWriteNotify ¬ify, - cacChannel::ioid * pId ); + void initiatePutNotify ( + epicsGuard < epicsMutex > &, dbChannelIO &, struct dbAddr &, + unsigned type, unsigned long count, const void * pValue, + cacWriteNotify & notify, cacChannel::ioid * pId ); void show ( unsigned level ) const; - void showAllIO ( const dbChannelIO &chan, unsigned level ) const; - void destroyAllIO ( dbChannelIO & chan ); - void ioCancel ( dbChannelIO & chan, const cacChannel::ioid &id ); - void ioShow ( const cacChannel::ioid &id, unsigned level ) const; + void showAllIO ( const dbChannelIO & chan, unsigned level ) const; + void destroyAllIO ( + epicsGuard < epicsMutex > &, dbChannelIO & chan ); + void ioCancel ( epicsGuard < epicsMutex > &, + dbChannelIO & chan, const cacChannel::ioid &id ); + void ioShow ( epicsGuard < epicsMutex > &, + const cacChannel::ioid & id, unsigned level ) const; private: - mutable epicsMutex mutex; - tsFreeList < dbPutNotifyBlocker > dbPutNotifyBlockerFreeList; - tsFreeList < dbSubscriptionIO > dbSubscriptionIOFreeList; - tsFreeList < dbChannelIO > dbChannelIOFreeList; + tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > dbPutNotifyBlockerFreeList; + tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > dbSubscriptionIOFreeList; + tsFreeList < dbChannelIO, 256, epicsMutexNOOP > dbChannelIOFreeList; chronIntIdResTable < dbBaseIO > ioTable; - dbServiceIOReadNotifyCache readNotifyCache; + dbContextReadNotifyCache readNotifyCache; dbEventCtx ctx; unsigned long stateNotifyCacheSize; - char *pStateNotifyCache; - dbServiceIO ( const dbServiceIO & ); - dbServiceIO & operator = ( const dbServiceIO & ); + mutable epicsMutex & mutex; + cacContextNotify & notify; + epics_auto_ptr < cacContext > pNetContext; + char * pStateNotifyCache; + + cacChannel & createChannel ( + epicsGuard < epicsMutex > &, + const char * pChannelName, cacChannelNotify &, + cacChannel::priLev ); + void flush ( + epicsGuard < epicsMutex > & ); + unsigned circuitCount ( + epicsGuard < epicsMutex > & ) const; + void selfTest ( + epicsGuard < epicsMutex > & ) const; + unsigned beaconAnomaliesSinceProgramStart ( + epicsGuard < epicsMutex > & ) const; + void show ( + epicsGuard < epicsMutex > &, unsigned level ) const; + + dbContext ( const dbContext & ); + dbContext & operator = ( const dbContext & ); }; -inline dbServicePrivateListOfIO::dbServicePrivateListOfIO () : +inline dbContextPrivateListOfIO::dbContextPrivateListOfIO () : pBlocker ( 0 ) { } -inline void dbServiceIO::callReadNotify ( struct dbAddr &addr, - unsigned type, unsigned long count, - cacReadNotify ¬ify ) +inline void dbContext::callReadNotify ( + epicsGuard < epicsMutex > & guard, struct dbAddr &addr, + unsigned type, unsigned long count, cacReadNotify & notify ) { - this->readNotifyCache.callReadNotify ( addr, type, count, notify ); + guard.assertIdenticalMutex ( this-> mutex ); + this->readNotifyCache.callReadNotify ( guard, addr, type, count, notify ); } #endif // dbCACh diff --git a/src/db/dbChannelIO.cpp b/src/db/dbChannelIO.cpp index 2d522cb52..e8f1ee420 100644 --- a/src/db/dbChannelIO.cpp +++ b/src/db/dbChannelIO.cpp @@ -40,46 +40,59 @@ #include "dbChannelIO.h" #include "dbPutNotifyBlocker.h" -dbChannelIO::dbChannelIO ( cacChannelNotify & notify, - const dbAddr & addrIn, dbServiceIO & serviceIO ) : - cacChannel ( notify ), serviceIO ( serviceIO ), +dbChannelIO::dbChannelIO ( + epicsMutex & mutexIn, cacChannelNotify & notify, + const dbAddr & addrIn, dbContext & serviceIO ) : + mutex ( mutexIn ), cacChannel ( notify ), serviceIO ( serviceIO ), addr ( addrIn ) { } -void dbChannelIO::initiateConnect () +void dbChannelIO::initiateConnect ( epicsGuard < epicsMutex > & guard ) { - this->notify ().connectNotify (); + guard.assertIdenticalMutex ( this->mutex ); + this->notify().connectNotify ( guard ); } -dbChannelIO::~dbChannelIO () +dbChannelIO::~dbChannelIO () { - this->serviceIO.destroyAllIO ( *this ); } -void dbChannelIO::destroy () +void dbChannelIO::destructor ( epicsGuard < epicsMutex > & guard ) { - this->serviceIO.destroyChannel ( *this ); + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.destroyAllIO ( guard, *this ); + this->~dbChannelIO (); +} + +void dbChannelIO::destroy ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.destroyChannel ( guard, *this ); // dont access this pointer after above call because // object nolonger exists } -cacChannel::ioStatus dbChannelIO::read ( unsigned type, - unsigned long count, cacReadNotify ¬ify, ioid * ) +cacChannel::ioStatus dbChannelIO::read ( + epicsGuard < epicsMutex > & guard, unsigned type, + unsigned long count, cacReadNotify & notify, ioid * ) { - this->serviceIO.callReadNotify ( this->addr, + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.callReadNotify ( guard, this->addr, type, count, notify ); return iosSynch; } -void dbChannelIO::write ( unsigned type, unsigned long count, const void *pValue ) +void dbChannelIO::write ( + epicsGuard < epicsMutex > & guard, unsigned type, + unsigned long count, const void *pValue ) { - int status; + epicsGuardRelease < epicsMutex > unguard ( guard ); if ( count > LONG_MAX ) { throw outOfBounds(); } - status = db_put_field ( &this->addr, type, pValue, + int status = db_put_field ( &this->addr, type, pValue, static_cast (count) ); if ( status ) { throw std::logic_error ( @@ -87,33 +100,46 @@ void dbChannelIO::write ( unsigned type, unsigned long count, const void *pValue } } -cacChannel::ioStatus dbChannelIO::write ( unsigned type, unsigned long count, - const void *pValue, cacWriteNotify ¬ify, ioid *pId ) +cacChannel::ioStatus dbChannelIO::write ( + epicsGuard < epicsMutex > & guard, unsigned type, + unsigned long count, const void * pValue, + cacWriteNotify & notify, ioid * pId ) { + guard.assertIdenticalMutex ( this->mutex ); + if ( count > LONG_MAX ) { throw outOfBounds(); } - this->serviceIO.initiatePutNotify ( *this, this->addr, type, count, pValue, notify, pId ); + this->serviceIO.initiatePutNotify ( + guard, *this, this->addr, + type, count, pValue, notify, pId ); return iosAsynch; } -void dbChannelIO::subscribe ( unsigned type, unsigned long count, +void dbChannelIO::subscribe ( + epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, unsigned mask, cacStateNotify & notify, ioid * pId ) { - this->serviceIO.subscribe ( this->addr, *this, + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.subscribe ( + guard, this->addr, *this, type, count, mask, notify, pId ); } -void dbChannelIO::ioCancel ( const ioid & id ) +void dbChannelIO::ioCancel ( + epicsGuard < epicsMutex > & guard, const ioid & id ) { - this->serviceIO.ioCancel ( *this, id ); + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.ioCancel ( guard, *this, id ); } -void dbChannelIO::ioShow ( const ioid &id, unsigned level ) const +void dbChannelIO::ioShow ( + const ioid & id, unsigned level ) const { - this->serviceIO.ioShow ( id, level ); + epicsGuard < epicsMutex > guard ( this->mutex ); + this->serviceIO.ioShow ( guard, id, level ); } void dbChannelIO::show ( unsigned level ) const @@ -133,7 +159,7 @@ void dbChannelIO::show ( unsigned level ) const } void * dbChannelIO::operator new ( size_t size, - tsFreeList < dbChannelIO > & freeList ) + tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } @@ -147,7 +173,7 @@ void * dbChannelIO::operator new ( size_t ) // X aCC 361 #ifdef CXX_PLACEMENT_DELETE void dbChannelIO::operator delete ( void *pCadaver, - tsFreeList < dbChannelIO > & freeList ) + tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } diff --git a/src/db/dbChannelIO.h b/src/db/dbChannelIO.h index 0abf0451f..d8acf9135 100644 --- a/src/db/dbChannelIO.h +++ b/src/db/dbChannelIO.h @@ -41,40 +41,52 @@ # define epicsExportSharedSymbols #endif -class dbChannelIO : public cacChannel, public dbServicePrivateListOfIO { +class dbChannelIO : public cacChannel, public dbContextPrivateListOfIO { public: - dbChannelIO ( cacChannelNotify ¬ify, - const dbAddr &addr, dbServiceIO &serviceIO ); - ~dbChannelIO (); - void destroy (); - void callReadNotify ( unsigned type, unsigned long count, - cacReadNotify ¬ify ); + dbChannelIO ( epicsMutex &, cacChannelNotify &, + const dbAddr &, dbContext & ); + void destructor ( epicsGuard < epicsMutex > & ); + void destroy ( epicsGuard < epicsMutex > & ); + void callReadNotify ( epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, + cacReadNotify & notify ); void callStateNotify ( unsigned type, unsigned long count, - const struct db_field_log *pfl, cacStateNotify ¬ify ); + const struct db_field_log * pfl, cacStateNotify & notify ); void show ( unsigned level ) const; - const char *pName () const; - void * operator new ( size_t size, tsFreeList < dbChannelIO > & ); - epicsPlacementDeleteOperator (( void *, tsFreeList < dbChannelIO > & )) + const char * pName () const; + void * operator new ( size_t size, + tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & )) private: - dbServiceIO & serviceIO; + epicsMutex & mutex; + dbContext & serviceIO; dbAddr addr; - void initiateConnect (); - ioStatus read ( unsigned type, unsigned long count, + void initiateConnect ( + epicsGuard < epicsMutex > & ); + ioStatus read ( epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, cacReadNotify &, ioid * ); - void write ( unsigned type, unsigned long count, + void write ( epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, const void *pvalue ); - ioStatus write ( unsigned type, unsigned long count, + ioStatus write ( epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, const void *pvalue, cacWriteNotify &, ioid * ); - void subscribe ( unsigned type, unsigned long count, + void subscribe ( epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, unsigned mask, cacStateNotify ¬ify, ioid * ); - void ioCancel ( const ioid & ); - void ioShow ( const ioid &, unsigned level ) const; + void ioCancel ( epicsGuard < epicsMutex > &, + const ioid & ); + void ioShow ( + const ioid &, unsigned level ) const; short nativeType () const; unsigned long nativeElementCount () const; dbChannelIO ( const dbChannelIO & ); dbChannelIO & operator = ( const dbChannelIO & ); void * operator new ( size_t size ); void operator delete ( void * ); + ~dbChannelIO (); }; @@ -96,10 +108,12 @@ inline short dbChannelIO::nativeType () const return this->addr.dbr_field_type; } -inline void dbChannelIO::callReadNotify ( unsigned type, unsigned long count, - cacReadNotify ¬ify ) +inline void dbChannelIO::callReadNotify ( + epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, + cacReadNotify & notify ) { - this->serviceIO.callReadNotify ( this->addr, type, count, notify ); + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.callReadNotify ( guard, this->addr, type, count, notify ); } inline void dbChannelIO::callStateNotify ( unsigned type, unsigned long count, diff --git a/src/db/dbServiceIOReadNotifyCache.cpp b/src/db/dbContextReadNotifyCache.cpp similarity index 64% rename from src/db/dbServiceIOReadNotifyCache.cpp rename to src/db/dbContextReadNotifyCache.cpp index 84f10a6d2..baade3bd1 100644 --- a/src/db/dbServiceIOReadNotifyCache.cpp +++ b/src/db/dbContextReadNotifyCache.cpp @@ -24,18 +24,21 @@ #include "db_access_routines.h" #include "dbCAC.h" -dbServiceIOReadNotifyCache::dbServiceIOReadNotifyCache () : - readNotifyCacheSize ( 0 ), pReadNotifyCache ( 0 ) +dbContextReadNotifyCache::dbContextReadNotifyCache ( epicsMutex & mutexIn ) : + readNotifyCacheSize ( 0 ), mutex ( mutexIn ), pReadNotifyCache ( 0 ) { } -dbServiceIOReadNotifyCache::~dbServiceIOReadNotifyCache () +dbContextReadNotifyCache::~dbContextReadNotifyCache () { delete this->pReadNotifyCache; } -void dbServiceIOReadNotifyCache::show ( unsigned level ) const +void dbContextReadNotifyCache::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const { + guard.assertIdenticalMutex ( this->mutex ); + if ( level > 0 ) { printf ( "\tget call back cache location %p, and its size %lu\n", static_cast ( this->pReadNotifyCache ), @@ -44,23 +47,24 @@ void dbServiceIOReadNotifyCache::show ( unsigned level ) const } // extra effort taken here to not hold the lock when caslling the callback -void dbServiceIOReadNotifyCache::callReadNotify ( struct dbAddr &addr, - unsigned type, unsigned long count, cacReadNotify ¬ify ) +void dbContextReadNotifyCache::callReadNotify ( + epicsGuard < epicsMutex > & guard, struct dbAddr & addr, + unsigned type, unsigned long count, cacReadNotify & notify ) { - epicsGuard < epicsMutex > locker ( this->mutex ); + guard.assertIdenticalMutex ( this->mutex ); unsigned long size = dbr_size_n ( type, count ); if ( type > INT_MAX ) { - notify.exception ( ECA_BADTYPE, + notify.exception ( guard, ECA_BADTYPE, "type code out of range (high side)", type, count ); return; } - if ( count > static_cast(INT_MAX) || - count > static_cast(addr.no_elements) ) { - notify.exception ( ECA_BADCOUNT, + if ( count > static_cast < unsigned > ( INT_MAX ) || + count > static_cast < unsigned > ( addr.no_elements ) ) { + notify.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; @@ -69,7 +73,7 @@ void dbServiceIOReadNotifyCache::callReadNotify ( struct dbAddr &addr, if ( this->readNotifyCacheSize < size) { char * pTmp = new char [size]; if ( ! pTmp ) { - notify.exception ( ECA_ALLOCMEM, + notify.exception ( guard, ECA_ALLOCMEM, "unable to allocate callback cache", type, count ); return; @@ -78,14 +82,18 @@ void dbServiceIOReadNotifyCache::callReadNotify ( struct dbAddr &addr, this->pReadNotifyCache = pTmp; this->readNotifyCacheSize = size; } - int status = db_get_field ( &addr, static_cast ( type ), + int status; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + status = db_get_field ( &addr, static_cast ( type ), this->pReadNotifyCache, static_cast ( count ), 0 ); + } if ( status ) { - notify.exception ( ECA_GETFAIL, + notify.exception ( guard, ECA_GETFAIL, "db_get_field() completed unsuccessfuly", type, count); } else { - notify.completion ( type, count, this->pReadNotifyCache ); + notify.completion ( guard, type, count, this->pReadNotifyCache ); } } diff --git a/src/db/dbPutNotifyBlocker.cpp b/src/db/dbPutNotifyBlocker.cpp index 2dad719c0..c6fe4101d 100644 --- a/src/db/dbPutNotifyBlocker.cpp +++ b/src/db/dbPutNotifyBlocker.cpp @@ -45,8 +45,9 @@ #include "dbChannelIO.h" #include "dbPutNotifyBlocker.h" -dbPutNotifyBlocker::dbPutNotifyBlocker () : - pNotify ( 0 ), maxValueSize ( sizeof ( this->dbrScalarValue ) ) +dbPutNotifyBlocker::dbPutNotifyBlocker ( epicsMutex & mutexIn ) : + mutex ( mutexIn ), pNotify ( 0 ), + maxValueSize ( sizeof ( this->dbrScalarValue ) ) { memset ( & this->pn, '\0', sizeof ( this->pn ) ); memset ( & this->dbrScalarValue, '\0', sizeof ( this->dbrScalarValue ) ); @@ -55,24 +56,35 @@ dbPutNotifyBlocker::dbPutNotifyBlocker () : dbPutNotifyBlocker::~dbPutNotifyBlocker () { - this->cancel (); +} + +void dbPutNotifyBlocker::destructor ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->cancel ( guard ); if ( this->maxValueSize > sizeof ( this->dbrScalarValue ) ) { char * pBuf = static_cast < char * > ( this->pn.pbuffer ); delete [] pBuf; } + this->~dbPutNotifyBlocker (); } -void dbPutNotifyBlocker::cancel () +void dbPutNotifyBlocker::cancel ( + epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->mutex ); if ( this->pn.paddr ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); dbNotifyCancel ( &this->pn ); } this->pNotify = 0; this->block.signal (); } -void dbPutNotifyBlocker::expandValueBuf ( unsigned long newSize ) +void dbPutNotifyBlocker::expandValueBuf ( + epicsGuard < epicsMutex > & guard, unsigned long newSize ) { + guard.assertIdenticalMutex ( this->mutex ); if ( this->maxValueSize < newSize ) { if ( this->maxValueSize > sizeof ( this->dbrScalarValue ) ) { char * pBuf = static_cast < char * > ( this->pn.pbuffer ); @@ -87,31 +99,34 @@ void dbPutNotifyBlocker::expandValueBuf ( unsigned long newSize ) extern "C" void putNotifyCompletion ( putNotify *ppn ) { - dbPutNotifyBlocker *pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt ); - if ( pBlocker->pNotify ) { - if ( pBlocker->pn.status != putNotifyOK) { - pBlocker->pNotify->exception ( - ECA_PUTFAIL, "put notify unsuccessful", - static_cast (pBlocker->pn.dbrType), - static_cast (pBlocker->pn.nRequest) ); + dbPutNotifyBlocker * pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt ); + { + epicsGuard < epicsMutex > guard ( pBlocker->mutex ); + if ( pBlocker->pNotify ) { + if ( pBlocker->pn.status != putNotifyOK) { + pBlocker->pNotify->exception ( + guard, ECA_PUTFAIL, "put notify unsuccessful", + static_cast (pBlocker->pn.dbrType), + static_cast (pBlocker->pn.nRequest) ); + } + else { + pBlocker->pNotify->completion ( guard ); + } } else { - pBlocker->pNotify->completion (); + errlogPrintf ( "put notify completion with nill pNotify?\n" ); } + pBlocker->pNotify = 0; } - else { - errlogPrintf ( "put notify completion with nill pNotify?\n" ); - } - // no need to lock here because only one put notify at a time is allowed to run - pBlocker->pNotify = 0; pBlocker->block.signal (); } -void dbPutNotifyBlocker::initiatePutNotify ( epicsGuard < epicsMutex > & locker, cacWriteNotify & notify, - struct dbAddr & addr, unsigned type, unsigned long count, const void * pValue ) +void dbPutNotifyBlocker::initiatePutNotify ( + epicsGuard < epicsMutex > & guard, cacWriteNotify & notify, + struct dbAddr & addr, unsigned type, unsigned long count, + const void * pValue ) { - int status; - + guard. assertIdenticalMutex ( this->mutex ); epicsTime begin; bool beginTimeInit = false; while ( true ) { @@ -129,7 +144,7 @@ void dbPutNotifyBlocker::initiatePutNotify ( epicsGuard < epicsMutex > & locker, beginTimeInit = true; } { - epicsGuardRelease < epicsMutex > autoRelease ( locker ); + epicsGuardRelease < epicsMutex > autoRelease ( guard ); this->block.wait ( 1.0 ); } } @@ -142,7 +157,7 @@ void dbPutNotifyBlocker::initiatePutNotify ( epicsGuard < epicsMutex > & locker, throw cacChannel::badType(); } - status = dbPutNotifyMapType ( + int status = dbPutNotifyMapType ( &this->pn, static_cast ( type ) ); if ( status ) { this->pNotify = 0; @@ -155,13 +170,23 @@ void dbPutNotifyBlocker::initiatePutNotify ( epicsGuard < epicsMutex > & locker, this->pn.usrPvt = this; unsigned long size = dbr_size_n ( type, count ); - this->expandValueBuf ( size ); + this->expandValueBuf ( guard, size ); memcpy ( this->pn.pbuffer, pValue, size ); - ::dbPutNotify ( &this->pn ); + { + epicsGuardRelease < epicsMutex > autoRelease ( guard ); + ::dbPutNotify ( &this->pn ); + } } void dbPutNotifyBlocker::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->show ( guard, level ); +} + +void dbPutNotifyBlocker::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const { printf ( "put notify blocker at %p\n", static_cast ( this ) ); @@ -176,7 +201,7 @@ dbSubscriptionIO * dbPutNotifyBlocker::isSubscription () } void * dbPutNotifyBlocker::operator new ( size_t size, - tsFreeList < dbPutNotifyBlocker > & freeList ) + tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } @@ -190,7 +215,7 @@ void * dbPutNotifyBlocker::operator new ( size_t ) // X aCC 361 #ifdef CXX_PLACEMENT_DELETE void dbPutNotifyBlocker::operator delete ( void *pCadaver, - tsFreeList < dbPutNotifyBlocker > & freeList ) + tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } diff --git a/src/db/dbPutNotifyBlocker.h b/src/db/dbPutNotifyBlocker.h index 6df57b273..34376e375 100644 --- a/src/db/dbPutNotifyBlocker.h +++ b/src/db/dbPutNotifyBlocker.h @@ -41,17 +41,18 @@ class dbPutNotifyBlocker : public dbBaseIO { public: - dbPutNotifyBlocker (); - virtual ~dbPutNotifyBlocker (); - void initiatePutNotify ( epicsGuard < epicsMutex > & locker, - cacWriteNotify & notify, struct dbAddr & addr, + dbPutNotifyBlocker ( epicsMutex & ); + void destructor ( epicsGuard < epicsMutex > & ); + void initiatePutNotify ( epicsGuard < epicsMutex > &, + cacWriteNotify &, struct dbAddr &, unsigned type, unsigned long count, const void * pValue ); - void cancel (); + void cancel ( epicsGuard < epicsMutex > & ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void show ( unsigned level ) const; void * operator new ( size_t size, - tsFreeList < dbPutNotifyBlocker > & ); + tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & ); epicsPlacementDeleteOperator (( void *, - tsFreeList < dbPutNotifyBlocker > & )) + tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & )) private: putNotify pn; // @@ -71,13 +72,16 @@ private: dbr_double_t doubleval; } dbrScalarValue; epicsEvent block; + epicsMutex & mutex; cacWriteNotify * pNotify; unsigned long maxValueSize; dbSubscriptionIO * isSubscription (); - void expandValueBuf ( unsigned long newSize ); - friend void putNotifyCompletion ( putNotify *ppn ); + void expandValueBuf ( + epicsGuard < epicsMutex > &, unsigned long newSize ); + friend void putNotifyCompletion ( putNotify * ppn ); dbPutNotifyBlocker ( const dbPutNotifyBlocker & ); dbPutNotifyBlocker & operator = ( const dbPutNotifyBlocker & ); + virtual ~dbPutNotifyBlocker (); void * operator new ( size_t size ); void operator delete ( void * ); }; diff --git a/src/db/dbServiceIO.cpp b/src/db/dbServiceIO.cpp index f56602785..acf9bf08d 100644 --- a/src/db/dbServiceIO.cpp +++ b/src/db/dbServiceIO.cpp @@ -23,6 +23,8 @@ * 505 665 1831 */ +#pragma message ( "file name needs to change" ) + #include #include "epicsMutex.h" @@ -41,37 +43,35 @@ #include "dbChannelIO.h" #include "dbPutNotifyBlocker.h" -class dbServiceIOLoadTimeInit { +class dbService : public cacService { public: - dbServiceIOLoadTimeInit (); -private: - dbServiceIO dbio; - dbServiceIOLoadTimeInit ( const dbServiceIOLoadTimeInit & ); - dbServiceIOLoadTimeInit & operator = ( const dbServiceIOLoadTimeInit & ); + cacContext & contextCreate ( + epicsMutex &, cacContextNotify & ); }; -// The following is just to force lti to be constructed -extern "C" void dbServiceIOInit() +static dbService dbs; + +cacContext & dbService::contextCreate ( + epicsMutex & mutex, cacContextNotify & notify ) { - static dbServiceIOLoadTimeInit lti; + return * new dbContext ( mutex, notify ); +} + +extern "C" void dbServiceIOInit () +{ + caInstallDefaultService ( dbs ); } dbBaseIO::dbBaseIO () {} -dbServiceIOLoadTimeInit::dbServiceIOLoadTimeInit () -{ - epicsSingleton < cacServiceList > :: reference - ref ( globalServiceListCAC.getReference () ); - ref->registerService ( this->dbio ); -} - -dbServiceIO::dbServiceIO () : - ctx ( 0 ), stateNotifyCacheSize ( 0 ), - pStateNotifyCache ( 0 ) +dbContext::dbContext ( epicsMutex & mutexIn, cacContextNotify & notifyIn ) : + readNotifyCache ( mutexIn ), ctx ( 0 ), + stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), + notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ) { } -dbServiceIO::~dbServiceIO () +dbContext::~dbContext () { delete [] this->pStateNotifyCache; if ( this->ctx ) { @@ -79,35 +79,49 @@ dbServiceIO::~dbServiceIO () } } -cacChannel *dbServiceIO::createChannel ( // X aCC 361 - const char * pName, cacChannelNotify & notify, - cacChannel::priLev ) +cacChannel & dbContext::createChannel ( // X aCC 361 + epicsGuard < epicsMutex > & guard, const char * pName, + cacChannelNotify & notifyIn, cacChannel::priLev priority ) { - struct dbAddr addr; + guard.assertIdenticalMutex ( this->mutex ); - int status = db_name_to_addr ( pName, & addr ); - if ( status ) { - return 0; + struct dbAddr addr; + int status; + { + // dont know if the database might call a put callback + // while holding its lock ... + epicsGuardRelease < epicsMutex > unguard ( guard ); + status = db_name_to_addr ( pName, & addr ); } - else if ( ! ca_preemtive_callback_is_enabled () ) { - errlogPrintf ( - "dbServiceIO: preemptive callback required for direct in\n" - "memory interfacing of CA channels to the DB.\n" ); - return 0; + if ( status ) { + if ( ! this->pNetContext.get() ) { + this->pNetContext.reset ( + & this->notify.createNetworkContext ( this->mutex ) ); + } + return this->pNetContext->createChannel ( + guard, pName, notifyIn, priority ); + } + else if ( ca_preemtive_callback_is_enabled () ) { + return * new ( this->dbChannelIOFreeList ) + dbChannelIO ( this->mutex, notifyIn, addr, *this ); } else { - return new ( this->dbChannelIOFreeList ) - dbChannelIO ( notify, addr, *this ); + errlogPrintf ( + "dbContext: preemptive callback required for direct in\n" + "memory interfacing of CA channels to the DB.\n" ); + throw cacChannel::unsupportedByService (); } } -void dbServiceIO::destroyChannel ( dbChannelIO & chan ) +void dbContext::destroyChannel ( + epicsGuard < epicsMutex > & guard, dbChannelIO & chan ) { - chan.~dbChannelIO (); + guard.assertIdenticalMutex ( this->mutex ); + chan.destructor ( guard ); this->dbChannelIOFreeList.release ( & chan ); } -void dbServiceIO::callStateNotify ( struct dbAddr & addr, +void dbContext::callStateNotify ( struct dbAddr & addr, unsigned type, unsigned long count, const struct db_field_log * pfl, cacStateNotify & notify ) @@ -115,14 +129,16 @@ void dbServiceIO::callStateNotify ( struct dbAddr & addr, unsigned long size = dbr_size_n ( type, count ); if ( type > INT_MAX ) { - notify.exception ( ECA_BADTYPE, + epicsGuard < epicsMutex > guard ( this->mutex ); + notify.exception ( guard, ECA_BADTYPE, "type code out of range (high side)", type, count ); return; } if ( count > INT_MAX ) { - notify.exception ( ECA_BADCOUNT, + epicsGuard < epicsMutex > guard ( this->mutex ); + notify.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; @@ -140,11 +156,13 @@ void dbServiceIO::callStateNotify ( struct dbAddr & addr, int status = db_get_field ( &addr, static_cast ( type ), this->pStateNotifyCache, static_cast ( count ), pvfl ); if ( status ) { - notify.exception ( ECA_GETFAIL, + epicsGuard < epicsMutex > guard ( this->mutex ); + notify.exception ( guard, ECA_GETFAIL, "db_get_field() completed unsuccessfuly", type, count ); } else { - notify.current ( type, count, this->pStateNotifyCache ); + epicsGuard < epicsMutex > guard ( this->mutex ); + notify.current ( guard, type, count, this->pStateNotifyCache ); } } @@ -154,11 +172,14 @@ extern "C" void cacAttachClientCtx ( void * pPrivate ) assert ( status == ECA_NORMAL ); } -void dbServiceIO::subscribe ( +void dbContext::subscribe ( + epicsGuard < epicsMutex > & guard, struct dbAddr & addr, dbChannelIO & chan, unsigned type, unsigned long count, unsigned mask, cacStateNotify & notify, cacChannel::ioid * pId ) { + guard.assertIdenticalMutex ( this->mutex ); + /* * the database uses type "int" to store these parameters */ @@ -169,14 +190,15 @@ void dbServiceIO::subscribe ( throw cacChannel::outOfBounds(); } - { - epicsGuard < epicsMutex > locker ( this->mutex ); - if ( ! this->ctx ) { - this->ctx = db_init_events (); - if ( ! this->ctx ) { + if ( ! this->ctx ) { + dbEventCtx tmpctx = 0; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + tmpctx = db_init_events (); + if ( ! tmpctx ) { throw std::bad_alloc (); } - + unsigned selfPriority = epicsThreadGetPrioritySelf (); unsigned above; epicsThreadBooleanStatus tbs = @@ -184,136 +206,178 @@ void dbServiceIO::subscribe ( if ( tbs != epicsThreadBooleanStatusSuccess ) { above = selfPriority; } - int status = db_start_events ( this->ctx, "CAC-event", + int status = db_start_events ( tmpctx, "CAC-event", cacAttachClientCtx, ca_current_context (), above ); if ( status ) { - db_close_events ( this->ctx ); - this->ctx = 0; + db_close_events ( tmpctx ); throw std::bad_alloc (); } } + if ( this->ctx ) { + // another thread tried to simultaneously setup + // the event system + db_close_events ( tmpctx ); + } + else { + this->ctx = tmpctx; + } } dbSubscriptionIO & subscr = * new ( this->dbSubscriptionIOFreeList ) - dbSubscriptionIO ( *this, chan, + dbSubscriptionIO ( guard, this->mutex, *this, chan, addr, notify, type, count, mask, this->ctx ); - { - epicsGuard < epicsMutex > locker ( this->mutex ); - chan.dbServicePrivateListOfIO::eventq.add ( subscr ); - this->ioTable.add ( subscr ); - } + chan.dbContextPrivateListOfIO::eventq.add ( subscr ); + this->ioTable.add ( subscr ); if ( pId ) { *pId = subscr.getId (); } } -void dbServiceIO::initiatePutNotify ( +void dbContext::initiatePutNotify ( + epicsGuard < epicsMutex > & guard, dbChannelIO & chan, struct dbAddr & addr, unsigned type, unsigned long count, const void * pValue, cacWriteNotify & notify, cacChannel::ioid * pId ) { - epicsGuard < epicsMutex > locker ( this->mutex ); - if ( ! chan.dbServicePrivateListOfIO::pBlocker ) { - chan.dbServicePrivateListOfIO::pBlocker = + guard.assertIdenticalMutex ( this->mutex ); + if ( ! chan.dbContextPrivateListOfIO::pBlocker ) { + chan.dbContextPrivateListOfIO::pBlocker = new ( this->dbPutNotifyBlockerFreeList ) - dbPutNotifyBlocker (); - this->ioTable.add ( *chan.dbServicePrivateListOfIO::pBlocker ); + dbPutNotifyBlocker ( this->mutex ); + this->ioTable.add ( *chan.dbContextPrivateListOfIO::pBlocker ); } - chan.dbServicePrivateListOfIO::pBlocker->initiatePutNotify ( - locker, notify, addr, type, count, pValue ); + chan.dbContextPrivateListOfIO::pBlocker->initiatePutNotify ( + guard, notify, addr, type, count, pValue ); if ( pId ) { - *pId = chan.dbServicePrivateListOfIO::pBlocker->getId (); + *pId = chan.dbContextPrivateListOfIO::pBlocker->getId (); } } -void dbServiceIO::destroyAllIO ( dbChannelIO & chan ) +void dbContext::destroyAllIO ( + epicsGuard < epicsMutex > & guard, dbChannelIO & chan ) { - dbSubscriptionIO *pIO; + guard.assertIdenticalMutex ( this->mutex ); + dbSubscriptionIO * pIO; tsDLList < dbSubscriptionIO > tmp; - { - epicsGuard < epicsMutex > locker ( this->mutex ); - while ( ( pIO = chan.dbServicePrivateListOfIO::eventq.get() ) ) { - this->ioTable.remove ( *pIO ); - tmp.add ( *pIO ); - } - if ( chan.dbServicePrivateListOfIO::pBlocker ) { - this->ioTable.remove ( *chan.dbServicePrivateListOfIO::pBlocker ); - } + + while ( ( pIO = chan.dbContextPrivateListOfIO::eventq.get() ) ) { + this->ioTable.remove ( *pIO ); + tmp.add ( *pIO ); } + if ( chan.dbContextPrivateListOfIO::pBlocker ) { + this->ioTable.remove ( *chan.dbContextPrivateListOfIO::pBlocker ); + } + while ( ( pIO = tmp.get() ) ) { // This prevents a db event callback from coming // through after the notify IO is deleted - pIO->unsubscribe (); + pIO->unsubscribe ( guard ); // If they call ioCancel() here it will be ignored // because the IO has been unregistered above. - pIO->channelDeleteException (); - pIO->~dbSubscriptionIO (); + pIO->channelDeleteException ( guard ); + pIO->destructor ( guard ); this->dbSubscriptionIOFreeList.release ( pIO ); } - if ( chan.dbServicePrivateListOfIO::pBlocker ) { - chan.dbServicePrivateListOfIO::pBlocker->~dbPutNotifyBlocker (); - this->dbPutNotifyBlockerFreeList.release ( chan.dbServicePrivateListOfIO::pBlocker ); - chan.dbServicePrivateListOfIO::pBlocker = 0; + + if ( chan.dbContextPrivateListOfIO::pBlocker ) { + chan.dbContextPrivateListOfIO::pBlocker->destructor ( guard ); + this->dbPutNotifyBlockerFreeList.release ( chan.dbContextPrivateListOfIO::pBlocker ); + chan.dbContextPrivateListOfIO::pBlocker = 0; } } -void dbServiceIO::ioCancel ( dbChannelIO & chan, const cacChannel::ioid &id ) +void dbContext::ioCancel ( + epicsGuard < epicsMutex > & guard, dbChannelIO & chan, + const cacChannel::ioid &id ) { - epicsGuard < epicsMutex > locker ( this->mutex ); + guard.assertIdenticalMutex ( this->mutex ); dbBaseIO * pIO = this->ioTable.remove ( id ); if ( pIO ) { dbSubscriptionIO *pSIO = pIO->isSubscription (); if ( pSIO ) { - chan.dbServicePrivateListOfIO::eventq.remove ( *pSIO ); - pSIO->~dbSubscriptionIO (); + chan.dbContextPrivateListOfIO::eventq.remove ( *pSIO ); + pSIO->destructor ( guard ); this->dbSubscriptionIOFreeList.release ( pSIO ); } - else if ( pIO == chan.dbServicePrivateListOfIO::pBlocker ) { - chan.dbServicePrivateListOfIO::pBlocker->cancel (); + else if ( pIO == chan.dbContextPrivateListOfIO::pBlocker ) { + chan.dbContextPrivateListOfIO::pBlocker->cancel ( guard ); } else { - errlogPrintf ( "dbServiceIO::ioCancel() unrecognized IO was probably leaked or not canceled\n" ); + errlogPrintf ( "dbContext::ioCancel() unrecognized IO was probably leaked or not canceled\n" ); } } } -void dbServiceIO::ioShow ( const cacChannel::ioid &id, unsigned level ) const +void dbContext::ioShow ( + epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id, + unsigned level ) const { - epicsGuard < epicsMutex > locker ( this->mutex ); - const dbBaseIO *pIO = this->ioTable.lookup ( id ); + guard.assertIdenticalMutex ( this->mutex ); + const dbBaseIO * pIO = this->ioTable.lookup ( id ); if ( pIO ) { - pIO->show ( level ); + pIO->show ( guard, level ); } } -void dbServiceIO::showAllIO ( const dbChannelIO &chan, unsigned level ) const +void dbContext::showAllIO ( const dbChannelIO & chan, unsigned level ) const { - epicsGuard < epicsMutex > locker ( this->mutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); tsDLIterConst < dbSubscriptionIO > pItem = - chan.dbServicePrivateListOfIO::eventq.firstIter (); + chan.dbContextPrivateListOfIO::eventq.firstIter (); while ( pItem.valid () ) { - pItem->show ( level ); + pItem->show ( guard, level ); pItem++; } - if ( chan.dbServicePrivateListOfIO::pBlocker ) { - chan.dbServicePrivateListOfIO::pBlocker->show ( level ); + if ( chan.dbContextPrivateListOfIO::pBlocker ) { + chan.dbContextPrivateListOfIO::pBlocker->show ( guard, level ); } } -void dbServiceIO::show ( unsigned level ) const +void dbContext::show ( unsigned level ) const { - epicsGuard < epicsMutex > locker ( this->mutex ); - printf ( "dbServiceIO at %p\n", + epicsGuard < epicsMutex > guard ( this->mutex ); +} + +void dbContext::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + printf ( "dbContext at %p\n", static_cast ( this ) ); if ( level > 0u ) { printf ( "\tevent call back cache location %p, and its size %lu\n", static_cast ( this->pStateNotifyCache ), this->stateNotifyCacheSize ); - this->readNotifyCache.show ( level - 1 ); + this->readNotifyCache.show ( guard, level - 1 ); } if ( level > 1u ) { this->mutex.show ( level - 2u ); } } +void dbContext::flush ( + epicsGuard < epicsMutex > & ) +{ +} + +unsigned dbContext::circuitCount ( + epicsGuard < epicsMutex > & ) const +{ + return 0u; +} + +void dbContext::selfTest ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + this->ioTable.verify (); +} + +unsigned dbContext::beaconAnomaliesSinceProgramStart ( + epicsGuard < epicsMutex > & ) const +{ + return 0u; +} + + diff --git a/src/db/dbSubscriptionIO.cpp b/src/db/dbSubscriptionIO.cpp index 27cf98e35..79c81ce7c 100644 --- a/src/db/dbSubscriptionIO.cpp +++ b/src/db/dbSubscriptionIO.cpp @@ -41,37 +41,57 @@ #include "dbChannelIO.h" #include "db_access_routines.h" -dbSubscriptionIO::dbSubscriptionIO ( dbServiceIO & /* serviceIO */, dbChannelIO & chanIO, - dbAddr & addr, cacStateNotify & notifyIn, unsigned typeIn, unsigned long countIn, - unsigned maskIn, dbEventCtx ctx ) : - notify ( notifyIn ), chan ( chanIO ), es ( 0 ), - type ( typeIn ), count ( countIn ), id ( 0u ) +dbSubscriptionIO::dbSubscriptionIO ( + epicsGuard < epicsMutex > & guard, epicsMutex & mutexIn, + dbContext &, dbChannelIO & chanIO, + dbAddr & addr, cacStateNotify & notifyIn, unsigned typeIn, + unsigned long countIn, unsigned maskIn, dbEventCtx ctx ) : + mutex ( mutexIn ), count ( countIn ), notify ( notifyIn ), + chan ( chanIO ), es ( 0 ), type ( typeIn ), id ( 0u ) { - this->es = db_add_event ( ctx, & addr, - dbSubscriptionEventCallback, (void *) this, maskIn ); - if ( this->es == 0 ) { - throw std::bad_alloc(); + guard.assertIdenticalMutex ( this->mutex ); + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->es = db_add_event ( ctx, & addr, + dbSubscriptionEventCallback, (void *) this, maskIn ); + if ( this->es == 0 ) { + throw std::bad_alloc(); + } + db_post_single_event ( this->es ); + db_event_enable ( this->es ); } - db_post_single_event ( this->es ); - db_event_enable ( this->es ); } -dbSubscriptionIO::~dbSubscriptionIO () +dbSubscriptionIO::~dbSubscriptionIO () { - this->unsubscribe (); } -void dbSubscriptionIO::unsubscribe () +void dbSubscriptionIO::destructor ( epicsGuard < epicsMutex > & guard ) { + guard.assertIdenticalMutex ( this->mutex ); + this->unsubscribe ( guard ); + this->~dbSubscriptionIO (); +} + +void dbSubscriptionIO::unsubscribe ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); if ( this->es ) { - db_cancel_event ( this->es ); + dbEventSubscription tmp = this->es; this->es = 0; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + db_cancel_event ( tmp ); + } } } -void dbSubscriptionIO::channelDeleteException () +void dbSubscriptionIO::channelDeleteException ( + epicsGuard < epicsMutex > & guard ) { - this->notify.exception ( ECA_CHANDESTROY, + guard.assertIdenticalMutex ( this->mutex ); + this->notify.exception ( guard, ECA_CHANDESTROY, this->chan.pName(), this->type, this->count ); } @@ -94,14 +114,14 @@ void dbSubscriptionIO::operator delete ( void * ) } void * dbSubscriptionIO::operator new ( size_t size, - tsFreeList < dbSubscriptionIO > & freeList ) + tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE void dbSubscriptionIO::operator delete ( void * pCadaver, - tsFreeList < dbSubscriptionIO > & freeList ) + tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } @@ -110,12 +130,21 @@ void dbSubscriptionIO::operator delete ( void * pCadaver, extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr * /* paddr */, int /* eventsRemaining */, struct db_field_log *pfl ) { - dbSubscriptionIO *pIO = static_cast < dbSubscriptionIO * > ( pPrivate ); + dbSubscriptionIO * pIO = static_cast < dbSubscriptionIO * > ( pPrivate ); pIO->chan.callStateNotify ( pIO->type, pIO->count, pfl, pIO->notify ); } void dbSubscriptionIO::show ( unsigned level ) const { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->show ( guard, level ); +} + +void dbSubscriptionIO::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + printf ( "Data base subscription IO at %p\n", static_cast ( this ) ); if ( level > 0u ) {