first cut at multi-threading version

This commit is contained in:
Jeff Hill
2000-03-11 01:27:54 +00:00
parent 63c647198c
commit a266d0e586
37 changed files with 10127 additions and 10961 deletions

View File

@@ -8,7 +8,10 @@ CMPLR = STRICT
#
# includes to install from this subproject
#
INC += cadef.h
INC += caerr.h
INC += caProto.h
INC += db_access.h
#LIBSRCS += caOsDependent.c
LIBSRCS += access.cpp

View File

@@ -14,13 +14,13 @@ INC += caProto.h
#
# on generic system
ca_SRCS_DEFAULT = if_depen.c
#ca_SRCS_DEFAULT = if_depen.c
# on WIN32 only
ca_SRCS_WIN32 = windows_depen.c
#ca_SRCS_WIN32 = windows_depen.c
# on vxWorks only
ca_SRCS_vxWorks = vxWorks_depen.c
#ca_SRCS_vxWorks = vxWorks_depen.c
# on all systems
ca_SRCS += caOsDependent.c

File diff suppressed because it is too large Load Diff

3343
src/ca/access.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,16 @@
#include "envDefs.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "envDefs.h"
#if 0
void caSetupAddrList(
ELLLIST *pList,
SOCKET socket);
void caPrintAddrList(ELLLIST *pList);
epicsShareFunc void epicsShareAPI caDiscoverInterfaces(
@@ -17,7 +19,8 @@ epicsShareFunc void epicsShareAPI caDiscoverInterfaces(
unsigned short port,
struct in_addr matchAddr);
epicsShareFunc void epicsShareAPI caAddConfiguredAddr(
epicsShareFunc void caAddConfiguredAddr (
cac *pcac,
ELLLIST *pList,
const ENV_PARAM *pEnv,
SOCKET socket,
@@ -28,15 +31,7 @@ int local_addr(SOCKET socket, struct sockaddr_in *plcladdr);
epicsShareFunc unsigned short epicsShareAPI
caFetchPortConfig(const ENV_PARAM *pEnv, unsigned short defaultPort);
typedef union ca_addr {
struct sockaddr_in in;
struct sockaddr sa;
}caAddr;
typedef struct {
ELLNODE node;
caAddr destAddr;
}caAddrNode;
#endif
#ifdef __cplusplus
}

View File

@@ -1,224 +0,0 @@
/*
* $Id$
* Author: Jeffrey O. Hill
* hill@luke.lanl.gov
* (505) 665 1831
* Date: 9-93
*
* 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 "osiSleep.h"
#include "bsdSocketResource.h"
/*
* cac_select_io()
*
* NOTE: on multithreaded systems this assumes that the
* local implementation of select is reentrant
*/
int cac_select_io (CA_STATIC *ca_static, struct timeval *ptimeout, int flags)
{
/*
* Use auto timeout so there is no chance of
* recursive reuse of ptimeout
*/
struct timeval autoTimeOut = *ptimeout;
long status;
IIU *piiu;
unsigned long freespace;
SOCKET maxfd;
caFDInfo *pfdi;
int ioPending;
LOCK;
pfdi = (caFDInfo *) ellGet(&ca_static->fdInfoFreeList);
if (!pfdi) {
pfdi = (caFDInfo *) calloc (1, sizeof(*pfdi));
if (!pfdi) {
ca_printf("CAC: no mem for select ctx?\n");
UNLOCK;
return -1;
}
}
ellAdd (&ca_static->fdInfoList, &pfdi->node);
FD_ZERO (&pfdi->readMask);
FD_ZERO (&pfdi->writeMask);
maxfd = 0;
ioPending = FALSE;
for( piiu = (IIU *) iiuList.node.next;
piiu; piiu = (IIU *) piiu->node.next) {
if (piiu->state==iiu_disconnected) {
continue;
}
if (!FD_IN_FDSET(piiu->sock_chan))
{
ca_printf(
"%s.%d: file number %d > FD_SETSIZE=%d ignored\n",
__FILE__, __LINE__, piiu->sock_chan, FD_SETSIZE);
continue;
}
/*
* Dont bother receiving if we have insufficient
* space for the maximum UDP message, or space
* for one TCP byte.
*/
if (flags&CA_DO_RECVS) {
freespace = cacRingBufferWriteSize (&piiu->recv, TRUE);
if (freespace>=piiu->minfreespace) {
maxfd = max (maxfd,piiu->sock_chan);
FD_SET (piiu->sock_chan, &pfdi->readMask);
piiu->recvPending = TRUE;
ioPending = TRUE;
}
else {
piiu->recvPending = FALSE;
}
}
else {
piiu->recvPending = FALSE;
}
if (piiu->state==iiu_connecting) {
FD_SET (piiu->sock_chan, &pfdi->writeMask);
ioPending = TRUE;
}
else if (flags&CA_DO_SENDS || piiu->pushPending) {
if (cacRingBufferReadSize(&piiu->send, FALSE)>0) {
maxfd = max (maxfd,piiu->sock_chan);
FD_SET (piiu->sock_chan, &pfdi->writeMask);
ioPending = TRUE;
}
}
}
UNLOCK;
/*
* win32 requires this (others will
* run faster with this installed)
*/
if (!ioPending) {
/*
* recover from subtle differences between
* windows sockets and UNIX sockets implementation
* of select()
*/
if ( ptimeout->tv_sec!=0 || ptimeout->tv_usec!=0 ) {
osiSleep (ptimeout->tv_sec, ptimeout->tv_usec);
}
status = 0;
}
else {
status = select (maxfd+1, &pfdi->readMask, &pfdi->writeMask,
NULL, &autoTimeOut);
if (status<0) {
int errnoCpy = SOCKERRNO;
if (errnoCpy!=SOCK_EINTR) {
ca_printf (
"CAC: unexpected select fail: %s\n",
SOCKERRSTR(errnoCpy));
}
}
}
/*
* get a new time stamp if we have been waiting
* for any significant length of time
*/
if (ptimeout->tv_sec || ptimeout->tv_usec) {
cac_gettimeval (&ca_static->currentTime);
}
LOCK;
/*
* must run through the IIU list even if no IO is pending
* if any of the IOCs are in flow control (so that an exit
* flow control msg can be sent to each of them that are)
*/
if (status>0 || (ca_static->ca_number_iiu_in_fc>0u && status>=0)) {
status = 0;
for (piiu = (IIU *) iiuList.node.next;
piiu; piiu = (IIU *) piiu->node.next) {
if (piiu->state==iiu_disconnected) {
continue;
}
if (FD_ISSET(piiu->sock_chan,&pfdi->readMask)) {
unsigned long bytesReceived;
bytesReceived = (*piiu->recvBytes)(piiu);
if (bytesReceived>0) {
status++;
/*
* if we are not blocking and there is a
* message present then start to suspect that
* we are getting behind
*/
if (piiu->sock_proto==IPPROTO_TCP) {
if (ptimeout->tv_sec==0 && ptimeout->tv_usec==0) {
flow_control_on(piiu);
}
}
}
}
else if (piiu->recvPending) {
/*
* if we are looking for incoming messages
* and there are none then we are certain that
* we are not getting behind
*/
if (piiu->sock_proto==IPPROTO_TCP) {
flow_control_off(piiu);
}
}
if (FD_ISSET(piiu->sock_chan,&pfdi->writeMask)) {
unsigned long bytesSent;
bytesSent = (*piiu->sendBytes)(piiu);
if (bytesSent>0) {
status++;
}
}
}
}
ellDelete (&ca_static->fdInfoList, &pfdi->node);
ellAdd (&ca_static->fdInfoFreeList, &pfdi->node);
UNLOCK;
return status;
}

View File

@@ -35,37 +35,37 @@
* CA protocol number
* TCP/UDP port number (bumped each major protocol change)
*/
#define CA_PROTOCOL_VERSION 4u
#define CA_MINOR_VERSION 8u
#define CA_VERSION_STRING "4.8"
#define CA_UKN_MINOR_VERSION 0u /* unknown minor version */
#define CA_PROTOCOL_VERSION 4u
#define CA_MINOR_VERSION 8u
#define CA_VERSION_STRING "4.8"
#define CA_UKN_MINOR_VERSION 0u /* unknown minor version */
#if CA_PROTOCOL_VERSION == 4u
#define CA_V41(MAJOR,MINOR) ((MINOR)>=1u)
#define CA_V42(MAJOR,MINOR) ((MINOR)>=2u)
#define CA_V43(MAJOR,MINOR) ((MINOR)>=3u)
#define CA_V44(MAJOR,MINOR) ((MINOR)>=4u)
#define CA_V45(MAJOR,MINOR) ((MINOR)>=5u)
#define CA_V46(MAJOR,MINOR) ((MINOR)>=6u)
#define CA_V47(MAJOR,MINOR) ((MINOR)>=7u)
#define CA_V48(MAJOR,MINOR) ((MINOR)>=8u)
#define CA_V41(MAJOR,MINOR) ((MINOR)>=1u)
#define CA_V42(MAJOR,MINOR) ((MINOR)>=2u)
#define CA_V43(MAJOR,MINOR) ((MINOR)>=3u)
#define CA_V44(MAJOR,MINOR) ((MINOR)>=4u)
#define CA_V45(MAJOR,MINOR) ((MINOR)>=5u)
#define CA_V46(MAJOR,MINOR) ((MINOR)>=6u)
#define CA_V47(MAJOR,MINOR) ((MINOR)>=7u)
#define CA_V48(MAJOR,MINOR) ((MINOR)>=8u)
#elif CA_PROTOCOL_VERSION > 4u
#define CA_V41(MAJOR,MINOR) ( 1u )
#define CA_V42(MAJOR,MINOR) ( 1u )
#define CA_V43(MAJOR,MINOR) ( 1u )
#define CA_V44(MAJOR,MINOR) ( 1u )
#define CA_V45(MAJOR,MINOR) ( 1u )
#define CA_V46(MAJOR,MINOR) ( 1u )
#define CA_V47(MAJOR,MINOR) ( 1u )
#define CA_V48(MAJOR,MINOR) ( 1u )
#define CA_V41(MAJOR,MINOR) ( 1u )
#define CA_V42(MAJOR,MINOR) ( 1u )
#define CA_V43(MAJOR,MINOR) ( 1u )
#define CA_V44(MAJOR,MINOR) ( 1u )
#define CA_V45(MAJOR,MINOR) ( 1u )
#define CA_V46(MAJOR,MINOR) ( 1u )
#define CA_V47(MAJOR,MINOR) ( 1u )
#define CA_V48(MAJOR,MINOR) ( 1u )
#else
#define CA_V41(MAJOR,MINOR) ( 0u )
#define CA_V42(MAJOR,MINOR) ( 0u )
#define CA_V43(MAJOR,MINOR) ( 0u )
#define CA_V44(MAJOR,MINOR) ( 0u )
#define CA_V45(MAJOR,MINOR) ( 0u )
#define CA_V46(MAJOR,MINOR) ( 0u )
#define CA_V47(MAJOR,MINOR) ( 0u )
#define CA_V48(MAJOR,MINOR) ( 0u )
#define CA_V41(MAJOR,MINOR) ( 0u )
#define CA_V42(MAJOR,MINOR) ( 0u )
#define CA_V43(MAJOR,MINOR) ( 0u )
#define CA_V44(MAJOR,MINOR) ( 0u )
#define CA_V45(MAJOR,MINOR) ( 0u )
#define CA_V46(MAJOR,MINOR) ( 0u )
#define CA_V47(MAJOR,MINOR) ( 0u )
#define CA_V48(MAJOR,MINOR) ( 0u )
#endif
/*
@@ -74,14 +74,14 @@
* environment variables "EPICS_CA_REPEATER_PORT" and
* "EPICS_CA_SERVER_PORT"
*/
#define CA_PORT_BASE IPPORT_USERRESERVED + 56U
#define CA_SERVER_PORT (CA_PORT_BASE+CA_PROTOCOL_VERSION*2u)
#define CA_REPEATER_PORT (CA_PORT_BASE+CA_PROTOCOL_VERSION*2u+1u)
#define CA_PORT_BASE IPPORT_USERRESERVED + 56U
#define CA_SERVER_PORT (CA_PORT_BASE+CA_PROTOCOL_VERSION*2u)
#define CA_REPEATER_PORT (CA_PORT_BASE+CA_PROTOCOL_VERSION*2u+1u)
/* 1492(min of ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP) joh 8-6-97 */
#define ETHERNET_MAX_UDP (1482u-20u-8u)
#define MAX_UDP 1024u /* original MAX_UDP */
#define MAX_TCP (MAX_UDP*16u) /* so waveforms fit */
#define MAX_MSG_SIZE (MAX_TCP) /* the larger of tcp and udp max */
#define ETHERNET_MAX_UDP (1482u-20u-8u)
#define MAX_UDP 1024u /* original MAX_UDP */
#define MAX_TCP (MAX_UDP*16u) /* so waveforms fit */
#define MAX_MSG_SIZE (MAX_TCP) /* the larger of tcp and udp max */
/*
* architecture independent types
@@ -89,39 +89,39 @@
* (so far this works on all archs we have ported to)
*/
typedef unsigned short ca_uint16_t;
typedef unsigned int ca_uint32_t;
typedef unsigned int ca_uint32_t;
typedef float ca_float32_t;
typedef ca_uint32_t caResId;
typedef ca_uint32_t caResId;
/* values for m_cmmd */
#define CA_PROTO_NOOP 0u /* do nothing, but verify TCP */
#define CA_PROTO_EVENT_ADD 1u /* add an event */
#define CA_PROTO_EVENT_CANCEL 2u /* cancel an event */
#define CA_PROTO_READ 3u /* read and return a channel value*/
#define CA_PROTO_WRITE 4u /* write a channel value */
#define CA_PROTO_SNAPSHOT 5u /* snapshot of the system */
#define CA_PROTO_SEARCH 6u /* IOC channel search */
#define CA_PROTO_BUILD 7u /* build - obsolete */
#define CA_PROTO_EVENTS_OFF 8u /* flow control */
#define CA_PROTO_EVENTS_ON 9u /* flow control */
#define CA_PROTO_READ_SYNC 10u /* purge old reads */
#define CA_PROTO_ERROR 11u /* an operation failed */
#define CA_PROTO_CLEAR_CHANNEL 12u /* free chan resources */
#define CA_PROTO_RSRV_IS_UP 13u /* CA server has joined the net */
#define CA_PROTO_NOT_FOUND 14u /* channel not found */
#define CA_PROTO_READ_NOTIFY 15u /* add a one shot event */
#define CA_PROTO_READ_BUILD 16u /* read and build - obsolete */
#define REPEATER_CONFIRM 17u /* registration confirmation */
#define CA_PROTO_CLAIM_CIU 18u /* client claims resource in server */
#define CA_PROTO_WRITE_NOTIFY 19u /* notify after write chan value */
#define CA_PROTO_CLIENT_NAME 20u /* CA V4.1 identify client */
#define CA_PROTO_HOST_NAME 21u /* CA V4.1 identify client */
#define CA_PROTO_ACCESS_RIGHTS 22u /* CA V4.2 asynch access rights chg */
#define CA_PROTO_ECHO 23u /* CA V4.3 connection verify */
#define REPEATER_REGISTER 24u /* registr for repeater fan out */
#define CA_PROTO_SIGNAL 25u /* knock the server out of select */
#define CA_PROTO_CLAIM_CIU_FAILED 26u /* unable to create chan resource in server */
#define CA_PROTO_SERVER_DISCONN 27u /* server deletes PV (or channel) */
/* values for m_cmmd */
#define CA_PROTO_NOOP 0u /* do nothing, but verify TCP */
#define CA_PROTO_EVENT_ADD 1u /* add an event */
#define CA_PROTO_EVENT_CANCEL 2u /* cancel an event */
#define CA_PROTO_READ 3u /* read and return a channel value*/
#define CA_PROTO_WRITE 4u /* write a channel value */
#define CA_PROTO_SNAPSHOT 5u /* snapshot of the system */
#define CA_PROTO_SEARCH 6u /* IOC channel search */
#define CA_PROTO_BUILD 7u /* build - obsolete */
#define CA_PROTO_EVENTS_OFF 8u /* flow control */
#define CA_PROTO_EVENTS_ON 9u /* flow control */
#define CA_PROTO_READ_SYNC 10u /* purge old reads */
#define CA_PROTO_ERROR 11u /* an operation failed */
#define CA_PROTO_CLEAR_CHANNEL 12u /* free chan resources */
#define CA_PROTO_RSRV_IS_UP 13u /* CA server has joined the net */
#define CA_PROTO_NOT_FOUND 14u /* channel not found */
#define CA_PROTO_READ_NOTIFY 15u /* add a one shot event */
#define CA_PROTO_READ_BUILD 16u /* read and build - obsolete */
#define REPEATER_CONFIRM 17u /* registration confirmation */
#define CA_PROTO_CLAIM_CIU 18u /* client claims resource in server */
#define CA_PROTO_WRITE_NOTIFY 19u /* notify after write chan value */
#define CA_PROTO_CLIENT_NAME 20u /* CA V4.1 identify client */
#define CA_PROTO_HOST_NAME 21u /* CA V4.1 identify client */
#define CA_PROTO_ACCESS_RIGHTS 22u /* CA V4.2 asynch access rights chg */
#define CA_PROTO_ECHO 23u /* CA V4.3 connection verify */
#define REPEATER_REGISTER 24u /* registr for repeater fan out */
#define CA_PROTO_SIGNAL 25u /* knock the server out of select */
#define CA_PROTO_CLAIM_CIU_FAILED 26u /* unable to create chan resource in server */
#define CA_PROTO_SERVER_DISCONN 27u /* server deletes PV (or channel) */
#define CA_PROTO_LAST_CMMD CA_PROTO_CLAIM_CIU_FAILED
@@ -129,28 +129,28 @@ typedef ca_uint32_t caResId;
* for use with search and not_found (if search fails and
* its not a broadcast tell the client to look elesewhere)
*/
#define DOREPLY 10u
#define DONTREPLY 5u
#define DOREPLY 10u
#define DONTREPLY 5u
/* size of object in bytes rounded up to nearest oct word */
#define OCT_ROUND(A) ((((unsigned long)(A))+7u)>>3u)
#define OCT_SIZEOF(A) (OCT_ROUND(sizeof(A)))
#define OCT_ROUND(A) (((A)+7)/8)
#define OCT_SIZEOF(A) (OCT_ROUND(sizeof(A)))
/* size of object in bytes rounded up to nearest long word */
#define QUAD_ROUND(A) (((unsigned long)(A))+3u)>>2u)
#define QUAD_SIZEOF(A) (QUAD_ROUND(sizeof(A)))
#define QUAD_ROUND(A) ((A)+3)/4)
#define QUAD_SIZEOF(A) (QUAD_ROUND(sizeof(A)))
/* size of object in bytes rounded up to nearest short word */
#define BI_ROUND(A) ((((unsigned long)(A))+1u)>>1u)
#define BI_SIZEOF(A) (BI_ROUND(sizeof(A)))
#define BI_ROUND(A) (((A)+1)/2)
#define BI_SIZEOF(A) (BI_ROUND(sizeof(A)))
/*
* For communicating access rights to the clients
*
* (placed in m_available hdr field of CA_PROTO_ACCESS_RIGHTS cmmd
*/
#define CA_PROTO_ACCESS_RIGHT_READ (1u<<0u)
#define CA_PROTO_ACCESS_RIGHT_WRITE (1u<<1u)
#define CA_PROTO_ACCESS_RIGHT_READ (1u<<0u)
#define CA_PROTO_ACCESS_RIGHT_WRITE (1u<<1u)
/*
* All structures passed in the protocol must have individual
@@ -166,30 +166,30 @@ typedef ca_uint32_t caResId;
* the common part of each message sent/recv by the
* CA server.
*/
typedef struct ca_hdr {
ca_uint16_t m_cmmd; /* operation to be performed */
ca_uint16_t m_postsize; /* size of message extension */
ca_uint16_t m_dataType; /* operation data type */
ca_uint16_t m_count; /* operation data count */
ca_uint32_t m_cid; /* channel identifier */
ca_uint32_t m_available; /* undefined message location for use
* by client processes */
typedef struct ca_hdr {
ca_uint16_t m_cmmd; /* operation to be performed */
ca_uint16_t m_postsize; /* size of message extension */
ca_uint16_t m_dataType; /* operation data type */
ca_uint16_t m_count; /* operation data count */
ca_uint32_t m_cid; /* channel identifier */
ca_uint32_t m_available; /* undefined message location for use
* by client processes */
}caHdr;
/*
* for monitor (event) message extension
*/
struct mon_info{
ca_float32_t m_lval; /* low delta */
ca_float32_t m_hval; /* high delta */
ca_float32_t m_toval; /* period btween samples */
ca_uint16_t m_mask; /* event select mask */
ca_uint16_t m_pad; /* extend to 32 bits */
ca_float32_t m_lval; /* low delta */
ca_float32_t m_hval; /* high delta */
ca_float32_t m_toval; /* period btween samples */
ca_uint16_t m_mask; /* event select mask */
ca_uint16_t m_pad; /* extend to 32 bits */
};
struct monops { /* monitor req opi to ioc */
caHdr m_header;
struct mon_info m_info;
struct monops { /* monitor req opi to ioc */
caHdr m_header;
struct mon_info m_info;
};
/*

75
src/ca/caPutNotify.cpp Normal file
View File

@@ -0,0 +1,75 @@
/*
* caExtraEventLabor ()
*/
LOCAL void caExtraEventLabor (void *pArg)
{
cac *pcac = (cac *) pArg;
dbcaPutNotify *ppnb;
struct event_handler_args args;
while (TRUE) {
semTakeStatus semStatus;
/*
* independent lock used here in order to
* avoid any possibility of blocking
* the database (or indirectly blocking
* one client on another client).
*/
semStatus = semMutexTakeTimeout (pcac->localIIU.putNotifyLock, 60.0 /* sec */);
if (semStatus!=semTakeOK) {
ca_printf (pcac, "cac: put notify deadlock condition detected\n");
(*pcac->localIIU.pdbAdapter->p_db_post_extra_labor) (pcac->localIIU.evctx);
break;
}
ppnb = (dbcaPutNotify *) ellGet (&pcac->localIIU.putNotifyQue);
semMutexGive (pcac->localIIU.putNotifyLock);
/*
* break to loop exit
*/
if (!ppnb) {
break;
}
/*
* setup arguments and call user's function
*/
args.usr = ppnb->caUserArg;
args.chid = ppnb->dbPutNotify.usrPvt;
#error type must be an external type
args.type = ppnb->dbPutNotify.dbrType;
args.count = ppnb->dbPutNotify.nRequest;
args.dbr = NULL;
if (ppnb->dbPutNotify.status) {
if (ppnb->dbPutNotify.status == S_db_Blocked) {
args.status = ECA_PUTCBINPROG;
}
else {
args.status = ECA_PUTFAIL;
}
}
else {
args.status = ECA_NORMAL;
}
LOCK (pcac);
(*ppnb->caUserCallback) (args);
UNLOCK (pcac);
ppnb->busy = FALSE;
}
/*
* wakeup the TCP thread if it is waiting for a cb to complete
*/
semBinaryGive (pcac->ca_blockSem);
}

View File

@@ -1,33 +0,0 @@
/*
* caRepeater.c
* share/src/ca/caRepeater.c
*
* CA broadcast repeater executable
*
* Author: Jeff Hill
* Date: 3-27-90
*
* PURPOSE:
* Broadcasts fan out over the LAN, but old IP kernels do not allow
* two processes on the same machine to get the same broadcast
* (and modern IP kernels do not allow two processes on the same machine
* to receive the same unicast).
*
* This code fans out UDP messages sent to the CA repeater port
* to all CA client processes that have subscribed.
*
* NOTES:
*
* see repeater.c
*
*/
#include "iocinf.h"
int main()
{
ca_repeater ();
assert (0);
return (0);
}

35
src/ca/caRepeater.cpp Normal file
View File

@@ -0,0 +1,35 @@
/*
* $Id$
*
* CA UDP repeater standalone executable
*
* Author: Jeff Hill
* Date: 3-27-90
*
* PURPOSE:
* Broadcasts fan out over the LAN, but old IP kernels do not allow
* two processes on the same machine to get the same broadcast
* (and modern IP kernels do not allow two processes on the same machine
* to receive the same unicast).
*
* This code fans out UDP messages sent to the CA repeater port
* to all CA client processes that have subscribed.
*
* NOTES:
*
* see repeater.c
*
*/
#include "epicsAssert.h"
#include "shareLib.h"
epicsShareFunc void epicsShareAPI ca_repeater (void);
int main()
{
ca_repeater ();
assert (0);
return (0);
}

View File

@@ -25,9 +25,7 @@
#define LOCAL static
#endif
#ifndef OK
#define OK 0
#endif
#define CATIME_OK 0
#ifndef NULL
#define NULL 0
@@ -199,7 +197,7 @@ int catime (char *channelName, enum appendNumberFlag appNF)
SEVCHK (ca_task_exit (), "Unable to free resources at exit");
return OK;
return CATIME_OK;
}
/*
@@ -216,7 +214,7 @@ LOCAL void printSearchStat(unsigned iterations)
double stdDev;
for (pi=itemList; pi<&itemList[iterations]; pi++) {
double retry = pi->chix->retry;
double retry = ca_search_attempts (pi->chix);
X += retry;
XX += retry*retry;
if (retry>max) {

File diff suppressed because it is too large Load Diff

677
src/ca/conn.cpp Normal file
View File

@@ -0,0 +1,677 @@
/* $Id$ */
/*
*
* L O S A L A M O S
* Los Alamos National Laboratory
* Los Alamos, New Mexico 87545
*
* Copyright, 1986, The Regents of the University of California.
*
* Author: Jeff Hill
*/
#include "iocinf.h"
#ifdef DEBUG
#define LOGRETRYINTERVAL logRetryInterval(__FILE__, __LINE__);
LOCAL void logRetryInterval (pcac, char *pFN, unsigned lineno);
#else
#define LOGRETRYINTERVAL
#endif
/*
* 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(index<NELEMENTS(pcac->ca_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;
}

View File

@@ -28,8 +28,6 @@
*
*/
static char *sccsId = "@(#) $Id$";
#include <string.h>
#include "iocinf.h"
@@ -168,8 +166,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
char *pSrc = s;
char *pDest = d;
char *pSrc = (char *) s;
char *pDest = (char *) d;
/* convert "in place" -> nothing to do */
if (s == d)
@@ -196,8 +194,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
dbr_short_t *pSrc = s;
dbr_short_t *pDest = d;
dbr_short_t *pSrc = (dbr_short_t *) s;
dbr_short_t *pDest = (dbr_short_t *) d;
unsigned long i;
for(i=0; i<num; i++){
@@ -225,8 +223,8 @@ unsigned long num /* number of values */
)
{
unsigned long i;
dbr_char_t *pSrc = s;
dbr_char_t *pDest = d;
dbr_char_t *pSrc = (dbr_char_t *) s;
dbr_char_t *pDest = (dbr_char_t *) d;
/* convert "in place" -> nothing to do */
if (s == d)
@@ -251,8 +249,8 @@ unsigned long num /* number of values */
)
{
unsigned long i;
dbr_long_t *pSrc = s;
dbr_long_t *pDest = d;
dbr_long_t *pSrc = (dbr_long_t *) s;
dbr_long_t *pDest = (dbr_long_t *) d;
for(i=0; i<num; i++){
*pDest = dbr_ntohl( *pSrc );
@@ -282,8 +280,8 @@ unsigned long num /* number of values */
dbr_enum_t *pSrc;
dbr_enum_t *pDest;
pSrc = s;
pDest = d;
pSrc = (dbr_enum_t *) s;
pDest = (dbr_enum_t *) d;
for(i=0; i<num; i++){
*pDest = dbr_ntohs(*pSrc);
/*
@@ -311,8 +309,8 @@ unsigned long num /* number of values */
)
{
unsigned long i;
dbr_float_t *pSrc = s;
dbr_float_t *pDest = d;
dbr_float_t *pSrc = (dbr_float_t *) s;
dbr_float_t *pDest = (dbr_float_t *) d;
for(i=0; i<num; i++){
if(encode){
@@ -344,8 +342,8 @@ unsigned long num /* number of values */
)
{
unsigned long i;
dbr_double_t *pSrc = s;
dbr_double_t *pDest = d;
dbr_double_t *pSrc = (dbr_double_t *) s;
dbr_double_t *pDest = (dbr_double_t *) d;
for(i=0; i<num; i++){
if(encode){
@@ -385,8 +383,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_sts_string *pSrc = s;
struct dbr_sts_string *pDest = d;
struct dbr_sts_string *pSrc = (struct dbr_sts_string *) s;
struct dbr_sts_string *pDest = (struct dbr_sts_string *) d;
/* convert ieee to vax format or vax to ieee */
pDest->status = dbr_ntohs(pSrc->status);
@@ -423,8 +421,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_sts_int *pSrc = s;
struct dbr_sts_int *pDest = d;
struct dbr_sts_int *pSrc = (struct dbr_sts_int *) s;
struct dbr_sts_int *pDest = (struct dbr_sts_int *) d;
/* convert vax to ieee or ieee to vax format -- same code*/
pDest->status = dbr_ntohs(pSrc->status);
@@ -458,8 +456,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_sts_float *pSrc = s;
struct dbr_sts_float *pDest = d;
struct dbr_sts_float *pSrc = (struct dbr_sts_float *) s;
struct dbr_sts_float *pDest = (struct dbr_sts_float *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -484,8 +482,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_sts_double *pSrc = s;
struct dbr_sts_double *pDest = d;
struct dbr_sts_double *pSrc = (struct dbr_sts_double *) s;
struct dbr_sts_double *pDest = (struct dbr_sts_double *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -513,8 +511,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_sts_enum *pSrc = s;
struct dbr_sts_enum *pDest = d;
struct dbr_sts_enum *pSrc = (struct dbr_sts_enum *) s;
struct dbr_sts_enum *pDest = (struct dbr_sts_enum *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -541,8 +539,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_gr_int *pSrc = s;
struct dbr_gr_int *pDest = d;
struct dbr_gr_int *pSrc = (struct dbr_gr_int *) s;
struct dbr_gr_int *pDest = (struct dbr_gr_int *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -578,8 +576,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_gr_char *pSrc = s;
struct dbr_gr_char *pDest = d;
struct dbr_gr_char *pSrc = (struct dbr_gr_char *) s;
struct dbr_gr_char *pDest = (struct dbr_gr_char *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -619,8 +617,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_gr_long *pSrc = s;
struct dbr_gr_long *pDest = d;
struct dbr_gr_long *pSrc = (struct dbr_gr_long *) s;
struct dbr_gr_long *pDest = (struct dbr_gr_long *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -657,8 +655,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_gr_enum *pSrc = s;
struct dbr_gr_enum *pDest = d;
struct dbr_gr_enum *pSrc = (struct dbr_gr_enum *) s;
struct dbr_gr_enum *pDest = (struct dbr_gr_enum *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -690,8 +688,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_gr_double *pSrc = s;
struct dbr_gr_double *pDest = d;
struct dbr_gr_double *pSrc = (struct dbr_gr_double *) s;
struct dbr_gr_double *pDest = (struct dbr_gr_double *) d;
/* these are same for vax to ieee or ieee to vax */
pDest->status = dbr_ntohs(pSrc->status);
@@ -752,8 +750,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_gr_float *pSrc = s;
struct dbr_gr_float *pDest = d;
struct dbr_gr_float *pSrc = (struct dbr_gr_float *) s;
struct dbr_gr_float *pDest = (struct dbr_gr_float *) d;
/* these are same for vax to ieee or ieee to vax */
pDest->status = dbr_ntohs(pSrc->status);
@@ -815,8 +813,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_ctrl_int *pSrc = s;
struct dbr_ctrl_int *pDest = d;
struct dbr_ctrl_int *pSrc = (struct dbr_ctrl_int *) s;
struct dbr_ctrl_int *pDest = (struct dbr_ctrl_int *) d;
/* vax to ieee or ieee to vax -- same code */
pDest->status = dbr_ntohs(pSrc->status);
@@ -855,8 +853,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_ctrl_long *pSrc = s;
struct dbr_ctrl_long *pDest = d;
struct dbr_ctrl_long *pSrc = (struct dbr_ctrl_long*) s;
struct dbr_ctrl_long *pDest = (struct dbr_ctrl_long *) d;
/* vax to ieee or ieee to vax -- same code */
pDest->status = dbr_ntohs(pSrc->status);
@@ -895,8 +893,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_ctrl_char *pSrc = s;
struct dbr_ctrl_char *pDest = d;
struct dbr_ctrl_char *pSrc = (struct dbr_ctrl_char *) s;
struct dbr_ctrl_char *pDest = (struct dbr_ctrl_char *) d;
/* vax to ieee or ieee to vax -- same code */
pDest->status = dbr_ntohs(pSrc->status);
@@ -933,8 +931,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_ctrl_double *pSrc = s;
struct dbr_ctrl_double *pDest = d;
struct dbr_ctrl_double *pSrc = (struct dbr_ctrl_double *) s;
struct dbr_ctrl_double *pDest = (struct dbr_ctrl_double *) d;
/* these are the same for ieee to vax or vax to ieee */
pDest->status = dbr_ntohs(pSrc->status);
@@ -997,8 +995,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_ctrl_float *pSrc = s;
struct dbr_ctrl_float *pDest = d;
struct dbr_ctrl_float *pSrc = (struct dbr_ctrl_float *) s;
struct dbr_ctrl_float *pDest = (struct dbr_ctrl_float *) d;
/* these are the same for ieee to vaax or vax to ieee */
pDest->status = dbr_ntohs(pSrc->status);
@@ -1060,8 +1058,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_ctrl_enum *pSrc = s;
struct dbr_ctrl_enum *pDest = d;
struct dbr_ctrl_enum *pSrc = (struct dbr_ctrl_enum *) s;
struct dbr_ctrl_enum *pDest = (struct dbr_ctrl_enum *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -1096,8 +1094,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_sts_char *pSrc = s;
struct dbr_sts_char *pDest = d;
struct dbr_sts_char *pSrc = (struct dbr_sts_char *) s;
struct dbr_sts_char *pDest = (struct dbr_sts_char *) d;
/* convert vax to ieee or ieee to vax format -- same code*/
pDest->status = dbr_ntohs(pSrc->status);
@@ -1127,8 +1125,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_sts_long *pSrc = s;
struct dbr_sts_long *pDest = d;
struct dbr_sts_long *pSrc = (struct dbr_sts_long *) s;
struct dbr_sts_long *pDest = (struct dbr_sts_long *) d;
/* convert vax to ieee or ieee to vax format -- same code*/
pDest->status = dbr_ntohs(pSrc->status);
@@ -1159,8 +1157,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_time_string *pSrc = s;
struct dbr_time_string *pDest = d;
struct dbr_time_string *pSrc = (struct dbr_time_string *) s;
struct dbr_time_string *pDest = (struct dbr_time_string *) d;
/* convert ieee to vax format or vax to ieee */
pDest->status = dbr_ntohs(pSrc->status);
@@ -1191,8 +1189,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_time_short *pSrc = s;
struct dbr_time_short *pDest = d;
struct dbr_time_short *pSrc = (struct dbr_time_short *) s;
struct dbr_time_short *pDest = (struct dbr_time_short *) d;
/* convert vax to ieee or ieee to vax format -- same code*/
pDest->status = dbr_ntohs(pSrc->status);
@@ -1225,8 +1223,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_time_float *pSrc = s;
struct dbr_time_float *pDest = d;
struct dbr_time_float *pSrc = (struct dbr_time_float *) s;
struct dbr_time_float *pDest = (struct dbr_time_float *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -1253,8 +1251,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_time_double *pSrc = s;
struct dbr_time_double *pDest = d;
struct dbr_time_double *pSrc = (struct dbr_time_double *) s;
struct dbr_time_double *pDest = (struct dbr_time_double *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -1282,8 +1280,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_time_enum *pSrc = s;
struct dbr_time_enum *pDest = d;
struct dbr_time_enum *pSrc = (struct dbr_time_enum *) s;
struct dbr_time_enum *pDest = (struct dbr_time_enum *) d;
pDest->status = dbr_ntohs(pSrc->status);
pDest->severity = dbr_ntohs(pSrc->severity);
@@ -1312,8 +1310,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_time_char *pSrc = s;
struct dbr_time_char *pDest = d;
struct dbr_time_char *pSrc = (struct dbr_time_char *) s;
struct dbr_time_char *pDest = (struct dbr_time_char *) d;
/* convert vax to ieee or ieee to vax format -- same code*/
pDest->status = dbr_ntohs(pSrc->status);
@@ -1344,8 +1342,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_time_long *pSrc = s;
struct dbr_time_long *pDest = d;
struct dbr_time_long *pSrc = (struct dbr_time_long *) s;
struct dbr_time_long *pDest = (struct dbr_time_long *) d;
/* convert vax to ieee or ieee to vax format -- same code*/
pDest->status = dbr_ntohs(pSrc->status);
@@ -1375,8 +1373,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
dbr_put_ackt_t *pSrc = s;
dbr_put_ackt_t *pDest = d;
dbr_put_ackt_t *pSrc = (dbr_put_ackt_t *) s;
dbr_put_ackt_t *pDest = (dbr_put_ackt_t *) d;
unsigned long i;
for(i=0; i<num; i++){
@@ -1409,8 +1407,8 @@ int encode, /* cvrt HOST to NET if T */
unsigned long num /* number of values */
)
{
struct dbr_stsack_string *pSrc = s;
struct dbr_stsack_string *pDest = d;
struct dbr_stsack_string *pSrc = (struct dbr_stsack_string *) s;
struct dbr_stsack_string *pDest = (struct dbr_stsack_string *) d;
/* convert ieee to vax format or vax to ieee */
pDest->status = dbr_ntohs(pSrc->status);

View File

@@ -40,7 +40,7 @@ int evtime(char *pname)
status = ca_pend_io(10.0);
if(status != ECA_NORMAL){
printf("%s not found\n", pname);
return OK;
return 0;
}
status = ca_add_event(

View File

@@ -1,106 +0,0 @@
/************************************************************************/
/* */
/* 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. */
/* */
/* */
/* History */
/* ------- */
/* 06xx89 joh First Release */
/* 060591 joh delinting */
/* */
/*_begin */
/************************************************************************/
/* */
/* Title: IOC network flow control module */
/* File: atcs:[ca]flow_control.c */
/* Environment: VMS. UNIX, VRTX */
/* Equipment: VAX, SUN, VME */
/* */
/* */
/* Purpose */
/* ------- */
/* */
/* ioc flow control module */
/* */
/* */
/* Special comments */
/* ------- -------- */
/* */
/************************************************************************/
/*_end */
static char *sccsId = "@(#) $Id$";
#include "iocinf.h"
/*
* FLOW CONTROL
*
* Keep track of how many times messages have
* come with out a break in between and
* suppress monitors if we are behind
* (an update is sent when we catch up)
*/
void flow_control_on(struct ioc_in_use *piiu)
{
int status;
CA_STATIC *ca_static;
ca_static = piiu->pcas;
LOCK;
/*
* I prefer to avoid going into flow control
* as this impacts the performance of batched fetches
*/
if (piiu->contiguous_msg_count >= MAX_CONTIGUOUS_MSG_COUNT) {
if (!piiu->client_busy) {
status = ca_busy_message(piiu);
if (status==ECA_NORMAL) {
assert(ca_static->ca_number_iiu_in_fc<UINT_MAX);
ca_static->ca_number_iiu_in_fc++;
piiu->client_busy = TRUE;
# if defined(DEBUG)
printf("fc on\n");
# endif
}
}
}
else {
piiu->contiguous_msg_count++;
}
UNLOCK;
return;
}
void flow_control_off(struct ioc_in_use *piiu)
{
int status;
CA_STATIC *ca_static;
ca_static = piiu->pcas;
LOCK;
piiu->contiguous_msg_count = 0;
if (piiu->client_busy) {
status = ca_ready_message(piiu);
if (status==ECA_NORMAL) {
assert(ca_static->ca_number_iiu_in_fc>0u);
ca_static->ca_number_iiu_in_fc--;
piiu->client_busy = FALSE;
# if defined(DEBUG)
printf("fc off\n");
# endif
}
}
UNLOCK;
return;
}

76
src/ca/flow_control.cpp Normal file
View File

@@ -0,0 +1,76 @@
/* $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"
/*
* FLOW CONTROL
*
* Keep track of how many times messages have
* come with out a break in between and
* suppress monitors if we are behind
* (an update is sent when we catch up)
*/
void flow_control_on (tcpiiu *piiu)
{
int status;
LOCK (piiu->niiu.iiu.pcas);
/*
* I prefer to avoid going into flow control
* as this impacts the performance of batched fetches
*/
if (piiu->contiguous_msg_count >= MAX_CONTIGUOUS_MSG_COUNT) {
if (!piiu->client_busy) {
status = ca_busy_message(piiu);
if (status==ECA_NORMAL) {
assert(piiu->niiu.iiu.pcas->ca_number_iiu_in_fc<UINT_MAX);
piiu->niiu.iiu.pcas->ca_number_iiu_in_fc++;
piiu->client_busy = TRUE;
# if defined(DEBUG)
printf("fc on\n");
# endif
}
}
}
else {
piiu->contiguous_msg_count++;
}
UNLOCK (piiu->niiu.iiu.pcas);
return;
}
void flow_control_off (tcpiiu *piiu)
{
int status;
LOCK (piiu->niiu.iiu.pcas);
piiu->contiguous_msg_count = 0;
if (piiu->client_busy) {
status = ca_ready_message(piiu);
if (status==ECA_NORMAL) {
assert (piiu->niiu.iiu.pcas->ca_number_iiu_in_fc>0u);
piiu->niiu.iiu.pcas->ca_number_iiu_in_fc--;
piiu->client_busy = FALSE;
# if defined(DEBUG)
printf("fc off\n");
# endif
}
}
UNLOCK (piiu->niiu.iiu.pcas);
return;
}

View File

@@ -1,326 +0,0 @@
/* if_depen.c */
/* share/src/ca/$Id$ */
/*
* Author: Jeff Hill
* Date: 04-05-94
* 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
*
* Modification Log:
* -----------------
* 8/87 Jeff Hill Init Release
* 072792 Jeff Hill better messages
* 09-DEC-1992 Gerhard Grygiel (GeG) support VMS/UCX
* 050593 Jeff Hill now checks all N interfaces
* (and not N-1 interfaces)
*/
static char *sccsId = "@(#) $Id$";
#include "iocinf.h"
#ifdef DEBUG
#define ifDepenDebugPrintf(argsInParen) printf argsInParen
#else
#define ifDepenDebugPrintf(argsInParen)
#endif
/*
* Dont use ca_static based lock macros here because this is
* also called by the server. All locks required are applied at
* a higher level.
*/
/*
* local_addr()
*
* A valid non-loopback local address is required in the
* beacon message in certain situations where
* there are beacon repeaters and there are addresses
* in the EPICS_CA_ADDRESS_LIST for which we dont have
* a strictly correct local server address on a multi-interface
* system. In this situation we use the first valid non-loopback local
* address found in the beacon message.
*/
int local_addr(int s, struct sockaddr_in *plcladdr)
{
int status;
struct ifconf ifconf;
struct ifreq ifreq[25];
struct ifreq *pifreq;
static struct sockaddr_in addr;
static char init = FALSE;
struct sockaddr_in *tmpaddr;
if (init){
*plcladdr = addr;
return OK;
}
/*
* get the addr of the first interface found
* (report inconsistent interfaces however)
*/
ifconf.ifc_len = sizeof ifreq;
ifconf.ifc_req = ifreq;
status = socket_ioctl(s, SIOCGIFCONF, &ifconf);
if (status < 0 || ifconf.ifc_len == 0) {
ca_printf(
"CAC: SIOCGIFCONF ioctl failed because \"%s\"\n",
SOCKERRSTR(SOCKERRNO));
ifconf.ifc_len = 0;
}
ifDepenDebugPrintf ( ("local_addr: %ld net intf(s) found\n",
(unsigned long) (ifconf.ifc_len/sizeof(*pifreq))) );
for ( pifreq = ifconf.ifc_req;
((size_t)ifconf.ifc_len) >= sizeof(*pifreq);
pifreq++, ifconf.ifc_len -= sizeof(*pifreq)) {
unsigned flags;
status = socket_ioctl(s, SIOCGIFFLAGS, pifreq);
if (status == ERROR){
ca_printf("local_addr: net intf flags fetch for %s failed\n", pifreq->ifr_name);
continue;
}
/*
* flags are stored in a union now
*/
flags = (unsigned) pifreq->ifr_flags;
if (!(flags & IFF_UP)) {
ifDepenDebugPrintf ( ("local_addr: net intf %s was down\n", pifreq->ifr_name) );
continue;
}
/*
* dont use the loop back interface
*/
if (flags & IFF_LOOPBACK) {
ifDepenDebugPrintf ( ("local_addr: ignoring loopback interface: %s\n", pifreq->ifr_name) );
continue;
}
status = socket_ioctl(s, SIOCGIFADDR, pifreq);
if (status == ERROR){
ifDepenDebugPrintf ( ("local_addr: could not obtain addr for %s\n", pifreq->ifr_name) );
continue;
}
if (pifreq->ifr_addr.sa_family != AF_INET){
ifDepenDebugPrintf ( ("local_addr: interface %s was not AF_INET\n", pifreq->ifr_name) );
continue;
}
ifDepenDebugPrintf ( ("local_addr: net intf %s found\n", pifreq->ifr_name) );
tmpaddr = (struct sockaddr_in *) &pifreq->ifr_addr;
init = TRUE;
addr = *tmpaddr;
break;
}
if(!init){
return ERROR;
}
*plcladdr = addr;
return OK;
}
/*
* caDiscoverInterfaces()
*
* This routine is provided with the address of an ELLLIST, a socket
* a destination port number, and a match address. When the
* routine returns there will be one additional inet address
* (a caAddrNode) in the list for each inet interface found that
* is up and isnt a loop back interface (match addr is INADDR_ANY)
* or it matches the specified interface (match addr isnt INADDR_ANY).
* If the interface supports broadcast then I add its broadcast
* address to the list. If the interface is a point to
* point link then I add the destination address of the point to
* point link to the list. In either case I set the port number
* in the address node to the port supplied in the argument
* list.
*
* LOCK should be applied here for (pList)
* (this is also called from the server)
*/
void epicsShareAPI caDiscoverInterfaces
(ELLLIST *pList, int socket, unsigned short port, struct in_addr matchAddr)
{
struct sockaddr_in *pInetAddr;
caAddrNode *pNode;
int status;
struct ifconf ifconf;
struct ifreq *pIfreqList;
struct ifreq *pifreq;
unsigned long nelem;
/*
* use pool so that we avoid using to much stack space
* under vxWorks
*
* nelem is set to the maximum interfaces
* on one machine here
*/
nelem = 100;
pIfreqList = (struct ifreq *)calloc(nelem, sizeof(*pifreq));
if(!pIfreqList){
return;
}
ifconf.ifc_len = nelem*sizeof(*pifreq);
ifconf.ifc_req = pIfreqList;
status = socket_ioctl(socket, SIOCGIFCONF, &ifconf);
if (status < 0 || ifconf.ifc_len == 0) {
free(pIfreqList);
return;
}
nelem = ifconf.ifc_len/sizeof(struct ifreq);
ifDepenDebugPrintf ( ("caDiscoverInterfaces: %ld net intf(s) found\n", nelem) );
for (pifreq = pIfreqList; pifreq<(pIfreqList+nelem); pifreq++) {
unsigned flags;
status = socket_ioctl(socket, SIOCGIFFLAGS, pifreq);
if (status) {
ca_printf ("caDiscoverInterfaces: net intf flags fetch for %s failed\n", pifreq->ifr_name);
continue;
}
/*
* flags are stored in a union now
*/
flags = (unsigned) pifreq->ifr_flags;
/*
* dont bother with interfaces that have been disabled
*/
if (!(flags & IFF_UP)) {
ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s was down\n", pifreq->ifr_name) );
continue;
}
/*
* dont use the loop back interface
*/
if (flags & IFF_LOOPBACK) {
ifDepenDebugPrintf ( ("caDiscoverInterfaces: ignoring loopback interface: %s\n", pifreq->ifr_name) );
continue;
}
/*
* Fetch the local address for this interface
*/
status = socket_ioctl(socket, SIOCGIFADDR, pifreq);
if (status){
ifDepenDebugPrintf ( ("caDiscoverInterfaces: could not obtain addr for %s\n", pifreq->ifr_name) );
continue;
}
/*
* If its not an internet inteface
* then dont use it.
*/
if (pifreq->ifr_addr.sa_family != AF_INET) {
ifDepenDebugPrintf ( ("caDiscoverInterfaces: interface %s was not AF_INET\n", pifreq->ifr_name) );
continue;
}
/*
* save the interface's IP address
*/
pInetAddr = (struct sockaddr_in *)&pifreq->ifr_addr;
/*
* if it isnt a wildcarded interface then look for
* an exact match
*/
if (matchAddr.s_addr != htonl(INADDR_ANY)) {
if (pInetAddr->sin_addr.s_addr != matchAddr.s_addr) {
ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s didnt match\n", pifreq->ifr_name) );
continue;
}
}
/*
* If this is an interface that supports
* broadcast fetch the broadcast address.
*
* Otherwise if this is a point to point
* interface then use the destination address.
*
* Otherwise CA will not query through the
* interface.
*/
if (flags & IFF_BROADCAST) {
status = socket_ioctl(
socket,
SIOCGIFBRDADDR,
pifreq);
if (status) {
ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s: bcast addr fetch fail\n", pifreq->ifr_name) );
continue;
}
}
else if(flags & IFF_POINTOPOINT){
status = socket_ioctl(
socket,
SIOCGIFDSTADDR,
pifreq);
if (status){
ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s: pt to pt addr fetch fail\n", pifreq->ifr_name) );
continue;
}
}
else{
ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s: not pt to pt or bcast\n", pifreq->ifr_name) );
continue;
}
ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s found\n", pifreq->ifr_name) );
pNode = (caAddrNode *) calloc(1,sizeof(*pNode));
if(!pNode){
ca_printf ("caDiscoverInterfaces: malloc failed for net intf %s: \n", pifreq->ifr_name);
continue;
}
pNode->destAddr.in = *pInetAddr;
pNode->destAddr.in.sin_port = htons(port);
/*
* LOCK applied externally
*/
ellAdd(pList, &pNode->node);
}
free(pIfreqList);
}

File diff suppressed because it is too large Load Diff

1570
src/ca/iocinf.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -47,6 +47,9 @@ extern "C" {
#elif (defined(__ALPHA) && defined(VMS) || defined(__alpha)) && defined(VMS)
# define CA_FLOAT_MIT
# define CA_LITTLE_ENDIAN
#elif (defined(__ALPHA) && defined(__VMS) || defined(__alpha)) && defined(__VMS)
# define CA_FLOAT_MIT
# define CA_LITTLE_ENDIAN
#elif (defined(__ALPHA) && defined(UNIX) || defined(__alpha)) && defined(UNIX)
# define CA_FLOAT_IEEE
# define CA_LITTLE_ENDIAN

View File

@@ -59,20 +59,7 @@ static char *os_depenhSccsId = "$Id$";
#error Please define one of iocCore, UNIX, VMS, or _WIN32
#endif
#if defined(iocCore)
# define POST_IO_EV semBinaryGive(io_done_sem)
# define VXTASKIDNONE 0
# define LOCK semMutexMustTake(client_lock);
# define UNLOCK semMutexGive(client_lock);
# define EVENTLOCKTEST (lock_tid==threadGetIdSelf())
# define VXTHISTASKID threadGetIdSelf();
# define abort() threadSuspend(threadGetIdSelf())
#else
# define POST_IO_EV
# define LOCK
# define UNLOCK
# define EVENTLOCKTEST (post_msg_active)
#endif /*defined(iocCore) */
#endif /* INCos_depenh */

114
src/ca/processThread.cpp Normal file
View File

@@ -0,0 +1,114 @@
/*
* $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>
processThread::processThread () :
osiThread ("CAC process", 0x1000, threadPriorityMedium),
shutDown (false)
{
ellInit (&this->recvActivity);
}
processThread::~processThread ()
{
this->shutDown = true;
this->exit.signal ();
while ( !this->exit.wait (5.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 ();
}
this->exit.signal ();
}
void processThread::signalShutDown ()
{
this->shutDown = true;
this->wakeup.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 ();
}

View File

@@ -1,576 +0,0 @@
/*
* $Id$
*
* REPEATER.C
*
* CA broadcast repeater
*
* Author: Jeff Hill
* Date: 3-27-90
*
* Control System Software for the GTA Project
*
* Copyright 1988, 1989, the Regents of the University of California.
*
* This software was produced under a U.S. Government contract
* (W-7405-ENG-36) at the Los Alamos National Laboratory, which is
* operated by the University of California for the U.S. Department
* of Energy.
*
* Developed by the Controls and Automation Group (AT-8)
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Direct inqueries to:
* Jeff HIll, AT-8, Mail Stop H820
* Los Alamos National Laboratory
* Los Alamos, New Mexico 87545
* Phone: (505) 665-1831
* E-mail: johill@lanl.gov
*
* PURPOSE:
* Broadcasts fan out over the LAN, but old IP kernels do not allow
* two processes on the same machine to get the same broadcast
* (and modern IP kernels do not allow two processes on the same machine
* to receive the same unicast).
*
* This code fans out UDP messages sent to the CA repeater port
* to all CA client processes that have subscribed.
*
*
* Modification Log:
* -----------------
* .01 060691 joh Took out 4 byte count at message begin to
* in preparation for SPARC alignment
* .02 042892 joh No longer checking the status from free
* since it varies from OS to OS
* .03 042892 joh made local routines static
* .04 072392 joh set reuse addr socket option so that
* the repeater will restart if it gets killed
* .05 072392 joh no longer needs to loop waiting for the timeout
* to expire because of the change introduced
* in .04
* .06 120492 joh removed unnecessary includes
* .07 120992 joh now uses dll list routines
* .08 102993 joh toggle set sock opt to set
* .09 070195 joh discover client has vanished by connecting its
* datagram socket (and watching for ECONNREFUSED)
*
* $Log$
* Revision 1.48 1999/09/02 21:44:50 jhill
* improved the way that socket error numbers are converted to strings,
* changed () to (void) in func proto, and fixed missing parameter to
* checkConnWatchdogs() bug resulting from this
*
* Revision 1.47 1998/09/29 20:50:37 jhill
* more robust in situations wherelocal IP cant be determined
*
* Revision 1.46 1998/09/24 21:22:54 jhill
* conn.c
*
* Revision 1.45 1998/06/16 00:58:12 jhill
* attach to winsock when its a static build
*
* Revision 1.44 1998/05/29 00:03:20 jhill
* allow CA to run systems w/o local interface query capabilities (ie cygwin32)
*
* Revision 1.43 1998/04/15 21:58:29 jhill
* fixed the doc
*
* Revision 1.42 1998/02/27 01:04:03 jhill
* fixed benign WIN32 message from overwritten errno
*
* Revision 1.41 1998/02/05 22:34:33 jhill
* dont delete client if send returns ECONNREFUSED
*
* Revision 1.40 1997/08/04 23:37:15 jhill
* added beacon anomaly flag init/allow ip 255.255.255.255
*
* Revision 1.39 1997/04/23 17:05:09 jhill
* pc port changes
*
* Revision 1.38 1996/11/02 00:51:04 jhill
* many pc port, const in API, and other changes
*
* Revision 1.37 1996/09/04 20:02:32 jhill
* fixed gcc warning
*
* Revision 1.36 1996/07/12 00:40:48 jhill
* fixed client disconnect problem under solaris
*
* Revision 1.32.6.1 1996/07/12 00:39:59 jhill
* fixed client disconnect problem under solaris
*
* Revision 1.35 1996/07/10 23:30:11 jhill
* fixed GNU warnings
*
* Revision 1.34 1996/06/19 17:59:24 jhill
* many 3.13 beta changes
*
* Revision 1.33 1995/11/29 19:19:05 jhill
* Added $log$
*
*/
/*
* It would be preferable to avoid using the repeater on multicast enhanced IP kernels, but
* this is not going to work in all situations because (according to Steven's TCP/IP
* illustrated volume I) if a broadcast is received it goes to all sockets on the same port,
* but if a unicast is received it goes to only one of the sockets on the same port
* (we can only guess at which one it will be).
*
* I have observed this behavior under winsock II:
* o only one of the sockets on the same port receives the message if we send to the
* loop back address
* o both of the sockets on the same port receives the message if we send to the
* broadcast address
*
*/
static char *sccsId = "@(#)$Id$";
#include "iocinf.h"
#include "bsdSocketResource.h"
/*
* one socket per client so we will get the ECONNREFUSED
* error code (and then delete the client)
*/
struct one_client{
ELLNODE node;
struct sockaddr_in from;
SOCKET sock;
};
/*
* these can be external since there is only one instance
* per machine so we dont care about reentrancy
*/
static ELLLIST client_list;
static char buf[ETHERNET_MAX_UDP];
#define PORT_ANY 0U
typedef struct {
SOCKET sock;
int errNumber;
const char *pErrStr;
}makeSocketReturn;
LOCAL void register_new_client(struct sockaddr_in *pLocal,
struct sockaddr_in *pFrom);
LOCAL void verifyClients();
LOCAL makeSocketReturn makeSocket (unsigned short port, int reuseAddr);
LOCAL void fanOut(struct sockaddr_in *pFrom, const char *pMsg, unsigned msgSize);
/*
*
* ca_repeater()
*
*
*/
void epicsShareAPI ca_repeater()
{
int status;
int size;
SOCKET sock;
struct sockaddr_in from;
struct sockaddr_in local;
int from_size = sizeof from;
unsigned short port;
makeSocketReturn msr;
assert (bsdSockAttach());
port = caFetchPortConfig(
&EPICS_CA_REPEATER_PORT,
CA_REPEATER_PORT);
ellInit(&client_list);
msr = makeSocket(port, TRUE);
if (msr.sock==INVALID_SOCKET) {
/*
* test for server was already started
*/
if (msr.errNumber==SOCK_EADDRINUSE) {
bsdSockRelease();
exit(0);
}
ca_printf("%s: Unable to create repeater socket because %d=\"%s\" - fatal\n",
__FILE__,
msr.errNumber,
msr.pErrStr);
bsdSockRelease();
exit(0);
}
sock = msr.sock;
status = local_addr (sock, &local);
if(status != OK){
/*
* 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 clients or later
*/
local.sin_family = AF_INET;
local.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
local.sin_port = htons (0);
}
#ifdef DEBUG
ca_printf("CA Repeater: Attached and initialized\n");
#endif
while(TRUE){
caHdr *pMsg;
size = recvfrom(
sock,
buf,
sizeof(buf),
0,
(struct sockaddr *)&from,
&from_size);
if(size < 0){
int errnoCpy = SOCKERRNO;
# ifdef linux
/*
* Avoid spurious ECONNREFUSED bug
* in linux
*/
if (errnoCpy==SOCK_ECONNREFUSED) {
continue;
}
# endif
ca_printf("CA Repeater: unexpected UDP recv err: %s\n",
SOCKERRSTR(errnoCpy));
continue;
}
pMsg = (caHdr *) buf;
/*
* both zero length message and a registration message
* will register a new client
*/
if( ((size_t)size) >= sizeof(*pMsg)){
if(ntohs(pMsg->m_cmmd) == REPEATER_REGISTER){
register_new_client(&local, &from);
/*
* strip register client message
*/
pMsg++;
size -= sizeof(*pMsg);
if (size==0) {
continue;
}
}
}
else if(size == 0){
register_new_client(&local, &from);
continue;
}
fanOut(&from, (char *) pMsg, size);
}
}
/*
* fanOut()
*/
LOCAL void fanOut(struct sockaddr_in *pFrom, const char *pMsg, unsigned msgSize)
{
ELLLIST theClients;
struct one_client *pclient;
int status;
int verify = FALSE;
ellInit(&theClients);
while ( (pclient=(struct one_client *)ellGet(&client_list)) ) {
ellAdd(&theClients, &pclient->node);
/*
* Dont reflect back to sender
*/
if(pFrom->sin_port == pclient->from.sin_port &&
pFrom->sin_addr.s_addr == pclient->from.sin_addr.s_addr){
continue;
}
status = send(
pclient->sock,
(char *)pMsg,
msgSize,
0);
if (status>=0) {
#ifdef DEBUG
ca_printf ("Sent to %d\n",
ntohs (pclient->from.sin_port));
#endif
}
if(status < 0){
int errnoCpy = SOCKERRNO;
if (errnoCpy == SOCK_ECONNREFUSED) {
#ifdef DEBUG
ca_printf ("Deleted client %d\n",
ntohs (pclient->from.sin_port));
#endif
verify = TRUE;
}
else {
ca_printf(
"CA Repeater: UDP fan out err was \"%s\"\n",
SOCKERRSTR(errnoCpy));
}
}
}
ellConcat(&client_list, &theClients);
if (verify) {
verifyClients ();
}
}
/*
* verifyClients()
* (this is required because solaris has a half baked version of sockets)
*/
LOCAL void verifyClients()
{
ELLLIST theClients;
struct one_client *pclient;
makeSocketReturn msr;
ellInit(&theClients);
while ( (pclient=(struct one_client *)ellGet(&client_list)) ) {
ellAdd(&theClients, &pclient->node);
msr = makeSocket(ntohs(pclient->from.sin_port), FALSE);
if (msr.sock!=INVALID_SOCKET) {
#ifdef DEBUG
ca_printf("Deleted client %d\n",
ntohs(pclient->from.sin_port));
#endif
ellDelete(&theClients, &pclient->node);
socket_close(msr.sock);
socket_close(pclient->sock);
free(pclient);
}
else {
/*
* win sock does not set SOCKERRNO when this fails
*/
if (msr.errNumber!=SOCK_EADDRINUSE) {
ca_printf(
"CA Repeater: bind test err was %d=\"%s\"\n",
msr.errNumber, msr.pErrStr);
}
}
}
ellConcat(&client_list, &theClients);
}
/*
* makeSocket()
*/
LOCAL makeSocketReturn makeSocket(unsigned short port, int reuseAddr)
{
int status;
struct sockaddr_in bd;
makeSocketReturn msr;
int true = 1;
msr.sock = socket( AF_INET, /* domain */
SOCK_DGRAM, /* type */
0); /* deflt proto */
if (msr.sock == INVALID_SOCKET) {
msr.errNumber = SOCKERRNO;
msr.pErrStr = SOCKERRSTR(msr.errNumber);
return msr;
}
/*
* no need to bind if unconstrained
*/
if (port != PORT_ANY) {
memset((char *)&bd, 0, sizeof(bd));
bd.sin_family = AF_INET;
bd.sin_addr.s_addr = htonl(INADDR_ANY);
bd.sin_port = htons(port);
status = bind(msr.sock, (struct sockaddr *)&bd, (int)sizeof(bd));
if (status<0) {
msr.errNumber = SOCKERRNO;
msr.pErrStr = SOCKERRSTR(msr.errNumber);
socket_close(msr.sock);
msr.sock = INVALID_SOCKET;
return msr;
}
if (reuseAddr) {
status = setsockopt(
msr.sock,
SOL_SOCKET,
SO_REUSEADDR,
(char *)&true,
sizeof(true));
if (status<0) {
int errnoCpy = SOCKERRNO;
ca_printf(
"%s: set socket option failed because \"%s\"\n",
__FILE__, SOCKERRSTR(errnoCpy));
}
}
}
msr.errNumber = 0;
msr.pErrStr = "no error";
return msr;
}
/*
* register_new_client()
*/
LOCAL void register_new_client(
struct sockaddr_in *pLocal,
struct sockaddr_in *pFrom)
{
int status;
struct one_client *pclient;
caHdr confirm;
caHdr noop;
int newClient = FALSE;
makeSocketReturn msr;
if (pFrom->sin_family != AF_INET) {
return;
}
/*
* the repeater and its clients must be on the same host
*/
if (htonl(INADDR_LOOPBACK) != pFrom->sin_addr.s_addr) {
/*
* Unfortunately on 3.13 beta 11 and before the
* repeater would not always allow the loopback address
* as a local client address so all clients must continue to
* use the address from the first non-loopback interface
* found to communicate with the CA repeater until all
* CA repeaters have been restarted.
*/
if (pLocal->sin_addr.s_addr != pFrom->sin_addr.s_addr) {
return;
}
}
for(pclient = (struct one_client *) ellFirst(&client_list);
pclient; pclient = (struct one_client *) ellNext(&pclient->node)){
if (pFrom->sin_port == pclient->from.sin_port) {
break;
}
}
if (!pclient) {
pclient = (struct one_client *)calloc (1, sizeof(*pclient));
if (!pclient) {
ca_printf("%s: no memory for new client\n",
__FILE__);
return;
}
msr = makeSocket(PORT_ANY, FALSE);
if (msr.sock==INVALID_SOCKET) {
free(pclient);
ca_printf("%s: no client sock because %d=\"%s\"\n",
__FILE__,
msr.errNumber,
msr.pErrStr);
return;
}
pclient->sock = msr.sock;
status = connect(pclient->sock,
(struct sockaddr *)pFrom,
sizeof(*pFrom));
if (status<0) {
int errnoCpy = SOCKERRNO;
ca_printf(
"%s: unable to connect client sock because \"%s\"\n",
__FILE__, SOCKERRSTR(errnoCpy));
socket_close(pclient->sock);
free(pclient);
return;
}
pclient->from = *pFrom;
ellAdd (&client_list, &pclient->node);
newClient = TRUE;
#ifdef DEBUG
ca_printf (
"Added %d\n",
ntohs(pFrom->sin_port));
#endif
}
memset((char *)&confirm, '\0', sizeof(confirm));
confirm.m_cmmd = htons(REPEATER_CONFIRM);
confirm.m_available = pFrom->sin_addr.s_addr;
status = send(
pclient->sock,
(char *)&confirm,
sizeof(confirm),
0);
if (status >= 0) {
assert(status == sizeof(confirm));
}
else if (SOCKERRNO == SOCK_ECONNREFUSED){
#ifdef DEBUG
ca_printf("Deleted repeater client=%d sending ack\n",
ntohs(pFrom->sin_port));
#endif
ellDelete(&client_list, &pclient->node);
socket_close(pclient->sock);
free(pclient);
}
else {
ca_printf("CA Repeater: confirm err was \"%s\"\n",
SOCKERRSTR(SOCKERRNO));
}
/*
* send a noop message to all other clients so that we dont
* accumulate sockets when there are no beacons
*/
memset((char *)&noop, '\0', sizeof(noop));
confirm.m_cmmd = htons(CA_PROTO_NOOP);
fanOut(pFrom, (char *)&noop, sizeof(noop));
if (newClient) {
/*
* on solaris we need to verify that the clients
* have not gone away (because ICMP does not
* get through to send()
*
* this is done each time that a new client is
* created
*
* this is done here in order to avoid deleting
* a client prior to sending its confirm message
*/
verifyClients();
}
}

490
src/ca/repeater.cpp Normal file
View File

@@ -0,0 +1,490 @@
/*
* $Id$
*
* REPEATER.C
*
* CA broadcast repeater
*
* Author: Jeff Hill
* Date: 3-27-90
*
* Control System Software for the GTA Project
*
* Copyright 1988, 1989, the Regents of the University of California.
*
* This software was produced under a U.S. Government contract
* (W-7405-ENG-36) at the Los Alamos National Laboratory, which is
* operated by the University of California for the U.S. Department
* of Energy.
*
* Developed by the Controls and Automation Group (AT-8)
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Direct inqueries to:
* Jeff HIll, AT-8, Mail Stop H820
* Los Alamos National Laboratory
* Los Alamos, New Mexico 87545
* Phone: (505) 665-1831
* E-mail: johill@lanl.gov
*
* PURPOSE:
* Broadcasts fan out over the LAN, but old IP kernels do not allow
* two processes on the same machine to get the same broadcast
* (and modern IP kernels do not allow two processes on the same machine
* to receive the same unicast).
*
* This code fans out UDP messages sent to the CA repeater port
* to all CA client processes that have subscribed.
*
*/
/*
* It would be preferable to avoid using the repeater on multicast enhanced IP kernels, but
* this is not going to work in all situations because (according to Steven's TCP/IP
* illustrated volume I) if a broadcast is received it goes to all sockets on the same port,
* but if a unicast is received it goes to only one of the sockets on the same port
* (we can only guess at which one it will be).
*
* I have observed this behavior under winsock II:
* o only one of the sockets on the same port receives the message if we send to the
* loop back address
* o both of the sockets on the same port receives the message if we send to the
* broadcast address
*
*/
#include "iocinf.h"
#include "taskwd.h"
/*
* one socket per client so we will get the ECONNREFUSED
* error code (and then delete the client)
*/
struct one_client {
ELLNODE node;
struct sockaddr_in from;
SOCKET sock;
};
/*
* these can be external since there is only one instance
* per machine so we dont care about reentrancy
*/
static ELLLIST client_list;
static char buf[ETHERNET_MAX_UDP];
static const unsigned short PORT_ANY = 0u;
typedef struct {
SOCKET sock;
int errNumber;
const char *pErrStr;
} makeSocketReturn;
/*
* makeSocket()
*/
LOCAL makeSocketReturn makeSocket (unsigned short port, int reuseAddr)
{
int status;
struct sockaddr_in bd;
makeSocketReturn msr;
int flag;
msr.sock = socket (AF_INET, SOCK_DGRAM, 0);
if (msr.sock == INVALID_SOCKET) {
msr.errNumber = SOCKERRNO;
msr.pErrStr = SOCKERRSTR (msr.errNumber);
return msr;
}
/*
* no need to bind if unconstrained
*/
if (port != PORT_ANY) {
memset ( (char *) &bd, 0, sizeof (bd) );
bd.sin_family = AF_INET;
bd.sin_addr.s_addr = htonl (INADDR_ANY);
bd.sin_port = htons (port);
status = bind (msr.sock, (struct sockaddr *)&bd, (int)sizeof(bd));
if (status<0) {
msr.errNumber = SOCKERRNO;
msr.pErrStr = SOCKERRSTR (msr.errNumber);
socket_close (msr.sock);
msr.sock = INVALID_SOCKET;
return msr;
}
if (reuseAddr) {
flag = TRUE;
status = setsockopt ( msr.sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&flag, sizeof (flag) );
if (status<0) {
int errnoCpy = SOCKERRNO;
errlogPrintf(
"%s: set socket option failed because \"%s\"\n",
__FILE__, SOCKERRSTR(errnoCpy));
}
}
}
msr.errNumber = 0;
msr.pErrStr = "no error";
return msr;
}
/*
* verifyClients()
* (this is required because solaris has a half baked version of sockets)
*/
LOCAL void verifyClients()
{
ELLLIST theClients;
struct one_client *pclient;
makeSocketReturn msr;
ellInit(&theClients);
while ( (pclient=(struct one_client *)ellGet(&client_list)) ) {
ellAdd (&theClients, &pclient->node);
msr = makeSocket ( ntohs (pclient->from.sin_port), FALSE );
if ( msr.sock != INVALID_SOCKET ) {
#ifdef DEBUG
errlogPrintf ("Deleted client %d\n",
ntohs (pclient->from.sin_port) );
#endif
ellDelete (&theClients, &pclient->node);
socket_close (msr.sock);
socket_close (pclient->sock);
free (pclient);
}
else {
/*
* win sock does not set SOCKERRNO when this fails
*/
if ( msr.errNumber != SOCK_EADDRINUSE ) {
errlogPrintf (
"CA Repeater: bind test err was %d=\"%s\"\n",
msr.errNumber, msr.pErrStr);
}
}
}
ellConcat (&client_list, &theClients);
}
/*
* fanOut()
*/
LOCAL void fanOut (struct sockaddr_in *pFrom, const char *pMsg, unsigned msgSize)
{
ELLLIST theClients;
struct one_client *pclient;
int status;
int verify = FALSE;
ellInit(&theClients);
while ( ( pclient = (struct one_client *) ellGet (&client_list) ) ) {
ellAdd(&theClients, &pclient->node);
/*
* Dont reflect back to sender
*/
if(pFrom->sin_port == pclient->from.sin_port &&
pFrom->sin_addr.s_addr == pclient->from.sin_addr.s_addr){
continue;
}
status = send ( pclient->sock, (char *)pMsg, msgSize, 0);
if (status>=0) {
#ifdef DEBUG
errlogPrintf ("Sent to %d\n",
ntohs (pclient->from.sin_port));
#endif
}
if(status < 0){
int errnoCpy = SOCKERRNO;
if (errnoCpy == SOCK_ECONNREFUSED) {
#ifdef DEBUG
errlogPrintf ("Deleted client %d\n",
ntohs (pclient->from.sin_port));
#endif
verify = TRUE;
}
else {
errlogPrintf(
"CA Repeater: UDP fan out err was \"%s\"\n",
SOCKERRSTR(errnoCpy));
}
}
}
ellConcat(&client_list, &theClients);
if (verify) {
verifyClients ();
}
}
/*
* register_new_client()
*/
LOCAL void register_new_client (struct sockaddr_in *pFrom)
{
int status;
struct one_client *pclient;
caHdr confirm;
caHdr noop;
int newClient = FALSE;
makeSocketReturn msr;
if (pFrom->sin_family != AF_INET) {
return;
}
/*
* the repeater and its clients must be on the same host
*/
if (htonl(INADDR_LOOPBACK) != pFrom->sin_addr.s_addr) {
static SOCKET testSock = INVALID_SOCKET;
static int init;
struct sockaddr_in ina;
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",
__FILE__, msr.errNumber, msr.pErrStr);
}
else {
testSock = msr.sock;
}
init = TRUE;
}
/*
* Unfortunately on 3.13 beta 11 and before the
* repeater would not always allow the loopback address
* as a local client address so current clients alternate
* between the address of the first non-loopback interface
* found and the loopback addresss when subscribing with
* the CA repeater until all CA repeaters have been updated
* to current code.
*/
if ( testSock != INVALID_SOCKET ) {
ina = *pFrom;
ina.sin_port = PORT_ANY;
/* we can only bind to a local address */
status = bind ( testSock, (struct sockaddr *)&ina, (int) sizeof(ina) );
if (status) {
return;
}
}
else {
return;
}
}
for (pclient = (struct one_client *) ellFirst (&client_list);
pclient; pclient = (struct one_client *) ellNext (&pclient->node)){
if (pFrom->sin_port == pclient->from.sin_port) {
break;
}
}
if (!pclient) {
pclient = (struct one_client *) calloc (1, sizeof(*pclient));
if (!pclient) {
errlogPrintf ("%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",
__FILE__, msr.errNumber, msr.pErrStr);
return;
}
pclient->sock = msr.sock;
status = connect ( pclient->sock,
(struct sockaddr *) pFrom,
sizeof (*pFrom) );
if (status<0) {
int errnoCpy = SOCKERRNO;
errlogPrintf (
"%s: unable to connect client sock because \"%s\"\n",
__FILE__, SOCKERRSTR(errnoCpy));
socket_close (pclient->sock);
free (pclient);
return;
}
pclient->from = *pFrom;
ellAdd (&client_list, &pclient->node);
newClient = TRUE;
#ifdef DEBUG
errlogPrintf ( "Added %d\n", ntohs (pFrom->sin_port) );
#endif
}
memset ( (char *) &confirm, '\0', sizeof (confirm) );
confirm.m_cmmd = htons (REPEATER_CONFIRM);
confirm.m_available = pFrom->sin_addr.s_addr;
status = send(
pclient->sock,
(char *)&confirm,
sizeof(confirm),
0);
if (status >= 0) {
assert(status == sizeof(confirm));
}
else if (SOCKERRNO == SOCK_ECONNREFUSED){
#ifdef DEBUG
errlogPrintf ("Deleted repeater client=%d sending ack\n",
ntohs (pFrom->sin_port) );
#endif
ellDelete (&client_list, &pclient->node);
socket_close (pclient->sock);
free (pclient);
}
else {
errlogPrintf ("CA Repeater: confirm err was \"%s\"\n",
SOCKERRSTR (SOCKERRNO) );
}
/*
* send a noop message to all other clients so that we dont
* accumulate sockets when there are no beacons
*/
memset ( (char *) &noop, '\0', sizeof (noop) );
confirm.m_cmmd = htons (CA_PROTO_NOOP);
fanOut ( pFrom, (char *)&noop, sizeof (noop) );
if (newClient) {
/*
* on solaris we need to verify that the clients
* have not gone away (because ICMP does not
* get through to send()
*
* this is done each time that a new client is
* created
*
* this is done here in order to avoid deleting
* a client prior to sending its confirm message
*/
verifyClients ();
}
}
/*
* ca_repeater()
*/
void epicsShareAPI ca_repeater ()
{
int size;
SOCKET sock;
struct sockaddr_in from;
int from_size = sizeof from;
unsigned short port;
makeSocketReturn msr;
assert (bsdSockAttach());
port = caFetchPortConfig ( NULL, &EPICS_CA_REPEATER_PORT, CA_REPEATER_PORT );
ellInit(&client_list);
msr = makeSocket (port, TRUE);
if ( msr.sock == INVALID_SOCKET ) {
/*
* test for server was already started
*/
if ( msr.errNumber == SOCK_EADDRINUSE ) {
bsdSockRelease ();
exit (0);
}
errlogPrintf("%s: Unable to create repeater socket because %d=\"%s\" - fatal\n",
__FILE__, msr.errNumber, msr.pErrStr);
bsdSockRelease ();
exit(0);
}
sock = msr.sock;
#ifdef DEBUG
errlogPrintf ("CA Repeater: Attached and initialized\n");
#endif
while (TRUE) {
caHdr *pMsg;
size = recvfrom(
sock,
buf,
sizeof(buf),
0,
(struct sockaddr *)&from,
&from_size);
if(size < 0){
int errnoCpy = SOCKERRNO;
# ifdef linux
/*
* Avoid spurious ECONNREFUSED bug
* in linux
*/
if (errnoCpy==SOCK_ECONNREFUSED) {
continue;
}
# endif
errlogPrintf ("CA Repeater: unexpected UDP recv err: %s\n",
SOCKERRSTR(errnoCpy));
continue;
}
pMsg = (caHdr *) buf;
/*
* both zero length message and a registration message
* will register a new client
*/
if ( ( (size_t) size) >= sizeof (*pMsg) ) {
if ( ntohs(pMsg->m_cmmd) == REPEATER_REGISTER ) {
register_new_client (&from);
/*
* strip register client message
*/
pMsg++;
size -= sizeof (*pMsg);
if (size==0) {
continue;
}
}
}
else if (size == 0) {
register_new_client (&from);
continue;
}
fanOut (&from, (char *) pMsg, size);
}
}
/*
* caRepeaterThread ()
*/
void caRepeaterThread (void *pDummy)
{
taskwdInsert (threadGetIdSelf(), NULL, NULL);
ca_repeater();
}

View File

@@ -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"
repeaterSubscribeTimer::repeaterSubscribeTimer (udpiiu &iiuIn, osiTimerQueue &queueIn) :
osiTimer (queueIn), iiu (iiuIn)
{
}
void repeaterSubscribeTimer::expire ()
{
this->iiu.contactRepeater = 1u;
semBinaryGive (this->iiu.xmitSignal);
}
void repeaterSubscribeTimer::destroy ()
{
}
bool repeaterSubscribeTimer::again () const
{
if (this->iiu.repeaterContacted) {
return false;
}
else {
return true;
}
}
double repeaterSubscribeTimer::delay () const
{
return REPEATER_TRY_PERIOD;
}
void repeaterSubscribeTimer::show (unsigned level) const
{
}
const char *repeaterSubscribeTimer::name () const
{
return "repeaterSubscribeTimer";
}

461
src/ca/ringBuffer.cpp Normal file
View File

@@ -0,0 +1,461 @@
/*
* $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 <string.h>
#include "ringBuffer.h"
static const unsigned ringIndexMask = nElementsInRing-1;
/*
* cacRingBufferConstruct ()
*/
int cacRingBufferConstruct (ringBuffer *pBuf)
{
pBuf->shutDown = 0u;
pBuf->rdix = 0u;
pBuf->wtix = 1u;
pBuf->readSignal = semBinaryCreate (semEmpty);
if (!pBuf->readSignal) {
return -1;
}
pBuf->writeSignal = semBinaryCreate (semEmpty);
if (!pBuf->writeSignal) {
semBinaryDestroy (pBuf->readSignal);
return -1;
}
pBuf->readLock = semMutexCreate ();
if (!pBuf->readLock) {
semBinaryDestroy (pBuf->readSignal);
semBinaryDestroy (pBuf->writeSignal);
return -1;
}
pBuf->writeLock = semMutexCreate ();
if (!pBuf->writeLock) {
semBinaryDestroy (pBuf->readSignal);
semBinaryDestroy (pBuf->writeSignal);
semMutexDestroy (pBuf->readLock);
return -1;
}
return 0;
}
/*
* cacRingBufferDestroy ()
*/
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);
semMutexDestroy (pBuf->writeLock);
}
/*
* cacRingBufferShutDown ();
*/
void cacRingBufferShutDown (ringBuffer *pBuf)
{
pBuf->shutDown = 1u;
semBinaryGive (pBuf->readSignal);
semBinaryGive (pBuf->writeSignal);
}
/*
* cacRingBufferReadSize ()
*/
static inline unsigned cacRingBufferReadSize (ringBuffer *pBuf)
{
unsigned long count;
if ( pBuf->wtix <= pBuf->rdix ) {
static const unsigned bufSizeM1 = sizeof (pBuf->buf) - 1u;
count = ( bufSizeM1 - pBuf->rdix ) + pBuf->wtix;
}
else {
count = (pBuf->wtix - pBuf->rdix) - 1u;
}
return count;
}
/*
* cacRingBufferContiguousReadSize ()
*/
static inline unsigned cacRingBufferContiguousReadSize (ringBuffer *pBuf)
{
static const unsigned bufSizeM1 = sizeof (pBuf->buf) - 1u;
unsigned long count;
if ( pBuf->wtix <= pBuf->rdix ) {
if (pBuf->rdix==bufSizeM1) {
count = pBuf->wtix;
}
else {
count = bufSizeM1 - pBuf->rdix;
}
}
else {
count = (pBuf->wtix - pBuf->rdix) - 1u;
}
return count;
}
/*
* cacRingBufferWriteSize ()
*/
static inline unsigned cacRingBufferWriteSize (ringBuffer *pBuf)
{
unsigned long count;
if (pBuf->wtix <= pBuf->rdix) {
count = pBuf->rdix - pBuf->wtix;
}
else {
count = ( sizeof (pBuf->buf) - pBuf->wtix ) + pBuf->rdix;
}
return count;
}
/*
* cacRingBufferContiguousWriteSize ()
*/
static inline unsigned cacRingBufferContiguousWriteSize (ringBuffer *pBuf)
{
unsigned long count;
if (pBuf->wtix <= pBuf->rdix) {
count = pBuf->rdix - pBuf->wtix;
}
else {
count = sizeof (pBuf->buf) - pBuf->wtix;
}
return count;
}
/*
* cacRingBufferReadPartial ()
*
* returns the number of bytes read which may be less than
* the number requested.
*/
static inline unsigned cacRingBufferReadPartial (ringBuffer *pRing, void *pBuf,
unsigned nBytes)
{
unsigned totalBytes;
if ( pRing->wtix < pRing->rdix ) {
static const unsigned bufSizeM1 = sizeof (pRing->buf) - 1u;
unsigned nBytesAvail1stBlock, nBytesAvail2ndBlock;
nBytesAvail1stBlock = bufSizeM1 - pRing->rdix;
nBytesAvail2ndBlock = pRing->wtix;
if ( nBytesAvail1stBlock >= nBytes ) {
totalBytes = nBytes;
memcpy ( pBuf, pRing->buf + pRing->rdix + 1u, totalBytes );
}
else {
char *pChar = (char *) pBuf;
memcpy ( pBuf, pRing->buf + pRing->rdix + 1u, nBytesAvail1stBlock );
nBytes -= nBytesAvail1stBlock;
if ( nBytesAvail2ndBlock > nBytes ) {
nBytesAvail2ndBlock = nBytes;
}
memcpy ( pChar + nBytesAvail1stBlock, pRing->buf, nBytesAvail2ndBlock);
totalBytes = nBytesAvail1stBlock + nBytesAvail2ndBlock;
}
pRing->rdix += totalBytes;
pRing->rdix &= ringIndexMask;
}
else if ( pRing->wtix > pRing->rdix ) {
totalBytes = (pRing->wtix - pRing->rdix) - 1;
if ( totalBytes > nBytes ) {
totalBytes = nBytes;
}
memcpy (pBuf, pRing->buf+pRing->rdix+1, totalBytes);
pRing->rdix += totalBytes;
pRing->rdix &= ringIndexMask;
}
else {
totalBytes = 0;
}
return totalBytes;
}
/*
* cacRingBufferRead ()
*
* returns the number of bytes read which may be less than
* the number requested.
*/
unsigned cacRingBufferRead (ringBuffer *pRing, void *pBuf,
unsigned nBytes)
{
unsigned char *pBufTmp = (unsigned char *) pBuf;
unsigned totalBytes = 0;
unsigned curBytes;
semMutexMustTake (pRing->readLock);
while (totalBytes<nBytes) {
curBytes = cacRingBufferReadPartial (pRing,
pBufTmp+totalBytes, nBytes-totalBytes);
if (curBytes==0) {
semBinaryMustTake (pRing->readSignal);
if (pRing->shutDown) {
semMutexGive (pRing->readLock);
return totalBytes;
}
}
else {
totalBytes += curBytes;
}
}
semMutexGive (pRing->readLock);
return totalBytes;
}
/*
* cacRingBufferWritePartial ()
*
* returns the number of bytes written which may be less than
* the number requested.
*/
static inline unsigned cacRingBufferWritePartial (ringBuffer *pRing,
const void *pBuf, unsigned nBytes)
{
unsigned totalBytes;
if ( pRing->wtix < pRing->rdix ) {
totalBytes = pRing->rdix - pRing->wtix;
if ( totalBytes > nBytes ) {
totalBytes = nBytes;
}
memcpy (pRing->buf+pRing->wtix, pBuf, totalBytes);
pRing->wtix += totalBytes;
pRing->wtix &= ringIndexMask;
}
else if ( pRing->wtix > pRing->rdix ) {
unsigned nBytesAvail1stBlock, nBytesAvail2ndBlock;
nBytesAvail1stBlock = sizeof (pRing->buf) - pRing->wtix;
nBytesAvail2ndBlock = pRing->rdix;
if ( nBytesAvail1stBlock >= nBytes ) {
totalBytes = nBytes;
memcpy ( pRing->buf+pRing->wtix, pBuf, totalBytes );
}
else {
char *pChar = (char *) pBuf;
memcpy ( pRing->buf+pRing->wtix, pBuf, nBytesAvail1stBlock );
nBytes -= nBytesAvail1stBlock;
if ( nBytesAvail2ndBlock > nBytes ) {
nBytesAvail2ndBlock = nBytes;
}
memcpy (pRing->buf, pChar+nBytesAvail1stBlock,
nBytesAvail2ndBlock);
totalBytes = nBytesAvail2ndBlock + nBytesAvail1stBlock;
}
pRing->wtix += totalBytes;
pRing->wtix &= ringIndexMask;
}
else {
totalBytes = 0;
}
return totalBytes;
}
/*
* cacRingBufferWrite ()
*
* returns the number of bytes written which may be less than
* the number requested.
*/
unsigned cacRingBufferWrite (ringBuffer *pRing, const void *pBuf,
unsigned nBytes)
{
unsigned char *pBufTmp = (unsigned char *) pBuf;
unsigned totalBytes = 0;
unsigned curBytes;
semMutexMustTake (pRing->writeLock);
while (totalBytes<nBytes) {
curBytes = cacRingBufferWritePartial (pRing,
pBufTmp+totalBytes, nBytes-totalBytes);
if (curBytes==0) {
semBinaryGive (pRing->readSignal);
semBinaryMustTake (pRing->writeSignal);
if (pRing->shutDown) {
semMutexGive (pRing->writeLock);
return totalBytes;
}
}
else {
totalBytes += curBytes;
}
}
semMutexGive (pRing->writeLock);
return totalBytes;
}
bool cacRingBufferWriteLockNoBlock (ringBuffer *pBuf, unsigned bytesRequired)
{
semMutexMustTake (pBuf->writeLock);
if (cacRingBufferWriteSize (pBuf)<bytesRequired) {
semMutexGive (pBuf->writeLock);
return false;
}
return true;
}
void cacRingBufferWriteUnlock (ringBuffer *pBuf)
{
semMutexGive (pBuf->writeLock);
}
void *cacRingBufferWriteReserve (ringBuffer *pRing, unsigned *pBytesAvail)
{
unsigned avail;
semMutexMustTake (pRing->writeLock);
avail = cacRingBufferContiguousWriteSize (pRing);
while (avail==0) {
semBinaryGive (pRing->readSignal);
semBinaryMustTake (pRing->writeSignal);
if (pRing->shutDown) {
semMutexGive (pRing->writeLock);
*pBytesAvail = 0u;
return 0;
}
avail = cacRingBufferContiguousWriteSize (pRing);
}
*pBytesAvail = avail;
return (void *) &pRing->buf[pRing->wtix];
}
void *cacRingBufferWriteReserveNoBlock (ringBuffer *pRing, unsigned *pBytesAvail)
{
unsigned avail;
semMutexMustTake (pRing->writeLock);
avail = cacRingBufferContiguousWriteSize (pRing);
if ( avail==0 || pRing->shutDown ) {
*pBytesAvail = 0u;
semMutexGive (pRing->writeLock);
return NULL;
}
*pBytesAvail = avail;
return (void *) &pRing->buf[pRing->wtix];
}
void cacRingBufferWriteCommit (ringBuffer *pRing, unsigned delta)
{
pRing->wtix += delta;
pRing->wtix &= ringIndexMask;
semMutexGive (pRing->writeLock);
}
void *cacRingBufferReadReserve (ringBuffer *pRing, unsigned *pBytesAvail)
{
unsigned avail;
semMutexMustTake (pRing->readLock);
avail = cacRingBufferContiguousReadSize (pRing);
while (avail==0) {
semBinaryMustTake (pRing->readSignal);
if (pRing->shutDown) {
semMutexGive (pRing->readLock);
*pBytesAvail = 0u;
return NULL;
}
avail = cacRingBufferContiguousReadSize (pRing);
}
*pBytesAvail = avail;
return (void *) &pRing->buf[(pRing->rdix+1) & ringIndexMask];
}
void *cacRingBufferReadReserveNoBlock (ringBuffer *pRing, unsigned *pBytesAvail)
{
unsigned avail;
semMutexMustTake (pRing->readLock);
avail = cacRingBufferContiguousReadSize (pRing);
if ( avail==0 || pRing->shutDown ) {
*pBytesAvail = 0u;
semMutexGive (pRing->readLock);
return NULL;
}
*pBytesAvail = avail;
return (void *) &pRing->buf[(pRing->rdix+1) & ringIndexMask];
}
void cacRingBufferReadCommit (ringBuffer *pRing, unsigned delta)
{
pRing->rdix += delta;
pRing->rdix &= ringIndexMask;
semMutexGive (pRing->readLock);
}
void cacRingBufferWriteFlush (ringBuffer *pRing)
{
semBinaryGive (pRing->readSignal);
}
void cacRingBufferReadFlush (ringBuffer *pRing)
{
semBinaryGive (pRing->writeSignal);
}

66
src/ca/ringBuffer.h Normal file
View File

@@ -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 Jeffrey O. Hill
* johill@lanl.gov
* 505 665 1831
*/
#ifndef ringBufferh
#define ringBufferh
#include "osiSem.h"
#define nBitsRingIndex 14
#define nElementsInRing (1<<nBitsRingIndex)
typedef struct ringBuffer {
char buf[nElementsInRing];
semBinaryId readSignal;
semBinaryId writeSignal;
semMutexId readLock;
semMutexId writeLock;
unsigned rdix; /* index of last char read */
unsigned wtix; /* index of next char to write */
unsigned shutDown;
} ringBuffer;
int cacRingBufferConstruct (ringBuffer *pBuf);
void cacRingBufferDestroy (ringBuffer *pBuf);
unsigned cacRingBufferWrite (ringBuffer *pRing,
const void *pBuf, unsigned nBytes);
unsigned cacRingBufferRead (ringBuffer *pRing,
void *pBuf, unsigned nBytes);
bool cacRingBufferWriteLockNoBlock (ringBuffer *pBuf, unsigned bytesRequired);
void cacRingBufferWriteUnlock (ringBuffer *pBuf);
void *cacRingBufferWriteReserve (ringBuffer *pBuf, unsigned *pAvailBytes);
void cacRingBufferWriteCommit (ringBuffer *pBuf, unsigned delta);
void *cacRingBufferReadReserve (ringBuffer *pBuf, unsigned *pBytesAvail);
void *cacRingBufferReadReserveNoBlock (ringBuffer *pBuf, unsigned *pBytesAvail);
void cacRingBufferReadCommit (ringBuffer *pBuf, unsigned delta);
void cacRingBufferReadFlush (ringBuffer *pBuf);
void cacRingBufferWriteFlush (ringBuffer *pBuf);
void cacRingBufferShutDown (ringBuffer *pBuf);
#endif /* ringBufferh */

348
src/ca/searchTimer.cpp Normal file
View File

@@ -0,0 +1,348 @@
/* * $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"
//
// searchTimer::searchTimer ()
//
searchTimer::searchTimer (udpiiu &iiuIn, osiTimerQueue &queueIn) :
osiTimer (queueIn),
iiu (iiuIn),
framesPerTry (INITIALTRIESPERFRAME),
framesPerTryCongestThresh (UINT_MAX),
minRetry (UINT_MAX),
retry (0u),
searchTries (0u),
searchResponses (0u),
retrySeqNo (0u),
retrySeqNoAtListBegin (0u),
period (CA_RECAST_DELAY)
{
}
//
// searchTimer::reset ()
//
void searchTimer::reset (double period)
{
LOCK (this->iiu.niiu.iiu.pcas);
this->retry = 0;
this->period = period;
UNLOCK (this->iiu.niiu.iiu.pcas);
if (this->timeRemaining()>period) {
this->reschedule (0.0);
}
}
/*
* searchTimer::setRetryInterval ()
*/
void searchTimer::setRetryInterval (unsigned retryNo)
{
unsigned idelay;
double delay;
LOCK (this->iiu.niiu.iiu.pcas);
/*
* set the retry number
*/
this->retry = min (retryNo, MAXCONNTRIES+1u);
/*
* set the retry interval
*/
idelay = 1u << min (this->retry, CHAR_BIT*sizeof(idelay)-1u);
delay = idelay * CA_RECAST_DELAY; /* sec */
/*
* place upper limit on the retry delay
*/
this->period = min (CA_RECAST_PERIOD, delay);
UNLOCK (this->iiu.niiu.iiu.pcas);
#ifdef DEBUG
printf ("new CA search period is %f sec\n", this->period);
#endif
}
//
// searchTimer::notifySearchResponse ()
//
// Reset the delay to the next search request if we get
// at least one response. However, dont reset this delay if we
// get a delayed response to an old search request.
//
void searchTimer::notifySearchResponse (nciu *pChan)
{
LOCK (this->iiu.niiu.iiu.pcas);
if ( this->retrySeqNoAtListBegin <= pChan->retrySeqNo ) {
if ( this->searchResponses < ULONG_MAX ) {
this->searchResponses++;
}
}
UNLOCK (this->iiu.niiu.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;
/*
* check to see if there is nothing to do here
*/
if (ellCount(&this->iiu.niiu.chidList)==0) {
return;
}
LOCK (this->iiu.niiu.iiu.pcas);
/*
* increment the retry sequence number
*/
this->retrySeqNo++; /* allowed to roll over */
/*
* dynamically adjust the number of UDP frames per
* try depending how many search requests are not
* replied to
*
* This determines how many search request can be
* sent together (at the same instant in time).
*
* The variable this->framesPerTry
* determines the number of UDP frames to be sent
* each time that retrySearchRequest() 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
* buffer associated with the server's port and
* will therefore take longer to connect. We
* initialize this->framesPerTry
* to a prime number so that it is less likely that the
* same channel is in the last UDP frame
* sent every time that this is called (and
* potentially discarded by a CA server with
* a small UDP input queue).
*/
/*
* increase frames per try only if we see better than
* a 93.75% success rate for one pass through the list
*/
if (this->searchResponses >
(this->searchTries-(this->searchTries/16u)) ) {
/*
* increase UDP frames per try if we have a good score
*/
if ( this->framesPerTry < MAXTRIESPERFRAME ) {
/*
* a congestion avoidance threshold similar to TCP is now used
*/
if ( this->framesPerTry < this->framesPerTryCongestThresh ) {
this->framesPerTry += this->framesPerTry;
}
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
}
}
/*
* if we detect congestion because we have less than a 87.5% success
* rate then gradually reduce the frames per try
*/
else if ( this->searchResponses <
(this->searchTries-(this->searchTries/8u)) ) {
if (this->framesPerTry>1) {
this->framesPerTry--;
}
this->framesPerTryCongestThresh = this->framesPerTry/2 + 1;
#if 0
printf ("Congestion detected - set frames per try to %u t=%u r=%u\n",
this->framesPerTry, this->searchTries,
this->searchResponses);
#endif
}
/*
* a successful cac_search_msg() sends channel to
* the end of the list
*/
firstChan = chan = (nciu *) ellFirst (&this->iiu.niiu.chidList);
while (chan) {
this->minRetry = min (this->minRetry, chan->retry);
/*
* clear counter when we reach the end of the list
*
* if we are making some progress then
* dont increase the delay between search
* requests
*/
if ( this->iiu.niiu.iiu.pcas->ca_pEndOfBCastList == chan ) {
if ( this->searchResponses == 0u ) {
#if 0
printf ("increasing search try interval\n");
#endif
this->setRetryInterval (this->minRetry + 1u);
}
this->minRetry = UINT_MAX;
/*
* increment the retry sequence number
* (this prevents the time of the next search
* try from being set to the current time if
* we are handling a response from an old
* search message)
*/
this->retrySeqNo++; /* allowed to roll over */
/*
* so that old search tries will not update the counters
*/
this->retrySeqNoAtListBegin = this->retrySeqNo;
/*
* reset the search try/response counters at the end of the list
* (sequence number) so that we dont overflow, but dont subtract
* out tries that dont have a matching response yet in case they
* are delayed
*/
if ( this->searchTries > this->searchResponses ) {
this->searchTries -= this->searchResponses;
}
else {
this->searchTries = 0;
}
this->searchResponses = 0;
#if 0
printf ("saw end of list\n");
#endif
}
/*
* this moves the channel to the end of the
* list (if successful)
*/
status = cac_search_msg (chan);
if (status != ECA_NORMAL) {
nSent++;
if (nSent>=this->framesPerTry) {
break;
}
/* flush out the search request buffer */
semBinaryGive (this->iiu.xmitSignal);
/* try again */
status = cac_search_msg (chan);
if (status != ECA_NORMAL) {
break;
}
}
if (this->searchTries<ULONG_MAX) {
this->searchTries++;
}
chan->retrySeqNo = this->retrySeqNo;
chan = (nciu *) ellFirst (&this->iiu.niiu.chidList);
/*
* dont send any of the channels twice within one try
*/
if (chan==firstChan) {
/*
* add one to nSent because there may be
* one more partial frame to be sent
*/
nSent++;
/*
* cap this->framesPerTry to
* the number of frames required for all of
* the unresolved channels
*/
if (this->framesPerTry>nSent) {
this->framesPerTry = nSent;
}
break;
}
}
UNLOCK (this->iiu.niiu.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
}
void searchTimer::destroy ()
{
}
bool searchTimer::again () const
{
if (ellCount(&this->iiu.niiu.chidList)==0) {
return false;
}
else {
if (this->retry < MAXCONNTRIES) {
return true;
}
else {
return false;
}
}
}
double searchTimer::delay () const
{
return this->period;
}
void searchTimer::show (unsigned level) const
{
}
const char *searchTimer::name () const
{
return "CAC Search Timer";
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,645 +0,0 @@
/*
* $Id$
* Author: Jeffrey O. Hill
* hill@luke.lanl.gov
* (505) 665 1831
* Date: 9-93
*
* 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
*
* Modification Log:
* -----------------
* $Log$
* Revision 1.27 1999/07/16 17:08:05 jhill
* fixed bug occurring when connection dropped while waiting to send, and
* initialize new search gongestion thresh parm
*
* Revision 1.26.6.1 1999/07/15 21:02:25 jhill
* fixed bug where client disconnects while waiting to send TCP
*
* Revision 1.26 1997/08/04 23:37:18 jhill
* added beacon anomaly flag init/allow ip 255.255.255.255
*
* Revision 1.24 1997/06/13 09:14:26 jhill
* connect/search proto changes
*
* Revision 1.23 1997/04/29 06:12:42 jhill
* use free lists
*
* Revision 1.22 1996/11/22 19:08:02 jhill
* added const to API
*
* Revision 1.21 1996/11/02 00:51:08 jhill
* many pc port, const in API, and other changes
*
* Revision 1.20 1996/07/10 23:30:12 jhill
* fixed GNU warnings
*
* Revision 1.19 1996/06/19 17:59:29 jhill
* many 3.13 beta changes
*
* Revision 1.18 1995/10/12 01:36:39 jhill
* New ca_flush_io() mechanism
*
* Revision 1.17 1995/09/29 22:13:59 jhill
* check for nill dbr pointer
*
* Revision 1.16 1995/08/22 00:27:55 jhill
* added cvs style mod log
*
*
* NOTES:
* 1) Need to fix if the OP is on a FD that
* becomes disconneted it will stay on the
* queue forever.
*/
#include "iocinf.h"
#include "freeList.h"
LOCAL void io_complete(struct event_handler_args args);
/*
* ca_sg_init()
*/
void ca_sg_init(CA_STATIC *ca_static)
{
/*
* init all sync group lists
*/
freeListInitPvt(&ca_static->ca_sgFreeListPVT,sizeof(CASG),32);
freeListInitPvt(&ca_static->ca_sgopFreeListPVT,sizeof(CASGOP),256);
return;
}
/*
* ca_sg_shutdown()
*/
void ca_sg_shutdown(CA_STATIC *ca_static)
{
CASG *pcasg;
CASG *pnextcasg;
int status;
/*
* free all sync group lists
*/
LOCK;
pcasg = (CASG *) ellFirst (&ca_static->activeCASG);
while (pcasg) {
pnextcasg = (CASG *) ellNext (&pcasg->node);
status = ca_sg_delete (pcasg->id);
assert (status==ECA_NORMAL);
pcasg = pnextcasg;
}
assert (ellCount(&ca_static->activeCASG)==0);
/*
* per sync group
*/
freeListCleanup(ca_static->ca_sgFreeListPVT);
/*
* per sync group op
*/
ellFree (&ca_static->activeCASGOP);
freeListCleanup(ca_static->ca_sgopFreeListPVT);
UNLOCK;
return;
}
/*
* ca_sg_create()
*/
int epicsShareAPI ca_sg_create(CA_SYNC_GID *pgid)
{
int status;
CASG *pcasg;
CA_OSD_GET_CA_STATIC
/*
* Force the CA client id bucket to
* init if needed.
* Return error if unable to init.
*/
INITCHK;
/*
* first look on a free list. If not there
* allocate dynamic memory for it.
*/
pcasg = (CASG *) freeListMalloc(ca_static->ca_sgFreeListPVT);
if(!pcasg){
return ECA_ALLOCMEM;
}
LOCK;
/*
* 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;
#ifdef iocCore
pcasg->sem = semBinaryCreate(semEmpty);
assert(pcasg->sem );
#endif
do {
pcasg->id = CLIENT_SLOW_ID_ALLOC;
status = bucketAddItemUnsignedId (pSlowBucket,
&pcasg->id, pcasg);
} while (status == S_bucket_idInUse);
if (status == S_bucket_success) {
/*
* place it on the active sync group list
*/
ellAdd (&ca_static->activeCASG, &pcasg->node);
}
else {
/*
* place it back on the free sync group list
*/
freeListFree(ca_static->ca_sgFreeListPVT, pcasg);
UNLOCK;
if (status == S_bucket_noMemory) {
return ECA_ALLOCMEM;
}
else {
return ECA_INTERNAL;
}
}
UNLOCK;
*pgid = pcasg->id;
return ECA_NORMAL;
}
/*
* ca_sg_delete()
*/
int epicsShareAPI ca_sg_delete(const CA_SYNC_GID gid)
{
int status;
CASG *pcasg;
CA_OSD_GET_CA_STATIC
/*
* Force the CA client id bucket to
* init if needed.
* Return error if unable to init.
*/
INITCHK;
LOCK;
pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid);
if(!pcasg || pcasg->magic != CASG_MAGIC){
UNLOCK;
return ECA_BADSYNCGRP;
}
status = bucketRemoveItemUnsignedId(pSlowBucket, &gid);
assert (status == S_bucket_success);
#ifdef iocCore
semBinaryDestroy(pcasg->sem);
#endif
pcasg->magic = 0;
ellDelete(&ca_static->activeCASG, &pcasg->node);
UNLOCK;
freeListFree(ca_static->ca_sgFreeListPVT, pcasg);
return ECA_NORMAL;
}
/*
* ca_sg_block()
*/
int epicsShareAPI ca_sg_block(const CA_SYNC_GID gid, ca_real timeout)
{
struct timeval beg_time;
ca_real delay;
int status;
CASG *pcasg;
CA_OSD_GET_CA_STATIC
/*
* Force the CA client id bucket to
* init if needed.
* Return error if unable to init.
*/
INITCHK;
if(timeout<0.0){
return ECA_TIMEOUT;
}
/*
* until CAs input mechanism is
* revised we have to dissallow
* this routine from an event routine
* (to avoid excess recursion)
*/
if(EVENTLOCKTEST){
return ECA_EVDISALLOW;
}
LOCK;
pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid);
if(!pcasg || pcasg->magic != CASG_MAGIC){
UNLOCK;
return ECA_BADSYNCGRP;
}
UNLOCK;
/*
* always flush at least once.
*/
ca_static->ca_flush_pending = TRUE;
cac_gettimeval (&ca_static->currentTime);
beg_time = ca_static->currentTime;
delay = 0.0;
status = ECA_NORMAL;
while(pcasg->opPendCount){
ca_real remaining;
struct timeval tmo;
/*
* Exit if the timeout has expired
* (dont wait forever for an itsy bitsy
* delay which will no be updated if
* select is called with no delay)
*
* current time is only updated by
* cac_select_io() if we specify
* at least 1 usec to wait
*/
remaining = timeout-delay;
if (remaining<=(1.0/USEC_PER_SEC)) {
/*
* Make sure that we take care of
* recv backlog at least once
*/
tmo.tv_sec = 0L;
tmo.tv_usec = 0L;
cac_mux_io (ca_static, &tmo, TRUE);
status = ECA_TIMEOUT;
break;
}
/*
* Allow for CA background labor
*/
remaining = min(cac_fetch_poll_period(ca_static), remaining);
/*
* wait for asynch notification
*/
tmo.tv_sec = (long) remaining;
tmo.tv_usec = (long) ((remaining-tmo.tv_sec)*USEC_PER_SEC);
#ifndef iocCore
cac_mux_io(ca_static,&tmo, TRUE);
#else
{
struct timeval itimeout;
double waitTime;
itimeout.tv_usec = 0;
itimeout.tv_sec = 0;
cac_mux_io(ca_static,&itimeout, TRUE);
waitTime = tmo.tv_sec + tmo.tv_usec/1000.0;
if(waitTime>POLLDELAY) waitTime = POLLDELAY;
semBinaryTakeTimeout(ca_static->ca_io_done_sem,waitTime);
/*
*force a time update because we are not
*going to get one with a nill timeout in ca_mux_io()
*/
cac_gettimeval (&ca_static->currentTime);
}
#endif
delay = cac_time_diff (&ca_static->currentTime, &beg_time);
}
pcasg->opPendCount = 0;
pcasg->seqNo++;
return status;
}
/*
* ca_sg_reset
*/
int epicsShareAPI ca_sg_reset(const CA_SYNC_GID gid)
{
CASG *pcasg;
CA_OSD_GET_CA_STATIC
LOCK;
pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid);
if(!pcasg || pcasg->magic != CASG_MAGIC){
UNLOCK;
return ECA_BADSYNCGRP;
}
pcasg->opPendCount = 0;
pcasg->seqNo++;
UNLOCK;
return ECA_NORMAL;
}
/*
* ca_sg_stat
*/
int epicsShareAPI ca_sg_stat(const CA_SYNC_GID gid)
{
CASG *pcasg;
CASGOP *pcasgop;
CA_OSD_GET_CA_STATIC
LOCK;
pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid);
if(!pcasg || pcasg->magic != CASG_MAGIC){
UNLOCK;
printf("Bad Sync Group Id\n");
return ECA_BADSYNCGRP;
}
UNLOCK;
printf("Sync Group: id=%u, magic=%lu, opPend=%lu, seqNo=%lu\n",
pcasg->id, pcasg->magic, pcasg->opPendCount,
pcasg->seqNo);
LOCK;
pcasgop = (CASGOP *) ellFirst(&ca_static->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);
}
return ECA_NORMAL;
}
/*
* ca_sg_test
*/
int epicsShareAPI ca_sg_test(const CA_SYNC_GID gid)
{
CASG *pcasg;
CA_OSD_GET_CA_STATIC
LOCK;
pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid);
if(!pcasg || pcasg->magic != CASG_MAGIC){
UNLOCK;
return ECA_BADSYNCGRP;
}
UNLOCK;
if(pcasg->opPendCount){
return ECA_IOINPROGRESS;
}
else{
return ECA_IODONE;
}
}
/*
* ca_sg_array_put()
*/
int epicsShareAPI ca_sg_array_put(
const CA_SYNC_GID gid,
chtype type,
unsigned long count,
chid chix,
const void *pvalue)
{
int status;
CASGOP *pcasgop;
CASG *pcasg;
CA_OSD_GET_CA_STATIC
/*
* first look on a free list. If not there
* allocate dynamic memory for it.
*/
pcasgop = (CASGOP *) freeListMalloc(ca_static->ca_sgopFreeListPVT);
if(!pcasgop){
return ECA_ALLOCMEM;
}
LOCK;
pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid);
if(!pcasg || pcasg->magic != CASG_MAGIC){
UNLOCK;
freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop);
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 */
ellAdd(&ca_static->activeCASGOP, &pcasgop->node);
pcasg->opPendCount++;
UNLOCK;
status = ca_array_put_callback(
type,
count,
chix,
pvalue,
io_complete,
pcasgop);
if(status != ECA_NORMAL){
LOCK;
assert(pcasg->opPendCount>=1u);
pcasg->opPendCount--;
ellDelete(&ca_static->activeCASGOP, &pcasgop->node);
UNLOCK;
freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop);
}
return status;
}
/*
* ca_sg_array_get()
*/
int epicsShareAPI ca_sg_array_get(
const CA_SYNC_GID gid,
chtype type,
unsigned long count,
chid chix,
void *pvalue)
{
int status;
CASGOP *pcasgop;
CASG *pcasg;
CA_OSD_GET_CA_STATIC
/*
* first look on a free list. If not there
* allocate dynamic memory for it.
*/
pcasgop = (CASGOP *) freeListMalloc(ca_static->ca_sgopFreeListPVT);
if(!pcasgop){
return ECA_ALLOCMEM;
}
LOCK;
pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid);
if(!pcasg || pcasg->magic != CASG_MAGIC){
UNLOCK;
freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop);
return ECA_BADSYNCGRP;
}
memset((char *)pcasgop, 0,sizeof(*pcasgop));
pcasgop->id = gid;
pcasgop->seqNo = pcasg->seqNo;
pcasgop->magic = CASG_MAGIC;
pcasgop->pValue = pvalue;
ellAdd(&ca_static->activeCASGOP, &pcasgop->node);
pcasg->opPendCount++;
UNLOCK;
status = ca_array_get_callback(
type,
count,
chix,
io_complete,
pcasgop);
if(status != ECA_NORMAL){
LOCK;
assert(pcasg->opPendCount>=1u);
pcasg->opPendCount--;
ellDelete(&ca_static->activeCASGOP, &pcasgop->node);
UNLOCK;
freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop);
}
return status;
}
/*
* io_complete()
*/
LOCAL void io_complete(struct event_handler_args args)
{
unsigned long size;
CASGOP *pcasgop;
CASG *pcasg;
CA_OSD_GET_CA_STATIC
pcasgop = args.usr;
assert(pcasgop->magic == CASG_MAGIC);
LOCK;
ellDelete(&ca_static->activeCASGOP, &pcasgop->node);
pcasgop->magic = 0;
/*
* ignore stale replies
*/
pcasg = bucketLookupItemUnsignedId(pSlowBucket, &pcasgop->id);
if(!pcasg || pcasg->seqNo != pcasgop->seqNo){
UNLOCK;
return;
}
assert(pcasg->magic == CASG_MAGIC);
assert(pcasg->id == pcasgop->id);
if(!(args.status&CA_M_SUCCESS)){
ca_printf(
"CA Sync Group (id=%d) request failed because \"%s\"\n",
pcasgop->id,
ca_message(args.status));
UNLOCK;
freeListFree(ca_static->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;
freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop);
/*
* Wake up any tasks pending
*
* occurs through select on UNIX
*/
#ifdef iocCore
if(pcasg->opPendCount == 0){
semBinaryGive(pcasg->sem);
}
#endif
return;
}

619
src/ca/syncgrp.cpp Normal file
View File

@@ -0,0 +1,619 @@
/*
* $Id$
* Author: Jeffrey O. Hill
* hill@luke.lanl.gov
* (505) 665 1831
* Date: 9-93
*
* 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 "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 caStatus;
int status;
CASG *pcasg;
cac *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);
}
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;
}
}
UNLOCK (pcac);
*pgid = pcasg->id;
return ECA_NORMAL;
}
/*
* ca_sg_delete()
*/
int epicsShareAPI ca_sg_delete(const CA_SYNC_GID gid)
{
int caStatus;
int status;
CASG *pcasg;
cac *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);
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);
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)
{
cac *pcac;
int status;
status = fetchClientContext (&pcac);
if ( status != ECA_NORMAL ) {
return status;
}
/*
* dont allow recursion
*/
{
void *p = threadPrivateGet (cacRecursionLock);
if (p) {
return ECA_EVDISALLOW;
}
threadPrivateSet (cacRecursionLock, &cacRecursionLock);
}
status = ca_sg_block_private (pcac, gid, timeout);
threadPrivateSet (cacRecursionLock, NULL);
return status;
}
/*
* ca_sg_reset
*/
int epicsShareAPI ca_sg_reset (const CA_SYNC_GID gid)
{
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->opPendCount = 0;
pcasg->seqNo++;
UNLOCK (pcac);
return ECA_NORMAL;
}
/*
* ca_sg_stat
*/
int epicsShareAPI ca_sg_stat (const CA_SYNC_GID gid)
{
CASG *pcasg;
CASGOP *pcasgop;
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);
printf("Bad Sync Group Id\n");
return 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);
return ECA_NORMAL;
}
/*
* ca_sg_test
*/
int epicsShareAPI ca_sg_test (const CA_SYNC_GID gid)
{
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;
}
UNLOCK (pcac);
if(pcasg->opPendCount){
return ECA_IOINPROGRESS;
}
else{
return ECA_IODONE;
}
}
/*
* io_complete()
*/
LOCAL void 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;
}
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;
}
/*
* ca_sg_array_put()
*/
int epicsShareAPI ca_sg_array_put (
const CA_SYNC_GID gid,
chtype type,
unsigned long count,
chid chix,
const void *pvalue)
{
int status;
CASGOP *pcasgop;
CASG *pcasg;
cac *pcac;
int caStatus;
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);
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, 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;
}
/*
* ca_sg_array_get()
*/
int epicsShareAPI ca_sg_array_get (
const CA_SYNC_GID gid,
chtype type,
unsigned long count,
chid chix,
void *pvalue)
{
int status;
CASGOP *pcasgop;
CASG *pcasg;
cac *pcac;
int caStatus;
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);
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,
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;
}

View File

@@ -1,77 +0,0 @@
/*
*
* T E S T _ E V E N T . C
* Author: Jeffrey O. Hill
* simple stub for testing monitors
*
*
* History
* joh 031891 printed type in decimal instead of hex
* joh 072792 better messages
*
*
*
*
*/
static char *sccsId = "$Id$";
#include "iocinf.h"
void epicsShareAPI ca_test_event(struct event_handler_args args)
{
ca_printf("CAC: ~~~### in test event for [%s] ###~~~\n",args.chid+1);
ca_printf("CAC: User argument\t%x\n", args.usr);
ca_printf("CAC: Native channel data type\t%d\n", ca_field_type(args.chid));
ca_printf("CAC: Monitor data type\t%d\n", args.type);
ca_printf("CAC: CA Status \"%s\"\n", ca_message(args.status));
if(!args.dbr || !(CA_M_SUCCESS&args.status)){
return;
}
switch(args.type){
case DBR_STRING:
ca_printf("CAC: Value:\t<%s>\n",args.dbr);
break;
case DBR_CHAR:
ca_printf("CAC: Value:\t<%d>\n",*(char *)args.dbr);
break;
#if DBR_INT != DBR_SHORT
case DBR_INT:
#endif
case DBR_SHORT:
case DBR_ENUM:
ca_printf("CAC: Value:\t<%d>\n",*(short *)args.dbr);
break;
case DBR_LONG:
ca_printf("CAC: Value:\t<%d>\n",*(long *)args.dbr);
break;
case DBR_FLOAT:
ca_printf("CAC: Value:\t<%f>\n",*(float *)args.dbr);
break;
case DBR_DOUBLE:
ca_printf("CAC: Value:\t<%lf>\n",*(double *)args.dbr);
break;
case DBR_STS_STRING:
ca_printf("CAC: Value:\t<%s>\n",((struct dbr_sts_string *)args.dbr)->value);
break;
case DBR_STS_INT:
ca_printf("CAC: Value:\t<%d>\n",((struct dbr_sts_int *)args.dbr)->value);
break;
case DBR_STS_FLOAT:
ca_printf("CAC: Value:\t<%f>\n",((struct dbr_sts_float *)args.dbr)->value);
break;
case DBR_STS_ENUM:
ca_printf("CAC: Value:\t<%d>\n",((struct dbr_sts_enum *)args.dbr)->value);
break;
case DBR_GR_FLOAT:
ca_printf("CAC: Value:\t<%f>\n",((struct dbr_gr_float *)args.dbr)->value);
break;
default:
ca_printf( "CAC: Sorry test_event does not handle data type %d yet\n",
args.type);
}
}

75
src/ca/test_event.cpp Normal file
View File

@@ -0,0 +1,75 @@
/*
*
* T E S T _ E V E N T . C
* Author: Jeffrey O. Hill
* simple stub for testing monitors
*
*
* History
* joh 031891 printed type in decimal instead of hex
* joh 072792 better messages
*
*
*
*
*/
#include "iocinf.h"
void epicsShareAPI ca_test_event(struct event_handler_args args)
{
printf ("CAC: ~~~### in test event for [%s] ###~~~\n", ca_name(args.chid));
printf ("CAC: User argument\t%p\n", args.usr);
printf ("CAC: Native channel data type\t%d\n", ca_field_type(args.chid));
printf ("CAC: Monitor data type\t%ld\n", args.type);
printf ("CAC: CA Status \"%s\"\n", ca_message(args.status));
if(!args.dbr || !(CA_M_SUCCESS&args.status)){
return;
}
switch(args.type){
case DBR_STRING:
printf ("CAC: Value:\t<%s>\n", (const char *)args.dbr);
break;
case DBR_CHAR:
printf ("CAC: Value:\t<%d>\n",*(char *)args.dbr);
break;
#if DBR_INT != DBR_SHORT
case DBR_INT:
#endif
case DBR_SHORT:
case DBR_ENUM:
printf ("CAC: Value:\t<%d>\n",*(short *)args.dbr);
break;
case DBR_LONG:
printf ("CAC: Value:\t<%ld>\n",*(long *)args.dbr);
break;
case DBR_FLOAT:
printf ("CAC: Value:\t<%f>\n",*(float *)args.dbr);
break;
case DBR_DOUBLE:
printf ("CAC: Value:\t<%f>\n",*(double *)args.dbr);
break;
case DBR_STS_STRING:
printf ("CAC: Value:\t<%s>\n",((struct dbr_sts_string *)args.dbr)->value);
break;
case DBR_STS_INT:
printf ("CAC: Value:\t<%d>\n",((struct dbr_sts_int *)args.dbr)->value);
break;
case DBR_STS_FLOAT:
printf ("CAC: Value:\t<%f>\n",((struct dbr_sts_float *)args.dbr)->value);
break;
case DBR_STS_ENUM:
printf ("CAC: Value:\t<%d>\n",((struct dbr_sts_enum *)args.dbr)->value);
break;
case DBR_GR_FLOAT:
printf ("CAC: Value:\t<%f>\n",((struct dbr_gr_float *)args.dbr)->value);
break;
default:
printf ( "CAC: Sorry test_event does not handle data type %ld yet\n",
args.type);
}
}

View File

@@ -191,6 +191,7 @@ struct dbr_alDouble {DBRalDouble};
#define S_db_bkptLogic (M_dbAccess|61) /*Logic error in breakpoint routine*/
#define S_db_cntSpwn (M_dbAccess|63) /*Cannot spawn dbContTask*/
#define S_db_cntCont (M_dbAccess|65) /*Cannot resume dbContTask*/
#define S_db_noMemory (M_dbAccess|66) /*unable to allocate data structure from pool*/
/* Global Database Access Routines*/
#define dbGetLink(PLNK,DBRTYPE,PBUFFER,OPTIONS,NREQUEST) \