diff --git a/src/ca/CASG.cpp b/src/ca/CASG.cpp new file mode 100644 index 000000000..5bc9fe3ba --- /dev/null +++ b/src/ca/CASG.cpp @@ -0,0 +1,213 @@ +/* + * $Id$ + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + */ + +#include "iocinf.h" +#include "oldAccess.h" + +tsFreeList < struct CASG > CASG::freeList; + +CASG::CASG (cac &cacIn) : + client (cacIn), magic (CASG_MAGIC), opPendCount (0u), seqNo (0u) +{ + client.installCASG ( *this ); +} + +void CASG::destroy () +{ + delete this; +} + +CASG::~CASG () +{ + if ( this->verify () ) { + this->mutex.lock (); + tsDLIterBD notify ( this->ioList.first () ); + while ( notify != notify.eol () ) { + notify->destroy (); + notify = this->ioList.first (); + } + this->mutex.unlock (); + this->client.uninstallCASG ( *this ); + this->magic = 0; + } + else { + ca_printf ("cac: attempt to destroy invalid sync group ignored\n"); + } +} + +bool CASG::verify () const +{ + return ( this->magic == CASG_MAGIC ); +} + +/* + * CASG::block () + */ +int CASG::block ( double timeout ) +{ + unsigned long initialSeqNo = this->seqNo; + osiTime cur_time; + osiTime beg_time; + double delay; + double remaining; + int status; + + if ( timeout < 0.0 ) { + return ECA_TIMEOUT; + } + + /* + * dont allow recursion + */ + void *p = threadPrivateGet (cacRecursionLock); + if ( p ) { + return ECA_EVDISALLOW; + } + threadPrivateSet (cacRecursionLock, &cacRecursionLock); + + cur_time = osiTime::getCurrent (); + + this->client.flush (); + + beg_time = cur_time; + delay = 0.0; + + status = ECA_NORMAL; + while ( 1 ) { + this->mutex.lock (); + if ( this->seqNo != initialSeqNo ) { + this->mutex.unlock (); + break; + } + if ( this->opPendCount == 0u ) { + this->seqNo++; + this->mutex.unlock (); + break; + } + this->mutex.unlock (); + + /* + * Exit if the timeout has expired + * (dont wait forever for an itsy bitsy + * delay which will not be updated if + * select is called with no delay) + * + * current time is only updated by + * cac_select_io() if we specify + * at non-zero delay + */ + remaining = timeout - delay; + if ( remaining <= CAC_SIGNIFICANT_SELECT_DELAY ) { + /* + * Make sure that we take care of + * recv backlog at least once + */ + status = ECA_TIMEOUT; + this->mutex.lock (); + this->seqNo++; + this->mutex.unlock (); + break; + } + + remaining = min ( 60.0, remaining ); + + /* + * wait for asynch notification + */ + this->sem.wait ( remaining ); + + /* + * force a time update + */ + cur_time = osiTime::getCurrent (); + + delay = cur_time - beg_time; + } + + threadPrivateSet (cacRecursionLock, NULL); + + return status; +} + +void CASG::reset () +{ + this->mutex.lock (); + this->opPendCount = 0; + this->seqNo++; + this->mutex.unlock (); +} + +void CASG::show ( unsigned level) const +{ + printf ("Sync Group: id=%u, magic=%lu, opPend=%lu, seqNo=%lu\n", + this->getId (), this->magic, this->opPendCount, this->seqNo); + + if ( level ) { + this->mutex.lock (); + tsDLIterBD notify ( this->ioList.first () ); + while ( notify != notify.eol () ) { + notify->show (level); + } + this->mutex.unlock (); + } +} + +bool CASG::ioComplete () const +{ + return ( this->opPendCount == 0u ); +} + +void * CASG::operator new (size_t size) +{ + return CASG::freeList.allocate ( size ); +} + +void CASG::operator delete (void *pCadaver, size_t size) +{ + CASG::freeList.release ( pCadaver, size ); +} + +int CASG::put (chid pChan, unsigned type, unsigned long count, const void *pValue) +{ + syncGroupNotify *pNotify = new syncGroupNotify ( *this, 0); + if ( ! pNotify ) { + return ECA_ALLOCMEM; + } + return pChan->write ( type, count, pValue, *pNotify ); +} + +int CASG::get (chid pChan, unsigned type, unsigned long count, void *pValue) +{ + syncGroupNotify *pNotify = new syncGroupNotify ( *this, pValue); + if ( ! pNotify ) { + return ECA_ALLOCMEM; + } + return pChan->read ( type, count, *pNotify ); +} + diff --git a/src/ca/Makefile b/src/ca/Makefile index 6298d84b8..a42a168b9 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -3,8 +3,6 @@ TOP=../.. include $(TOP)/configure/CONFIG -CMPLR = STRICT - # # includes to install from this subproject # @@ -14,22 +12,46 @@ INC += caeventmask.h INC += caProto.h INC += db_access.h INC += pvAdapter.h +INC += addrList.h +INC += cacIO.h -#LIBSRCS += caOsDependent.c +LIBSRCS += cacChannel.cpp +LIBSRCS += cacChannelIO.cpp +LIBSRCS += cacNotify.cpp +LIBSRCS += cacNotifyIO.cpp +LIBSRCS += cacServiceList.cpp LIBSRCS += access.cpp LIBSRCS += processThread.cpp LIBSRCS += iocinf.cpp -LIBSRCS += service.cpp -LIBSRCS += conn.cpp LIBSRCS += flow_control.cpp -LIBSRCS += syncgrp.cpp LIBSRCS += convert.cpp LIBSRCS += test_event.cpp -#LIBSRCS += bsd_depen.c LIBSRCS += repeater.cpp LIBSRCS += ringBuffer.cpp LIBSRCS += searchTimer.cpp LIBSRCS += repeaterSubscribeTimer.cpp +LIBSRCS += tcpiiu.cpp +LIBSRCS += udpiiu.cpp +LIBSRCS += netiiu.cpp +LIBSRCS += nciu.cpp +LIBSRCS += baseNMIU.cpp +LIBSRCS += netReadCopyIO.cpp +LIBSRCS += netReadNotifyIO.cpp +LIBSRCS += netWriteNotifyIO.cpp +LIBSRCS += netSubscription.cpp +LIBSRCS += cac.cpp +LIBSRCS += conn.cpp +LIBSRCS += tcpSendWatchdog.cpp +LIBSRCS += tcpRecvWatchdog.cpp +LIBSRCS += bhe.cpp +LIBSRCS += oldChannel.cpp +LIBSRCS += oldSubscription.cpp +LIBSRCS += getCallback.cpp +LIBSRCS += putCallback.cpp +LIBSRCS += syncgrp.cpp +LIBSRCS += CASG.cpp +LIBSRCS += syncGroupNotify.cpp + LIBRARY=ca @@ -53,8 +75,8 @@ caRepeater_SRCS = caRepeater.cpp PROD += caRepeater PROD += catime acctst -catime_SRCS = catime.c -acctst_SRCS = acctst.c +catime_SRCS = catimeMain.c catime.c +acctst_SRCS = acctstMain.c acctst.c include $(TOP)/configure/RULES diff --git a/src/ca/Makefile.Host b/src/ca/Makefile.Host index b234fd83f..c7cad000d 100644 --- a/src/ca/Makefile.Host +++ b/src/ca/Makefile.Host @@ -2,8 +2,6 @@ TOP=../.. include $(TOP)/configure/CONFIG -CMPLR = STRICT - # # includes to install from this subproject # diff --git a/src/ca/Makefile.Ioc b/src/ca/Makefile.Ioc index 53cdeb0cd..8e2cd59c7 100644 --- a/src/ca/Makefile.Ioc +++ b/src/ca/Makefile.Ioc @@ -3,8 +3,6 @@ TOP=../.. include $(TOP)/configure/CONFIG -CMPLR = STRICT - USR_CFLAGS += -DiocCore caLib_SRCS += caOsDependent.c diff --git a/src/ca/access.cpp b/src/ca/access.cpp index 2c9c3ff31..a8bae7626 100644 --- a/src/ca/access.cpp +++ b/src/ca/access.cpp @@ -12,10 +12,6 @@ * */ -#include "osiSigPipeIgnore.h" -#include "freeList.h" -#include "osiProcess.h" - /* * allocate error message string array * here so I can use sizeof @@ -27,19 +23,14 @@ */ #define CAC_VERSION_GLOBAL -#include "iocinf.h" +#include "iocinf.h" +#include "oldAccess.h" -const static caHdr nullmsg = { +const caHdr cacnullmsg = { 0,0,0,0,0,0 }; -const static char nullBuff[32] = { - 0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0, - 0,0 -}; -static threadPrivateId caClientContextId; +threadPrivateId caClientContextId; threadPrivateId cacRecursionLock; @@ -70,150 +61,6 @@ int fetchClientContext (cac **ppcac) return status; } -#if 0 -/* - * cacSetSendPending () - */ -LOCAL void cacSetSendPending (tcpiiu *piiu) -{ - if (!piiu->sendPending) { - piiu->timeAtSendBlock = piiu->niiu.iiu.pcas->currentTime; - piiu->sendPending = TRUE; - } -} -#endif - -/* - * cac_push_tcp_msg() - */ -LOCAL int cac_push_tcp_msg (tcpiiu *piiu, const caHdr *pmsg, const void *pext) -{ - caHdr msg; - ca_uint16_t actualextsize; - ca_uint16_t extsize; - unsigned msgsize; - unsigned bytesSent; - - if ( pext == NULL ) { - extsize = actualextsize = 0; - } - else { - if ( pmsg->m_postsize > 0xffff-7 ) { - return ECA_TOLARGE; - } - actualextsize = pmsg->m_postsize; - extsize = CA_MESSAGE_ALIGN (actualextsize); - } - - msg = *pmsg; - msg.m_postsize = htons (extsize); - msgsize = extsize + sizeof (msg); - - /* - * push the header onto the ring - */ - bytesSent = cacRingBufferWrite ( &piiu->send, &msg, sizeof (msg) ); - if ( bytesSent != sizeof (msg) ) { - return ECA_DISCONNCHID; - } - - /* - * push message body onto the ring - * - * (optionally encode in network format as we send) - */ - if (extsize>0u) { - bytesSent = cacRingBufferWrite ( &piiu->send, pext, actualextsize ); - if ( bytesSent != actualextsize ) { - return ECA_DISCONNCHID; - } - /* - * force pad bytes at the end of the message to nill - * if present (this avoids messages from purify) - */ - { - unsigned long n; - - n = extsize-actualextsize; - if (n) { - assert ( n <= sizeof (nullBuff) ); - bytesSent = cacRingBufferWrite ( &piiu->send, nullBuff, n ); - if ( bytesSent != n ) { - return ECA_DISCONNCHID; - } - } - } - } - - return ECA_NORMAL; -} - -/* - * cac_push_tcp_msg_no_block () - */ -LOCAL bool cac_push_tcp_msg_no_block (tcpiiu *piiu, const caHdr *pmsg, const void *pext) -{ - unsigned size; - int status; - - size = sizeof (*pmsg); - if ( pext != NULL ) { - size += CA_MESSAGE_ALIGN (pmsg->m_postsize); - } - - if ( cacRingBufferWriteLockNoBlock (&piiu->send, size) ) { - status = cac_push_tcp_msg (piiu, pmsg, pext); - cacRingBufferWriteUnlock (&piiu->send); - if (status == ECA_NORMAL) { - return true; - } - else { - return false; - } - } - else { - return false; - } -} - -/* - * cac_push_udp_msg() - */ -LOCAL int cac_push_udp_msg (udpiiu *piiu, const caHdr *pMsg, const void *pExt, ca_uint16_t extsize) -{ - unsigned long msgsize; - ca_uint16_t allignedExtSize; - caHdr *pbufmsg; - - allignedExtSize = CA_MESSAGE_ALIGN (extsize); - msgsize = sizeof (caHdr) + allignedExtSize; - - - /* fail out if max message size exceeded */ - if ( msgsize >= sizeof (piiu->xmitBuf)-7 ) { - return ECA_TOLARGE; - } - - semMutexMustTake (piiu->xmitBufLock); - if ( msgsize + piiu->nBytesInXmitBuf > sizeof (piiu->xmitBuf) ) { - semMutexGive (piiu->xmitBufLock); - return ECA_TOLARGE; - } - - pbufmsg = (caHdr *) &piiu->xmitBuf[piiu->nBytesInXmitBuf]; - *pbufmsg = *pMsg; - memcpy (pbufmsg+1, pExt, extsize); - if ( extsize != allignedExtSize ) { - char *pDest = (char *) (pbufmsg+1); - memset (pDest + extsize, '\0', allignedExtSize - extsize); - } - pbufmsg->m_postsize = htons (allignedExtSize); - piiu->nBytesInXmitBuf += msgsize; - semMutexGive (piiu->xmitBufLock); - - return ECA_NORMAL; -} - /* * Default Exception Handler */ @@ -240,280 +87,16 @@ extern "C" void ca_default_exception_handler (struct exception_handler_args args } } -/* - * default local pv interface entry points that always fail - */ -extern "C" pvId pvNameToIdNoop (const char *) +void caClientExitHandler () { - return 0; -} -extern "C" int pvPutFieldNoop (pvId, int, - const void *, int) -{ - return -1; -} -extern "C" int pvGetFieldNoop (pvId, int, void *, int, void *) -{ - return -1; -} -extern "C" long pvPutNotifyInitiateNoop (pvId, - unsigned, unsigned long, const void *, - void (*)(void *), void *, putNotifyId *) -{ - return -1; -} -extern "C" void pvPutNotifyDestroyNoop (putNotifyId) -{ -} -extern "C" const char * pvNameNoop (pvId) -{ - return ""; -} -extern "C" unsigned long pvNoElementsNoop (pvId) -{ - return 0u; -} -extern "C" short pvTypeNoop (pvId) -{ - return -1; -} -extern "C" dbEventCtx pvEventQueueInitNoop () -{ - return NULL; -} -extern "C" int pvEventQueueStartNoop (dbEventCtx, const char *, - void (*)(void *), void *, int) -{ - return -1; -} -extern "C" void pvEventQueueCloseNoop (dbEventCtx) -{ -} -extern "C" dbEventSubscription pvEventQueueAddEventNoop (dbEventCtx, pvId, - void (*)(void *, pvId, int, struct db_field_log *), - void *, unsigned) -{ - return NULL; -} -extern "C" void pvEventQueuePostSingleEventNoop (dbEventSubscription) -{ -} -extern "C" void pvEventQueueCancelEventNoop (dbEventSubscription) -{ -} -extern "C" int pvEventQueueAddExtraLaborEventNoop (dbEventCtx, - void (*)(void *), void *) -{ - return -1; -} -extern "C" int pvEventQueuePostExtraLaborNoop (dbEventCtx) -{ - return -1; -} - -LOCAL const pvAdapter pvAdapterNOOP = -{ - pvNameToIdNoop, - pvPutFieldNoop, - pvGetFieldNoop, - pvPutNotifyInitiateNoop, - pvPutNotifyDestroyNoop, - pvNameNoop, - pvNoElementsNoop, - pvTypeNoop, - pvEventQueueInitNoop, - pvEventQueueStartNoop, - pvEventQueueCloseNoop, - pvEventQueuePostSingleEventNoop, - pvEventQueueAddEventNoop, - pvEventQueueCancelEventNoop, - pvEventQueueAddExtraLaborEventNoop, - pvEventQueuePostExtraLaborNoop -}; - -/* - * event_import() - */ -extern "C" void cac_event_import (void *pParam) -{ - threadPrivateSet (caClientContextId, pParam); -} - -/* - * constructLocalIIU - */ -LOCAL int constructLocalIIU (cac *pcac, const pvAdapter *pva, lclIIU *piiu) -{ - long status; - - freeListInitPvt (&piiu->localSubscrFreeListPVT, sizeof (lmiu), 256); - - piiu->putNotifyLock = semMutexCreate (); - if (!piiu->putNotifyLock) { - return ECA_ALLOCMEM; + if ( caClientContextId ) { + threadPrivateDelete ( caClientContextId ); + caClientContextId = 0; } - - ellInit (&piiu->buffList); - ellInit (&piiu->chidList); - piiu->iiu.pcas = pcac; - piiu->pva = pva; - - piiu->evctx = (*piiu->pva->p_pvEventQueueInit) (); - if (piiu->evctx) { - - /* higher priority */ - status = (*piiu->pva->p_pvEventQueueStart) - (piiu->evctx, "CAC Event", cac_event_import, pcac, +1); - if (status) { - (*piiu->pva->p_pvEventQueueClose) (piiu->evctx); - semMutexDestroy (piiu->putNotifyLock); - return ECA_ALLOCMEM; - } + if ( cacRecursionLock ) { + threadPrivateDelete ( cacRecursionLock ); + cacRecursionLock = 0; } - return ECA_NORMAL; -} - -/* - * convert a generic ciu pointer to a network ciu - */ -LOCAL nciu *ciuToNCIU (baseCIU *pIn) -{ - char *pc = (char *) pIn; - - assert (pIn->piiu != &pIn->piiu->pcas->localIIU.iiu); - pc -= offsetof (nciu, ciu); - return (nciu *) pc; -} - -/* - * convert a generic baseCIU pointer to a local ciu - */ -LOCAL lciu *ciuToLCIU (baseCIU *pIn) -{ - char *pc = (char *) pIn; - - assert (pIn->piiu == &pIn->piiu->pcas->localIIU.iiu); - pc -= offsetof (lciu, ciu); - return (lciu *) pc; -} - -/* - * convert a generic miu pointer to a network miu - */ -LOCAL nmiu *miuToNMIU (baseMIU *pIn) -{ - char *pc = (char *) pIn; - - assert (pIn->pChan->piiu != &pIn->pChan->piiu->pcas->localIIU.iiu); - pc -= offsetof (nmiu, miu); - return (nmiu *) pc; -} - -/* - * convert a generic miu pointer to a local miu - */ -LOCAL lmiu *miuToLMIU (baseMIU *pIn) -{ - char *pc = (char *) pIn; - - assert (pIn->pChan->piiu == &pIn->pChan->piiu->pcas->localIIU.iiu); - pc -= offsetof (lmiu, miu); - return (lmiu *) pc; -} - -/* - * localMonitorResourceDestroy () - */ -LOCAL void localMonitorResourceDestroy (lmiu *pSubscription) -{ - lciu *pLocalChan = ciuToLCIU (pSubscription->miu.pChan); - - /* - * lock must be off when canceling the event so that - * the event queue can be flushed - */ - (*pSubscription->miu.pChan->piiu->pcas->localIIU.pva->p_pvEventQueueCancelEvent) (pSubscription->es); - - LOCK (pLocalChan->ciu.piiu->pcas); - ellDelete (&pLocalChan->eventq, &pSubscription->node); - freeListFree (pSubscription->miu.pChan->piiu->pcas->localIIU.localSubscrFreeListPVT, pSubscription); - UNLOCK (pLocalChan->ciu.piiu->pcas); -} - -/* - * caPutNotifydestroy () - */ -LOCAL void caPutNotifydestroy (lclIIU *piiu, caPutNotify *ppn) -{ - if (ppn) { - (*piiu->pva->p_pvPutNotifyDestroy) (ppn->dbPutNotify); - free (ppn); - } -} - -/* - * localChannelDestroy () - */ -LOCAL void localChannelDestroy (lclIIU *piiu, lciu *pChan) -{ - lmiu *pSubscription; - - LOCK (piiu->iiu.pcas); - - pChan->ciu.pAccessRightsFunc = NULL; - pChan->ciu.pConnFunc = NULL; - - while ( (pSubscription = (lmiu *) ellFirst (&pChan->eventq)) ) { - - /* - * temp release lock so that the event task - * can flush the event - */ - UNLOCK (piiu->iiu.pcas); - localMonitorResourceDestroy (pSubscription); - LOCK (piiu->iiu.pcas); - } - - caPutNotifydestroy (piiu, pChan->ppn); - - ellDelete (&piiu->chidList, &pChan->node); - - UNLOCK (piiu->iiu.pcas); - - pChan->ciu.puser = NULL; - pChan->ciu.piiu = NULL; - - free (pChan); -} - -/* - * liiu_destroy () - */ -LOCAL void liiu_destroy (lclIIU *piiu) -{ - lciu *pChan; - - freeListCleanup (piiu->localSubscrFreeListPVT); - - /* - * destroy all channels - */ - pChan = (lciu *) ellFirst (&piiu->chidList); - while (pChan) { - lciu *pChanNext = (lciu *) ellNext (&pChan->node); - localChannelDestroy (piiu, pChan); - pChan = pChanNext; - } - - /* - * All local events must be canceled prior to closing the - * local event facility - */ - (*piiu->pva->p_pvEventQueueClose) (piiu->evctx); - - ellFree (&piiu->buffList); - - semMutexDestroy (piiu->putNotifyLock); } /* @@ -528,6 +111,7 @@ int epicsShareAPI ca_task_initialize (void) if (!caClientContextId) { return ECA_ALLOCMEM; } + atexit (caClientExitHandler); } pcac = (cac *) threadPrivateGet (caClientContextId); @@ -543,163 +127,20 @@ int epicsShareAPI ca_task_initialize (void) } // -// cac::cac () +// ca_register_service () // -cac::cac () +epicsShareFunc int epicsShareAPI ca_register_service ( cacServiceIO *pService ) { - long status; - int caStatus; - - if (cacRecursionLock==NULL) { - cacRecursionLock = threadPrivateCreate (); - if (!cacRecursionLock) { - throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); - } + cac *pcac; + int caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; } - - if (!bsdSockAttach()) { - throwWithLocation ( caErrorCode (ECA_INTERNAL) ); - } - - ellInit (&this->ca_taskVarList); - ellInit (&this->putCvrtBuf); - ellInit (&this->ca_iiuList); - ellInit (&this->fdInfoFreeList); - ellInit (&this->fdInfoList); - this->ca_printf_func = errlogVprintf; - this->pudpiiu = NULL; - this->ca_exception_func = NULL; - this->ca_exception_arg = NULL; - this->ca_fd_register_func = NULL; - this->ca_fd_register_arg = NULL; - this->ca_pEndOfBCastList = NULL; - this->ca_pndrecvcnt = 0; - this->ca_nextSlowBucketId = 0; - this->ca_nextFastBucketId = 0; - this->ca_flush_pending = FALSE; - this->ca_number_iiu_in_fc = 0u; - this->ca_manage_conn_active = FALSE; - memset (this->ca_beaconHash, '\0', sizeof (this->ca_beaconHash) ); - ca_sg_init (this); - - this->ca_client_lock = semMutexCreate(); - if (!this->ca_client_lock) { - throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + if ( ! pService ) { + return ECA_NORMAL; } - this->ca_io_done_sem = semBinaryCreate(semEmpty); - if (!this->ca_io_done_sem) { - semMutexDestroy (this->ca_client_lock); - throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); - } - this->ca_blockSem = semBinaryCreate(semEmpty); - if (!this->ca_blockSem) { - semMutexDestroy (this->ca_client_lock); - semBinaryDestroy (this->ca_io_done_sem); - throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); - } - - installSigPipeIgnore(); - - { - char tmp[256]; - size_t len; - osiGetUserNameReturn gunRet; - - gunRet = osiGetUserName ( tmp, sizeof (tmp) ); - if (gunRet!=osiGetUserNameSuccess) { - tmp[0] = '\0'; - } - len = strlen (tmp) + 1; - this->ca_pUserName = (char *) malloc ( len ); - if ( ! this->ca_pUserName ) { - semMutexDestroy (this->ca_client_lock); - semBinaryDestroy (this->ca_io_done_sem); - semBinaryDestroy (this->ca_blockSem); - throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); - } - strncpy (this->ca_pUserName, tmp, len); - } - - /* record the host name */ - this->ca_pHostName = localHostName(); - if (!this->ca_pHostName) { - semMutexDestroy (this->ca_client_lock); - semBinaryDestroy (this->ca_io_done_sem); - semBinaryDestroy (this->ca_blockSem); - free (this->ca_pUserName); - throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); - } - - status = tsStampGetCurrent (&this->currentTime); - if (status!=0) { - semMutexDestroy (this->ca_client_lock); - semBinaryDestroy (this->ca_io_done_sem); - semBinaryDestroy (this->ca_blockSem); - free (this->ca_pUserName); - free (this->ca_pHostName); - throwWithLocation ( caErrorCode (ECA_INTERNAL) ); - } - this->programBeginTime = this->currentTime; - this->programBeginTime = this->currentTime; - - /* - * construct the local IIU with a default - * NOOP database adapter) - */ - caStatus = constructLocalIIU (this, &pvAdapterNOOP, &this->localIIU); - if (caStatus!=ECA_NORMAL) { - ca_sg_shutdown (this); - semMutexDestroy (this->ca_client_lock); - semBinaryDestroy (this->ca_io_done_sem); - semBinaryDestroy (this->ca_blockSem); - free (this->ca_pUserName); - free (this->ca_pHostName); - throwWithLocation ( caErrorCode (caStatus) ); - } - - freeListInitPvt (&this->ca_ioBlockFreeListPVT, sizeof (nmiu), 256); - - this->ca_pSlowBucket = bucketCreate (CLIENT_HASH_TBL_SIZE); - if (this->ca_pSlowBucket==NULL) { - ca_sg_shutdown (this); - liiu_destroy (&this->localIIU); - semMutexDestroy (this->ca_client_lock); - semBinaryDestroy (this->ca_io_done_sem); - semBinaryDestroy (this->ca_blockSem); - free (this->ca_pUserName); - free (this->ca_pHostName); - throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); - } - - this->ca_pFastBucket = bucketCreate (CLIENT_HASH_TBL_SIZE); - if (this->ca_pFastBucket==NULL) { - ca_sg_shutdown (this); - liiu_destroy (&this->localIIU);\ - bucketFree (this->ca_pSlowBucket); - semMutexDestroy (this->ca_client_lock); - semBinaryDestroy (this->ca_io_done_sem); - semBinaryDestroy (this->ca_blockSem); - free (this->ca_pUserName); - free (this->ca_pHostName); - throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); - } - - status = envGetDoubleConfigParam (&EPICS_CA_CONN_TMO, &this->ca_connectTMO); - if (status) { - this->ca_connectTMO = CA_CONN_VERIFY_PERIOD; - ca_printf (this, - "EPICS \"%s\" float fetch failed\n", - EPICS_CA_CONN_TMO.name); - ca_printf (this, - "Setting \"%s\" = %f\n", - EPICS_CA_CONN_TMO.name, - this->ca_connectTMO); - } - - this->ca_server_port = - envGetInetPortConfigParam (&EPICS_CA_SERVER_PORT, CA_SERVER_PORT); - - threadPrivateSet (caClientContextId, (void *) this); + pcac->registerService ( *pService ); + return ECA_NORMAL; } /* @@ -748,105 +189,6 @@ epicsShareFunc int epicsShareAPI ca_task_exit (void) return ECA_NORMAL; } -/* - * cac_destroy () - * - * releases all resources alloc to a channel access client - */ -cac::~cac () -{ - tcpiiu *piiu; - int status; - - threadPrivateSet (caClientContextId, NULL); - - // - // shutdown all tcp connections and wait for threads to exit - // - while ( 1 ) { - semBinaryId id; - - LOCK (this); - piiu = (tcpiiu *) ellFirst (&this->ca_iiuList); - if (piiu) { - id = piiu->recvThreadExitSignal; - initiateShutdownTCPIIU (piiu); - } - UNLOCK (this); - - if (piiu) { - semBinaryTake (id); - } - else { - break; - } - } - - // - // shutdown udp and wait for threads to exit - // - if (this->pudpiiu) { - cacShutdownUDP (*this->pudpiiu); - delete this->pudpiiu; - } - - LOCK (this); - - liiu_destroy (&this->localIIU); - - /* remove put convert block free list */ - ellFree (&this->putCvrtBuf); - - /* reclaim sync group resources */ - ca_sg_shutdown (this); - - /* remove remote waiting ev blocks */ - freeListCleanup (this->ca_ioBlockFreeListPVT); - - /* free select context lists */ - ellFree (&this->fdInfoFreeList); - ellFree (&this->fdInfoList); - - /* - * remove IOCs in use - */ - ellFree (&this->ca_iiuList); - - /* - * free user name string - */ - if (this->ca_pUserName) { - free (this->ca_pUserName); - } - - /* - * free host name string - */ - if (this->ca_pHostName) { - free (this->ca_pHostName); - } - - /* - * free hash tables - */ - status = bucketFree (this->ca_pSlowBucket); - assert (status == S_bucket_success); - status = bucketFree (this->ca_pFastBucket); - assert (status == S_bucket_success); - - /* - * free beacon hash table - */ - freeBeaconHash (this); - - semBinaryDestroy (this->ca_io_done_sem); - semBinaryDestroy (this->ca_blockSem); - - semMutexDestroy (this->ca_client_lock); - - bsdSockRelease (); -} - /* * * CA_BUILD_AND_CONNECT @@ -864,1027 +206,102 @@ int epicsShareAPI ca_build_and_connect (const char *name_str, chtype get_type, return ca_search_and_connect (name_str, chan, conn_func, puser); } -/* - * constructCoreChannel () - */ -LOCAL void constructCoreChannel (baseCIU *pciu, - caCh *conn_func, void *puser) -{ - pciu->piiu = NULL; - pciu->puser = puser; - pciu->pConnFunc = conn_func; - pciu->pAccessRightsFunc = NULL; -} - -/* - * constructLocalChannel () - */ -LOCAL int constructLocalChannel (cac *pcac, pvId idIn, - caCh *conn_func, void *puser, chid *pid) -{ - lciu *pchan; - struct connection_handler_args args; - - /* - * allocate channel data structue and also allocate enough - * space for the channel name - */ - pchan = (lciu *) calloc (1, sizeof(*pchan)); - if (!pchan){ - return ECA_ALLOCMEM; - } - - LOCK (pcac); - - constructCoreChannel (&pchan->ciu, conn_func, puser); - ellInit (&pchan->eventq); - pchan->id = idIn; - pchan->ppn = NULL; - pchan->ciu.piiu = &pcac->localIIU.iiu; - ellAdd (&pcac->localIIU.chidList, &pchan->node); - - args.chid = (chid) &pchan->ciu; - args.op = CA_OP_CONN_UP; - (*conn_func) (args); - - UNLOCK (pcac); - - *pid = (chid *) &pchan->ciu; - - return ECA_NORMAL; -} - -/* - * constructNetChannel () - */ -LOCAL int constructNetChannel (cac *pcac, - caCh *conn_func, void *puser, const char *pName, - unsigned nameLength, chid *pid) -{ - nciu *pchan; - char *pNameDest; - long status; - - if (nameLength>USHRT_MAX) { - return ECA_STRTOBIG; - } - - if (!pcac->pudpiiu) { - pcac->pudpiiu = new udpiiu (pcac); - if (!pcac->pudpiiu) { - return ECA_NOCAST; - } - } - - /* - * allocate channel data structue and also allocate enough - * space for the channel name - */ - pchan = (nciu *) calloc (1, sizeof(*pchan) + nameLength); - if (!pchan){ - return ECA_ALLOCMEM; - } - - LOCK (pcac); - - do { - pchan->cid = CLIENT_SLOW_ID_ALLOC (pcac); - status = bucketAddItemUnsignedId (pcac->ca_pSlowBucket, &pchan->cid, pchan); - } while (status == S_bucket_idInUse); - if (status != S_bucket_success) { - UNLOCK (pcac); - free (pchan); - if (status == S_bucket_noMemory) { - return ECA_ALLOCMEM; - } - return ECA_INTERNAL; - } - - constructCoreChannel (&pchan->ciu, conn_func, puser); - - pNameDest = (char *)(pchan + 1); - strncpy (pNameDest, pName, nameLength); - pNameDest[nameLength-1] = '\0'; - - pchan->type = USHRT_MAX; /* invalid initial type */ - pchan->count = 0; /* invalid initial count */ - pchan->sid = UINT_MAX; /* invalid initial server id */ - pchan->ar.read_access = FALSE; - pchan->ar.write_access = FALSE; - pchan->nameLength = nameLength; - pchan->previousConn = 0; - pchan->connected = 0; - addToChanList (pchan, &pcac->pudpiiu->niiu); - ellInit (&pchan->eventq); - - /* - * reset broadcasted search counters - */ - pcac->pudpiiu->searchTmr.reset (CA_RECAST_DELAY); - - /* - * Connection Management takes care - * of sending the the search requests - */ - if (!pchan->ciu.pConnFunc) { - pcac->ca_pndrecvcnt++; - } - - UNLOCK (pcac); - - *pid = (chid *) &pchan->ciu; - - return ECA_NORMAL; -} - /* * ca_search_and_connect() */ int epicsShareAPI ca_search_and_connect (const char *name_str, chid *chanptr, caCh *conn_func, void *puser) { + oldChannel *pChan; int caStatus; cac *pcac; - pvId tmpId; - unsigned strcnt; caStatus = fetchClientContext (&pcac); if ( caStatus != ECA_NORMAL ) { return caStatus; } - /* - * rational limit on user supplied string size - */ - if (name_str==NULL) { - return ECA_EMPTYSTR; - } - strcnt = strlen (name_str) + 1; - if (strcnt > MAX_UDP-sizeof(caHdr)) { - return ECA_STRTOBIG; - } - if (strcnt <= 1) { + if ( name_str == NULL ) { return ECA_EMPTYSTR; } - /* - * check to see if the channel is hosted within this address space - */ - tmpId = (*pcac->localIIU.pva->p_pvNameToId) (name_str); - if (tmpId) { - return constructLocalChannel (pcac, tmpId, conn_func, puser, chanptr); + if ( *name_str == '\0' ) { + return ECA_EMPTYSTR; + } + + pChan = new oldChannel (conn_func, puser); + if ( ! pChan ) { + return ECA_ALLOCMEM; + } + + if ( pcac->createChannel ( name_str, *pChan ) ) { + *chanptr = pChan; + return ECA_NORMAL; } else { - return constructNetChannel (pcac, conn_func, puser, name_str, strcnt, chanptr); + return ECA_ALLOCMEM; } } -/* - * cac_search_msg () - */ -int cac_search_msg (nciu *chan) -{ - udpiiu *piiu = chan->ciu.piiu->pcas->pudpiiu; - int status; - caHdr msg; - - if ( chan->ciu.piiu != &piiu->niiu.iiu ) { - return ECA_INTERNAL; - } - - if (chan->nameLength > 0xffff) { - return ECA_STRTOBIG; - } - - msg.m_cmmd = htons (CA_PROTO_SEARCH); - msg.m_available = chan->cid; - msg.m_dataType = htons (DONTREPLY); - msg.m_count = htons (CA_MINOR_VERSION); - msg.m_cid = chan->cid; - - status = cac_push_udp_msg (piiu, &msg, chan+1, chan->nameLength); - if (status != ECA_NORMAL) { - return status; - } - - /* - * increment the number of times we have tried to find this channel - */ - if (chan->retryretry++; - } - - /* - * move the channel to the end of the list so - * that all channels get a equal chance - */ - LOCK (chan->ciu.piiu->pcas); - ellDelete (&chan->ciu.piiu->pcas->pudpiiu->niiu.chidList, &chan->node); - ellAdd (&chan->ciu.piiu->pcas->pudpiiu->niiu.chidList, &chan->node); - UNLOCK (chan->ciu.piiu->pcas); - - return ECA_NORMAL; -} - -/* - * caIOBlockCreate () - */ -LOCAL nmiu *caIOBlockCreate (nciu *pChan, unsigned cmdIn, chtype type, - unsigned long count, caEventCallBackFunc *pFunc, void *pParam) -{ - int status; - nmiu *pIOBlock; - - LOCK (pChan->ciu.piiu->pcas); - - pIOBlock = (nmiu *) freeListCalloc (pChan->ciu.piiu->pcas->ca_ioBlockFreeListPVT); - if (pIOBlock) { - do { - pIOBlock->id = CLIENT_FAST_ID_ALLOC (pChan->ciu.piiu->pcas); - status = bucketAddItemUnsignedId (pChan->ciu.piiu->pcas->ca_pFastBucket, &pIOBlock->id, pIOBlock); - } while (status == S_bucket_idInUse); - - if(status != S_bucket_success){ - freeListFree (pChan->ciu.piiu->pcas->ca_ioBlockFreeListPVT, pIOBlock); - pIOBlock = NULL; - } - } - - pIOBlock->cmd = cmdIn; - pIOBlock->miu.pChan = &pChan->ciu; - pIOBlock->miu.type = type; - pIOBlock->miu.count = count; - pIOBlock->miu.usr_func = pFunc; - pIOBlock->miu.usr_arg = pParam; - - ellAdd (&pChan->eventq, &pIOBlock->node); - - UNLOCK (pChan->ciu.piiu->pcas); - - return pIOBlock; -} - -/* - * cac_lcl_event_handler () - */ -extern "C" void cac_lcl_event_handler (void *usrArg, - pvId idIn, int /* hold */, struct db_field_log *pfl) -{ - lmiu *monix = (lmiu *) usrArg; - lciu *pChan = ciuToLCIU (monix->miu.pChan); - lclIIU *piiu = iiuToLIIU (pChan->ciu.piiu); - union db_access_val valbuf; - unsigned long count; - unsigned long nativeElementCount; - void *pval; - size_t size; - int status; - struct tmp_buff { - ELLNODE node; - size_t size; - }; - struct tmp_buff *pbuf = NULL; - - nativeElementCount = (*piiu->pva->p_pvNoElements) (pChan->id); - - /* - * clip to the native count - * and set to the native count if they specify zero - */ - if (monix->miu.count > nativeElementCount || monix->miu.count == 0){ - count = nativeElementCount; - } - else { - count = monix->miu.count; - } - - size = dbr_size_n (monix->miu.type, count); - - if ( size <= sizeof(valbuf) ) { - pval = (void *) &valbuf; - } - else { - /* - * find a preallocated block which fits - * (stored with largest block first) - */ - LOCK (piiu->iiu.pcas); - pbuf = (struct tmp_buff *) ellFirst (&piiu->buffList); - if (pbuf && pbuf->size >= size) { - ellDelete (&piiu->buffList, &pbuf->node); - } - else { - pbuf = NULL; - } - UNLOCK (piiu->iiu.pcas); - - /* - * test again so malloc is not inside LOCKED - * section - */ - if (!pbuf) { - pbuf = (struct tmp_buff *) malloc(sizeof(*pbuf)+size); - if (!pbuf) { - ca_printf (piiu->iiu.pcas, - "%s: No Mem, Event Discarded\n", - __FILE__); - return; - } - pbuf->size = size; - } - pval = (void *) (pbuf+1); - } - status = (*piiu->pva->p_pvGetField) (idIn, monix->miu.type, - pval, count, pfl); - - /* - * Call user's callback - */ - LOCK (piiu->iiu.pcas); - if (monix->miu.usr_func) { - struct event_handler_args args; - - args.usr = (void *) monix->miu.usr_arg; - args.chid = monix->miu.pChan; - args.type = monix->miu.type; - args.count = count; - args.dbr = pval; - - if (status) { - args.status = ECA_GETFAIL; - } - else{ - args.status = ECA_NORMAL; - } - - (*monix->miu.usr_func)(args); - } - - /* - * insert the buffer back into the que in size order if - * one was used. - */ - if(pbuf){ - struct tmp_buff *ptbuf; - - for (ptbuf = (struct tmp_buff *) ellFirst (&piiu->buffList); - ptbuf; ptbuf = (struct tmp_buff *) ellNext (&pbuf->node) ){ - - if(ptbuf->size <= pbuf->size){ - break; - } - } - if (ptbuf) { - ptbuf = (struct tmp_buff *) ptbuf->node.previous; - } - - ellInsert (&piiu->buffList, &ptbuf->node, &pbuf->node); - } - UNLOCK (piiu->iiu.pcas); - - return; -} - -/* - * issue_get () - */ -LOCAL int issue_get (ca_uint16_t cmd, nciu *chan, chtype type, - unsigned long count, caEventCallBackFunc *pfunc, void *pParam) -{ - int status; - caHdr hdr; - ca_uint16_t type_u16; - ca_uint16_t count_u16; - tcpiiu *piiu; - - /* - * fail out if channel isnt connected or arguments are - * otherwise invalid - */ - if (!chan->connected) { - return ECA_DISCONNCHID; - } - if (INVALID_DB_REQ(type)) { - return ECA_BADTYPE; - } - if (!chan->ar.read_access) { - return ECA_NORDACCESS; - } - if (count > chan->count || count>0xffff) { - return ECA_BADCOUNT; - } - if (count == 0) { - if (cmd==CA_PROTO_READ_NOTIFY) { - count = chan->count; - } - else { - return ECA_BADCOUNT; - } - } - - /* - * only after range checking type and count cast - * them down to a smaller size - */ - type_u16 = (ca_uint16_t) type; - count_u16 = (ca_uint16_t) count; - - LOCK (chan->ciu.piiu->pcas); - { - nmiu *monix = caIOBlockCreate (chan, cmd, type, count, pfunc, pParam); - if (!monix) { - UNLOCK (chan->ciu.piiu->pcas); - return ECA_ALLOCMEM; - } - - hdr.m_cmmd = htons (cmd); - hdr.m_dataType = htons (type_u16); - hdr.m_count = htons (count_u16); - hdr.m_available = monix->id; - hdr.m_postsize = 0; - hdr.m_cid = chan->sid; - } - UNLOCK (chan->ciu.piiu->pcas); - - piiu = iiuToTCPIIU (chan->ciu.piiu); - status = cac_push_tcp_msg (piiu, &hdr, NULL); - if (status!=ECA_NORMAL && status!=ECA_DISCONNCHID) { - /* - * we need to be careful about touching the monix - * pointer after the lock has been released - */ - caIOBlockFree (chan->ciu.piiu->pcas, hdr.m_available); - } - - return status; -} /* * ca_array_get () */ -int epicsShareAPI ca_array_get (chtype type, unsigned long count, chid chanIn, void *pvalue) +int epicsShareAPI ca_array_get ( chtype type, unsigned long count, chid pChan, void *pValue ) { - baseCIU *pChan = (baseCIU *) chanIn; - int status; - - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - lciu *pLocalChan = ciuToLCIU (pChan); - - status = (*pChan->piiu->pcas->localIIU.pva->p_pvGetField) ( - pLocalChan->id, type, pvalue, count, NULL); - if (status) { - return ECA_GETFAIL; - } - else { - return ECA_NORMAL; - } - } - else { - nciu *pNetChan = ciuToNCIU (pChan); - status = issue_get (CA_PROTO_READ, pNetChan, type, count, NULL, pvalue); - if (status==ECA_NORMAL) { - pChan->piiu->pcas->ca_pndrecvcnt++; - } - return status; - } + return pChan->read ( type, count, pValue ); } /* * ca_array_get_callback () */ -int epicsShareAPI ca_array_get_callback (chtype type, unsigned long count, chid chanIn, +int epicsShareAPI ca_array_get_callback (chtype type, unsigned long count, chid pChan, caEventCallBackFunc *pfunc, void *arg) { - baseCIU *pChan = (baseCIU *) chanIn; - - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - lciu *pLocalChan = ciuToLCIU (pChan); - lmiu ev; - - ev.miu.usr_func = pfunc; - ev.miu.usr_arg = arg; - ev.miu.pChan = pChan; - ev.miu.type = type; - ev.miu.count = count; - cac_lcl_event_handler (&ev, pLocalChan->id, 0, NULL); - return ECA_NORMAL; - } - else { - nciu *pNetChan = ciuToNCIU (pChan); - return issue_get (CA_PROTO_READ_NOTIFY, pNetChan, type, count, pfunc, arg); - } -} - -/* - * caIOBlockFree () - */ -void caIOBlockFree (cac *pcac, unsigned id) -{ - nmiu *pIOBlock; - nciu *pNetChan; - - LOCK (pcac); - pIOBlock = (nmiu *) bucketLookupAndRemoveItemUnsignedId ( - pcac->ca_pFastBucket, &id); - if (!pIOBlock) { - ca_printf (pcac, "CAC: Delete of invalid IO block identifier = %u ignored?\n", id); - UNLOCK (pcac); - return; - } - pIOBlock->id = ~0U; /* this id always invalid */ - pNetChan = ciuToNCIU (pIOBlock->miu.pChan); - ellDelete (&pNetChan->eventq, &pIOBlock->node); - freeListFree (pcac->ca_ioBlockFreeListPVT, pIOBlock); - UNLOCK (pcac); -} - -/* - * check_a_dbr_string() - */ -LOCAL int check_a_dbr_string (const char *pStr, const unsigned count) -{ - unsigned i; - - for (i=0; i< count; i++) { - unsigned int strsize; - - strsize = strlen(pStr) + 1; - - if (strsize>MAX_STRING_SIZE) { - return ECA_STRTOBIG; - } - - pStr += MAX_STRING_SIZE; + getCallback *pNotify = new getCallback ( *pChan, pfunc, arg ); + if ( ! pNotify ) { + return ECA_ALLOCMEM; } - return ECA_NORMAL; -} - -/* - * malloc_put_convert() - */ -#ifdef CONVERSION_REQUIRED -LOCAL void *malloc_put_convert (cac *pcac, unsigned long size) -{ - struct putCvrtBuf *pBuf; - - LOCK (pcac); - while ( (pBuf = (struct putCvrtBuf *) ellGet(&pcac->putCvrtBuf)) ) { - if(pBuf->size >= size){ - break; - } - else { - free (pBuf); - } - } - UNLOCK (pcac); - - if (!pBuf) { - pBuf = (struct putCvrtBuf *) malloc (sizeof(*pBuf)+size); - if (!pBuf) { - return NULL; - } - pBuf->size = size; - pBuf->pBuf = (void *) (pBuf+1); - } - - return pBuf->pBuf; -} -#endif /* CONVERSION_REQUIRED */ - -/* - * free_put_convert() - */ -#ifdef CONVERSION_REQUIRED -LOCAL void free_put_convert(cac *pcac, void *pBuf) -{ - struct putCvrtBuf *pBufHdr; - - pBufHdr = (struct putCvrtBuf *)pBuf; - pBufHdr -= 1; - assert (pBufHdr->pBuf == (void *)(pBufHdr+1)); - - LOCK (pcac); - ellAdd (&pcac->putCvrtBuf, &pBufHdr->node); - UNLOCK (pcac); - - return; -} -#endif /* CONVERSION_REQUIRED */ - -/* - * issue_put() - */ -LOCAL int issue_put (ca_uint16_t cmd, unsigned id, nciu *chan, chtype type, - unsigned long count, const void *pvalue) -{ - int status; - caHdr hdr; - unsigned postcnt; - ca_uint16_t type_u16; - ca_uint16_t count_u16; -# ifdef CONVERSION_REQUIRED - void *pCvrtBuf; -# endif /*CONVERSION_REQUIRED*/ - tcpiiu *piiu; - - /* - * fail out if the conn is down or the arguments are otherwise invalid - */ - if (!chan->connected) { - return ECA_DISCONNCHID; - } - if (INVALID_DB_REQ(type)) { - return ECA_BADTYPE; - } - /* - * compound types not allowed - */ - if (dbr_value_offset[type]) { - return ECA_BADTYPE; - } - if (!chan->ar.write_access) { - return ECA_NOWTACCESS; - } - if ( count > chan->count || count > 0xffff || count == 0 ) { - return ECA_BADCOUNT; - } - if (type==DBR_STRING) { - status = check_a_dbr_string ( (char *) pvalue, count ); - if (status != ECA_NORMAL) { - return status; - } - } - postcnt = dbr_size_n (type,count); - if (postcnt>0xffff) { - return ECA_TOLARGE; - } - - /* - * only after range checking type and count cast - * them down to a smaller size - */ - type_u16 = (ca_uint16_t) type; - count_u16 = (ca_uint16_t) count; - - if (type == DBR_STRING && count == 1) { - char *pstr = (char *)pvalue; - - postcnt = strlen(pstr)+1; - } - -# ifdef CONVERSION_REQUIRED - { - unsigned i; - void *pdest; - unsigned size_of_one; - - size_of_one = dbr_size[type]; - - pCvrtBuf = pdest = malloc_put_convert (chan->ciu.piiu->pcas, postcnt); - if (!pdest) { - return ECA_ALLOCMEM; - } - - /* - * No compound types here because these types are read only - * and therefore only appropriate for gets or monitors - * - * I changed from a for to a while loop here to avoid bounds - * checker pointer out of range error, and unused pointer - * update when it is a single element. - */ - i=0; - while (TRUE) { - switch (type) { - case DBR_LONG: - *(dbr_long_t *)pdest = htonl (*(dbr_long_t *)pvalue); - break; - - case DBR_CHAR: - *(dbr_char_t *)pdest = *(dbr_char_t *)pvalue; - break; - - case DBR_ENUM: - case DBR_SHORT: - case DBR_PUT_ACKT: - case DBR_PUT_ACKS: -# if DBR_INT != DBR_SHORT -# error DBR_INT != DBR_SHORT ? -# endif /*DBR_INT != DBR_SHORT*/ - *(dbr_short_t *)pdest = htons (*(dbr_short_t *)pvalue); - break; - - case DBR_FLOAT: - dbr_htonf ((dbr_float_t *)pvalue, (dbr_float_t *)pdest); - break; - - case DBR_DOUBLE: - dbr_htond ((dbr_double_t *)pvalue, (dbr_double_t *)pdest); - break; - - case DBR_STRING: - /* - * string size checked above - */ - strcpy ( (char *) pdest, (char *) pvalue ); - break; - - default: - return ECA_BADTYPE; - } - - if (++i>=count) { - break; - } - - pdest = ((char *)pdest) + size_of_one; - pvalue = ((char *)pvalue) + size_of_one; - } - - pvalue = pCvrtBuf; - } -# endif /*CONVERSION_REQUIRED*/ - - hdr.m_cmmd = htons (cmd); - hdr.m_dataType = htons (type_u16); - hdr.m_count = htons (count_u16); - hdr.m_cid = chan->sid; - hdr.m_available = id; - hdr.m_postsize = (ca_uint16_t) postcnt; - - piiu = iiuToTCPIIU (chan->ciu.piiu); - status = cac_push_tcp_msg (piiu, &hdr, pvalue); - -# ifdef CONVERSION_REQUIRED - free_put_convert (chan->ciu.piiu->pcas, pCvrtBuf); -# endif /*CONVERSION_REQUIRED*/ - - return status; -} - -/* - * cac_put_notify_action - */ -extern "C" void cac_put_notify_action (void *pPrivate) -{ - lciu *pChan = (lciu *) pPrivate; - lclIIU *pliiu = iiuToLIIU (pChan->ciu.piiu); - - /* - * independent lock used here in order to - * avoid any possibility of blocking - * the database (or indirectly blocking - * one client on another client). - */ - semMutexMustTake (pliiu->putNotifyLock); - ellAdd (&pliiu->putNotifyQue, &pChan->ppn->node); - semMutexGive (pliiu->putNotifyLock); - - /* - * offload the labor for this to the - * event task so that we never block - * the db or another client. - */ - (*pliiu->pva->p_pvEventQueuePostExtraLabor) (pliiu->evctx); -} - -/* - * localPutNotifyInitiate () - */ -int localPutNotifyInitiate (lciu *pChan, chtype type, unsigned long count, - const void *pValue, caEventCallBackFunc *pCallback, void *usrArg) -{ - lclIIU *pliiu = iiuToLIIU (pChan->ciu.piiu); - unsigned size; - long dbStatus; - - size = dbr_size_n (type, count); - - LOCK (pChan->ciu.piiu->pcas); - - if (pChan->ppn) { - /* - * wait while it is busy - */ - while (pChan->ppn->dbPutNotify) { - semTakeStatus semStatus; - UNLOCK (pChan->ciu.piiu->pcas); - semStatus = semBinaryTakeTimeout ( - pChan->ciu.piiu->pcas->ca_blockSem, 60.0); - if (semStatus != semTakeOK) { - return ECA_PUTCBINPROG; - } - LOCK (pChan->ciu.piiu->pcas); - } - - /* - * once its not busy then free the current - * block if it is too small - */ - if ( pChan->ppn->valueSize < size ) { - free ( pChan->ppn ); - pChan->ppn = NULL; - } - } - - if ( !pChan->ppn ) { - - pChan->ppn = (caPutNotify *) calloc (1, sizeof(*pChan->ppn)+size); - if ( !pChan->ppn ) { - UNLOCK (pChan->ciu.piiu->pcas); - return ECA_ALLOCMEM; - } - } - pChan->ppn->pValue = pChan->ppn + 1; - memcpy (pChan->ppn->pValue, pValue, size); - pChan->ppn->caUserCallback = pCallback; - pChan->ppn->caUserArg = usrArg; - - dbStatus = (*pliiu->pva->p_pvPutNotifyInitiate) - (pChan->id, type, count, pChan->ppn->pValue, cac_put_notify_action, pChan, - &pChan->ppn->dbPutNotify); - UNLOCK (pChan->ciu.piiu->pcas); - if (dbStatus!=0 && dbStatus!=S_db_Pending) { - errMessage (dbStatus, "CAC: unable to initiate put callback\n"); - if (dbStatus==S_db_Blocked) { - return ECA_PUTCBINPROG; - } - return ECA_PUTFAIL; - } - return ECA_NORMAL; + return pChan->read ( type, count, *pNotify); } /* * ca_array_put_callback () */ int epicsShareAPI ca_array_put_callback (chtype type, unsigned long count, - chid pChanIn, const void *pvalue, caEventCallBackFunc *pfunc, void *usrarg) + chid pChan, const void *pvalue, caEventCallBackFunc *pfunc, void *usrarg) { - baseCIU *pChan = (baseCIU *) pChanIn; - int status; - unsigned id; - - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - lciu *pLocalChan = ciuToLCIU (pChan); - - return localPutNotifyInitiate (pLocalChan, type, count, pvalue, pfunc, usrarg); + putCallback *pNotify = new putCallback ( *pChan, pfunc, usrarg ); + if ( ! pNotify ) { + return ECA_ALLOCMEM; } - else { - nciu *pNetChan = ciuToNCIU (pChan); - tcpiiu *piiu; - nmiu *monix; - if ( !pNetChan->connected ) { - return ECA_DISCONNCHID; - } - - piiu = iiuToTCPIIU (pChan->piiu); - - if (!CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number)) { - return ECA_NOSUPPORT; - } - - /* - * lock around io block create and list add - * so that we are not deleted without - * reclaiming the resource - */ - LOCK (pChan->piiu->pcas); - - monix = caIOBlockCreate (pNetChan, CA_PROTO_WRITE_NOTIFY, - type, count, pfunc, usrarg); - if (!monix) { - UNLOCK (pChan->piiu->pcas); - return ECA_ALLOCMEM; - } - - id = monix->id; - - UNLOCK (pChan->piiu->pcas); - - status = issue_put (CA_PROTO_WRITE_NOTIFY, id, pNetChan, type, count, pvalue); - if (status!=ECA_NORMAL && status!=ECA_DISCONNCHID) { - /* - * we need to be careful about touching the monix - * pointer after the lock has been released - */ - caIOBlockFree (pChan->piiu->pcas, id); - } - return status; - } + return pChan->write ( type, count, pvalue, *pNotify ); } /* * ca_array_put () */ -int epicsShareAPI ca_array_put (chtype type, unsigned long count, chid pChanIn, const void *pvalue) +int epicsShareAPI ca_array_put (chtype type, unsigned long count, chid pChan, const void *pvalue) { - baseCIU *pChan = (baseCIU *) pChanIn; - - /* - * If channel is on this client's host then - * call the database directly - */ - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - lciu *pLocalChan = ciuToLCIU (pChan); - int status; - - status = (*pChan->piiu->pcas->localIIU.pva->p_pvPutField) (pLocalChan->id, - type, pvalue, count); - if (status) { - return ECA_PUTFAIL; - } - else { - return ECA_NORMAL; - } - } - else { - return issue_put (CA_PROTO_WRITE, ~0U, ciuToNCIU (pChan), type, count, pvalue); - } + return pChan->write ( type, count, pvalue); } /* * Specify an event subroutine to be run for connection events */ -int epicsShareAPI ca_change_connection_event (chid pChanIn, caCh *pfunc) +int epicsShareAPI ca_change_connection_event (chid pChan, caCh *pfunc) { - baseCIU *pChan = (baseCIU *) pChanIn; - - if (!pChan->piiu) { - return ECA_BADCHID; - } - - if (pChan->pConnFunc == pfunc) { - return ECA_NORMAL; - } - - LOCK (pChan->piiu->pcas); - if (pChan->piiu != &pChan->piiu->pcas->localIIU.iiu) { - nciu *pNetChan = ciuToNCIU (pChan); - - if (!pNetChan->previousConn) { - if (!pChan->pConnFunc) { - if (--pChan->piiu->pcas->ca_pndrecvcnt==0u) { - semBinaryGive (pChan->piiu->pcas->ca_io_done_sem); - } - } - if (!pfunc) { - pChan->piiu->pcas->ca_pndrecvcnt++; - } - } - } - pChan->pConnFunc = pfunc; - UNLOCK (pChan->piiu->pcas); - - return ECA_NORMAL; + return pChan->changeConnCallBack (pfunc); } /* * ca_replace_access_rights_event */ -int epicsShareAPI ca_replace_access_rights_event (chid pChanIn, caArh *pfunc) +int epicsShareAPI ca_replace_access_rights_event (chid pChan, caArh *pfunc) { - baseCIU *pChan = (baseCIU *) pChanIn; - struct access_rights_handler_args args; - caar ar; - int connected; - - if (!pChan->piiu) { - return ECA_BADCHID; - } - - LOCK (pChan->piiu->pcas); - pChan->pAccessRightsFunc = pfunc; - - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - connected = TRUE; - ar.read_access = TRUE; - ar.write_access = TRUE; - } - else { - nciu *pNetChan = ciuToNCIU (pChan); - if (pNetChan->connected) { - connected = TRUE; - } - else { - connected = FALSE; - } - ar = pNetChan->ar; - } - - /* - * make certain that it runs at least once - */ - if ( connected && pChan->pAccessRightsFunc ) { - args.chid = (chid) pChan; - args.ar = ar; - (*pChan->pAccessRightsFunc)(args); - } - - UNLOCK (pChan->piiu->pcas); - - return ECA_NORMAL; + return pChan->replaceAccessRightsEvent (pfunc); } /* @@ -1917,20 +334,28 @@ int epicsShareAPI ca_add_exception_event (caExceptionHandler *pfunc, void *arg) /* * ca_add_masked_array_event */ -int epicsShareAPI ca_add_masked_array_event (chtype type, unsigned long count, chid pChanIn, +int epicsShareAPI ca_add_masked_array_event (chtype type, unsigned long count, chid pChan, caEventCallBackFunc *pCallBack, void *pCallBackArg, ca_real p_delta, ca_real n_delta, ca_real timeout, evid *monixptr, long mask) { - baseCIU *pChan = (baseCIU *) pChanIn; - baseMIU *pMon; + static const long maskMask = USHRT_MAX; + oldSubscription *pSubsr; int status; - if (!pChan->piiu) { - return ECA_BADCHID; + if ( INVALID_DB_REQ (type) ) { + return ECA_BADTYPE; } - if ( INVALID_DB_REQ(type) ) { - return ECA_BADTYPE; + if ( pCallBack == NULL ) { + return ECA_BADFUNCPTR; + } + + if ( (mask & maskMask) == 0) { + return ECA_BADMASK; + } + + if ( mask & ~maskMask ) { + return ECA_BADMASK; } /* @@ -1943,157 +368,20 @@ int epicsShareAPI ca_add_masked_array_event (chtype type, unsigned long count, c * verifying that the requested count is valid here isnt * required) */ - if (dbr_size_n(type,count)>MAX_MSG_SIZE-sizeof(caHdr)) { + if ( dbr_size_n ( type, count ) > MAX_MSG_SIZE - sizeof ( caHdr ) ) { return ECA_TOLARGE; } - if (pCallBack==NULL) { - return ECA_BADFUNCPTR; + pSubsr = new oldSubscription (*pChan, pCallBack, pCallBackArg ); + if ( ! pSubsr ) { + return ECA_ALLOCMEM; } - if (mask&USHRT_MAX==0) { - return ECA_BADMASK; + status = pChan->subscribe ( type, count, mask, *pSubsr ); + if ( status == ECA_NORMAL ) { + *monixptr = pSubsr; } - /* - * lock around io block create and list add - * so that we are not deleted while - * creating the resource - */ - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - lciu *pLocalChan = ciuToLCIU (pChan); - lmiu *pLclMon; - - LOCK (pChan->piiu->pcas); - - pLclMon = (lmiu *) freeListMalloc (pChan->piiu->pcas->localIIU.localSubscrFreeListPVT); - if (!pLclMon) { - UNLOCK (pChan->piiu->pcas); - return ECA_ALLOCMEM; - } - - pLclMon->miu.type = type; - pLclMon->miu.usr_func = pCallBack; - pLclMon->miu.usr_arg = pCallBackArg; - pLclMon->miu.count = count; - pLclMon->miu.pChan = pChan; - - pLclMon->es = (*pChan->piiu->pcas->localIIU.pva->p_pvEventQueueAddEvent) - (pChan->piiu->pcas->localIIU.evctx, - pLocalChan->id, cac_lcl_event_handler, pLclMon, mask); - - if (!pLclMon->es) { - freeListFree (pChan->piiu->pcas->localIIU.localSubscrFreeListPVT, pLclMon); - UNLOCK (pChan->piiu->pcas); - return ECA_ALLOCMEM; - } - - ellAdd (&pLocalChan->eventq, &pLclMon->node); - - UNLOCK (pChan->piiu->pcas); - - (*pChan->piiu->pcas->localIIU.pva->p_pvEventQueuePostSingleEvent) (pLclMon->es); - - pMon = &pLclMon->miu; - } - else { - nciu *pNetChan = ciuToNCIU (pChan); - nmiu *pNetMon; - unsigned id; - - LOCK (pChan->piiu->pcas); - - pNetMon = caIOBlockCreate (pNetChan, CA_PROTO_EVENT_ADD, - type, count, pCallBack, pCallBackArg); - if (!pNetMon) { - UNLOCK (pChan->piiu->pcas); - return ECA_ALLOCMEM; - } - - pNetMon->p_delta = p_delta; - pNetMon->n_delta = n_delta; - pNetMon->timeout = timeout; - pNetMon->mask = (unsigned short) mask; - - id = pNetMon->id; - UNLOCK (pChan->piiu->pcas); - if (pNetChan->connected) { - status = ca_request_event (pNetChan, pNetMon); - if (status != ECA_NORMAL) { - if ( !pNetChan->connected ) { - caIOBlockFree (pNetChan->ciu.piiu->pcas, id); - } - return status; - } - } - pMon = &pNetMon->miu; - } - - if (monixptr) { - *monixptr = pMon; - } - - return ECA_NORMAL; -} - -/* - * CA_REQUEST_EVENT() - */ -int ca_request_event (nciu *pNetChan, nmiu *pNetMon) -{ - int status; - unsigned long count; - struct monops msg; - ca_float32_t p_delta; - ca_float32_t n_delta; - ca_float32_t tmo; - - /* - * dont send the message if the conn is down - * (it will be sent once connected) - */ - if (!pNetChan->connected) { - return ECA_DISCONNCHID; - } - - /* - * clip to the native count and set to the native count if they - * specify zero - */ - if (pNetMon->miu.count > pNetChan->count || pNetMon->miu.count == 0){ - count = pNetChan->count; - } - else { - count = pNetMon->miu.count; - } - - /* - * dont allow overflow when converting to ca_uint16_t - */ - if (count>0xffff) { - count = 0xffff; - } - - /* msg header */ - msg.m_header.m_cmmd = htons (CA_PROTO_EVENT_ADD); - msg.m_header.m_available = pNetMon->id; - msg.m_header.m_dataType = htons ( ((ca_uint16_t)pNetMon->miu.type) ); - msg.m_header.m_count = htons ( ((ca_uint16_t)count) ); - msg.m_header.m_cid = pNetChan->sid; - msg.m_header.m_postsize = sizeof (msg.m_info); - - /* msg body */ - p_delta = (ca_float32_t) pNetMon->p_delta; - n_delta = (ca_float32_t) pNetMon->n_delta; - tmo = (ca_float32_t) pNetMon->timeout; - dbr_htonf (&p_delta, &msg.m_info.m_hval); - dbr_htonf (&n_delta, &msg.m_info.m_lval); - dbr_htonf (&tmo, &msg.m_info.m_toval); - msg.m_info.m_mask = htons (pNetMon->mask); - msg.m_info.m_pad = 0; /* allow future use */ - - status = cac_push_tcp_msg (iiuToTCPIIU(pNetChan->ciu.piiu), &msg.m_header, &msg.m_info); - return status; } @@ -2113,324 +401,19 @@ int ca_request_event (nciu *pNetChan, nmiu *pNetMon) * after leaving this routine. * */ -int epicsShareAPI ca_clear_event (evid evidIn) +int epicsShareAPI ca_clear_event ( evid pMon ) { - baseMIU *pMon = (baseMIU *) evidIn; - int status; - - /* disable any further events from this event block */ - pMon->usr_func = NULL; - - /* - * is it a local event subscription ? - */ - if (pMon->pChan->piiu == &pMon->pChan->piiu->pcas->localIIU.iiu) { - localMonitorResourceDestroy (miuToLMIU (pMon)); - status = ECA_NORMAL; - } - else { - nmiu *pNetMIU = miuToNMIU (pMon); - nciu *pNetCIU = ciuToNCIU (pMon->pChan); - - if (pNetCIU->connected) { - caHdr hdr; - ca_uint16_t type, count; - - type = (ca_uint16_t) pNetCIU->type; - if (pNetCIU->count>0xffff) { - count = 0xffff; - } - else { - count = (ca_uint16_t) pNetCIU->count; - } - - hdr.m_cmmd = htons (CA_PROTO_EVENT_CANCEL); - hdr.m_available = pNetMIU->id; - hdr.m_dataType = htons (type); - hdr.m_count = htons (count); - hdr.m_cid = pNetCIU->sid; - hdr.m_postsize = 0; - - status = cac_push_tcp_msg (iiuToTCPIIU(pMon->pChan->piiu), &hdr, NULL); - } - else { - status = ECA_NORMAL; - } - - caIOBlockFree (pMon->pChan->piiu->pcas, pNetMIU->id); - } - - return status; + pMon->destroy (); + return ECA_NORMAL; } /* - * * ca_clear_channel () - * - * clear the resources allocated for a channel by search - * - * NOTE: returns before operation completes in the server - * (and the client). - * This is a good reason not to allow them to make the monix - * block part of a larger structure. - * Nevertheless the caller is gauranteed that his specified - * event is disabled and therefore will not run - * (from this source) after leaving this routine. - * */ -int epicsShareAPI ca_clear_channel (chid pChanIn) +int epicsShareAPI ca_clear_channel (chid pChan) { - baseCIU *pChan = (baseCIU *) pChanIn; - int status; - - /* - * is it a local channel ? - */ - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - localChannelDestroy (iiuToLIIU(pChan->piiu), ciuToLCIU(pChan)); - status = ECA_NORMAL; - } - else { - nciu *pNetCIU = ciuToNCIU (pChan); - - if (pNetCIU->connected) { - caHdr hdr; - - hdr.m_cmmd = htons (CA_PROTO_CLEAR_CHANNEL); - hdr.m_available = pNetCIU->cid; - hdr.m_cid = pNetCIU->sid; - hdr.m_dataType = htons (0); - hdr.m_count = htons (0); - hdr.m_postsize = 0; - - status = cac_push_tcp_msg (iiuToTCPIIU(pChan->piiu), &hdr, NULL); - } - else { - status = ECA_NORMAL; - } - - netChannelDestroy (pChan->piiu->pcas, pNetCIU->cid); - } - return status; -} - -/* - * netChannelDestroy () - */ -void netChannelDestroy (cac *pcac, unsigned id) -{ - nciu *chan; - nmiu *monix; - nmiu *next; - - LOCK (pcac); - - chan = (nciu *) bucketLookupAndRemoveItemUnsignedId (pcac->ca_pSlowBucket, &id); - if (chan==NULL) { - UNLOCK (pcac); - genLocalExcep (pcac, ECA_BADCHID, "netChannelDestroy()"); - return; - } - - /* - * if this channel does not have a connection handler - * and it has not connected for the first time then clear the - * outstanding IO count - */ - if (!chan->previousConn && !chan->ciu.pConnFunc) { - if (--pcac->ca_pndrecvcnt==0u) { - semBinaryGive (pcac->ca_io_done_sem); - } - } - - /* - * remove any IO blocks still attached to this channel - */ - for (monix = (nmiu *) ellFirst (&chan->eventq); - monix; monix = next) { - - next = (nmiu *) ellNext (&monix->node); - - caIOBlockFree (pcac, monix->id); - } - - removeFromChanList (chan); - - /* - * attempt to catch use of this channel after it - * is returned to pool - */ - chan->ciu.piiu = NULL; - - free (chan); - - UNLOCK (pcac); -} - -#if 0 -/* - * cacSetPushPending () - */ -LOCAL void cacSetPushPending (tcpiiu *piiu) -{ - unsigned long sendCnt; - - cacRingBufferWriteFlush (&piiu->send); - - sendCnt = cacRingBufferReadSize (&piiu->send, TRUE); - if (sendCnt) { - piiu->pushPending = TRUE; - cacSetSendPending (piiu); - } -} -#endif - -/* - * cacFlushAllIIU () - */ -void cacFlushAllIIU (cac *pcac) -{ - tcpiiu *piiu; - - /* - * set the push pending flag on all virtual circuits - */ - LOCK (pcac); - for ( piiu = (tcpiiu *) ellFirst (&pcac->ca_iiuList); - piiu; piiu = (tcpiiu *) ellNext (&piiu->node) ) { - cacRingBufferWriteFlush (&piiu->send); - } - UNLOCK (pcac); -} - -/* - * cacNoopConnHandler () - * This is installed into channels which dont have - * a connection handler when ca_pend_io() times - * out so that we will not decrement the pending - * recv count in the future. - */ -extern "C" void cacNoopConnHandler (struct connection_handler_args) -{ -} - -/* - * - * set pending IO count back to zero and - * send a sync to each IOC and back. dont - * count reads until we recv the sync - * - */ -LOCAL void ca_pend_io_cleanup (cac *pcac) -{ - tcpiiu *piiu; - nciu *pchan; - - LOCK (pcac); - - pchan = (nciu *) ellFirst (&pcac->pudpiiu->niiu.chidList); - while (pchan) { - if (!pchan->ciu.pConnFunc) { - pchan->ciu.pConnFunc = cacNoopConnHandler; - } - pchan = (nciu *) ellNext (&pchan->node); - } - - for (piiu = (tcpiiu *) ellFirst (&pcac->ca_iiuList); - piiu; piiu = (tcpiiu *) ellNext (&piiu->node.next) ){ - - caHdr hdr; - - if ( piiu->state != iiu_connected ) { - continue; - } - - piiu->cur_read_seq++; - - hdr = nullmsg; - hdr.m_cmmd = htons (CA_PROTO_READ_SYNC); - cac_push_tcp_msg (piiu, &hdr, NULL); - } - UNLOCK (pcac); - pcac->ca_pndrecvcnt = 0u; -} - -/* - * ca_pend_private () - */ -static int ca_pend_private (cac *pcac, ca_real timeout, int early) -{ - TS_STAMP beg_time; - TS_STAMP cur_time; - double delay; - int caStatus; - - caStatus = tsStampGetCurrent (&cur_time); - if (caStatus != 0) { - return ECA_INTERNAL; - } - - LOCK (pcac); - pcac->currentTime = cur_time; - UNLOCK (pcac); - - cacFlushAllIIU (pcac); - - if (pcac->ca_pndrecvcnt==0u && early) { - return ECA_NORMAL; - } - - if (timeout<0.0) { - if (early) ca_pend_io_cleanup (pcac); - return ECA_TIMEOUT; - } - - beg_time = cur_time; - delay = 0.0; - while (TRUE) { - ca_real remaining; - - if(timeout == 0.0){ - remaining = 60.0; - } - else{ - remaining = timeout-delay; - remaining = min (60.0, remaining); - - /* - * If we are not waiting for any significant delay - * then force the delay to zero so that we avoid - * scheduling delays (which can be substantial - * on some os) - */ - if (remaining <= CAC_SIGNIFICANT_SELECT_DELAY) { - if (early) ca_pend_io_cleanup (pcac); - return ECA_TIMEOUT; - } - } - - /* recv occurs in another thread */ - semBinaryTakeTimeout (pcac->ca_io_done_sem, remaining); - - if (pcac->ca_pndrecvcnt==0 && early) { - return ECA_NORMAL; - } - - /* force time update */ - caStatus = tsStampGetCurrent (&cur_time); - if (caStatus != 0) { - if (early) ca_pend_io_cleanup (pcac); - return ECA_INTERNAL; - } - - LOCK (pcac); - pcac->currentTime = cur_time; - UNLOCK (pcac); - - if (timeout != 0.0) { - delay = tsStampDiffInSeconds (&cur_time, &beg_time); - } - } + pChan->destroy (); + return ECA_NORMAL; } /* @@ -2440,67 +423,29 @@ int epicsShareAPI ca_pend (ca_real timeout, int early) { cac *pcac; int status; - void *p; status = fetchClientContext (&pcac); if ( status != ECA_NORMAL ) { return status; } - /* - * dont allow recursion - */ - p = threadPrivateGet (cacRecursionLock); - if (p) { - return ECA_EVDISALLOW; - } - - threadPrivateSet (cacRecursionLock, &cacRecursionLock); - - status = ca_pend_private (pcac, timeout, early); - - threadPrivateSet (cacRecursionLock, NULL); - - return status; + return pcac->pend (timeout, early); } -#if 0 /* - * cac_fetch_poll_period() - */ -double cac_fetch_poll_period (cac *pcac) -{ - if (!pcac->pudpiiu) { - return SELECT_POLL_NO_SEARCH; - } - else if ( ellCount (&pcac->pudpiiu->niiu.chidList) == 0 ) { - return SELECT_POLL_NO_SEARCH; - } - else { - return SELECT_POLL_SEARCH; - } -} -#endif /* #if 0 */ - -/* - * CA_FLUSH_IO() - * reprocess connection state and - * flush the send buffer - * - * Wait for all send buffers to be flushed - * while performing socket io and processing recv backlog + * ca_flush_io () */ int epicsShareAPI ca_flush_io () { - int caStatus; - cac *pcac; + int caStatus; + cac *pcac; caStatus = fetchClientContext (&pcac); if ( caStatus != ECA_NORMAL ) { return caStatus; } - cacFlushAllIIU (pcac); + pcac->flush (); return ECA_NORMAL; } @@ -2510,15 +455,15 @@ int epicsShareAPI ca_flush_io () */ int epicsShareAPI ca_test_io () { - int caStatus; - cac *pcac; + int caStatus; + cac *pcac; caStatus = fetchClientContext (&pcac); if ( caStatus != ECA_NORMAL ) { return caStatus; } - if (pcac->ca_pndrecvcnt==0u) { + if ( pcac->ioComplete () ) { return ECA_IODONE; } else{ @@ -2630,25 +575,21 @@ void epicsShareAPI ca_signal_formated (long ca_status, const char *pfilenm, va_start (theArgs, pFormat); - ca_printf (pcac, - "CA.Client.Diagnostic..............................................\n"); + ca_printf ("CA.Client.Diagnostic..............................................\n"); - ca_printf (pcac, - " %s: \"%s\"\n", + ca_printf (" %s: \"%s\"\n", severity[CA_EXTRACT_SEVERITY(ca_status)], ca_message (ca_status)); if (pFormat) { - ca_printf (pcac, " Context: \""); - ca_vPrintf (pcac, pFormat, theArgs); - ca_printf (pcac, "\"\n"); + ca_printf (" Context: \""); + ca_vPrintf (pFormat, theArgs); + ca_printf ("\"\n"); } if (pfilenm) { - ca_printf (pcac, - " Source File: %s Line Number: %d\n", - pfilenm, - lineno); + ca_printf (" Source File: %s Line Number: %d\n", + pfilenm, lineno); } /* @@ -2659,242 +600,11 @@ void epicsShareAPI ca_signal_formated (long ca_status, const char *pfilenm, abort(); } - ca_printf (pcac, - "..................................................................\n"); + ca_printf ("..................................................................\n"); va_end (theArgs); } - -/* - * ca_busy_message () - */ -int ca_busy_message (tcpiiu *piiu) -{ - caHdr hdr; - int status; - - hdr = nullmsg; - hdr.m_cmmd = htons (CA_PROTO_EVENTS_OFF); - hdr.m_postsize = 0; - - status = cac_push_tcp_msg (piiu, &hdr, NULL); - if (status == ECA_NORMAL) { - cacRingBufferWriteFlush (&piiu->send); - } - return status; -} - -/* - * ca_ready_message () - */ -int ca_ready_message (tcpiiu *piiu) -{ - caHdr hdr; - int status; - - hdr = nullmsg; - hdr.m_cmmd = htons (CA_PROTO_EVENTS_ON); - hdr.m_postsize = 0; - - status = cac_push_tcp_msg (piiu, &hdr, NULL); - if (status == ECA_NORMAL) { - cacRingBufferWriteFlush (&piiu->send); - } - return status; -} - -/* - * echo_request () - */ -void echo_request (tcpiiu *piiu) -{ - caHdr hdr; - - hdr.m_cmmd = htons (CA_PROTO_ECHO); - hdr.m_dataType = htons (0); - hdr.m_count = htons (0); - hdr.m_cid = htons (0); - hdr.m_available = htons (0); - hdr.m_postsize = 0u; - - /* - * If we are out of buffer space then postpone this - * operation until later. This avoids any possibility - * of a push pull deadlock (since this can be sent when - * parsing the UDP input buffer). - */ - if (cac_push_tcp_msg_no_block (piiu, &hdr, NULL)) { - piiu->echoPending = TRUE; - LOCK (piiu->niiu.iiu.pcas); - piiu->timeAtEchoRequest = piiu->niiu.iiu.pcas->currentTime; - UNLOCK (piiu->niiu.iiu.pcas) - cacRingBufferWriteFlush (&piiu->send); - } -} - -/* - * noop_msg () - */ -void noop_msg (tcpiiu *piiu) -{ - caHdr hdr; - bool status; - - hdr.m_cmmd = htons (CA_PROTO_NOOP); - hdr.m_dataType = htons (0); - hdr.m_count = htons (0); - hdr.m_cid = htons (0); - hdr.m_available = htons (0); - hdr.m_postsize = 0; - - status = cac_push_tcp_msg_no_block (piiu, &hdr, NULL); - if (status) { - cacRingBufferWriteFlush (&piiu->send); - } -} - -/* - * issue_client_host_name () - */ -void issue_client_host_name (tcpiiu *piiu) -{ - unsigned size; - caHdr hdr; - char *pName; - - if (!CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number)) { - return; - } - - /* - * allocate space in the outgoing buffer - */ - pName = piiu->niiu.iiu.pcas->ca_pHostName, - size = strlen (pName) + 1; - hdr = nullmsg; - hdr.m_cmmd = htons(CA_PROTO_HOST_NAME); - hdr.m_postsize = size; - - cac_push_tcp_msg (piiu, &hdr, pName); - - return; -} - -/* - * issue_identify_client () - */ -void issue_identify_client (tcpiiu *piiu) -{ - unsigned size; - caHdr hdr; - char *pName; - - if (!CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number)) { - return; - } - - /* - * allocate space in the outgoing buffer - */ - pName = piiu->niiu.iiu.pcas->ca_pUserName, - size = strlen (pName) + 1; - hdr = nullmsg; - hdr.m_cmmd = htons (CA_PROTO_CLIENT_NAME); - hdr.m_postsize = size; - - cac_push_tcp_msg (piiu, &hdr, pName); - - return; -} - -/* - * issue_claim_channel () - */ -bool issue_claim_channel (nciu *pchan) -{ - tcpiiu *pNetIIU; - caHdr hdr; - unsigned size; - const char *pStr; - bool success; - - LOCK (pchan->ciu.piiu->pcas); - - if (pchan->ciu.piiu == &pchan->ciu.piiu->pcas->pudpiiu->niiu.iiu) { - ca_printf (pchan->ciu.piiu->pcas, - "CAC: UDP claim attempted?\n"); - UNLOCK (pchan->ciu.piiu->pcas); - return false; - } - - pNetIIU = iiuToTCPIIU (pchan->ciu.piiu); - - if (!pchan->claimPending) { - ca_printf (pchan->ciu.piiu->pcas, - "CAC: duplicate claim attempted (while disconnected)?\n"); - UNLOCK (pchan->ciu.piiu->pcas); - return false; - } - - if (pchan->connected) { - ca_printf (pchan->ciu.piiu->pcas, - "CAC: duplicate claim attempted (while connected)?\n"); - UNLOCK (pchan->ciu.piiu->pcas); - return false; - } - - hdr = nullmsg; - hdr.m_cmmd = htons (CA_PROTO_CLAIM_CIU); - - if (CA_V44(CA_PROTOCOL_VERSION, pNetIIU->minor_version_number)) { - hdr.m_cid = pchan->cid; - pStr = ca_name (&pchan->ciu); - size = pchan->nameLength; - } - else { - hdr.m_cid = pchan->sid; - pStr = NULL; - size = 0u; - } - - hdr.m_postsize = size; - - /* - * The available field is used (abused) - * here to communicate the minor version number - * starting with CA 4.1. - */ - hdr.m_available = htonl (CA_MINOR_VERSION); - - /* - * If we are out of buffer space then postpone this - * operation until later. This avoids any possibility - * of a push pull deadlock (since this is sent when - * parsing the UDP input buffer). - */ - success = cac_push_tcp_msg_no_block (pNetIIU, &hdr, pStr); - if ( success ) { - - /* - * move to the end of the list once the claim has been sent - */ - pchan->claimPending = FALSE; - ellDelete (&pNetIIU->niiu.chidList, &pchan->node); - ellAdd (&pNetIIU->niiu.chidList, &pchan->node); - - if (!CA_V42(CA_PROTOCOL_VERSION, pNetIIU->minor_version_number)) { - cac_reconnect_channel (pNetIIU, pchan->cid, USHRT_MAX, 0); - } - } - else { - pNetIIU->claimsPending = TRUE; - } - UNLOCK (pchan->ciu.piiu->pcas); - - return success; -} - /* * CA_ADD_FD_REGISTRATION * @@ -2928,67 +638,34 @@ int epicsShareAPI ca_add_fd_registration(CAFDHANDLER *func, void *arg) */ int ca_defunct() { - SEVCHK (ECA_DEFUNCT, NULL); + SEVCHK ( ECA_DEFUNCT, NULL ); return ECA_DEFUNCT; } /* - * CA_HOST_NAME() - * - * returns a pointer to the channel's host name - * - * currently implemented as a function - * (may be implemented as a MACRO in the future) + * ca_get_host_name () */ -const char * epicsShareAPI ca_host_name (chid pChanIn) +const void epicsShareAPI ca_get_host_name ( chid pChan, char *pBuf, unsigned bufLength ) { - baseCIU *pChan = (baseCIU *) pChanIn; - - /* - * is it a local channel ? - */ - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - return pChan->piiu->pcas->ca_pHostName; - } - else { - nciu *pNetChan = (nciu *) ciuToNCIU (pChan); - - if (pNetChan->connected) { - tcpiiu *piiu = iiuToTCPIIU (pChan->piiu); - return piiu->host_name_str; - } - else { - return ""; - } - } + pChan->hostName ( pBuf, bufLength ); +} +/* + * ca_host_name () + */ +const char * epicsShareAPI ca_host_name ( chid pChan ) +{ + static char hostNameBuf [256]; + pChan->hostName ( hostNameBuf, sizeof ( hostNameBuf ) ); + return hostNameBuf; } /* * ca_v42_ok(chid chan) */ -int epicsShareAPI ca_v42_ok (chid pChanIn) +int epicsShareAPI ca_v42_ok (chid pChan) { - baseCIU *pChan = (baseCIU *) pChanIn; - - /* - * is it a local channel ? - */ - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - return TRUE; - } - else { - nciu *pNetChan = (nciu *) ciuToNCIU (pChan); - - if (pNetChan->connected) { - tcpiiu *piiu = iiuToTCPIIU (pChan->piiu); - return CA_V42 (CA_PROTOCOL_VERSION, - piiu->minor_version_number); - } - else { - return FALSE; - } - } + return pChan->ca_v42_ok (); } /* @@ -3028,14 +705,14 @@ int epicsShareAPI ca_replace_printf_handler (caPrintfFunc *ca_printf_func) /* * ca_printf() */ -int ca_printf (cac *pcac, const char *pformat, ...) +int ca_printf (const char *pformat, ...) { va_list theArgs; - int status; - + int status; + va_start (theArgs, pformat); - status = ca_vPrintf (pcac, pformat, theArgs); + status = ca_vPrintf (pformat, theArgs); va_end (theArgs); @@ -3043,14 +720,15 @@ int ca_printf (cac *pcac, const char *pformat, ...) } /* - * ca_vPrintf() + * ca_vPrintf() */ -int ca_vPrintf (cac *pcac, const char *pformat, va_list args) +int ca_vPrintf (const char *pformat, va_list args) { caPrintfFunc *ca_printf_func; - - if (pcac) { - if (pcac->ca_printf_func) { + + if ( caClientContextId ) { + cac *pcac = (cac *) threadPrivateGet (caClientContextId); + if (pcac) { ca_printf_func = pcac->ca_printf_func; } else { @@ -3060,173 +738,78 @@ int ca_vPrintf (cac *pcac, const char *pformat, va_list args) else { ca_printf_func = errlogVprintf; } - - return (*ca_printf_func) (pformat, args); + + return (*ca_printf_func) ( pformat, args ); } /* * ca_field_type() */ -short epicsShareAPI ca_field_type (chid pChanIn) +short epicsShareAPI ca_field_type (chid pChan) { - baseCIU *pChan = (baseCIU *) pChanIn; - - /* - * is it a local channel ? - */ - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - lciu *pLocalChan = ciuToLCIU (pChan); - return (*pChan->piiu->pcas->localIIU.pva->p_pvType) (pLocalChan->id); - } - else { - nciu *pNetChan = ciuToNCIU (pChan); - if (pNetChan->connected) { - return (short) pNetChan->type; - } - else { - return TYPENOTCONN; - } - } + return pChan->nativeType (); } /* * ca_element_count () */ -unsigned long epicsShareAPI ca_element_count (chid pChanIn) +unsigned long epicsShareAPI ca_element_count (chid pChan) { - baseCIU *pChan = (baseCIU *) pChanIn; - - /* - * is it a local channel ? - */ - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - lciu *pLocalChan = ciuToLCIU (pChan); - return (*pChan->piiu->pcas->localIIU.pva->p_pvNoElements) (pLocalChan->id); - } - else { - nciu *pNetChan = ciuToNCIU (pChan); - if (pNetChan->connected) { - return pNetChan->count; - } - else { - return 0; - } - } + return pChan->nativeElementCount (); } /* * ca_state () */ -epicsShareFunc enum channel_state epicsShareAPI ca_state (chid pChanIn) +epicsShareFunc enum channel_state epicsShareAPI ca_state (chid pChan) { - baseCIU *pChan = (baseCIU *) pChanIn; - - if ( pChan->piiu == &pChan->piiu->pcas->localIIU.iiu ) { - return cs_conn; - } - else if ( pChan->piiu == NULL ) { - return cs_closed; - } - else { - nciu *pNChan = ciuToNCIU (pChan); - if (pNChan->connected) { - return cs_conn; - } - else if (pNChan->previousConn) { - return cs_prev_conn; - } - else { - return cs_never_conn; - } - } + return pChan->state (); } /* * ca_set_puser () */ -epicsShareFunc void epicsShareAPI ca_set_puser (chid pChanIn, void *puser) +epicsShareFunc void epicsShareAPI ca_set_puser (chid pChan, void *puser) { - baseCIU *pChan = (baseCIU *) pChanIn; - pChan->puser = puser; + pChan->setPrivatePointer (puser); } /* * ca_get_puser () */ -epicsShareFunc void * epicsShareAPI ca_puser (chid pChanIn) +epicsShareFunc void * epicsShareAPI ca_puser (chid pChan) { - baseCIU *pChan = (baseCIU *) pChanIn; - return pChan->puser; + return pChan->privatePointer (); } /* * ca_read_access () */ -epicsShareFunc unsigned epicsShareAPI ca_read_access (chid pChanIn) +epicsShareFunc unsigned epicsShareAPI ca_read_access (chid pChan) { - baseCIU *pChan = (baseCIU *) pChanIn; - - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - return TRUE; - } - else { - nciu *pNetChan = ciuToNCIU (pChan); - return pNetChan->ar.read_access; - } + return pChan->readAccess (); } /* * ca_write_access () */ -epicsShareFunc unsigned epicsShareAPI ca_write_access (chid pChanIn) +epicsShareFunc unsigned epicsShareAPI ca_write_access (chid pChan) { - baseCIU *pChan = (baseCIU *) pChanIn; - - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - return TRUE; - } - else { - nciu *pNetChan = ciuToNCIU (pChan); - return pNetChan->ar.write_access; - } + return pChan->writeAccess (); } /* * ca_name () */ -epicsShareFunc const char * epicsShareAPI ca_name (chid pChanIn) +epicsShareFunc const char * epicsShareAPI ca_name (chid pChan) { - baseCIU *pChan = (baseCIU *) pChanIn; - - /* - * is it a local channel ? - */ - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - lciu *pLocalChan = ciuToLCIU (pChan); - lclIIU *pLocalIIU = iiuToLIIU (pChan->piiu); - return (*pLocalIIU->pva->p_pvName) (pLocalChan->id); - } - else { - nciu *pNetChan = ciuToNCIU (pChan); - return (const char *) (pNetChan+1); - } + return pChan->pName (); } -epicsShareFunc unsigned epicsShareAPI ca_search_attempts (chid pChanIn) +epicsShareFunc unsigned epicsShareAPI ca_search_attempts (chid pChan) { - baseCIU *pChan = (baseCIU *) pChanIn; - - /* - * is it a local channel ? - */ - if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { - return 0; - } - else { - nciu *pNetChan = ciuToNCIU (pChan); - return pNetChan->retry; - } + return pChan->searchAttempts (); } /* @@ -3237,35 +820,32 @@ epicsShareFunc unsigned epicsShareAPI ca_search_attempts (chid pChanIn) */ unsigned epicsShareAPI ca_get_ioc_connection_count () { - unsigned count; - cac *pcac; - int caStatus; + cac *pcac; + int caStatus; caStatus = fetchClientContext (&pcac); if ( caStatus != ECA_NORMAL ) { return caStatus; } - LOCK (pcac); - - count = ellCount (&pcac->ca_iiuList); - - UNLOCK (pcac); - - return count; + return pcac->connectionCount (); } -LOCAL void niiuShow (netIIU *piiu, unsigned /* level */) +void netiiu::show (unsigned /* level */) { - nciu *pChan; + nciu *pChan; - pChan = (nciu *) ellFirst (&piiu->chidList); - while ( pChan ) { + LOCK (this->pcas); + + tsDLIter iter (this->pcas->pudpiiu->chidList); + while ( ( pChan = iter () ) ) { + char hostName [256]; printf( "%s native type=%d ", - ca_name (&pChan->ciu), ca_field_type (&pChan->ciu)); + pChan->pName (), pChan->nativeType () ); + pChan->hostName ( hostName, sizeof (hostName) ); printf( "N elements=%lu server=%s state=", - ca_element_count (&pChan->ciu), ca_host_name(&pChan->ciu)); - switch ( ca_state (&pChan->ciu) ) { + pChan->nativeElementCount (), hostName ); + switch ( pChan->state () ) { case cs_never_conn: printf ("never connected to an IOC"); break; @@ -3282,31 +862,23 @@ LOCAL void niiuShow (netIIU *piiu, unsigned /* level */) break; } printf("\n"); - pChan = (nciu *) ellNext (&pChan->node); } + + UNLOCK (this->pcas); } epicsShareFunc int epicsShareAPI ca_channel_status (threadId /* tid */) { - tcpiiu *piiu; - cac *pcac; - int caStatus; + cac *pcac; + int caStatus; caStatus = fetchClientContext (&pcac); if ( caStatus != ECA_NORMAL ) { return caStatus; } - LOCK (pcac); - if (pcac->pudpiiu) { - niiuShow (&pcac->pudpiiu->niiu, 10); - } - piiu = (tcpiiu *) ellFirst (&pcac->ca_iiuList); - while (piiu) { - niiuShow (&piiu->niiu, 10); - piiu = (tcpiiu *) ellNext (&piiu->node); - } - UNLOCK (pcac); + pcac->show (10u); + return ECA_NORMAL; } @@ -3345,7 +917,9 @@ epicsShareFunc int epicsShareAPI ca_attach_context (caClientCtx context) return ECA_NORMAL; } -epicsShareDef const int epicsTypeToDBR_XXXX [lastEpicsType+1] = { +extern "C" { + +extern epicsShareDef const int epicsTypeToDBR_XXXX [lastEpicsType+1] = { DBR_SHORT, /* forces conversion fronm uint8 to int16 */ DBR_CHAR, DBR_SHORT, @@ -3359,7 +933,7 @@ epicsShareDef const int epicsTypeToDBR_XXXX [lastEpicsType+1] = { DBR_STRING }; -epicsShareDef READONLY epicsType DBR_XXXXToEpicsType [LAST_BUFFER_TYPE+1] = { +extern epicsShareDef READONLY epicsType DBR_XXXXToEpicsType [LAST_BUFFER_TYPE+1] = { epicsOldStringT, epicsInt16T, epicsFloat32T, @@ -3406,7 +980,7 @@ epicsShareDef READONLY epicsType DBR_XXXXToEpicsType [LAST_BUFFER_TYPE+1] = { epicsOldStringT }; -epicsShareDef const unsigned short dbr_size[LAST_BUFFER_TYPE+1] = { +extern epicsShareDef const unsigned short dbr_size[LAST_BUFFER_TYPE+1] = { sizeof(dbr_string_t), /* string max size */ sizeof(dbr_short_t), /* short */ sizeof(dbr_float_t), /* IEEE Float */ @@ -3448,7 +1022,7 @@ epicsShareDef const unsigned short dbr_size[LAST_BUFFER_TYPE+1] = { sizeof(dbr_string_t), /* string max size */ }; -epicsShareDef const unsigned short dbr_value_size[LAST_BUFFER_TYPE+1] = { +extern epicsShareDef const unsigned short dbr_value_size[LAST_BUFFER_TYPE+1] = { sizeof(dbr_string_t), /* string max size */ sizeof(dbr_short_t), /* short */ sizeof(dbr_float_t), /* IEEE Float */ @@ -3490,7 +1064,7 @@ epicsShareDef const unsigned short dbr_value_size[LAST_BUFFER_TYPE+1] = { sizeof(dbr_string_t), /* string max size */ }; -epicsShareDef const enum dbr_value_class dbr_value_class[LAST_BUFFER_TYPE+1] = { +extern epicsShareDef const enum dbr_value_class dbr_value_class[LAST_BUFFER_TYPE+1] = { dbr_class_string, /* string max size */ dbr_class_int, /* short */ dbr_class_float, /* IEEE Float */ @@ -3536,7 +1110,7 @@ epicsShareDef const enum dbr_value_class dbr_value_class[LAST_BUFFER_TYPE+1] = { dbr_class_string, /* string max size */ }; -epicsShareDef const unsigned short dbr_value_offset[LAST_BUFFER_TYPE+1] = { +extern epicsShareDef const unsigned short dbr_value_offset[LAST_BUFFER_TYPE+1] = { 0, /* string */ 0, /* short */ 0, /* IEEE Float */ @@ -3578,7 +1152,7 @@ epicsShareDef const unsigned short dbr_value_offset[LAST_BUFFER_TYPE+1] = { 0, /* string */ }; -epicsShareDef const char *db_field_text[] = { +extern epicsShareDef const char *db_field_text[] = { "DBF_STRING", "DBF_SHORT", "DBF_FLOAT", @@ -3588,11 +1162,11 @@ epicsShareDef const char *db_field_text[] = { "DBF_DOUBLE" }; -epicsShareDef const char *dbf_text_invalid = "DBF_invalid"; +extern epicsShareDef const char *dbf_text_invalid = "DBF_invalid"; -epicsShareDef const short dbf_text_dim = (sizeof dbf_text)/(sizeof (char *)); +extern epicsShareDef const short dbf_text_dim = (sizeof dbf_text)/(sizeof (char *)); -epicsShareDef const char *dbr_text[LAST_BUFFER_TYPE+1] = { +extern epicsShareDef const char *dbr_text[LAST_BUFFER_TYPE+1] = { "DBR_STRING", "DBR_SHORT", "DBR_FLOAT", @@ -3634,6 +1208,7 @@ epicsShareDef const char *dbr_text[LAST_BUFFER_TYPE+1] = { "DBR_CLASS_NAME" }; -epicsShareDef const char *dbr_text_invalid = "DBR_invalid"; -epicsShareDef const short dbr_text_dim = (sizeof dbr_text) / (sizeof (char *)) + 1; +extern epicsShareDef const char *dbr_text_invalid = "DBR_invalid"; +extern epicsShareDef const short dbr_text_dim = (sizeof dbr_text) / (sizeof (char *)) + 1; +} \ No newline at end of file diff --git a/src/ca/acctst.c b/src/ca/acctst.c index 3508f8ebf..560536150 100644 --- a/src/ca/acctst.c +++ b/src/ca/acctst.c @@ -1,10 +1,8 @@ /* - * CA test/debug routine + * CA regression test */ -static char *sccsId = "@(#) $Id$"; - /* * ANSI */ @@ -25,6 +23,8 @@ static char *sccsId = "@(#) $Id$"; */ #include "cadef.h" +#include "caDiagnostics.h" + #define EVENT_ROUTINE null_event #define CONN_ROUTINE conn @@ -78,7 +78,7 @@ void performGrEnumTest (chid chan); void performCtrlDoubleTest (chid chan); -int acctst(char *pname) +int acctst (char *pname) { chid chix1; chid chix2; @@ -198,63 +198,63 @@ int acctst(char *pname) * look for problems with ca_search(), ca_clear_channel(), * ca_change_connection_event(), and ca_pend_io(() combo */ - status = ca_search(pname,& chix3); - SEVCHK(status, NULL); + status = ca_search ( pname,& chix3 ); + SEVCHK ( status, NULL ); - status = ca_replace_access_rights_event(chix3, accessSecurity_cb); - SEVCHK(status, NULL); + status = ca_replace_access_rights_event ( chix3, accessSecurity_cb ); + SEVCHK ( status, NULL ); /* * verify clear before connect */ - status = ca_search(pname, &chix4); - SEVCHK(status, NULL); + status = ca_search ( pname, &chix4 ); + SEVCHK ( status, NULL ); - status = ca_clear_channel(chix4); - SEVCHK(status, NULL); + status = ca_clear_channel ( chix4 ); + SEVCHK ( status, NULL ); - status = ca_search(pname, &chix4); - SEVCHK(status, NULL); + status = ca_search ( pname, &chix4 ); + SEVCHK ( status, NULL ); - status = ca_replace_access_rights_event(chix4, accessSecurity_cb); - SEVCHK(status, NULL); + status = ca_replace_access_rights_event ( chix4, accessSecurity_cb ); + SEVCHK ( status, NULL ); - status = ca_search(pname, &chix2); - SEVCHK(status, NULL); + status = ca_search ( pname, &chix2 ); + SEVCHK (status, NULL); - status = ca_replace_access_rights_event(chix2, accessSecurity_cb); - SEVCHK(status, NULL); + status = ca_replace_access_rights_event (chix2, accessSecurity_cb); + SEVCHK ( status, NULL ); - status = ca_search(pname, &chix1); - SEVCHK(status, NULL); + status = ca_search ( pname, &chix1 ); + SEVCHK ( status, NULL ); - status = ca_replace_access_rights_event(chix1, accessSecurity_cb); - SEVCHK(status, NULL); + status = ca_replace_access_rights_event ( chix1, accessSecurity_cb ); + SEVCHK ( status, NULL ); - status = ca_change_connection_event(chix1,conn); - SEVCHK(status, NULL); + status = ca_change_connection_event ( chix1, conn ); + SEVCHK ( status, NULL ); - status = ca_change_connection_event(chix1,NULL); - SEVCHK(status, NULL); + status = ca_change_connection_event ( chix1, NULL ); + SEVCHK ( status, NULL ); - status = ca_change_connection_event(chix1,conn); - SEVCHK(status, NULL); + status = ca_change_connection_event ( chix1, conn ); + SEVCHK ( status, NULL ); - status = ca_change_connection_event(chix1,NULL); - SEVCHK(status, NULL); + status = ca_change_connection_event ( chix1, NULL ); + SEVCHK ( status, NULL ); - status = ca_pend_io(1000.0); - SEVCHK(status, NULL); + status = ca_pend_io ( 1000.0 ); + SEVCHK ( status, NULL ); - assert (ca_state(chix1) == cs_conn); - assert (ca_state(chix2) == cs_conn); - assert (ca_state(chix3) == cs_conn); - assert (ca_state(chix4) == cs_conn); + assert ( ca_state (chix1) == cs_conn ); + assert ( ca_state (chix2) == cs_conn ); + assert ( ca_state (chix3) == cs_conn ); + assert ( ca_state (chix4) == cs_conn ); - assert (INVALID_DB_REQ(ca_field_type(chix1)) == FALSE); - assert (INVALID_DB_REQ(ca_field_type(chix2)) == FALSE); - assert (INVALID_DB_REQ(ca_field_type(chix3)) == FALSE); - assert (INVALID_DB_REQ(ca_field_type(chix4)) == FALSE); + assert ( INVALID_DB_REQ (ca_field_type (chix1) ) == FALSE ); + assert ( INVALID_DB_REQ (ca_field_type (chix2) ) == FALSE ); + assert ( INVALID_DB_REQ (ca_field_type (chix3) ) == FALSE ); + assert ( INVALID_DB_REQ (ca_field_type (chix4) ) == FALSE ); printf("%s Read Access=%d Write Access=%d\n", ca_name(chix1), ca_read_access(chix1), ca_write_access(chix1)); @@ -446,7 +446,7 @@ int acctst(char *pname) else { iter = 10ul; } - floatTest(chix1, base, incr, epsil, iter); + floatTest (chix1, base, incr, epsil, iter); printf ("."); fflush (stdout); } @@ -647,7 +647,7 @@ int acctst(char *pname) dbr_string_t respStr; memset(stimStr, 'a', sizeof(stimStr)); status = ca_array_put(DBR_STRING, 1u, chix1, stimStr); - assert(status==ECA_STRTOBIG); + assert(status!=ECA_NORMAL); sprintf(stimStr, "%u", 8u); status = ca_array_put(DBR_STRING, 1u, chix1, stimStr); assert(status==ECA_NORMAL); @@ -898,19 +898,6 @@ int acctst(char *pname) return(0); } -#ifndef iocCore -int main(int argc, char **argv) -{ - if(argc == 2){ - acctst(argv[1]); - } - else{ - printf("usage: %s \n", argv[0]); - } - return 0; -} -#endif /*iocCore */ - /* * pend_event_delay_test() */ @@ -957,7 +944,7 @@ unsigned iterations) SEVCHK (status, NULL); status = ca_get (DBR_FLOAT, chan, &fretval); SEVCHK (status, NULL); - status = ca_pend_io (100.0); + status = ca_pend_io (10.0); SEVCHK (status, NULL); if (fabs(fval-fretval) > epsilon) { printf ("float test failed val written %f\n", fval); @@ -1038,7 +1025,7 @@ void null_event (struct event_handler_args args) /* * write_event () */ -void write_event(struct event_handler_args args) +void write_event (struct event_handler_args args) { int status; dbr_float_t *pFloat = (dbr_float_t *) args.dbr; @@ -1051,35 +1038,44 @@ void write_event(struct event_handler_args args) a = *pFloat; a += 10.1F; - status = ca_array_put( - DBR_FLOAT, - 1, - args.chid, - &a); - SEVCHK(status,NULL); - SEVCHK(ca_flush_io(), NULL); + status = ca_array_put ( DBR_FLOAT, 1, args.chid, &a); + SEVCHK ( status, NULL ); + SEVCHK ( ca_flush_io (), NULL ); } -void conn(struct connection_handler_args args) +void conn (struct connection_handler_args args) { -#if 0 - if (args.op == CA_OP_CONN_UP) - printf("Channel On Line [%s]\n", ca_name(args.chid)); - else if (args.op == CA_OP_CONN_DOWN) - printf("Channel Off Line [%s]\n", ca_name(args.chid)); - else + int status; + + if (args.op == CA_OP_CONN_UP) { +# if 0 + printf("Channel On Line [%s]\n", ca_name(args.chid)); +# endif + status = ca_get_callback (DBR_GR_FLOAT, args.chid, get_cb, NULL); + SEVCHK (status, "get call back in connection handler"); + status = ca_flush_io (); + SEVCHK (status, "get call back flush in connection handler"); + } + else if (args.op == CA_OP_CONN_DOWN) { +# if 0 + printf("Channel Off Line [%s]\n", ca_name(args.chid)); +# endif + } + else { printf("Ukn conn ev\n"); -#endif - ca_get_callback(DBR_GR_FLOAT, args.chid, get_cb, NULL); + } + } void get_cb (struct event_handler_args args) { - if(!(args.status & CA_M_SUCCESS)){ + if ( ! ( args.status & CA_M_SUCCESS ) ) { printf("Get cb failed because \"%s\"\n", - ca_message(args.status)); + ca_message (args.status) ); + } + else { + conn_cb_count++; } - conn_cb_count++; } /* @@ -1167,7 +1163,6 @@ void multiple_sg_requests(chid chix, CA_SYNC_GID gid) } } - /* * accessSecurity_cb() */ @@ -1192,20 +1187,21 @@ void performGrEnumTest (chid chan) int status; unsigned i; + ge.no_str = -1; + status = ca_get (DBR_GR_ENUM, chan, &ge); SEVCHK (status, "DBR_GR_ENUM ca_get()"); status = ca_pend_io (2.0); assert (status == ECA_NORMAL); - if (ge.no_str>0) { - count = (unsigned) ge.no_str; - printf ("Enum state str = "); - for (i=0; i= 0 && ge.no_str < NELEMENTS(ge.strs) ); + printf ("Enum state str = {"); + count = (unsigned) ge.no_str; + for (i=0; i + +#include "caDiagnostics.h" + +int main(int argc, char **argv) +{ + if(argc == 2){ + acctst(argv[1]); + } + else{ + printf("usage: %s \n", argv[0]); + } + return 0; +} diff --git a/src/ca/addrList.h b/src/ca/addrList.h index 6e22b4d74..6d0e4a71b 100644 --- a/src/ca/addrList.h +++ b/src/ca/addrList.h @@ -12,7 +12,10 @@ epicsShareFunc void epicsShareAPI configureChannelAccessAddressList epicsShareFunc void epicsShareAPI addAddrToChannelAccessAddressList (ELLLIST *pList, const ENV_PARAM *pEnv, unsigned short port); -epicsShareFunc void epicsShareAPI printChannelAccessAddressList (ELLLIST *pList); +epicsShareFunc void epicsShareAPI printChannelAccessAddressList (const ELLLIST *pList); + +epicsShareFunc void epicsShareAPI setPortAndRemoveDuplicates + (ELLLIST *pDestList, ELLLIST *pSrcList, unsigned short port); #ifdef __cplusplus } diff --git a/src/ca/baseNMIU.cpp b/src/ca/baseNMIU.cpp new file mode 100644 index 000000000..caa0cba2a --- /dev/null +++ b/src/ca/baseNMIU.cpp @@ -0,0 +1,33 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, the Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +baseNMIU::baseNMIU ( nciu &chanIn ) : chan (chanIn) +{ + this->chan.installIO ( *this ); +} + +void baseNMIU::destroy () +{ + delete this; +} + +baseNMIU::~baseNMIU () +{ + this->chan.uninstallIO ( *this ); +} + +int baseNMIU::subscriptionMsg () +{ + return ECA_NORMAL; +} diff --git a/src/ca/bhe.cpp b/src/ca/bhe.cpp new file mode 100644 index 000000000..57579eede --- /dev/null +++ b/src/ca/bhe.cpp @@ -0,0 +1,177 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +tsFreeList < class bhe > bhe::freeList; + +/* + * update beacon period + * + * updates beacon period, and looks for beacon anomalies + */ +bool bhe::updateBeaconPeriod (osiTime programBeginTime) +{ + double currentPeriod; + bool netChange = false; + osiTime current = osiTime::getCurrent (); + + if ( this->timeStamp == osiTime () ) { + /* + * this is the 1st beacon seen - the beacon time stamp + * was not initialized during BHE create because + * a TCP/IP connection created the beacon. + * (nothing to do but set the beacon time stamp and return) + */ + this->timeStamp = current; + + /* + * be careful about using beacons to reset the connection + * time out watchdog until we have received a ping response + * from the IOC (this makes the software detect reconnects + * faster when the server is rebooted twice in rapid + * succession before a 1st or 2nd beacon has been received) + */ + if (this->piiu) { + this->piiu->beaconAnomaly = TRUE; + } + + return netChange; + } + + /* + * compute the beacon period (if we have seen at least two beacons) + */ + currentPeriod = current - this->timeStamp; + if ( this->averagePeriod < 0.0 ) { + ca_real totalRunningTime; + + /* + * this is the 2nd beacon seen. We cant tell about + * the change in period at this point so we just + * initialize the average period and return. + */ + this->averagePeriod = currentPeriod; + + /* + * be careful about using beacons to reset the connection + * time out watchdog until we have received a ping response + * from the IOC (this makes the software detect reconnects + * faster when the server is rebooted twice in rapid + * succession before a 2nd beacon has been received) + */ + if (this->piiu) { + this->piiu->beaconAnomaly = TRUE; + } + + /* + * ignore beacons seen for the first time shortly after + * init, but do not ignore beacons arriving with a short + * period because the IOC was rebooted soon after the + * client starts up. + */ + totalRunningTime = this->timeStamp - programBeginTime; + if ( currentPeriod <= totalRunningTime ) { + netChange = true; +# ifdef DEBUG + { + char name[64]; + + ipAddrToA ( &this->inetAddr, name, sizeof (name) ); + ca_printf ( + "new beacon from %s with period=%f running time to first beacon=%f\n", + name, currentPeriod, totalRunningTime ); + } +# endif + } + } + else { + + /* + * Is this an IOC seen because of a restored + * network segment? + * + * It may be possible to get false triggers here + * if the client is busy, but this does not cause + * problems because the echo response will tell us + * that the server is available + */ + if ( currentPeriod >= this->averagePeriod * 1.25 ) { + + if ( this->piiu ) { + /* + * trigger on any missing beacon + * if connected to this server + */ + this->piiu->beaconAnomaly = true; + } + + if ( currentPeriod >= this->averagePeriod * 3.25 ) { + /* + * trigger on any 3 contiguous missing beacons + * if not connected to this server + */ + netChange = true; + } + } + + +# ifdef DEBUG + if (netChange) { + char name[64]; + + ipAddrToA (&pBHE->inetAddr, name, sizeof(name)); + ca_printf ( + "net resume seen %s cur=%f avg=%f\n", + name, currentPeriod, pBHE->averagePeriod); + } +# endif + + /* + * Is this an IOC seen because of an IOC reboot + * (beacon come at a higher rate just after the + * IOC reboots). Lower tolarance here because we + * dont have to worry about lost beacons. + * + * It may be possible to get false triggers here + * if the client is busy, but this does not cause + * problems because the echo response will tell us + * that the server is available + */ + if ( currentPeriod <= this->averagePeriod * 0.80 ) { +# ifdef DEBUG + { + char name[64]; + + ipAddrToA ( &this->inetAddr, name, sizeof (name) ); + ca_printf ( + "reboot seen %s cur=%f avg=%f\n", + name, currentPeriod, this->averagePeriod); + } +# endif + netChange = true; + if ( this->piiu ) { + this->piiu->beaconAnomaly = true; + } + } + + /* + * update a running average period + */ + this->averagePeriod = currentPeriod * 0.125 + this->averagePeriod * 0.875; + } + + this->timeStamp = current; + + return netChange; +} + diff --git a/src/ca/bhe_IL.h b/src/ca/bhe_IL.h new file mode 100644 index 000000000..72b1898df --- /dev/null +++ b/src/ca/bhe_IL.h @@ -0,0 +1,70 @@ + +/* + * $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 + */ + +/* + * set average to -1.0 so that when the next beacon + * occurs we can distinguish between: + * o new server + * o existing server's beacon we are seeing + * for the first time shortly after program + * start up + * + * if creating this in response to a search reply + * and not in response to a beacon then + * we set the beacon time stamp to + * zero (so we can correctly compute the period + * between the 1st and 2nd beacons) + */ +inline bhe::bhe (class cac &cacIn, const osiTime &initialTimeStamp, const inetAddrID &addr) : + cac (cacIn), inetAddrID (addr), piiu (0), timeStamp (initialTimeStamp), averagePeriod (-1.0) +{ +# ifdef DEBUG + { + char name[64]; + ipAddrToA (&addr, name, sizeof(name)); + printf ("created beacon entry for %s\n", name); + } +# endif +} + +inline bhe::~bhe () +{ + this->cac.removeBeaconInetAddr (*this); +} + +inline void * bhe::operator new ( size_t size ) +{ + return bhe::freeList.allocate ( size ); +} + +inline void bhe::operator delete ( void *pCadaver, size_t size ) +{ + bhe::freeList.release ( pCadaver, size ); +} + +inline tcpiiu *bhe::getIIU ()const +{ + return this->piiu; +} + +inline void bhe::bindToIIU (tcpiiu *piiuIn) +{ + assert ( this->piiu == 0 ); + this->piiu = piiuIn; +} + +inline void bhe::destroy () +{ + delete this; +} + diff --git a/src/ca/caDiagnostics.h b/src/ca/caDiagnostics.h new file mode 100644 index 000000000..1807f5a46 --- /dev/null +++ b/src/ca/caDiagnostics.h @@ -0,0 +1,6 @@ + +enum appendNumberFlag {appendNumber, dontAppendNumber}; + +int catime (char *channelName, enum appendNumberFlag appNF); + +int acctst (char *pname); \ No newline at end of file diff --git a/src/ca/caPutNotify.cpp b/src/ca/caPutNotify.cpp index e6d320af6..ce7f1f890 100644 --- a/src/ca/caPutNotify.cpp +++ b/src/ca/caPutNotify.cpp @@ -22,7 +22,7 @@ LOCAL void caExtraEventLabor (void *pArg) */ semStatus = semMutexTakeTimeout (pcac->localIIU.putNotifyLock, 60.0 /* sec */); if (semStatus!=semTakeOK) { - ca_printf (pcac, "cac: put notify deadlock condition detected\n"); + ca_printf ("cac: put notify deadlock condition detected\n"); (*pcac->localIIU.pdbAdapter->p_db_post_extra_labor) (pcac->localIIU.evctx); break; } diff --git a/src/ca/cac.cpp b/src/ca/cac.cpp new file mode 100644 index 000000000..fab8a71b2 --- /dev/null +++ b/src/ca/cac.cpp @@ -0,0 +1,816 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "osiProcess.h" +#include "osiSigPipeIgnore.h" +#include "iocinf.h" +#include "inetAddrID_IL.h" +#include "bhe_IL.h" + + +// +// cac::cac () +// +cac::cac () : + beaconTable (1024), + endOfBCastList (0), + ioTable (1024), + chanTable (1024), + sgTable (128), + pndrecvcnt (0) +{ + long status; + + if ( cacRecursionLock == NULL ) { + cacRecursionLock = threadPrivateCreate (); + if ( ! cacRecursionLock ) { + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + } + + if (!bsdSockAttach()) { + throwWithLocation ( caErrorCode (ECA_INTERNAL) ); + } + + ellInit (&this->ca_taskVarList); + ellInit (&this->putCvrtBuf); + ellInit (&this->fdInfoFreeList); + ellInit (&this->fdInfoList); + this->ca_printf_func = errlogVprintf; + this->pudpiiu = NULL; + this->ca_exception_func = ca_default_exception_handler; + this->ca_exception_arg = NULL; + this->ca_fd_register_func = NULL; + this->ca_fd_register_arg = NULL; + this->ca_flush_pending = FALSE; + this->ca_number_iiu_in_fc = 0u; + this->ca_manage_conn_active = FALSE; + this->readSeq = 0u; + + this->ca_client_lock = semMutexCreate(); + if (!this->ca_client_lock) { + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + this->ca_io_done_sem = semBinaryCreate(semEmpty); + if (!this->ca_io_done_sem) { + semMutexDestroy (this->ca_client_lock); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + + this->ca_blockSem = semBinaryCreate(semEmpty); + if (!this->ca_blockSem) { + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + + installSigPipeIgnore (); + + { + char tmp[256]; + size_t len; + osiGetUserNameReturn gunRet; + + gunRet = osiGetUserName ( tmp, sizeof (tmp) ); + if (gunRet!=osiGetUserNameSuccess) { + tmp[0] = '\0'; + } + len = strlen (tmp) + 1; + this->ca_pUserName = (char *) malloc ( len ); + if ( ! this->ca_pUserName ) { + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + strncpy (this->ca_pUserName, tmp, len); + } + + /* record the host name */ + this->ca_pHostName = localHostName(); + if (!this->ca_pHostName) { + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + free (this->ca_pUserName); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + + this->programBeginTime = osiTime::getCurrent (); + + status = envGetDoubleConfigParam (&EPICS_CA_CONN_TMO, &this->ca_connectTMO); + if (status) { + this->ca_connectTMO = CA_CONN_VERIFY_PERIOD; + ca_printf ( + "EPICS \"%s\" float fetch failed\n", + EPICS_CA_CONN_TMO.name); + ca_printf ( + "Setting \"%s\" = %f\n", + EPICS_CA_CONN_TMO.name, + this->ca_connectTMO); + } + + this->ca_server_port = + envGetInetPortConfigParam (&EPICS_CA_SERVER_PORT, CA_SERVER_PORT); + + this->pProcThread = new processThread (this); + if ( ! this->pProcThread ) { + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + free (this->ca_pUserName); + free (this->ca_pHostName); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + + threadPrivateSet (caClientContextId, (void *) this); +} + +/* + * cac::~cac () + * + * releases all resources alloc to a channel access client + */ +cac::~cac () +{ + threadPrivateSet (caClientContextId, NULL); + + // + // make certain that process thread isnt deleting + // tcpiiu objects at the same that this thread is + // + delete this->pProcThread; + + // + // shutdown all tcp connections and wait for threads to exit + // + this->iiuListLock.lock (); + tsDLIterBD piiu ( this->iiuListIdle.first () ); + while ( piiu != piiu.eol () ) { + tsDLIterBD pnext = piiu.itemAfter (); + piiu->suicide (); + piiu = pnext; + } + piiu = this->iiuListRecvPending.first (); + while ( piiu != piiu.eol () ) { + tsDLIterBD pnext = piiu.itemAfter (); + piiu->suicide (); + piiu = pnext; + } + this->iiuListLock.unlock (); + + // + // shutdown udp and wait for threads to exit + // + if (this->pudpiiu) { + delete this->pudpiiu; + } + + LOCK (this); + + /* remove put convert block free list */ + ellFree (&this->putCvrtBuf); + + /* reclaim sync group resources */ + this->sgTable.destroyAllEntries (); + + /* free select context lists */ + ellFree (&this->fdInfoFreeList); + ellFree (&this->fdInfoList); + + /* + * free user name string + */ + if (this->ca_pUserName) { + free (this->ca_pUserName); + } + + /* + * free host name string + */ + if (this->ca_pHostName) { + free (this->ca_pHostName); + } + + this->beaconTable.destroyAllEntries (); + + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + + semMutexDestroy (this->ca_client_lock); + + bsdSockRelease (); +} + +void cac::safeDestroyNMIU (unsigned id) +{ + LOCK (this); + + baseNMIU *pIOBlock = this->ioTable.lookup (id); + if (pIOBlock) { + pIOBlock->destroy (); + } + + UNLOCK (this); +} + +void cac::processRecvBacklog () +{ + tcpiiu *piiu; + + while ( 1 ) { + int status; + unsigned bytesToProcess; + + this->iiuListLock.lock (); + piiu = this->iiuListRecvPending.get (); + if (!piiu) { + this->iiuListLock.unlock (); + break; + } + + piiu->recvPending = false; + this->iiuListIdle.add (*piiu); + this->iiuListLock.unlock (); + + if (piiu->state == iiu_disconnected) { + delete piiu; + continue; + } + + char *pProto = (char *) cacRingBufferReadReserveNoBlock + (&piiu->recv, &bytesToProcess); + while (pProto) { + status = piiu->post_msg (pProto, bytesToProcess); + if ( status == ECA_NORMAL ) { + cacRingBufferReadCommit (&piiu->recv, bytesToProcess); + cacRingBufferReadFlush (&piiu->recv); + } + else { + delete piiu; + } + pProto = (char *) cacRingBufferReadReserveNoBlock + (&piiu->recv, &bytesToProcess); + } + } +} + +/* + * cac::flush () + */ +void cac::flush () +{ + /* + * set the push pending flag on all virtual circuits + */ + this->iiuListLock.lock (); + tsDLIterBD piiu ( this->iiuListIdle.first () ); + while ( piiu != tsDLIterBD::eol () ) { + piiu->flush (); + piiu++; + } + piiu = this->iiuListRecvPending.first (); + while ( piiu != tsDLIterBD::eol () ) { + piiu->flush (); + piiu++; + } + this->iiuListLock.unlock (); +} + + + +/* + * + * set pending IO count back to zero and + * send a sync to each IOC and back. dont + * count reads until we recv the sync + * + */ +void cac::cleanUpPendIO () +{ + nciu *pchan; + + LOCK (this); + + this->readSeq++; + this->pndrecvcnt = 0u; + + tsDLIter iter ( this->pudpiiu->chidList ); + while ( ( pchan = iter () ) ) { + pchan->connectTimeoutNotify (); + } + + UNLOCK (this); +} + +unsigned cac::connectionCount () const +{ + unsigned count; + + this->iiuListLock.lock (); + count = this->iiuListIdle.count () + this->iiuListRecvPending.count (); + this->iiuListLock.unlock (); + return count; +} + +void cac::show (unsigned level) const +{ + LOCK (this); + if (this->pudpiiu) { + this->pudpiiu->show (level); + } + + this->iiuListLock.lock (); + + tsDLIterConstBD piiu ( this->iiuListIdle.first () ); + while ( piiu != piiu.eol () ) { + piiu->show (level); + piiu++; + } + + piiu = this->iiuListRecvPending.first (); + while ( piiu != piiu.eol () ) { + piiu->show (level); + piiu++; + } + + this->iiuListLock.unlock (); + + UNLOCK (this); +} + +void cac::installIIU (tcpiiu &iiu) +{ + this->iiuListLock.lock (); + iiu.recvPending = false; + this->iiuListIdle.add (iiu); + this->iiuListLock.unlock (); + + if (this->ca_fd_register_func) { + ( * this->ca_fd_register_func ) ( (void *) this->ca_fd_register_arg, iiu.sock, TRUE); + } +} + +void cac::signalRecvActivityIIU (tcpiiu &iiu) +{ + bool change; + + this->iiuListLock.lock (); + + if ( iiu.recvPending ) { + change = false; + } + else { + this->iiuListIdle.remove (iiu); + this->iiuListRecvPending.add (iiu); + iiu.recvPending = true; + change = true; + } + + this->iiuListLock.unlock (); + + // + // wakeup after unlock improves performance + // + if (change) { + this->recvActivity.signal (); + } +} + +void cac::removeIIU (tcpiiu &iiu) +{ + this->iiuListLock.lock (); + + if (iiu.recvPending) { + this->iiuListRecvPending.remove (iiu); + } + else { + this->iiuListIdle.remove (iiu); + } + + this->iiuListLock.unlock (); +} + +/* + * cac::lookupBeaconInetAddr() + * + * LOCK must be applied + */ +bhe * cac::lookupBeaconInetAddr (const inetAddrID &ina) +{ + bhe *pBHE; + LOCK (this); + pBHE = this->beaconTable.lookup (ina); + UNLOCK (this); + return pBHE; +} + +/* + * cac::createBeaconHashEntry () + */ +bhe *cac::createBeaconHashEntry (const inetAddrID &ina, const osiTime &initialTimeStamp) +{ + bhe *pBHE; + + LOCK (this); + + pBHE = this->beaconTable.lookup ( ina ); + if ( !pBHE ) { + pBHE = new bhe (*this, initialTimeStamp, ina); + if ( pBHE ) { + if ( this->beaconTable.add (*pBHE) < 0 ) { + pBHE->destroy (); + pBHE = 0; + } + } + } + + UNLOCK (this); + + return pBHE; +} + +/* + * cac::beaconNotify + */ +void cac::beaconNotify ( const inetAddrID &addr ) +{ + bhe *pBHE; + unsigned port; + int netChange; + + if ( ! this->pudpiiu ) { + return; + } + + LOCK ( this ); + /* + * look for it in the hash table + */ + pBHE = this->lookupBeaconInetAddr ( addr ); + if (pBHE) { + + netChange = pBHE->updateBeaconPeriod (this->programBeginTime); + + /* + * update state of health for active virtual circuits + * (only if we are not suspicious about past beacon changes + * until the next echo reply) + */ + tcpiiu *piiu = pBHE->getIIU (); + if ( piiu ) { + if ( ! piiu->beaconAnomaly ) { + piiu->tcpRecvWatchdog::reschedule (); // reset connection activity watchdog + } + } + } + else { + /* + * This is the first beacon seen from this server. + * Wait until 2nd beacon is seen before deciding + * if it is a new server (or just the first + * time that we have seen a server's beacon + * shortly after the program started up) + */ + netChange = FALSE; + this->createBeaconHashEntry (addr, osiTime::getCurrent () ); + } + + if ( ! netChange ) { + UNLOCK (this); + return; + } + + /* + * This part is needed when many machines + * have channels in a disconnected state that + * dont exist anywhere on the network. This insures + * that we dont have many CA clients synchronously + * flooding the network with broadcasts (and swamping + * out requests for valid channels). + * + * I fetch the local port number and use the low order bits + * as a pseudo random delay to prevent every one + * from replying at once. + */ + { + struct sockaddr_in saddr; + int saddr_length = sizeof(saddr); + int status; + + status = getsockname ( this->pudpiiu->sock, (struct sockaddr *) &saddr, + &saddr_length); + assert ( status >= 0 ); + port = ntohs ( saddr.sin_port ); + } + + { + ca_real delay; + + delay = (port&CA_RECAST_PORT_MASK); + delay /= MSEC_PER_SEC; + delay += CA_RECAST_DELAY; + + this->pudpiiu->searchTmr.reset ( delay ); + } + + /* + * set retry count of all disconnected channels + * to zero + */ + tsDLIterBD iter ( this->pudpiiu->chidList.first () ); + while ( iter != tsDLIterBD::eol () ) { + iter->retry = 0u; + iter++; + } + + UNLOCK ( this ); + +# if DEBUG + { + char buf[64]; + ipAddrToA (pnet_addr, buf, sizeof (buf) ); + printf ("new server available: %s\n", buf); + } +# endif + +} + +/* + * cac::removeBeaconInetAddr () + */ +void cac::removeBeaconInetAddr (const inetAddrID &ina) +{ + bhe *pBHE; + + LOCK (this); + pBHE = this->beaconTable.remove ( ina ); + UNLOCK (this); + + assert (pBHE); +} + +void cac::decrementOutstandingIO (unsigned seqNumber) +{ + LOCK (this); + if ( this->readSeq == seqNumber ) { + if ( this->pndrecvcnt > 0u ) { + this->pndrecvcnt--; + } + } + UNLOCK (this); + if ( this->pndrecvcnt == 0u ) { + semBinaryGive (this->ca_io_done_sem); + } +} + +void cac::decrementOutstandingIO () +{ + LOCK (this); + if ( this->pndrecvcnt > 0u ) { + this->pndrecvcnt--; + } + UNLOCK (this); + if ( this->pndrecvcnt == 0u ) { + semBinaryGive (this->ca_io_done_sem); + } +} + +void cac::incrementOutstandingIO () +{ + LOCK (this); + if ( this->pndrecvcnt < UINT_MAX ) { + this->pndrecvcnt++; + } + UNLOCK (this); +} + +unsigned cac::readSequence () const +{ + return this->readSeq; +} + +int cac::pend (double timeout, int early) +{ + int status; + void *p; + + /* + * dont allow recursion + */ + p = threadPrivateGet (cacRecursionLock); + if (p) { + return ECA_EVDISALLOW; + } + + threadPrivateSet (cacRecursionLock, &cacRecursionLock); + + status = this->pendPrivate (timeout, early); + + threadPrivateSet (cacRecursionLock, NULL); + + return status; +} + +/* + * cac::pendPrivate () + */ +int cac::pendPrivate (double timeout, int early) +{ + osiTime cur_time; + osiTime beg_time; + double delay; + + this->flush (); + + if (this->pndrecvcnt==0u && early) { + return ECA_NORMAL; + } + + if ( timeout < 0.0 ) { + if (early) { + this->cleanUpPendIO (); + } + return ECA_TIMEOUT; + } + + beg_time = cur_time = osiTime::getCurrent (); + + delay = 0.0; + while ( true ) { + ca_real remaining; + + if ( timeout == 0.0 ) { + remaining = 60.0; + } + else{ + remaining = timeout - delay; + remaining = min ( 60.0, remaining ); + + /* + * If we are not waiting for any significant delay + * then force the delay to zero so that we avoid + * scheduling delays (which can be substantial + * on some os) + */ + if ( remaining <= CAC_SIGNIFICANT_SELECT_DELAY ) { + if ( early ) { + this->cleanUpPendIO (); + } + return ECA_TIMEOUT; + } + } + + /* recv occurs in another thread */ + semBinaryTakeTimeout ( this->ca_io_done_sem, remaining ); + + if ( this->pndrecvcnt == 0 && early ) { + return ECA_NORMAL; + } + + cur_time = osiTime::getCurrent (); + + if ( timeout != 0.0 ) { + delay = cur_time - beg_time; + } + } +} + +bool cac::ioComplete () const +{ + if ( this->pndrecvcnt == 0u ) { + return true; + } + else{ + return false; + } +} + +void cac::installIO ( baseNMIU &io ) +{ + LOCK (this); + this->ioTable.add ( io ); + UNLOCK (this); +} + +void cac::uninstallIO ( baseNMIU &io ) +{ + LOCK (this); + this->ioTable.remove ( io ); + UNLOCK (this); +} + +baseNMIU * cac::lookupIO (unsigned id) +{ + LOCK (this); + baseNMIU * pmiu = this->ioTable.lookup ( id ); + UNLOCK (this); + return pmiu; +} + +void cac::installChannel (nciu &chan) +{ + LOCK (this); + this->chanTable.add ( chan ); + UNLOCK (this); +} + +void cac::uninstallChannel (nciu &chan) +{ + LOCK (this); + this->chanTable.remove ( chan ); + UNLOCK (this); +} + +nciu * cac::lookupChan (unsigned id) +{ + LOCK (this); + nciu * pchan = this->chanTable.lookup ( id ); + UNLOCK (this); + return pchan; +} + +void cac::installCASG (CASG &sg) +{ + LOCK (this); + this->sgTable.add ( sg ); + UNLOCK (this); +} + +void cac::uninstallCASG (CASG &sg) +{ + LOCK (this); + this->sgTable.remove ( sg ); + UNLOCK (this); +} + +CASG * cac::lookupCASG (unsigned id) +{ + LOCK (this); + CASG * psg = this->sgTable.lookup ( id ); + if ( psg ) { + if ( ! psg->verify () ) { + psg = 0; + } + } + UNLOCK (this); + return psg; +} + +void cac::exceptionNotify (int status, const char *pContext, + const char *pFileName, unsigned lineNo) +{ + ca_signal_with_file_and_lineno ( status, pContext, pFileName, lineNo ); +} + +void cac::exceptionNotify (int status, const char *pContext, + unsigned type, unsigned long count, + const char *pFileName, unsigned lineNo) +{ + ca_signal_formated ( status, pFileName, lineNo, "%s type=%d count=%ld\n", + pContext, type, count ); +} + +void cac::registerService ( cacServiceIO &service ) +{ + this->services.registerService ( service ); +} + +bool cac::createChannel (const char *pName, cacChannel &chan) +{ + if ( this->services.createChannel (pName, chan) ) { + return true; + } + if ( cacGlobalServiceList.createChannel (pName, chan) ) { + return true; + } + if ( ! this->pudpiiu ) { + this->pudpiiu = new udpiiu ( this ); + if ( ! this->pudpiiu ) { + return false; + } + } + nciu *pNetChan = new nciu ( this, chan, pName ); + if ( pNetChan ) { + if ( ! pNetChan->fullyConstructed () ) { + pNetChan->destroy (); + return false; + } + else { + return true; + } + } + else { + return false; + } +} + diff --git a/src/ca/cacChannel.cpp b/src/ca/cacChannel.cpp new file mode 100644 index 000000000..c2422d1d7 --- /dev/null +++ b/src/ca/cacChannel.cpp @@ -0,0 +1,273 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "iocinf.h" + +osiMutex cacChannel::defaultMutex; + +cacChannel::cacChannel () : pChannelIO (0) +{ +} + +cacChannel::~cacChannel () +{ + if ( this->pChannelIO ) { + this->pChannelIO->destroy (); + } +} + +void cacChannel::attachIO (cacChannelIO &io) +{ + assert ( ! this->pChannelIO ); + this->lock (); + this->pChannelIO = &io; + this->ioAttachNotify (); + this->unlock (); +} + +int cacChannel::read ( unsigned type, unsigned long count, cacNotify & notify ) +{ + if ( this->pChannelIO ) { + return pChannelIO->read (type, count, notify); + } + else { + return ECA_DISCONNCHID; + } +} + +int cacChannel::read ( unsigned type, unsigned long count, void *pValue ) +{ + if ( this->pChannelIO ) { + return pChannelIO->read (type, count, pValue); + } + else { + return ECA_DISCONNCHID; + } +} + +int cacChannel::write (unsigned type, unsigned long count, const void *pvalue ) +{ + if ( this->pChannelIO ) { + return pChannelIO->write (type, count, pvalue); + } + else { + return ECA_DISCONNCHID; + } +} + +int cacChannel::write (unsigned type, unsigned long count, + const void *pvalue, cacNotify & notify ) +{ + if ( this->pChannelIO ) { + return pChannelIO->write (type, count, pvalue, notify); + } + else { + return ECA_DISCONNCHID; + } +} + +int cacChannel::subscribe ( unsigned type, unsigned long count, + unsigned mask, cacNotify ¬ify ) +{ + if ( this->pChannelIO ) { + return pChannelIO->subscribe (type, count, mask, notify); + } + else { + return ECA_DISCONNCHID; + } +} + +void cacChannel::hostName ( char *pBuf, unsigned bufLength ) const +{ + if ( bufLength ) { + if ( this->pChannelIO ) { + pChannelIO->hostName (pBuf, bufLength); + } + else { + strncpy ( pBuf, "", bufLength ); + pBuf[bufLength-1u] = '\0'; + } + } +} + +short cacChannel::nativeType () const +{ + if ( this->pChannelIO ) { + return pChannelIO->nativeType (); + } + else { + return TYPENOTCONN; + } +} + +unsigned long cacChannel::nativeElementCount () const +{ + if ( this->pChannelIO ) { + return pChannelIO->nativeElementCount (); + } + else { + return 0ul; + } +} + +channel_state cacChannel::state () const +{ + if ( this->pChannelIO ) { + return pChannelIO->state (); + } + else { + return cs_never_conn; + } +} + +bool cacChannel::readAccess () const +{ + if ( this->pChannelIO ) { + caar ar = pChannelIO->accessRights (); + return ar.read_access; + } + else { + return false; + } +} + +bool cacChannel::writeAccess () const +{ + if ( this->pChannelIO ) { + caar ar = pChannelIO->accessRights (); + return ar.write_access; + } + else { + return false; + } +} + +const char *cacChannel::pName () const +{ + if ( this->pChannelIO ) { + return pChannelIO->pName (); + } + else { + return ""; + } +} + +unsigned cacChannel::searchAttempts () const +{ + if ( this->pChannelIO ) { + return pChannelIO->searchAttempts (); + } + else { + return 0u; + } +} + +bool cacChannel::ca_v42_ok () const +{ + if ( this->pChannelIO ) { + return pChannelIO->ca_v42_ok (); + } + else { + return false; + } +} + +bool cacChannel::connected () const +{ + if ( this->pChannelIO ) { + return pChannelIO->connected (); + } + else { + return false; + } +} + +caar cacChannel::accessRights () const +{ + if ( this->pChannelIO ) { + return pChannelIO->accessRights (); + } + else { + caar ar; + ar.read_access = false; + ar.write_access = false; + return ar; + } +} + +void cacChannel::ioAttachNotify () +{ +} + +void cacChannel::ioReleaseNotify () +{ +} + +void cacChannel::connectNotify () +{ +} + +void cacChannel::disconnectNotify () +{ +} + +void cacChannel::accessRightsNotify ( caar ) +{ +} + +void cacChannel::exceptionNotify ( int status, const char *pContext ) +{ + ca_signal ( status, pContext ); +} + +void cacChannel::connectTimeoutNotify () +{ +} + +void cacChannel::lock () const +{ + this->defaultMutex.lock(); +} + +void cacChannel::unlock () const +{ + this->defaultMutex.unlock(); +} + +unsigned cacChannel::readSequence () const +{ + if ( this->pChannelIO ) { + return this->pChannelIO->readSequence (); + } + else { + return 0u; + } +} + +void cacChannel::decrementOutstandingIO () +{ + if ( this->pChannelIO ) { + this->pChannelIO->decrementOutstandingIO (); + } +} + +void cacChannel::incrementOutstandingIO () +{ + if ( this->pChannelIO ) { + this->pChannelIO->incrementOutstandingIO (); + } +} diff --git a/src/ca/cacChannelIO.cpp b/src/ca/cacChannelIO.cpp new file mode 100644 index 000000000..de4b5aabd --- /dev/null +++ b/src/ca/cacChannelIO.cpp @@ -0,0 +1,100 @@ + + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +cacChannelIO::cacChannelIO ( cacChannel &chanIn ) : + chan ( chanIn ) +{ + chan.attachIO ( *this ); +} + +cacChannelIO::~cacChannelIO () +{ + this->chan.lock (); + this->chan.ioReleaseNotify (); + this->chan.pChannelIO = 0; + this->chan.unlock (); +} + +void cacChannelIO::connectNotify () +{ + this->chan.connectNotify (); +} + +void cacChannelIO::disconnectNotify () +{ + this->chan.disconnectNotify (); +} + +void cacChannelIO::connectTimeoutNotify () +{ + this->chan.connectTimeoutNotify (); +} + +void cacChannelIO::accessRightsNotify ( caar ar ) +{ + this->chan.accessRightsNotify ( ar ); +} + +channel_state cacChannelIO::state () const +{ + return cs_conn; +} + +caar cacChannelIO::accessRights () const +{ + caar ar; + ar.read_access = true; + ar.write_access = true; + return ar; +} + +unsigned cacChannelIO::searchAttempts () const +{ + return 0u; +} + +bool cacChannelIO::ca_v42_ok () const +{ + return true; +} + +bool cacChannelIO::connected () const +{ + return true; +} + +unsigned cacChannelIO::readSequence () const +{ + return 0u; +} + +void cacChannelIO::hostName ( char *pBuf, unsigned bufLength ) const +{ + if ( bufLength ) { + int status = gethostname ( pBuf, bufLength ); + if ( status ) { + strncpy ( pBuf, "", bufLength ); + pBuf[ bufLength - 1u ] = '\0'; + } + } +} + +void cacChannelIO::incrementOutstandingIO () +{ +} + +void cacChannelIO::decrementOutstandingIO () +{ +} diff --git a/src/ca/cacIO.h b/src/ca/cacIO.h new file mode 100644 index 000000000..bb1b7aedc --- /dev/null +++ b/src/ca/cacIO.h @@ -0,0 +1,107 @@ + +/* + * $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 "tsDLList.h" +#include "osiMutex.h" + +#include "shareLib.h" + +class cacChannel; +class cacNotifyIO; + +class epicsShareClass cacNotify { +public: + cacNotify (); + virtual ~cacNotify () = 0; + virtual void destroy () = 0; + virtual void completionNotify (); + virtual void completionNotify ( unsigned type, unsigned long count, const void *pData ); + virtual void exceptionNotify ( int status, const char *pContext ); + virtual void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); +private: + cacNotifyIO *pIO; + virtual lock (); + virtual unlock (); + static osiMutex defaultMutex; + friend class cacNotifyIO; +}; + +class epicsShareClass cacNotifyIO { +public: + cacNotifyIO ( cacNotify &); + virtual ~cacNotifyIO () = 0; + virtual void destroy () = 0; + void completionNotify (); + void completionNotify ( unsigned type, unsigned long count, const void *pData ); + void exceptionNotify ( int status, const char *pContext ); + void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); +private: + cacNotify ¬ify; + friend class cacNotify; +}; + +class epicsShareClass cacChannelIO { +public: + cacChannelIO ( cacChannel &chan ); + virtual ~cacChannelIO () = 0; + virtual void destroy () = 0; + + void connectNotify (); + void disconnectNotify (); + void connectTimeoutNotify (); + void accessRightsNotify ( caar ); + + virtual const char *pName () const = 0; + +private: + virtual int read ( unsigned type, unsigned long count, void *pValue) = 0; + virtual int read ( unsigned type, unsigned long count, cacNotify ¬ify ) = 0; + virtual int write ( unsigned type, unsigned long count, const void *pValue ) = 0; + virtual int write ( unsigned type, unsigned long count, const void *pValue, cacNotify ¬ify ) = 0; + virtual int subscribe ( unsigned type, unsigned long count, unsigned mask, cacNotify ¬ify ) = 0; + virtual short nativeType () const = 0; + virtual unsigned long nativeElementCount () const = 0; + virtual void hostName (char *pBuf, unsigned bufLength) const; // defaults to local host name + virtual channel_state state () const; // defaults to always connected + virtual caar accessRights () const; // defaults to unrestricted access + virtual unsigned searchAttempts () const; // defaults to zero + virtual bool ca_v42_ok () const; // defaults to true + virtual bool connected () const; // defaults to true + virtual unsigned readSequence () const; // defaults to always zero + virtual void incrementOutstandingIO (); + virtual void decrementOutstandingIO (); + + cacChannel &chan; + friend class cacChannel; +}; + +struct cacServiceIO : public tsDLNode { +public: + epicsShareFunc virtual cacChannelIO *createChannelIO ( cacChannel &chan, const char *pName ) = 0; +private: +}; + +class cacServiceList : private osiMutex { +public: + epicsShareFunc cacServiceList (); + epicsShareFunc void registerService ( cacServiceIO &service ); + epicsShareFunc bool createChannel (const char *pName, cacChannel &chan); +private: + tsDLList services; +}; + +epicsShareExtern cacServiceList cacGlobalServiceList; diff --git a/src/ca/cacNotify.cpp b/src/ca/cacNotify.cpp new file mode 100644 index 000000000..cbcdfaadf --- /dev/null +++ b/src/ca/cacNotify.cpp @@ -0,0 +1,60 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +osiMutex cacNotify::defaultMutex; + +cacNotify::cacNotify () : pIO (0) +{ +} + +cacNotify::~cacNotify () +{ + cacNotifyIO *pTmpIO = this->pIO; + if ( pTmpIO ) { + this->pIO = 0; + pTmpIO->destroy (); + } +} + +cacNotify::lock () +{ + this->defaultMutex.lock (); +} + +cacNotify::unlock () +{ + this->defaultMutex.unlock (); +} + +void cacNotify::completionNotify () +{ + ca_printf ("CAC: IO completion with no handler installed?\n"); +} + +void cacNotify::completionNotify ( unsigned type, unsigned long count, const void *pData ) +{ + ca_printf ("IO completion with no handler installed? type=%u count=%u data pointer=%p\n", + type, count, pData); +} + +void cacNotify::exceptionNotify ( int status, const char *pContext ) +{ + ca_signal (status, pContext); +} + +void cacNotify::exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ) +{ + ca_signal_formated (status, __FILE__, __LINE__, "%s type=%d count=%ld\n", + pContext, type, count); +} diff --git a/src/ca/cacNotifyIO.cpp b/src/ca/cacNotifyIO.cpp new file mode 100644 index 000000000..4a9035f29 --- /dev/null +++ b/src/ca/cacNotifyIO.cpp @@ -0,0 +1,53 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +cacNotifyIO::cacNotifyIO ( cacNotify ¬ifyIn ) : notify ( notifyIn ) +{ + assert ( ! this->notify.pIO ); + this->notify.pIO = this; +} + +cacNotifyIO::~cacNotifyIO () +{ + if ( this->notify.pIO == this ) { + this->notify.pIO = 0; + this->notify.destroy (); + } +} + +void cacNotifyIO::destroy () +{ + delete this; +} + +void cacNotifyIO::completionNotify () +{ + this->notify.completionNotify (); +} + +void cacNotifyIO::completionNotify ( unsigned type, unsigned long count, const void *pData ) +{ + this->notify.completionNotify ( type, count, pData ); +} + +void cacNotifyIO::exceptionNotify ( int status, const char *pContext ) +{ + this->notify.exceptionNotify ( status, pContext ); +} + +void cacNotifyIO::exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ) +{ + this->notify.exceptionNotify ( status, pContext, type, count ); +} + diff --git a/src/ca/cacServiceList.cpp b/src/ca/cacServiceList.cpp new file mode 100644 index 000000000..52dc7fd24 --- /dev/null +++ b/src/ca/cacServiceList.cpp @@ -0,0 +1,55 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "iocinf.h" + +epicsShareDef cacServiceList cacGlobalServiceList; + +cacServiceList::cacServiceList () +{ +} + +void cacServiceList::registerService ( cacServiceIO &service ) +{ + this->lock (); + this->services.add ( service ); + this->unlock (); +} + +bool cacServiceList::createChannel (const char *pName, cacChannel &chan) +{ + cacChannelIO *pChanIO = 0; + + this->lock (); + tsDLIterBD iter ( this->services.first () ); + while ( iter != iter.eol () ) { + pChanIO = iter->createChannelIO ( chan, pName ); + if ( pChanIO ) { + break; + } + iter++; + } + this->unlock (); + + if ( pChanIO ) { + return true; + } + else { + return false; + } +} + diff --git a/src/ca/cadef.h b/src/ca/cadef.h index 296719677..9bef61047 100644 --- a/src/ca/cadef.h +++ b/src/ca/cadef.h @@ -49,11 +49,11 @@ extern "C" { #endif -typedef void *chid; -typedef chid chanId; /* for when the structures field name is "chid" */ -typedef long chtype; -typedef void *evid; -typedef double ca_real; +typedef struct oldChannel *chid; +typedef chid chanId; /* for when the structures field name is "chid" */ +typedef long chtype; +typedef struct oldSubscription *evid; +typedef double ca_real; /* Format for the arguments to user connection handlers */ struct connection_handler_args { @@ -197,6 +197,8 @@ epicsShareFunc int epicsShareAPI ca_task_initialize (void); /************************************************************************/ epicsShareFunc int epicsShareAPI ca_task_exit (void); +epicsShareFunc int epicsShareAPI ca_register_service ( struct cacServiceIO *pService ); + /************************************************************************ * anachronistic entry points * * **** Fetching a value while searching no longer supported**** * @@ -720,6 +722,8 @@ epicsShareFunc void epicsShareAPI ca_signal_formated (long ca_status, const char * channel R channel identifier */ epicsShareFunc const char * epicsShareAPI ca_host_name (chid channel); +/* thread safe version */ +const void epicsShareAPI ca_get_host_name ( chid pChan, char *pBuf, unsigned bufLength ); /* * CA_ADD_FD_REGISTRATION @@ -870,7 +874,7 @@ epicsShareFunc int epicsShareAPI ca_sg_stat (CA_SYNC_GID gid); * * pUserName R new user name string copied from this location */ -epicsShareFunc int epicsShareAPI ca_modify_user_name (const char *pUserName); +epicsShareFunc int epicsShareAPI ca_modify_user_name ( const char *pUserName ); /* * CA_MODIFY_HOST_NAME() @@ -880,7 +884,7 @@ epicsShareFunc int epicsShareAPI ca_modify_user_name (const char *pUserName); * * pHostName R new host name string copied from this location */ -epicsShareFunc int epicsShareAPI ca_modify_host_name (const char *pHostName); +epicsShareFunc int epicsShareAPI ca_modify_host_name ( const char *pHostName ); /* * ca_v42_ok() diff --git a/src/ca/caerr.h b/src/ca/caerr.h index 5adca223a..74c4e86a5 100644 --- a/src/ca/caerr.h +++ b/src/ca/caerr.h @@ -114,6 +114,7 @@ #define ECA_NOCONVERT DEFMSG(CA_K_WARNING, 50) #define ECA_BADCHID DEFMSG(CA_K_ERROR, 51) #define ECA_BADFUNCPTR DEFMSG(CA_K_ERROR, 52) +#define ECA_OPWILLBLOCK DEFMSG(CA_K_WARNING, 53) #ifndef CA_ERROR_GLBLSOURCE epicsShareExtern READONLY char *ca_message_text[]; @@ -166,14 +167,15 @@ READONLY char *ca_message_text[] "IO operations have completed", "IO operations are in progress", "Invalid synchronous group identifier", -"Put call back operation collision with put call back operation in progress", +"Put call back operation timed out waiting for put call back operation in progress", "Read access denied", "Write access denied", "Sorry, that anachronistic feature of CA is no longer supported", "The search request/beacon address list was empty after initialization", "Data conversion between client's type and the server's type failed", "Invalid channel identifier", -"Invalid function pointer" +"Invalid function pointer", +"op will block (not to be returned to user)" }; #endif diff --git a/src/ca/catime.c b/src/ca/catime.c index ac50a2894..b49629cc0 100644 --- a/src/ca/catime.c +++ b/src/ca/catime.c @@ -1,10 +1,10 @@ /* * - * CA performance test + * CA performance test * - * History - * joh 09-12-89 Initial release - * joh 12-20-94 portability + * History + * joh 09-12-89 Initial release + * joh 12-20-94 portability * * */ @@ -21,6 +21,8 @@ #include "caProto.h" #include "tsStamp.h" +#include "caDiagnostics.h" + #ifndef LOCAL #define LOCAL static #endif @@ -48,156 +50,136 @@ #define WAIT_FOR_ACK typedef struct testItem { - chid chix; - char name[40]; - int type; - int count; - union db_access_val val; + chid chix; + char name[40]; + int type; + int count; + union db_access_val val; }ti; ti itemList[ITERATION_COUNT]; -enum appendNumberFlag {appendNumber, dontAppendNumber}; - -int catime (char *channelName, enum appendNumberFlag appNF); typedef void tf (ti *pItems, unsigned iterations, unsigned *pInlineIter); LOCAL void test ( - ti *pItems, - unsigned iterations + ti *pItems, + unsigned iterations ); LOCAL void printSearchStat(unsigned iterations); -LOCAL tf test_pend; -LOCAL tf test_search; -LOCAL tf test_sync_search; -LOCAL tf test_free; -LOCAL tf test_wait; -LOCAL tf test_put; -LOCAL tf test_wait; -LOCAL tf test_get; +LOCAL tf test_pend; +LOCAL tf test_search; +LOCAL tf test_sync_search; +LOCAL tf test_free; +LOCAL tf test_wait; +LOCAL tf test_put; +LOCAL tf test_wait; +LOCAL tf test_get; LOCAL void measure_get_latency (ti *pItems, unsigned iterations); void timeIt( - tf *pfunc, - ti *pItem, - unsigned iterations + tf *pfunc, + ti *pItem, + unsigned iterations, + unsigned nBytes ); -#ifndef iocCore -int main(int argc, char **argv) -{ - char *pname; - if(argc <= 1 || argc>3){ -printf("usage: %s []\n", - argv[0]); - return -1; - } - else{ - pname = argv[1]; - if (argc==3) { - catime(pname, appendNumber); - } - else { - catime(pname, dontAppendNumber); - } - } - return 0; -} -#endif /* * catime () */ int catime (char *channelName, enum appendNumberFlag appNF) { - long i; - unsigned strsize; + long i; + unsigned strsize; + unsigned nBytes; - SEVCHK (ca_task_initialize(),"Unable to initialize"); + SEVCHK (ca_task_initialize(),"Unable to initialize"); - if (appNF==appendNumber) { - printf("Testing with %lu channels named %snnn\n", - (unsigned long) NELEMENTS(itemList), channelName); - } - else { - printf("Testing with %lu channels named %s\n", - (unsigned long) NELEMENTS(itemList), channelName); - } + if (appNF==appendNumber) { + printf("Testing with %lu channels named %snnn\n", + (unsigned long) NELEMENTS(itemList), channelName); + } + else { + printf("Testing with %lu channels named %s\n", + (unsigned long) NELEMENTS(itemList), channelName); + } - strsize = sizeof(itemList[i].name)-1; - for (i=0; ichix); - X += retry; - XX += retry*retry; - if (retry>max) { - max = retry; - } - if (retrychix); + X += retry; + XX += retry*retry; + if (retry>max) { + max = retry; + } + if (retry0.0) { - printf ("Elapsed Per Item = %12.8f sec, %10.1f Items per sec", - delay/(iterations*inlineIter), - (iterations*inlineIter)/delay); - if (pItems!=NULL) { - nBytes = sizeof(caHdr) + OCT_ROUND(dbr_size[pItems[0].type]); - printf(", %3.1f Mbps\n", - (iterations*inlineIter*nBytes*CHAR_BIT)/(delay*1e6)); - } - else { - printf ("\n"); - } - } - else { - printf ("Elapsed Per Item = %12.8f sec\n", - delay/(iterations*inlineIter)); - } + tsStampGetCurrent (&start_time); + (*pfunc) (pItems, iterations, &inlineIter); + tsStampGetCurrent (&end_time); + delay = tsStampDiffInSeconds (&end_time, &start_time); + if (delay>0.0) { + printf ("Elapsed Per Item = %12.8f sec, %10.1f Items per sec", + delay/(iterations*inlineIter), (iterations*inlineIter)/delay); + if ( pItems != NULL ) { + printf(", %3.1f Mbps\n", + (inlineIter*nBytes*CHAR_BIT)/(delay*1e6)); + } + else { + printf ("\n"); + } + } + else { + printf ("Elapsed Per Item = %12.8f sec\n", + delay/(iterations*inlineIter)); + } } - /* * test_pend() */ LOCAL void test_pend( -ti *pItems, -unsigned iterations, -unsigned *pInlineIter +ti *pItems, +unsigned iterations, +unsigned *pInlineIter ) { - unsigned i; - int status; + unsigned i; + int status; - for (i=0; itype, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_put( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_put( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_put( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_put( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_put( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_put( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_put( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_put( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_put( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - } + for (pi=pItems; pi<&pItems[iterations]; pi++) { + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + } #ifdef WAIT_FOR_ACK - status = ca_array_get (DBR_INT, 1, pItems[0].chix, &val); - SEVCHK (status, NULL); - status = ca_pend_io(100.0); + status = ca_array_get (DBR_INT, 1, pItems[0].chix, &val); + SEVCHK (status, NULL); + status = ca_pend_io(100.0); #endif - status = ca_array_put( - pItems[0].type, - pItems[0].count, - pItems[0].chix, - &pItems[0].val); - SEVCHK (status, NULL); - status = ca_flush_io(); - SEVCHK (status, NULL); + status = ca_array_put( + pItems[0].type, + pItems[0].count, + pItems[0].chix, + &pItems[0].val); + SEVCHK (status, NULL); + status = ca_flush_io(); + SEVCHK (status, NULL); - *pInlineIter = 10; + *pInlineIter = 10; } - /* * test_get () */ LOCAL void test_get( -ti *pItems, -unsigned iterations, -unsigned *pInlineIter +ti *pItems, +unsigned iterations, +unsigned *pInlineIter ) { - ti *pi; - int status; + ti *pi; + int status; - for (pi=pItems; pi<&pItems[iterations]; pi++) { - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - } - status = ca_pend_io(100.0); - SEVCHK (status, NULL); + for (pi=pItems; pi<&pItems[iterations]; pi++) { + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + } + status = ca_pend_io(100.0); + SEVCHK (status, NULL); - *pInlineIter = 10; + *pInlineIter = 10; } @@ -580,26 +553,26 @@ unsigned *pInlineIter * test_wait () */ LOCAL void test_wait ( -ti *pItems, -unsigned iterations, -unsigned *pInlineIter +ti *pItems, +unsigned iterations, +unsigned *pInlineIter ) { - ti *pi; - int status; + ti *pi; + int status; - for (pi=pItems; pi<&pItems[iterations]; pi++) { - status = ca_array_get( - pi->type, - pi->count, - pi->chix, - &pi->val); - SEVCHK (status, NULL); - status = ca_pend_io(100.0); - SEVCHK (status, NULL); - } + for (pi=pItems; pi<&pItems[iterations]; pi++) { + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + &pi->val); + SEVCHK (status, NULL); + status = ca_pend_io(100.0); + SEVCHK (status, NULL); + } - *pInlineIter = 1; + *pInlineIter = 1; } /* @@ -607,45 +580,45 @@ unsigned *pInlineIter */ LOCAL void measure_get_latency (ti *pItems, unsigned iterations) { - TS_STAMP end_time; - TS_STAMP start_time; - double delay; - double X = 0u; - double XX = 0u; - double max = DBL_MIN; - double min = DBL_MAX; - double mean; - double stdDev; - ti *pi; - int status; + TS_STAMP end_time; + TS_STAMP start_time; + double delay; + double X = 0u; + double XX = 0u; + double max = DBL_MIN; + double min = DBL_MAX; + double mean; + double stdDev; + ti *pi; + int status; - for (pi=pItems; pi<&pItems[iterations]; pi++) { - tsStampGetCurrent (&start_time); - status = ca_array_get (pi->type, pi->count, - pi->chix, &pi->val); - SEVCHK (status, NULL); - status = ca_pend_io (100.0); - SEVCHK (status, NULL); + for (pi=pItems; pi<&pItems[iterations]; pi++) { + tsStampGetCurrent (&start_time); + status = ca_array_get (pi->type, pi->count, + pi->chix, &pi->val); + SEVCHK (status, NULL); + status = ca_pend_io (100.0); + SEVCHK (status, NULL); - tsStampGetCurrent(&end_time); + tsStampGetCurrent(&end_time); - delay = tsStampDiffInSeconds(&end_time,&start_time); + delay = tsStampDiffInSeconds(&end_time,&start_time); - X += delay; - XX += delay*delay; + X += delay; + XX += delay*delay; - if (delay>max) { - max = delay; - } + if (delay>max) { + max = delay; + } - if (delay + +#include "caDiagnostics.h" + +int main(int argc, char **argv) +{ + char *pname; + + if(argc <= 1 || argc>3){ +printf("usage: %s []\n", + argv[0]); + return -1; + } + else{ + pname = argv[1]; + if (argc==3) { + catime(pname, appendNumber); + } + else { + catime(pname, dontAppendNumber); + } + } + return 0; +} diff --git a/src/ca/conn.cpp b/src/ca/conn.cpp index 4c4f5834f..8e94d9d23 100644 --- a/src/ca/conn.cpp +++ b/src/ca/conn.cpp @@ -19,659 +19,3 @@ LOCAL void logRetryInterval (pcac, char *pFN, unsigned lineno); #define LOGRETRYINTERVAL #endif -/* - * checkConnWatchdogs() - */ -void checkConnWatchdogs (cac *pcac) -{ - tcpiiu *piiu; - ca_real delay; - - LOCK (pcac); - - piiu = (tcpiiu *) ellFirst (&pcac->ca_iiuList); - while (piiu) { - tcpiiu *pnextiiu = (tcpiiu *) ellNext (&piiu->node); - - /* - * mark connection for shutdown if outgoing messages - * are not accepted by TCP/IP for EPICS_CA_CONN_TMO seconds - */ - if (piiu->sendPending) { - delay = tsStampDiffInSeconds (&pcac->currentTime, - &piiu->timeAtSendBlock); - if (delay>pcac->ca_connectTMO) { - initiateShutdownTCPIIU (piiu); - } - } - /* - * otherwise if we dont already have a send timer pending - * mark the connection for shutdown if an echo response - * times out - */ - else if (piiu->echoPending) { - - delay = tsStampDiffInSeconds (&pcac->currentTime, - &piiu->timeAtEchoRequest); - if (delay > CA_ECHO_TIMEOUT) { - initiateShutdownTCPIIU (piiu); - } - } - - piiu = pnextiiu; - } - - UNLOCK (pcac); -} - -/* - * - * MANAGE_CONN - * - * manages - * o retry of disconnected channels - * o connection heart beats - * - * - */ -void manage_conn (cac *pcac) -{ - tcpiiu *piiu; - ca_real delay; - - /* - * prevent recursion - */ - if(pcac->ca_manage_conn_active){ - return; - } - - pcac->ca_manage_conn_active = TRUE; - - /* - * issue connection heartbeat - * (if we dont see a beacon) - */ - LOCK (pcac); - for (piiu = (tcpiiu *) ellFirst (&pcac->ca_iiuList); - piiu; piiu = (tcpiiu *) ellNext (&piiu->node) ) { - - if (piiu->state!=iiu_connected) { - continue; - } - - /* - * remain backwards compatible with old servers - * ( this isnt an echo request ) - */ - if(!CA_V43(CA_PROTOCOL_VERSION, piiu->minor_version_number)){ - int stmo; - int rtmo; - - delay = tsStampDiffInSeconds (&pcac->currentTime, - &piiu->timeAtEchoRequest); - stmo = delay > CA_RETRY_PERIOD; - delay = tsStampDiffInSeconds (&pcac->currentTime, - &piiu->timeAtLastRecv); - rtmo = delay > CA_RETRY_PERIOD; - if(stmo && rtmo && !piiu->sendPending){ - piiu->timeAtEchoRequest = pcac->currentTime; - noop_msg (piiu); - } - continue; - } - - if (!piiu->echoPending) { - /* - * check delay since last beacon - */ - delay = tsStampDiffInSeconds (&pcac->currentTime, - &piiu->pBHE->timeStamp); - if (delay>pcac->ca_connectTMO) { - /* - * check delay since last virtual circuit receive - */ - delay = tsStampDiffInSeconds (&pcac->currentTime, - &piiu->timeAtLastRecv); - if (delay>pcac->ca_connectTMO) { - /* - * no activity - so ping through the virtual circuit - */ - echo_request (piiu); - } - } - } - } - UNLOCK (pcac); - - /* - * Stop here if there are not any disconnected channels - */ - if (!pcac->pudpiiu) { - pcac->ca_manage_conn_active = FALSE; - return; - } - if (pcac->pudpiiu->niiu.chidList.count == 0) { - pcac->ca_manage_conn_active = FALSE; - return; - } - - pcac->ca_manage_conn_active = FALSE; -} - -/* - * update beacon period - * - * updates beacon period, and looks for beacon anomalies - */ -LOCAL int updateBeaconPeriod (cac *pcac, bhe *pBHE) -{ - ca_real currentPeriod; - int netChange = FALSE; - - if (pBHE->timeStamp.secPastEpoch==0 && pBHE->timeStamp.nsec==0) { - /* - * this is the 1st beacon seen - the beacon time stamp - * was not initialized during BHE create because - * a TCP/IP connection created the beacon. - * (nothing to do but set the beacon time stamp and return) - */ - pBHE->timeStamp = pcac->currentTime; - - /* - * be careful about using beacons to reset the connection - * time out watchdog until we have received a ping response - * from the IOC (this makes the software detect reconnects - * faster when the server is rebooted twice in rapid - * succession before a 1st or 2nd beacon has been received) - */ - if (pBHE->piiu) { - pBHE->piiu->beaconAnomaly = TRUE; - } - - return netChange; - } - - /* - * compute the beacon period (if we have seen at least two beacons) - */ - currentPeriod = tsStampDiffInSeconds (&pcac->currentTime, - &pBHE->timeStamp); - if (pBHE->averagePeriod<0.0) { - ca_real totalRunningTime; - - /* - * this is the 2nd beacon seen. We cant tell about - * the change in period at this point so we just - * initialize the average period and return. - */ - pBHE->averagePeriod = currentPeriod; - - /* - * be careful about using beacons to reset the connection - * time out watchdog until we have received a ping response - * from the IOC (this makes the software detect reconnects - * faster when the server is rebooted twice in rapid - * succession before a 2nd beacon has been received) - */ - if (pBHE->piiu) { - pBHE->piiu->beaconAnomaly = TRUE; - } - - /* - * ignore beacons seen for the first time shortly after - * init, but do not ignore beacons arriving with a short - * period because the IOC was rebooted soon after the - * client starts up. - */ - totalRunningTime = tsStampDiffInSeconds (&pBHE->timeStamp, - &pcac->programBeginTime); - if (currentPeriod<=totalRunningTime) { - netChange = TRUE; -# ifdef DEBUG - { - char name[64]; - - ipAddrToA (&pBHE->inetAddr, name, sizeof(name)); - ca_printf (pcac, - "new beacon from %s with period=%f running time to first beacon=%f\n", - name, currentPeriod, totalRunningTime); - } -# endif - } - } - else { - - /* - * Is this an IOC seen because of a restored - * network segment? - * - * It may be possible to get false triggers here - * if the client is busy, but this does not cause - * problems because the echo response will tell us - * that the server is available - */ - if (currentPeriod >= pBHE->averagePeriod*1.25) { - - if (pBHE->piiu) { - /* - * trigger on any missing beacon - * if connected to this server - */ - pBHE->piiu->beaconAnomaly = TRUE; - } - - if (currentPeriod >= pBHE->averagePeriod*3.25) { - /* - * trigger on any 3 contiguous missing beacons - * if not connected to this server - */ - netChange = TRUE; - } - } - - -# ifdef DEBUG - if (netChange) { - char name[64]; - - ipAddrToA (&pBHE->inetAddr, name, sizeof(name)); - ca_printf (pcac, - "net resume seen %s cur=%f avg=%f\n", - name, currentPeriod, pBHE->averagePeriod); - } -# endif - - /* - * Is this an IOC seen because of an IOC reboot - * (beacon come at a higher rate just after the - * IOC reboots). Lower tolarance here because we - * dont have to worry about lost beacons. - * - * It may be possible to get false triggers here - * if the client is busy, but this does not cause - * problems because the echo response will tell us - * that the server is available - */ - if (currentPeriod <= pBHE->averagePeriod * 0.80) { -# ifdef DEBUG - { - char name[64]; - - ipAddrToA (&pBHE->inetAddr, name, sizeof(name)); - ca_printf (pcac, - "reboot seen %s cur=%f avg=%f\n", - name, currentPeriod, pBHE->averagePeriod); - } -# endif - netChange = TRUE; - if (pBHE->piiu) { - pBHE->piiu->beaconAnomaly = TRUE; - } - } - - /* - * update a running average period - */ - pBHE->averagePeriod = currentPeriod*0.125 + pBHE->averagePeriod*0.875; - } - - /* - * update beacon time stamp - */ - pBHE->timeStamp = pcac->currentTime; - - return netChange; -} - -/* - * MARK_SERVER_AVAILABLE - */ -void mark_server_available (cac *pcac, const struct sockaddr_in *pnet_addr) -{ - nciu *chan; - bhe *pBHE; - unsigned port; - int netChange; - - if (!pcac->pudpiiu) { - return; - } - - LOCK (pcac); - /* - * look for it in the hash table - */ - pBHE = lookupBeaconInetAddr (pcac, pnet_addr); - if (pBHE) { - - netChange = updateBeaconPeriod (pcac, pBHE); - - /* - * update state of health for active virtual circuits - * (only if we are not suspicious about past beacon changes - * until the next echo reply) - */ - if (pBHE->piiu) { - if (!pBHE->piiu->beaconAnomaly) { - pBHE->piiu->timeAtLastRecv = pcac->currentTime; - } - } - } - else { - /* - * This is the first beacon seen from this server. - * Wait until 2nd beacon is seen before deciding - * if it is a new server (or just the first - * time that we have seen a server's beacon - * shortly after the program started up) - */ - netChange = FALSE; - createBeaconHashEntry (pcac, pnet_addr, TRUE); - } - - if (!netChange) { - UNLOCK (pcac); - return; - } - - /* - * This part is needed when many machines - * have channels in a disconnected state that - * dont exist anywhere on the network. This insures - * that we dont have many CA clients synchronously - * flooding the network with broadcasts (and swamping - * out requests for valid channels). - * - * I fetch the local port number and use the low order bits - * as a pseudo random delay to prevent every one - * from replying at once. - */ - { - struct sockaddr_in saddr; - int saddr_length = sizeof(saddr); - int status; - - status = getsockname ( - pcac->pudpiiu->sock, - (struct sockaddr *)&saddr, - &saddr_length); - assert (status>=0); - port = ntohs(saddr.sin_port); - } - - { - ca_real delay; - - delay = (port&CA_RECAST_PORT_MASK); - delay /= MSEC_PER_SEC; - delay += CA_RECAST_DELAY; - - pcac->pudpiiu->searchTmr.reset (delay); - } - - /* - * set retry count of all disconnected channels - * to zero - */ - chan = (nciu *) ellFirst (&pcac->pudpiiu->niiu.chidList); - while (chan) { - chan->retry = 0u; - chan = (nciu *) ellNext (&chan->node); - } - - UNLOCK (pcac); - -# if DEBUG - { - char buf[64]; - ipAddrToA (pnet_addr, buf, sizeof(buf)); - printf ("new server available: %s\n", buf); - } -# endif - -} - -/* - * bhtHashIP () - */ -LOCAL unsigned bhtHashIP (cac *pcac, const struct sockaddr_in *pina) -{ - unsigned index; - -#if BHT_INET_ADDR_MASK != 0xff -# error BHT_INET_ADDR_MASK changed - recode this routine ! -#endif - - index = pina->sin_addr.s_addr; - index ^= pina->sin_port; - index = (index>>16u) ^ index; - index = (index>>8u) ^ index; - index &= BHT_INET_ADDR_MASK; - assert(indexca_beaconHash)); - return index; -} - -/* - * createBeaconHashEntry() - * - * LOCK must be applied - */ -bhe *createBeaconHashEntry( -cac *pcac, -const struct sockaddr_in *pina, -unsigned sawBeacon) -{ - bhe *pBHE; - unsigned index; - - pBHE = lookupBeaconInetAddr(pcac, pina); - if(pBHE){ - return pBHE; - } - - index = bhtHashIP (pcac,pina); - - pBHE = (bhe *)calloc (1,sizeof(*pBHE)); - if(!pBHE){ - return NULL; - } - -#ifdef DEBUG - { - char name[64]; - - ipAddrToA (pina, name, sizeof(name)); - ca_printf (pcac, "created beacon entry for %s\n", name); - } -#endif - - /* - * store the inet address - */ - pBHE->inetAddr = *pina; - - /* - * set average to -1.0 so that when the next beacon - * occurs we can distinguish between: - * o new server - * o existing server's beacon we are seeing - * for the first time shortly after program - * start up - */ - pBHE->averagePeriod = -1.0; - - /* - * if creating this in response to a search reply - * and not in response to a beacon then sawBeacon - * is false and we set the beacon time stamp to - * zero (so we can correctly compute the period - * between the 1st and 2nd beacons) - */ - if (sawBeacon) { - LOCK (pcac) - pBHE->timeStamp = pcac->currentTime; - UNLOCK (pcac) - } - else { - pBHE->timeStamp.secPastEpoch = 0; - pBHE->timeStamp.nsec = 0; - } - - /* - * install in the hash table - */ - pBHE->pNext = pcac->ca_beaconHash[index]; - pcac->ca_beaconHash[index] = pBHE; - - return pBHE; -} - -/* - * lookupBeaconInetAddr() - * - * LOCK must be applied - */ -bhe *lookupBeaconInetAddr ( -cac *pcac, -const struct sockaddr_in *pina) -{ - bhe *pBHE; - unsigned index; - - index = bhtHashIP (pcac,pina); - - pBHE = pcac->ca_beaconHash[index]; - while (pBHE) { - if ( pBHE->inetAddr.sin_addr.s_addr == pina->sin_addr.s_addr && - pBHE->inetAddr.sin_port == pina->sin_port) { - break; - } - pBHE = pBHE->pNext; - } - return pBHE; -} - -/* - * removeBeaconInetAddr() - * - * LOCK must be applied - */ -void removeBeaconInetAddr ( -cac *pcac, -const struct sockaddr_in *pina) -{ - bhe *pBHE; - bhe **ppBHE; - unsigned index; - - index = bhtHashIP (pcac,pina); - - ppBHE = &pcac->ca_beaconHash[index]; - pBHE = *ppBHE; - while (pBHE) { - if ( pBHE->inetAddr.sin_addr.s_addr == pina->sin_addr.s_addr && - pBHE->inetAddr.sin_port == pina->sin_port) { - *ppBHE = pBHE->pNext; - free (pBHE); - return; - } - ppBHE = &pBHE->pNext; - pBHE = *ppBHE; - } - assert (0); -} - -/* - * freeBeaconHash() - * - * LOCK must be applied - */ -void freeBeaconHash(struct cac *ca_temp) -{ - bhe *pBHE; - bhe **ppBHE; - int len; - - len = NELEMENTS(ca_temp->ca_beaconHash); - for( ppBHE = ca_temp->ca_beaconHash; - ppBHE < &ca_temp->ca_beaconHash[len]; - ppBHE++){ - - pBHE = *ppBHE; - while(pBHE){ - bhe *pOld; - - pOld = pBHE; - pBHE = pBHE->pNext; - free(pOld); - } - } -} - -/* - * Add chan to iiu and guarantee that - * one chan on the B cast iiu list is pointed to by - * ca_pEndOfBCastList - */ -void addToChanList (nciu *chan, netIIU *piiu) -{ - LOCK (piiu->iiu.pcas); - if ( piiu == &piiu->iiu.pcas->pudpiiu->niiu ) { - /* - * add to the beginning of the list so that search requests for - * this channel will be sent first (since the retry count is zero) - */ - if (ellCount(&piiu->chidList)==0) { - piiu->iiu.pcas->ca_pEndOfBCastList = chan; - } - /* - * add to the front of the list so that - * search requests for new channels will be sent first - */ - chan->retry = 0u; - ellInsert (&piiu->chidList, NULL, &chan->node); - } - else { - /* - * add to the beginning of the list until we - * have sent the claim message (after which we - * move it to the end of the list) - */ - chan->claimPending = TRUE; - ellInsert (&piiu->chidList, NULL, &chan->node); - } - chan->ciu.piiu = &piiu->iiu; - UNLOCK (piiu->iiu.pcas); -} - -/* - * Remove chan from B-cast IIU and guarantee that - * one chan on the list is pointed to by - * ca_pEndOfBCastList - */ -void removeFromChanList (nciu *chan) -{ - if ( chan->ciu.piiu == &chan->ciu.piiu->pcas->pudpiiu->niiu.iiu ) { - if (chan->ciu.piiu->pcas->ca_pEndOfBCastList == chan) { - if (ellPrevious(&chan->node)) { - chan->ciu.piiu->pcas->ca_pEndOfBCastList = (nciu *) - ellPrevious (&chan->node); - } - else { - chan->ciu.piiu->pcas->ca_pEndOfBCastList = (nciu *) - ellLast (&chan->ciu.piiu->pcas->pudpiiu->niiu.chidList); - } - } - ellDelete (&chan->ciu.piiu->pcas->pudpiiu->niiu.chidList, &chan->node); - } - else { - tcpiiu *piiu = iiuToTCPIIU (chan->ciu.piiu); - if ( ellCount (&piiu->niiu.chidList) == 1 ) { - initiateShutdownTCPIIU (piiu); - } - ellDelete (&piiu->niiu.chidList, &chan->node); - } - chan->ciu.piiu = NULL; -} - diff --git a/src/ca/convert.cpp b/src/ca/convert.cpp index dd4af8ab0..f5dc6719b 100644 --- a/src/ca/convert.cpp +++ b/src/ca/convert.cpp @@ -47,11 +47,6 @@ * * net format: big endian and IEEE float * - * typedef void CACVRTFUNC( - * void *pSource, - * void *pDestination, - * int hton, - * unsigned long count); */ LOCAL CACVRTFUNC cvrt_string; @@ -160,7 +155,7 @@ epicsShareDef CACVRTFUNC *cac_dbr_cvrt[] * */ LOCAL void cvrt_string( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -188,7 +183,7 @@ unsigned long num /* number of values */ * */ LOCAL void cvrt_short( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -216,7 +211,7 @@ unsigned long num /* number of values */ * */ LOCAL void cvrt_char( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -242,7 +237,7 @@ unsigned long num /* number of values */ * */ LOCAL void cvrt_long( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -270,7 +265,7 @@ unsigned long num /* number of values */ * */ LOCAL void cvrt_enum( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -302,7 +297,7 @@ unsigned long num /* number of values */ * */ LOCAL void cvrt_float( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -335,7 +330,7 @@ unsigned long num /* number of values */ * */ LOCAL void cvrt_double( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -377,7 +372,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_sts_string( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -415,7 +410,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_sts_short( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -450,7 +445,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_sts_float( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -476,7 +471,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_sts_double( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -505,7 +500,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_sts_enum( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -533,7 +528,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_gr_short( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -570,7 +565,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_gr_char( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -611,7 +606,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_gr_long( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -649,7 +644,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_gr_enum( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -682,7 +677,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_gr_double( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -744,7 +739,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_gr_float( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -807,7 +802,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_ctrl_short( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -847,7 +842,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_ctrl_long( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -887,7 +882,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_ctrl_char( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -925,7 +920,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_ctrl_double( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -989,7 +984,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_ctrl_float( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1052,7 +1047,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_ctrl_enum( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1088,7 +1083,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_sts_char( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1119,7 +1114,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_sts_long( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1151,7 +1146,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_time_string( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1183,7 +1178,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_time_short( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1217,7 +1212,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_time_float( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1245,7 +1240,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_time_double( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1274,7 +1269,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_time_enum( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1304,7 +1299,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_time_char( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1336,7 +1331,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_time_long( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1367,7 +1362,7 @@ unsigned long num /* number of values */ * */ LOCAL void cvrt_put_ackt( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ @@ -1401,7 +1396,7 @@ unsigned long num /* number of values */ ****************************************************************************/ LOCAL void cvrt_stsack_string( -void *s, /* source */ +const void *s, /* source */ void *d, /* destination */ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ diff --git a/src/ca/disconnectTimer.cpp b/src/ca/disconnectTimer.cpp new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/ca/disconnectTimer.cpp @@ -0,0 +1 @@ + diff --git a/src/ca/flow_control.cpp b/src/ca/flow_control.cpp index 09552b5f2..4e819bbc4 100644 --- a/src/ca/flow_control.cpp +++ b/src/ca/flow_control.cpp @@ -25,7 +25,7 @@ void flow_control_on (tcpiiu *piiu) { int status; - LOCK (piiu->niiu.iiu.pcas); + LOCK (piiu->pcas); /* * I prefer to avoid going into flow control @@ -33,10 +33,10 @@ void flow_control_on (tcpiiu *piiu) */ if (piiu->contiguous_msg_count >= MAX_CONTIGUOUS_MSG_COUNT) { if (!piiu->client_busy) { - status = ca_busy_message(piiu); + status = piiu->busyRequestMsg (); if (status==ECA_NORMAL) { - assert(piiu->niiu.iiu.pcas->ca_number_iiu_in_fcniiu.iiu.pcas->ca_number_iiu_in_fc++; + assert(piiu->pcas->ca_number_iiu_in_fcpcas->ca_number_iiu_in_fc++; piiu->client_busy = TRUE; # if defined(DEBUG) printf("fc on\n"); @@ -48,7 +48,7 @@ void flow_control_on (tcpiiu *piiu) piiu->contiguous_msg_count++; } - UNLOCK (piiu->niiu.iiu.pcas); + UNLOCK (piiu->pcas); return; } @@ -56,14 +56,14 @@ void flow_control_off (tcpiiu *piiu) { int status; - LOCK (piiu->niiu.iiu.pcas); + LOCK (piiu->pcas); piiu->contiguous_msg_count = 0; if (piiu->client_busy) { - status = ca_ready_message(piiu); + status = piiu->readyRequestMsg (); if (status==ECA_NORMAL) { - assert (piiu->niiu.iiu.pcas->ca_number_iiu_in_fc>0u); - piiu->niiu.iiu.pcas->ca_number_iiu_in_fc--; + assert (piiu->pcas->ca_number_iiu_in_fc>0u); + piiu->pcas->ca_number_iiu_in_fc--; piiu->client_busy = FALSE; # if defined(DEBUG) printf("fc off\n"); @@ -71,6 +71,6 @@ void flow_control_off (tcpiiu *piiu) } } - UNLOCK (piiu->niiu.iiu.pcas); + UNLOCK (piiu->pcas); return; } diff --git a/src/ca/getCallback.cpp b/src/ca/getCallback.cpp new file mode 100644 index 000000000..97cf06083 --- /dev/null +++ b/src/ca/getCallback.cpp @@ -0,0 +1,69 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "iocinf.h" +#include "oldAccess.h" + +tsFreeList < class getCallback > getCallback::freeList; + +getCallback::getCallback (oldChannel &chanIn, caEventCallBackFunc *pFuncIn, void *pPrivateIn) : + chan (chanIn), pFunc (pFuncIn), pPrivate (pPrivateIn) +{ +} + +getCallback::~getCallback () +{ +} + +void getCallback::destroy () +{ + delete this; +} + +void getCallback::completionNotify ( unsigned type, unsigned long count, const void *pData ) +{ + struct event_handler_args args; + args.usr = this->pPrivate; + args.chid = &this->chan; + args.type = type; + args.count = count; + args.status = ECA_NORMAL; + args.dbr = pData; + (*this->pFunc) (args); +} + +void getCallback::exceptionNotify (int status, const char *pContext) +{ + struct event_handler_args args; + args.usr = this->pPrivate; + args.chid = &this->chan; + args.type = 0; + args.count = 0; + args.status = status; + args.dbr = 0; + (*this->pFunc) (args); +} + +void * getCallback::operator new ( size_t size ) +{ + return getCallback::freeList.allocate ( size ); +} + +void getCallback::operator delete ( void *pCadaver, size_t size ) +{ + getCallback::freeList.release ( pCadaver, size ); +} \ No newline at end of file diff --git a/src/ca/inetAddrID_IL.h b/src/ca/inetAddrID_IL.h new file mode 100644 index 000000000..b3b4692cf --- /dev/null +++ b/src/ca/inetAddrID_IL.h @@ -0,0 +1,44 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +inline inetAddrID::inetAddrID (const struct sockaddr_in &addrIn) : + addr (addrIn) +{ +} + +inline bool inetAddrID::operator == (const inetAddrID &rhs) +{ + if (this->addr.sin_addr.s_addr == rhs.addr.sin_addr.s_addr) { + if (this->addr.sin_port == rhs.addr.sin_port) { + return true; + } + } + return false; +} + +inline resTableIndex inetAddrID::hash (unsigned nBitsHashIndex) const +{ + unsigned index; + index = this->addr.sin_addr.s_addr; + index ^= this->addr.sin_port; + return intId < unsigned, 8u, 32u > :: hashEngine (index); +} + +inline unsigned inetAddrID::maxIndexBitWidth () +{ + return 32u; +} + +inline unsigned inetAddrID::minIndexBitWidth () +{ + return 8u; +} diff --git a/src/ca/iocinf.cpp b/src/ca/iocinf.cpp index ab36b2e2f..7ae14b717 100644 --- a/src/ca/iocinf.cpp +++ b/src/ca/iocinf.cpp @@ -9,1149 +9,12 @@ * Author: Jeff Hill */ +#include "locationException.h" + /* Allocate storage for global variables in this module */ -#define CA_GLBLSOURCE -#include "iocinf.h" -#include "locationException.h" -#include "osiProcess.h" -#include "addrList.h" - -/* - * retryPendingClaims() - * - * This assumes that all channels with claims pending are at the - * front of the list (and that the channel is moved to the end of - * the list when a claim message has been sent for it) - * - * We send claim messages here until the outgoing message buffer - * will not accept any more messages - */ -LOCAL void retryPendingClaims (tcpiiu *piiu) -{ - bool success; - nciu *chan; - - LOCK (piiu->niiu.iiu.pcas); - while ( (chan = (nciu *) ellFirst (&piiu->niiu.chidList)) ) { - if (!chan->claimPending) { - piiu->claimsPending = FALSE; - cacRingBufferWriteFlush (&piiu->send); - break; - } - success = issue_claim_channel (chan); - if (!success) { - cacRingBufferWriteFlush (&piiu->send); - break; - } - } - UNLOCK (piiu->niiu.iiu.pcas); -} - -/* - * cac_connect_tcp () - */ -LOCAL void cac_connect_tcp (tcpiiu *piiu) -{ - int status; - - /* - * attempt to connect to a CA server - */ - while (1) { - int errnoCpy; - - status = connect ( piiu->sock, &piiu->dest.sa, - sizeof (piiu->dest.sa) ); - if (status == 0) { - break; - } - - errnoCpy = SOCKERRNO; - if (errnoCpy==SOCK_EISCONN) { - /* - * called connect after we are already connected - * (this appears to be how they provide - * connect completion notification) - */ - break; - } - else if ( - errnoCpy==SOCK_EINPROGRESS || - errnoCpy==SOCK_EWOULDBLOCK /* for WINSOCK */ - ) { - /* - * The socket is non-blocking and a - * connection attempt has been initiated, - * but not completed. - */ - return; - } - else if (errnoCpy==SOCK_EALREADY) { - return; - } -#ifdef _WIN32 - /* - * including this with vxWorks appears to - * cause trouble - */ - else if (errnoCpy==SOCK_EINVAL) { /* a SOCK_EALREADY alias used by early WINSOCK */ - return; - } -#endif - else if(errnoCpy==SOCK_EINTR) { - /* - * restart the system call if interrupted - */ - continue; - } - else { - initiateShutdownTCPIIU (piiu); - ca_printf (piiu->niiu.iiu.pcas, - "CAC: Unable to connect to \"%s\" because %d=\"%s\"\n", - piiu->host_name_str, errnoCpy, SOCKERRSTR(errnoCpy)); - return; - } - } - - /* - * put the iiu into the connected state - */ - piiu->state = iiu_connected; - - status = tsStampGetCurrent (&piiu->timeAtLastRecv); - assert (status==0); - - return; -} - -/* - * constructNIIU () - */ -void constructNIIU (cac *pcac, netIIU *piiu) -{ - piiu->iiu.pcas = pcac; - ellInit (&piiu->chidList); - - piiu->curDataMax = 0u; - piiu->curMsgBytes = 0u; - piiu->curDataBytes = 0u; - memset ( (void *) &piiu->curMsg, '\0', sizeof (piiu->curMsg) ); - piiu->pCurData = 0; -} - -/* - * destroyNIIU () - */ -LOCAL void destroyNIIU (netIIU *piiu) -{ - nciu *pChan, *pNext; - - pChan = (nciu *) ellFirst (&piiu->chidList); - while (pChan) { - pNext = (nciu *) ellNext (&pChan->node); - /* channel is destroyed here if it is disconnected */ - cacDisconnectChannel (pChan); - pChan = pNext; - } - - /* - * free message body cache - */ - if (piiu->pCurData) { - free (piiu->pCurData); - } -} - -/* - * cac_tcp_recv_msg () - */ -LOCAL void cac_tcp_recv_msg (tcpiiu *piiu) -{ - char *pProto; - unsigned writeSpace; - unsigned totalBytes; - int status; - - if ( piiu->state != iiu_connected ) { - return; - } - - pProto = (char *) cacRingBufferWriteReserve (&piiu->recv, &writeSpace); - - assert (writeSpace<=INT_MAX); - status = recv ( piiu->sock, pProto, (int) writeSpace, 0); - if ( status <= 0 ) { - int localErrno = SOCKERRNO; - - cacRingBufferWriteCommit (&piiu->recv, 0); - - if (status == 0) { - initiateShutdownTCPIIU (piiu); - return; - } - - if (localErrno == SOCK_SHUTDOWN) { - return; - } - - if ( localErrno == SOCK_EWOULDBLOCK || localErrno == SOCK_EINTR ) { - return; - } - - if ( localErrno != SOCK_EPIPE && - localErrno != SOCK_ECONNRESET && - localErrno != SOCK_ETIMEDOUT){ - ca_printf (piiu->niiu.iiu.pcas, - "CAC: unexpected TCP recv error: %s\n", SOCKERRSTR(localErrno)); - } - initiateShutdownTCPIIU (piiu); - return; - } - - assert ( ( (unsigned) status ) <= writeSpace ); - totalBytes = (unsigned) status; - - cacRingBufferWriteCommit (&piiu->recv, totalBytes); - cacRingBufferWriteFlush (&piiu->recv); - - status = tsStampGetCurrent (&piiu->timeAtLastRecv); - assert (status==0); - - return; -} - -/* - * cacSendThreadTCP () - */ -extern "C" void cacSendThreadTCP (void *pParam) -{ - tcpiiu *piiu = (tcpiiu *) pParam; - - while (1) { - unsigned sendCnt; - char *pOutBuf; - - pOutBuf = (char *) cacRingBufferReadReserve (&piiu->send, &sendCnt); - if (pOutBuf) { - int status; - - assert ( sendCnt <= INT_MAX ); - status = send ( piiu->sock, pOutBuf, (int) sendCnt, 0); - if (status>0) { - cacRingBufferReadCommit (&piiu->send, (unsigned long) status); - cacRingBufferReadFlush (&piiu->send); - if (piiu->claimsPending) { - retryPendingClaims (piiu); - } - } - else { - int localError = SOCKERRNO; - - cacRingBufferReadCommit (&piiu->send, 0); - - if (status==0) { - initiateShutdownTCPIIU (piiu); - break; - } - - if (localError == SOCK_SHUTDOWN) { - break; - } - - if ( localError != SOCK_EWOULDBLOCK && localError != SOCK_EINTR ) { - if ( localError != SOCK_EPIPE && localError != SOCK_ECONNRESET && - localError != SOCK_ETIMEDOUT) { - ca_printf ( piiu->niiu.iiu.pcas, "CAC: unexpected TCP send error: %s\n", - SOCKERRSTR (localError) ); - } - - initiateShutdownTCPIIU (piiu); - break; - } - } - } - else if ( piiu->state != iiu_connected ) { - break; - } - } - - semBinaryGive (piiu->sendThreadExitSignal); -} - -/* - * cacRecvThreadTCP () - */ -extern "C" void cacRecvThreadTCP (void *pParam) -{ - tcpiiu *piiu = (tcpiiu *) pParam; - unsigned chanDisconnectCount; - - cac_connect_tcp (piiu); - if ( piiu->state == iiu_connected ) { - threadId tid; - - tid = threadCreate ("CAC TCP Send", threadPriorityChannelAccessClient, - threadGetStackSize (threadStackMedium), cacSendThreadTCP, piiu); - if (tid) { - while (1) { - cac_tcp_recv_msg (piiu); - if ( piiu->state != iiu_connected ) { - break; - } - piiu->niiu.iiu.pcas->procThread.installLabor (*piiu); - } - } - } - - semBinaryMustTake (piiu->sendThreadExitSignal); - - piiu->niiu.iiu.pcas->procThread.removeLabor (*piiu); - - LOCK (piiu->niiu.iiu.pcas); - - chanDisconnectCount = ellCount (&piiu->niiu.chidList); - if (chanDisconnectCount) { - genLocalExcep (piiu->niiu.iiu.pcas, ECA_DISCONN, piiu->host_name_str); - } - - removeBeaconInetAddr (piiu->niiu.iiu.pcas, &piiu->dest.ia); - - ellDelete (&piiu->niiu.iiu.pcas->ca_iiuList, &piiu->node); - - destroyNIIU (&piiu->niiu); - - if (piiu->niiu.iiu.pcas->ca_fd_register_func) { - (*piiu->niiu.iiu.pcas->ca_fd_register_func) - ((void *)piiu->niiu.iiu.pcas->ca_fd_register_arg, piiu->sock, FALSE); - } - cacRingBufferDestroy (&piiu->recv); - cacRingBufferDestroy (&piiu->send); - socket_close (piiu->sock); - semBinaryDestroy (piiu->sendThreadExitSignal); - - semBinaryGive (piiu->recvThreadExitSignal); - - UNLOCK (piiu->niiu.iiu.pcas); - - free (piiu); -} - -/* - * constructTCPIIU () - * (lock must be applied by caller) - */ -tcpiiu * constructTCPIIU (cac *pcac, const struct sockaddr_in *pina, - unsigned minorVersion) -{ - SOCKET sock; - tcpiiu *piiu; - int status; - int flag; - threadId tid; - bhe *pBHE; - - /* - * look for an existing virtual circuit - */ - pBHE = lookupBeaconInetAddr (pcac, pina); - if (!pBHE) { - pBHE = createBeaconHashEntry (pcac, pina, FALSE); - if (!pBHE){ - UNLOCK (pcac); - return NULL; - } - } - - if (pBHE->piiu) { - if ( pBHE->piiu->state == iiu_connecting || - pBHE->piiu->state == iiu_connected ) { - return pBHE->piiu; - } - else { - return NULL; - } - } - - sock = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); - if (sock == INVALID_SOCKET) { - ca_printf (pcac, "CAC: unable to create virtual circuit because \"%s\"\n", - SOCKERRSTR (SOCKERRNO)); - return NULL; - } - - flag = TRUE; - status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, - (char *)&flag, sizeof(flag) ); - if (status < 0) { - ca_printf (pcac, "CAC: problems setting socket option TCP_NODELAY = \"%s\"\n", - SOCKERRSTR (SOCKERRNO)); - } - - flag = TRUE; - status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, - (char *)&flag, sizeof (flag) ); - if (status < 0) { - ca_printf (pcac, "CAC: problems setting socket option SO_KEEPALIVE = \"%s\"\n", - SOCKERRSTR (SOCKERRNO)); - } - -#if 0 - { - int i; - - /* - * some concern that vxWorks will run out of mBuf's - * if this change is made joh 11-10-98 - */ - i = MAX_MSG_SIZE; - status = setsockopt ( piiu->sock, SOL_SOCKET, SO_SNDBUF, - (char *)&i, sizeof (i) ); - if (status < 0) { - ca_printf (pcac, "CAC: problems setting socket option SO_SNDBUF = \"%s\"\n", - SOCKERRSTR (SOCKERRNO)); - } - i = MAX_MSG_SIZE; - status = setsockopt (piiu->sock, SOL_SOCKET, SO_RCVBUF, - (char *)&i, sizeof (i) ); - if (status < 0) { - ca_printf (pcac, "CAC: problems setting socket option SO_RCVBUF = \"%s\"\n", - SOCKERRSTR (SOCKERRNO)); - } - } -#endif - - piiu = (tcpiiu *) calloc (1, sizeof (*piiu) ); - if (!piiu) { - socket_close (sock); - return NULL; - } - - constructNIIU (pcac, &piiu->niiu); - piiu->sock = sock; - piiu->minor_version_number = minorVersion; - piiu->claimsPending = FALSE; - piiu->recvPending = FALSE; - piiu->pushPending = FALSE; - piiu->beaconAnomaly = FALSE; - piiu->dest.ia = *pina; - - status = cacRingBufferConstruct (&piiu->recv); - if (status) { - ca_printf (pcac, "CA: unable to create recv ring buffer\n"); - socket_close (sock); - destroyNIIU (&piiu->niiu); - free (piiu); - } - - status = cacRingBufferConstruct (&piiu->send); - if (status) { - ca_printf (pcac, "CA: unable to create send ring buffer\n"); - cacRingBufferDestroy (&piiu->recv); - socket_close (sock); - destroyNIIU (&piiu->niiu); - free (piiu); - } - - status = tsStampGetCurrent (&piiu->timeAtLastRecv); - assert (status==0); - - /* - * Save the Host name for efficient access in the - * future. - */ - ipAddrToA (&piiu->dest.ia, piiu->host_name_str, sizeof (piiu->host_name_str) ); - - /* - * TCP starts out in the connecting state and later transitions - * to the connected state - */ - piiu->state = iiu_connecting; - - piiu->sendThreadExitSignal = semBinaryCreate (semEmpty); - if ( !piiu->sendThreadExitSignal ) { - ca_printf (pcac, "CA: unable to create CA client send thread exit semaphore\n"); - cacRingBufferDestroy (&piiu->recv); - cacRingBufferDestroy (&piiu->send); - destroyNIIU (&piiu->niiu); - socket_close (sock); - free (piiu); - return NULL; - } - - piiu->recvThreadExitSignal = semBinaryCreate (semEmpty); - if ( !piiu->sendThreadExitSignal ) { - ca_printf (pcac, "CA: unable to create CA client send thread exit semaphore\n"); - semBinaryDestroy (piiu->sendThreadExitSignal); - cacRingBufferDestroy (&piiu->recv); - cacRingBufferDestroy (&piiu->send); - destroyNIIU (&piiu->niiu); - socket_close (sock); - free (piiu); - return NULL; - } - - tid = threadCreate ( - "CAC TCP Recv", - threadPriorityChannelAccessClient, - threadGetStackSize (threadStackMedium), - cacRecvThreadTCP, - piiu); - if (tid==0) { - ca_printf (pcac, "CA: unable to create CA client receive thread\n"); - semBinaryDestroy (piiu->recvThreadExitSignal); - semBinaryDestroy (piiu->sendThreadExitSignal); - cacRingBufferDestroy (&piiu->recv); - cacRingBufferDestroy (&piiu->send); - destroyNIIU (&piiu->niiu); - socket_close (sock); - free (piiu); - return NULL; - } - pBHE->piiu = piiu; - - ellAdd (&pcac->ca_iiuList, &piiu->node); - - if (pcac->ca_fd_register_func) { - (*pcac->ca_fd_register_func) ((void *)pcac->ca_fd_register_arg, piiu->sock, TRUE); - } - - return piiu; -} - -/* - * initiateShutdownTCPIIU () - */ -void initiateShutdownTCPIIU (tcpiiu *piiu) -{ - LOCK (piiu->niiu.iiu.pcas); - if (piiu->state != iiu_disconnected) { - piiu->state = iiu_disconnected; - shutdown (piiu->sock, SD_BOTH); - cacRingBufferShutDown (&piiu->send); - cacRingBufferShutDown (&piiu->recv); - } - UNLOCK (piiu->niiu.iiu.pcas); -} - -/* - * cac_udp_recv_msg () - */ -LOCAL int cac_udp_recv_msg (udpiiu *piiu) -{ - osiSockAddr src; - int src_size = sizeof (src); - int status; - - status = recvfrom (piiu->sock, piiu->recvBuf, sizeof (piiu->recvBuf), 0, - &src.sa, &src_size); - if (status < 0) { - int errnoCpy = SOCKERRNO; - - if (errnoCpy == SOCK_SHUTDOWN) { - return -1; - } - if (errnoCpy == SOCK_EWOULDBLOCK || errnoCpy == SOCK_EINTR) { - return 0; - } -# ifdef linux - /* - * Avoid spurious ECONNREFUSED bug - * in linux - */ - if (errnoCpy==SOCK_ECONNREFUSED) { - return 0; - } -# endif - ca_printf (piiu->niiu.iiu.pcas, - "Unexpected UDP recv error %s\n", SOCKERRSTR(errnoCpy)); - } - else if (status > 0) { - - status = post_msg (&piiu->niiu, &src.ia, - piiu->recvBuf, (unsigned long) status); - if ( status!=ECA_NORMAL || piiu->niiu.curMsgBytes ) { - char buf[64]; - - ipAddrToA (&src.ia, buf, sizeof(buf)); - - ca_printf (piiu->niiu.iiu.pcas, - "%s: bad UDP msg from %s\n", __FILE__, buf); - - /* - * resync the ring buffer - * (discard existing messages) - */ - piiu->niiu.curMsgBytes = 0; - piiu->niiu.curDataBytes = 0; - return 0; - } - } - - return 0; -} - -/* - * cacRecvThreadUDP () - */ -extern "C" void cacRecvThreadUDP (void *pParam) -{ - udpiiu *piiu = (udpiiu *) pParam; - int status; - - do { - status = cac_udp_recv_msg (piiu); - } while ( status == 0 ); - - semBinaryGive (piiu->recvThreadExitSignal); -} - -/* - * NOTIFY_CA_REPEATER() - * - * tell the cast repeater that another client needs fan out - * - * NOTES: - * 1) local communication only (no LAN traffic) - * - */ -void notify_ca_repeater (udpiiu *piiu) -{ - caHdr msg; - osiSockAddr saddr; - int status; - static int once = FALSE; - int len; - - if (piiu->repeaterContacted) { - return; - } - - if (piiu->repeaterTries > N_REPEATER_TRIES_PRIOR_TO_MSG ) { - if (!once) { - ca_printf (piiu->niiu.iiu.pcas, - "Unable to contact CA repeater after %d tries\n", - N_REPEATER_TRIES_PRIOR_TO_MSG); - ca_printf (piiu->niiu.iiu.pcas, - "Silence this message by starting a CA repeater daemon\n"); - once = TRUE; - } - } - - /* - * In 3.13 beta 11 and before the CA repeater calls local_addr() - * to determine a local address and does not allow registration - * messages originating from other addresses. In these - * releases local_addr() returned the address of the first enabled - * interface found, and this address may or may not have been the loop - * back address. Starting with 3.13 beta 12 local_addr() was - * changed to always return the address of the first enabled - * non-loopback interface because a valid non-loopback local - * address is required in the beacon messages. Therefore, to - * guarantee compatibility with past versions of the repeater - * we alternate between the address returned by local_addr() - * and the loopback address here. - * - * CA repeaters in R3.13 beta 12 and higher allow - * either the loopback address or the address returned - * by local address (the first non-loopback address found) - */ - if (piiu->repeaterTries&1) { - saddr = osiLocalAddr (piiu->sock); - if (saddr.sa.sa_family != AF_INET) { - /* - * use the loop back address to communicate with the CA repeater - * if this os does not have interface query capabilities - * - * this will only work with 3.13 beta 12 CA repeaters or later - */ - saddr.ia.sin_family = AF_INET; - saddr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - saddr.ia.sin_port = htons (piiu->repeaterPort); - } - } - else { - saddr.ia.sin_family = AF_INET; - saddr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - saddr.ia.sin_port = htons (piiu->repeaterPort); - } - - memset ((char *)&msg, 0, sizeof(msg)); - msg.m_cmmd = htons (REPEATER_REGISTER); - msg.m_available = saddr.ia.sin_addr.s_addr; - - /* - * Intentionally sending a zero length message here - * until most CA repeater daemons have been restarted - * (and only then will accept the above protocol) - * (repeaters began accepting this protocol - * starting with EPICS 3.12) - * - * SOLARIS will not accept a zero length message - * and we are just porting there for 3.12 so - * we will use the new protocol for 3.12 - * - * recent versions of UCX will not accept a zero - * length message and we will assume that folks - * using newer versions of UCX have rebooted (and - * therefore restarted the CA repeater - and therefore - * moved it to an EPICS release that accepts this protocol) - */ -# if defined (DOES_NOT_ACCEPT_ZERO_LENGTH_UDP) - len = sizeof (msg); -# else - len = 0; -# endif - - status = sendto (piiu->sock, (char *)&msg, len, - 0, (struct sockaddr *)&saddr, sizeof(saddr)); - if (status < 0) { - int errnoCpy = SOCKERRNO; - if( errnoCpy != SOCK_EINTR && - errnoCpy != SOCK_EWOULDBLOCK && - /* - * This is returned from Linux when - * the repeater isnt running - */ - errnoCpy != SOCK_ECONNREFUSED - ) { - ca_printf (piiu->niiu.iiu.pcas, - "CAC: error sending to repeater was \"%s\"\n", - SOCKERRSTR(errnoCpy)); - } - } - piiu->repeaterTries++; - piiu->contactRepeater = 0u; -} - -/* - * cacSendThreadUDP () - */ -extern "C" void cacSendThreadUDP (void *pParam) -{ - udpiiu *piiu = (udpiiu *) pParam; - - while (1) { - int status; - - if (piiu->contactRepeater) { - notify_ca_repeater (piiu); - } - - semMutexMustTake (piiu->xmitBufLock); - - if (piiu->nBytesInXmitBuf > 0) { - osiSockAddrNode *pNode; - - pNode = (osiSockAddrNode *) ellFirst (&piiu->dest); - while (pNode) { - - assert ( piiu->nBytesInXmitBuf <= INT_MAX ); - status = sendto (piiu->sock, piiu->xmitBuf, - (int) piiu->nBytesInXmitBuf, 0, - &pNode->addr.sa, sizeof(pNode->addr.sa)); - if (status <= 0) { - int localErrno = SOCKERRNO; - - if (status==0) { - break; - } - - if (localErrno == SOCK_SHUTDOWN) { - break; - } - else if ( localErrno == SOCK_EINTR ) { - status = 1; - } - else { - char buf[64]; - - ipAddrToA (&pNode->addr.ia, buf, sizeof (buf)); - - ca_printf (piiu->niiu.iiu.pcas, - "CAC: error = \"%s\" sending UDP msg to %s\n", - SOCKERRSTR(localErrno), buf); - - break; - } - } - pNode = (osiSockAddrNode *) ellNext (&pNode->node); - } - - piiu->nBytesInXmitBuf = 0u; - - if (status<=0) { - break; - } - } - - semMutexGive (piiu->xmitBufLock); - - semBinaryMustTake (piiu->xmitSignal); - } - - semBinaryGive (piiu->sendThreadExitSignal); -} - -/* - * cacShutdownUDP () - */ -void cacShutdownUDP (udpiiu &iiu) -{ - shutdown (iiu.sock, SD_BOTH); - semBinaryGive (iiu.xmitSignal); -} - -/* - * udpiiu::~udpiiu () - */ -udpiiu::~udpiiu () -{ - semBinaryMustTake (this->recvThreadExitSignal); - semBinaryMustTake (this->sendThreadExitSignal); - - semBinaryDestroy (this->xmitSignal); - semMutexDestroy (this->xmitBufLock); - semBinaryDestroy (this->recvThreadExitSignal); - semBinaryDestroy (this->sendThreadExitSignal); - ellFree (&this->dest); - - if (this->niiu.iiu.pcas->ca_fd_register_func) { - (*this->niiu.iiu.pcas->ca_fd_register_func) - (this->niiu.iiu.pcas->ca_fd_register_arg, this->sock, FALSE); - } - destroyNIIU (&this->niiu); - socket_close (this->sock); -} - -/* - * repeater_installed () - * - * Test for the repeater already installed - * - * NOTE: potential race condition here can result - * in two copies of the repeater being spawned - * however the repeater detects this, prints a message, - * and lets the other task start the repeater. - * - * QUESTION: is there a better way to test for a port in use? - * ANSWER: none that I can find. - * - * Problems with checking for the repeater installed - * by attempting to bind a socket to its address - * and port. - * - * 1) Closed socket may not release the bound port - * before the repeater wakes up and tries to grab it. - * Attempting to bind the open socket to another port - * also does not work. - * - * 072392 - problem solved by using SO_REUSEADDR - */ -int repeater_installed (udpiiu *piiu) -{ - int installed = FALSE; - int status; - SOCKET sock; - struct sockaddr_in bd; - int flag; - - sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock == INVALID_SOCKET) { - return installed; - } - - memset ( (char *) &bd, 0, sizeof (bd) ); - bd.sin_family = AF_INET; - bd.sin_addr.s_addr = htonl (INADDR_ANY); - bd.sin_port = htons (piiu->repeaterPort); - status = bind (sock, (struct sockaddr *) &bd, sizeof(bd) ); - if (status<0) { - if (SOCKERRNO == SOCK_EADDRINUSE) { - installed = TRUE; - } - } - - /* - * turn on reuse only after the test so that - * this works on kernels that support multicast - */ - flag = TRUE; - status = setsockopt ( sock, SOL_SOCKET, SO_REUSEADDR, - (char *)&flag, sizeof (flag) ); - if (status<0) { - ca_printf (piiu->niiu.iiu.pcas, "CAC: set socket option reuseaddr set failed\n"); - } - - socket_close (sock); - - return installed; -} - -// -// udpiiu::udpiiu () -// -udpiiu::udpiiu (cac *pcac) : - searchTmr (*this, pcac->timerQueue), - repeaterSubscribeTmr (*this, pcac->timerQueue) -{ - static const unsigned short PORT_ANY = 0u; - osiSockAddr addr; - int boolValue = TRUE; - int status; - threadId tid; - - this->repeaterPort = - envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT, CA_REPEATER_PORT); - - this->sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (this->sock == INVALID_SOCKET) { - ca_printf (pcac, "CAC: unable to create datagram socket because = \"%s\"\n", - SOCKERRSTR (SOCKERRNO)); - throwWithLocation ( noSocket () ); - } - - status = setsockopt ( this->sock, SOL_SOCKET, SO_BROADCAST, - (char *) &boolValue, sizeof (boolValue) ); - if (status<0) { - ca_printf (pcac, "CAC: unable to enable IP broadcasting because = \"%s\"\n", - SOCKERRSTR (SOCKERRNO)); - } - -#if 0 - { - /* - * some concern that vxWorks will run out of mBuf's - * if this change is made joh 11-10-98 - * - * bump up the UDP recv buffer - */ - int size = 1u<<15u; - status = setsockopt ( this->sock, SOL_SOCKET, SO_RCVBUF, - (char *)&size, sizeof (size) ); - if (status<0) { - ca_printf ("CAC: unable to set socket option SO_RCVBUF because \"%s\"\n", - SOCKERRSTR (SOCKERRNO)); - } - } -#endif - - /* - * force a bind to an unconstrained address because we may end - * up receiving first - */ - memset ( (char *)&addr, 0 , sizeof (addr) ); - addr.ia.sin_family = AF_INET; - addr.ia.sin_addr.s_addr = htonl (INADDR_ANY); - addr.ia.sin_port = htons (PORT_ANY); - status = bind (this->sock, &addr.sa, sizeof (addr) ); - if (status<0) { - socket_close (this->sock); - ca_printf (pcac, "CAC: unable to bind to an unconstrained address because = \"%s\"\n", - SOCKERRSTR (SOCKERRNO)); - throwWithLocation ( noSocket () ); - } - - constructNIIU (pcac, &this->niiu); - - this->nBytesInXmitBuf = 0u; - this->contactRepeater = 0u; - this->repeaterContacted = 0u; - this->repeaterTries = 0u; - - this->xmitBufLock = semMutexCreate (); - if (!this->xmitBufLock) { - socket_close (this->sock); - destroyNIIU (&this->niiu); - throwWithLocation ( noMemory () ); - } - - this->recvThreadExitSignal = semBinaryCreate (semEmpty); - if (!this->recvThreadExitSignal) { - semMutexDestroy (this->xmitBufLock); - socket_close (this->sock); - destroyNIIU (&this->niiu); - throwWithLocation ( noMemory () ); - } - - this->sendThreadExitSignal = semBinaryCreate (semEmpty); - if (!this->sendThreadExitSignal) { - semBinaryDestroy (this->recvThreadExitSignal); - semMutexDestroy (this->xmitBufLock); - destroyNIIU (&this->niiu); - socket_close (this->sock); - throwWithLocation ( noMemory () ); - } - - this->xmitSignal = semBinaryCreate (semEmpty); - if (!this->sendThreadExitSignal) { - ca_printf (pcac, "CA: unable to create xmit signal\n"); - semBinaryDestroy (this->recvThreadExitSignal); - semBinaryDestroy (this->sendThreadExitSignal); - semMutexDestroy (this->xmitBufLock); - destroyNIIU (&this->niiu); - socket_close (this->sock); - throwWithLocation ( noMemory () ); - } - - /* - * load user and auto configured - * broadcast address list - */ - ellInit (&this->dest); - configureChannelAccessAddressList (&this->dest, this->sock, pcac->ca_server_port); - if ( ellCount ( &this->dest ) == 0 ) { - genLocalExcep ( NULL, ECA_NOSEARCHADDR, NULL ); - } - - tid = threadCreate ("CAC UDP Recv", threadPriorityChannelAccessClient, - threadGetStackSize (threadStackMedium), cacRecvThreadUDP, this); - if (tid==0) { - ca_printf (pcac, "CA: unable to create UDP receive thread\n"); - shutdown (this->sock, SD_BOTH); - semBinaryDestroy (this->xmitSignal); - semBinaryDestroy (this->recvThreadExitSignal); - semBinaryDestroy (this->sendThreadExitSignal); - semMutexDestroy (this->xmitBufLock); - destroyNIIU (&this->niiu); - socket_close (this->sock); - throwWithLocation ( noMemory () ); - } - - tid = threadCreate ( "CAC UDP Send", threadPriorityChannelAccessClient, - threadGetStackSize (threadStackMedium), cacSendThreadUDP, this); - if (tid==0) { - ca_printf (pcac, "CA: unable to create UDP transmitt thread\n"); - shutdown (this->sock, SD_BOTH); - semMutexMustTake (this->recvThreadExitSignal); - semBinaryDestroy (this->xmitSignal); - semBinaryDestroy (this->recvThreadExitSignal); - semBinaryDestroy (this->sendThreadExitSignal); - semMutexDestroy (this->xmitBufLock); - destroyNIIU (&this->niiu); - socket_close (this->sock); - throwWithLocation ( noMemory () ); - } - - if (pcac->ca_fd_register_func) { - (*pcac->ca_fd_register_func) (pcac->ca_fd_register_arg, this->sock, TRUE); - } - - if ( ! repeater_installed (this) ) { - osiSpawnDetachedProcessReturn osptr; - - /* - * This is not called if the repeater is known to be - * already running. (in the event of a race condition - * the 2nd repeater exits when unable to attach to the - * repeater's port) - */ - osptr = osiSpawnDetachedProcess ("CA Repeater", "caRepeater"); - if (osptr==osiSpawnDetachedProcessNoSupport) { - tid = threadCreate ( - "CA repeater", - threadPriorityChannelAccessClient, - threadGetStackSize (threadStackMedium), - caRepeaterThread, - 0); - if (tid==0) { - ca_printf (pcac, "CA: unable to create CA repeater daemon thread\n"); - } - } - else if (osptr==osiSpawnDetachedProcessFail) { - ca_printf (pcac, "CA: unable to start CA repeater daemon detached process\n"); - } - } - - this->repeaterSubscribeTmr.reschedule (); -} - - -/* - * cacDisconnectChannel() - */ -void cacDisconnectChannel (nciu *chan) -{ - cac *pcac = chan->ciu.piiu->pcas; - - LOCK (pcac); - - if (chan->ciu.piiu == &pcac->pudpiiu->niiu.iiu) { - netChannelDestroy (pcac, chan->cid); - UNLOCK (pcac); - return; - } - - chan->type = USHRT_MAX; - chan->count = 0u; - chan->sid = UINT_MAX; - chan->ar.read_access = FALSE; - chan->ar.write_access = FALSE; - - /* - * call their connection handler as required - */ - if ( chan->connected ) { - nmiu *monix, *next; - - /* - * look for events that have an event cancel in progress - */ - for (monix = (nmiu *) ellFirst (&chan->eventq); - monix; monix = next) { - - next = (nmiu *) ellNext (&monix->node); - - if (monix->cmd==CA_PROTO_EVENT_ADD) { - /* - * if there is an event cancel in progress - * delete the event - we will never receive - * an event cancel confirm from this server - */ - if (monix->miu.usr_func == NULL) { - caIOBlockFree (chan->ciu.piiu->pcas, monix->id); - } - } - else { - struct event_handler_args args; - - args.usr = monix->miu.usr_arg; - args.chid = monix->miu.pChan; - args.type = monix->miu.type; - args.count = monix->miu.count; - args.status = ECA_DISCONN; - args.dbr = NULL; - - if (monix->miu.usr_func) { - (*monix->miu.usr_func) (args); - } - caIOBlockFree (chan->ciu.piiu->pcas, monix->id); - } - } - - if (chan->ciu.pConnFunc) { - struct connection_handler_args args; - - args.chid = (chid) &chan->ciu; - args.op = CA_OP_CONN_DOWN; - (*chan->ciu.pConnFunc) (args); - } - if (chan->ciu.pAccessRightsFunc) { - struct access_rights_handler_args args; - - args.chid = (chid) &chan->ciu; - args.ar = chan->ar; - (*chan->ciu.pAccessRightsFunc) (args); - } - } - removeFromChanList (chan); - /* - * try to reconnect - */ - assert (pcac->pudpiiu); - addToChanList (chan, &pcac->pudpiiu->niiu); - pcac->pudpiiu->searchTmr.reset (0.0); - UNLOCK (pcac); -} +#define CA_GLBLSOURCE +#include "iocinf.h" +#include "addrList.h" /* * localHostName() @@ -1191,164 +54,6 @@ char *localHostName () return pTmp; } -#if 0 -/* - * cacPushPending() - */ -LOCAL unsigned cacPushPending (cac *pcac) -{ - unsigned pending = FALSE; - unsigned long bytesPending; - netIIU *piiu; - - LOCK (pcac); - for ( piiu = (netIIU *) ellFirst (&pcac->ca_iiuList); - piiu; piiu = (netIIU *) ellNext (&piiu->node) ){ - - if (piiu == pcac->pudpiiu) { - continue; - } - - if ( piiu->state == iiu_connected && piiu->pushPending ) { - bytesPending = cacRingBufferReadSize (&piiu->send, FALSE); - if(bytesPending > 0u){ - pending = TRUE; - break; - } - } - } - UNLOCK (pcac); - - return pending; -} -#endif - -#if 0 -/* - * CAC_MUX_IO() - */ -void cac_mux_io (cac *pcac, double maxDelay, unsigned iocCloseAllowed) -{ - int count; - double timeOut; - unsigned countDown; - - /* - * first check for pending recv's with a zero time out so that - * 1) flow control works correctly (and) - * 2) we queue up sends resulting from recvs properly - * (this results in improved max throughput) - * - * ... but dont allow this to go on forever if a fast - * server is flooding a slow client with monitors ... - */ - countDown = 512u; - while (--countDown) { - /* - * NOTE cac_select_io() will set the - * send flag for a particular iiu irregradless - * of what is requested here if piiu->pushPending - * is set - */ - count = cac_select_io (pcac, 0.0, CA_DO_RECVS); - if (count<=0) { - break; - } - ca_process_input_queue (pcac); - } - - /* - * manage search timers and detect disconnects - */ - manage_conn(pcac); - - /* - * next check for pending writes's with the specified time out - * - * ... but dont allow this to go on forever if a fast - * server is flooding a slow client with monitors ... - */ - countDown = 512u; - timeOut = maxDelay; - while (TRUE) { - count = cac_select_io (pcac, timeOut, CA_DO_RECVS|CA_DO_SENDS); - countDown--; - if (count<=0 || countDown==0u) { - /* - * if its a flush then loop until all - * of the send buffers are empty - */ - if (pcac->ca_flush_pending) { - /* - * complete flush is deferred if we are - * inside an event routine - */ - if (EVENTLOCKTEST(pcac)) { - break; - } - else { - if ( cacPushPending (pcac) ) { - countDown = 512u; - timeOut = cac_fetch_poll_period (pcac); - /* - * allow connection to be marked disconnected - * if we wait too long for the flush to occurr - */ - checkConnWatchdogs (pcac, FALSE); - } - else { - pcac->ca_flush_pending = FALSE; - break; - } - } - } - else { - break; - } - } - else { - timeOut = 0.0; - } - ca_process_input_queue (pcac); - } - - checkConnWatchdogs (pcac); -} -#endif - -tcpiiu *iiuToTCPIIU (baseIIU *pIn) -{ - char *pc = (char *) pIn; - - assert (pIn != &pIn->pcas->localIIU.iiu); - assert (pIn != &pIn->pcas->pudpiiu->niiu.iiu); - pc -= offsetof (tcpiiu, niiu.iiu); - return (tcpiiu *) pc; -} - -/* - * convert a generic IIU pointer to a network IIU - */ -netIIU *iiuToNIIU (baseIIU *pIn) -{ - char *pc = (char *) pIn; - - assert (pIn != &pIn->pcas->localIIU.iiu); - pc -= offsetof (netIIU, iiu); - return (netIIU *) pc; -} - -/* - * convert a generic IIU pointer to a local IIU - */ -lclIIU *iiuToLIIU (baseIIU *pIn) -{ - char *pc = (char *) pIn; - - assert (pIn == &pIn->pcas->localIIU.iiu); - pc -= offsetof (lclIIU, iiu); - return (lclIIU *) pc; -} /* * getToken() @@ -1402,14 +107,14 @@ epicsShareFunc void epicsShareAPI addAddrToChannelAccessAddressList while ( ( pToken = getToken (&pStr, buf, sizeof (buf) ) ) ) { status = aToIPAddr ( pToken, port, &addr ); if (status<0) { - errlogPrintf ("%s: Parsing '%s'\n", __FILE__, pEnv->name); - errlogPrintf ("\tBad internet address or host name: '%s'\n", pToken); + ca_printf ("%s: Parsing '%s'\n", __FILE__, pEnv->name); + ca_printf ("\tBad internet address or host name: '%s'\n", pToken); continue; } pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode)); if (pNewNode==NULL) { - errlogPrintf ("addAddrToChannelAccessAddressList(): no memory available for configuration\n"); + ca_printf ("addAddrToChannelAccessAddressList(): no memory available for configuration\n"); return; } @@ -1424,13 +129,57 @@ epicsShareFunc void epicsShareAPI addAddrToChannelAccessAddressList return; } +/* + * setPortAndRemoveDuplicates () + */ +epicsShareFunc void epicsShareAPI setPortAndRemoveDuplicates + (ELLLIST *pDestList, ELLLIST *pSrcList, unsigned short port) +{ + osiSockAddrNode *pNode; + + /* + * eliminate duplicates and set the port + */ + while ( (pNode = (osiSockAddrNode *) ellGet ( pSrcList ) ) ) { + osiSockAddrNode *pTmpNode; + + if ( pNode->addr.sa.sa_family == AF_INET ) { + /* + * set the correct destination port + */ + pNode->addr.ia.sin_port = htons (port); + + pTmpNode = (osiSockAddrNode *) ellFirst (pDestList); + while ( pTmpNode ) { + if (pTmpNode->addr.sa.sa_family == AF_INET) { + if (pNode->addr.ia.sin_addr.s_addr == pTmpNode->addr.ia.sin_addr.s_addr && + pNode->addr.ia.sin_port == pTmpNode->addr.ia.sin_port) { + char buf[64]; + ipAddrToA (&pNode->addr.ia, buf, sizeof(buf)); + ca_printf ("Warning: Duplicate EPICS CA Address list entry \"%s\" discarded\n", buf); + free (pNode); + pNode = NULL; + break; + } + } + pTmpNode = (osiSockAddrNode *) ellNext (&pNode->node); + } + if (pNode) { + ellAdd (pDestList, &pNode->node); + } + } + else { + ellAdd (pDestList, &pNode->node); + } + } +} + /* * configureChannelAccessAddressList () */ epicsShareFunc void epicsShareAPI configureChannelAccessAddressList (ELLLIST *pList, SOCKET sock, unsigned short port) { - osiSockAddrNode *pNode; ELLLIST tmpList; char *pstr; char yesno[32u]; @@ -1469,48 +218,14 @@ epicsShareFunc void epicsShareAPI configureChannelAccessAddressList addAddrToChannelAccessAddressList ( &tmpList, &EPICS_CA_ADDR_LIST, port ); - /* - * eliminate duplicates and set the port - */ - while ( (pNode = (osiSockAddrNode *) ellGet ( &tmpList ) ) ) { - osiSockAddrNode *pTmpNode; - - if ( pNode->addr.sa.sa_family == AF_INET ) { - /* - * set the correct destination port - */ - pNode->addr.ia.sin_port = htons (port); - - pTmpNode = (osiSockAddrNode *) ellFirst (pList); - while ( pTmpNode ) { - if (pTmpNode->addr.sa.sa_family == AF_INET) { - if (pNode->addr.ia.sin_addr.s_addr == pTmpNode->addr.ia.sin_addr.s_addr && - pNode->addr.ia.sin_port == pTmpNode->addr.ia.sin_port) { - char buf[64]; - ipAddrToA (&pNode->addr.ia, buf, sizeof(buf)); - errlogPrintf ("Warning: Duplicate EPICS CA Address list entry \"%s\" discarded\n", buf); - free (pNode); - pNode = NULL; - break; - } - } - pTmpNode = (osiSockAddrNode *) ellNext (&pNode->node); - } - if (pNode) { - ellAdd (pList, &pNode->node); - } - } - else { - ellAdd (pList, &pNode->node); - } - } + setPortAndRemoveDuplicates (pList, &tmpList, port); } /* * printChannelAccessAddressList () */ -epicsShareFunc void epicsShareAPI printChannelAccessAddressList (ELLLIST *pList) +epicsShareFunc void epicsShareAPI printChannelAccessAddressList (const ELLLIST *pList) { osiSockAddrNode *pNode; diff --git a/src/ca/iocinf.h b/src/ca/iocinf.h index 03d6fe672..cf8d13eca 100644 --- a/src/ca/iocinf.h +++ b/src/ca/iocinf.h @@ -42,18 +42,22 @@ /* * EPICS includes */ +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "epicsAssert.h" #include "bucketLib.h" #include "ellLib.h" #include "envDefs.h" #include "epicsPrint.h" #include "tsStamp.h" +#include "tsFreeList.h" +#include "tsDLList.h" #include "osiSock.h" #include "osiSem.h" #include "osiThread.h" #include "osiTimer.h" #include "osiMutex.h" #include "osiEvent.h" +#include "resourceLib.h" #if defined(epicsExportSharedSymbols) #error suspect that libCom was not imported @@ -64,6 +68,7 @@ */ #define epicsExportSharedSymbols #include "cadef.h" +#include "cacIO.h" /* * CA private includes @@ -73,136 +78,250 @@ #include "ringBuffer.h" #ifndef FALSE -#define FALSE 0 +# define FALSE 0 #elif FALSE -#error FALSE isnt boolean false +# error FALSE isnt boolean false #endif #ifndef TRUE -#define TRUE 1 +# define TRUE 1 #elif !TRUE -#error TRUE isnt boolean true +# error TRUE isnt boolean true #endif #ifndef NELEMENTS -#define NELEMENTS(array) (sizeof(array)/sizeof((array)[0])) +# define NELEMENTS(array) (sizeof(array)/sizeof((array)[0])) #endif #ifndef LOCAL -#define LOCAL static +# define LOCAL static #endif #ifndef min -#define min(A,B) ((A)>(B)?(B):(A)) +# define min(A,B) ((A)>(B)?(B):(A)) #endif #ifndef max -#define max(A,B) ((A)<(B)?(B):(A)) +# define max(A,B) ((A)<(B)?(B):(A)) #endif #define MSEC_PER_SEC 1000L #define USEC_PER_SEC 1000000L -#define LOCK(PCAC) semMutexMustTake ((PCAC)->ca_client_lock); -#define UNLOCK(PCAC) semMutexGive ((PCAC)->ca_client_lock); +#define LOCK(PCAC) semMutexMustTake ( (PCAC)->ca_client_lock ); +#define UNLOCK(PCAC) semMutexGive ( (PCAC)->ca_client_lock ); /* * catch when they use really large strings */ #define STRING_LIMIT 512 -/* throw out requests prior to last ECA_TIMEOUT from ca_pend */ -#define VALID_MSG(PIIU) ((PIIU)->read_seq == (PIIU)->cur_read_seq) +class cacChannel { +public: + cacChannel (); + virtual ~cacChannel () = 0; + virtual void destroy () = 0; -struct baseCIU { - void *puser; - caCh *pConnFunc; - caArh *pAccessRightsFunc; - struct baseIIU *piiu; + void attachIO ( class cacChannelIO &io ); + int read ( unsigned type, unsigned long count, void *pValue ); + int read ( unsigned type, unsigned long count, cacNotify & ); + int write ( unsigned type, unsigned long count, const void *pvalue ); + int write ( unsigned type, unsigned long count, const void *pvalue, cacNotify ¬ify ); + int subscribe ( unsigned type, unsigned long count, unsigned mask, cacNotify ¬ify ); + void hostName ( char *pBuf, unsigned bufLength ) const; + short nativeType () const; + unsigned long nativeElementCount () const; + channel_state state () const; + bool readAccess () const; + bool writeAccess () const; + const char *pName () const; + unsigned searchAttempts () const; + bool ca_v42_ok () const; + bool connected () const; + caar accessRights () const; + unsigned readSequence () const; + void incrementOutstandingIO (); + void decrementOutstandingIO (); + void decrementOutstandingIO ( unsigned seqNumber ); + +protected: + class cacChannelIO *pChannelIO; + + virtual void lock () const; + virtual void unlock () const; + +private: + virtual void ioAttachNotify (); + virtual void ioReleaseNotify (); + virtual void connectNotify (); + virtual void disconnectNotify (); + virtual void accessRightsNotify ( caar ); + virtual void exceptionNotify ( int status, const char *pContext ); + virtual void connectTimeoutNotify (); + + static osiMutex defaultMutex; + + friend class cacChannelIO; }; -struct nciu { - ELLNODE node; /* !! MUST be first !! */ - ELLLIST eventq; /* nmiu go here */ - baseCIU ciu; - unsigned long count; /* array element count */ +class caClient { +public: + virtual void exceptionNotify (int status, const char *pContext, + const char *pFileName, unsigned lineNo) = 0; + virtual void exceptionNotify (int status, const char *pContext, + unsigned type, unsigned long count, + const char *pFileName, unsigned lineNo) = 0; +private: +}; + +class nciu : public cacChannelIO, public tsDLNode , + public chronIntIdRes { +public: + nciu ( class cac *pcac, cacChannel &chan, const char *pNameIn ); + void destroy (); + void connect ( class tcpiiu &iiu, unsigned nativeType, unsigned long nativeCount, unsigned sid ); + void disconnect (); + void searchReplySetUp ( unsigned sid, unsigned typeCode, unsigned long count ); + int read ( unsigned type, unsigned long count, void *pValue ); + int read ( unsigned type, unsigned long count, cacNotify ¬ify ); + int write ( unsigned type, unsigned long count, const void *pValue ); + int write ( unsigned type, unsigned long count, const void *pValue, cacNotify & ); + int subscribe ( unsigned type, unsigned long count, unsigned mask, cacNotify ¬ify ); + void hostName ( char *pBuf, unsigned bufLength ) const; + bool ca_v42_ok () const; + short nativeType () const; + unsigned long nativeElementCount () const; + channel_state state () const; + caar accessRights () const; + const char *pName () const; + unsigned searchAttempts () const; + bool connected () const; + unsigned readSequence () const; + void incrementOutstandingIO (); + void decrementOutstandingIO (); + void decrementOutstandingIO ( unsigned seqNumber ); + int searchMsg (); + bool claimMsg ( class tcpiiu *piiu ); + bool fullyConstructed () const; + void installIO ( class baseNMIU &io ); + void uninstallIO ( class baseNMIU &io ); + + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); + + tsDLList + eventq; + class netiiu *piiu; + char *pNameStr; unsigned sid; /* server id */ - unsigned cid; /* client id */ unsigned retry; /* search retry number */ unsigned short retrySeqNo; /* search retry seq number */ unsigned short nameLength; /* channel name length */ - unsigned short type; /* database field type */ caar ar; /* access rights */ unsigned claimPending:1; /* T if claim msg was sent */ unsigned previousConn:1; /* T if connected in the past */ - unsigned connected:1; /* T if connected */ - /* - * channel name stored directly after this structure in a - * null terminated string. - */ +private: + unsigned long count; + unsigned short typeCode; + unsigned f_connected:1; + unsigned f_fullyConstructed:1; + static tsFreeList < class nciu > freeList; + ~nciu (); // force pool allocation + int nciu::issuePut ( ca_uint16_t cmd, unsigned id, chtype type, + unsigned long count, const void *pvalue ); }; -struct caPutNotify { - ELLNODE node; - void *dbPutNotify; - unsigned long valueSize; /* size of block pointed to by pValue */ - caEventCallBackFunc *caUserCallback; - void *caUserArg; - void *pValue; +class baseNMIU : public tsDLNode , + public chronIntIdRes { +public: + baseNMIU ( nciu &chan ); + void destroy (); + unsigned identifier () const; + class cacChannelIO & channelIO () const; + virtual void completionNotify () = 0; + virtual void completionNotify ( unsigned type, unsigned long count, const void *pData ) = 0; + virtual void exceptionNotify ( int status, const char *pContext ) = 0; + virtual void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ) = 0; + virtual int subscriptionMsg (); + virtual void disconnect ( const char *pHostName ) = 0; +protected: + virtual ~baseNMIU (); // must be allocated from pool + nciu &chan; }; -struct lciu { - ELLNODE node; /* !! MUST be first !! */ - ELLLIST eventq; /* lmiu go here */ - baseCIU ciu; - caPutNotify *ppn; - pvId id; -}; - -struct baseMIU { - caEventCallBackFunc *usr_func; - void *usr_arg; - chtype type; /* requested type for local CA */ - unsigned long count; /* requested count for local CA */ - baseCIU *pChan; -}; - -struct nmiu { - ELLNODE node; /* list ptrs */ - baseMIU miu; - ca_real p_delta; - ca_real n_delta; - ca_real timeout; - unsigned id; +class netSubscription : public cacNotifyIO, public baseNMIU { +public: + netSubscription ( nciu &chan, chtype type, unsigned long count, + unsigned short mask, cacNotify ¬ify ); + void destroy (); + void completionNotify (); + void completionNotify ( unsigned type, unsigned long count, const void *pData ); + void exceptionNotify ( int status, const char *pContext ); + void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); + int subscriptionMsg (); + void disconnect ( const char *pHostName ); + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); +private: + chtype type; + unsigned long count; unsigned short mask; - unsigned short cmd; + ~netSubscription (); + static tsFreeList < class netSubscription > freeList; }; -typedef void * dbEventSubscription; - -struct lmiu { - ELLNODE node; /* list ptrs */ - baseMIU miu; - dbEventSubscription es; +class netReadCopyIO : public baseNMIU { +public: + netReadCopyIO ( nciu &chan, unsigned type, unsigned long count, + void *pValue, unsigned seqNumber ); + void disconnect ( const char *pHostName ); + void destroy (); + void completionNotify (); + void completionNotify ( unsigned type, unsigned long count, const void *pData ); + void exceptionNotify ( int status, const char *pContext ); + void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); +private: + unsigned type; + unsigned long count; + void *pValue; + unsigned seqNumber; + ~netReadCopyIO (); // must be allocated from pool + static tsFreeList < class netReadCopyIO > freeList; }; -struct putCvrtBuf { - ELLNODE node; - unsigned long size; - void *pBuf; +class netReadNotifyIO : public cacNotifyIO, public baseNMIU { +public: + netReadNotifyIO ( nciu &chan, cacNotify ¬ify ); + void destroy (); + void disconnect ( const char *pHostName ); + void completionNotify (); + void completionNotify ( unsigned type, unsigned long count, const void *pData ); + void exceptionNotify ( int status, const char *pContext ); + void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); +private: + ~netReadNotifyIO (); + static tsFreeList < class netReadNotifyIO > freeList; }; -/* - * for use with cac_select_io() - */ -#define CA_DO_SENDS (1<<0) -#define CA_DO_RECVS (1<<1) - -#define LD_CA_TIME(FLOAT_TIME,PCATIME) \ -((PCATIME)->tv_sec = (long) (FLOAT_TIME), \ -(PCATIME)->tv_usec = (long) ( ((FLOAT_TIME)-(PCATIME)->tv_sec)*USEC_PER_SEC )) - -#define CLR_CA_TIME(PCATIME) ((PCATIME)->tv_sec = 0u,(PCATIME)->tv_usec = 0u) +class netWriteNotifyIO : public cacNotifyIO, public baseNMIU { +public: + netWriteNotifyIO ( nciu &chan, cacNotify ¬ify ); + void destroy (); + void disconnect ( const char *pHostName ); + void completionNotify (); + void completionNotify ( unsigned type, unsigned long count, const void *pData ); + void exceptionNotify ( int status, const char *pContext ); + void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); +private: + ~netWriteNotifyIO (); + static tsFreeList < class netWriteNotifyIO > freeList; +}; /* * these control the duration and period of name resolution @@ -213,35 +332,12 @@ struct putCvrtBuf { #define INITIALTRIESPERFRAME 1u /* initial UDP frames per search try */ #define MAXTRIESPERFRAME 64u /* max UDP frames per search try */ -#if 0 -/* - * NOTE: These must be larger than one vxWorks tick or we will end up - * using the CPU. A vxWorks tick is usually 1/60th of a sec. The - * vxWorks implementation of select is not efficient. - * select() with no delay takes (on a hk nitro 060): - * 17uS (no fd) - * 56uS (one UDP read fd) - * 80uS (two UDP read fd) - * 113uS (one UDP read/write fd) - * 185uS (two UDP read/write fd) - * 3 uS are required to call FD_ZERO (&readMask) - * and FD_ZERO (&writeMask) - * 4 uS required to call tsStampGetCurrent () - */ -#ifdef iocCore -#define SELECT_POLL_SEARCH (0.075) /* units sec - polls for search request (4 ticks)*/ -#else -#define SELECT_POLL_SEARCH (0.025) /* units sec - polls for search request */ -#endif -#define SELECT_POLL_NO_SEARCH (0.5) /* units sec - polls for conn heartbeat */ -#endif /* #if 0 */ - -#define CA_RECAST_DELAY 0.5 /* initial delay to next search (sec) */ +#define CA_RECAST_DELAY 0.5 /* initial delay to next search (sec) */ #define CA_RECAST_PORT_MASK 0xff /* additional random search interval from port */ #define CA_RECAST_PERIOD 5.0 /* quiescent search period (sec) */ #if defined (CLOCKS_PER_SEC) -#define CAC_SIGNIFICANT_SELECT_DELAY (1.0 / CLOCKS_PER_SEC) +#define CAC_SIGNIFICANT_SELECT_DELAY ( 1.0 / CLOCKS_PER_SEC ) #else /* on sunos4 GNU does not provide CLOCKS_PER_SEC */ #define CAC_SIGNIFICANT_SELECT_DELAY (1.0 / 1000000u) @@ -279,57 +375,46 @@ struct putCvrtBuf { */ #define MAX_CONTIGUOUS_MSG_COUNT 10 -/* - * ! lock needs to be applied when an id is allocated ! - */ -#define CLIENT_HASH_TBL_SIZE (1<<12) -#define CLIENT_ID_WIDTH 28 /* bits */ -#define CLIENT_ID_COUNT (1<ca_nextFastBucketId++) -#define CLIENT_SLOW_ID_ALLOC(PCAC) (CLIENT_ID_MASK&(PCAC)->ca_nextSlowBucketId++) - #define SEND_RETRY_COUNT_INIT 100 enum iiu_conn_state {iiu_connecting, iiu_connected, iiu_disconnected}; extern threadPrivateId cacRecursionLock; +extern threadPrivateId caClientContextId; -struct baseIIU { - struct cac *pcas; +class baseIIU { +public: + baseIIU (class cac *pcac) : pcas (pcac) {} + class cac *pcas; }; -typedef void *dbEventCtx; +class netiiu : public baseIIU { +public: + netiiu (class cac *pcac); + ~netiiu (); + void show (unsigned level); -struct lclIIU { - ELLLIST chidList; - baseIIU iiu; - dbEventCtx evctx; - const pvAdapter *pva; - void *localSubscrFreeListPVT; - ELLLIST buffList; - ELLLIST putNotifyQue; - semMutexId putNotifyLock; + virtual bool compareIfTCP (nciu &chan, const sockaddr_in &) const = 0; + virtual void hostName (char *pBuf, unsigned bufLength) const = 0; + virtual bool ca_v42_ok () const = 0; + virtual bool ca_v41_ok () const = 0; + virtual int pushStreamMsg (const caHdr *pmsg, const void *pext, bool BlockingOk) = 0; + virtual int pushDatagramMsg (const caHdr *pMsg, const void *pExt, ca_uint16_t extsize) = 0; + virtual void addToChanList (nciu *chan) = 0; + virtual void removeFromChanList (nciu *chan) = 0; + virtual void disconnect (nciu *chan) = 0; + + tsDLList chidList; }; -struct netIIU { - ELLLIST chidList; - baseIIU iiu; - unsigned long curDataMax; - unsigned long curDataBytes; - caHdr curMsg; - void *pCurData; - unsigned curMsgBytes; -}; - -struct udpiiu; +class udpiiu; class searchTimer : public osiTimer { public: searchTimer (udpiiu &iiu, osiTimerQueue &queue); void notifySearchResponse (nciu *pChan); - void reset (double period); + void reset (double delayToNextTry); private: virtual void expire (); @@ -368,10 +453,23 @@ private: udpiiu &iiu; }; -struct udpiiu { +class udpiiu : public netiiu { +public: udpiiu (cac *pcac); ~udpiiu (); + void shutdown (); + void hostName ( char *pBuf, unsigned bufLength ) const; + bool ca_v42_ok () const; + bool ca_v41_ok () const; + void addToChanList (nciu *chan); + void removeFromChanList (nciu *chan); + void disconnect (nciu *chan); + int post_msg (const struct sockaddr_in *pnet_addr, + char *pInBuf, unsigned long blockSize); + int pushStreamMsg ( const caHdr *pmsg, const void *pext, bool BlockingOk ); + int pushDatagramMsg (const caHdr *pMsg, const void *pExt, ca_uint16_t extsize); + osiTime recvTime; char xmitBuf[MAX_UDP]; char recvBuf[ETHERNET_MAX_UDP]; searchTimer searchTmr; @@ -379,64 +477,151 @@ struct udpiiu { semMutexId xmitBufLock; semBinaryId xmitSignal; ELLLIST dest; - netIIU niiu; SOCKET sock; semBinaryId recvThreadExitSignal; semBinaryId sendThreadExitSignal; unsigned nBytesInXmitBuf; unsigned repeaterTries; unsigned short repeaterPort; - unsigned contactRepeater:1; - unsigned repeaterContacted:1; + bool contactRepeater; + bool repeaterContacted; + bool sendThreadExitCmd; // exceptions class noSocket {}; class noMemory {}; +private: + bool compareIfTCP (nciu &chan, const sockaddr_in &) const; }; -struct tcpiiu { - ELLNODE node; - ELLNODE recvActivityNode; - netIIU niiu; +class tcpRecvWatchdog : public osiTimer { +public: + tcpRecvWatchdog (double periodIn, osiTimerQueue & queueIn, bool echoProtocolAcceptedIn); + void echoResponseNotify (); +private: + void expire (); + void destroy (); + bool again () const; + double delay () const; + const char *name () const; + virtual void shutdown () = 0; + virtual void noopRequestMsg () = 0; + virtual void echoRequestMsg () = 0; + + const double period; + const bool echoProtocolAccepted; + bool echoResponsePending; +}; + +class tcpSendWatchdog : public osiTimer { +public: + tcpSendWatchdog (double periodIn, osiTimerQueue & queueIn); +private: + void expire (); + void destroy (); + bool again () const; + double delay () const; + const char *name () const; + virtual void shutdown () = 0; + + const double period; +}; + +class tcpiiu : public tcpRecvWatchdog, public tcpSendWatchdog, + public netiiu, public tsDLNode { +public: + tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, class bhe &bhe); + ~tcpiiu (); + void suicide (); + void shutdown (); + static void * operator new (size_t size); + static void operator delete (void *pCadaver, size_t size); + + void hostName (char *pBuf, unsigned bufLength) const; + bool ca_v42_ok () const; + bool ca_v41_ok () const; + int pushStreamMsg ( const caHdr *pmsg, const void *pext, bool BlockingOk ); + int post_msg (char *pInBuf, unsigned long blockSize); + void addToChanList (nciu *chan); + void removeFromChanList (nciu *chan); + void disconnect (nciu *chan); + bool fullyConstructed () const; + void recvMsg (); + void flush (); + + void noopRequestMsg (); + void echoRequestMsg (); + int busyRequestMsg (); + int readyRequestMsg (); + void hostNameSetMsg (); + void userNameSetMsg (); + char host_name_str[32]; ringBuffer send; ringBuffer recv; osiSockAddr dest; - TS_STAMP timeAtSendBlock; - TS_STAMP timeAtEchoRequest; - TS_STAMP timeAtLastRecv; - struct beaconHashEntry *pBHE; + caHdr curMsg; + unsigned long curDataMax; + unsigned long curDataBytes; + class bhe &bhe; + void *pCurData; semBinaryId sendThreadExitSignal; semBinaryId recvThreadExitSignal; unsigned minor_version_number; unsigned contiguous_msg_count; - unsigned read_seq; - unsigned cur_read_seq; + unsigned curMsgBytes; SOCKET sock; unsigned char state; /* for use with iiu_conn_state enum */ - /* - * bit fields placed together for better packing density - */ - unsigned client_busy:1; - unsigned echoPending:1; - unsigned sendPending:1; - unsigned claimsPending:1; - unsigned recvPending:1; - unsigned pushPending:1; - unsigned beaconAnomaly:1; + bool client_busy; + bool echoRequestPending; + bool claimRequestsPending; + bool sendPending; + bool recvPending; + bool pushPending; + bool beaconAnomaly; + + virtual void show (unsigned level) const; + +private: + + bool compareIfTCP (nciu &chan, const sockaddr_in &) const; + int pushDatagramMsg (const caHdr *pMsg, const void *pExt, ca_uint16_t extsize); + + bool fc; + static tsFreeList < class tcpiiu, 16 > freeList; }; -/* - * for the beacon's recvd hash table - */ -#define BHT_INET_ADDR_MASK 0xff -typedef struct beaconHashEntry { - struct beaconHashEntry *pNext; - tcpiiu *piiu; - struct sockaddr_in inetAddr; - TS_STAMP timeStamp; - ca_real averagePeriod; -} bhe; +class inetAddrID { +public: + inetAddrID (const struct sockaddr_in &addrIn); + bool operator == (const inetAddrID &); + resTableIndex hash (unsigned nBitsHashIndex) const; + static unsigned maxIndexBitWidth (); + static unsigned minIndexBitWidth (); +private: + struct sockaddr_in addr; +}; + +class bhe : public tsSLNode , public inetAddrID { +public: + bhe (class cac &cacIn, const osiTime &initialTimeStamp, const inetAddrID &addr); + tcpiiu *getIIU () const; + void bindToIIU (tcpiiu *); + void destroy (); + bool updateBeaconPeriod (osiTime programBeginTime); + + static void * operator new (size_t size); + static void operator delete (void *pCadaver, size_t size); + +private: + class cac &cac; + tcpiiu *piiu; + osiTime timeStamp; + double averagePeriod; + + static tsFreeList < class bhe > freeList; + ~bhe (); // force allocation from freeList +}; class caErrorCode { public: @@ -457,32 +642,126 @@ struct caFDInfo { class processThread : public osiThread { public: - processThread (); - virtual ~processThread (); - virtual void entryPoint (); + processThread (class cac *pcacIn); + ~processThread (); + void entryPoint (); void signalShutDown (); - void installLabor (tcpiiu &iiu); - void removeLabor (tcpiiu &iiu); private: - ELLLIST recvActivity; - osiEvent wakeup; + class cac *pcac; osiEvent exit; - osiMutex mutex; bool shutDown; }; -struct cac { - lclIIU localIIU; +#define CASG_MAGIC 0xFAB4CAFE + +class syncGroupNotify : public cacNotify, public tsDLNode { +public: + syncGroupNotify ( struct CASG &sgIn, void *pValue ); + void destroy (); + void show (unsigned level) const; + + static void * operator new (size_t size); + static void operator delete (void *pCadaver, size_t size); + +private: + void completionNotify (); + void completionNotify ( unsigned type, unsigned long count, const void *pData ); + void exceptionNotify ( int status, const char *pContext ); + void exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ); + void lock () const; + void unlock () const; + ~syncGroupNotify (); // allocate only from pool + + struct CASG &sg; + unsigned magic; + void *pValue; + unsigned long seqNo; + + static tsFreeList < class syncGroupNotify > freeList; +}; + +/* + * one per synch group + */ +struct CASG : public chronIntIdRes { +public: + CASG (cac &cacIn); + void destroy (); + bool verify () const; + bool ioComplete () const; + int block ( double timeout ); + void reset (); + void show (unsigned level) const; + int get (chid pChan, unsigned type, unsigned long count, void *pValue); + int put (chid pChan, unsigned type, unsigned long count, const void *pValue); + + static void * operator new (size_t size); + static void operator delete (void *pCadaver, size_t size); + +private: + cac &client; + unsigned magic; + unsigned long opPendCount; + unsigned long seqNo; + osiEvent sem; + osiMutex mutex; + tsDLList ioList; + + static tsFreeList < struct CASG > freeList; + + ~CASG (); + friend class syncGroupNotify; +}; + +class cac : public caClient { +public: + cac (); + ~cac (); + void safeDestroyNMIU (unsigned id); + void processRecvBacklog (); + void flush (); + void cleanUpPendIO (); + unsigned connectionCount () const; + void show (unsigned level) const; + void installIIU (tcpiiu &iiu); + void removeIIU (tcpiiu &iiu); + void signalRecvActivityIIU (tcpiiu &iiu); + void beaconNotify (const inetAddrID &addr); + bhe *lookupBeaconInetAddr (const inetAddrID &ina); + bhe *createBeaconHashEntry (const inetAddrID &ina, + const osiTime &initialTimeStamp); + void removeBeaconInetAddr (const inetAddrID &ina); + void decrementOutstandingIO (); + void incrementOutstandingIO (); + void decrementOutstandingIO ( unsigned seqNumber ); + unsigned readSequence () const; + int pend (double timeout, int early); + bool ioComplete () const; + void lock () const; + void unlock () const; + void exceptionNotify (int status, const char *pContext, + const char *pFileName, unsigned lineNo); + void exceptionNotify (int status, const char *pContext, + unsigned type, unsigned long count, + const char *pFileName, unsigned lineNo); + baseNMIU * lookupIO (unsigned id); + void installIO (baseNMIU &io); + void uninstallIO (baseNMIU &io); + nciu * lookupChan (unsigned id); + void installChannel (nciu &chan); + void uninstallChannel (nciu &chan); + CASG * lookupCASG (unsigned id); + void installCASG (CASG &); + void uninstallCASG (CASG &); + void registerService ( cacServiceIO &service ); + bool createChannel (const char *name_str, cacChannel &chan); + osiTimerQueue timerQueue; - ELLLIST ca_iiuList; - ELLLIST activeCASG; ELLLIST activeCASGOP; ELLLIST putCvrtBuf; ELLLIST fdInfoFreeList; ELLLIST fdInfoList; - TS_STAMP currentTime; - TS_STAMP programBeginTime; - TS_STAMP ca_last_repeater_try; + osiTime programBeginTime; ca_real ca_connectTMO; udpiiu *pudpiiu; caExceptionHandler *ca_exception_func; @@ -492,20 +771,16 @@ struct cac { void *ca_fd_register_arg; char *ca_pUserName; char *ca_pHostName; - BUCKET *ca_pSlowBucket; - BUCKET *ca_pFastBucket; - bhe *ca_beaconHash[BHT_INET_ADDR_MASK+1]; - void *ca_ioBlockFreeListPVT; - void *ca_sgFreeListPVT; - void *ca_sgopFreeListPVT; - nciu *ca_pEndOfBCastList; + resTable + < bhe, inetAddrID > beaconTable; + tsDLIterBD endOfBCastList; semBinaryId ca_io_done_sem; + osiEvent recvActivity; semBinaryId ca_blockSem; semMutexId ca_client_lock; - processThread procThread; - unsigned ca_pndrecvcnt; + processThread *pProcThread; + unsigned readSeq; unsigned ca_nextSlowBucketId; - unsigned ca_nextFastBucketId; unsigned ca_number_iiu_in_fc; unsigned short ca_server_port; char ca_sprintf_buf[256]; @@ -514,102 +789,129 @@ struct cac { unsigned ca_flush_pending:1; ELLLIST ca_taskVarList; - - cac (); - ~cac (); +private: + cacServiceList services; + osiMutex iiuListLock; + tsDLList iiuListIdle; + tsDLList iiuListRecvPending; + chronIntIdResTable + < baseNMIU > ioTable; + chronIntIdResTable + < nciu > chanTable; + chronIntIdResTable + < CASG > sgTable; + unsigned pndrecvcnt; + int pendPrivate (double timeout, int early); }; -#define CASG_MAGIC 0xFAB4CAFE - -/* - * one per outstanding op - */ -struct CASGOP{ - ELLNODE node; - CA_SYNC_GID id; - void *pValue; - unsigned long magic; - unsigned long seqNo; - cac *pcac; -}; - -/* - * one per synch group - */ -struct CASG { - ELLNODE node; - CA_SYNC_GID id; - unsigned long magic; - unsigned long opPendCount; - unsigned long seqNo; - semBinaryId sem; -}; +extern const caHdr cacnullmsg; /* * CA internal functions */ -#if 0 -void cac_mux_io (cac *pcac, double maxDelay, unsigned iocCloseAllowed); -#endif /* #if 0 */ -int cac_search_msg (nciu *chix); -int ca_request_event (nciu *pChan, nmiu *monix); -int ca_busy_message (tcpiiu *piiu); -int ca_ready_message (tcpiiu *piiu); -void noop_msg (tcpiiu *piiu); -void echo_request (tcpiiu *piiu); -bool issue_claim_channel (nciu *pchan); -void issue_identify_client (tcpiiu *piiu); -void issue_client_host_name (tcpiiu *piiu); int ca_defunct (void); -int ca_printf (struct cac *, const char *pformat, ...); -int ca_vPrintf (struct cac *, const char *pformat, va_list args); +int ca_printf (const char *pformat, ...); +int ca_vPrintf (const char *pformat, va_list args); void manage_conn (cac *pcac); -void checkConnWatchdogs (cac *pcac); -void mark_server_available (cac *pcac, const struct sockaddr_in *pnet_addr); void flow_control_on (tcpiiu *piiu); void flow_control_off (tcpiiu *piiu); epicsShareFunc void epicsShareAPI ca_repeater (void); -void ca_sg_init (cac *pcac); -void ca_sg_shutdown (cac *pcac); int cac_select_io (cac *pcac, double maxDelay, int flags); -int post_msg (netIIU *piiu, const struct sockaddr_in *pnet_addr, - char *pInBuf, unsigned long blockSize); char *localHostName (void); int ca_os_independent_init (cac *pcac, const pvAdapter *ppva); -void freeBeaconHash (struct cac *ca_temp); -void removeBeaconInetAddr (cac *pcac, - const struct sockaddr_in *pnet_addr); bhe *lookupBeaconInetAddr(cac *pcac, const struct sockaddr_in *pnet_addr); -bhe *createBeaconHashEntry (cac *pcac, - const struct sockaddr_in *pnet_addr, unsigned sawBeacon); -void caIOBlockFree (cac *pcac, unsigned id); -void netChannelDestroy (cac *pcac, unsigned id); -void cacDisconnectChannel (nciu *chix); void genLocalExcepWFL (cac *pcac, long stat, const char *ctx, const char *pFile, unsigned line); #define genLocalExcep(PCAC, STAT, PCTX) \ genLocalExcepWFL (PCAC, STAT, PCTX, __FILE__, __LINE__) -void cac_reconnect_channel (tcpiiu *piiu, caResId id, unsigned short type, unsigned long count); -void addToChanList (nciu *chan, netIIU *piiu); -void removeFromChanList (nciu *chan); -void cacFlushAllIIU (cac *pcac); double cac_fetch_poll_period (cac *pcac); tcpiiu * constructTCPIIU (cac *pcac, const struct sockaddr_in *pina, unsigned minorVersion); void cac_destroy (cac *pcac); - -tcpiiu *iiuToTCPIIU (baseIIU *pIn); -netIIU *iiuToNIIU (baseIIU *pIn); -lclIIU *iiuToLIIU (baseIIU *pIn); int fetchClientContext (cac **ppcac); extern "C" void caRepeaterThread (void *pDummy); -void initiateShutdownTCPIIU (tcpiiu *piiu); -void cacShutdownUDP (udpiiu &iiu); +extern "C" void ca_default_exception_handler (struct exception_handler_args args); + +// +// nciu inline member functions +// + +inline void * nciu::operator new (size_t size) +{ + return nciu::freeList.allocate (size); +} + +inline void nciu::operator delete (void *pCadaver, size_t size) +{ + nciu::freeList.release (pCadaver,size); +} + +inline bool nciu::fullyConstructed () const +{ + return this->f_fullyConstructed; +} + +// +// netSubscription inline member functions +// +inline void * netSubscription::operator new (size_t size) +{ + return netSubscription::freeList.allocate (size); +} + +inline void netSubscription::operator delete (void *pCadaver, size_t size) +{ + netSubscription::freeList.release (pCadaver,size); +} + +// +// netReadNotifyIO inline member functions +// +inline void * netReadNotifyIO::operator new (size_t size) +{ + return netReadNotifyIO::freeList.allocate (size); +} + +inline void netReadNotifyIO::operator delete (void *pCadaver, size_t size) +{ + netReadNotifyIO::freeList.release (pCadaver,size); +} + +// +// netWriteNotifyIO inline member functions +// +inline void * netWriteNotifyIO::operator new (size_t size) +{ + return netWriteNotifyIO::freeList.allocate (size); +} + +inline void netWriteNotifyIO::operator delete (void *pCadaver, size_t size) +{ + netWriteNotifyIO::freeList.release (pCadaver,size); +} + +// +// tcpiiu inline functions +// +inline void * tcpiiu::operator new (size_t size) +{ + return tcpiiu::freeList.allocate (size); +} + +inline void tcpiiu::operator delete (void *pCadaver, size_t size) +{ + tcpiiu::freeList.release (pCadaver,size); +} + +inline bool tcpiiu::fullyConstructed () const +{ + return this->fc; +} /* * !!KLUDGE!! diff --git a/src/ca/nciu.cpp b/src/ca/nciu.cpp new file mode 100644 index 000000000..7b625025d --- /dev/null +++ b/src/ca/nciu.cpp @@ -0,0 +1,838 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +tsFreeList < class nciu > nciu::freeList; + +struct putCvrtBuf { + ELLNODE node; + unsigned long size; + void *pBuf; +}; + +/* + * nciu::nciu () + */ +nciu::nciu (cac *pcac, cacChannel &chan, const char *pNameIn) : + cacChannelIO (chan) +{ + static const caar defaultAccessRights = { false, false }; + size_t strcnt; + + strcnt = strlen (pNameIn) + 1; + if ( strcnt > MAX_UDP - sizeof (caHdr) ) { + throwWithLocation ( caErrorCode (ECA_STRTOBIG) ); + } + this->pNameStr = reinterpret_cast ( malloc (strcnt) ); + if ( ! this->pNameStr ) { + this->f_fullyConstructed = false; + return; + } + strcpy ( this->pNameStr, pNameIn ); + + LOCK (pcac); + + pcac->installChannel (*this); + + this->typeCode = USHRT_MAX; /* invalid initial type */ + this->count = 0; /* invalid initial count */ + this->sid = UINT_MAX; /* invalid initial server id */ + this->ar = defaultAccessRights; + this->nameLength = strcnt; + this->previousConn = 0; + this->f_connected = false; + + pcac->pudpiiu->addToChanList (this); + + /* + * reset broadcasted search counters + */ + pcac->pudpiiu->searchTmr.reset (0.0); + + this->f_fullyConstructed = true; + + UNLOCK (pcac); +} + +/* + * nciu::~nciu () + */ +nciu::~nciu () +{ + netiiu *piiuCopy = this->piiu; + + if ( ! this->fullyConstructed () ) { + return; + } + + if ( this->f_connected ) { + caHdr hdr; + + hdr.m_cmmd = htons ( CA_PROTO_CLEAR_CHANNEL ); + hdr.m_available = this->getId (); + hdr.m_cid = this->sid; + hdr.m_dataType = htons ( 0 ); + hdr.m_count = htons ( 0 ); + hdr.m_postsize = 0; + + this->piiu->pushStreamMsg (&hdr, NULL, true); + } + + LOCK ( this->piiu->pcas ); + + /* + * remove any IO blocks still attached to this channel + */ + tsDLIterBD iter = this->eventq.first (); + while ( iter != iter.eol () ) { + tsDLIterBD next = iter.itemAfter (); + iter->destroy (); + iter = next; + } + + this->piiu->pcas->uninstallChannel (*this); + + this->piiu->removeFromChanList ( this ); + + free ( reinterpret_cast (this->pNameStr) ); + + UNLOCK ( piiuCopy->pcas ); // remove clears this->piiu +} + +int nciu::read ( unsigned type, unsigned long count, cacNotify ¬ify ) +{ + int status; + caHdr hdr; + ca_uint16_t type_u16; + ca_uint16_t count_u16; + + /* + * fail out if channel isnt connected or arguments are + * otherwise invalid + */ + if ( ! this->f_connected ) { + return ECA_DISCONNCHID; + } + if ( INVALID_DB_REQ (type) ) { + return ECA_BADTYPE; + } + if ( ! this->ar.read_access ) { + return ECA_NORDACCESS; + } + if ( count > this->count || count > 0xffff ) { + return ECA_BADCOUNT; + } + if ( count == 0 ) { + count = this->count; + } + + /* + * only after range checking type and count cast + * them down to a smaller size + */ + type_u16 = (ca_uint16_t) type; + count_u16 = (ca_uint16_t) count; + + LOCK (this->piiu->pcas); + { + netReadNotifyIO *monix = new netReadNotifyIO ( *this, notify ); + if ( ! monix ) { + UNLOCK (this->piiu->pcas); + return ECA_ALLOCMEM; + } + + hdr.m_cmmd = htons (CA_PROTO_READ_NOTIFY); + hdr.m_dataType = htons (type_u16); + hdr.m_count = htons (count_u16); + hdr.m_available = monix->getId (); + hdr.m_postsize = 0; + hdr.m_cid = this->sid; + } + UNLOCK (this->piiu->pcas); + + status = this->piiu->pushStreamMsg (&hdr, NULL, true); + if ( status != ECA_NORMAL ) { + /* + * we need to be careful about touching the monix + * pointer after the lock has been released + */ + this->piiu->pcas->safeDestroyNMIU (hdr.m_available); + } + + return status; +} + +int nciu::read ( unsigned type, unsigned long count, void *pValue ) +{ + int status; + caHdr hdr; + ca_uint16_t type_u16; + ca_uint16_t count_u16; + + /* + * fail out if channel isnt connected or arguments are + * otherwise invalid + */ + if ( ! this->f_connected ) { + return ECA_DISCONNCHID; + } + if ( INVALID_DB_REQ ( type ) ) { + return ECA_BADTYPE; + } + if ( ! this->ar.read_access ) { + return ECA_NORDACCESS; + } + if ( count > this->count || count > 0xffff ) { + return ECA_BADCOUNT; + } + if ( count == 0 ) { + count = this->count; + } + + /* + * only after range checking type and count cast + * them down to a smaller size + */ + type_u16 = ( ca_uint16_t ) type; + count_u16 = ( ca_uint16_t ) count; + + LOCK ( this->piiu->pcas ); + { + netReadCopyIO *monix = new netReadCopyIO ( *this, type, count, pValue, this->readSequence () ); + if ( ! monix ) { + UNLOCK ( this->piiu->pcas ); + return ECA_ALLOCMEM; + } + + hdr.m_cmmd = htons ( CA_PROTO_READ ); + hdr.m_dataType = htons ( type_u16 ); + hdr.m_count = htons ( count_u16 ); + hdr.m_available = monix->getId (); + hdr.m_postsize = 0; + hdr.m_cid = this->sid; + } + UNLOCK ( this->piiu->pcas ); + + status = this->piiu->pushStreamMsg ( &hdr, NULL, true ); + if ( status != ECA_NORMAL ) { + /* + * we need to be careful about touching the monix + * pointer after the lock has been released + */ + this->piiu->pcas->safeDestroyNMIU ( hdr.m_available ); + } + + return status; +} + +/* + * free_put_convert() + */ +#ifdef CONVERSION_REQUIRED +LOCAL void free_put_convert (cac *pcac, void *pBuf) +{ + struct putCvrtBuf *pBufHdr; + + pBufHdr = (struct putCvrtBuf *)pBuf; + pBufHdr -= 1; + assert ( pBufHdr->pBuf == (void *) ( pBufHdr + 1 ) ); + + LOCK (pcac); + ellAdd (&pcac->putCvrtBuf, &pBufHdr->node); + UNLOCK (pcac); + + return; +} +#endif /* CONVERSION_REQUIRED */ + +/* + * check_a_dbr_string() + */ +LOCAL int check_a_dbr_string (const char *pStr, const unsigned count) +{ + unsigned i; + + for ( i = 0; i < count; i++ ) { + unsigned int strsize = 0; + while ( 1 ) { + if (strsize >= MAX_STRING_SIZE ) { + return ECA_STRTOBIG; + } + if ( pStr[strsize] == '\0' ) { + break; + } + strsize++; + } + pStr += MAX_STRING_SIZE; + } + + return ECA_NORMAL; +} + +/* + * malloc_put_convert() + */ +#ifdef CONVERSION_REQUIRED +LOCAL void *malloc_put_convert (cac *pcac, unsigned long size) +{ + struct putCvrtBuf *pBuf; + + LOCK (pcac); + while ( (pBuf = (struct putCvrtBuf *) ellGet(&pcac->putCvrtBuf)) ) { + if(pBuf->size >= size){ + break; + } + else { + free (pBuf); + } + } + UNLOCK (pcac); + + if (!pBuf) { + pBuf = (struct putCvrtBuf *) malloc (sizeof(*pBuf)+size); + if (!pBuf) { + return NULL; + } + pBuf->size = size; + pBuf->pBuf = (void *) (pBuf+1); + } + + return pBuf->pBuf; +} +#endif /* CONVERSION_REQUIRED */ + +/* + * nciu::issuePut () + */ +int nciu::issuePut (ca_uint16_t cmd, unsigned id, chtype type, + unsigned long count, const void *pvalue) +{ + int status; + caHdr hdr; + unsigned postcnt; + ca_uint16_t type_u16; + ca_uint16_t count_u16; +# ifdef CONVERSION_REQUIRED + void *pCvrtBuf; +# endif /*CONVERSION_REQUIRED*/ + + /* + * fail out if the conn is down or the arguments are otherwise invalid + */ + if ( ! this->f_connected ) { + return ECA_DISCONNCHID; + } + if ( INVALID_DB_REQ (type) ) { + return ECA_BADTYPE; + } + /* + * compound types not allowed + */ + if ( dbr_value_offset[type] ) { + return ECA_BADTYPE; + } + if ( ! this->ar.write_access ) { + return ECA_NOWTACCESS; + } + if ( count > this->count || count > 0xffff || count == 0 ) { + return ECA_BADCOUNT; + } + if (type==DBR_STRING) { + status = check_a_dbr_string ( (char *) pvalue, count ); + if (status != ECA_NORMAL) { + return status; + } + } + postcnt = dbr_size_n (type,count); + if (postcnt>0xffff) { + return ECA_TOLARGE; + } + + /* + * only after range checking type and count cast + * them down to a smaller size + */ + type_u16 = (ca_uint16_t) type; + count_u16 = (ca_uint16_t) count; + + if (type == DBR_STRING && count == 1) { + char *pstr = (char *)pvalue; + + postcnt = strlen(pstr)+1; + } + +# ifdef CONVERSION_REQUIRED + { + unsigned i; + void *pdest; + unsigned size_of_one; + + size_of_one = dbr_size[type]; + + pCvrtBuf = pdest = malloc_put_convert (this->piiu->pcas, postcnt); + if (!pdest) { + return ECA_ALLOCMEM; + } + + /* + * No compound types here because these types are read only + * and therefore only appropriate for gets or monitors + * + * I changed from a for to a while loop here to avoid bounds + * checker pointer out of range error, and unused pointer + * update when it is a single element. + */ + i=0; + while (TRUE) { + switch (type) { + case DBR_LONG: + *(dbr_long_t *)pdest = htonl (*(dbr_long_t *)pvalue); + break; + + case DBR_CHAR: + *(dbr_char_t *)pdest = *(dbr_char_t *)pvalue; + break; + + case DBR_ENUM: + case DBR_SHORT: + case DBR_PUT_ACKT: + case DBR_PUT_ACKS: +# if DBR_INT != DBR_SHORT +# error DBR_INT != DBR_SHORT ? +# endif /*DBR_INT != DBR_SHORT*/ + *(dbr_short_t *)pdest = htons (*(dbr_short_t *)pvalue); + break; + + case DBR_FLOAT: + dbr_htonf ((dbr_float_t *)pvalue, (dbr_float_t *)pdest); + break; + + case DBR_DOUBLE: + dbr_htond ((dbr_double_t *)pvalue, (dbr_double_t *)pdest); + break; + + case DBR_STRING: + /* + * string size checked above + */ + strcpy ( (char *) pdest, (char *) pvalue ); + break; + + default: + return ECA_BADTYPE; + } + + if (++i>=count) { + break; + } + + pdest = ((char *)pdest) + size_of_one; + pvalue = ((char *)pvalue) + size_of_one; + } + + pvalue = pCvrtBuf; + } +# endif /*CONVERSION_REQUIRED*/ + + hdr.m_cmmd = htons (cmd); + hdr.m_dataType = htons (type_u16); + hdr.m_count = htons (count_u16); + hdr.m_cid = this->sid; + hdr.m_available = id; + hdr.m_postsize = (ca_uint16_t) postcnt; + + status = this->piiu->pushStreamMsg (&hdr, pvalue, true); + +# ifdef CONVERSION_REQUIRED + free_put_convert (this->piiu->pcas, pCvrtBuf); +# endif /*CONVERSION_REQUIRED*/ + + return status; +} + +int nciu::write (unsigned type, unsigned long count, const void *pValue) +{ + return this->issuePut (CA_PROTO_WRITE, ~0U, type, count, pValue); +} + +int nciu::write (unsigned type, unsigned long count, const void *pValue, cacNotify ¬ify) +{ + netWriteNotifyIO *monix; + unsigned id; + int status; + + if ( ! this->f_connected ) { + return ECA_DISCONNCHID; + } + + if ( ! this->piiu->ca_v41_ok () ) { + return ECA_NOSUPPORT; + } + + /* + * lock around io block create and list add + * so that we are not deleted without + * reclaiming the resource + */ + LOCK (this->piiu->pcas); + + monix = new netWriteNotifyIO (*this, notify); + if ( ! monix ) { + UNLOCK (this->piiu->pcas); + return ECA_ALLOCMEM; + } + + id = monix->getId (); + + UNLOCK (this->piiu->pcas); + + status = this->issuePut (CA_PROTO_WRITE_NOTIFY, id, type, count, pValue); + if ( status != ECA_NORMAL ) { + /* + * we need to be careful about touching the monix + * pointer after the lock has been released + */ + this->piiu->pcas->safeDestroyNMIU (id); + } + return status; +} + +int nciu::subscribe (unsigned type, unsigned long count, + unsigned mask, cacNotify ¬ify) +{ + netSubscription *pNetMon; + + LOCK (this->piiu->pcas); + + pNetMon = new netSubscription (*this, type, count, + static_cast (mask), notify); + if ( ! pNetMon ) { + UNLOCK (this->piiu->pcas); + return ECA_ALLOCMEM; + } + + UNLOCK (this->piiu->pcas); + + pNetMon->subscriptionMsg (); + + return ECA_NORMAL; +} + +void nciu::destroy () +{ + delete this; +} + +void nciu::hostName ( char *pBuf, unsigned bufLength ) const +{ + this->piiu->hostName ( pBuf, bufLength ); +} + +bool nciu::ca_v42_ok () const +{ + return this->piiu->ca_v42_ok (); +} + +short nciu::nativeType () const +{ + if ( this->f_connected ) { + return static_cast (this->typeCode); + } + else { + return TYPENOTCONN; + } +} + +unsigned long nciu::nativeElementCount () const +{ + if ( this->f_connected ) { + return this->count; + } + else { + return 0ul; + } +} + +channel_state nciu::state () const +{ + if (this->f_connected) { + return cs_conn; + } + else if (this->previousConn) { + return cs_prev_conn; + } + else { + return cs_never_conn; + } +} + +caar nciu::accessRights () const +{ + return this->ar; +} + +const char *nciu::pName () const +{ + return this->pNameStr; +} + +unsigned nciu::searchAttempts () const +{ + return this->retry; +} + +void nciu::connect (tcpiiu &iiu, unsigned nativeType, unsigned long nativeCount, unsigned sid) +{ + LOCK ( iiu.pcas ); + + if ( this->connected () ) { + ca_printf ( + "CAC: Ignored conn resp to conn chan CID=%u SID=%u?\n", + this->getId (), this->sid ); + UNLOCK ( iiu.pcas ); + return; + } + + this->typeCode = nativeType; + this->count = nativeCount; + this->sid = sid; + this->f_connected = true; + this->previousConn = true; + + /* + * if less than v4.1 then the server will never + * send access rights and we know that there + * will always be access and call their call back + * here + */ + if ( ! CA_V41 ( CA_PROTOCOL_VERSION, iiu.minor_version_number ) ) { + this->ar.read_access = true; + this->ar.write_access = true; + this->accessRightsNotify ( this->ar ); + } + + this->connectNotify (); + + // resubscribe for monitors from this channel + tsDLIterBD iter = this->eventq.first (); + while ( iter != iter.eol () ) { + iter->subscriptionMsg (); + iter++; + } + + UNLOCK ( iiu.pcas ); +} + +void nciu::disconnect () +{ + LOCK (this->piiu->pcas); + + this->typeCode = USHRT_MAX; + this->count = 0u; + this->sid = UINT_MAX; + this->ar.read_access = false; + this->ar.write_access = false; + this->f_connected = false; + + char hostNameBuf[64]; + this->piiu->hostName ( hostNameBuf, sizeof (hostNameBuf) ); + + /* + * look for events that have an event cancel in progress + */ + tsDLIterBD iter = this->eventq.first (); + while ( iter != iter.eol () ) { + tsDLIterBD next = iter.itemAfter (); + iter->disconnect ( hostNameBuf ); + iter = next; + } + + this->disconnectNotify (); + this->accessRightsNotify (this->ar); + + UNLOCK ( this->piiu->pcas ); + + this->piiu->disconnect ( this ); +} + +/* + * nciu::searchMsg () + */ +int nciu::searchMsg () +{ + udpiiu *piiu = this->piiu->pcas->pudpiiu; + int status; + caHdr msg; + + if ( this->piiu != static_cast (piiu) ) { + return ECA_INTERNAL; + } + + if (this->nameLength > 0xffff) { + return ECA_STRTOBIG; + } + + msg.m_cmmd = htons (CA_PROTO_SEARCH); + msg.m_available = this->getId (); + msg.m_dataType = htons (DONTREPLY); + msg.m_count = htons (CA_MINOR_VERSION); + msg.m_cid = this->getId (); + + status = this->piiu->pushDatagramMsg (&msg, this->pNameStr, this->nameLength); + if (status != ECA_NORMAL) { + return status; + } + + /* + * increment the number of times we have tried to find this thisnel + */ + if (this->retryretry++; + } + + /* + * move the channel to the end of the list so + * that all channels get a equal chance + */ + LOCK (this->piiu->pcas); + this->piiu->chidList.remove (*this); + this->piiu->chidList.add (*this); + UNLOCK (this->piiu->pcas); + + return ECA_NORMAL; +} + +void nciu::searchReplySetUp (unsigned sid, unsigned typeCode, unsigned long count) +{ + this->typeCode = typeCode; + this->count = count; + this->sid = sid; +} + +/* + * nciu::claimMsg () + */ +bool nciu::claimMsg (tcpiiu *piiu) +{ + caHdr hdr; + unsigned size; + const char *pStr; + int status; + + LOCK (this->piiu->pcas); + + + if ( ! this->claimPending ) { + return false; + } + + if ( this->f_connected ) { + return false; + } + + hdr = cacnullmsg; + hdr.m_cmmd = htons (CA_PROTO_CLAIM_CIU); + + if ( CA_V44 (CA_PROTOCOL_VERSION, piiu->minor_version_number) ) { + hdr.m_cid = this->getId (); + pStr = this->pNameStr; + size = this->nameLength; + } + else { + hdr.m_cid = this->sid; + pStr = NULL; + size = 0u; + } + + hdr.m_postsize = size; + + /* + * The available field is used (abused) + * here to communicate the minor version number + * starting with CA 4.1. + */ + hdr.m_available = htonl (CA_MINOR_VERSION); + + /* + * If we are out of buffer space then postpone this + * operation until later. This avoids any possibility + * of a push pull deadlock (since this is sent when + * parsing the UDP input buffer). + */ + status = piiu->pushStreamMsg (&hdr, pStr, false); + if ( status == ECA_NORMAL ) { + + /* + * move to the end of the list once the claim has been sent + */ + this->claimPending = FALSE; + piiu->chidList.remove (*this); + piiu->chidList.add (*this); + + if ( ! CA_V42 (CA_PROTOCOL_VERSION, piiu->minor_version_number) ) { + this->connect (*piiu, this->typeCode, this->count, this->sid); + } + } + else { + piiu->claimRequestsPending = true; + } + UNLOCK (this->piiu->pcas); + + if ( status == ECA_NORMAL ) { + return true; + } + else { + return false; + } +} + +void nciu::installIO ( baseNMIU &io ) +{ + LOCK ( this->piiu->pcas ); + this->piiu->pcas->installIO ( io ); + this->eventq.add ( io ); + UNLOCK ( this->piiu->pcas ); +} + +void nciu::uninstallIO ( baseNMIU &io ) +{ + LOCK ( this->piiu->pcas ); + this->eventq.remove ( io ); + this->piiu->pcas->uninstallIO ( io ); + UNLOCK ( this->piiu->pcas ); +} + +bool nciu::connected () const +{ + return this->f_connected; +} + +unsigned nciu::readSequence () const +{ + return this->piiu->pcas->readSequence (); +} + +void nciu::incrementOutstandingIO () +{ + this->piiu->pcas->incrementOutstandingIO (); +} + +void nciu::decrementOutstandingIO () +{ + this->piiu->pcas->decrementOutstandingIO (); +} + +void nciu::decrementOutstandingIO ( unsigned seqNumber ) +{ + this->piiu->pcas->decrementOutstandingIO ( seqNumber ); +} \ No newline at end of file diff --git a/src/ca/netReadCopyIO.cpp b/src/ca/netReadCopyIO.cpp new file mode 100644 index 000000000..f00a8a5cb --- /dev/null +++ b/src/ca/netReadCopyIO.cpp @@ -0,0 +1,74 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +tsFreeList < class netReadCopyIO > netReadCopyIO::freeList; + +netReadCopyIO::netReadCopyIO ( nciu &chanIn, unsigned typeIn, unsigned long countIn, + void *pValueIn, unsigned seqNumberIn ) : + baseNMIU (chanIn), type (typeIn), count (countIn), + pValue (pValueIn), seqNumber (seqNumberIn) +{ + chanIn.incrementOutstandingIO (); +} + +netReadCopyIO::~netReadCopyIO () +{ +} + +void netReadCopyIO::disconnect ( const char *pHostName ) +{ + this->exceptionNotify ( ECA_DISCONN, pHostName ); + delete this; +} + +void netReadCopyIO::completionNotify () +{ + this->exceptionNotify ( ECA_INTERNAL, "get completion callback with no data?" ); +} + +void netReadCopyIO::completionNotify ( unsigned type, unsigned long count, const void *pData ) +{ + if ( type <= (unsigned) LAST_BUFFER_TYPE ) { +# ifdef CONVERSION_REQUIRED + (*cac_dbr_cvrt[type]) ( pData, this->pValue, FALSE, count ); +# else + memcpy (pData, pValue, dbr_size_n ( type, count ) ); +# endif + chan.decrementOutstandingIO (this->seqNumber); + } + else { + this->exceptionNotify ( ECA_INTERNAL, "bad data type in message" ); + } +} + +void netReadCopyIO::exceptionNotify ( int status, const char *pContext ) +{ + ca_signal (status, pContext); +} + +void netReadCopyIO::exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ) +{ + ca_signal_formated (status, __FILE__, __LINE__, "%s type=%d count=%ld\n", + pContext, type, count); +} + +void * netReadCopyIO::operator new ( size_t size ) +{ + return netReadCopyIO::freeList.allocate ( size ); +} + +void netReadCopyIO::operator delete ( void *pCadaver, size_t size ) +{ + netReadCopyIO::freeList.release ( pCadaver, size ); +} diff --git a/src/ca/netReadNotifyIO.cpp b/src/ca/netReadNotifyIO.cpp new file mode 100644 index 000000000..e8369e789 --- /dev/null +++ b/src/ca/netReadNotifyIO.cpp @@ -0,0 +1,57 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +tsFreeList < class netReadNotifyIO > netReadNotifyIO::freeList; + +netReadNotifyIO::netReadNotifyIO ( nciu &chan, cacNotify ¬ifyIn ) : + baseNMIU ( chan ), cacNotifyIO ( notifyIn ) +{ +} + +netReadNotifyIO::~netReadNotifyIO () +{ +} + +void netReadNotifyIO::destroy () +{ + delete this; +} + +void netReadNotifyIO::disconnect ( const char *pHostName ) +{ + this->cacNotifyIO::exceptionNotify ( ECA_DISCONN, pHostName ); + delete this; +} + +void netReadNotifyIO::completionNotify () +{ + this->cacNotifyIO::exceptionNotify ( ECA_INTERNAL, "no data returned ?" ); +} + +void netReadNotifyIO::completionNotify ( unsigned type, + unsigned long count, const void *pData ) +{ + this->cacNotifyIO::completionNotify ( type, count, pData ); +} + +void netReadNotifyIO::exceptionNotify ( int status, const char *pContext ) +{ + this->cacNotifyIO::exceptionNotify ( status, pContext ); +} + +void netReadNotifyIO::exceptionNotify ( int status, const char *pContext, + unsigned type, unsigned long count ) +{ + this->cacNotifyIO::exceptionNotify ( status, pContext, type ,count ); +} diff --git a/src/ca/netSubscription.cpp b/src/ca/netSubscription.cpp new file mode 100644 index 000000000..f2db2fc78 --- /dev/null +++ b/src/ca/netSubscription.cpp @@ -0,0 +1,123 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +tsFreeList < class netSubscription > netSubscription::freeList; + +netSubscription::netSubscription ( nciu &chan, chtype typeIn, unsigned long countIn, + unsigned short maskIn, cacNotify ¬ifyIn ) : + baseNMIU (chan), cacNotifyIO (notifyIn), + mask (maskIn), type (typeIn), count (countIn) +{ +} + +netSubscription::~netSubscription () +{ + if ( this->chan.connected () ) { + caHdr hdr; + ca_uint16_t type, count; + + type = (ca_uint16_t) this->chan.nativeType (); + if ( this->chan.nativeElementCount () > 0xffff ) { + count = 0xffff; + } + else { + count = (ca_uint16_t) this->chan.nativeElementCount (); + } + + hdr.m_cmmd = htons (CA_PROTO_EVENT_CANCEL); + hdr.m_available = this->id; + hdr.m_dataType = htons (type); + hdr.m_count = htons (count); + hdr.m_cid = this->chan.sid; + hdr.m_postsize = 0; + + this->chan.piiu->pushStreamMsg (&hdr, NULL, true); + } +} + +void netSubscription::destroy() +{ + delete this; +} + +int netSubscription::subscriptionMsg () +{ + unsigned long count; + struct monops msg; + ca_float32_t p_delta; + ca_float32_t n_delta; + ca_float32_t tmo; + + /* + * clip to the native count and set to the native count if they + * specify zero + */ + if ( this->count > this->chan.nativeElementCount () ){ + count = this->chan.nativeElementCount (); + } + else { + count = this->count; + } + + /* + * dont allow overflow when converting to ca_uint16_t + */ + if ( count > 0xffff ) { + count = 0xffff; + } + + /* msg header */ + msg.m_header.m_cmmd = htons (CA_PROTO_EVENT_ADD); + msg.m_header.m_available = this->id; + msg.m_header.m_dataType = htons ( static_cast (this->type) ); + msg.m_header.m_count = htons ( static_cast (count) ); + msg.m_header.m_cid = this->chan.sid; + msg.m_header.m_postsize = sizeof (msg.m_info); + + /* msg body */ + p_delta = 0.0; + n_delta = 0.0; + tmo = 0.0; + dbr_htonf (&p_delta, &msg.m_info.m_hval); + dbr_htonf (&n_delta, &msg.m_info.m_lval); + dbr_htonf (&tmo, &msg.m_info.m_toval); + msg.m_info.m_mask = htons (this->mask); + msg.m_info.m_pad = 0; /* allow future use */ + + return this->chan.piiu->pushStreamMsg (&msg.m_header, &msg.m_info, true); +} + +void netSubscription::disconnect ( const char *pHostName ) +{ +} + +void netSubscription::completionNotify () +{ + this->cacNotifyIO::completionNotify (); +} + +void netSubscription::completionNotify ( unsigned type, unsigned long count, const void *pData ) +{ + this->cacNotifyIO::completionNotify ( type, count, pData ); +} + +void netSubscription::exceptionNotify ( int status, const char *pContext ) +{ + this->cacNotifyIO::exceptionNotify ( status, pContext ); +} + +void netSubscription::exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ) +{ + this->cacNotifyIO::exceptionNotify ( status, pContext, type, count ); +} diff --git a/src/ca/netWriteNotifyIO.cpp b/src/ca/netWriteNotifyIO.cpp new file mode 100644 index 000000000..1bc00ee1d --- /dev/null +++ b/src/ca/netWriteNotifyIO.cpp @@ -0,0 +1,55 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +tsFreeList < class netWriteNotifyIO > netWriteNotifyIO::freeList; + +netWriteNotifyIO::netWriteNotifyIO (nciu &chan, cacNotify ¬ifyIn) : + baseNMIU (chan), cacNotifyIO (notifyIn) +{ +} + +netWriteNotifyIO::~netWriteNotifyIO () +{ +} + +void netWriteNotifyIO::destroy () +{ + delete this; +} + +void netWriteNotifyIO::disconnect ( const char *pHostName ) +{ + this->exceptionNotify (ECA_DISCONN, pHostName); + delete this; +} + +void netWriteNotifyIO::completionNotify () +{ + this->cacNotifyIO::completionNotify (); +} + +void netWriteNotifyIO::completionNotify ( unsigned type, unsigned long count, const void *pData ) +{ + this->cacNotifyIO::completionNotify (); +} + +void netWriteNotifyIO::exceptionNotify ( int status, const char *pContext ) +{ + this->cacNotifyIO::exceptionNotify (status, pContext); +} + +void netWriteNotifyIO::exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ) +{ + this->cacNotifyIO::exceptionNotify (status, pContext, type, count); +} \ No newline at end of file diff --git a/src/ca/net_convert.h b/src/ca/net_convert.h index b4cd2a481..27cf3e5c4 100644 --- a/src/ca/net_convert.h +++ b/src/ca/net_convert.h @@ -89,7 +89,7 @@ extern "C" { * net format: big endian and IEEE float */ -typedef void CACVRTFUNC(void *pSrc, void *pDest, int hton, unsigned long count); +typedef void CACVRTFUNC (const void *pSrc, void *pDest, int hton, unsigned long count); #ifdef CONVERSION_REQUIRED /* cvrt is (array of) (pointer to) (function returning) int */ diff --git a/src/ca/netiiu.cpp b/src/ca/netiiu.cpp new file mode 100644 index 000000000..38f2f666d --- /dev/null +++ b/src/ca/netiiu.cpp @@ -0,0 +1,30 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +/* + * constructNIIU () + */ +netiiu::netiiu (cac *pcac) : baseIIU (pcac) +{ + ellInit (&this->chidList); +} + +/* + * netiiu::~netiiu () + */ +netiiu::~netiiu () +{ +} + + diff --git a/src/ca/oldAccess.h b/src/ca/oldAccess.h new file mode 100644 index 000000000..4be4ff4c2 --- /dev/null +++ b/src/ca/oldAccess.h @@ -0,0 +1,101 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +struct oldChannel : public cacChannel { +public: + oldChannel (caCh *pConnCallBack, void *pPrivate); + void destroy (); + void setPrivatePointer (void *); + void * privatePointer () const; + int changeConnCallBack (caCh *pfunc); + int replaceAccessRightsEvent (caArh *pfunc); + void ioAttachNotify (); + void ioReleaseNotify (); + + static void * operator new (size_t size); + static void operator delete (void *pCadaver, size_t size); + +private: + caCh *pConnCallBack; + void *pPrivate; + caArh *pAccessRightsFunc; + + ~oldChannel (); // must allocate from pool + void connectTimeoutNotify (); + void connectNotify (); + void disconnectNotify (); + void accessRightsNotify ( caar ); + static tsFreeList < struct oldChannel > freeList; + + friend int epicsShareAPI ca_array_get (chtype type, unsigned long count, chid pChan, void *pValue); +}; + +class getCallback : public cacNotify { +public: + getCallback (oldChannel &chan, caEventCallBackFunc *pFunc, void *pPrivate); + void destroy (); + virtual void completionNotify (unsigned type, unsigned long count, const void *pData); + virtual void exceptionNotify (int status, const char *pContext); + + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); + +private: + oldChannel &chan; + caEventCallBackFunc *pFunc; + void *pPrivate; + ~getCallback (); // allocate only out of pool + static tsFreeList < class getCallback > freeList; +}; + +class putCallback : public cacNotify { +public: + putCallback (oldChannel &chan, caEventCallBackFunc *pFunc, void *pPrivate ); + void destroy (); + virtual void completionNotify (); + virtual void exceptionNotify ( int status, const char *pContext ); + + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); + +private: + oldChannel &chan; + caEventCallBackFunc *pFunc; + void *pPrivate; + ~putCallback (); // allocate only out of pool + static tsFreeList < class putCallback > freeList; +}; + +struct oldSubscription : public cacNotify { +public: + oldSubscription ( oldChannel &chan, caEventCallBackFunc *pFunc, void *pPrivate ); + void destroy (); + + static void * operator new ( size_t size ); + static void operator delete ( void *pCadaver, size_t size ); + +private: + oldChannel &chan; + caEventCallBackFunc *pFunc; + void *pPrivate; + + void completionNotify ( unsigned type, unsigned long count, const void *pData ); + void exceptionNotify ( int status, const char *pContext ); + + ~oldSubscription (); // must allocate from pool + static tsFreeList < struct oldSubscription > freeList; +}; \ No newline at end of file diff --git a/src/ca/oldChannel.cpp b/src/ca/oldChannel.cpp new file mode 100644 index 000000000..48297afef --- /dev/null +++ b/src/ca/oldChannel.cpp @@ -0,0 +1,172 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "iocinf.h" +#include "oldAccess.h" + +tsFreeList < struct oldChannel > oldChannel::freeList; + +oldChannel::oldChannel (caCh *pConnCallBackIn, void *pPrivateIn) : + pConnCallBack (pConnCallBackIn), pPrivate (pPrivateIn), pAccessRightsFunc (0) +{ +} + +oldChannel::~oldChannel () +{ + if ( ! this->pConnCallBack ) { + this->decrementOutstandingIO (); + } +} + +void oldChannel::destroy () +{ + delete this; +} + +void oldChannel::ioAttachNotify () +{ + this->lock (); + if ( ! this->pConnCallBack ) { + this->incrementOutstandingIO (); + } + this->unlock (); +} + +void oldChannel::ioReleaseNotify () +{ + this->lock (); + if ( ! this->pConnCallBack ) { + this->decrementOutstandingIO (); + } + this->unlock (); +} + +void oldChannel::setPrivatePointer (void *pPrivateIn) +{ + this->pPrivate = pPrivateIn; +} + +void * oldChannel::privatePointer () const +{ + return this->pPrivate; +} + +/* + * cacAlreadyConnHandler () + * This is installed into channels which dont have + * a connection handler when ca_pend_io() times + * out so that we will not decrement the pending + * recv count in the future. + */ +extern "C" void cacAlreadyConnHandler (struct connection_handler_args) +{ +} + +void oldChannel::connectTimeoutNotify () +{ + this->lock (); + if ( ! this->pConnCallBack ) { + this->pConnCallBack = cacAlreadyConnHandler; + } + this->unlock (); +} + +void oldChannel::connectNotify () +{ + this->lock (); + if ( this->pConnCallBack ) { + caCh *pCCB = this->pConnCallBack; + struct connection_handler_args args; + args.chid = this; + args.op = CA_OP_CONN_UP; + (*pCCB) (args); + } + else { + this->pConnCallBack = cacAlreadyConnHandler; + this->decrementOutstandingIO (); + } + this->unlock (); +} + +void oldChannel::disconnectNotify () +{ + if ( this->pConnCallBack ) { + struct connection_handler_args args; + args.chid = this; + args.op = CA_OP_CONN_DOWN; + (*this->pConnCallBack) (args); + } +} + +void oldChannel::accessRightsNotify (caar ar) +{ + if ( this->pAccessRightsFunc ) { + struct access_rights_handler_args args; + args.chid = this; + args.ar = ar; + ( *this->pAccessRightsFunc ) ( args ); + } +} + +int oldChannel::changeConnCallBack (caCh *pfunc) +{ + this->lock (); + if ( pfunc == 0) { + if ( this->pConnCallBack != 0 ) { + if ( this->pConnCallBack != cacAlreadyConnHandler ) { + this->incrementOutstandingIO (); + this->pConnCallBack = 0; + } + } + } + else { + if ( this->pConnCallBack == 0 ) { + this->decrementOutstandingIO (); + } + this->pConnCallBack = pfunc; + } + this->unlock (); + + return ECA_NORMAL; +} + +int oldChannel::replaceAccessRightsEvent (caArh *pfunc) +{ + this->lock (); + this->pAccessRightsFunc = pfunc; + if ( pfunc && this->connected () ) { + struct access_rights_handler_args args; + args.chid = this; + args.ar = this->accessRights (); + (*pfunc) (args); + } + this->unlock (); + + return ECA_NORMAL; +} + +void * oldChannel::operator new ( size_t size ) +{ + return oldChannel::freeList.allocate ( size ); +} + +void oldChannel::operator delete ( void *pCadaver, size_t size ) +{ + oldChannel::freeList.release ( pCadaver, size ); +} + + diff --git a/src/ca/oldSubscription.cpp b/src/ca/oldSubscription.cpp new file mode 100644 index 000000000..2da564ffc --- /dev/null +++ b/src/ca/oldSubscription.cpp @@ -0,0 +1,66 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" +#include "oldAccess.h" + +tsFreeList < struct oldSubscription > oldSubscription::freeList; + +oldSubscription::oldSubscription ( oldChannel &chanIn, caEventCallBackFunc *pFuncIn, void *pPrivateIn ) : + chan (chanIn), pFunc (pFuncIn), pPrivate (pPrivateIn) +{ +} + +oldSubscription::~oldSubscription () +{ +} + +void oldSubscription::completionNotify (unsigned type, unsigned long count, const void *pData) +{ + struct event_handler_args args; + + args.usr = this->pPrivate; + args.chid = &this->chan; + args.type = type; + args.count = count; + args.status = ECA_NORMAL; + args.dbr = pData; + (*this->pFunc) (args); +} + +void oldSubscription::exceptionNotify (int status, const char *pContext) +{ + struct event_handler_args args; + + args.usr = this->pPrivate; + args.chid = &this->chan; + args.type = 0; + args.count = 0; + args.status = status; + args.dbr = 0; + (*this->pFunc) (args); +} + +void oldSubscription::destroy () +{ + delete this; +} + +void * oldSubscription::operator new ( size_t size ) +{ + return oldSubscription::freeList.allocate ( size ); +} + +void oldSubscription::operator delete ( void *pCadaver, size_t size ) +{ + oldSubscription::freeList.release ( pCadaver, size ); +} diff --git a/src/ca/os_depen.h b/src/ca/os_depen.h deleted file mode 100644 index 8eebf9665..000000000 --- a/src/ca/os_depen.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * O S _ D E P E N . H - * - * - * OS dependent stuff for channel access - * Author Jeffrey O. Hill - * - * History - * .01 joh 110190 Moved to this file - * .02 joh 082891 on UNIX copy in a new delay into the timeval - * arg to select each call in TCPDELAY in case - * the system modifies my timeval structure. - * Also droped the TCPDELAY to 50 mS - * .03 joh 090391 Updated for V5 vxWorks - * .04 joh 092491 changed order of ops on LOCALTICKS - * .05 joh 092691 added NELEMENTS() - * .06 joh 111991 added EVENTLOCKTEST - * .07 joh 120291 added declaration of taskIdCurrent for - * compiling with V4 vxWorks - * .08 joh 062692 took out printf to logMsg MACRO - * .09 joh 101692 dont quit if select() is interrupted in - * the UNIX version of TCPDELAY - * .10 joh 112092 removed the requirement that VMS locking - * pairs reside at the same C bracket level - * .11 GeG 120992 support VMS/UCX - * .12 CJM 130794 define MYERRNO properly for UCX - * .13 CJM 311094 mods to support DEC C compiler - * .14 joh 100695 removed UNIX include of filio.h and sockio.h - * because they are include by ioctl.h under - * BSD and SVR4 (and they dont exist under linux) - */ - -#ifndef INCos_depenh -#define INCos_depenh - -static char *os_depenhSccsId = "$Id$"; - -#ifdef UNIX -# define CA_OS_CONFIGURED -#endif - -#ifdef iocCore -# include "osiSem.h" -# include "osiThread.h" -# include "taskwd.h" - -# define CA_OS_CONFIGURED -#endif - -#ifdef VMS -# define CA_OS_CONFIGURED -#endif /*VMS*/ - -#ifdef _WIN32 -# define CA_OS_CONFIGURED -#endif /*_WIN32*/ - -#ifndef CA_OS_CONFIGURED -#error Please define one of iocCore, UNIX, VMS, or _WIN32 -#endif - - - -#endif /* INCos_depenh */ - diff --git a/src/ca/processThread.cpp b/src/ca/processThread.cpp index 77abcf746..33d28b23a 100644 --- a/src/ca/processThread.cpp +++ b/src/ca/processThread.cpp @@ -17,60 +17,28 @@ #include -processThread::processThread () : +processThread::processThread (cac *pcacIn) : osiThread ("CAC process", 0x1000, threadPriorityMedium), + pcac (pcacIn), shutDown (false) { - ellInit (&this->recvActivity); } processThread::~processThread () { - this->shutDown = true; - this->exit.signal (); - while ( !this->exit.wait (5.0) ) { + this->signalShutDown (); + while ( ! this->exit.wait ( 10.0 ) ) { printf ("processThread::~processThread (): Warning, thread object destroyed before thread exit \n"); } } void processThread::entryPoint () { - char *pNode; - tcpiiu *piiu; - - while (!this->shutDown) { - while ( 1 ) { - int status; - unsigned bytesToProcess; - - this->mutex.lock (); - pNode = (char *) ellGet (&this->recvActivity); - if (pNode) { - piiu = (tcpiiu *) (pNode - offsetof (tcpiiu, recvActivityNode) ); - piiu->recvPending = FALSE; - } - this->mutex.unlock (); - - if (!pNode) { - break; - } - - if ( piiu->state == iiu_connected ) { - char *pProto = (char *) cacRingBufferReadReserveNoBlock - (&piiu->recv, &bytesToProcess); - if (pProto) { - status = post_msg (&piiu->niiu, &piiu->dest.ia, - pProto, bytesToProcess); - if (status!=ECA_NORMAL) { - initiateShutdownTCPIIU (piiu); - } - cacRingBufferReadCommit (&piiu->recv, bytesToProcess); - cacRingBufferReadFlush (&piiu->recv); - } - } - } - - this->wakeup.wait (); + int status = ca_attach_context ( this->pcac ); + SEVCHK ( status, "attaching to client context in process thread" ); + while ( ! this->shutDown ) { + pcac->processRecvBacklog (); + this->pcac->recvActivity.wait (); } this->exit.signal (); } @@ -78,37 +46,6 @@ void processThread::entryPoint () void processThread::signalShutDown () { this->shutDown = true; - this->wakeup.signal (); + this->pcac->recvActivity.signal (); } -void processThread::installLabor (tcpiiu &iiu) -{ - bool addedIt; - - this->mutex.lock (); - if ( !iiu.recvPending ) { - iiu.recvPending = TRUE; - ellAdd (&this->recvActivity, &iiu.recvActivityNode); - addedIt = true; - } - else { - addedIt = false; - } - this->mutex.unlock (); - - // - // wakeup after unlock improves performance - // - if (addedIt) { - this->wakeup.signal (); - } -} - -void processThread::removeLabor (tcpiiu &iiu) -{ - this->mutex.lock (); - if (iiu.recvPending) { - ellDelete (&this->recvActivity, &iiu.recvActivityNode); - } - this->mutex.unlock (); -} diff --git a/src/ca/putCallback.cpp b/src/ca/putCallback.cpp new file mode 100644 index 000000000..0d69837df --- /dev/null +++ b/src/ca/putCallback.cpp @@ -0,0 +1,71 @@ + +/* + * $Id$ + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "iocinf.h" +#include "oldAccess.h" + +tsFreeList < class putCallback > putCallback::freeList; + +putCallback::putCallback (oldChannel &chanIn, caEventCallBackFunc *pFuncIn, void *pPrivateIn ) : + chan (chanIn), pFunc (pFuncIn), pPrivate (pPrivateIn) +{ +} + +putCallback::~putCallback () +{ +} + +void putCallback::destroy () +{ + delete this; +} + +void putCallback::completionNotify () +{ + struct event_handler_args args; + + args.usr = this->pPrivate; + args.chid = &this->chan; + args.type = 0; + args.count = 0; + args.status = ECA_NORMAL; + args.dbr = 0; + (*this->pFunc) (args); +} + +void putCallback::exceptionNotify (int status, const char *pContext) +{ + struct event_handler_args args; + + args.usr = this->pPrivate; + args.chid = &this->chan; + args.type = 0; + args.count = 0; + args.status = status; + args.dbr = 0; + (*this->pFunc) (args); +} + +void * putCallback::operator new ( size_t size ) +{ + return putCallback::freeList.allocate ( size ); +} + +void putCallback::operator delete ( void *pCadaver, size_t size ) +{ + putCallback::freeList.release ( pCadaver, size ); +} \ No newline at end of file diff --git a/src/ca/repeater.cpp b/src/ca/repeater.cpp index b8d3e7433..49fa4e5b7 100644 --- a/src/ca/repeater.cpp +++ b/src/ca/repeater.cpp @@ -123,7 +123,7 @@ LOCAL makeSocketReturn makeSocket (unsigned short port, int reuseAddr) (char *)&flag, sizeof (flag) ); if (status<0) { int errnoCpy = SOCKERRNO; - errlogPrintf( + ca_printf ( "%s: set socket option failed because \"%s\"\n", __FILE__, SOCKERRSTR(errnoCpy)); } @@ -152,7 +152,7 @@ LOCAL void verifyClients() msr = makeSocket ( ntohs (pclient->from.sin_port), FALSE ); if ( msr.sock != INVALID_SOCKET ) { #ifdef DEBUG - errlogPrintf ("Deleted client %d\n", + ca_printf ("Deleted client %d\n", ntohs (pclient->from.sin_port) ); #endif ellDelete (&theClients, &pclient->node); @@ -165,7 +165,7 @@ LOCAL void verifyClients() * win sock does not set SOCKERRNO when this fails */ if ( msr.errNumber != SOCK_EADDRINUSE ) { - errlogPrintf ( + ca_printf ( "CA Repeater: bind test err was %d=\"%s\"\n", msr.errNumber, msr.pErrStr); } @@ -199,7 +199,7 @@ LOCAL void fanOut (struct sockaddr_in *pFrom, const char *pMsg, unsigned msgSize status = send ( pclient->sock, (char *)pMsg, msgSize, 0); if (status>=0) { #ifdef DEBUG - errlogPrintf ("Sent to %d\n", + ca_printf ("Sent to %d\n", ntohs (pclient->from.sin_port)); #endif } @@ -207,13 +207,13 @@ LOCAL void fanOut (struct sockaddr_in *pFrom, const char *pMsg, unsigned msgSize int errnoCpy = SOCKERRNO; if (errnoCpy == SOCK_ECONNREFUSED) { #ifdef DEBUG - errlogPrintf ("Deleted client %d\n", + ca_printf ("Deleted client %d\n", ntohs (pclient->from.sin_port)); #endif verify = TRUE; } else { - errlogPrintf( + ca_printf( "CA Repeater: UDP fan out err was \"%s\"\n", SOCKERRSTR(errnoCpy)); } @@ -253,7 +253,7 @@ LOCAL void register_new_client (struct sockaddr_in *pFrom) if (!init) { msr = makeSocket (PORT_ANY, TRUE); if ( msr.sock == INVALID_SOCKET ) { - errlogPrintf("%s: Unable to create repeater bind test socket because %d=\"%s\"\n", + ca_printf("%s: Unable to create repeater bind test socket because %d=\"%s\"\n", __FILE__, msr.errNumber, msr.pErrStr); } else { @@ -297,14 +297,14 @@ LOCAL void register_new_client (struct sockaddr_in *pFrom) if (!pclient) { pclient = (struct one_client *) calloc (1, sizeof(*pclient)); if (!pclient) { - errlogPrintf ("%s: no memory for new client\n", __FILE__); + ca_printf ("%s: no memory for new client\n", __FILE__); return; } msr = makeSocket (PORT_ANY, FALSE); if (msr.sock==INVALID_SOCKET) { free(pclient); - errlogPrintf ("%s: no client sock because %d=\"%s\"\n", + ca_printf ("%s: no client sock because %d=\"%s\"\n", __FILE__, msr.errNumber, msr.pErrStr); return; } @@ -316,7 +316,7 @@ LOCAL void register_new_client (struct sockaddr_in *pFrom) sizeof (*pFrom) ); if (status<0) { int errnoCpy = SOCKERRNO; - errlogPrintf ( + ca_printf ( "%s: unable to connect client sock because \"%s\"\n", __FILE__, SOCKERRSTR(errnoCpy)); socket_close (pclient->sock); @@ -329,7 +329,7 @@ LOCAL void register_new_client (struct sockaddr_in *pFrom) ellAdd (&client_list, &pclient->node); newClient = TRUE; #ifdef DEBUG - errlogPrintf ( "Added %d\n", ntohs (pFrom->sin_port) ); + ca_printf ( "Added %d\n", ntohs (pFrom->sin_port) ); #endif } @@ -346,7 +346,7 @@ LOCAL void register_new_client (struct sockaddr_in *pFrom) } else if (SOCKERRNO == SOCK_ECONNREFUSED){ #ifdef DEBUG - errlogPrintf ("Deleted repeater client=%d sending ack\n", + ca_printf ("Deleted repeater client=%d sending ack\n", ntohs (pFrom->sin_port) ); #endif ellDelete (&client_list, &pclient->node); @@ -354,7 +354,7 @@ LOCAL void register_new_client (struct sockaddr_in *pFrom) free (pclient); } else { - errlogPrintf ("CA Repeater: confirm err was \"%s\"\n", + ca_printf ("CA Repeater: confirm err was \"%s\"\n", SOCKERRSTR (SOCKERRNO) ); } @@ -410,7 +410,7 @@ void epicsShareAPI ca_repeater () bsdSockRelease (); exit (0); } - errlogPrintf("%s: Unable to create repeater socket because %d=\"%s\" - fatal\n", + ca_printf("%s: Unable to create repeater socket because %d=\"%s\" - fatal\n", __FILE__, msr.errNumber, msr.pErrStr); bsdSockRelease (); exit(0); @@ -419,7 +419,7 @@ void epicsShareAPI ca_repeater () sock = msr.sock; #ifdef DEBUG - errlogPrintf ("CA Repeater: Attached and initialized\n"); + ca_printf ("CA Repeater: Attached and initialized\n"); #endif while (TRUE) { @@ -444,7 +444,7 @@ void epicsShareAPI ca_repeater () continue; } # endif - errlogPrintf ("CA Repeater: unexpected UDP recv err: %s\n", + ca_printf ("CA Repeater: unexpected UDP recv err: %s\n", SOCKERRSTR(errnoCpy)); continue; } diff --git a/src/ca/ringBuffer.cpp b/src/ca/ringBuffer.cpp index 98430e4c2..7c62abbe3 100644 --- a/src/ca/ringBuffer.cpp +++ b/src/ca/ringBuffer.cpp @@ -64,19 +64,6 @@ int cacRingBufferConstruct (ringBuffer *pBuf) */ void cacRingBufferDestroy (ringBuffer *pBuf) { - /* - * force any read/write/reserve/commit ops in - * other threads to complete - */ - pBuf->shutDown = 1u; - semBinaryGive (pBuf->readSignal); - semBinaryGive (pBuf->writeSignal); - semMutexMustTake (pBuf->readLock); - semMutexMustTake (pBuf->writeLock); - - /* - * clean up - */ semBinaryDestroy (pBuf->readSignal); semBinaryDestroy (pBuf->writeSignal); semMutexDestroy (pBuf->readLock); @@ -313,16 +300,16 @@ unsigned cacRingBufferWrite (ringBuffer *pRing, const void *pBuf, unsigned totalBytes = 0; unsigned curBytes; - semMutexMustTake (pRing->writeLock); + semMutexMustTake ( pRing->writeLock ); - while (totalBytesreadSignal); - semBinaryMustTake (pRing->writeSignal); - if (pRing->shutDown) { - semMutexGive (pRing->writeLock); + while ( totalBytes < nBytes ) { + curBytes = cacRingBufferWritePartial ( pRing, + pBufTmp+totalBytes, nBytes-totalBytes ); + if ( curBytes == 0 ) { + semBinaryGive ( pRing->readSignal ); + semBinaryMustTake ( pRing->writeSignal ); + if ( pRing->shutDown ) { + semMutexGive ( pRing->writeLock ); return totalBytes; } } @@ -331,16 +318,21 @@ unsigned cacRingBufferWrite (ringBuffer *pRing, const void *pBuf, } } - semMutexGive (pRing->writeLock); + semMutexGive ( pRing->writeLock ); return totalBytes; } +void cacRingBufferWriteLock (ringBuffer *pBuf) +{ + semMutexMustTake (pBuf->writeLock); +} + bool cacRingBufferWriteLockNoBlock (ringBuffer *pBuf, unsigned bytesRequired) { semMutexMustTake (pBuf->writeLock); - if (cacRingBufferWriteSize (pBuf)writeLock); return false; } @@ -450,12 +442,20 @@ void cacRingBufferReadCommit (ringBuffer *pRing, unsigned delta) semMutexGive (pRing->readLock); } -void cacRingBufferWriteFlush (ringBuffer *pRing) +bool cacRingBufferWriteFlush (ringBuffer *pRing) { - semBinaryGive (pRing->readSignal); + if ( cacRingBufferReadSize (pRing) ) { + semBinaryGive (pRing->readSignal); + return true; + } + return false; } -void cacRingBufferReadFlush (ringBuffer *pRing) +bool cacRingBufferReadFlush (ringBuffer *pRing) { - semBinaryGive (pRing->writeSignal); + if ( cacRingBufferWriteSize (pRing) ) { + semBinaryGive (pRing->writeSignal); + return true; + } + return false; } diff --git a/src/ca/ringBuffer.h b/src/ca/ringBuffer.h index 63da879be..79e250a44 100644 --- a/src/ca/ringBuffer.h +++ b/src/ca/ringBuffer.h @@ -25,7 +25,6 @@ #define nElementsInRing (1<iiu.niiu.iiu.pcas); + LOCK (this->iiu.pcas); this->retry = 0; - this->period = periodIn; - UNLOCK (this->iiu.niiu.iiu.pcas); + this->period = CA_RECAST_DELAY; + UNLOCK (this->iiu.pcas); - if (this->timeRemaining()>this->period) { - this->reschedule (0.0); + if (this->timeRemaining()>delayToNextTry) { + this->reschedule (delayToNextTry); + debugPrintf ( ("reschedualed search timer for completion in %f sec\n", delayToNextTry) ); } } @@ -53,7 +60,7 @@ void searchTimer::setRetryInterval (unsigned retryNo) unsigned idelay; double delay; - LOCK (this->iiu.niiu.iiu.pcas); + LOCK (this->iiu.pcas); /* * set the retry number @@ -70,11 +77,9 @@ void searchTimer::setRetryInterval (unsigned retryNo) */ this->period = min (CA_RECAST_PERIOD, delay); - UNLOCK (this->iiu.niiu.iiu.pcas); + UNLOCK (this->iiu.pcas); -#ifdef DEBUG - printf ("new CA search period is %f sec\n", this->period); -#endif + debugPrintf ( ("new CA search period is %f sec\n", this->period) ); } // @@ -86,7 +91,7 @@ void searchTimer::setRetryInterval (unsigned retryNo) // void searchTimer::notifySearchResponse (nciu *pChan) { - LOCK (this->iiu.niiu.iiu.pcas); + LOCK (this->iiu.pcas); if ( this->retrySeqNoAtListBegin <= pChan->retrySeqNo ) { if ( this->searchResponses < ULONG_MAX ) { @@ -94,32 +99,31 @@ void searchTimer::notifySearchResponse (nciu *pChan) } } - UNLOCK (this->iiu.niiu.iiu.pcas); + UNLOCK (this->iiu.pcas); if (pChan->retrySeqNo == this->retrySeqNo) { this->reschedule (0.0); } } - // // searchTimer::expire () // void searchTimer::expire () { - nciu *chan; - nciu *firstChan; - int status; - unsigned nSent=0u; + tsDLIterBD chan(0); + tsDLIterBD firstChan(0); + int status; + unsigned nSent=0u; /* * check to see if there is nothing to do here */ - if (ellCount(&this->iiu.niiu.chidList)==0) { + if ( ellCount (&this->iiu.chidList) ==0 ) { return; } - LOCK (this->iiu.niiu.iiu.pcas); + LOCK ( this->iiu.pcas ); /* * increment the retry sequence number @@ -136,7 +140,7 @@ void searchTimer::expire () * * The variable this->framesPerTry * determines the number of UDP frames to be sent - * each time that retrySearchRequest() is called. + * each time that expire() is called. * If this value is too high we will waste some * network bandwidth. If it is too low we will * use very little of the incoming UDP message @@ -168,11 +172,8 @@ void searchTimer::expire () else { this->framesPerTry += (this->framesPerTry/8) + 1; } -#if 0 - printf ("Increasing frame count to %u t=%u r=%u\n", - this->framesPerTry, this->searchTries, - this->searchResponses); -#endif + debugPrintf ( ("Increasing frame count to %u t=%u r=%u\n", + this->framesPerTry, this->searchTries, this->searchResponses) ); } } /* @@ -185,19 +186,17 @@ void searchTimer::expire () this->framesPerTry--; } this->framesPerTryCongestThresh = this->framesPerTry/2 + 1; -#if 0 - printf ("Congestion detected - set frames per try to %u t=%u r=%u\n", + debugPrintf ( ("Congestion detected - set frames per try to %u t=%u r=%u\n", this->framesPerTry, this->searchTries, - this->searchResponses); -#endif + this->searchResponses) ); } /* - * a successful cac_search_msg() sends channel to + * a successful chan->searchMsg() sends channel to * the end of the list */ - firstChan = chan = (nciu *) ellFirst (&this->iiu.niiu.chidList); - while (chan) { + firstChan = chan = this->iiu.chidList.first (); + while ( chan != chan.eol () ) { this->minRetry = min (this->minRetry, chan->retry); @@ -208,11 +207,9 @@ void searchTimer::expire () * dont increase the delay between search * requests */ - if ( this->iiu.niiu.iiu.pcas->ca_pEndOfBCastList == chan ) { + if ( this->iiu.pcas->endOfBCastList == chan ) { if ( this->searchResponses == 0u ) { -#if 0 - printf ("increasing search try interval\n"); -#endif + debugPrintf ( ("increasing search try interval\n") ); this->setRetryInterval (this->minRetry + 1u); } @@ -246,16 +243,14 @@ void searchTimer::expire () } this->searchResponses = 0; -#if 0 - printf ("saw end of list\n"); -#endif + debugPrintf ( ("saw end of list\n") ); } /* * this moves the channel to the end of the * list (if successful) */ - status = cac_search_msg (chan); + status = chan->searchMsg (); if (status != ECA_NORMAL) { nSent++; @@ -267,7 +262,7 @@ void searchTimer::expire () semBinaryGive (this->iiu.xmitSignal); /* try again */ - status = cac_search_msg (chan); + status = chan->searchMsg (); if (status != ECA_NORMAL) { break; } @@ -278,8 +273,8 @@ void searchTimer::expire () } chan->retrySeqNo = this->retrySeqNo; - chan = (nciu *) ellFirst (&this->iiu.niiu.chidList); - + chan = this->iiu.chidList.first (); + /* * dont send any of the channels twice within one try */ @@ -303,15 +298,12 @@ void searchTimer::expire () } } - UNLOCK (this->iiu.niiu.iiu.pcas); + UNLOCK (this->iiu.pcas); /* flush out the search request buffer */ semBinaryGive (this->iiu.xmitSignal); -#ifdef DEBUG - printf ("sent %u delay sec=%f\n", nSent, this->period); -#endif - + debugPrintf ( ("sent %u delay sec=%f\n", nSent, this->period) ); } void searchTimer::destroy () @@ -320,11 +312,11 @@ void searchTimer::destroy () bool searchTimer::again () const { - if (ellCount(&this->iiu.niiu.chidList)==0) { + if ( ellCount (&this->iiu.chidList) == 0 ) { return false; } else { - if (this->retry < MAXCONNTRIES) { + if ( this->retry < MAXCONNTRIES ) { return true; } else { diff --git a/src/ca/service.cpp b/src/ca/service.cpp index d60492ace..e69de29bb 100644 --- a/src/ca/service.cpp +++ b/src/ca/service.cpp @@ -1,1084 +0,0 @@ - -/* - * $Id$ - * - * L O S A L A M O S - * Los Alamos National Laboratory - * Los Alamos, New Mexico 87545 - * - * Author: Jeff Hill - * - * Copyright, 1986, The Regents of the University of California. - * - */ - -#include "iocinf.h" - -#ifdef CONVERSION_REQUIRED -extern CACVRTFUNC *cac_dbr_cvrt[]; -#endif /*CONVERSION_REQUIRED*/ - -typedef void (*pProtoStubTCP) (tcpiiu *piiu); -typedef void (*pProtoStubUDP) (udpiiu *piiu, const struct sockaddr_in *pnet_addr); - -/* - * tcp_noop_action () - */ -LOCAL void tcp_noop_action (tcpiiu * /* piiu */) -{ - return; -} - -/* - * udp_noop_action () - */ -LOCAL void udp_noop_action (udpiiu * /* piiu */, const struct sockaddr_in * /* pnet_addr */) -{ - return; -} - - -/* - * echo_resp_action () - */ -LOCAL void echo_resp_action (tcpiiu *piiu) -{ - piiu->echoPending = FALSE; - piiu->beaconAnomaly = FALSE; - return; -} - -/* - * write_notify_resp_action () - */ -LOCAL void write_notify_resp_action (tcpiiu *piiu) -{ - struct event_handler_args args; - nmiu *monix; - - /* - * run the user's event handler - */ - LOCK (piiu->niiu.iiu.pcas); - monix = (nmiu *) bucketLookupItemUnsignedId (piiu->niiu.iiu.pcas->ca_pFastBucket, - &piiu->niiu.curMsg.m_available); - if (!monix) { - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - /* - * - * call handler, only if they did not clear the - * chid in the interim - */ - if (monix->miu.usr_func) { - args.usr = (void *) monix->miu.usr_arg; - args.chid = monix->miu.pChan; - args.type = monix->miu.type; - args.count = monix->miu.count; - args.dbr = NULL; - /* - * the channel id field is abused for - * write notify status - * - * write notify was added vo CA V4.1 - */ - args.status = ntohl (piiu->niiu.curMsg.m_cid); - - (*monix->miu.usr_func) (args); - } - caIOBlockFree (piiu->niiu.iiu.pcas, monix->id); - UNLOCK (piiu->niiu.iiu.pcas); - - return; -} - -/* - * read_notify_resp_action () - */ -LOCAL void read_notify_resp_action (tcpiiu *piiu) -{ - nmiu *monix; - struct event_handler_args args; - - /* - * run the user's event handler - */ - LOCK (piiu->niiu.iiu.pcas); - monix = (nmiu *) bucketLookupItemUnsignedId( - piiu->niiu.iiu.pcas->ca_pFastBucket, - &piiu->niiu.curMsg.m_available); - if(!monix){ - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - /* - * call handler, only if they did not clear the - * chid in the interim - */ - if (monix->miu.usr_func) { - int v41; - - /* - * convert the data buffer from net - * format to host format - */ -# ifdef CONVERSION_REQUIRED - if (piiu->niiu.curMsg.m_dataTypeniiu.curMsg.m_dataType])( - piiu->niiu.pCurData, - piiu->niiu.pCurData, - FALSE, - piiu->niiu.curMsg.m_count); - } - else { - piiu->niiu.curMsg.m_cid = htonl(ECA_BADTYPE); - } -# endif - - args.usr = (void *)monix->miu.usr_arg; - args.chid = monix->miu.pChan; - args.type = piiu->niiu.curMsg.m_dataType; - args.count = piiu->niiu.curMsg.m_count; - args.dbr = piiu->niiu.pCurData; - /* - * the channel id field is abused for - * read notify status starting - * with CA V4.1 - */ - v41 = CA_V41( - CA_PROTOCOL_VERSION, - piiu->minor_version_number); - if(v41){ - args.status = ntohl(piiu->niiu.curMsg.m_cid); - } - else{ - args.status = ECA_NORMAL; - } - - (*monix->miu.usr_func) (args); - } - caIOBlockFree (piiu->niiu.iiu.pcas, monix->id); - UNLOCK (piiu->niiu.iiu.pcas); - - return; -} - -/* - * event_resp_action () - */ -LOCAL void event_resp_action (tcpiiu *piiu) -{ - struct event_handler_args args; - nmiu *monix; - int v41; - - - /* - * run the user's event handler - */ - LOCK (piiu->niiu.iiu.pcas); - monix = (nmiu *) bucketLookupItemUnsignedId( - piiu->niiu.iiu.pcas->ca_pFastBucket, &piiu->niiu.curMsg.m_available); - if (!monix) { - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - /* - * m_postsize = 0 used to be a confirmation, but is - * now a noop because the above hash lookup will - * not find a matching IO block - */ - if (!piiu->niiu.curMsg.m_postsize) { - caIOBlockFree (piiu->niiu.iiu.pcas, monix->id); - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - /* only call if not disabled */ - if (!monix->miu.usr_func) { - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - /* - * convert the data buffer from net - * format to host format - */ -# ifdef CONVERSION_REQUIRED - if (piiu->niiu.curMsg.m_dataTypeniiu.curMsg.m_dataType])( - piiu->niiu.pCurData, - piiu->niiu.pCurData, - FALSE, - piiu->niiu.curMsg.m_count); - } - else { - piiu->niiu.curMsg.m_cid = htonl(ECA_BADTYPE); - } -# endif - - /* - * Orig version of CA didnt use this - * strucure. This would run faster if I had - * decided to pass a pointer to this - * structure rather than the structure itself - * early on. - */ - args.usr = (void *) monix->miu.usr_arg; - args.chid = monix->miu.pChan; - args.type = piiu->niiu.curMsg.m_dataType; - args.count = piiu->niiu.curMsg.m_count; - args.dbr = piiu->niiu.pCurData; - /* - * the channel id field is abused for - * event status starting - * with CA V4.1 - */ - v41 = CA_V41( - CA_PROTOCOL_VERSION, - piiu->minor_version_number); - if(v41){ - args.status = ntohl(piiu->niiu.curMsg.m_cid); - } - else{ - args.status = ECA_NORMAL; - } - - /* call their handler */ - (*monix->miu.usr_func) (args); - UNLOCK (piiu->niiu.iiu.pcas); - - return; -} - -/* - * read_resp_action () - */ -LOCAL void read_resp_action (tcpiiu *piiu) -{ - nmiu *pIOBlock; - - /* - * verify the event id - */ - LOCK (piiu->niiu.iiu.pcas); - pIOBlock = (nmiu *) bucketLookupItemUnsignedId (piiu->niiu.iiu.pcas->ca_pFastBucket, - &piiu->niiu.curMsg.m_available); - if (!pIOBlock) { - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - /* - * only count get returns if from the current - * read seq - */ - if (VALID_MSG(piiu)){ - /* - * convert the data buffer from net - * format to host format - */ - if (piiu->niiu.curMsg.m_dataType <= (unsigned) LAST_BUFFER_TYPE) { -# ifdef CONVERSION_REQUIRED - (*cac_dbr_cvrt[piiu->niiu.curMsg.m_dataType])( - piiu->niiu.pCurData, - pIOBlock->miu.usr_arg, - FALSE, - piiu->niiu.curMsg.m_count); -# else - if (piiu->niiu.curMsg.m_dataType == DBR_STRING && - piiu->niiu.curMsg.m_count == 1u) { - strcpy ( (char *) pIOBlock->miu.usr_arg, - (char *) piiu->niiu.pCurData ); - } - else { - memcpy( - (char *)pIOBlock->miu.usr_arg, - piiu->niiu.pCurData, - dbr_size_n ( - piiu->niiu.curMsg.m_dataType, - piiu->niiu.curMsg.m_count) - ); - } -# endif - /* - * decrement the outstanding IO count - */ - if (--piiu->niiu.iiu.pcas->ca_pndrecvcnt==0u) { - semBinaryGive (piiu->niiu.iiu.pcas->ca_io_done_sem); - } - } - } - caIOBlockFree (piiu->niiu.iiu.pcas, pIOBlock->id); - UNLOCK (piiu->niiu.iiu.pcas); - - return; -} - -/* - * search_resp_action () - */ -LOCAL void search_resp_action (udpiiu *piiu, const struct sockaddr_in *pnet_addr) -{ - struct sockaddr_in ina; - char rej[64]; - nciu *chan; - tcpiiu *allocpiiu; - unsigned short *pMinorVersion; - unsigned minorVersion; - - /* - * ignore broadcast replies for deleted channels - * - * lock required around use of the sprintf buffer - */ - LOCK (piiu->niiu.iiu.pcas); - chan = (nciu *) bucketLookupItemUnsignedId (piiu->niiu.iiu.pcas->ca_pSlowBucket, - &piiu->niiu.curMsg.m_available); - if (!chan) { - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - /* - * Starting with CA V4.1 the minor version number - * is appended to the end of each search reply. - * This value is ignored by earlier clients. - */ - if(piiu->niiu.curMsg.m_postsize >= sizeof(*pMinorVersion)){ - pMinorVersion = (unsigned short *)(piiu->niiu.pCurData); - minorVersion = ntohs(*pMinorVersion); - } - else{ - minorVersion = CA_UKN_MINOR_VERSION; - } - - /* - * the type field is abused to carry the port number - * so that we can have multiple servers on one host - */ - ina.sin_family = AF_INET; - if (CA_V48 (CA_PROTOCOL_VERSION,minorVersion)) { - if (piiu->niiu.curMsg.m_cid != INADDR_BROADCAST) { - /* - * Leave address in network byte order (m_cid has not been - * converted to the local byte order) - */ - ina.sin_addr.s_addr = piiu->niiu.curMsg.m_cid; - } - else { - ina.sin_addr = pnet_addr->sin_addr; - } - ina.sin_port = htons (piiu->niiu.curMsg.m_dataType); - } - else if (CA_V45 (CA_PROTOCOL_VERSION,minorVersion)) { - ina.sin_port = htons(piiu->niiu.curMsg.m_dataType); - ina.sin_addr = pnet_addr->sin_addr; - } - else { - ina.sin_port = htons(piiu->niiu.iiu.pcas->ca_server_port); - ina.sin_addr = pnet_addr->sin_addr; - } - - /* - * Ignore duplicate search replies - */ - if (&piiu->niiu.iiu.pcas->pudpiiu->niiu.iiu != chan->ciu.piiu) { - tcpiiu *ptcpiiu = iiuToTCPIIU (chan->ciu.piiu); - - if (ptcpiiu->dest.ia.sin_addr.s_addr != ina.sin_addr.s_addr || - ptcpiiu->dest.ia.sin_port != ina.sin_port) { - ipAddrToA (pnet_addr, rej, sizeof(rej)); - sprintf (piiu->niiu.iiu.pcas->ca_sprintf_buf, - "Channel: %s Accepted: %s Rejected: %s ", - ca_name (&chan->ciu), ptcpiiu->host_name_str, rej); - genLocalExcep (piiu->niiu.iiu.pcas, ECA_DBLCHNL, piiu->niiu.iiu.pcas->ca_sprintf_buf); - } - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - allocpiiu = constructTCPIIU (piiu->niiu.iiu.pcas, &ina, minorVersion); - if (!allocpiiu) { - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - /* - * If this is the first channel to be - * added to this niiu then communicate - * the client's name to the server. - * (CA V4.1 or higher) - */ - if (ellCount(&allocpiiu->niiu.chidList)==0) { - issue_identify_client (allocpiiu); - issue_client_host_name (allocpiiu); - } - - piiu->searchTmr.notifySearchResponse (chan); - - /* - * remove it from the broadcast niiu - */ - removeFromChanList (chan); - - /* - * chan->piiu must be correctly set prior to issuing the - * claim request - * - * add to the beginning of the list until we - * have sent the claim message (after which we - * move it to the end of the list) - * - * claim pending flag is set here - */ - addToChanList (chan, &allocpiiu->niiu); - - /* - * Assume that we have access once connected briefly - * until the server is able to tell us the correct - * state for backwards compatibility. - * - * Their access rights call back does not get - * called for the first time until the information - * arrives however. - */ - chan->ar.read_access = TRUE; - chan->ar.write_access = TRUE; - - /* - * claim the resource in the IOC - * over TCP so problems with duplicate UDP port - * after reboot go away - * - * if we cant immediately get buffer space then we - * attempt to flush once and then try again. - * If this fails then we will wait for the - * next search response. - */ - chan->sid = piiu->niiu.curMsg.m_cid; - - if (!CA_V42(CA_PROTOCOL_VERSION, minorVersion)) { - chan->type = piiu->niiu.curMsg.m_dataType; - chan->count = piiu->niiu.curMsg.m_count; - } - - issue_claim_channel (chan); - cacRingBufferWriteFlush (&allocpiiu->send); - UNLOCK (piiu->niiu.iiu.pcas); -} - -/* - * read_sync_resp_action () - */ -LOCAL void read_sync_resp_action (tcpiiu *piiu) -{ - piiu->read_seq++; - return; -} - -/* - * beacon_action () - */ -LOCAL void beacon_action (udpiiu *piiu, const struct sockaddr_in *pnet_addr) -{ - struct sockaddr_in ina; - - LOCK (piiu->niiu.iiu.pcas); - - /* - * this allows a fan-out server to potentially - * insert the true address of the CA server - * - * old servers: - * 1) set this field to one of the ip addresses of the host _or_ - * 2) set this field to htonl(INADDR_ANY) - * new servers: - * always set this field to htonl(INADDR_ANY) - * - * clients always assume that if this - * field is set to something that isnt htonl(INADDR_ANY) - * then it is the overriding IP address of the server. - */ - ina.sin_family = AF_INET; - if (piiu->niiu.curMsg.m_available != htonl(INADDR_ANY)) { - ina.sin_addr.s_addr = piiu->niiu.curMsg.m_available; - } - else { - ina.sin_addr = pnet_addr->sin_addr; - } - if (piiu->niiu.curMsg.m_count != 0) { - ina.sin_port = htons (piiu->niiu.curMsg.m_count); - } - else { - /* - * old servers dont supply this and the - * default port must be assumed - */ - ina.sin_port = htons (piiu->niiu.iiu.pcas->ca_server_port); - } - mark_server_available (piiu->niiu.iiu.pcas, &ina); - - UNLOCK (piiu->niiu.iiu.pcas); - - return; -} - -/* - * repeater_ack_action () - */ -LOCAL void repeater_ack_action (udpiiu *piiu, const struct sockaddr_in * /* pnet_addr */) -{ - piiu->repeaterContacted = 1u; -# ifdef DEBUG - ca_printf (piiu->niiu.iiu.pcas, "CAC: repeater confirmation recv\n"); -# endif - return; -} - -/* - * not_here_resp_action () - */ -LOCAL void not_here_resp_action (udpiiu * /* piiu */, const struct sockaddr_in * /* pnet_addr */) -{ - return; -} - -/* - * clear_channel_resp_action () - */ -LOCAL void clear_channel_resp_action (tcpiiu * /* piiu */) -{ - /* presently a noop */ - return; -} - -/* - * exception_resp_action () - */ -LOCAL void exception_resp_action (tcpiiu *piiu) -{ - nmiu *monix; - nciu *pChan; - char context[255]; - caHdr *req = (caHdr *) piiu->niiu.pCurData; - int op; - struct exception_handler_args args; - - /* - * dont process the message if they have - * disabled notification - */ - if (!piiu->niiu.iiu.pcas->ca_exception_func){ - return; - } - - if (piiu->niiu.curMsg.m_postsize > sizeof(caHdr)){ - sprintf (context, "detected by: %s for: %s", - piiu->host_name_str, (char *)(req+1)); - } - else{ - sprintf (context, "detected by: %s", piiu->host_name_str); - } - - /* - * Map internal op id to external op id so I - * can freely change the protocol in the - * future. This is quite wasteful of space - * however. - */ - monix = NULL; - args.addr = NULL; - args.pFile = NULL; - args.lineNo = 0u; - args.chid = NULL; - - LOCK (piiu->niiu.iiu.pcas); - switch (ntohs(req->m_cmmd)) { - case CA_PROTO_READ_NOTIFY: - monix = (nmiu *) bucketLookupItemUnsignedId( - piiu->niiu.iiu.pcas->ca_pFastBucket, &req->m_available); - op = CA_OP_GET; - break; - case CA_PROTO_READ: - monix = (nmiu *) bucketLookupItemUnsignedId( - piiu->niiu.iiu.pcas->ca_pFastBucket, &req->m_available); - if (monix) { - args.addr = monix->miu.usr_arg; - } - op = CA_OP_GET; - break; - case CA_PROTO_WRITE_NOTIFY: - monix = (nmiu *) bucketLookupItemUnsignedId( - piiu->niiu.iiu.pcas->ca_pFastBucket, &req->m_available); - op = CA_OP_PUT; - break; - case CA_PROTO_WRITE: - op = CA_OP_PUT; - pChan = (nciu *) bucketLookupItemUnsignedId - (piiu->niiu.iiu.pcas->ca_pSlowBucket, &piiu->niiu.curMsg.m_cid); - args.chid = (chid) &pChan->ciu; - break; - case CA_PROTO_SEARCH: - op = CA_OP_SEARCH; - pChan = (nciu *) bucketLookupItemUnsignedId - (piiu->niiu.iiu.pcas->ca_pSlowBucket, &piiu->niiu.curMsg.m_cid); - args.chid = (chid) &pChan->ciu; - break; - case CA_PROTO_EVENT_ADD: - monix = (nmiu *) bucketLookupItemUnsignedId( - piiu->niiu.iiu.pcas->ca_pFastBucket, &req->m_available); - op = CA_OP_ADD_EVENT; - break; - case CA_PROTO_EVENT_CANCEL: - monix = (nmiu *) bucketLookupItemUnsignedId( - piiu->niiu.iiu.pcas->ca_pFastBucket, &req->m_available); - op = CA_OP_CLEAR_EVENT; - break; - default: - op = CA_OP_OTHER; - break; - } - - if (monix) { - args.chid = monix->miu.pChan; - caIOBlockFree (piiu->niiu.iiu.pcas, monix->id); - } - - args.usr = piiu->niiu.iiu.pcas->ca_exception_arg; - args.type = ntohs (req->m_dataType); - args.count = ntohs (req->m_count); - args.stat = ntohl (piiu->niiu.curMsg.m_available); - args.op = op; - args.ctx = context; - - (*piiu->niiu.iiu.pcas->ca_exception_func) (args); - UNLOCK (piiu->niiu.iiu.pcas); - - return; -} - -/* - * access_rights_resp_action () - */ -LOCAL void access_rights_resp_action (tcpiiu *piiu) -{ - int ar; - nciu *chan; - - LOCK (piiu->niiu.iiu.pcas); - chan = (nciu *) bucketLookupItemUnsignedId( - piiu->niiu.iiu.pcas->ca_pSlowBucket, &piiu->niiu.curMsg.m_cid); - if (!chan) { - /* - * end up here if they delete the channel - * prior to connecting - */ - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - ar = ntohl (piiu->niiu.curMsg.m_available); - chan->ar.read_access = (ar&CA_PROTO_ACCESS_RIGHT_READ)?1:0; - chan->ar.write_access = (ar&CA_PROTO_ACCESS_RIGHT_WRITE)?1:0; - - if (chan->ciu.pAccessRightsFunc) { - struct access_rights_handler_args args; - - args.chid = (chid) &chan->ciu; - args.ar = chan->ar; - (*chan->ciu.pAccessRightsFunc)(args); - } - UNLOCK (piiu->niiu.iiu.pcas); - return; -} - -/* - * claim_ciu_resp_action () - */ -LOCAL void claim_ciu_resp_action (tcpiiu *piiu) -{ - cac_reconnect_channel (piiu, piiu->niiu.curMsg.m_cid, - piiu->niiu.curMsg.m_dataType, piiu->niiu.curMsg.m_count); - return; -} - -/* - * verifyAndDisconnectChan () - */ -LOCAL void verifyAndDisconnectChan (tcpiiu *piiu) -{ - nciu *chan; - - LOCK (piiu->niiu.iiu.pcas); - chan = (nciu *) bucketLookupItemUnsignedId( - piiu->niiu.iiu.pcas->ca_pSlowBucket, &piiu->niiu.curMsg.m_cid); - if (!chan) { - /* - * end up here if they delete the channel - * prior to this response - */ - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - /* - * need to move the channel back to the cast niiu - * (so we will be able to reconnect) - * - * this marks the niiu for disconnect if the channel - * count goes to zero - */ - cacDisconnectChannel (chan); - UNLOCK (piiu->niiu.iiu.pcas); - return; -} - -/* - * bad_tcp_resp_action () - */ -LOCAL void bad_tcp_resp_action (tcpiiu *piiu) -{ - ca_printf (piiu->niiu.iiu.pcas, "CAC: Bad response code in TCP message from %s was %u\n", - piiu->host_name_str, piiu->niiu.curMsg.m_cmmd); -} - -/* - * bad_udp_resp_action () - */ -LOCAL void bad_udp_resp_action (udpiiu *piiu, const struct sockaddr_in *pnet_addr) -{ - char buf[256]; - ipAddrToA ( pnet_addr, buf, sizeof (buf) ); - ca_printf (piiu->niiu.iiu.pcas, "CAC: Bad response code in UDP message from %s was %u\n", - buf, piiu->niiu.curMsg.m_cmmd); -} - -/* - * cac_reconnect_channel() - */ -void cac_reconnect_channel (tcpiiu *piiu, caResId cid, unsigned short type, unsigned long count) -{ - nmiu *pevent; - unsigned prevConn; - int v41; - nciu *chan; - - LOCK (piiu->niiu.iiu.pcas); - chan = (nciu *) bucketLookupItemUnsignedId (piiu->niiu.iiu.pcas->ca_pSlowBucket, &cid); - /* - * this test will fail if they delete the channel - * prior to the reply from the claim message - */ - if (!chan) { - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - if (chan->connected) { - ca_printf(piiu->niiu.iiu.pcas, - "CAC: Ignored conn resp to conn chan CID=%u SID=%u?\n", - chan->cid, chan->sid); - UNLOCK (piiu->niiu.iiu.pcas); - return; - } - - v41 = CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number); - - if (CA_V44(CA_PROTOCOL_VERSION, piiu->minor_version_number)) { - chan->sid = piiu->niiu.curMsg.m_available; - } - - /* - * Update rmt chid fields from caHdr fields - * if they are valid - */ - if (type != USHRT_MAX) { - chan->type = type; - chan->count = count; - } - - /* - * set state to connected before caling - * ca_request_event() so their channel - * connect tests wont fail - */ - chan->connected = 1; - prevConn = chan->previousConn; - chan->previousConn = 1; - - /* - * NOTE: monitor and callback reissue must occur prior to calling - * their connection routine otherwise they could be requested twice. - * - * resubscribe for monitors from this channel - */ - if ( ellCount (&chan->eventq) ) { - for (pevent = (nmiu *) ellFirst (&chan->eventq); - pevent; pevent = (nmiu *) ellNext (&pevent->node) ) { - ca_request_event (chan, pevent); - } - } - - /* - * if less than v4.1 then the server will never - * send access rights and we know that there - * will always be access and call their call back - * here - */ - if (chan->ciu.pAccessRightsFunc && !v41) { - struct access_rights_handler_args args; - - args.chid = (chid) &chan->ciu; - args.ar = chan->ar; - (*chan->ciu.pAccessRightsFunc)(args); - } - - if (chan->ciu.pConnFunc) { - struct connection_handler_args args; - - args.chid = (chid) &chan->ciu; - args.op = CA_OP_CONN_UP; - - (*chan->ciu.pConnFunc)(args); - - } - else if (!prevConn) { - /* - * decrement the outstanding IO count - */ - if (--piiu->niiu.iiu.pcas->ca_pndrecvcnt==0u) { - semBinaryGive (piiu->niiu.iiu.pcas->ca_io_done_sem); - } - } - UNLOCK (piiu->niiu.iiu.pcas); -} - - -/* - * TCP protocol jump table - */ -LOCAL const pProtoStubTCP tcpJumpTableCAC[] = -{ - tcp_noop_action, - event_resp_action, - bad_tcp_resp_action, - read_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - read_sync_resp_action, - exception_resp_action, - clear_channel_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - read_notify_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - claim_ciu_resp_action, - write_notify_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - access_rights_resp_action, - echo_resp_action, - bad_tcp_resp_action, - bad_tcp_resp_action, - verifyAndDisconnectChan, - verifyAndDisconnectChan -}; - -/* - * UDP protocol jump table - */ -LOCAL const pProtoStubUDP udpJumpTableCAC[] = -{ - udp_noop_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - search_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - beacon_action, - not_here_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - repeater_ack_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action, - bad_udp_resp_action -}; - -/* - * post_msg() - * - * LOCK should be applied when calling this routine - * - */ -int post_msg (netIIU *piiu, const struct sockaddr_in *pnet_addr, - char *pInBuf, unsigned long blockSize) -{ - unsigned long size; - - while (blockSize) { - - /* - * fetch a complete message header - */ - if ( piiu->curMsgBytes < sizeof (piiu->curMsg) ) { - char *pHdr; - - size = sizeof (piiu->curMsg) - piiu->curMsgBytes; - size = min (size, blockSize); - - pHdr = (char *) &piiu->curMsg; - memcpy( pHdr + piiu->curMsgBytes, pInBuf, size); - - piiu->curMsgBytes += size; - if (piiu->curMsgBytes < sizeof(piiu->curMsg)) { -#if 0 - printf ("waiting for %d msg hdr bytes\n", - sizeof(piiu->curMsg)-piiu->curMsgBytes); -#endif - return ECA_NORMAL; - } - - pInBuf += size; - blockSize -= size; - - /* - * fix endian of bytes - */ - piiu->curMsg.m_postsize = - ntohs(piiu->curMsg.m_postsize); - piiu->curMsg.m_cmmd = ntohs(piiu->curMsg.m_cmmd); - piiu->curMsg.m_dataType = ntohs(piiu->curMsg.m_dataType); - piiu->curMsg.m_count = ntohs(piiu->curMsg.m_count); -#if 0 - ca_printf (piiu->niiu.iiu.pcas, - "%s Cmd=%3d Type=%3d Count=%4d Size=%4d", - piiu->host_name_str, - piiu->curMsg.m_cmmd, - piiu->curMsg.m_dataType, - piiu->curMsg.m_count, - piiu->curMsg.m_postsize); - ca_printf (piiu->niiu.iiu.pcas, - " Avail=%8x Cid=%6d\n", - piiu->curMsg.m_available, - piiu->curMsg.m_cid); -#endif - } - - - /* - * dont allow huge msg body until - * the server supports it - */ - if (piiu->curMsg.m_postsize>(unsigned)MAX_TCP) { - piiu->curMsgBytes = 0; - piiu->curDataBytes = 0; - return ECA_TOLARGE; - } - - /* - * make sure we have a large enough message body cache - */ - if (piiu->curMsg.m_postsize>piiu->curDataMax) { - void *pCurData; - size_t cacheSize; - - /* - * scalar DBR_STRING is sometimes clipped to the - * actual string size so make sure this cache is - * as large as one DBR_STRING so they will - * not page fault if they read MAX_STRING_SIZE - * bytes (instead of the actual string size). - */ - cacheSize = max (piiu->curMsg.m_postsize, MAX_STRING_SIZE); - pCurData = (void *) calloc(1u, cacheSize); - if (!pCurData) { - return ECA_ALLOCMEM; - } - if (piiu->pCurData) { - free (piiu->pCurData); - } - piiu->pCurData = pCurData; - piiu->curDataMax = piiu->curMsg.m_postsize; - } - - /* - * Fetch a complete message body - * (allows for arrays larger than than the - * ring buffer size) - */ - if(piiu->curMsg.m_postsize>piiu->curDataBytes){ - char *pBdy; - - size = piiu->curMsg.m_postsize - piiu->curDataBytes; - size = min(size, blockSize); - pBdy = (char *) piiu->pCurData; - memcpy ( pBdy+piiu->curDataBytes, pInBuf, size); - piiu->curDataBytes += size; - if (piiu->curDataBytes < piiu->curMsg.m_postsize) { -#if 0 - printf ("waiting for %d msg bdy bytes\n", - piiu->curMsg.m_postsize-piiu->curDataBytes); -#endif - return ECA_NORMAL; - } - pInBuf += size; - blockSize -= size; - } - - /* - * execute the response message - */ - if (piiu == &piiu->iiu.pcas->pudpiiu->niiu) { - pProtoStubUDP pStub; - if (piiu->curMsg.m_cmmd>=NELEMENTS(udpJumpTableCAC)) { - pStub = bad_udp_resp_action; - } - else { - pStub = udpJumpTableCAC [piiu->curMsg.m_cmmd]; - } - (*pStub) (piiu->iiu.pcas->pudpiiu, pnet_addr); - - } - else { - pProtoStubTCP pStub; - if (piiu->curMsg.m_cmmd>=NELEMENTS(tcpJumpTableCAC)) { - pStub = bad_tcp_resp_action; - } - else { - pStub = tcpJumpTableCAC [piiu->curMsg.m_cmmd]; - } - (*pStub) (iiuToTCPIIU(&piiu->iiu)); - } - - piiu->curMsgBytes = 0; - piiu->curDataBytes = 0; - - } - return ECA_NORMAL; -} - - diff --git a/src/ca/syncGroupNotify.cpp b/src/ca/syncGroupNotify.cpp new file mode 100644 index 000000000..e3b3ff5a9 --- /dev/null +++ b/src/ca/syncGroupNotify.cpp @@ -0,0 +1,130 @@ + +/* + * $Id$ + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + */ + +#include "iocinf.h" + +tsFreeList < class syncGroupNotify > syncGroupNotify::freeList; + +syncGroupNotify::syncGroupNotify ( CASG &sgIn, void *pValueIn ) : + sg (sgIn), magic (CASG_MAGIC), pValue (pValueIn), seqNo ( sgIn.seqNo ) +{ + this->sg.mutex.lock (); + this->sg.ioList.add (*this); + this->sg.opPendCount++; + this->sg.mutex.unlock (); +} + +void syncGroupNotify::destroy () +{ + delete this; +} + +syncGroupNotify::~syncGroupNotify () +{ + this->sg.mutex.lock (); + this->sg.ioList.remove (*this); + this->sg.mutex.unlock (); +} + +void syncGroupNotify::completionNotify () +{ + if ( this->magic != CASG_MAGIC ) { + ca_printf ("cac: sync group io_complete(): bad sync grp op magic number?\n"); + return; + } + + this->sg.mutex.lock (); + if ( this->seqNo == this->sg.seqNo ) { + assert ( this->sg.opPendCount > 0u ); + this->sg.opPendCount--; + } + this->sg.mutex.unlock (); + + if ( this->sg.opPendCount == 0 ) { + this->sg.sem.signal (); + } +} + +void syncGroupNotify::completionNotify ( unsigned type, unsigned long count, const void *pData ) +{ + if ( this->magic != CASG_MAGIC ) { + ca_printf ("cac: sync group io_complete(): bad sync grp op magic number?\n"); + return; + } + + this->sg.mutex.lock (); + if ( this->seqNo == this->sg.seqNo ) { + /* + * Update the user's variable + */ + if ( this->pValue ) { + size_t size = dbr_size_n ( type, count ); + memcpy (this->pValue, pData, size); + } + assert ( this->sg.opPendCount > 0u ); + this->sg.opPendCount--; + } + this->sg.mutex.unlock (); + + if ( this->sg.opPendCount == 0 ) { + this->sg.sem.signal (); + } +} + +void syncGroupNotify::show (unsigned level) const +{ + printf ( "pending sg op: pVal=%p, magic=%u seqNo=%lu sg=%p\n", + this->pValue, this->magic, this->seqNo, &this->sg); +} + +void syncGroupNotify::exceptionNotify ( int status, const char *pContext ) +{ + ca_signal_formated ( status, __FILE__, __LINE__, + "CA Sync Group request failed because \"%s\"\n", pContext); +} + +void syncGroupNotify::exceptionNotify ( int status, const char *pContext, unsigned type, unsigned long count ) +{ + ca_signal_formated ( status, __FILE__, __LINE__, + "CA Sync Group request failed with type=%d count=%ld because \"%s\"\n", + type, count, pContext); +} + +void * syncGroupNotify::operator new (size_t size) +{ + return syncGroupNotify::freeList.allocate ( size ); +} + +void syncGroupNotify::operator delete (void *pCadaver, size_t size) +{ + syncGroupNotify::freeList.release ( pCadaver, size ); +} + + diff --git a/src/ca/syncgrp.cpp b/src/ca/syncgrp.cpp index b4f1c4deb..7f07cdcd8 100644 --- a/src/ca/syncgrp.cpp +++ b/src/ca/syncgrp.cpp @@ -3,7 +3,6 @@ * Author: Jeffrey O. Hill * hill@luke.lanl.gov * (505) 665 1831 - * Date: 9-93 * * Experimental Physics and Industrial Control System (EPICS) * @@ -28,266 +27,62 @@ * */ -#include "freeList.h" - #include "iocinf.h" -/* - * ca_sg_init() - */ -void ca_sg_init (cac *pcac) -{ - /* - * init all sync group lists - */ - ellInit (&pcac->activeCASG); - ellInit (&pcac->activeCASGOP); - freeListInitPvt (&pcac->ca_sgFreeListPVT, sizeof(CASG), 32); - freeListInitPvt (&pcac->ca_sgopFreeListPVT, sizeof(CASGOP), 256); - - return; -} - -/* - * ca_sg_shutdown() - */ -void ca_sg_shutdown (cac *pcac) -{ - CASG *pcasg; - CASG *pnextcasg; - int status; - - /* - * free all sync group lists - */ - LOCK (pcac); - pcasg = (CASG *) ellFirst (&pcac->activeCASG); - while (pcasg) { - pnextcasg = (CASG *) ellNext (&pcasg->node); - status = ca_sg_delete (pcasg->id); - assert (status==ECA_NORMAL); - pcasg = pnextcasg; - } - assert (ellCount(&pcac->activeCASG)==0); - - /* - * per sync group - */ - freeListCleanup(pcac->ca_sgFreeListPVT); - - /* - * per sync group op - */ - ellFree (&pcac->activeCASGOP); - freeListCleanup(pcac->ca_sgopFreeListPVT); - - UNLOCK (pcac); - - return; -} - /* * ca_sg_create() */ -int epicsShareAPI ca_sg_create (CA_SYNC_GID *pgid) +int epicsShareAPI ca_sg_create ( CA_SYNC_GID *pgid ) { int caStatus; - int status; CASG *pcasg; cac *pcac; - caStatus = fetchClientContext (&pcac); + caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } - /* - * first look on a free list. If not there - * allocate dynamic memory for it. - */ - pcasg = (CASG *) freeListMalloc (pcac->ca_sgFreeListPVT); - if(!pcasg){ - return ECA_ALLOCMEM; - } - - LOCK (pcac); - - /* - * setup initial values for all of the fields - * - * lock must be applied when allocating an id - * and using the id bucket - */ - memset((char *)pcasg,0,sizeof(*pcasg)); - pcasg->magic = CASG_MAGIC; - pcasg->opPendCount = 0; - pcasg->seqNo = 0; - - pcasg->sem = semBinaryMustCreate (semEmpty); - - do { - pcasg->id = CLIENT_SLOW_ID_ALLOC (pcac); - status = bucketAddItemUnsignedId (pcac->ca_pSlowBucket, &pcasg->id, pcasg); - } while (status == S_bucket_idInUse); - - if (status == S_bucket_success) { - /* - * place it on the active sync group list - */ - ellAdd (&pcac->activeCASG, &pcasg->node); + pcasg = new CASG ( *pcac ); + if ( pcasg ) { + *pgid = pcasg->getId (); + return ECA_NORMAL; } else { - /* - * place it back on the free sync group list - */ - freeListFree(pcac->ca_sgFreeListPVT, pcasg); - UNLOCK (pcac); - if (status == S_bucket_noMemory) { - return ECA_ALLOCMEM; - } - else { - return ECA_INTERNAL; - } + return ECA_ALLOCMEM; } - - UNLOCK (pcac); - - *pgid = pcasg->id; - return ECA_NORMAL; } /* * ca_sg_delete() */ -int epicsShareAPI ca_sg_delete(const CA_SYNC_GID gid) +int epicsShareAPI ca_sg_delete (const CA_SYNC_GID gid) { - int caStatus; - int status; - CASG *pcasg; - cac *pcac; + int caStatus; + CASG *pcasg; + cac *pcac; - caStatus = fetchClientContext (&pcac); + caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } - LOCK (pcac); - - pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); - if (!pcasg || pcasg->magic != CASG_MAGIC) { - UNLOCK (pcac); + pcasg = pcac->lookupCASG ( gid ); + if ( ! pcasg ) { return ECA_BADSYNCGRP; } - status = bucketRemoveItemUnsignedId (pcac->ca_pSlowBucket, &gid); - assert (status == S_bucket_success); - - semBinaryDestroy(pcasg->sem); - pcasg->magic = 0; - ellDelete(&pcac->activeCASG, &pcasg->node); - UNLOCK (pcac); - - freeListFree(pcac->ca_sgFreeListPVT, pcasg); + pcasg->destroy (); return ECA_NORMAL; } -/* - * ca_sg_block_private () - */ -static int ca_sg_block_private (cac *pcac, const CA_SYNC_GID gid, ca_real timeout) -{ - TS_STAMP cur_time; - TS_STAMP beg_time; - ca_real delay; - int status; - CASG *pcasg; - unsigned flushCompleted = FALSE; - - if (timeout<0.0) { - return ECA_TIMEOUT; - } - - status = tsStampGetCurrent (&cur_time); - if (status!=0) { - return ECA_INTERNAL; - } - - LOCK (pcac); - - pcac->currentTime = cur_time; - - pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); - if ( !pcasg || pcasg->magic != CASG_MAGIC ) { - UNLOCK (pcac); - return ECA_BADSYNCGRP; - } - - UNLOCK (pcac); - - cacFlushAllIIU (pcac); - - beg_time = cur_time; - delay = 0.0; - - status = ECA_NORMAL; - while (pcasg->opPendCount) { - ca_real remaining; - int tsStatus; - - /* - * Exit if the timeout has expired - * (dont wait forever for an itsy bitsy - * delay which will not be updated if - * select is called with no delay) - * - * current time is only updated by - * cac_select_io() if we specify - * at non-zero delay - */ - remaining = timeout-delay; - if (remaining<=CAC_SIGNIFICANT_SELECT_DELAY) { - /* - * Make sure that we take care of - * recv backlog at least once - */ - status = ECA_TIMEOUT; - break; - } - - remaining = min (60.0, remaining); - - /* - * wait for asynch notification - */ - semBinaryTakeTimeout (pcasg->sem, remaining); - - /* - * force a time update - */ - tsStatus = tsStampGetCurrent (&cur_time); - if (tsStatus!=0) { - status = ECA_INTERNAL; - break; - } - - LOCK (pcac); - pcac->currentTime = cur_time; - UNLOCK (pcac); - - flushCompleted = TRUE; - delay = tsStampDiffInSeconds (&cur_time, &beg_time); - } - pcasg->opPendCount = 0; - pcasg->seqNo++; - return status; -} - /* * ca_sg_block () */ int epicsShareAPI ca_sg_block (const CA_SYNC_GID gid, ca_real timeout) { + CASG *pcasg; cac *pcac; int status; @@ -296,20 +91,13 @@ int epicsShareAPI ca_sg_block (const CA_SYNC_GID gid, ca_real timeout) return status; } - /* - * dont allow recursion - */ - { - void *p = threadPrivateGet (cacRecursionLock); - if (p) { - return ECA_EVDISALLOW; - } - threadPrivateSet (cacRecursionLock, &cacRecursionLock); + pcasg = pcac->lookupCASG ( gid ); + if ( ! pcasg ) { + status = ECA_BADSYNCGRP; + } + else { + status = pcasg->block ( timeout ); } - - status = ca_sg_block_private (pcac, gid, timeout); - - threadPrivateSet (cacRecursionLock, NULL); return status; } @@ -319,37 +107,34 @@ int epicsShareAPI ca_sg_block (const CA_SYNC_GID gid, ca_real timeout) */ int epicsShareAPI ca_sg_reset (const CA_SYNC_GID gid) { - CASG *pcasg; - cac *pcac; - int caStatus; + CASG *pcasg; + cac *pcac; + int caStatus; caStatus = fetchClientContext (&pcac); if ( caStatus != ECA_NORMAL ) { return caStatus; } - LOCK (pcac); - pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK (pcac); - return ECA_BADSYNCGRP; + pcasg = pcac->lookupCASG ( gid ); + if ( ! pcasg ) { + caStatus = ECA_BADSYNCGRP; + } + else { + caStatus = ECA_NORMAL; + pcasg->reset (); } - pcasg->opPendCount = 0; - pcasg->seqNo++; - UNLOCK (pcac); - - return ECA_NORMAL; + return caStatus; } /* * ca_sg_stat */ -int epicsShareAPI ca_sg_stat (const CA_SYNC_GID gid) +int epicsShareAPI ca_sg_stat ( const CA_SYNC_GID gid ) { CASG *pcasg; - CASGOP *pcasgop; - cac *pcac; + cac *pcac; int caStatus; caStatus = fetchClientContext (&pcac); @@ -357,31 +142,13 @@ int epicsShareAPI ca_sg_stat (const CA_SYNC_GID gid) return caStatus; } - LOCK (pcac); - pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); - if (!pcasg || pcasg->magic != CASG_MAGIC) { - UNLOCK (pcac); + pcasg = pcac->lookupCASG ( gid ); + if ( ! pcasg ) { printf("Bad Sync Group Id\n"); - return ECA_BADSYNCGRP; + caStatus = ECA_BADSYNCGRP; } - UNLOCK (pcac); - printf("Sync Group: id=%u, magic=%lu, opPend=%lu, seqNo=%lu\n", - pcasg->id, pcasg->magic, pcasg->opPendCount, - pcasg->seqNo); - - LOCK (pcac); - pcasgop = (CASGOP *) ellFirst (&pcac->activeCASGOP); - while (pcasgop) { - if (pcasg->id == pcasgop->id) { - printf( - "pending op: id=%u pVal=%x, magic=%lu seqNo=%lu\n", - pcasgop->id, (unsigned)pcasgop->pValue, pcasgop->magic, - pcasgop->seqNo); - } - pcasgop = (CASGOP *) ellNext(&pcasgop->node); - } - UNLOCK (pcac); + pcasg->show (1000u); return ECA_NORMAL; } @@ -389,104 +156,28 @@ int epicsShareAPI ca_sg_stat (const CA_SYNC_GID gid) /* * ca_sg_test */ -int epicsShareAPI ca_sg_test (const CA_SYNC_GID gid) +int epicsShareAPI ca_sg_test ( const CA_SYNC_GID gid ) { - CASG *pcasg; - cac *pcac; - int caStatus; + CASG *pcasg; + cac *pcac; + int caStatus; caStatus = fetchClientContext (&pcac); if ( caStatus != ECA_NORMAL ) { return caStatus; } - LOCK (pcac); - pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK (pcac); + pcasg = pcac->lookupCASG ( gid ); + if ( ! pcasg ) { return ECA_BADSYNCGRP; } - UNLOCK (pcac); - if(pcasg->opPendCount){ - return ECA_IOINPROGRESS; - } - else{ + if ( pcasg->ioComplete () ) { return ECA_IODONE; } -} - -/* - * ca_sync_grp_io_complete () - */ -extern "C" void ca_sync_grp_io_complete (struct event_handler_args args) -{ - unsigned long size; - CASGOP *pcasgop; - CASG *pcasg; - - pcasgop = (CASGOP *) args.usr; - - if (pcasgop->magic != CASG_MAGIC) { - errlogPrintf ("cac: sync group io_complete(): bad sync grp op magic number?\n"); - return; + else{ + return ECA_IOINPROGRESS; } - - LOCK (pcasgop->pcac); - ellDelete (&pcasgop->pcac->activeCASGOP, &pcasgop->node); - pcasgop->magic = 0; - - /* - * ignore stale replies - */ - pcasg = (CASG *) bucketLookupItemUnsignedId (pcasgop->pcac->ca_pSlowBucket, &pcasgop->id); - if (!pcasg || pcasg->seqNo != pcasgop->seqNo) { - UNLOCK (pcasgop->pcac); - return; - } - - assert (pcasg->magic == CASG_MAGIC); - assert (pcasg->id == pcasgop->id); - - if ( !( args.status & CA_M_SUCCESS ) ) { - ca_printf ( - pcasgop->pcac, - "CA Sync Group (id=%d) request failed because \"%s\"\n", - pcasgop->id, - ca_message(args.status) ); - UNLOCK (pcasgop->pcac); - freeListFree(pcasgop->pcac->ca_sgopFreeListPVT, pcasgop); - return; - } - - /* - * Update the user's variable - * (if its a get) - */ - if (pcasgop->pValue && args.dbr) { - size = dbr_size_n (args.type, args.count); - memcpy (pcasgop->pValue, args.dbr, size); - } - - /* - * decrement the outstanding IO ops count - */ - assert (pcasg->opPendCount>=1u); - pcasg->opPendCount--; - - UNLOCK (pcasgop->pcac); - - freeListFree (pcasgop->pcac->ca_sgopFreeListPVT, pcasgop); - - /* - * Wake up any tasks pending - * - * occurs through select on UNIX - */ - if (pcasg->opPendCount == 0) { - semBinaryGive(pcasg->sem); - } - return; } /* @@ -496,59 +187,24 @@ int epicsShareAPI ca_sg_array_put ( const CA_SYNC_GID gid, chtype type, unsigned long count, -chid chix, -const void *pvalue) +chid pChan, +const void *pValue) { - int status; - CASGOP *pcasgop; CASG *pcasg; - cac *pcac; + cac *pcac; int caStatus; - caStatus = fetchClientContext (&pcac); + caStatus = fetchClientContext ( &pcac ); if ( caStatus != ECA_NORMAL ) { return caStatus; } - /* - * first look on a free list. If not there - * allocate dynamic memory for it. - */ - pcasgop = (CASGOP *) freeListMalloc (pcac->ca_sgopFreeListPVT); - if(!pcasgop){ - return ECA_ALLOCMEM; - } - - LOCK (pcac); - pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK (pcac); - freeListFree(pcac->ca_sgopFreeListPVT, pcasgop); + pcasg = pcac->lookupCASG ( gid ); + if ( ! pcasg ) { return ECA_BADSYNCGRP; } - memset((char *)pcasgop, 0,sizeof(*pcasgop)); - pcasgop->id = gid; - pcasgop->seqNo = pcasg->seqNo; - pcasgop->magic = CASG_MAGIC; - pcasgop->pValue = NULL; /* handler will know its a put */ - pcasgop->pcac = pcac; - ellAdd (&pcac->activeCASGOP, &pcasgop->node); - pcasg->opPendCount++; - UNLOCK (pcac); - - status = ca_array_put_callback (type, count, chix, - pvalue, ca_sync_grp_io_complete, pcasgop); - if (status != ECA_NORMAL) { - LOCK (pcac); - assert (pcasg->opPendCount>=1u); - pcasg->opPendCount--; - ellDelete (&pcac->activeCASGOP, &pcasgop->node); - UNLOCK (pcac); - freeListFree (pcac->ca_sgopFreeListPVT, pcasgop); - } - - return status; + return pcasg->put ( pChan, type, count, pValue ); } /* @@ -558,13 +214,11 @@ int epicsShareAPI ca_sg_array_get ( const CA_SYNC_GID gid, chtype type, unsigned long count, -chid chix, -void *pvalue) +chid pChan, +void *pValue) { - int status; - CASGOP *pcasgop; CASG *pcasg; - cac *pcac; + cac *pcac; int caStatus; caStatus = fetchClientContext (&pcac); @@ -572,44 +226,10 @@ void *pvalue) return caStatus; } - /* - * first look on a free list. If not there - * allocate dynamic memory for it. - */ - pcasgop = (CASGOP *) freeListMalloc (pcac->ca_sgopFreeListPVT); - if (!pcasgop) { - return ECA_ALLOCMEM; - } - - LOCK (pcac); - pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK (pcac); - freeListFree(pcac->ca_sgopFreeListPVT, pcasgop); + pcasg = pcac->lookupCASG ( gid ); + if ( ! pcasg ) { return ECA_BADSYNCGRP; } - memset((char *)pcasgop, 0,sizeof(*pcasgop)); - pcasgop->id = gid; - pcasgop->seqNo = pcasg->seqNo; - pcasgop->magic = CASG_MAGIC; - pcasgop->pValue = pvalue; - pcasgop->pcac = pcac; - ellAdd(&pcac->activeCASGOP, &pcasgop->node); - pcasg->opPendCount++; - - UNLOCK (pcac); - - status = ca_array_get_callback ( - type, count, chix, ca_sync_grp_io_complete, pcasgop); - - if(status != ECA_NORMAL){ - LOCK (pcac); - assert (pcasg->opPendCount>=1u); - pcasg->opPendCount--; - ellDelete (&pcac->activeCASGOP, &pcasgop->node); - UNLOCK (pcac); - freeListFree (pcac->ca_sgopFreeListPVT, pcasgop); - } - return status; + return pcasg->get ( pChan, type, count, pValue ); } diff --git a/src/ca/tcpRecvWatchdog.cpp b/src/ca/tcpRecvWatchdog.cpp new file mode 100644 index 000000000..9283bd1df --- /dev/null +++ b/src/ca/tcpRecvWatchdog.cpp @@ -0,0 +1,72 @@ + +/* * $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +tcpRecvWatchdog::tcpRecvWatchdog + (double periodIn, osiTimerQueue & queueIn, bool echoProtocolAcceptedIn) : + osiTimer (periodIn, queueIn), + period (periodIn), + echoProtocolAccepted (echoProtocolAcceptedIn), + echoResponsePending (false) +{ +} + +void tcpRecvWatchdog::echoResponseNotify () +{ + this->echoResponsePending = false; + this->reschedule ( this->period ); +} + +void tcpRecvWatchdog::expire () +{ + /* + * remain backwards compatible with old servers + * ( this isnt an echo request ) + */ + if ( ! this->echoProtocolAccepted ) { + this->noopRequestMsg (); + } + else if ( this->echoResponsePending ) { + ca_printf ( "CA server unresponsive for %f sec. Disconnecting\n", this->period ); + this->shutdown (); + } + else { + this->echoRequestMsg (); + this->echoResponsePending = true; + } +} + +void tcpRecvWatchdog::destroy () +{ + // ignore timer destroy requests +} + +bool tcpRecvWatchdog::again () const +{ + return true; +} + +double tcpRecvWatchdog::delay () const +{ + if (this->echoResponsePending) { + return CA_ECHO_TIMEOUT; + } + else { + return this->period; + } +} + +const char *tcpRecvWatchdog::name () const +{ + return "TCP Receive Watchdog"; +} diff --git a/src/ca/tcpSendWatchdog.cpp b/src/ca/tcpSendWatchdog.cpp new file mode 100644 index 000000000..2783c8297 --- /dev/null +++ b/src/ca/tcpSendWatchdog.cpp @@ -0,0 +1,46 @@ + +/* * $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +tcpSendWatchdog::tcpSendWatchdog + (double periodIn, osiTimerQueue & queueIn) : + osiTimer (queueIn), + period (periodIn) +{ +} + +void tcpSendWatchdog::expire () +{ + ca_printf ("Unable to deliver message for %f sec. Disconnecting from CA server\n", this->period); + this->shutdown (); +} + +void tcpSendWatchdog::destroy () +{ + // ignore timer destroy requests +} + +bool tcpSendWatchdog::again () const +{ + return false; // a one shot +} + +double tcpSendWatchdog::delay () const +{ + return this->period; +} + +const char *tcpSendWatchdog::name () const +{ + return "TCP Send Watchdog"; +} diff --git a/src/ca/tcpiiu.cpp b/src/ca/tcpiiu.cpp new file mode 100644 index 000000000..a7926ae73 --- /dev/null +++ b/src/ca/tcpiiu.cpp @@ -0,0 +1,1474 @@ + + +/* * $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" +#include "inetAddrID_IL.h" +#include "bhe_IL.h" + +tsFreeList < class tcpiiu, 16 > tcpiiu::freeList; + +#ifdef DEBUG +# define debugPrintf(argsInParen) printf argsInParen +#else +# define debugPrintf(argsInParen) +#endif + +#ifdef CONVERSION_REQUIRED +extern CACVRTFUNC *cac_dbr_cvrt[]; +#endif /*CONVERSION_REQUIRED*/ + +typedef void (*pProtoStubTCP) (tcpiiu *piiu); + +const static char nullBuff[32] = { + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0 +}; + +/* + * constructTCPIIU () + * (lock must be applied by caller) + */ +tcpiiu * constructTCPIIU (cac *pcac, const struct sockaddr_in *pina, + unsigned minorVersion) +{ + bhe *pBHE; + tcpiiu *piiu; + + /* + * look for an existing virtual circuit + */ + pBHE = pcac->lookupBeaconInetAddr ( *pina ); + if ( ! pBHE ) { + pBHE = pcac->createBeaconHashEntry ( *pina, osiTime () ); + if ( ! pBHE ) { + return NULL; + } + } + + piiu = pBHE->getIIU (); + if ( piiu ) { + if ( piiu->state == iiu_connecting || + piiu->state == iiu_connected ) { + return piiu; + } + else { + return NULL; + } + } + + piiu = new tcpiiu (pcac, *pina, minorVersion, *pBHE); + if (!piiu) { + return NULL; + } + + if ( piiu->fullyConstructed () ) { + return piiu; + } + else { + delete piiu; + return NULL; + } +} + + +/* + * cac_connect_tcp () + */ +LOCAL void cac_connect_tcp (tcpiiu *piiu) +{ + int status; + + /* + * attempt to connect to a CA server + */ + while (1) { + int errnoCpy; + + status = connect ( piiu->sock, &piiu->dest.sa, + sizeof (piiu->dest.sa) ); + if (status == 0) { + break; + } + + errnoCpy = SOCKERRNO; + if (errnoCpy==SOCK_EISCONN) { + /* + * called connect after we are already connected + * (this appears to be how they provide + * connect completion notification) + */ + break; + } + else if ( errnoCpy==SOCK_EINPROGRESS || + errnoCpy==SOCK_EWOULDBLOCK /* for WINSOCK */ ) { + /* + * The socket is non-blocking and a + * connection attempt has been initiated, + * but not completed. + */ + return; + } + else if (errnoCpy==SOCK_EALREADY) { + return; + } +#ifdef _WIN32 + /* + * including this with vxWorks appears to + * cause trouble + */ + else if ( errnoCpy == SOCK_EINVAL ) { /* a SOCK_EALREADY alias used by early WINSOCK */ + return; + } +#endif + else if ( errnoCpy == SOCK_EINTR ) { + /* + * restart the system call if interrupted + */ + continue; + } + else { + ca_printf ("Unable to connect because %d=\"%s\"\n", + errnoCpy, SOCKERRSTR (errnoCpy) ); + piiu->shutdown (); + return; + } + } + + /* + * put the iiu into the connected state + */ + piiu->state = iiu_connected; + + piiu->tcpRecvWatchdog::reschedule (); // reset connection activity watchdog + + return; +} + +/* + * retryPendingClaims() + * + * This assumes that all channels with claims pending are at the + * front of the list (and that the channel is moved to the end of + * the list when a claim message has been sent for it) + * + * We send claim messages here until the outgoing message buffer + * will not accept any more messages + */ +LOCAL void retryPendingClaims (tcpiiu *piiu) +{ + bool success; + + LOCK (piiu->pcas); + tsDLIterBD chan ( piiu->chidList.first () ); + while ( chan != chan.eol () ) { + if (!chan->claimPending) { + piiu->claimRequestsPending = false; + piiu->flush (); + break; + } + // this moves the channel to the end of the list + success = chan->claimMsg (piiu); + if ( ! success ) { + piiu->flush (); + break; + } + chan = piiu->chidList.first (); + } + UNLOCK (piiu->pcas); +} + +/* + * cacSendThreadTCP () + */ +extern "C" void cacSendThreadTCP (void *pParam) +{ + tcpiiu *piiu = (tcpiiu *) pParam; + + while ( true ) { + unsigned sendCnt; + char *pOutBuf; + int status; + + pOutBuf = static_cast ( cacRingBufferReadReserveNoBlock (&piiu->send, &sendCnt) ); + while (!pOutBuf) { + piiu->tcpSendWatchdog::cancel (); + pOutBuf = (char *) cacRingBufferReadReserve (&piiu->send, &sendCnt); + if ( piiu->state != iiu_connected ) { + semBinaryGive ( piiu->sendThreadExitSignal ); + return; + } + } + + assert ( sendCnt <= INT_MAX ); + status = send ( piiu->sock, pOutBuf, (int) sendCnt, 0 ); + if ( status > 0 ) { + cacRingBufferReadCommit ( &piiu->send, (unsigned long) status ); + cacRingBufferReadFlush ( &piiu->send ); + if ( piiu->claimRequestsPending ) { + retryPendingClaims ( piiu ); + } + if ( piiu->echoRequestPending ) { + piiu->echoRequestMsg (); + } + } + else { + int localError = SOCKERRNO; + + cacRingBufferReadCommit (&piiu->send, 0); + + if ( status == 0 ) { + semBinaryGive (piiu->sendThreadExitSignal); + piiu->shutdown (); + return; + } + + if ( localError == SOCK_SHUTDOWN ) { + break; + } + + if ( localError != SOCK_EWOULDBLOCK && localError != SOCK_EINTR ) { + if ( localError != SOCK_EPIPE && localError != SOCK_ECONNRESET && + localError != SOCK_ETIMEDOUT) { + ca_printf ("CAC: unexpected TCP send error: %s\n", SOCKERRSTR (localError) ); + } + + semBinaryGive ( piiu->sendThreadExitSignal ); + piiu->shutdown (); + return; + } + } + } + + semBinaryGive ( piiu->sendThreadExitSignal ); +} + +/* + * tcpiiu::recvMsg () + */ +void tcpiiu::recvMsg () +{ + char *pProto; + unsigned writeSpace; + unsigned totalBytes; + int status; + + if ( this->state != iiu_connected ) { + return; + } + + pProto = (char *) cacRingBufferWriteReserve (&this->recv, &writeSpace); + + assert ( writeSpace <= INT_MAX ); + status = ::recv ( this->sock, pProto, (int) writeSpace, 0); + if ( status <= 0 ) { + int localErrno = SOCKERRNO; + + cacRingBufferWriteCommit (&this->recv, 0); + + if ( status == 0 ) { + this->shutdown (); + return; + } + + if ( localErrno == SOCK_SHUTDOWN ) { + return; + } + + if ( localErrno == SOCK_EWOULDBLOCK || localErrno == SOCK_EINTR ) { + return; + } + + ca_printf ( "Disconnecting from CA server because: %s\n", SOCKERRSTR (localErrno) ); + this->shutdown (); + return; + } + + assert ( ( (unsigned) status ) <= writeSpace ); + totalBytes = (unsigned) status; + + cacRingBufferWriteCommit (&this->recv, totalBytes); + cacRingBufferWriteFlush (&this->recv); + + this->tcpRecvWatchdog::reschedule (); // reschedule connection activity watchdog + + return; +} + + +/* + * cacRecvThreadTCP () + */ +extern "C" void cacRecvThreadTCP (void *pParam) +{ + tcpiiu *piiu = (tcpiiu *) pParam; + + cac_connect_tcp (piiu); + if ( piiu->state == iiu_connected ) { + unsigned priorityOfSelf = threadGetPrioritySelf (); + unsigned priorityOfSend; + threadBoolStatus tbs; + threadId tid; + + tbs = threadHighestPriorityLevelBelow (priorityOfSelf, &priorityOfSend); + if ( tbs != tbsSuccess ) { + priorityOfSend = priorityOfSelf; + ca_printf ( + "CAC warning: unable to get a lower priority for a TCP send thread\n"); + } + tid = threadCreate ("CAC TCP Send", priorityOfSend, + threadGetStackSize (threadStackMedium), cacSendThreadTCP, piiu); + if (tid) { + while (1) { + piiu->recvMsg (); + if ( piiu->state != iiu_connected ) { + break; + } + piiu->pcas->signalRecvActivityIIU (*piiu); + } + } + else { + piiu->shutdown (); + } + } + semBinaryGive (piiu->recvThreadExitSignal); +} + +// +// tcpiiu::tcpiiu () +// +tcpiiu::tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, class bhe &bheIn) : + tcpRecvWatchdog (pcac->ca_connectTMO, pcac->timerQueue, CA_V43 (CA_PROTOCOL_VERSION, minorVersion) ), + tcpSendWatchdog (pcac->ca_connectTMO, pcac->timerQueue), + netiiu (pcac), + bhe (bheIn) +{ + SOCKET sock; + int status; + int flag; + + sock = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if (sock == INVALID_SOCKET) { + ca_printf ("CAC: unable to create virtual circuit because \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + this->fc = false; + return; + } + + // + // apparently this is nolonger necessary with modern IP kernels + // +#if 0 + flag = TRUE; + status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&flag, sizeof(flag) ); + if (status < 0) { + ca_printf ("CAC: problems setting socket option TCP_NODELAY = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } +#endif + flag = TRUE; + status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, + (char *)&flag, sizeof (flag) ); + if (status < 0) { + ca_printf ("CAC: problems setting socket option SO_KEEPALIVE = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } + +#if 0 + { + int i; + + /* + * some concern that vxWorks will run out of mBuf's + * if this change is made joh 11-10-98 + */ + i = MAX_MSG_SIZE; + status = setsockopt ( piiu->sock, SOL_SOCKET, SO_SNDBUF, + (char *)&i, sizeof (i) ); + if (status < 0) { + ca_printf ("CAC: problems setting socket option SO_SNDBUF = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } + i = MAX_MSG_SIZE; + status = setsockopt (piiu->sock, SOL_SOCKET, SO_RCVBUF, + (char *)&i, sizeof (i) ); + if (status < 0) { + ca_printf ("CAC: problems setting socket option SO_RCVBUF = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } + } +#endif + + this->sock = sock; + this->minor_version_number = minorVersion; + this->dest.ia = ina; + this->contiguous_msg_count = 0u; + this->client_busy = false; + this->claimRequestsPending = false; + this->echoRequestPending = false; + this->sendPending = false; + this->pushPending = false; + this->beaconAnomaly = false; + this->curDataMax = 0ul; + this->curMsgBytes = 0ul; + this->curDataBytes = 0ul; + memset ( (void *) &this->curMsg, '\0', sizeof (this->curMsg) ); + this->pCurData = 0; + + status = cacRingBufferConstruct (&this->recv); + if (status) { + ca_printf ("CA: unable to create recv ring buffer\n"); + socket_close (sock); + this->fc = false; + return; + } + + status = cacRingBufferConstruct (&this->send); + if (status) { + ca_printf ("CA: unable to create send ring buffer\n"); + cacRingBufferDestroy (&this->recv); + socket_close (sock); + this->fc = false; + return; + } + + /* + * Save the Host name for efficient access in the + * future. + */ + ipAddrToA ( &this->dest.ia, this->host_name_str, sizeof (this->host_name_str) ); + + /* + * TCP starts out in the connecting state and later transitions + * to the connected state + */ + this->state = iiu_connecting; + + this->sendThreadExitSignal = semBinaryCreate (semEmpty); + if ( ! this->sendThreadExitSignal ) { + ca_printf ("CA: unable to create CA client send thread exit semaphore\n"); + cacRingBufferDestroy (&this->recv); + cacRingBufferDestroy (&this->send); + socket_close (sock); + this->fc = false; + return; + } + + this->recvThreadExitSignal = semBinaryCreate (semEmpty); + if ( ! this->recvThreadExitSignal ) { + ca_printf ("CA: unable to create CA client send thread exit semaphore\n"); + semBinaryDestroy (this->sendThreadExitSignal); + cacRingBufferDestroy (&this->recv); + cacRingBufferDestroy (&this->send); + socket_close (sock); + this->fc = false; + return; + } + + { + unsigned priorityOfSelf = threadGetPrioritySelf (); + unsigned priorityOfRecv; + threadId tid; + threadBoolStatus tbs; + + tbs = threadHighestPriorityLevelBelow (priorityOfSelf, &priorityOfRecv); + if ( tbs != tbsSuccess ) { + priorityOfRecv = priorityOfSelf; + ca_printf ("CAC warning: unable to get a lower priority for a TCP recv thread\n"); + } + + tid = threadCreate ("CAC TCP Recv", priorityOfRecv, + threadGetStackSize (threadStackMedium), cacRecvThreadTCP, this); + if (tid==0) { + ca_printf ("CA: unable to create CA client receive thread\n"); + semBinaryDestroy (this->recvThreadExitSignal); + semBinaryDestroy (this->sendThreadExitSignal); + cacRingBufferDestroy (&this->recv); + cacRingBufferDestroy (&this->send); + socket_close (sock); + this->fc = false; + return; + } + } + + bhe.bindToIIU (this); + pcac->installIIU (*this); + + this->fc = true; +} + +/* + * tcpiiu::shutdown () + */ +void tcpiiu::shutdown () +{ + LOCK (this->pcas); + if (this->state != iiu_disconnected) { + this->state = iiu_disconnected; + ::shutdown (this->sock, SD_BOTH); + cacRingBufferShutDown (&this->send); + cacRingBufferShutDown (&this->recv); + this->pcas->signalRecvActivityIIU (*this); + } + UNLOCK (this->pcas); +} + +// +// tcpiiu::~tcpiiu () +// +tcpiiu::~tcpiiu () +{ + unsigned chanDisconnectCount; + + if ( ! this->fc ) { + return; + } + + this->fc = false; + + this->shutdown (); + + LOCK (this->pcas); + + chanDisconnectCount = ellCount (&this->chidList); + if ( chanDisconnectCount ) { + genLocalExcep (this->pcas, ECA_DISCONN, this->host_name_str); + } + + tsDLIterBD iter ( this->chidList.first () ); + while ( iter != iter.eol () ) { + tsDLIterBD next = iter.itemAfter (); + iter->disconnect (); + iter = next; + } + + UNLOCK (this->pcas); + + // wait for send and recv threads to exit + semBinaryMustTake ( this->sendThreadExitSignal ); + semBinaryMustTake ( this->recvThreadExitSignal ); + semBinaryDestroy (this->sendThreadExitSignal); + semBinaryDestroy (this->recvThreadExitSignal); + + this->pcas->removeIIU ( *this ); + + this->pcas->removeBeaconInetAddr ( this->dest.ia ); + + if ( this->pcas->ca_fd_register_func ) { + (*this->pcas->ca_fd_register_func) + ((void *)this->pcas->ca_fd_register_arg, this->sock, FALSE); + } + socket_close (this->sock); + + cacRingBufferDestroy (&this->recv); + cacRingBufferDestroy (&this->send); + + /* + * free message body cache + */ + if (this->pCurData) { + free (this->pCurData); + } +} + +void tcpiiu::suicide () +{ + delete this; +} + +bool tcpiiu::compareIfTCP ( nciu &chan, const sockaddr_in &addr ) const +{ + if ( this->dest.ia.sin_addr.s_addr != addr.sin_addr.s_addr || + this->dest.ia.sin_port != addr.sin_port ) { + + char rej[64]; + + ipAddrToA ( &addr, rej, sizeof (rej) ); + LOCK ( this->pcas ); + sprintf ( this->pcas->ca_sprintf_buf, + "Channel: %s Accepted: %s Rejected: %s ", + chan.pName (), this->host_name_str, rej ); + genLocalExcep (this->pcas, ECA_DBLCHNL, this->pcas->ca_sprintf_buf); + UNLOCK ( this->pcas ); + } + return true; +} + +void tcpiiu::flush () +{ + if ( cacRingBufferWriteFlush ( &this->send ) ) { + this->tcpSendWatchdog::reschedule (); + } +} + +void tcpiiu::show ( unsigned level ) const +{ +} + +/* + * tcpiiu::noopRequestMsg () + */ +void tcpiiu::noopRequestMsg () +{ + caHdr hdr; + int status; + + hdr.m_cmmd = htons (CA_PROTO_NOOP); + hdr.m_dataType = htons (0); + hdr.m_count = htons (0); + hdr.m_cid = htons (0); + hdr.m_available = htons (0); + hdr.m_postsize = 0; + + status = this->pushStreamMsg (&hdr, NULL, true); + if ( status == ECA_NORMAL ) { + this->flush (); + } +} + +/* + * tcpiiu::echoRequestMsg () + */ +void tcpiiu::echoRequestMsg () +{ + caHdr hdr; + + hdr.m_cmmd = htons (CA_PROTO_ECHO); + hdr.m_dataType = htons (0); + hdr.m_count = htons (0); + hdr.m_cid = htons (0); + hdr.m_available = htons (0); + hdr.m_postsize = 0u; + + /* + * If we are out of buffer space then postpone this + * operation until later. This avoids any possibility + * of a push pull deadlock (since this can be sent when + * parsing the UDP input buffer). + */ + if ( this->pushStreamMsg (&hdr, NULL, false) == ECA_NORMAL ) { + this->flush (); + this->echoRequestPending = false; + } + else { + this->echoRequestPending = true; + } +} + +/* + * tcpiiu::busyRequestMsg () + */ +int tcpiiu::busyRequestMsg () +{ + caHdr hdr; + int status; + + hdr = cacnullmsg; + hdr.m_cmmd = htons ( CA_PROTO_EVENTS_OFF ); + + status = this->pushStreamMsg ( &hdr, NULL, true ); + if ( status == ECA_NORMAL ) { + this->flush (); + } + return status; +} + +/* + * tcpiiu::readyRequestMsg () + */ +int tcpiiu::readyRequestMsg () +{ + caHdr hdr; + int status; + + hdr = cacnullmsg; + hdr.m_cmmd = htons (CA_PROTO_EVENTS_ON); + + status = this->pushStreamMsg (&hdr, NULL, true); + if ( status == ECA_NORMAL ) { + this->flush (); + } + return status; +} + +/* + * tcpiiu::hostNameSetMsg () + */ +void tcpiiu::hostNameSetMsg () +{ + unsigned size; + caHdr hdr; + char *pName; + + if ( ! CA_V41(CA_PROTOCOL_VERSION, this->minor_version_number) ) { + return; + } + + /* + * allocate space in the outgoing buffer + */ + pName = this->pcas->ca_pHostName, + size = strlen (pName) + 1; + hdr = cacnullmsg; + hdr.m_cmmd = htons (CA_PROTO_HOST_NAME); + hdr.m_postsize = size; + + this->pushStreamMsg (&hdr, pName, true); + + return; +} + +/* + * tcpiiu::userNameSetMsg () + */ +void tcpiiu::userNameSetMsg () +{ + unsigned size; + caHdr hdr; + char *pName; + + if ( ! CA_V41(CA_PROTOCOL_VERSION, this->minor_version_number) ) { + return; + } + + /* + * allocate space in the outgoing buffer + */ + pName = this->pcas->ca_pUserName, + size = strlen (pName) + 1; + hdr = cacnullmsg; + hdr.m_cmmd = htons ( CA_PROTO_CLIENT_NAME ); + hdr.m_postsize = size; + + this->pushStreamMsg ( &hdr, pName, true ); + + return; +} + + +/* + * tcp_noop_action () + */ +LOCAL void tcp_noop_action (tcpiiu * /* piiu */) +{ + return; +} + +/* + * echo_resp_action () + */ +LOCAL void echo_resp_action (tcpiiu *piiu) +{ + piiu->echoResponseNotify (); + piiu->beaconAnomaly = false; + return; +} + +/* + * write_notify_resp_action () + */ +LOCAL void write_notify_resp_action (tcpiiu *piiu) +{ + baseNMIU *monix; + + LOCK (piiu->pcas); + monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); + if (monix) { + int status = ntohl (piiu->curMsg.m_cid); + if ( status == ECA_NORMAL ) { + monix->completionNotify (); + } + else { + monix->exceptionNotify ( status, "write notify request rejected" ); + } + monix->destroy (); + } + UNLOCK (piiu->pcas); +} + +/* + * read_notify_resp_action () + */ +LOCAL void read_notify_resp_action ( tcpiiu *piiu ) +{ + baseNMIU *monix; + + LOCK (piiu->pcas); + monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); + + if (monix) { + int v41; + int status; + + /* + * convert the data buffer from net + * format to host format + */ +# ifdef CONVERSION_REQUIRED + if (piiu->curMsg.m_dataTypecurMsg.m_dataType])( + piiu->pCurData, + piiu->pCurData, + FALSE, + piiu->curMsg.m_count); + } + else { + piiu->curMsg.m_cid = htonl(ECA_BADTYPE); + } +# endif + + /* + * the channel id field is abused for + * read notify status starting + * with CA V4.1 + */ + v41 = CA_V41 (CA_PROTOCOL_VERSION, piiu->minor_version_number); + if (v41) { + status = ntohl (piiu->curMsg.m_cid); + } + else{ + status = ECA_NORMAL; + } + + if ( status == ECA_NORMAL ) { + monix->completionNotify (piiu->curMsg.m_dataType, piiu->curMsg.m_count, piiu->pCurData); + } + else { + monix->exceptionNotify (status, "read failed", piiu->curMsg.m_dataType, piiu->curMsg.m_count); + } + monix->destroy (); + } + + UNLOCK (piiu->pcas); + + return; +} + +/* + * event_resp_action () + */ +LOCAL void event_resp_action (tcpiiu *piiu) +{ + baseNMIU *monix; + + /* + * run the user's event handler + */ + LOCK (piiu->pcas); + monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); + if ( monix ) { + int v41; + int status; + + /* + * m_postsize = 0 used to be a confirmation, but is + * now a noop because the above hash lookup will + * not find a matching IO block + */ + if ( ! piiu->curMsg.m_postsize ) { + monix->destroy (); + UNLOCK (piiu->pcas); + return; + } + + /* + * convert the data buffer from net + * format to host format + */ +# ifdef CONVERSION_REQUIRED + if (piiu->curMsg.m_dataTypecurMsg.m_dataType])( + piiu->pCurData, + piiu->pCurData, + FALSE, + piiu->curMsg.m_count); + } + else { + piiu->curMsg.m_cid = htonl(ECA_BADTYPE); + } +# endif + + /* + * the channel id field is abused for + * read notify status starting + * with CA V4.1 + */ + v41 = CA_V41 (CA_PROTOCOL_VERSION, piiu->minor_version_number); + if (v41) { + status = ntohl (piiu->curMsg.m_cid); + } + else { + status = ECA_NORMAL; + } + if ( status == ECA_NORMAL ) { + monix->completionNotify ( piiu->curMsg.m_dataType, + piiu->curMsg.m_count, piiu->pCurData ); + } + else { + monix->exceptionNotify ( status, "subscription update failed", + piiu->curMsg.m_dataType, piiu->curMsg.m_count ); + } + } + + UNLOCK (piiu->pcas); + + return; +} + +/* + * read_resp_action () + */ +LOCAL void read_resp_action (tcpiiu *piiu) +{ + baseNMIU *pIOBlock; + + LOCK (piiu->pcas); + + /* + * verify the event id + */ + pIOBlock = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); + if ( pIOBlock ) { + + /* + * convert the data buffer from net + * format to host format + */ + pIOBlock->completionNotify (piiu->curMsg.m_dataType, piiu->curMsg.m_count, piiu->pCurData); + pIOBlock->destroy (); + } + + UNLOCK (piiu->pcas); + + return; +} + +/* + * clear_channel_resp_action () + */ +LOCAL void clear_channel_resp_action (tcpiiu * /* piiu */) +{ + /* presently a noop */ + return; +} + +/* + * exception_resp_action () + */ +LOCAL void exception_resp_action (tcpiiu *piiu) +{ + baseNMIU *monix; + char context[255]; + caHdr *req = (caHdr *) piiu->pCurData; + + if ( piiu->curMsg.m_postsize > sizeof (caHdr) ){ + sprintf (context, "detected by: %s for: %s", + piiu->host_name_str, (char *)(req+1)); + } + else{ + sprintf (context, "detected by: %s", piiu->host_name_str); + } + + LOCK (piiu->pcas); + switch ( ntohs (req->m_cmmd) ) { + case CA_PROTO_READ_NOTIFY: + monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); + if (monix) { + monix->exceptionNotify ( ntohl (piiu->curMsg.m_available), context, + ntohs (req->m_dataType), ntohs (req->m_count) ); + monix->destroy (); + } + else { + piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), + context, __FILE__, __LINE__); + } + break; + case CA_PROTO_READ: + monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); + if (monix) { + monix->exceptionNotify ( ntohl (piiu->curMsg.m_available), context, + ntohs (req->m_dataType), ntohs (req->m_count) ); + monix->destroy (); + } + else { + piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), + context, __FILE__, __LINE__); + } + break; + case CA_PROTO_WRITE_NOTIFY: + monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); + if (monix) { + monix->exceptionNotify (ntohl (piiu->curMsg.m_available), context); + monix->destroy (); + } + else { + piiu->pcas->exceptionNotify (ntohl ( piiu->curMsg.m_available), + context, __FILE__, __LINE__); + } + break; + case CA_PROTO_WRITE: + piiu->pcas->exceptionNotify (ntohl ( piiu->curMsg.m_available), + context, ntohs (req->m_dataType), ntohs (req->m_count), __FILE__, __LINE__); + break; + case CA_PROTO_EVENT_ADD: + monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); + if (monix) { + monix->exceptionNotify (ntohl (piiu->curMsg.m_available), context); + monix->destroy (); + } + else { + piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), + context, __FILE__, __LINE__); + } + break; + case CA_PROTO_EVENT_CANCEL: + monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); + if (monix) { + monix->exceptionNotify (ntohl (piiu->curMsg.m_available), context); + monix->destroy (); + } + else { + piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), + context, __FILE__, __LINE__); + } + break; + default: + piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), + context, __FILE__, __LINE__); + break; + } + + UNLOCK (piiu->pcas); + + return; +} + +/* + * access_rights_resp_action () + */ +LOCAL void access_rights_resp_action (tcpiiu *piiu) +{ + int ar; + nciu *chan; + + LOCK (piiu->pcas); + chan = piiu->pcas->lookupChan (piiu->curMsg.m_cid); + if ( ! chan ) { + /* + * end up here if they delete the channel + * prior to connecting + */ + UNLOCK (piiu->pcas); + return; + } + + ar = ntohl (piiu->curMsg.m_available); + chan->ar.read_access = (ar&CA_PROTO_ACCESS_RIGHT_READ)?1:0; + chan->ar.write_access = (ar&CA_PROTO_ACCESS_RIGHT_WRITE)?1:0; + + chan->accessRightsNotify (chan->ar); + + UNLOCK (piiu->pcas); + return; +} + +/* + * claim_ciu_resp_action () + */ +LOCAL void claim_ciu_resp_action (tcpiiu *piiu) +{ + unsigned sid; + nciu *chan; + + LOCK (piiu->pcas); + + chan = piiu->pcas->lookupChan (piiu->curMsg.m_cid); + if ( ! chan ) { + UNLOCK (piiu->pcas); + return; + } + + if ( CA_V44 (CA_PROTOCOL_VERSION, piiu->minor_version_number) ) { + sid = piiu->curMsg.m_available; + } + else { + sid = chan->sid; + } + + chan->connect (*piiu, piiu->curMsg.m_dataType, piiu->curMsg.m_count, sid); + + UNLOCK (piiu->pcas); + + return; +} + +/* + * verifyAndDisconnectChan () + */ +LOCAL void verifyAndDisconnectChan (tcpiiu *piiu) +{ + nciu *chan; + + LOCK (piiu->pcas); + chan = piiu->pcas->lookupChan (piiu->curMsg.m_cid); + if (!chan) { + /* + * end up here if they delete the channel + * prior to this response + */ + UNLOCK (piiu->pcas); + return; + } + + /* + * need to move the channel back to the cast niiu + * (so we will be able to reconnect) + * + * this marks the niiu for disconnect if the channel + * count goes to zero + */ + chan->disconnect (); + UNLOCK (piiu->pcas); + return; +} + +/* + * bad_tcp_resp_action () + */ +LOCAL void bad_tcp_resp_action (tcpiiu *piiu) +{ + ca_printf ("CAC: Bad response code in TCP message from %s was %u\n", + piiu->host_name_str, piiu->curMsg.m_cmmd); +} + +/* + * TCP protocol jump table + */ +LOCAL const pProtoStubTCP tcpJumpTableCAC[] = +{ + tcp_noop_action, + event_resp_action, + bad_tcp_resp_action, + read_resp_action, + bad_tcp_resp_action, + bad_tcp_resp_action, + bad_tcp_resp_action, + bad_tcp_resp_action, + bad_tcp_resp_action, + bad_tcp_resp_action, + bad_tcp_resp_action, + exception_resp_action, + clear_channel_resp_action, + bad_tcp_resp_action, + bad_tcp_resp_action, + read_notify_resp_action, + bad_tcp_resp_action, + bad_tcp_resp_action, + claim_ciu_resp_action, + write_notify_resp_action, + bad_tcp_resp_action, + bad_tcp_resp_action, + access_rights_resp_action, + echo_resp_action, + bad_tcp_resp_action, + bad_tcp_resp_action, + verifyAndDisconnectChan, + verifyAndDisconnectChan +}; + + +/* + * post_tcp_msg () + * + * LOCK should be applied when calling this routine + * + */ +int tcpiiu::post_msg (char *pInBuf, unsigned long blockSize) +{ + unsigned long size; + + while (blockSize) { + + /* + * fetch a complete message header + */ + if ( this->curMsgBytes < sizeof (this->curMsg) ) { + char *pHdr; + + size = sizeof (this->curMsg) - this->curMsgBytes; + size = min (size, blockSize); + + pHdr = (char *) &this->curMsg; + memcpy( pHdr + this->curMsgBytes, pInBuf, size); + + this->curMsgBytes += size; + if (this->curMsgBytes < sizeof(this->curMsg)) { +#if 0 + printf ("waiting for %d msg hdr bytes\n", + sizeof(this->curMsg) - this->curMsgBytes); +#endif + return ECA_NORMAL; + } + + pInBuf += size; + blockSize -= size; + + /* + * fix endian of bytes + */ + this->curMsg.m_postsize = + ntohs (this->curMsg.m_postsize); + this->curMsg.m_cmmd = ntohs (this->curMsg.m_cmmd); + this->curMsg.m_dataType = ntohs (this->curMsg.m_dataType); + this->curMsg.m_count = ntohs (this->curMsg.m_count); +#if 0 + ca_printf ( + "%s Cmd=%3d Type=%3d Count=%4d Size=%4d", + this->host_name_str, + this->curMsg.m_cmmd, + this->curMsg.m_dataType, + this->curMsg.m_count, + this->curMsg.m_postsize); + ca_printf ( + " Avail=%8x Cid=%6d\n", + this->curMsg.m_available, + this->curMsg.m_cid); +#endif + } + + /* + * dont allow huge msg body until + * the server supports it + */ + if ( this->curMsg.m_postsize > (unsigned) MAX_TCP ) { + this->curMsgBytes = 0; + this->curDataBytes = 0; + return ECA_TOLARGE; + } + + /* + * make sure we have a large enough message body cache + */ + if ( this->curMsg.m_postsize > this->curDataMax ) { + void *pCurData; + size_t cacheSize; + + /* + * scalar DBR_STRING is sometimes clipped to the + * actual string size so make sure this cache is + * as large as one DBR_STRING so they will + * not page fault if they read MAX_STRING_SIZE + * bytes (instead of the actual string size). + */ + cacheSize = max ( this->curMsg.m_postsize, MAX_STRING_SIZE ); + pCurData = (void *) calloc (1u, cacheSize); + if (!pCurData) { + return ECA_ALLOCMEM; + } + if (this->pCurData) { + free (this->pCurData); + } + this->pCurData = pCurData; + this->curDataMax = this->curMsg.m_postsize; + } + + /* + * Fetch a complete message body + * (allows for arrays larger than than the + * ring buffer size) + */ + if (this->curMsg.m_postsize > this->curDataBytes ) { + char *pBdy; + + size = this->curMsg.m_postsize - this->curDataBytes; + size = min (size, blockSize); + pBdy = (char *) this->pCurData; + memcpy ( pBdy + this->curDataBytes, pInBuf, size); + this->curDataBytes += size; + if (this->curDataBytes < this->curMsg.m_postsize) { +#if 0 + printf ("waiting for %d msg bdy bytes\n", + this->curMsg.m_postsize - this->curDataBytes); +#endif + return ECA_NORMAL; + } + pInBuf += size; + blockSize -= size; + } + + /* + * execute the response message + */ + pProtoStubTCP pStub; + if ( this->curMsg.m_cmmd >= NELEMENTS (tcpJumpTableCAC) ) { + pStub = bad_tcp_resp_action; + } + else { + pStub = tcpJumpTableCAC [this->curMsg.m_cmmd]; + } + (*pStub) (this); + + this->curMsgBytes = 0; + this->curDataBytes = 0; + } + return ECA_NORMAL; +} + +void tcpiiu::hostName ( char *pBuf, unsigned bufLength ) const +{ + if ( bufLength ) { + strncpy ( pBuf, this->host_name_str, bufLength ); + pBuf[bufLength - 1u] = '\0'; + } +} + +bool tcpiiu::ca_v42_ok () const +{ + return CA_V42 (CA_PROTOCOL_VERSION, + this->minor_version_number); +} + +bool tcpiiu::ca_v41_ok () const +{ + return CA_V41 (CA_PROTOCOL_VERSION, + this->minor_version_number); +} + +/* + * tcpiiu::pushStreamMsg () + */ +int tcpiiu::pushStreamMsg (const caHdr *pmsg, + const void *pext, bool BlockingOk) +{ + caHdr msg; + ca_uint16_t actualextsize; + ca_uint16_t extsize; + unsigned msgsize; + unsigned bytesSent; + + if ( pext == NULL ) { + extsize = actualextsize = 0; + } + else { + if ( pmsg->m_postsize > 0xffff-7 ) { + return ECA_TOLARGE; + } + actualextsize = pmsg->m_postsize; + extsize = CA_MESSAGE_ALIGN (actualextsize); + } + + msg = *pmsg; + msg.m_postsize = htons (extsize); + msgsize = extsize + sizeof (msg); + + if ( ! cacRingBufferWriteLockNoBlock ( &this->send, msgsize ) ) { + if ( BlockingOk ) { + this->tcpSendWatchdog::reschedule (); + cacRingBufferWriteLock ( &this->send ); + } + else { + return ECA_OPWILLBLOCK; + } + } + + /* + * push the header onto the ring + */ + bytesSent = cacRingBufferWrite ( &this->send, &msg, sizeof (msg) ); + if ( bytesSent != sizeof (msg) ) { + cacRingBufferWriteUnlock ( &this->send ); + return ECA_DISCONNCHID; + } + + /* + * push message body onto the ring + * + * (optionally encode in network format as we send) + */ + if ( extsize > 0u ) { + bytesSent = cacRingBufferWrite ( &this->send, pext, actualextsize ); + if ( bytesSent != actualextsize ) { + cacRingBufferWriteUnlock ( &this->send ); + return ECA_DISCONNCHID; + } + /* + * force pad bytes at the end of the message to nill + * if present (this avoids messages from purify) + */ + { + unsigned long n; + + n = extsize-actualextsize; + if (n) { + assert ( n <= sizeof (nullBuff) ); + bytesSent = cacRingBufferWrite ( &this->send, nullBuff, n ); + if ( bytesSent != n ) { + cacRingBufferWriteUnlock ( &this->send ); + return ECA_DISCONNCHID; + } + } + } + } + + cacRingBufferWriteUnlock ( &this->send ); + + return ECA_NORMAL; +} + +int tcpiiu::pushDatagramMsg (const caHdr * /* pMsg */, + const void * /* pExt */, ca_uint16_t /* extsize */) +{ + return ECA_INTERNAL; +} + +/* + * add to the beginning of the list until we + * have sent the claim message (after which we + * move it to the end of the list) + */ +void tcpiiu::addToChanList (nciu *chan) +{ + LOCK (this->pcas); + chan->claimPending = TRUE; + this->chidList.push (*chan); + chan->piiu = this; + UNLOCK (this->pcas); +} + +void tcpiiu::removeFromChanList (nciu *chan) +{ + LOCK (this->pcas); + this->chidList.remove (*chan); + chan->piiu = NULL; + UNLOCK (this->pcas); + + if ( this->chidList.count () == 0 ) { + this->shutdown (); + } +} + +void tcpiiu::disconnect (nciu *chan) +{ + LOCK (this->pcas); + this->removeFromChanList (chan); + /* + * try to reconnect + */ + assert (this->pcas->pudpiiu); + this->pcas->pudpiiu->addToChanList (chan); + this->pcas->pudpiiu->searchTmr.reset (0.0); + UNLOCK (this->pcas); +} + diff --git a/src/ca/udpiiu.cpp b/src/ca/udpiiu.cpp new file mode 100644 index 000000000..9ef9f578b --- /dev/null +++ b/src/ca/udpiiu.cpp @@ -0,0 +1,1022 @@ + +/* $Id$ + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "osiProcess.h" + +#include "iocinf.h" +#include "addrList.h" +#include "inetAddrID_IL.h" + +typedef void (*pProtoStubUDP) (udpiiu *piiu, caHdr *pMsg, const struct sockaddr_in *pnet_addr); + +/* + * cac_udp_recv_msg () + */ +LOCAL int cac_udp_recv_msg (udpiiu *piiu) +{ + osiSockAddr src; + int src_size = sizeof (src); + int status; + + status = recvfrom (piiu->sock, piiu->recvBuf, sizeof (piiu->recvBuf), 0, + &src.sa, &src_size); + if (status < 0) { + int errnoCpy = SOCKERRNO; + + if (errnoCpy == SOCK_SHUTDOWN) { + return -1; + } + if (errnoCpy == SOCK_EWOULDBLOCK || errnoCpy == SOCK_EINTR) { + return 0; + } +# ifdef linux + /* + * Avoid spurious ECONNREFUSED bug + * in linux + */ + if (errnoCpy==SOCK_ECONNREFUSED) { + return 0; + } +# endif + ca_printf ( + "Unexpected UDP recv error %s\n", SOCKERRSTR(errnoCpy)); + } + else if (status > 0) { + status = piiu->post_msg ( &src.ia, + piiu->recvBuf, (unsigned long) status ); + if ( status != ECA_NORMAL ) { + char buf[64]; + + ipAddrToA (&src.ia, buf, sizeof(buf)); + + ca_printf ( + "%s: bad UDP msg from %s because \"%s\"\n", __FILE__, + buf, ca_message (status) ); + + return 0; + } + } + + return 0; +} + +/* + * cacRecvThreadUDP () + */ +extern "C" void cacRecvThreadUDP (void *pParam) +{ + udpiiu *piiu = (udpiiu *) pParam; + int status; + + do { + status = cac_udp_recv_msg (piiu); + } while ( status == 0 ); + + semBinaryGive (piiu->recvThreadExitSignal); +} + +/* + * NOTIFY_CA_REPEATER() + * + * tell the cast repeater that another client needs fan out + * + * NOTES: + * 1) local communication only (no LAN traffic) + * + */ +void notify_ca_repeater (udpiiu *piiu) +{ + caHdr msg; + osiSockAddr saddr; + int status; + static int once = FALSE; + int len; + + if (piiu->repeaterContacted) { + return; + } + + if (piiu->repeaterTries > N_REPEATER_TRIES_PRIOR_TO_MSG ) { + if (!once) { + ca_printf ( + "Unable to contact CA repeater after %d tries\n", + N_REPEATER_TRIES_PRIOR_TO_MSG); + ca_printf ( + "Silence this message by starting a CA repeater daemon\n"); + once = TRUE; + } + } + + /* + * In 3.13 beta 11 and before the CA repeater calls local_addr() + * to determine a local address and does not allow registration + * messages originating from other addresses. In these + * releases local_addr() returned the address of the first enabled + * interface found, and this address may or may not have been the loop + * back address. Starting with 3.13 beta 12 local_addr() was + * changed to always return the address of the first enabled + * non-loopback interface because a valid non-loopback local + * address is required in the beacon messages. Therefore, to + * guarantee compatibility with past versions of the repeater + * we alternate between the address returned by local_addr() + * and the loopback address here. + * + * CA repeaters in R3.13 beta 12 and higher allow + * either the loopback address or the address returned + * by local address (the first non-loopback address found) + */ + if (piiu->repeaterTries&1) { + saddr = osiLocalAddr (piiu->sock); + if (saddr.sa.sa_family != AF_INET) { + /* + * use the loop back address to communicate with the CA repeater + * if this os does not have interface query capabilities + * + * this will only work with 3.13 beta 12 CA repeaters or later + */ + saddr.ia.sin_family = AF_INET; + saddr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + saddr.ia.sin_port = htons (piiu->repeaterPort); + } + } + else { + saddr.ia.sin_family = AF_INET; + saddr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + saddr.ia.sin_port = htons (piiu->repeaterPort); + } + + memset ((char *)&msg, 0, sizeof(msg)); + msg.m_cmmd = htons (REPEATER_REGISTER); + msg.m_available = saddr.ia.sin_addr.s_addr; + + /* + * Intentionally sending a zero length message here + * until most CA repeater daemons have been restarted + * (and only then will accept the above protocol) + * (repeaters began accepting this protocol + * starting with EPICS 3.12) + * + * SOLARIS will not accept a zero length message + * and we are just porting there for 3.12 so + * we will use the new protocol for 3.12 + * + * recent versions of UCX will not accept a zero + * length message and we will assume that folks + * using newer versions of UCX have rebooted (and + * therefore restarted the CA repeater - and therefore + * moved it to an EPICS release that accepts this protocol) + */ +# if defined (DOES_NOT_ACCEPT_ZERO_LENGTH_UDP) + len = sizeof (msg); +# else + len = 0; +# endif + + status = sendto (piiu->sock, (char *)&msg, len, + 0, (struct sockaddr *)&saddr, sizeof(saddr)); + if (status < 0) { + int errnoCpy = SOCKERRNO; + if( errnoCpy != SOCK_EINTR && + errnoCpy != SOCK_EWOULDBLOCK && + /* + * This is returned from Linux when + * the repeater isnt running + */ + errnoCpy != SOCK_ECONNREFUSED + ) { + ca_printf ( + "CAC: error sending to repeater was \"%s\"\n", + SOCKERRSTR(errnoCpy)); + } + } + piiu->repeaterTries++; + piiu->contactRepeater = 0u; +} + +/* + * cacSendThreadUDP () + */ +extern "C" void cacSendThreadUDP (void *pParam) +{ + udpiiu *piiu = (udpiiu *) pParam; + + while ( ! piiu->sendThreadExitCmd ) { + int status; + + if (piiu->contactRepeater) { + notify_ca_repeater (piiu); + } + + semMutexMustTake (piiu->xmitBufLock); + + if (piiu->nBytesInXmitBuf > 0) { + osiSockAddrNode *pNode; + + pNode = (osiSockAddrNode *) ellFirst (&piiu->dest); + while (pNode) { + + assert ( piiu->nBytesInXmitBuf <= INT_MAX ); + status = sendto (piiu->sock, piiu->xmitBuf, + (int) piiu->nBytesInXmitBuf, 0, + &pNode->addr.sa, sizeof(pNode->addr.sa)); + if (status <= 0) { + int localErrno = SOCKERRNO; + + if (status==0) { + break; + } + + if (localErrno == SOCK_SHUTDOWN) { + break; + } + else if ( localErrno == SOCK_EINTR ) { + status = 1; + } + else { + char buf[64]; + + ipAddrToA (&pNode->addr.ia, buf, sizeof (buf)); + + ca_printf ( + "CAC: error = \"%s\" sending UDP msg to %s\n", + SOCKERRSTR(localErrno), buf); + + break; + } + } + pNode = (osiSockAddrNode *) ellNext (&pNode->node); + } + + piiu->nBytesInXmitBuf = 0u; + + if (status<=0) { + break; + } + } + + semMutexGive (piiu->xmitBufLock); + + semBinaryMustTake (piiu->xmitSignal); + } + + semBinaryGive (piiu->sendThreadExitSignal); +} + +/* + * repeater_installed () + * + * Test for the repeater already installed + * + * NOTE: potential race condition here can result + * in two copies of the repeater being spawned + * however the repeater detects this, prints a message, + * and lets the other task start the repeater. + * + * QUESTION: is there a better way to test for a port in use? + * ANSWER: none that I can find. + * + * Problems with checking for the repeater installed + * by attempting to bind a socket to its address + * and port. + * + * 1) Closed socket may not release the bound port + * before the repeater wakes up and tries to grab it. + * Attempting to bind the open socket to another port + * also does not work. + * + * 072392 - problem solved by using SO_REUSEADDR + */ +int repeater_installed (udpiiu *piiu) +{ + int installed = FALSE; + int status; + SOCKET sock; + struct sockaddr_in bd; + int flag; + + sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == INVALID_SOCKET) { + return installed; + } + + memset ( (char *) &bd, 0, sizeof (bd) ); + bd.sin_family = AF_INET; + bd.sin_addr.s_addr = htonl (INADDR_ANY); + bd.sin_port = htons (piiu->repeaterPort); + status = bind (sock, (struct sockaddr *) &bd, sizeof(bd) ); + if (status<0) { + if (SOCKERRNO == SOCK_EADDRINUSE) { + installed = TRUE; + } + } + + /* + * turn on reuse only after the test so that + * this works on kernels that support multicast + */ + flag = TRUE; + status = setsockopt ( sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&flag, sizeof (flag) ); + if (status<0) { + ca_printf ( "CAC: set socket option reuseaddr set failed\n"); + } + + socket_close (sock); + + return installed; +} + +// +// udpiiu::udpiiu () +// +udpiiu::udpiiu (cac *pcac) : + netiiu (pcac), + searchTmr (*this, pcac->timerQueue), + repeaterSubscribeTmr (*this, pcac->timerQueue), + sendThreadExitCmd (false) +{ + static const unsigned short PORT_ANY = 0u; + osiSockAddr addr; + int boolValue = TRUE; + int status; + + this->repeaterPort = + envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT, CA_REPEATER_PORT); + + this->sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (this->sock == INVALID_SOCKET) { + ca_printf ("CAC: unable to create datagram socket because = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + throwWithLocation ( noSocket () ); + } + + status = setsockopt ( this->sock, SOL_SOCKET, SO_BROADCAST, + (char *) &boolValue, sizeof (boolValue) ); + if (status<0) { + ca_printf ("CAC: unable to enable IP broadcasting because = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } + +#if 0 + { + /* + * some concern that vxWorks will run out of mBuf's + * if this change is made joh 11-10-98 + * + * bump up the UDP recv buffer + */ + int size = 1u<<15u; + status = setsockopt ( this->sock, SOL_SOCKET, SO_RCVBUF, + (char *)&size, sizeof (size) ); + if (status<0) { + ca_printf ("CAC: unable to set socket option SO_RCVBUF because \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } + } +#endif + + /* + * force a bind to an unconstrained address because we may end + * up receiving first + */ + memset ( (char *)&addr, 0 , sizeof (addr) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl (INADDR_ANY); + addr.ia.sin_port = htons (PORT_ANY); + status = bind (this->sock, &addr.sa, sizeof (addr) ); + if (status<0) { + socket_close (this->sock); + ca_printf ("CAC: unable to bind to an unconstrained address because = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + throwWithLocation ( noSocket () ); + } + + this->nBytesInXmitBuf = 0u; + this->contactRepeater = 0u; + this->repeaterContacted = 0u; + this->repeaterTries = 0u; + + this->xmitBufLock = semMutexCreate (); + if (!this->xmitBufLock) { + socket_close (this->sock); + throwWithLocation ( noMemory () ); + } + + this->recvThreadExitSignal = semBinaryCreate (semEmpty); + if ( ! this->recvThreadExitSignal ) { + semMutexDestroy (this->xmitBufLock); + socket_close (this->sock); + throwWithLocation ( noMemory () ); + } + + this->sendThreadExitSignal = semBinaryCreate (semEmpty); + if ( ! this->sendThreadExitSignal ) { + semBinaryDestroy (this->recvThreadExitSignal); + semMutexDestroy (this->xmitBufLock); + socket_close (this->sock); + throwWithLocation ( noMemory () ); + } + + this->xmitSignal = semBinaryCreate (semEmpty); + if ( ! this->xmitSignal ) { + ca_printf ("CA: unable to create xmit signal\n"); + semBinaryDestroy (this->recvThreadExitSignal); + semBinaryDestroy (this->sendThreadExitSignal); + semMutexDestroy (this->xmitBufLock); + socket_close (this->sock); + throwWithLocation ( noMemory () ); + } + + /* + * load user and auto configured + * broadcast address list + */ + ellInit ( &this->dest ); + configureChannelAccessAddressList (&this->dest, this->sock, pcac->ca_server_port); + if ( ellCount ( &this->dest ) == 0 ) { + genLocalExcep ( NULL, ECA_NOSEARCHADDR, NULL ); + } + + { + unsigned priorityOfSelf = threadGetPrioritySelf (); + unsigned priorityOfRecv; + threadId tid; + threadBoolStatus tbs; + + tbs = threadLowestPriorityLevelAbove (priorityOfSelf, &priorityOfRecv); + if ( tbs != tbsSuccess ) { + priorityOfRecv = priorityOfSelf; + ca_printf ("CAC warning: unable to get a higher priority for a UDP recv thread\n"); + } + + tid = threadCreate ("CAC UDP Recv", priorityOfRecv, + threadGetStackSize (threadStackMedium), cacRecvThreadUDP, this); + if (tid==0) { + ca_printf ("CA: unable to create UDP receive thread\n"); + ::shutdown (this->sock, SD_BOTH); + semBinaryDestroy (this->xmitSignal); + semBinaryDestroy (this->recvThreadExitSignal); + semBinaryDestroy (this->sendThreadExitSignal); + semMutexDestroy (this->xmitBufLock); + socket_close (this->sock); + throwWithLocation ( noMemory () ); + } + } + + { + unsigned priorityOfSelf = threadGetPrioritySelf (); + unsigned priorityOfSend; + threadId tid; + threadBoolStatus tbs; + + tbs = threadHighestPriorityLevelBelow (priorityOfSelf, &priorityOfSend); + if ( tbs != tbsSuccess ) { + priorityOfSend = priorityOfSelf; + ca_printf ("CAC warning: unable to get a lower priority for a UDP send thread\n"); + } + + tid = threadCreate ( "CAC UDP Send", priorityOfSend, + threadGetStackSize (threadStackMedium), cacSendThreadUDP, this ); + if (tid==0) { + ca_printf ("CA: unable to create UDP transmitt thread\n"); + ::shutdown (this->sock, SD_BOTH); + semMutexMustTake (this->recvThreadExitSignal); + semBinaryDestroy (this->xmitSignal); + semBinaryDestroy (this->sendThreadExitSignal); + semBinaryDestroy (this->recvThreadExitSignal); + semMutexDestroy (this->xmitBufLock); + socket_close (this->sock); + throwWithLocation ( noMemory () ); + } + } + + if (pcac->ca_fd_register_func) { + (*pcac->ca_fd_register_func) (pcac->ca_fd_register_arg, this->sock, TRUE); + } + + if ( ! repeater_installed (this) ) { + osiSpawnDetachedProcessReturn osptr; + + /* + * This is not called if the repeater is known to be + * already running. (in the event of a race condition + * the 2nd repeater exits when unable to attach to the + * repeater's port) + */ + osptr = osiSpawnDetachedProcess ("CA Repeater", "caRepeater"); + if (osptr==osiSpawnDetachedProcessNoSupport) { + unsigned priorityOfSelf = threadGetPrioritySelf (); + unsigned priorityOfRepeater; + threadId tid; + threadBoolStatus tbs; + + tbs = threadLowestPriorityLevelAbove (priorityOfSelf, &priorityOfRepeater); + if ( tbs != tbsSuccess ) { + priorityOfRepeater = priorityOfSelf; + ca_printf ("CAC warning: unable to get a higher priority for repeater thread\n"); + } + + tid = threadCreate ( "CA repeater", priorityOfRepeater, + threadGetStackSize (threadStackMedium), caRepeaterThread, 0); + if (tid==0) { + ca_printf ("CA: unable to create CA repeater daemon thread\n"); + } + } + else if (osptr==osiSpawnDetachedProcessFail) { + ca_printf ("CA: unable to start CA repeater daemon detached process\n"); + } + } + + this->repeaterSubscribeTmr.reschedule (); +} + +/* + * udpiiu::~udpiiu () + */ +udpiiu::~udpiiu () +{ + nciu *pChan, *pNext; + + this->shutdown (); + + LOCK (this->pcas); + tsDLIter iter (this->chidList); + pChan = iter (); + while (pChan) { + pNext = iter (); + pChan->destroy (); + pChan = pNext; + } + UNLOCK (this->pcas); + + // wait for send and recv threads to exit + semBinaryMustTake (this->recvThreadExitSignal); + semBinaryMustTake (this->sendThreadExitSignal); + + semBinaryDestroy (this->xmitSignal); + semMutexDestroy (this->xmitBufLock); + semBinaryDestroy (this->recvThreadExitSignal); + semBinaryDestroy (this->sendThreadExitSignal); + ellFree (&this->dest); + + if (this->pcas->ca_fd_register_func) { + (*this->pcas->ca_fd_register_func) + (this->pcas->ca_fd_register_arg, this->sock, FALSE); + } + socket_close (this->sock); +} + +/* + * udpiiu::sutdown () + */ +void udpiiu::shutdown () +{ + ::shutdown (this->sock, SD_BOTH); + this->sendThreadExitCmd = true; + semBinaryGive (this->xmitSignal); +} + +/* + * bad_udp_resp_action () + */ +LOCAL void bad_udp_resp_action (udpiiu *piiu, caHdr *pMsg, const struct sockaddr_in *pnet_addr) +{ + char buf[256]; + ipAddrToA ( pnet_addr, buf, sizeof (buf) ); + ca_printf ( "CAC: Bad response code in UDP message from %s was %u\n", + buf, pMsg->m_cmmd); +} + +/* + * udp_noop_action () + */ +LOCAL void udp_noop_action (udpiiu * /* piiu */, caHdr * /* pMsg */, + const struct sockaddr_in * /* pnet_addr */) +{ + return; +} + +/* + * search_resp_action () + */ +LOCAL void search_resp_action (udpiiu *piiu, caHdr *pMsg, const struct sockaddr_in *pnet_addr) +{ + struct sockaddr_in ina; + nciu *chan; + tcpiiu *allocpiiu; + unsigned short *pMinorVersion; + unsigned minorVersion; + + /* + * ignore broadcast replies for deleted channels + * + * lock required around use of the sprintf buffer + */ + LOCK (piiu->pcas); + chan = piiu->pcas->lookupChan (pMsg->m_available); + if (!chan) { + UNLOCK (piiu->pcas); + return; + } + + /* + * Starting with CA V4.1 the minor version number + * is appended to the end of each search reply. + * This value is ignored by earlier clients. + */ + if ( pMsg->m_postsize >= sizeof (*pMinorVersion) ){ + pMinorVersion = (unsigned short *) (pMsg+1); + minorVersion = ntohs (*pMinorVersion); + } + else { + minorVersion = CA_UKN_MINOR_VERSION; + } + + /* + * the type field is abused to carry the port number + * so that we can have multiple servers on one host + */ + ina.sin_family = AF_INET; + if ( CA_V48 (CA_PROTOCOL_VERSION,minorVersion) ) { + if ( pMsg->m_cid != INADDR_BROADCAST ) { + /* + * Leave address in network byte order (m_cid has not been + * converted to the local byte order) + */ + ina.sin_addr.s_addr = pMsg->m_cid; + } + else { + ina.sin_addr = pnet_addr->sin_addr; + } + ina.sin_port = htons (pMsg->m_dataType); + } + else if ( CA_V45 (CA_PROTOCOL_VERSION,minorVersion) ) { + ina.sin_port = htons (pMsg->m_dataType); + ina.sin_addr = pnet_addr->sin_addr; + } + else { + ina.sin_port = htons (piiu->pcas->ca_server_port); + ina.sin_addr = pnet_addr->sin_addr; + } + + /* + * Ignore duplicate search replies + */ + if ( chan->piiu->compareIfTCP (*chan, *pnet_addr) ) { + UNLOCK (piiu->pcas); + return; + } + + allocpiiu = constructTCPIIU (piiu->pcas, &ina, minorVersion); + if ( ! allocpiiu ) { + UNLOCK (piiu->pcas); + return; + } + + /* + * If this is the first channel to be + * added to this niiu then communicate + * the client's name to the server. + * (CA V4.1 or higher) + */ + if ( ellCount ( &allocpiiu->chidList ) == 0 ) { + allocpiiu->userNameSetMsg (); + allocpiiu->hostNameSetMsg (); + } + + piiu->searchTmr.notifySearchResponse (chan); + + /* + * Assume that we have access once connected briefly + * until the server is able to tell us the correct + * state for backwards compatibility. + * + * Their access rights call back does not get + * called for the first time until the information + * arrives however. + */ + chan->ar.read_access = TRUE; + chan->ar.write_access = TRUE; + + /* + * remove it from the broadcast niiu + */ + chan->piiu->removeFromChanList ( chan ); + + /* + * chan->piiu must be correctly set prior to issuing the + * claim request + * + * add to the beginning of the list until we + * have sent the claim message (after which we + * move it to the end of the list) + * + * claim pending flag is set here + */ + allocpiiu->addToChanList ( chan ); + + if ( CA_V42 ( CA_PROTOCOL_VERSION, minorVersion ) ) { + chan->searchReplySetUp ( pMsg->m_cid, USHRT_MAX, 0 ); + } + else { + chan->searchReplySetUp ( pMsg->m_cid, pMsg->m_dataType, pMsg->m_count ); + } + + chan->claimMsg ( allocpiiu ); + cacRingBufferWriteFlush ( &allocpiiu->send ); + UNLOCK ( piiu->pcas ); +} + + +/* + * beacon_action () + */ +LOCAL void beacon_action (udpiiu *piiu, caHdr *pMsg, const struct sockaddr_in *pnet_addr) +{ + struct sockaddr_in ina; + + LOCK (piiu->pcas); + + /* + * this allows a fan-out server to potentially + * insert the true address of the CA server + * + * old servers: + * 1) set this field to one of the ip addresses of the host _or_ + * 2) set this field to htonl(INADDR_ANY) + * new servers: + * always set this field to htonl(INADDR_ANY) + * + * clients always assume that if this + * field is set to something that isnt htonl(INADDR_ANY) + * then it is the overriding IP address of the server. + */ + ina.sin_family = AF_INET; + if (pMsg->m_available != htonl(INADDR_ANY)) { + ina.sin_addr.s_addr = pMsg->m_available; + } + else { + ina.sin_addr = pnet_addr->sin_addr; + } + if (pMsg->m_count != 0) { + ina.sin_port = htons (pMsg->m_count); + } + else { + /* + * old servers dont supply this and the + * default port must be assumed + */ + ina.sin_port = htons (piiu->pcas->ca_server_port); + } + piiu->pcas->beaconNotify (ina); + + UNLOCK (piiu->pcas); + + return; +} + +/* + * repeater_ack_action () + */ +LOCAL void repeater_ack_action (udpiiu *piiu, caHdr *pMsg, const struct sockaddr_in * /* pnet_addr */) +{ + piiu->repeaterContacted = 1u; +# ifdef DEBUG + ca_printf ( "CAC: repeater confirmation recv\n"); +# endif + return; +} + +/* + * not_here_resp_action () + */ +LOCAL void not_here_resp_action (udpiiu * /* piiu */, caHdr * /* pMsg */, const struct sockaddr_in * /* pnet_addr */) +{ + return; +} + +/* + * UDP protocol jump table + */ +LOCAL const pProtoStubUDP udpJumpTableCAC[] = +{ + udp_noop_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + search_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + beacon_action, + not_here_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + repeater_ack_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action, + bad_udp_resp_action +}; + +/* + * post_udp_msg () + * + * LOCK should be applied when calling this routine + * + */ +int udpiiu::post_msg (const struct sockaddr_in *pnet_addr, + char *pInBuf, unsigned long blockSize) +{ + caHdr *pCurMsg; + + while ( blockSize ) { + unsigned long size; + + if ( blockSize < sizeof (*pCurMsg) ) { + return ECA_TOLARGE; + } + + pCurMsg = reinterpret_cast (pInBuf); + + /* + * fix endian of bytes + */ + pCurMsg->m_postsize = ntohs (pCurMsg->m_postsize); + pCurMsg->m_cmmd = ntohs (pCurMsg->m_cmmd); + pCurMsg->m_dataType = ntohs (pCurMsg->m_dataType); + pCurMsg->m_count = ntohs (pCurMsg->m_count); + +#if 0 + printf ("UDP Cmd=%3d Type=%3d Count=%4d Size=%4d", + pCurMsg->m_cmmd, + pCurMsg->m_dataType, + pCurMsg->m_count, + pCurMsg->m_postsize); + printf (" Avail=%8x Cid=%6d\n", + pCurMsg->m_available, + pCurMsg->m_cid); +#endif + + size = pCurMsg->m_postsize + sizeof (*pCurMsg); + + /* + * dont allow msg body extending beyond frame boundary + */ + if ( size > blockSize ) { + return ECA_TOLARGE; + } + + /* + * execute the response message + */ + pProtoStubUDP pStub; + if ( pCurMsg->m_cmmd>=NELEMENTS (udpJumpTableCAC) ) { + pStub = bad_udp_resp_action; + } + else { + pStub = udpJumpTableCAC [pCurMsg->m_cmmd]; + } + (*pStub) (this, pCurMsg, pnet_addr); + + blockSize -= size; + pInBuf += size;; + } + return ECA_NORMAL; +} + +void udpiiu::hostName ( char *pBuf, unsigned bufLength ) const +{ + if ( bufLength ) { + strncpy ( pBuf, "", bufLength ); + pBuf[bufLength - 1u] = '\0'; + } +} + +bool udpiiu::ca_v42_ok () const +{ + return false; +} + +bool udpiiu::ca_v41_ok () const +{ + return false; +} + +bool udpiiu::compareIfTCP (nciu &, const sockaddr_in &) const +{ + return false; +} + +/* + * Add chan to iiu and guarantee that + * one chan on the B cast iiu list is pointed to by + * ca_pEndOfBCastList + */ +void udpiiu::addToChanList (nciu *chan) +{ + LOCK (this->pcas); + /* + * add to the beginning of the list so that search requests for + * this channel will be sent first (since the retry count is zero) + */ + if ( ellCount (&this->chidList) == 0 ) { + this->pcas->endOfBCastList = tsDLIterBD(chan); + } + /* + * add to the front of the list so that + * search requests for new channels will be sent first + */ + chan->retry = 0u; + this->chidList.push (*chan); + chan->piiu = this; + UNLOCK (this->pcas); +} + +void udpiiu::removeFromChanList (nciu *chan) +{ + tsDLIterBD iter (chan); + + LOCK (this->pcas); + if ( chan->piiu->pcas->endOfBCastList == iter ) { + if ( iter.itemBefore () != tsDLIterBD::eol () ) { + chan->piiu->pcas->endOfBCastList = iter.itemBefore (); + } + else { + chan->piiu->pcas->endOfBCastList = + tsDLIterBD (chan->piiu->chidList.last()); + } + } + chan->piiu->chidList.remove (*chan); + chan->piiu = NULL; + UNLOCK (this->pcas); +} + +void udpiiu::disconnect (nciu *chan) +{ + // NOOP +} + +/* + * udpiiu::pushDatagramMsg () + */ +int udpiiu::pushDatagramMsg (const caHdr *pMsg, const void *pExt, ca_uint16_t extsize) +{ + unsigned long msgsize; + ca_uint16_t allignedExtSize; + caHdr *pbufmsg; + + allignedExtSize = CA_MESSAGE_ALIGN (extsize); + msgsize = sizeof (caHdr) + allignedExtSize; + + + /* fail out if max message size exceeded */ + if ( msgsize >= sizeof (this->xmitBuf)-7 ) { + return ECA_TOLARGE; + } + + semMutexMustTake (this->xmitBufLock); + if ( msgsize + this->nBytesInXmitBuf > sizeof (this->xmitBuf) ) { + semMutexGive (this->xmitBufLock); + return ECA_TOLARGE; + } + + pbufmsg = (caHdr *) &this->xmitBuf[this->nBytesInXmitBuf]; + *pbufmsg = *pMsg; + memcpy (pbufmsg+1, pExt, extsize); + if ( extsize != allignedExtSize ) { + char *pDest = (char *) (pbufmsg+1); + memset (pDest + extsize, '\0', allignedExtSize - extsize); + } + pbufmsg->m_postsize = htons (allignedExtSize); + this->nBytesInXmitBuf += msgsize; + semMutexGive (this->xmitBufLock); + + return ECA_NORMAL; +} + +int udpiiu::pushStreamMsg ( const caHdr *pmsg, const void *pext, bool blockingOk ) +{ + ca_printf ("in pushStreamMsg () for a udp iiu?\n"); + return ECA_DISCONNCHID; +}