From b2716f0a196f0db0d63185f35d1146c942779abf Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 10:46:20 -0400 Subject: [PATCH 1/6] dbCa: subscribe to variable length arrays --- src/ioc/db/dbCa.c | 29 ++++++++++++++++++++--------- src/ioc/db/dbCaPvt.h | 6 ++++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index caf0addbb..5e7a609fe 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -346,16 +346,19 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest, assert(pca->pgetNative); status = fConvert(pca->pgetNative, pdest, 0); } else { - long ntoget = *nelements; + unsigned long ntoreport = *nelements, ntoget; struct dbAddr dbAddr; long (*aConvert)(struct dbAddr *paddr, void *to, long nreq, long nto, long off); aConvert = dbGetConvertRoutine[newType][dbrType]; assert(pca->pgetNative); - if (ntoget > pca->nelements) - ntoget = pca->nelements; - *nelements = ntoget; + if (ntoreport > pca->nelements) + ntoreport = pca->nelements; + ntoget = ntoreport; + if (ntoget > pca->usedelements) + ntoget = pca->usedelements; + *nelements = ntoreport; memset((void *)&dbAddr, 0, sizeof(dbAddr)); dbAddr.pfield = pca->pgetNative; @@ -363,6 +366,12 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest, dbAddr.field_size = MAX_STRING_SIZE; /*Ignore error return*/ aConvert(&dbAddr, pdest, ntoget, ntoget, 0); + if(ntogetelementSize+(char*)pca->pgetNative, + 0, + (ntoreport-ntoget)*pca->elementSize); + } } done: if (pstat) *pstat = pca->stat; @@ -705,6 +714,7 @@ static void connectionCallback(struct connection_handler_args arg) } pca->gotFirstConnection = TRUE; pca->nelements = ca_element_count(arg.chid); + pca->usedelements = 0; pca->dbrType = ca_field_type(arg.chid); if ((plink->value.pv_link.pvlMask & pvlOptInpNative) && !pca->pgetNative) { link_action |= CA_MONITOR_NATIVE; @@ -773,6 +783,7 @@ static void eventCallback(struct event_handler_args arg) case DBR_TIME_DOUBLE: assert(pca->pgetNative); memcpy(pca->pgetNative, dbr_value_ptr(arg.dbr, arg.type), size); + pca->usedelements = arg.count; pca->gotInNative = TRUE; break; default: @@ -1031,15 +1042,15 @@ static void dbCaTask(void *arg) } } if (link_action & CA_MONITOR_NATIVE) { - size_t element_size; - - element_size = dbr_value_size[ca_field_type(pca->chid)]; + epicsMutexMustLock(pca->lock); - pca->pgetNative = dbCalloc(pca->nelements, element_size); + pca->elementSize = dbr_value_size[ca_field_type(pca->chid)]; + pca->pgetNative = dbCalloc(pca->nelements, pca->elementSize); epicsMutexUnlock(pca->lock); + status = ca_add_array_event( ca_field_type(pca->chid)+DBR_TIME_STRING, - ca_element_count(pca->chid), + 0, /* dynamic size */ pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0); if (status != ECA_NORMAL) { errlogPrintf("dbCaTask ca_add_array_event %s\n", diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index d5274c577..b50867eda 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -35,7 +35,7 @@ /* write type */ #define CA_PUT 0x1 #define CA_PUT_CALLBACK 0x2 - + typedef struct caLink { ELLNODE node; @@ -51,7 +51,9 @@ typedef struct caLink epicsTimeStamp timeStamp; /* The following have values after connection*/ short dbrType; - long nelements; + size_t elementSize; /* size of one element in pgetNative */ + unsigned long nelements; /* PVs max array size */ + unsigned long usedelements; /* currently used in pgetNative */ char hasReadAccess; char hasWriteAccess; char isConnected; From af1daea3e78bb20b691f398f4f309e1f3ee3fa67 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 10:46:20 -0400 Subject: [PATCH 2/6] dbContext: local CA variable length --- src/ioc/db/dbContext.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ioc/db/dbContext.cpp b/src/ioc/db/dbContext.cpp index 124a836cf..c0f57b759 100644 --- a/src/ioc/db/dbContext.cpp +++ b/src/ioc/db/dbContext.cpp @@ -154,7 +154,8 @@ void dbContext::callStateNotify ( struct dbChannel * dbch, const struct db_field_log * pfl, cacStateNotify & notifyIn ) { - unsigned long size = dbr_size_n ( type, count ); + long realcount = (count==0)?dbChannelElements(dbch):count; + unsigned long size = dbr_size_n ( type, realcount ); if ( type > INT_MAX ) { epicsGuard < epicsMutex > guard ( this->mutex ); @@ -181,8 +182,13 @@ void dbContext::callStateNotify ( struct dbChannel * dbch, this->stateNotifyCacheSize = size; } void *pvfl = (void *) pfl; - int status = dbChannel_get ( dbch, static_cast ( type ), - this->pStateNotifyCache, static_cast ( count ), pvfl ); + int status; + if(count==0) /* fetch actual number of elements (dynamic array) */ + status = dbChannel_get_count( dbch, static_cast ( type ), + this->pStateNotifyCache, &realcount, pvfl ); + else /* fetch requested number of elements, truncated or zero padded */ + status = dbChannel_get( dbch, static_cast ( type ), + this->pStateNotifyCache, realcount, pvfl ); if ( status ) { epicsGuard < epicsMutex > guard ( this->mutex ); notifyIn.exception ( guard, ECA_GETFAIL, @@ -190,7 +196,7 @@ void dbContext::callStateNotify ( struct dbChannel * dbch, } else { epicsGuard < epicsMutex > guard ( this->mutex ); - notifyIn.current ( guard, type, count, this->pStateNotifyCache ); + notifyIn.current ( guard, type, realcount, this->pStateNotifyCache ); } } From 44980a1dac7f104f56b5a02c99907914d2a84931 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Aug 2015 10:46:20 -0400 Subject: [PATCH 3/6] dbContextReadNotifyCache: variable length for CAC gets --- src/ioc/db/dbContextReadNotifyCache.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbContextReadNotifyCache.cpp b/src/ioc/db/dbContextReadNotifyCache.cpp index 700a07f7e..b172068be 100644 --- a/src/ioc/db/dbContextReadNotifyCache.cpp +++ b/src/ioc/db/dbContextReadNotifyCache.cpp @@ -61,27 +61,32 @@ void dbContextReadNotifyCache::callReadNotify ( return; } - if ( dbChannelElements(dbch) < 0 ) { + const long maxcount = dbChannelElements(dbch); + + if ( maxcount < 0 ) { notify.exception ( guard, ECA_BADCOUNT, "database has negetive element count", type, count); return; - } - if ( count > static_cast < unsigned long > ( dbChannelElements(dbch) ) ) { + } else if ( count > (unsigned long)maxcount ) { notify.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; } - unsigned long size = dbr_size_n ( type, count ); + long realcount = (count==0)?maxcount:count; + unsigned long size = dbr_size_n ( type, realcount ); + privateAutoDestroyPtr ptr ( _allocator, size ); int status; { epicsGuardRelease < epicsMutex > unguard ( guard ); - status = dbChannel_get ( dbch, static_cast ( type ), - ptr.get (), static_cast ( count ), 0 ); + if ( count==0 ) + status = dbChannel_get_count ( dbch, (int)type, ptr.get(), &realcount, 0); + else + status = dbChannel_get ( dbch, (int)type, ptr.get (), realcount, 0 ); } if ( status ) { notify.exception ( guard, ECA_GETFAIL, @@ -90,7 +95,7 @@ void dbContextReadNotifyCache::callReadNotify ( } else { notify.completion ( - guard, type, count, ptr.get () ); + guard, type, realcount, ptr.get () ); } } From d6eea14fd0a127a156967c48c40b25d2a9907b67 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 11 Jan 2016 23:01:26 -0500 Subject: [PATCH 4/6] dbCa: dbCaGet/PutLink dynamic size dbCaGetLink return actual number of elements w/o zero padding. dbCaPutLink write only requested number of elements, w/o padding --- src/ioc/db/dbCa.c | 24 +++++++----------------- src/ioc/db/dbCaPvt.h | 1 + src/ioc/db/test/dbCaLinkTest.c | 34 ++++++++++------------------------ 3 files changed, 18 insertions(+), 41 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 5e7a609fe..981503fee 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -346,19 +346,16 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest, assert(pca->pgetNative); status = fConvert(pca->pgetNative, pdest, 0); } else { - unsigned long ntoreport = *nelements, ntoget; + unsigned long ntoget = *nelements; struct dbAddr dbAddr; long (*aConvert)(struct dbAddr *paddr, void *to, long nreq, long nto, long off); aConvert = dbGetConvertRoutine[newType][dbrType]; assert(pca->pgetNative); - if (ntoreport > pca->nelements) - ntoreport = pca->nelements; - ntoget = ntoreport; if (ntoget > pca->usedelements) ntoget = pca->usedelements; - *nelements = ntoreport; + *nelements = ntoget; memset((void *)&dbAddr, 0, sizeof(dbAddr)); dbAddr.pfield = pca->pgetNative; @@ -366,12 +363,6 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest, dbAddr.field_size = MAX_STRING_SIZE; /*Ignore error return*/ aConvert(&dbAddr, pdest, ntoget, ntoget, 0); - if(ntogetelementSize+(char*)pca->pgetNative, - 0, - (ntoreport-ntoget)*pca->elementSize); - } } done: if (pstat) *pstat = pca->stat; @@ -418,6 +409,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType, if (!pca->pputNative) { pca->pputNative = dbCalloc(pca->nelements, dbr_value_size[ca_field_type(pca->chid)]); + pca->putnelements = 0; /* Fixed and disabled by ANJ, see comment above. plink->value.pv_link.pvlMask |= pvlOptOutNative; */ @@ -439,10 +431,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType, if(nRequest>pca->nelements) nRequest = pca->nelements; status = aConvert(&dbAddr, pbuffer, nRequest, pca->nelements, 0); - if(nRequestnelements) { - long elemsize = dbr_value_size[ca_field_type(pca->chid)]; - memset(nRequest*elemsize+(char*)pca->pputNative, 0, (pca->nelements-nRequest)*elemsize); - } + pca->putnelements = nRequest; } link_action |= CA_WRITE_NATIVE; pca->gotOutNative = TRUE; @@ -767,6 +756,7 @@ static void eventCallback(struct event_handler_args arg) goto done; } assert(arg.dbr); + assert(arg.count<=pca->nelements); size = arg.count * dbr_value_size[arg.type]; if (arg.type == DBR_TIME_STRING && ca_field_type(pca->chid) == DBR_ENUM) { @@ -988,11 +978,11 @@ static void dbCaTask(void *arg) assert(pca->pputNative); if (pca->putType == CA_PUT) { status = ca_array_put( - pca->dbrType, pca->nelements, + pca->dbrType, pca->putnelements, pca->chid, pca->pputNative); } else if (pca->putType==CA_PUT_CALLBACK) { status = ca_array_put_callback( - pca->dbrType, pca->nelements, + pca->dbrType, pca->putnelements, pca->chid, pca->pputNative, putComplete, pca); } else { diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index b50867eda..6149d08ae 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -54,6 +54,7 @@ typedef struct caLink size_t elementSize; /* size of one element in pgetNative */ unsigned long nelements; /* PVs max array size */ unsigned long usedelements; /* currently used in pgetNative */ + unsigned long putnelements; /* currently used in pputNative */ char hasReadAccess; char hasWriteAccess; char isConnected; diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c index c0b5f7b9d..43a00e8eb 100644 --- a/src/ioc/db/test/dbCaLinkTest.c +++ b/src/ioc/db/test/dbCaLinkTest.c @@ -294,45 +294,35 @@ static void fillArrayDouble(double *buf, unsigned count, double first) static void checkArray(const char *msg, epicsInt32 *buf, epicsInt32 first, - unsigned used, unsigned total) + unsigned used) { int match = 1; unsigned i; epicsInt32 x, *b; for(b=buf,x=first,i=0;inelm); + checkArray("array update", bufsrc, 1, nReq); } else { testFail("dbGetLink"); testSkip(2, "dbGetLink fails"); @@ -415,12 +405,8 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); dbScanLock((dbCommon*)ptarg); - /* CA links always write the full target array length */ - testOp("%ld",(long)ptarg->nord,==,(long)ptarg->nelm); - /* However, if the source length is less, then the target - * is zero filled - */ - checkArray("array update", buftarg, 2, num, ptarg->nelm); + testOp("%ld",(long)ptarg->nord,==,(long)num); + dbScanUnlock((dbCommon*)ptarg); /* write again to ensure that buffer is completely updated */ @@ -429,8 +415,8 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); dbScanLock((dbCommon*)ptarg); - testOp("%ld",(long)ptarg->nord,==,(long)ptarg->nelm); - checkArray("array update", buftarg, 3, num, ptarg->nelm); + testOp("%ld",(long)ptarg->nord,==,(long)num); + checkArray("array update", buftarg, 3, num); dbScanUnlock((dbCommon*)ptarg); testIocShutdownOk(); @@ -513,7 +499,7 @@ static void testreTargetTypeChange(void) dbScanLock((dbCommon*)psrc); testOp("%ld",(long)psrc->nord,==,(long)5); - checkArrayDouble("array update", bufsrc, 1, 5, psrc->nelm); + checkArrayDouble("array update", bufsrc, 1, 5); dbScanUnlock((dbCommon*)psrc); testDiag("Retarget"); @@ -523,7 +509,7 @@ static void testreTargetTypeChange(void) dbScanLock((dbCommon*)psrc); testOp("%ld",(long)psrc->nord,==,(long)5); - checkArrayDouble("array update", bufsrc, 2, 5, psrc->nelm); + checkArrayDouble("array update", bufsrc, 2, 5); dbScanUnlock((dbCommon*)psrc); testIocShutdownOk(); @@ -583,7 +569,7 @@ static void testCAC(void) MAIN(dbCaLinkTest) { - testPlan(91); + testPlan(87); testNativeLink(); testStringLink(); testCP(); From 1255e75828083bc30fa50ce1ca587de12b8a7c57 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 12 Jan 2016 09:32:47 -0500 Subject: [PATCH 5/6] db/test: missing coverage for dbGetLink w/ larger element count --- src/ioc/db/test/dbCaLinkTest.c | 40 +++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c index 43a00e8eb..e2a87e079 100644 --- a/src/ioc/db/test/dbCaLinkTest.c +++ b/src/ioc/db/test/dbCaLinkTest.c @@ -16,6 +16,7 @@ #include "epicsString.h" #include "dbUnitTest.h" #include "epicsThread.h" +#include "cantProceed.h" #include "epicsEvent.h" #include "iocInit.h" #include "dbBase.h" @@ -345,12 +346,13 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) char buf[100]; arrRecord *psrc, *ptarg; DBLINK *psrclnk; - epicsInt32 *bufsrc, *buftarg; + epicsInt32 *bufsrc, *buftarg, *tmpbuf; long nReq; - unsigned num; + unsigned num_min, num_max; testDiag("Link to a array numeric field"); + /* source.INP = "target CA" */ epicsSnprintf(buf, sizeof(buf), "TARGET=target CA,FTVL=LONG,SNELM=%u,TNELM=%u", nsrc, ntarg); testDiag("%s", buf); @@ -374,9 +376,15 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) bufsrc = psrc->bptr; buftarg= ptarg->bptr; - num=psrc->nelm; - if(num>ptarg->nelm) - num=ptarg->nelm; + num_max=num_min=psrc->nelm; + if(num_min>ptarg->nelm) + num_min=ptarg->nelm; + if(num_maxnelm) + num_max=ptarg->nelm; + /* always request more than can possibly be filled */ + num_max += 2; + + tmpbuf = callocMustSucceed(num_max, sizeof(*tmpbuf), "tmpbuf"); startWait(psrclnk); @@ -389,15 +397,26 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) waitForUpdate(psrclnk); dbScanLock((dbCommon*)psrc); + testDiag("fetch source.INP into source.BPTR"); nReq = psrc->nelm; if(dbGetLink(psrclnk, DBR_LONG, bufsrc, NULL, &nReq)==0) { testPass("dbGetLink"); - testOp("%ld",nReq,==,(long)num); + testOp("%ld",nReq,==,(long)num_min); checkArray("array update", bufsrc, 1, nReq); } else { testFail("dbGetLink"); testSkip(2, "dbGetLink fails"); } + testDiag("fetch source.INP into temp buffer w/ larger capacity"); + nReq = num_max; + if(dbGetLink(psrclnk, DBR_LONG, tmpbuf, NULL, &nReq)==0) { + testPass("dbGetLink"); + testOp("%ld",nReq,==,(long)ntarg); + checkArray("array update", tmpbuf, 1, nReq); + } else { + testFail("dbGetLink"); + testSkip(2, "dbGetLink fails"); + } dbScanUnlock((dbCommon*)psrc); fillArray(bufsrc, psrc->nelm, 2); @@ -405,7 +424,7 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); dbScanLock((dbCommon*)ptarg); - testOp("%ld",(long)ptarg->nord,==,(long)num); + testOp("%ld",(long)ptarg->nord,==,(long)num_min); dbScanUnlock((dbCommon*)ptarg); @@ -415,14 +434,15 @@ static void testArrayLink(unsigned nsrc, unsigned ntarg) putLink(psrclnk, DBR_LONG, bufsrc, psrc->nelm); dbScanLock((dbCommon*)ptarg); - testOp("%ld",(long)ptarg->nord,==,(long)num); - checkArray("array update", buftarg, 3, num); + testOp("%ld",(long)ptarg->nord,==,(long)num_min); + checkArray("array update", buftarg, 3, num_min); dbScanUnlock((dbCommon*)ptarg); testIocShutdownOk(); testdbCleanup(); + free(tmpbuf); /* records don't cleanup after themselves * so do here to silence valgrind */ @@ -569,7 +589,7 @@ static void testCAC(void) MAIN(dbCaLinkTest) { - testPlan(87); + testPlan(99); testNativeLink(); testStringLink(); testCP(); From b74ecff2fc8d0e532712661bc0c72e608968ddf2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 12 Jan 2016 09:38:28 -0500 Subject: [PATCH 6/6] dbCa: set putnelements for scalar case ensure that putnelements is kept in sync. w/ pputNative --- src/ioc/db/dbCa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 981503fee..8d8d19ae2 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -419,6 +419,7 @@ long dbCaPutLinkCallback(struct link *plink,short dbrType, fConvert = dbFastPutConvertRoutine[dbrType][newType]; status = fConvert(pbuffer, pca->pputNative, 0); + pca->putnelements = 1; } else { struct dbAddr dbAddr; long (*aConvert)(struct dbAddr *paddr, const void *from, long nreq, long nfrom, long off);