diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 3939cec7a..965115f3d 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -346,15 +346,15 @@ long dbCaGetLink(struct link *plink,short dbrType, void *pdest, assert(pca->pgetNative); status = fConvert(pca->pgetNative, pdest, 0); } else { - long ntoget = *nelements; + 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 (ntoget > pca->nelements) - ntoget = pca->nelements; + if (ntoget > pca->usedelements) + ntoget = pca->usedelements; *nelements = ntoget; memset((void *)&dbAddr, 0, sizeof(dbAddr)); @@ -409,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; */ @@ -418,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); @@ -430,10 +432,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; @@ -705,6 +704,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; @@ -757,6 +757,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) { @@ -773,6 +774,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: @@ -978,11 +980,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 { @@ -1032,15 +1034,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( dbf_type_to_DBR_TIME(ca_field_type(pca->chid)), - 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..6149d08ae 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,10 @@ 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 */ + unsigned long putnelements; /* currently used in pputNative */ char hasReadAccess; char hasWriteAccess; char isConnected; 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 ); } } 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 () ); } } diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c index 2e086e6ce..e1955a5ad 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" @@ -301,7 +302,7 @@ 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; @@ -309,22 +310,17 @@ static void checkArray(const char *msg, for(b=buf,x=first,i=0;ibptr; 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); @@ -406,11 +404,22 @@ 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); - checkArray("array update", bufsrc, 1, nReq, psrc->nelm); + 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"); @@ -422,12 +431,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_min); + dbScanUnlock((dbCommon*)ptarg); /* write again to ensure that buffer is completely updated */ @@ -436,14 +441,15 @@ 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_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 */ @@ -522,7 +528,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"); @@ -532,7 +538,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(); @@ -592,7 +598,7 @@ static void testCAC(void) MAIN(dbCaLinkTest) { - testPlan(91); + testPlan(99); testNativeLink(); testStringLink(); testCP();