From 46a8fac619c1f413f438c8b84c664f7b8591695c Mon Sep 17 00:00:00 2001 From: Marty Kraimer Date: Thu, 6 Nov 2003 16:39:05 +0000 Subject: [PATCH] add dbCaPutLinkCallback --- src/db/dbCa.c | 371 +++++++++++++++++++++++++++++----------------- src/db/dbCa.h | 6 +- src/db/dbCaPvt.h | 9 +- src/db/dbCaTest.c | 2 +- 4 files changed, 248 insertions(+), 140 deletions(-) diff --git a/src/db/dbCa.c b/src/db/dbCa.c index e90eb7c7e..f8db28345 100644 --- a/src/db/dbCa.c +++ b/src/db/dbCa.c @@ -61,6 +61,10 @@ struct ca_client_context * dbCaClientContext; void dbCaTask(void); /*The Channel Access Task*/ extern void dbServiceIOInit(); + +#define printLinks(pcaLink) \ + errlogPrintf("%s has DB CA link to %s\n",\ + ((dbCommon*)((pcaLink)->plink->value.pv_link.precord))->name,(pcaLink->pvname)) /* caLink locking * @@ -108,6 +112,15 @@ extern void dbServiceIOInit(); * it finds pca->plink=0 and does nothing. Once ca_clear_channel * is called no other callback for this caLink will be called. * + * dbCaPutLinkCallback causes an additional complication because + * because when dbCaRemoveLink is called The callback may not have occured. + * What is done is the following: + * If callback has not occured dbCaRemoveLink sets plinkPutCallback=plink + * If putCallback is called before dbCaTask calls ca_clear_channel + * It does NOT call the users callback. + * dbCaTask calls the users callback passing plinkPutCallback AFTER + * it has called ca_clear_channel + * Thus the users callback will get called exactly once */ STATIC void addAction(caLink *pca, short link_action) @@ -119,12 +132,14 @@ STATIC void addAction(caLink *pca, short link_action) if((pca->link_action&CA_CLEAR_CHANNEL)!=0) { errlogPrintf("dbCa:addAction %d but CA_CLEAR_CHANNEL already requested\n", link_action); + printLinks(pca); callAdd = FALSE; link_action=0; } if(link_action&CA_CLEAR_CHANNEL) { if(++removesOutstanding>=removesOutstandingWarning) { errlogPrintf("dbCa: Warning removesOutstanding %d\n",removesOutstanding); + printLinks(pca); } while(removesOutstanding>=removesOutstandingWarning) { epicsMutexUnlock(workListLock); @@ -148,7 +163,7 @@ void epicsShareAPI dbCaLinkInit(void) epicsThreadGetStackSize(epicsThreadStackBig), (EPICSTHREADFUNC) dbCaTask,0); } - + void epicsShareAPI dbCaAddLink( struct link *plink) { caLink *pca; @@ -177,6 +192,7 @@ void epicsShareAPI dbCaRemoveLink( struct link *plink) epicsMutexMustLock(pca->lock); pca->plink = 0; plink->value.pv_link.pvt = 0; + if(pca->putCallback) pca->plinkPutCallback = plink; /*Must unlock before addAction. Otherwise dbCaTask might free first*/ epicsMutexUnlock(pca->lock); addAction(pca,CA_CLEAR_CHANNEL); @@ -246,8 +262,8 @@ done: return(status); } -long epicsShareAPI dbCaPutLink(struct link *plink,short dbrType, - const void *psource,long nelements) +long putLinkPvt(struct link *plink,short dbrType, + const void *psource,long nelements,dbCaPutCallback callback) { caLink *pca = (caLink *)plink->value.pv_link.pvt; long (*pconvert)(); @@ -299,10 +315,24 @@ long epicsShareAPI dbCaPutLink(struct link *plink,short dbrType, if(pca->newOutNative) pca->nNoWrite++; pca->newOutNative = TRUE; } + if(callback) { + pca->putType = CA_PUT_CALLBACK; + pca->putCallback = callback; + } else { + pca->putType = CA_PUT; + pca->putCallback = 0; + } addAction(pca,link_action); epicsMutexUnlock(pca->lock); return(status); } + +long epicsShareAPI dbCaPutLink(struct link *plink,short dbrType, + const void *pbuffer,long nRequest) +{return(putLinkPvt(plink,dbrType,pbuffer,nRequest,0));} +long epicsShareAPI dbCaPutLinkCallback( struct link *plink,short dbrType, + const void *pbuffer,long nRequest,dbCaPutCallback callback) +{return(putLinkPvt(plink,dbrType,pbuffer,nRequest,callback));} #define pcaGetCheck \ assert(plink); \ @@ -474,29 +504,72 @@ long epicsShareAPI dbCaGetUnits( return(gotAttributes ? 0 : -1); } -STATIC void exceptionCallback(struct exception_handler_args args) +STATIC void connectionCallback(struct connection_handler_args arg) { - const char *context = (args.ctx ? args.ctx : "unknown"); + caLink *pca; + short link_action = 0; + struct link *plink; - errlogPrintf("DB CA Link Exception: \"%s\", context \"%s\"\n", - ca_message(args.stat), context); - if (args.chid) { - errlogPrintf( - "DB CA Link Exception: channel \"%s\"\n", - ca_name(args.chid)); - if (ca_state(args.chid)==cs_conn) { - errlogPrintf( - "DB CA Link Exception: native T=%s, request T=%s," - " native N=%ld, request N=%ld, " - " access rights {%s%s}\n", - dbr_type_to_text(ca_field_type(args.chid)), - dbr_type_to_text(args.type), - ca_element_count(args.chid), - args.count, - ca_read_access(args.chid) ? "R" : "", - ca_write_access(args.chid) ? "W" : ""); + pca = ca_puser(arg.chid); + assert(pca); + epicsMutexMustLock(pca->lock); + plink = pca->plink; + if(!plink) goto done; + pca->isConnected = (ca_state(arg.chid)==cs_conn) ? TRUE : FALSE ; + if(!pca->isConnected) { + struct pv_link *ppv_link = &(plink->value.pv_link); + dbCommon *precord = ppv_link->precord; + + pca->nDisconnect++; + if(precord) { + if((ppv_link->pvlMask&pvlOptCP) + || ((ppv_link->pvlMask&pvlOptCPP)&&(precord->scan==0))) + scanOnce(precord); + } + goto done; + } + pca->hasReadAccess = ca_read_access(arg.chid); + pca->hasWriteAccess = ca_write_access(arg.chid); + if(pca->gotFirstConnection) { + if((pca->nelements != ca_element_count(arg.chid)) + || (pca->dbrType != ca_field_type(arg.chid))){ + /* field type or nelements changed */ + /* Let next dbCaGetLink and/or dbCaPutLink determine options*/ + plink->value.pv_link.pvlMask &= + ~(pvlOptInpNative|pvlOptInpString|pvlOptOutNative|pvlOptOutString); + + pca->gotInNative = 0; + pca->gotOutNative=0; + pca->gotInString=0; + pca->gotOutString=0; + free(pca->pgetNative); pca->pgetNative = 0; + free(pca->pgetString); pca->pgetString = 0; + free(pca->pputNative); pca->pputNative = 0; + free(pca->pputString); pca->pputString = 0; } } + pca->gotFirstConnection = TRUE; + pca->nelements = ca_element_count(arg.chid); + pca->dbrType = ca_field_type(arg.chid); + if((plink->value.pv_link.pvlMask & pvlOptInpNative) && (!pca->pgetNative)){ + link_action |= CA_MONITOR_NATIVE; + } + if((plink->value.pv_link.pvlMask & pvlOptInpString) && (!pca->pgetString)){ + link_action |= CA_MONITOR_STRING; + } + if((plink->value.pv_link.pvlMask & pvlOptOutNative) && (pca->gotOutNative)){ + link_action |= CA_WRITE_NATIVE; + } + if((plink->value.pv_link.pvlMask & pvlOptOutString) && (pca->gotOutString)){ + link_action |= CA_WRITE_STRING; + } + pca->gotAttributes = 0; + if(pca->dbrType!=DBR_STRING) { + link_action |= CA_GET_ATTRIBUTES; + } +done: + if(link_action) addAction(pca,link_action); + epicsMutexUnlock(pca->lock); } STATIC void eventCallback(struct event_handler_args arg) @@ -559,6 +632,74 @@ done: epicsMutexUnlock(pca->lock); } +STATIC void exceptionCallback(struct exception_handler_args args) +{ + const char *context = (args.ctx ? args.ctx : "unknown"); + + errlogPrintf("DB CA Link Exception: \"%s\", context \"%s\"\n", + ca_message(args.stat), context); + if (args.chid) { + errlogPrintf( + "DB CA Link Exception: channel \"%s\"\n", + ca_name(args.chid)); + if (ca_state(args.chid)==cs_conn) { + errlogPrintf( + "DB CA Link Exception: native T=%s, request T=%s," + " native N=%ld, request N=%ld, " + " access rights {%s%s}\n", + dbr_type_to_text(ca_field_type(args.chid)), + dbr_type_to_text(args.type), + ca_element_count(args.chid), + args.count, + ca_read_access(args.chid) ? "R" : "", + ca_write_access(args.chid) ? "W" : ""); + } + } +} + +STATIC void putCallback(struct event_handler_args arg) +{ + caLink *pca = (caLink *)arg.usr; + struct link *plink; + dbCaPutCallback callback = 0; + + epicsMutexMustLock(pca->lock); + plink = pca->plink; + if(!plink) goto done; + callback = pca->putCallback; + pca->putCallback = 0; + pca->putType = 0; +done: + epicsMutexUnlock(pca->lock); + if(callback) callback(plink); +} + +STATIC void accessRightsCallback(struct access_rights_handler_args arg) +{ + caLink *pca = (caLink *)ca_puser(arg.chid); + struct link *plink; + struct pv_link *ppv_link; + dbCommon *precord; + + assert(pca); + if(ca_state(pca->chid) != cs_conn) return;/*connectionCallback will handle*/ + epicsMutexMustLock(pca->lock); + plink = pca->plink; + if(!plink) goto done; + pca->hasReadAccess = ca_read_access(arg.chid); + pca->hasWriteAccess = ca_write_access(arg.chid); + if(pca->hasReadAccess && pca->hasWriteAccess) goto done; + ppv_link = &(plink->value.pv_link); + precord = ppv_link->precord; + if(precord) { + if((ppv_link->pvlMask&pvlOptCP) + || ((ppv_link->pvlMask&pvlOptCPP)&&(precord->scan==0))) + scanOnce(precord); + } +done: + epicsMutexUnlock(pca->lock); +} + STATIC void getAttribEventCallback(struct event_handler_args arg) { caLink *pca = (caLink *)arg.usr; @@ -600,106 +741,9 @@ STATIC void getAttribEventCallback(struct event_handler_args arg) epicsMutexUnlock(pca->lock); if(pca->callback) (*pca->callback)(pca->userPvt); } - -STATIC void accessRightsCallback(struct access_rights_handler_args arg) -{ - caLink *pca = (caLink *)ca_puser(arg.chid); - struct link *plink; - struct pv_link *ppv_link; - dbCommon *precord; - - assert(pca); - if(ca_state(pca->chid) != cs_conn) return;/*connectionCallback will handle*/ - epicsMutexMustLock(pca->lock); - plink = pca->plink; - if(!plink) goto done; - pca->hasReadAccess = ca_read_access(arg.chid); - pca->hasWriteAccess = ca_write_access(arg.chid); - if(pca->hasReadAccess && pca->hasWriteAccess) goto done; - ppv_link = &(plink->value.pv_link); - precord = ppv_link->precord; - if(precord) { - if((ppv_link->pvlMask&pvlOptCP) - || ((ppv_link->pvlMask&pvlOptCPP)&&(precord->scan==0))) - scanOnce(precord); - } -done: - epicsMutexUnlock(pca->lock); -} -STATIC void connectionCallback(struct connection_handler_args arg) -{ - caLink *pca; - short link_action = 0; - struct link *plink; - - pca = ca_puser(arg.chid); - assert(pca); - epicsMutexMustLock(pca->lock); - plink = pca->plink; - if(!plink) goto done; - pca->isConnected = (ca_state(arg.chid)==cs_conn) ? TRUE : FALSE ; - if(!pca->isConnected) { - struct pv_link *ppv_link = &(plink->value.pv_link); - dbCommon *precord = ppv_link->precord; - - pca->nDisconnect++; - if(precord) { - if((ppv_link->pvlMask&pvlOptCP) - || ((ppv_link->pvlMask&pvlOptCPP)&&(precord->scan==0))) - scanOnce(precord); - } - goto done; - } - pca->hasReadAccess = ca_read_access(arg.chid); - pca->hasWriteAccess = ca_write_access(arg.chid); - if(pca->gotFirstConnection) { - if((pca->nelements != ca_element_count(arg.chid)) - || (pca->dbrType != ca_field_type(arg.chid))){ - /* field type or nelements changed */ - /* Let next dbCaGetLink and/or dbCaPutLink determine options*/ - plink->value.pv_link.pvlMask &= - ~(pvlOptInpNative|pvlOptInpString|pvlOptOutNative|pvlOptOutString); - - pca->gotInNative = 0; - pca->gotOutNative=0; - pca->gotInString=0; - pca->gotOutString=0; - free(pca->pgetNative); pca->pgetNative = 0; - free(pca->pgetString); pca->pgetString = 0; - free(pca->pputNative); pca->pputNative = 0; - free(pca->pputString); pca->pputString = 0; - } - } - pca->gotFirstConnection = TRUE; - pca->nelements = ca_element_count(arg.chid); - pca->dbrType = ca_field_type(arg.chid); - if((plink->value.pv_link.pvlMask & pvlOptInpNative) && (!pca->pgetNative)){ - link_action |= CA_MONITOR_NATIVE; - } - if((plink->value.pv_link.pvlMask & pvlOptInpString) && (!pca->pgetString)){ - link_action |= CA_MONITOR_STRING; - } - if((plink->value.pv_link.pvlMask & pvlOptOutNative) && (pca->gotOutNative)){ - link_action |= CA_WRITE_NATIVE; - } - if((plink->value.pv_link.pvlMask & pvlOptOutString) && (pca->gotOutString)){ - link_action |= CA_WRITE_STRING; - } - pca->gotAttributes = 0; - link_action |= CA_GET_ATTRIBUTES; -done: - if(link_action) addAction(pca,link_action); - epicsMutexUnlock(pca->lock); -} - - void dbCaTask() { - caLink *pca; - short link_action; - int status; - taskwdInsert(epicsThreadGetIdSelf(),NULL,NULL); SEVCHK(ca_context_create(ca_enable_preemptive_callback), "dbCaTask calling ca_context_create"); @@ -712,6 +756,10 @@ void dbCaTask() while (TRUE){ epicsEventMustWait(workListEvent); while(TRUE) { /* process all requests in workList*/ + caLink *pca; + short link_action; + int status; + epicsMutexMustLock(workListLock); if(!(pca = (caLink *)ellFirst(&workList))){/*Take off list head*/ epicsMutexUnlock(workListLock); @@ -723,7 +771,17 @@ void dbCaTask() if(link_action&CA_CLEAR_CHANNEL) --removesOutstanding; epicsMutexUnlock(workListLock); /*Give it back immediately*/ if(link_action&CA_CLEAR_CHANNEL) {/*This must be first*/ + dbCaPutCallback callback; + struct link *plinkPutCallback = 0; + if(pca->chid) ca_clear_channel(pca->chid); + callback = pca->putCallback; + if(callback) { + plinkPutCallback = pca->plinkPutCallback; + pca->plinkPutCallback = 0; + pca->putCallback = 0; + pca->putType = 0; + } free(pca->pgetNative); free(pca->pputNative); free(pca->pgetString); @@ -731,6 +789,8 @@ void dbCaTask() free(pca->pvname); epicsMutexDestroy(pca->lock); free(pca); + /*No alarm is raised. Since link is changing so what.*/ + if(callback) callback(plinkPutCallback); continue; /*No other link_action makes sense*/ } if(link_action&CA_CONNECT) { @@ -738,32 +798,63 @@ void dbCaTask() pca->pvname,connectionCallback,(void *)pca, CA_PRIORITY_DB_LINKS, &(pca->chid)); if(status!=ECA_NORMAL) { - errlogPrintf("dbCaTask ca_create_channel %s\n", - ca_message(status)); + errlogPrintf("dbCaTask ca_create_channel %s\n", + ca_message(status)); + printLinks(pca); continue; } status = ca_replace_access_rights_event(pca->chid, accessRightsCallback); - if(status!=ECA_NORMAL) - errlogPrintf("dbCaTask replace_access_rights_event %s\n", - ca_message(status)); + if(status!=ECA_NORMAL) { + errlogPrintf("dbCaTask replace_access_rights_event %s\n", + ca_message(status)); + printLinks(pca); + } continue; /*Other options must wait until connect*/ } if(ca_state(pca->chid) != cs_conn) continue; if(link_action&CA_WRITE_NATIVE) { assert(pca->pputNative); - status = ca_array_put( - pca->dbrType,pca->nelements, - pca->chid,pca->pputNative); + if(pca->putType==CA_PUT) { + status = ca_array_put( + pca->dbrType,pca->nelements, + pca->chid,pca->pputNative); + } else if(pca->putType==CA_PUT_CALLBACK) { + status = ca_array_put_callback( + pca->dbrType,pca->nelements, + pca->chid,pca->pputNative, + putCallback,pca); + } else { + status = ECA_PUTFAIL; + } + if(status!=ECA_NORMAL) { + errlogPrintf("dbCaTask ca_array_put %s\n", + ca_message(status)); + printLinks(pca); + } epicsMutexMustLock(pca->lock); if(status==ECA_NORMAL) pca->newOutNative = FALSE; epicsMutexUnlock(pca->lock); } if(link_action&CA_WRITE_STRING) { assert(pca->pputString); - status = ca_array_put( - DBR_STRING,1, - pca->chid,pca->pputString); + if(pca->putType==CA_PUT) { + status = ca_array_put( + DBR_STRING,1, + pca->chid,pca->pputString); + } else if(pca->putType==CA_PUT_CALLBACK) { + status = ca_array_put_callback( + DBR_STRING,1, + pca->chid,pca->pputString, + putCallback,pca); + } else { + status = ECA_PUTFAIL; + } + if(status!=ECA_NORMAL) { + errlogPrintf("dbCaTask ca_array_put %s\n", + ca_message(status)); + printLinks(pca); + } epicsMutexMustLock(pca->lock); if(status==ECA_NORMAL) pca->newOutString = FALSE; epicsMutexUnlock(pca->lock); @@ -773,9 +864,11 @@ void dbCaTask() if(link_action&CA_GET_ATTRIBUTES) { status = ca_get_callback(DBR_CTRL_DOUBLE, pca->chid,getAttribEventCallback,pca); - if(status!=ECA_NORMAL) - errlogPrintf("dbCaTask ca_add_array_event %s\n", - ca_message(status)); + if(status!=ECA_NORMAL) { + errlogPrintf("dbCaTask ca_get_callback %s\n", + ca_message(status)); + printLinks(pca); + } } if(link_action&CA_MONITOR_NATIVE) { short element_size; @@ -788,9 +881,11 @@ void dbCaTask() ca_field_type(pca->chid)+DBR_TIME_STRING, ca_element_count(pca->chid), pca->chid, eventCallback,pca,0.0,0.0,0.0,0); - if(status!=ECA_NORMAL) + if(status!=ECA_NORMAL) { errlogPrintf("dbCaTask ca_add_array_event %s\n", - ca_message(status)); + ca_message(status)); + printLinks(pca); + } } if(link_action&CA_MONITOR_STRING) { epicsMutexMustLock(pca->lock); @@ -799,9 +894,11 @@ void dbCaTask() status = ca_add_array_event(DBR_TIME_STRING,1, pca->chid, eventCallback,pca,0.0,0.0,0.0, 0); - if(status!=ECA_NORMAL) + if(status!=ECA_NORMAL) { errlogPrintf("dbCaTask ca_add_array_event %s\n", - ca_message(status)); + ca_message(status)); + printLinks(pca); + } } } SEVCHK(ca_flush_io(),"dbCaTask"); diff --git a/src/db/dbCa.h b/src/db/dbCa.h index 10c0f8ca2..a008aab4a 100644 --- a/src/db/dbCa.h +++ b/src/db/dbCa.h @@ -18,6 +18,8 @@ extern "C" { #endif +typedef void (*dbCaPutCallback)(struct link *plink); + epicsShareFunc void epicsShareAPI dbCaLinkInit(void); epicsShareFunc void epicsShareAPI dbCaAddLink(struct link *plink); epicsShareFunc void epicsShareAPI dbCaRemoveLink(struct link *plink); @@ -26,7 +28,9 @@ epicsShareFunc long epicsShareAPI dbCaGetLink( unsigned short *psevr,long *nRequest); epicsShareFunc long epicsShareAPI dbCaPutLink( struct link *plink,short dbrType,const void *pbuffer,long nRequest); - +epicsShareFunc long epicsShareAPI dbCaPutLinkCallback( + struct link *plink,short dbrType,const void *pbuffer,long nRequest, + dbCaPutCallback callback); epicsShareFunc int epicsShareAPI dbCaIsLinkConnected(const struct link *plink); /* The following are available after the link is connected*/ epicsShareFunc long epicsShareAPI dbCaGetNelements( diff --git a/src/db/dbCaPvt.h b/src/db/dbCaPvt.h index 222c5750a..724366e3f 100644 --- a/src/db/dbCaPvt.h +++ b/src/db/dbCaPvt.h @@ -27,7 +27,10 @@ #define CA_MONITOR_NATIVE 0x10 #define CA_MONITOR_STRING 0x20 #define CA_GET_ATTRIBUTES 0x40 - +/* write type */ +#define CA_PUT 0x1 +#define CA_PUT_CALLBACK 0x2 + typedef struct caLink { ELLNODE node; @@ -46,6 +49,10 @@ typedef struct caLink char hasWriteAccess; char isConnected; char gotFirstConnection; + /* The following are for write request */ + short putType; + dbCaPutCallback putCallback; + struct link *plinkPutCallback; /* The following are for access to additional attributes*/ char gotAttributes; void (*callback)(void *usrPvt); diff --git a/src/db/dbCaTest.c b/src/db/dbCaTest.c index 14737621e..ff11dba19 100644 --- a/src/db/dbCaTest.c +++ b/src/db/dbCaTest.c @@ -44,8 +44,8 @@ #include "dbLock.h" #include "db_access_routines.h" #include "db_convert.h" -#include "dbCaPvt.h" #include "dbCa.h" +#include "dbCaPvt.h" #include "dbCaTest.h"