From 8cc20393f1e34cf43678dd82f2b29fa5e3522cf0 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 5 Jun 2020 11:00:58 +0200 Subject: [PATCH 01/43] fix dbr size of empty arrays. Fixes caget returning non 0 in first element --- modules/ca/src/client/db_access.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ca/src/client/db_access.h b/modules/ca/src/client/db_access.h index 810f82ff7..ad10a88e1 100644 --- a/modules/ca/src/client/db_access.h +++ b/modules/ca/src/client/db_access.h @@ -516,7 +516,7 @@ struct dbr_ctrl_double{ }; #define dbr_size_n(TYPE,COUNT)\ -((unsigned)((COUNT)<=0?dbr_size[TYPE]:dbr_size[TYPE]+((COUNT)-1)*dbr_value_size[TYPE])) +((unsigned)((COUNT)<0?dbr_size[TYPE]:dbr_size[TYPE]+((COUNT)-1)*dbr_value_size[TYPE])) /* size for each type - array indexed by the DBR_ type code */ LIBCA_API extern const unsigned short dbr_size[]; From 19c50d4c3db32668b4d5bebfb7e6d15e87cd10f4 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 5 Jun 2020 16:01:14 +0200 Subject: [PATCH 02/43] fix aai and waveform soft device support to support reading empty arrays --- modules/database/src/std/dev/devAaiSoft.c | 11 ++++++++--- modules/database/src/std/dev/devWfSoft.c | 7 +++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/modules/database/src/std/dev/devAaiSoft.c b/modules/database/src/std/dev/devAaiSoft.c index 1f5765650..0fbb1148e 100644 --- a/modules/database/src/std/dev/devAaiSoft.c +++ b/modules/database/src/std/dev/devAaiSoft.c @@ -60,9 +60,10 @@ static long init_record(dbCommon *pcommon) } status = dbLoadLinkArray(plink, prec->ftvl, prec->bptr, &nRequest); - if (!status && nRequest > 0) { + if (!status) { prec->nord = nRequest; prec->udf = FALSE; + return status; } } return 0; @@ -74,7 +75,7 @@ static long readLocked(struct link *pinp, void *dummy) long nRequest = prec->nelm; long status = dbGetLink(pinp, prec->ftvl, prec->bptr, 0, &nRequest); - if (!status && nRequest > 0) { + if (!status) { prec->nord = nRequest; prec->udf = FALSE; @@ -89,8 +90,12 @@ static long read_aai(aaiRecord *prec) { epicsUInt32 nord = prec->nord; struct link *pinp = prec->simm == menuYesNoYES ? &prec->siol : &prec->inp; - long status = dbLinkDoLocked(pinp, readLocked, NULL); + long status; + if (dbLinkIsConstant(pinp)) + return 0; + + status = dbLinkDoLocked(pinp, readLocked, NULL); if (status == S_db_noLSET) status = readLocked(pinp, NULL); diff --git a/modules/database/src/std/dev/devWfSoft.c b/modules/database/src/std/dev/devWfSoft.c index 0a089b831..29a617be3 100644 --- a/modules/database/src/std/dev/devWfSoft.c +++ b/modules/database/src/std/dev/devWfSoft.c @@ -41,7 +41,7 @@ static long init_record(dbCommon *pcommon) long nelm = prec->nelm; long status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nelm); - if (!status && nelm > 0) { + if (!status) { prec->nord = nelm; prec->udf = FALSE; } @@ -77,11 +77,14 @@ static long read_wf(waveformRecord *prec) rt.ptime = (dbLinkIsConstant(&prec->tsel) && prec->tse == epicsTimeEventDeviceTime) ? &prec->time : NULL; + if (dbLinkIsConstant(&prec->inp)) + return 0; + status = dbLinkDoLocked(&prec->inp, readLocked, &rt); if (status == S_db_noLSET) status = readLocked(&prec->inp, &rt); - if (!status && rt.nRequest > 0) { + if (!status) { prec->nord = rt.nRequest; prec->udf = FALSE; if (nord != prec->nord) From c4c13d8ce03743446154903bd360e0da8991ce67 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 5 Jun 2020 16:14:02 +0200 Subject: [PATCH 03/43] fix subArray soft device support to support reading empty arrays --- modules/database/src/std/dev/devSASoft.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/database/src/std/dev/devSASoft.c b/modules/database/src/std/dev/devSASoft.c index be32af458..8db192f14 100644 --- a/modules/database/src/std/dev/devSASoft.c +++ b/modules/database/src/std/dev/devSASoft.c @@ -65,7 +65,7 @@ static long init_record(dbCommon *pcommon) status = dbLoadLinkArray(&prec->inp, prec->ftvl, prec->bptr, &nRequest); - if (!status && nRequest > 0) + if (!status) subset(prec, nRequest); return status; @@ -115,7 +115,7 @@ static long read_sa(subArrayRecord *prec) status = readLocked(&prec->inp, &rt); } - if (!status && rt.nRequest > 0) { + if (!status) { subset(prec, rt.nRequest); if (nord != prec->nord) From e4dcd3cefdabc52157ed139e06ec7a6309c55dcb Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 5 Jun 2020 16:19:10 +0200 Subject: [PATCH 04/43] fix aSub record to support reading empty arrays --- modules/database/src/std/rec/aSubRecord.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/database/src/std/rec/aSubRecord.c b/modules/database/src/std/rec/aSubRecord.c index 666558bbf..1bcbb63e4 100644 --- a/modules/database/src/std/rec/aSubRecord.c +++ b/modules/database/src/std/rec/aSubRecord.c @@ -277,10 +277,9 @@ static long fetch_values(aSubRecord *prec) long nRequest = (&prec->noa)[i]; status = dbGetLink(&(&prec->inpa)[i], (&prec->fta)[i], (&prec->a)[i], 0, &nRequest); - if (nRequest > 0) - (&prec->nea)[i] = nRequest; if (status) return status; + (&prec->nea)[i] = nRequest; } return 0; } From f8035d8d5eb19501b729efd3369a002be94aa7bc Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 8 Jun 2020 10:36:40 +0200 Subject: [PATCH 05/43] support arrays in dbpf --- modules/database/src/ioc/db/dbTest.c | 43 +++++++++++++++++++++------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c index 11939545b..9fcbfdf84 100644 --- a/modules/database/src/ioc/db/dbTest.c +++ b/modules/database/src/ioc/db/dbTest.c @@ -363,8 +363,9 @@ long dbpf(const char *pname,const char *pvalue) { DBADDR addr; long status; - short dbrType; - size_t n = 1; + short dbrType = DBR_STRING; + long n = 1; + epicsOldString *array = NULL; if (!pname || !*pname || !pvalue) { printf("Usage: dbpf \"pv name\", \"value\"\n"); @@ -379,16 +380,36 @@ long dbpf(const char *pname,const char *pvalue) return -1; } - if (addr.no_elements > 1 && - (addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR)) { - dbrType = addr.dbr_field_type; - n = strlen(pvalue) + 1; - } - else { - dbrType = DBR_STRING; - } + if (addr.no_elements > 1) { + if (addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR) { + dbrType = addr.dbr_field_type; + n = (long)strlen(pvalue) + 1; + } else { + const char *p = pvalue; + epicsOldString *array; - status = dbPutField(&addr, dbrType, pvalue, (long) n); + for (n = 0; *p && n < addr.no_elements; n++) { + while (isspace(*p)) p++; + while (*p && !isspace(*p)) { + if (p[0] == '\\' && p[1]) p++; + p++; + } + } + p = pvalue; + array = dbCalloc(n, sizeof(epicsOldString)); + for (n = 0; *p && n < addr.no_elements; n++) { + char* c = array[n]; + while (isspace(*p)) p++; + while (*p && !isspace(*p)) { + if (p[0] == '\\' && p[1]) p++; + *c++=*p++; + } + } + pvalue = (void*)array; + } + } + status = dbPutField(&addr, dbrType, pvalue, n); + free(array); dbgf(pname); return status; } From a42197f0d670066c63f46b0af2eb3d01e8bb14f5 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 8 Jun 2020 16:03:27 +0200 Subject: [PATCH 06/43] allow to write empty arrays with caput --- modules/ca/src/client/nciu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ca/src/client/nciu.cpp b/modules/ca/src/client/nciu.cpp index 0a7e70899..ebf30fec7 100644 --- a/modules/ca/src/client/nciu.cpp +++ b/modules/ca/src/client/nciu.cpp @@ -328,7 +328,7 @@ void nciu::write ( if ( ! this->accessRightState.writePermit() ) { throw cacChannel::noWriteAccess(); } - if ( countIn > this->count || countIn == 0 ) { + if ( countIn > this->count) { throw cacChannel::outOfBounds(); } if ( type == DBR_STRING ) { @@ -349,7 +349,7 @@ cacChannel::ioStatus nciu::write ( if ( ! this->accessRightState.writePermit() ) { throw cacChannel::noWriteAccess(); } - if ( countIn > this->count || countIn == 0 ) { + if ( countIn > this->count) { throw cacChannel::outOfBounds(); } if ( type == DBR_STRING ) { From 3176651c7116b2bbc8aedc0ec28a562c900cfbc3 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 9 Jun 2020 16:14:49 +0200 Subject: [PATCH 07/43] fix dbGet to fail when reading scalars from empty arrays --- modules/database/src/ioc/db/dbAccess.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index 9cda401ad..112b6152b 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -945,6 +945,11 @@ long dbGet(DBADDR *paddr, short dbrType, if (offset == 0 && (!nRequest || no_elements == 1)) { if (nRequest) *nRequest = 1; + else if (no_elements < 1) { + status = S_db_onlyOne; + goto done; + } + if (!pfl || pfl->type == dbfl_type_rec) { status = dbFastGetConvertRoutine[field_type][dbrType] (paddr->pfield, pbuf, paddr); From e68e38ad95f6cfae2d619dda179548f2ccc146c2 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 10 Jun 2020 17:48:09 +0200 Subject: [PATCH 08/43] update RELEASE_NOTES.md about empty arrays --- documentation/RELEASE_NOTES.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 6fd80615a..274d65f4e 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -17,6 +17,36 @@ should also be read to understand what has changed since earlier releases. +### Support for empty arrays + +Several problems with empty arrays have been fixed. + +#### Changed dbr_size_n(TYPE,COUNT) macro + +When called with COUNT=0 the macro no longer returns the number of bytes +required for a scalar (1 element) but for an empty array (0 elements). +Make sure you don't call it with COUNT=0 when you really mean COUNT=1. + +#### Array records + +The soft supports of array records aai, waveform, and subArray as well as +the aSub record type have been fixed to correctly report 0 elements read +when reading empty arrays from an input link. + +#### Array support for dbpf + +The dbpf function now accepts arrays, including empty arrays, as a quoted +whitespace separated list of values. + +#### Scalar records reading from empty arrays + +Scalar records reading from empty arrays via database links are now set to +INVALID/LINK alarm status. +Links have to call dbGet with pnRequest=NULL to be recognised as requests +for scalars. +This changes the semantics of pnRequest=NULL. It is now different from +requesting 1 element, which is counted as an array request and may return +a valid empty array. ## EPICS Release 7.0.4 From dec4fc30d948d4c717430ada3f1366d6dfa204b2 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 22 Jun 2020 11:30:59 +0200 Subject: [PATCH 09/43] bugfix in dbpf --- modules/database/src/ioc/db/dbTest.c | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c index 9fcbfdf84..7000c4679 100644 --- a/modules/database/src/ioc/db/dbTest.c +++ b/modules/database/src/ioc/db/dbTest.c @@ -386,7 +386,6 @@ long dbpf(const char *pname,const char *pvalue) n = (long)strlen(pvalue) + 1; } else { const char *p = pvalue; - epicsOldString *array; for (n = 0; *p && n < addr.no_elements; n++) { while (isspace(*p)) p++; From 73b86d49211ac11de35ee654bd3db707951e3bd2 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 22 Jun 2020 13:23:26 +0200 Subject: [PATCH 10/43] prevent buffer overflow in dbpf --- modules/database/src/ioc/db/dbTest.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c index 7000c4679..19d392216 100644 --- a/modules/database/src/ioc/db/dbTest.c +++ b/modules/database/src/ioc/db/dbTest.c @@ -399,12 +399,18 @@ long dbpf(const char *pname,const char *pvalue) for (n = 0; *p && n < addr.no_elements; n++) { char* c = array[n]; while (isspace(*p)) p++; + pvalue = p; while (*p && !isspace(*p)) { if (p[0] == '\\' && p[1]) p++; + if (c >= array[n+1]-1) { + printf("Value [%ld] %.*s too long\n", n, (int)(p-pvalue), pvalue); + free(array); + return -1; + } *c++=*p++; } } - pvalue = (void*)array; + pvalue = array[0]; } } status = dbPutField(&addr, dbrType, pvalue, n); From 0353ede517311191400d2b6fff5cdc2846d97c4b Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Sat, 27 Jun 2020 16:05:54 +0200 Subject: [PATCH 11/43] don't use epicsOldString --- modules/database/src/ioc/db/dbTest.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c index 19d392216..25782a243 100644 --- a/modules/database/src/ioc/db/dbTest.c +++ b/modules/database/src/ioc/db/dbTest.c @@ -365,7 +365,7 @@ long dbpf(const char *pname,const char *pvalue) long status; short dbrType = DBR_STRING; long n = 1; - epicsOldString *array = NULL; + char *array = NULL; if (!pname || !*pname || !pvalue) { printf("Usage: dbpf \"pv name\", \"value\"\n"); @@ -395,14 +395,14 @@ long dbpf(const char *pname,const char *pvalue) } } p = pvalue; - array = dbCalloc(n, sizeof(epicsOldString)); + array = dbCalloc(n, MAX_STRING_SIZE); for (n = 0; *p && n < addr.no_elements; n++) { - char* c = array[n]; + char* c = &array[n*MAX_STRING_SIZE]; while (isspace(*p)) p++; pvalue = p; while (*p && !isspace(*p)) { if (p[0] == '\\' && p[1]) p++; - if (c >= array[n+1]-1) { + if (c >= &array[(n+1)*MAX_STRING_SIZE]-1) { printf("Value [%ld] %.*s too long\n", n, (int)(p-pvalue), pvalue); free(array); return -1; @@ -410,7 +410,7 @@ long dbpf(const char *pname,const char *pvalue) *c++=*p++; } } - pvalue = array[0]; + pvalue = array; } } status = dbPutField(&addr, dbrType, pvalue, n); From 473790124bbf1a5f0cd377082ae71a399caf3a30 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 13 Feb 2020 17:07:42 +0100 Subject: [PATCH 12/43] bugfix: ai SoftDevice should return error status when get fails --- modules/database/src/std/dev/devAiSoft.c | 3 ++- modules/database/test/std/rec/regressTest.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/database/src/std/dev/devAiSoft.c b/modules/database/src/std/dev/devAiSoft.c index e41d9fedb..a9b32a335 100644 --- a/modules/database/src/std/dev/devAiSoft.c +++ b/modules/database/src/std/dev/devAiSoft.c @@ -85,9 +85,10 @@ static long read_ai(aiRecord *prec) prec->udf = FALSE; prec->dpvt = &devAiSoft; /* Any non-zero value */ + return 2; } else prec->dpvt = NULL; - return 2; + return status; } diff --git a/modules/database/test/std/rec/regressTest.c b/modules/database/test/std/rec/regressTest.c index 661463984..fd4f0a885 100644 --- a/modules/database/test/std/rec/regressTest.c +++ b/modules/database/test/std/rec/regressTest.c @@ -132,7 +132,7 @@ void testCADisconn(void) startRegressTestIoc("badCaLink.db"); - testdbPutFieldOk("ai:disconn.PROC", DBF_LONG, 1); + testdbPutFieldFail(-1, "ai:disconn.PROC", DBF_LONG, 1); testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM); testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM); } From 0a1fb25e6bb9523f69b69d845706028c35ac72ca Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 29 Jun 2020 22:23:21 +0200 Subject: [PATCH 13/43] fix dbCaGetLink to fail when reading scalars from empty arrays --- modules/database/src/ioc/db/dbCa.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index 4ae39bbce..28e0df6d8 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/src/ioc/db/dbCa.c @@ -410,9 +410,15 @@ long dbCaGetLink(struct link *plink, short dbrType, void *pdest, goto done; } newType = dbDBRoldToDBFnew[pca->dbrType]; - if (!nelements || *nelements == 1) { + if (!nelements) { long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); + if (pca->usedelements < 1) { + pca->sevr = INVALID_ALARM; + pca->stat = LINK_ALARM; + status = -1; + goto done; + } fConvert = dbFastGetConvertRoutine[newType][dbrType]; assert(pca->pgetNative); status = fConvert(pca->pgetNative, pdest, 0); From e5a48f152a09f72ea01577efbfdd5a174c878778 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 29 Jun 2020 23:00:30 +0200 Subject: [PATCH 14/43] RELEASE_NOTES updated --- documentation/RELEASE_NOTES.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 274d65f4e..03021e482 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -40,13 +40,12 @@ whitespace separated list of values. #### Scalar records reading from empty arrays -Scalar records reading from empty arrays via database links are now set to -INVALID/LINK alarm status. -Links have to call dbGet with pnRequest=NULL to be recognised as requests +Scalar records reading from empty arrays are now set to INVALID/LINK alarm +status. +Links have to call dbGetLink with pnRequest=NULL to be recognised as requests for scalars. This changes the semantics of pnRequest=NULL. It is now different from -requesting 1 element, which is counted as an array request and may return -a valid empty array. +requesting up to 1 array element, which may return a valid empty array. ## EPICS Release 7.0.4 From 12cfd418d62e066e1ea08ef2af6603d27168999d Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 6 Jul 2020 14:58:00 +0200 Subject: [PATCH 15/43] fix dbPut to set target to INVALID/LINK alarm when writing empty arrays into scalars --- modules/database/src/ioc/db/dbAccess.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index 112b6152b..4fbfa966e 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -1334,25 +1334,21 @@ long dbPut(DBADDR *paddr, short dbrType, status = prset->get_array_info(paddr, &dummy, &offset); /* paddr->pfield may be modified */ if (status) goto done; - } else - offset = 0; - - if (no_elements <= 1) { - status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, - paddr->pfield, paddr); - nRequest = 1; - } else { if (no_elements < nRequest) nRequest = no_elements; status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer, nRequest, no_elements, offset); - } - - /* update array info */ - if (!status && - paddr->pfldDes->special == SPC_DBADDR && - prset && prset->put_array_info) { - status = prset->put_array_info(paddr, nRequest); + /* update array info */ + if (!status) + status = prset->put_array_info(paddr, nRequest); + } else { + if (nRequest < 1) { + recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); + } else { + status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, + paddr->pfield, paddr); + nRequest = 1; + } } /* Always do special processing if needed */ From 4368697f58a2dda9d94b0d18384120d2b24b9221 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 13 Jul 2020 14:53:20 +0200 Subject: [PATCH 16/43] Updated RELEASE_NOTES.md --- documentation/RELEASE_NOTES.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 03021e482..6464c7470 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -40,13 +40,21 @@ whitespace separated list of values. #### Scalar records reading from empty arrays -Scalar records reading from empty arrays are now set to INVALID/LINK alarm -status. -Links have to call dbGetLink with pnRequest=NULL to be recognised as requests +Records reading scalar fields from empty arrays are now set to INVALID/LINK +alarm status. +Links have to call dbGetLink with pnRequest=NULL to be recognized as requests for scalars. This changes the semantics of pnRequest=NULL. It is now different from requesting up to 1 array element, which may return a valid empty array. +### Writing empty arrays to scalar records + +Witing an empty array to a scalar field now sets the target record to +INVALID/LINK alarm but does not modify the value. Before, the value used +to be set to 0 (without any alarm). +A target field needs to have the SPC_DBADDR tag to be recognized as an array +field and the record support must define a put_array_info method. + ## EPICS Release 7.0.4 ### Bug fixes From a9731b90f6c5469449dd33085f76aae1bae3fc78 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 17 Jul 2020 09:12:54 +0200 Subject: [PATCH 17/43] Don't freeze the shell when we are out of memory --- modules/database/src/ioc/db/dbTest.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c index 25782a243..a312abbf7 100644 --- a/modules/database/src/ioc/db/dbTest.c +++ b/modules/database/src/ioc/db/dbTest.c @@ -395,7 +395,11 @@ long dbpf(const char *pname,const char *pvalue) } } p = pvalue; - array = dbCalloc(n, MAX_STRING_SIZE); + array = calloc(n, MAX_STRING_SIZE); + if (!array) { + printf("Out of memory\n"); + return -1; + } for (n = 0; *p && n < addr.no_elements; n++) { char* c = &array[n*MAX_STRING_SIZE]; while (isspace(*p)) p++; From 77092396369ce5655900817c299953c7f1a1465b Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 17 Jul 2020 09:26:55 +0200 Subject: [PATCH 18/43] make sure put_array_info exists before using it --- modules/database/src/ioc/db/dbAccess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index 4fbfa966e..28273f667 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -1339,7 +1339,7 @@ long dbPut(DBADDR *paddr, short dbrType, status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer, nRequest, no_elements, offset); /* update array info */ - if (!status) + if (!status && prset->put_array_info) status = prset->put_array_info(paddr, nRequest); } else { if (nRequest < 1) { From d1491e0860efc2ea3bd85a9f68cd83f18b575ae0 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 17 Jul 2020 15:03:53 +0200 Subject: [PATCH 19/43] Use JSON arrays in dbpf --- documentation/RELEASE_NOTES.md | 3 +-- modules/database/src/ioc/db/dbTest.c | 33 ++++++---------------------- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 6464c7470..fb6f691e3 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -35,8 +35,7 @@ when reading empty arrays from an input link. #### Array support for dbpf -The dbpf function now accepts arrays, including empty arrays, as a quoted -whitespace separated list of values. +The dbpf function now accepts arrays, including empty arrays as a JSON string. #### Scalar records reading from empty arrays diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c index a312abbf7..9368bfc71 100644 --- a/modules/database/src/ioc/db/dbTest.c +++ b/modules/database/src/ioc/db/dbTest.c @@ -40,6 +40,7 @@ #include "recGbl.h" #include "recSup.h" #include "special.h" +#include "dbConvertJSON.h" #define MAXLINE 80 #define MAXMESS 128 @@ -381,39 +382,19 @@ long dbpf(const char *pname,const char *pvalue) } if (addr.no_elements > 1) { + dbrType = addr.dbr_field_type; if (addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR) { - dbrType = addr.dbr_field_type; n = (long)strlen(pvalue) + 1; } else { - const char *p = pvalue; - - for (n = 0; *p && n < addr.no_elements; n++) { - while (isspace(*p)) p++; - while (*p && !isspace(*p)) { - if (p[0] == '\\' && p[1]) p++; - p++; - } - } - p = pvalue; - array = calloc(n, MAX_STRING_SIZE); + n = addr.no_elements; + array = calloc(n, dbValueSize(dbrType)); if (!array) { printf("Out of memory\n"); return -1; } - for (n = 0; *p && n < addr.no_elements; n++) { - char* c = &array[n*MAX_STRING_SIZE]; - while (isspace(*p)) p++; - pvalue = p; - while (*p && !isspace(*p)) { - if (p[0] == '\\' && p[1]) p++; - if (c >= &array[(n+1)*MAX_STRING_SIZE]-1) { - printf("Value [%ld] %.*s too long\n", n, (int)(p-pvalue), pvalue); - free(array); - return -1; - } - *c++=*p++; - } - } + status = dbPutConvertJSON(pvalue, dbrType, array, &n); + if (status) + return status; pvalue = array; } } From 297f04bddc6765d7fc2b5941e16054b448fc994e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 30 Oct 2020 13:37:50 -0500 Subject: [PATCH 20/43] Make dbgf display something for an empty array Also significantly expands on Dirk's Release Notes entries. --- documentation/RELEASE_NOTES.md | 86 +++++++++++++++++++++------- modules/database/src/ioc/db/dbTest.c | 8 +-- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 0a54dc67f..f21fbdcec 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -17,42 +17,84 @@ should also be read to understand what has changed since earlier releases. -### Support for empty arrays +### Support for zero-length arrays -Several problems with empty arrays have been fixed. +Several modifications have been made to properly support zero-length +array values inside the IOC and over Channel Access. Some of these changes +may affect external code that interfaces with the IOC, either directly or +over the CA client API so we recommend thorough testing of any external +code that handles array fields when upgrading to this release. -#### Changed dbr_size_n(TYPE,COUNT) macro +Since these changes affect the Channel Access client-side API they will +require rebuilding any CA Gateways against this version or Base to +properly handle zero-length arrays. The `caget`, `caput` and `camonitor` +client programs are known to work with empty arrays as long as they were +built with this or a later version of EPICS. -When called with COUNT=0 the macro no longer returns the number of bytes +#### Change to the db_access.h `dbr_size_n(TYPE, COUNT)` macro + +When called with COUNT=0 this macro no longer returns the number of bytes required for a scalar (1 element) but for an empty array (0 elements). -Make sure you don't call it with COUNT=0 when you really mean COUNT=1. +Make sure code that uses this doesn't call it with COUNT=0 when it really +means COUNT=1. + +Note that the db_access.h header file is included by cadef.h so the change +can impact Channel Access client programs that use this macro. + +#### Channel Access support for zero-length arrays + +The `ca_array_put()` and `ca_array_put_callback()` routines now accept an +element count of zero, and will write a zero-length array to the PV if +possible. No error will be raised if the target is a scalar field though, +and the field's value will not be changed. + +The `ca_array_get_callback()` and `ca_create_subscription()` routines +still accept a count of zero to mean fetch as many elements as the PV +currently holds. + +Client programs should be prepared for the `count` fields of any +`struct event_handler_args` or `struct exception_handler_args` passed to +their callback routines to be zero. #### Array records -The soft supports of array records aai, waveform, and subArray as well as -the aSub record type have been fixed to correctly report 0 elements read -when reading empty arrays from an input link. +The soft device support for the array records aai, waveform, and subArray +as well as the aSub record type now correctly report reading 0 elements +when getting an empty array from an input link. #### Array support for dbpf -The dbpf function now accepts arrays, including empty arrays as a JSON string. +The dbpf command now accepts array values, including empty arrays, when +provided as a JSON string. This must be enclosed in quotes so the iocsh +argument parser sees the JSON as a single argument: -#### Scalar records reading from empty arrays +``` +epics> dbpf wf10:i32 '[1, 2, 3, 4, 5]' +DBF_LONG[5]: 1 = 0x1 2 = 0x2 3 = 0x3 4 = 0x4 5 = 0x5 +``` -Records reading scalar fields from empty arrays are now set to INVALID/LINK -alarm status. -Links have to call dbGetLink with pnRequest=NULL to be recognized as requests -for scalars. -This changes the semantics of pnRequest=NULL. It is now different from -requesting up to 1 array element, which may return a valid empty array. +#### Reading empty arrays as scalar values -### Writing empty arrays to scalar records +Record links that get a scalar value from an array that is currently +empty will cause the record that has the link field to be set to an +`INVALID/LINK` alarm status. +The record code must call `dbGetLink()` with `pnRequest=NULL` for it to +be recognized as a request for a scalar value though. -Witing an empty array to a scalar field now sets the target record to -INVALID/LINK alarm but does not modify the value. Before, the value used -to be set to 0 (without any alarm). -A target field needs to have the SPC_DBADDR tag to be recognized as an array -field and the record support must define a put_array_info method. +This changes the semantics of passing `pnRequest=NULL` to `dbGetLink()`, +which now behaves differently than passing it a pointer to a long integer +containing the value 1, which was previously equivalent. +The latter can successfully fetch a zero-element array without triggering +a LINK alarm. + +#### Writing empty arrays to scalar fields + +Record links that put a zero-element array into a scalar field will now set +the target record to `INVALID/LINK` alarm without changing the field's value. +Previously the field was set to 0 in this case (with no alarm). +The target field must be marked as `special(SPC_DBADDR)` to be recognized +as an array field, and its record support must define a `put_array_info()` +routine. ### Add registerAllRecordDeviceDrivers() diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c index d4ac1d792..6106027a8 100644 --- a/modules/database/src/ioc/db/dbTest.c +++ b/modules/database/src/ioc/db/dbTest.c @@ -954,13 +954,13 @@ static void printBuffer( } /* Now print values */ - if (no_elements == 0) - return; - if (no_elements == 1) sprintf(pmsg, "DBF_%s: ", dbr[dbr_type]); - else + else { sprintf(pmsg, "DBF_%s[%ld]: ", dbr[dbr_type], no_elements); + if (no_elements == 0) + strcat(pmsg, "(empty)"); + } dbpr_msgOut(pMsgBuff, tab_size); if (status != 0) { From b1f445925d8de11bca94e7d0210a40af8916bcd9 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 11 Feb 2020 17:29:16 +0100 Subject: [PATCH 21/43] use dbChannel in link instead of DBADDR --- modules/database/src/ioc/db/dbAccess.c | 24 +++--- modules/database/src/ioc/db/dbDbLink.c | 112 ++++++++++++++----------- modules/database/src/ioc/db/dbDbLink.h | 2 +- modules/database/src/ioc/db/dbLink.c | 2 +- modules/database/src/ioc/db/dbLink.h | 3 +- modules/database/src/ioc/db/dbLock.c | 6 +- 6 files changed, 79 insertions(+), 70 deletions(-) diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index 2f20f41b8..da8467a0c 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -1042,7 +1042,7 @@ static long dbPutFieldLink(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest) { dbLinkInfo link_info; - DBADDR *pdbaddr = NULL; + dbChannel *chan = NULL; dbCommon *precord = paddr->precord; dbCommon *lockrecs[2]; dbLocker locker; @@ -1080,16 +1080,11 @@ static long dbPutFieldLink(DBADDR *paddr, if (link_info.ltype == PV_LINK && (link_info.modifiers & (pvlOptCA | pvlOptCP | pvlOptCPP)) == 0) { - DBADDR tempaddr; - - if (dbNameToAddr(link_info.target, &tempaddr)==0) { - /* This will become a DB link. */ - pdbaddr = malloc(sizeof(*pdbaddr)); - if (!pdbaddr) { - status = S_db_noMemory; - goto cleanup; - } - *pdbaddr = tempaddr; /* struct copy */ + chan = dbChannelCreate(link_info.target); + if (chan && dbChannelOpen(chan) != 0) { + errlogPrintf("ERROR: dbPutFieldLink %s.%s=%s: dbChannelOpen() failed\n", + precord->name, pfldDes->name, link_info.target); + goto cleanup; } } @@ -1098,7 +1093,7 @@ static long dbPutFieldLink(DBADDR *paddr, memset(&locker, 0, sizeof(locker)); lockrecs[0] = precord; - lockrecs[1] = pdbaddr ? pdbaddr->precord : NULL; + lockrecs[1] = chan ? dbChannelRecord(chan) : NULL; dbLockerPrepare(&locker, lockrecs, 2); dbScanLockMany(&locker); @@ -1186,7 +1181,8 @@ static long dbPutFieldLink(DBADDR *paddr, case PV_LINK: case CONSTANT: case JSON_LINK: - dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr); + dbAddLink(&locker, plink, pfldDes->field_type, chan); + chan = NULL; /* don't clean it up */ break; case DB_LINK: @@ -1216,6 +1212,8 @@ unlock: dbScanUnlockMany(&locker); dbLockerFinalize(&locker); cleanup: + if (chan) + dbChannelDelete(chan); free(link_info.target); return status; } diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index e89759a90..a86272eb9 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -74,7 +74,7 @@ #include "recSup.h" #include "special.h" #include "dbDbLink.h" - +#include "dbChannel.h" /***************************** Database Links *****************************/ @@ -83,45 +83,50 @@ static lset dbDb_lset; static long processTarget(dbCommon *psrc, dbCommon *pdst); +#define linkChannel(plink) ((dbChannel *) (plink)->value.pv_link.pvt) + long dbDbInitLink(struct link *plink, short dbfType) { - DBADDR dbaddr; long status; - DBADDR *pdbAddr; + dbChannel *chan; + dbCommon *precord; - status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr); + chan = dbChannelCreate(plink->value.pv_link.pvname); + if (!chan) + return S_db_notFound; + status = dbChannelOpen(chan); if (status) return status; + precord = dbChannelRecord(chan); plink->lset = &dbDb_lset; plink->type = DB_LINK; - pdbAddr = dbCalloc(1, sizeof(struct dbAddr)); - *pdbAddr = dbaddr; /* structure copy */ - plink->value.pv_link.pvt = pdbAddr; - ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode); + plink->value.pv_link.pvt = chan; + ellAdd(&precord->bklnk, &plink->value.pv_link.backlinknode); /* merging into the same lockset is deferred to the caller. * cf. initPVLinks() */ - dbLockSetMerge(NULL, plink->precord, dbaddr.precord); - assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet); + dbLockSetMerge(NULL, plink->precord, precord); + assert(plink->precord->lset->plockSet == precord->lset->plockSet); return 0; } void dbDbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, - DBADDR *ptarget) + dbChannel *ptarget) { plink->lset = &dbDb_lset; plink->type = DB_LINK; plink->value.pv_link.pvt = ptarget; - ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode); + ellAdd(&dbChannelRecord(ptarget)->bklnk, &plink->value.pv_link.backlinknode); /* target record is already locked in dbPutFieldLink() */ - dbLockSetMerge(locker, plink->precord, ptarget->precord); + dbLockSetMerge(locker, plink->precord, dbChannelRecord(ptarget)); } static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink) { - DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt; + dbChannel *chan = linkChannel(plink); + dbCommon *precord = dbChannelRecord(chan); plink->type = PV_LINK; @@ -131,10 +136,10 @@ static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink) plink->value.pv_link.getCvt = 0; plink->value.pv_link.pvlMask = 0; plink->value.pv_link.lastGetdbrType = 0; - ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode); - dbLockSetSplit(locker, plink->precord, pdbAddr->precord); + ellDelete(&precord->bklnk, &plink->value.pv_link.backlinknode); + dbLockSetSplit(locker, plink->precord, precord); } - free(pdbAddr); + dbChannelDelete(chan); } static int dbDbIsConnected(const struct link *plink) @@ -144,16 +149,14 @@ static int dbDbIsConnected(const struct link *plink) static int dbDbGetDBFtype(const struct link *plink) { - DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; - - return paddr->field_type; + dbChannel *chan = linkChannel(plink); + return dbChannelFinalFieldType(chan); } static long dbDbGetElements(const struct link *plink, long *nelements) { - DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; - - *nelements = paddr->no_elements; + dbChannel *chan = linkChannel(plink); + *nelements = dbChannelFinalElements(chan); return 0; } @@ -161,30 +164,31 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, long *pnRequest) { struct pv_link *ppv_link = &plink->value.pv_link; - DBADDR *paddr = ppv_link->pvt; + dbChannel *chan = linkChannel(plink); + DBADDR *paddr = &chan->addr; dbCommon *precord = plink->precord; long status; /* scan passive records if link is process passive */ if (ppv_link->pvlMask & pvlOptPP) { - status = dbScanPassive(precord, paddr->precord); + status = dbScanPassive(precord, dbChannelRecord(chan)); if (status) return status; } if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { - status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); + status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr); } else { - unsigned short dbfType = paddr->field_type; + unsigned short dbfType = dbChannelFinalFieldType(chan); if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE) return S_db_badDbrtype; - if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1) - && paddr->special != SPC_DBADDR - && paddr->special != SPC_ATTRIBUTE) { + if (dbChannelFinalElements(chan) == 1 && (!pnRequest || *pnRequest == 1) + && dbChannelSpecial(chan) != SPC_DBADDR + && dbChannelSpecial(chan) != SPC_ATTRIBUTE) { ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; - status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); + status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr); } else { ppv_link->getCvt = NULL; status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL); @@ -192,16 +196,18 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, ppv_link->lastGetdbrType = dbrType; } - if (!status && precord != paddr->precord) + if (!status && precord != dbChannelRecord(chan)) recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode, - plink->precord, paddr->precord->stat, paddr->precord->sevr); + plink->precord, + dbChannelRecord(chan)->stat, dbChannelRecord(chan)->sevr); return status; } static long dbDbGetControlLimits(const struct link *plink, double *low, double *high) { - DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + dbChannel *chan = linkChannel(plink); + DBADDR *paddr = &chan->addr; struct buffer { DBRctrlDouble double value; @@ -222,7 +228,8 @@ static long dbDbGetControlLimits(const struct link *plink, double *low, static long dbDbGetGraphicLimits(const struct link *plink, double *low, double *high) { - DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + dbChannel *chan = linkChannel(plink); + DBADDR *paddr = &chan->addr; struct buffer { DBRgrDouble double value; @@ -243,7 +250,8 @@ static long dbDbGetGraphicLimits(const struct link *plink, double *low, static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, double *low, double *high, double *hihi) { - DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + dbChannel *chan = linkChannel(plink); + DBADDR *paddr = &chan->addr; struct buffer { DBRalDouble double value; @@ -265,7 +273,8 @@ static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, static long dbDbGetPrecision(const struct link *plink, short *precision) { - DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + dbChannel *chan = linkChannel(plink); + DBADDR *paddr = &chan->addr; struct buffer { DBRprecision double value; @@ -284,7 +293,8 @@ static long dbDbGetPrecision(const struct link *plink, short *precision) static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize) { - DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + dbChannel *chan = linkChannel(plink); + DBADDR *paddr = &chan->addr; struct buffer { DBRunits double value; @@ -304,20 +314,20 @@ static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize) static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status, epicsEnum16 *severity) { - DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; - + dbChannel *chan = linkChannel(plink); + dbCommon *precord = dbChannelRecord(chan); if (status) - *status = paddr->precord->stat; + *status = precord->stat; if (severity) - *severity = paddr->precord->sevr; + *severity = precord->sevr; return 0; } static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) { - DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; - - *pstamp = paddr->precord->time; + dbChannel *chan = linkChannel(plink); + dbCommon *precord = dbChannelRecord(chan); + *pstamp = precord->time; return 0; } @@ -325,9 +335,10 @@ static long dbDbPutValue(struct link *plink, short dbrType, const void *pbuffer, long nRequest) { struct pv_link *ppv_link = &plink->value.pv_link; + dbChannel *chan = linkChannel(plink); struct dbCommon *psrce = plink->precord; - DBADDR *paddr = (DBADDR *) ppv_link->pvt; - dbCommon *pdest = paddr->precord; + DBADDR *paddr = &chan->addr; + dbCommon *pdest = dbChannelRecord(chan); long status = dbPut(paddr, dbrType, pbuffer, nRequest); recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta, @@ -335,7 +346,7 @@ static long dbDbPutValue(struct link *plink, short dbrType, if (status) return status; - if (paddr->pfield == (void *) &pdest->proc || + if (dbChannelField(chan) == (void *) &pdest->proc || (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) { status = processTarget(psrce, pdest); } @@ -346,9 +357,8 @@ static long dbDbPutValue(struct link *plink, short dbrType, static void dbDbScanFwdLink(struct link *plink) { dbCommon *precord = plink->precord; - dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt; - - dbScanPassive(precord, paddr->precord); + dbChannel *chan = linkChannel(plink); + dbScanPassive(precord, dbChannelRecord(chan)); } static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) diff --git a/modules/database/src/ioc/db/dbDbLink.h b/modules/database/src/ioc/db/dbDbLink.h index ce99c0345..66bd452ee 100644 --- a/modules/database/src/ioc/db/dbDbLink.h +++ b/modules/database/src/ioc/db/dbDbLink.h @@ -27,7 +27,7 @@ struct dbLocker; epicsShareFunc long dbDbInitLink(struct link *plink, short dbfType); epicsShareFunc void dbDbAddLink(struct dbLocker *locker, struct link *plink, - short dbfType, DBADDR *ptarget); + short dbfType, dbChannel *ptarget); #ifdef __cplusplus } diff --git a/modules/database/src/ioc/db/dbLink.c b/modules/database/src/ioc/db/dbLink.c index 421da5068..ef8336818 100644 --- a/modules/database/src/ioc/db/dbLink.c +++ b/modules/database/src/ioc/db/dbLink.c @@ -144,7 +144,7 @@ void dbInitLink(struct link *plink, short dbfType) } void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, - DBADDR *ptarget) + dbChannel *ptarget) { struct dbCommon *precord = plink->precord; diff --git a/modules/database/src/ioc/db/dbLink.h b/modules/database/src/ioc/db/dbLink.h index 89a0aaf9f..f7c11c748 100644 --- a/modules/database/src/ioc/db/dbLink.h +++ b/modules/database/src/ioc/db/dbLink.h @@ -21,6 +21,7 @@ #include "epicsTypes.h" #include "epicsTime.h" #include "dbAddr.h" +#include "dbChannel.h" #ifdef __cplusplus extern "C" { @@ -369,7 +370,7 @@ epicsShareFunc const char * dbLinkFieldName(const struct link *plink); epicsShareFunc void dbInitLink(struct link *plink, short dbfType); epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, - short dbfType, DBADDR *ptarget); + short dbfType, dbChannel *ptarget); epicsShareFunc void dbLinkOpen(struct link *plink); epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink); diff --git a/modules/database/src/ioc/db/dbLock.c b/modules/database/src/ioc/db/dbLock.c index cac17fb41..002b5cfc2 100644 --- a/modules/database/src/ioc/db/dbLock.c +++ b/modules/database/src/ioc/db/dbLock.c @@ -743,14 +743,14 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond) for(i=0; ino_links; i++) { dbFldDes *pdesc = rtype->papFldDes[rtype->link_ind[i]]; DBLINK *plink = (DBLINK*)((char*)prec + pdesc->offset); - DBADDR *ptarget; + dbChannel *chan; lockRecord *lr; if(plink->type!=DB_LINK) continue; - ptarget = plink->value.pv_link.pvt; - lr = ptarget->precord->lset; + chan = plink->value.pv_link.pvt; + lr = dbChannelRecord(chan)->lset; assert(lr); if(lr->precord==pfirst) { From 17a8dbc2d7a8fd8673ed7cc3bc044cfeef25951e Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 12 Feb 2020 12:09:40 +0100 Subject: [PATCH 22/43] apply filters in dbDbGetValue --- modules/database/src/ioc/db/dbDbLink.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index a86272eb9..0eb3ad34f 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -61,6 +61,7 @@ #include "dbConvertFast.h" #include "dbConvert.h" #include "db_field_log.h" +#include "db_access_routines.h" #include "dbFldTypes.h" #include "dbLink.h" #include "dbLockPvt.h" @@ -184,7 +185,18 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE) return S_db_badDbrtype; - if (dbChannelFinalElements(chan) == 1 && (!pnRequest || *pnRequest == 1) + /* If filters are involved in a read, create field log and run filters */ + if (ellCount(&chan->filters)) { + db_field_log *pfl; + long options = 0; + pfl = db_create_read_log(chan); + if (pfl) { + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); + status = dbChannelGet(chan, dbrType, pbuffer, &options, pnRequest, pfl); + db_delete_field_log(pfl); + } + } else if (dbChannelFinalElements(chan) == 1 && (!pnRequest || *pnRequest == 1) && dbChannelSpecial(chan) != SPC_DBADDR && dbChannelSpecial(chan) != SPC_ATTRIBUTE) { ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; From 3627c38a575dc7f08a93ae7879da2b4c5def0e29 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 12 Feb 2020 17:45:28 +0100 Subject: [PATCH 23/43] don't crash when filter results in 0 elements --- modules/database/src/ioc/db/dbDbLink.c | 53 +++++++++++++++++--------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index 0eb3ad34f..c12fd6b02 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -100,6 +100,13 @@ long dbDbInitLink(struct link *plink, short dbfType) return status; precord = dbChannelRecord(chan); + + if (dbChannelFinalElements(chan) < 1) { + errlogPrintf("Warning: %s.%s=%s has %ld elements. This will not work.\n", + plink->precord->name, dbLinkFieldName(plink), + dbChannelName(chan), dbChannelFinalElements(chan)); + } + plink->lset = &dbDb_lset; plink->type = DB_LINK; plink->value.pv_link.pvt = chan; @@ -113,15 +120,21 @@ long dbDbInitLink(struct link *plink, short dbfType) } void dbDbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, - dbChannel *ptarget) + dbChannel *chan) { + if (dbChannelFinalElements(chan) < 1) { + errlogPrintf("Warning: %s.%s=%s has %ld elements. This will not work.\n", + plink->precord->name, dbLinkFieldName(plink), + dbChannelName(chan), dbChannelFinalElements(chan)); + } + plink->lset = &dbDb_lset; plink->type = DB_LINK; - plink->value.pv_link.pvt = ptarget; - ellAdd(&dbChannelRecord(ptarget)->bklnk, &plink->value.pv_link.backlinknode); + plink->value.pv_link.pvt = chan; + ellAdd(&dbChannelRecord(chan)->bklnk, &plink->value.pv_link.backlinknode); /* target record is already locked in dbPutFieldLink() */ - dbLockSetMerge(locker, plink->precord, dbChannelRecord(ptarget)); + dbLockSetMerge(locker, plink->precord, dbChannelRecord(chan)); } static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink) @@ -177,7 +190,24 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, return status; } - if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { + /* If filters are involved in a read, create field log and run filters */ + if (ellCount(&chan->filters)) { + db_field_log *pfl; + long options = 0; + + if (dbChannelFinalElements(chan) < 1) + { + recGblSetSevr(precord, LINK_ALARM, UDF_ALARM); + return S_db_badField; + } + pfl = db_create_read_log(chan); + if (pfl) { + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); + status = dbChannelGet(chan, dbrType, pbuffer, &options, pnRequest, pfl); + db_delete_field_log(pfl); + } + } else if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr); } else { unsigned short dbfType = dbChannelFinalFieldType(chan); @@ -185,18 +215,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE) return S_db_badDbrtype; - /* If filters are involved in a read, create field log and run filters */ - if (ellCount(&chan->filters)) { - db_field_log *pfl; - long options = 0; - pfl = db_create_read_log(chan); - if (pfl) { - pfl = dbChannelRunPreChain(chan, pfl); - pfl = dbChannelRunPostChain(chan, pfl); - status = dbChannelGet(chan, dbrType, pbuffer, &options, pnRequest, pfl); - db_delete_field_log(pfl); - } - } else if (dbChannelFinalElements(chan) == 1 && (!pnRequest || *pnRequest == 1) + if (dbChannelFinalElements(chan) == 1 && (!pnRequest || *pnRequest == 1) && dbChannelSpecial(chan) != SPC_DBADDR && dbChannelSpecial(chan) != SPC_ATTRIBUTE) { ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; From e0dfb6cff8db1f192a5eee0bd8dc56095f51d290 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 13 Feb 2020 15:33:09 +0100 Subject: [PATCH 24/43] fix crash in PINI: use local db_field_log --- modules/database/src/ioc/db/dbDbLink.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index c12fd6b02..f3335f4ba 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -192,21 +192,20 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, /* If filters are involved in a read, create field log and run filters */ if (ellCount(&chan->filters)) { - db_field_log *pfl; long options = 0; + db_field_log fl = {}; + fl.ctx = dbfl_context_read; + fl.type = dbfl_type_rec; if (dbChannelFinalElements(chan) < 1) { recGblSetSevr(precord, LINK_ALARM, UDF_ALARM); return S_db_badField; } - pfl = db_create_read_log(chan); - if (pfl) { - pfl = dbChannelRunPreChain(chan, pfl); - pfl = dbChannelRunPostChain(chan, pfl); - status = dbChannelGet(chan, dbrType, pbuffer, &options, pnRequest, pfl); - db_delete_field_log(pfl); - } + + dbChannelRunPreChain(chan, &fl); + dbChannelRunPostChain(chan, &fl); + status = dbChannelGet(chan, dbrType, pbuffer, &options, pnRequest, &fl); } else if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr); } else { From 39c8d5619a7cba9e95495cc99be9c25ab514f05c Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 13 Feb 2020 22:06:44 +0100 Subject: [PATCH 25/43] bugfix: dbGet should not crash because of empty array requests --- modules/database/src/ioc/db/dbAccess.c | 5 +++++ modules/database/src/ioc/db/dbDbLink.c | 31 ++++++++++---------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index da8467a0c..5d63c51d4 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -1001,6 +1001,11 @@ long dbGet(DBADDR *paddr, short dbrType, } else { DBADDR localAddr = *paddr; /* Structure copy */ + if (pfl->no_elements < 1) { + status = S_db_badField; + goto done; + } + localAddr.field_type = pfl->field_type; localAddr.field_size = pfl->field_size; localAddr.no_elements = pfl->no_elements; diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index f3335f4ba..c8452fd86 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -101,12 +101,6 @@ long dbDbInitLink(struct link *plink, short dbfType) precord = dbChannelRecord(chan); - if (dbChannelFinalElements(chan) < 1) { - errlogPrintf("Warning: %s.%s=%s has %ld elements. This will not work.\n", - plink->precord->name, dbLinkFieldName(plink), - dbChannelName(chan), dbChannelFinalElements(chan)); - } - plink->lset = &dbDb_lset; plink->type = DB_LINK; plink->value.pv_link.pvt = chan; @@ -122,12 +116,6 @@ long dbDbInitLink(struct link *plink, short dbfType) void dbDbAddLink(struct dbLocker *locker, struct link *plink, short dbfType, dbChannel *chan) { - if (dbChannelFinalElements(chan) < 1) { - errlogPrintf("Warning: %s.%s=%s has %ld elements. This will not work.\n", - plink->precord->name, dbLinkFieldName(plink), - dbChannelName(chan), dbChannelFinalElements(chan)); - } - plink->lset = &dbDb_lset; plink->type = DB_LINK; plink->value.pv_link.pvt = chan; @@ -197,15 +185,20 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, fl.ctx = dbfl_context_read; fl.type = dbfl_type_rec; - if (dbChannelFinalElements(chan) < 1) + /* For the moment, empty arrays are not supported by EPICS */ + if (dbChannelFinalElements(chan) > 0) { - recGblSetSevr(precord, LINK_ALARM, UDF_ALARM); - return S_db_badField; + dbChannelRunPreChain(chan, &fl); + dbChannelRunPostChain(chan, &fl); + status = dbChannelGet(chan, dbrType, pbuffer, &options, pnRequest, &fl); + if (!status && pnRequest && *pnRequest <= 0) + status = S_db_badField; + } else { + status = S_db_badField; + } + if (status) { + recGblSetSevr(precord, LINK_ALARM, UDF_ALARM); } - - dbChannelRunPreChain(chan, &fl); - dbChannelRunPostChain(chan, &fl); - status = dbChannelGet(chan, dbrType, pbuffer, &options, pnRequest, &fl); } else if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr); } else { From 7ab56a68d16367e0244dc29cac574df75e7415f3 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 14 Feb 2020 13:14:04 +0100 Subject: [PATCH 26/43] db link filter tests added --- modules/database/test/std/rec/Makefile | 7 + .../database/test/std/rec/linkFilterTest.c | 157 ++++++++++++++++++ .../database/test/std/rec/linkFilterTest.db | 16 ++ 3 files changed, 180 insertions(+) create mode 100644 modules/database/test/std/rec/linkFilterTest.c create mode 100644 modules/database/test/std/rec/linkFilterTest.db diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index d48424f65..e8c546442 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -159,6 +159,13 @@ asyncproctest_SRCS += asyncproctest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/asyncproctest.dbd ../asyncproctest.db TESTS += asyncproctest +TESTPROD_HOST += linkFilterTest +linkFilterTest_SRCS += linkFilterTest.c +linkFilterTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += linkFilterTest.c +TESTFILES += ../linkFilterTest.db +TESTS += linkFilterTest + # dbHeader* is only a compile test # no need to actually run TESTPROD += dbHeaderTest diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c new file mode 100644 index 000000000..a336abc5c --- /dev/null +++ b/modules/database/test/std/rec/linkFilterTest.c @@ -0,0 +1,157 @@ +/*************************************************************************\ +* Copyright (c) 2020 Dirk Zimoch +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "dbAccess.h" +#include "devSup.h" +#include "alarm.h" +#include "dbUnitTest.h" +#include "errlog.h" +#include "epicsThread.h" + +#include "longinRecord.h" + +#include "testMain.h" + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static void startTestIoc(const char *dbfile) +{ + testdbPrepare(); + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + recTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase(dbfile, NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); +} + +static void expectProcSuccess(const char *rec) +{ + char fieldname[20]; + testDiag("expecting success from %s", rec); + sprintf(fieldname, "%s.PROC", rec); + testdbPutFieldOk(fieldname, DBF_LONG, 1); + sprintf(fieldname, "%s.SEVR", rec); + testdbGetFieldEqual(fieldname, DBF_LONG, NO_ALARM); + sprintf(fieldname, "%s.STAT", rec); + testdbGetFieldEqual(fieldname, DBF_LONG, NO_ALARM); +} + +static void expectProcFailure(const char *rec) +{ + char fieldname[20]; + testDiag("expecting failure S_db_badField %#x from %s", S_db_badField, rec); + sprintf(fieldname, "%s.PROC", rec); + testdbPutFieldFail(S_db_badField, fieldname, DBF_LONG, 1); + sprintf(fieldname, "%s.SEVR", rec); + testdbGetFieldEqual(fieldname, DBF_LONG, INVALID_ALARM); + sprintf(fieldname, "%s.STAT", rec); + testdbGetFieldEqual(fieldname, DBF_LONG, LINK_ALARM); +} + +static void changeRange(long start, long stop, long step) +{ + char linkstring[60]; + if (step) + sprintf(linkstring, "src.[%ld:%ld:%ld]", start, step, stop); + else if (stop) + sprintf(linkstring, "src.[%ld:%ld]", start, stop); + else + sprintf(linkstring, "src.[%ld]", start); + testDiag("modifying link: %s", linkstring); + testdbPutFieldOk("ai.INP", DBF_STRING, linkstring); + testdbPutFieldOk("wf.INP", DBF_STRING, linkstring); +} + +static double buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 2, 4, 6}; + +static void expectRange(long start, long end) +{ + long n = end-start+1; + testdbGetFieldEqual("ai.VAL", DBF_DOUBLE, buf[start]); + testdbGetFieldEqual("wf.NORD", DBF_LONG, n); + testdbGetArrFieldEqual("wf.VAL", DBF_DOUBLE, n+2, n, buf+start); +} + +#if 0 +static void expectEmptyArray(void) +{ + /* empty arrays are now allowed at the moment */ + testDiag("expecting empty array"); + testdbGetFieldEqual("wf.NORD", DBF_LONG, 0); +} +#endif + +MAIN(linkFilterTest) +{ + testPlan(0); + startTestIoc("linkFilterTest.db"); + + testDiag("PINI"); + expectRange(2,4); + + testDiag("modify range"); + changeRange(3,6,0); + expectProcSuccess("ai"); + expectProcSuccess("wf"); + expectRange(3,6); + + testDiag("step 2"); + changeRange(1,6,2); + expectProcSuccess("ai"); + expectProcSuccess("wf"); + expectRange(10,12); + + testDiag("single value"); + changeRange(5,0,0); + expectProcSuccess("ai"); + expectProcSuccess("wf"); + expectRange(5,5); + + testDiag("backward range"); + changeRange(5,3,0); + expectProcFailure("ai"); + expectProcFailure("wf"); + + testDiag("range end beyond src.NORD"); + changeRange(3,9,0); + expectProcSuccess("ai"); + expectProcSuccess("wf"); + expectRange(3,7); /* clipped range */ + + testDiag("range end beyond src.NELM"); + changeRange(4,12,0); + expectProcSuccess("ai"); + expectProcSuccess("wf"); + expectRange(4,7); /* clipped range */ + + testDiag("range start beyond src.NORD"); + changeRange(8,9,0); + expectProcFailure("ai"); + expectProcFailure("wf"); + + testDiag("range start beyond src.NELM"); + changeRange(11,12,0); + expectProcFailure("ai"); + expectProcFailure("wf"); + + testDiag("single value beyond src.NORD"); + changeRange(8,0,0); + expectProcFailure("ai"); + expectProcFailure("wf"); + + testDiag("single beyond rec.NELM"); + changeRange(12,0,0); + expectProcFailure("ai"); + expectProcFailure("wf"); + + testIocShutdownOk(); + testdbCleanup(); + return testDone(); +} diff --git a/modules/database/test/std/rec/linkFilterTest.db b/modules/database/test/std/rec/linkFilterTest.db new file mode 100644 index 000000000..5ee371e51 --- /dev/null +++ b/modules/database/test/std/rec/linkFilterTest.db @@ -0,0 +1,16 @@ +record(waveform, "src") { + field(NELM, "10") + field(FTVL, "SHORT") + field(INP, [1, 2, 3, 4, 5, 6, 7, 8]) +} +record(ai, "ai") { + field(INP, "src.[2]") # expect 3 + field(PINI, "YES") +} +record(waveform, "wf") { + field(NELM, "5") + field(FTVL, "DOUBLE") + field(INP, "src.[2:4]") # expect 3,4,5 + field(PINI, "YES") +} + From d0ef45acc3f75f86c8533e6285af4a9e042b0e35 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 25 Feb 2020 09:38:17 +0100 Subject: [PATCH 27/43] initialize free lists when starting dbChannel --- modules/database/src/ioc/db/dbChannel.c | 1 + modules/database/src/ioc/db/dbEvent.c | 27 ++++++++++++++++++------- modules/database/src/ioc/db/dbEvent.h | 1 + 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c index 032293bc5..34a3de45f 100644 --- a/modules/database/src/ioc/db/dbChannel.c +++ b/modules/database/src/ioc/db/dbChannel.c @@ -68,6 +68,7 @@ void dbChannelInit (void) freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128); freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64); freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128); + db_init_event_freelists(); } static void chf_value(parseContext *parser, parse_result *presult) diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c index 4b3caac9d..437cb6d5d 100644 --- a/modules/database/src/ioc/db/dbEvent.c +++ b/modules/database/src/ioc/db/dbEvent.c @@ -252,18 +252,15 @@ int dbel ( const char *pname, unsigned level ) } /* - * DB_INIT_EVENTS() + * DB_INIT_EVENT_FREELISTS() * * - * Initialize the event facility for this task. Must be called at least once - * by each task which uses the db event facility + * Initialize the free lists used by the event facility. + * Safe to be called multiple times. * - * returns: ptr to event user block or NULL if memory can't be allocated */ -dbEventCtx db_init_events (void) +void db_init_event_freelists (void) { - struct event_user * evUser; - if (!dbevEventUserFreeList) { freeListInitPvt(&dbevEventUserFreeList, sizeof(struct event_user),8); @@ -280,6 +277,22 @@ dbEventCtx db_init_events (void) freeListInitPvt(&dbevFieldLogFreeList, sizeof(struct db_field_log),2048); } +} + +/* + * DB_INIT_EVENTS() + * + * + * Initialize the event facility for this task. Must be called at least once + * by each task which uses the db event facility + * + * returns: ptr to event user block or NULL if memory can't be allocated + */ +dbEventCtx db_init_events (void) +{ + struct event_user * evUser; + + db_init_event_freelists(); evUser = (struct event_user *) freeListCalloc(dbevEventUserFreeList); diff --git a/modules/database/src/ioc/db/dbEvent.h b/modules/database/src/ioc/db/dbEvent.h index c8254800b..d7ebdf805 100644 --- a/modules/database/src/ioc/db/dbEvent.h +++ b/modules/database/src/ioc/db/dbEvent.h @@ -51,6 +51,7 @@ epicsShareFunc int db_post_events ( typedef void * dbEventCtx; typedef void EXTRALABORFUNC (void *extralabor_arg); +void db_init_event_freelists (void); epicsShareFunc dbEventCtx db_init_events (void); epicsShareFunc int db_start_events ( dbEventCtx ctx, const char *taskname, void (*init_func)(void *), From c51c83b1d507bccc2ca30daecb7022feb64c2765 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 25 Feb 2020 09:59:21 +0100 Subject: [PATCH 28/43] Revert "fix crash in PINI: use local db_field_log" This reverts commit a590151accb1d187562c515a48e013244dd98a45. Conflicts: modules/database/src/ioc/db/dbDbLink.c --- modules/database/src/ioc/db/dbDbLink.c | 27 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index c8452fd86..1a6fa6e44 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -180,21 +180,28 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, /* If filters are involved in a read, create field log and run filters */ if (ellCount(&chan->filters)) { + db_field_log *pfl; long options = 0; - db_field_log fl = {}; - fl.ctx = dbfl_context_read; - fl.type = dbfl_type_rec; /* For the moment, empty arrays are not supported by EPICS */ - if (dbChannelFinalElements(chan) > 0) + if (dbChannelFinalElements(chan) <= 0) { - dbChannelRunPreChain(chan, &fl); - dbChannelRunPostChain(chan, &fl); - status = dbChannelGet(chan, dbrType, pbuffer, &options, pnRequest, &fl); - if (!status && pnRequest && *pnRequest <= 0) - status = S_db_badField; - } else { + /* empty array request */ status = S_db_badField; + } else { + pfl = db_create_read_log(chan); + if (!pfl) { + status = S_db_noMemory; + } else { + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); + status = dbChannelGet(chan, dbrType, pbuffer, &options, pnRequest, pfl); + db_delete_field_log(pfl); + if (!status && pnRequest && *pnRequest <= 0) { + /* empty array result */ + status = S_db_badField; + } + } } if (status) { recGblSetSevr(precord, LINK_ALARM, UDF_ALARM); From 0ee36388cb1c63059a2742171861f8bcd83c0300 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 25 Feb 2020 11:07:35 +0100 Subject: [PATCH 29/43] unused variable removed --- modules/database/src/ioc/db/dbDbLink.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index 1a6fa6e44..c48716b4b 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -181,7 +181,6 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, /* If filters are involved in a read, create field log and run filters */ if (ellCount(&chan->filters)) { db_field_log *pfl; - long options = 0; /* For the moment, empty arrays are not supported by EPICS */ if (dbChannelFinalElements(chan) <= 0) @@ -195,7 +194,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, } else { pfl = dbChannelRunPreChain(chan, pfl); pfl = dbChannelRunPostChain(chan, pfl); - status = dbChannelGet(chan, dbrType, pbuffer, &options, pnRequest, pfl); + status = dbChannelGet(chan, dbrType, pbuffer, NULL, pnRequest, pfl); db_delete_field_log(pfl); if (!status && pnRequest && *pnRequest <= 0) { /* empty array result */ From bc34526bcb2a115531c08e06fdb7e58b4ae34639 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 25 Feb 2020 11:22:38 +0100 Subject: [PATCH 30/43] re-order link filter tests to alternate between success and failure --- .../database/test/std/rec/linkFilterTest.c | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c index a336abc5c..1d99ec891 100644 --- a/modules/database/test/std/rec/linkFilterTest.c +++ b/modules/database/test/std/rec/linkFilterTest.c @@ -38,9 +38,9 @@ static void expectProcSuccess(const char *rec) sprintf(fieldname, "%s.PROC", rec); testdbPutFieldOk(fieldname, DBF_LONG, 1); sprintf(fieldname, "%s.SEVR", rec); - testdbGetFieldEqual(fieldname, DBF_LONG, NO_ALARM); + testdbGetFieldEqual(fieldname, DBF_LONG, NO_ALARM); sprintf(fieldname, "%s.STAT", rec); - testdbGetFieldEqual(fieldname, DBF_LONG, NO_ALARM); + testdbGetFieldEqual(fieldname, DBF_LONG, NO_ALARM); } static void expectProcFailure(const char *rec) @@ -50,9 +50,9 @@ static void expectProcFailure(const char *rec) sprintf(fieldname, "%s.PROC", rec); testdbPutFieldFail(S_db_badField, fieldname, DBF_LONG, 1); sprintf(fieldname, "%s.SEVR", rec); - testdbGetFieldEqual(fieldname, DBF_LONG, INVALID_ALARM); + testdbGetFieldEqual(fieldname, DBF_LONG, INVALID_ALARM); sprintf(fieldname, "%s.STAT", rec); - testdbGetFieldEqual(fieldname, DBF_LONG, LINK_ALARM); + testdbGetFieldEqual(fieldname, DBF_LONG, LINK_ALARM); } static void changeRange(long start, long stop, long step) @@ -70,7 +70,7 @@ static void changeRange(long start, long stop, long step) } static double buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 2, 4, 6}; - + static void expectRange(long start, long end) { long n = end-start+1; @@ -102,20 +102,19 @@ MAIN(linkFilterTest) expectProcSuccess("wf"); expectRange(3,6); + testDiag("backward range"); + changeRange(5,3,0); + expectProcFailure("ai"); + expectProcFailure("wf"); + testDiag("step 2"); changeRange(1,6,2); expectProcSuccess("ai"); expectProcSuccess("wf"); expectRange(10,12); - testDiag("single value"); - changeRange(5,0,0); - expectProcSuccess("ai"); - expectProcSuccess("wf"); - expectRange(5,5); - - testDiag("backward range"); - changeRange(5,3,0); + testDiag("range start beyond src.NORD"); + changeRange(8,9,0); expectProcFailure("ai"); expectProcFailure("wf"); @@ -125,27 +124,28 @@ MAIN(linkFilterTest) expectProcSuccess("wf"); expectRange(3,7); /* clipped range */ + testDiag("range start beyond src.NELM"); + changeRange(11,12,0); + expectProcFailure("ai"); + expectProcFailure("wf"); + testDiag("range end beyond src.NELM"); changeRange(4,12,0); expectProcSuccess("ai"); expectProcSuccess("wf"); expectRange(4,7); /* clipped range */ - testDiag("range start beyond src.NORD"); - changeRange(8,9,0); - expectProcFailure("ai"); - expectProcFailure("wf"); - - testDiag("range start beyond src.NELM"); - changeRange(11,12,0); - expectProcFailure("ai"); - expectProcFailure("wf"); - testDiag("single value beyond src.NORD"); changeRange(8,0,0); expectProcFailure("ai"); expectProcFailure("wf"); + testDiag("single value"); + changeRange(5,0,0); + expectProcSuccess("ai"); + expectProcSuccess("wf"); + expectRange(5,5); + testDiag("single beyond rec.NELM"); changeRange(12,0,0); expectProcFailure("ai"); From 8f5be5f0ad1b6cb27c23a2a0152f6e647ce1f835 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 25 Feb 2020 11:34:57 +0100 Subject: [PATCH 31/43] removed unnecessary recGblSetSevr call --- modules/database/src/ioc/db/dbDbLink.c | 35 ++++++++++---------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index c48716b4b..b1548bd16 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -183,28 +183,19 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, db_field_log *pfl; /* For the moment, empty arrays are not supported by EPICS */ - if (dbChannelFinalElements(chan) <= 0) - { - /* empty array request */ - status = S_db_badField; - } else { - pfl = db_create_read_log(chan); - if (!pfl) { - status = S_db_noMemory; - } else { - pfl = dbChannelRunPreChain(chan, pfl); - pfl = dbChannelRunPostChain(chan, pfl); - status = dbChannelGet(chan, dbrType, pbuffer, NULL, pnRequest, pfl); - db_delete_field_log(pfl); - if (!status && pnRequest && *pnRequest <= 0) { - /* empty array result */ - status = S_db_badField; - } - } - } - if (status) { - recGblSetSevr(precord, LINK_ALARM, UDF_ALARM); - } + if (dbChannelFinalElements(chan) <= 0) /* empty array request */ + return S_db_badField; + pfl = db_create_read_log(chan); + if (!pfl) + return S_db_noMemory; + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); + status = dbChannelGet(chan, dbrType, pbuffer, NULL, pnRequest, pfl); + db_delete_field_log(pfl); + if (status) + return status; + if (pnRequest && *pnRequest <= 0) /* empty array result */ + return S_db_badField; } else if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr); } else { From edb8f1a5df7d9ea97b0bbece93a65fc5cfab2a63 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 26 Feb 2020 09:12:48 +0100 Subject: [PATCH 32/43] set number of planned link filter tests --- modules/database/test/std/rec/linkFilterTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c index 1d99ec891..6f38d249e 100644 --- a/modules/database/test/std/rec/linkFilterTest.c +++ b/modules/database/test/std/rec/linkFilterTest.c @@ -90,7 +90,7 @@ static void expectEmptyArray(void) MAIN(linkFilterTest) { - testPlan(0); + testPlan(98); startTestIoc("linkFilterTest.db"); testDiag("PINI"); From ea05bab26ab2734e08768767fc5788adce7fc36b Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 16 Mar 2020 13:11:03 +0100 Subject: [PATCH 33/43] Release notes updated --- documentation/RELEASE_NOTES.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index f21fbdcec..a7f2806f9 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -373,6 +373,15 @@ As long as all support modules and IOCs are rebuilt from source after updating them to use this release of EPICS Base, these changes should not have any affect. +### Filters in database links + +Input links can now use filters, most importantly array element and sub array +access, even if they are not channel access links. + +### ai Soft Channel support + +The Soft Channel device support for ai records now returns failure when +fetching the INP link fails. ### logClient reliability From afdf34b791ecd19957ed3d31958ae379e8266b9f Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 27 Mar 2020 08:39:04 +0100 Subject: [PATCH 34/43] clean up code structure --- modules/database/src/ioc/db/dbDbLink.c | 72 +++++++++++++++----------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index b1548bd16..a502df2f8 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -169,6 +169,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, dbChannel *chan = linkChannel(plink); DBADDR *paddr = &chan->addr; dbCommon *precord = plink->precord; + db_field_log *pfl = NULL; long status; /* scan passive records if link is process passive */ @@ -178,42 +179,55 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, return status; } - /* If filters are involved in a read, create field log and run filters */ - if (ellCount(&chan->filters)) { - db_field_log *pfl; - - /* For the moment, empty arrays are not supported by EPICS */ - if (dbChannelFinalElements(chan) <= 0) /* empty array request */ - return S_db_badField; - pfl = db_create_read_log(chan); - if (!pfl) - return S_db_noMemory; - pfl = dbChannelRunPreChain(chan, pfl); - pfl = dbChannelRunPostChain(chan, pfl); - status = dbChannelGet(chan, dbrType, pbuffer, NULL, pnRequest, pfl); - db_delete_field_log(pfl); - if (status) - return status; - if (pnRequest && *pnRequest <= 0) /* empty array result */ - return S_db_badField; - } else if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { + if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) + { + /* shortcut: scalar with known conversion, no filter */ status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr); - } else { + } + else if (dbChannelFinalElements(chan) == 1 && (!pnRequest || *pnRequest == 1) + && dbChannelSpecial(chan) != SPC_DBADDR + && dbChannelSpecial(chan) != SPC_ATTRIBUTE + && ellCount(&chan->filters) == 0) + { + /* simple scalar: set up shortcut */ unsigned short dbfType = dbChannelFinalFieldType(chan); if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE) return S_db_badDbrtype; - if (dbChannelFinalElements(chan) == 1 && (!pnRequest || *pnRequest == 1) - && dbChannelSpecial(chan) != SPC_DBADDR - && dbChannelSpecial(chan) != SPC_ATTRIBUTE) { - ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; - status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr); - } else { - ppv_link->getCvt = NULL; - status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL); - } + ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; ppv_link->lastGetdbrType = dbrType; + status = ppv_link->getCvt(dbChannelField(chan), pbuffer, paddr); + } + else + { + /* filter, array, or special */ + ppv_link->getCvt = NULL; + + /* For the moment, empty arrays are not supported by EPICS */ + if (dbChannelFinalElements(chan) <= 0) /* empty array request */ + return S_db_badField; + + if (ellCount(&chan->filters)) { + /* If filters are involved in a read, create field log and run filters */ + pfl = db_create_read_log(chan); + if (!pfl) + return S_db_noMemory; + + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); + } + + status = dbChannelGet(chan, dbrType, pbuffer, NULL, pnRequest, pfl); + + if (pfl) + db_delete_field_log(pfl); + + if (status) + return status; + + if (pnRequest && *pnRequest <= 0) /* empty array result */ + return S_db_badField; } if (!status && precord != dbChannelRecord(chan)) From 5d808b7c0242486f0dc4e18fa62cb2af3a40a75c Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 7 May 2020 15:42:51 +0200 Subject: [PATCH 35/43] new error code for empty arrays --- modules/database/src/ioc/db/dbAccess.c | 2 +- modules/database/src/ioc/db/dbAccessDefs.h | 1 + modules/database/src/ioc/db/dbDbLink.c | 4 ++-- modules/database/test/std/rec/linkFilterTest.c | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index 5d63c51d4..d338d7665 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -958,7 +958,7 @@ long dbGet(DBADDR *paddr, short dbrType, DBADDR localAddr = *paddr; /* Structure copy */ if (pfl->no_elements < 1) { - status = S_db_badField; + status = S_db_emptyArray; goto done; } diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h index 5715df35d..16bc471fc 100644 --- a/modules/database/src/ioc/db/dbAccessDefs.h +++ b/modules/database/src/ioc/db/dbAccessDefs.h @@ -205,6 +205,7 @@ struct dbr_alDouble {DBRalDouble}; #define S_db_noMemory (M_dbAccess|66) /*unable to allocate data structure from pool*/ #define S_db_notInit (M_dbAccess|67) /*Not initialized*/ #define S_db_bufFull (M_dbAccess|68) /*Buffer full*/ +#define S_db_emptyArray (M_dbAccess|69) /*Array has no elements*/ struct dbEntry; diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index a502df2f8..ba5294c18 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -206,7 +206,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, /* For the moment, empty arrays are not supported by EPICS */ if (dbChannelFinalElements(chan) <= 0) /* empty array request */ - return S_db_badField; + return S_db_emptyArray; if (ellCount(&chan->filters)) { /* If filters are involved in a read, create field log and run filters */ @@ -227,7 +227,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, return status; if (pnRequest && *pnRequest <= 0) /* empty array result */ - return S_db_badField; + return S_db_emptyArray; } if (!status && precord != dbChannelRecord(chan)) diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c index 6f38d249e..12eba74ca 100644 --- a/modules/database/test/std/rec/linkFilterTest.c +++ b/modules/database/test/std/rec/linkFilterTest.c @@ -46,9 +46,9 @@ static void expectProcSuccess(const char *rec) static void expectProcFailure(const char *rec) { char fieldname[20]; - testDiag("expecting failure S_db_badField %#x from %s", S_db_badField, rec); + testDiag("expecting failure S_db_emptyArray %#x from %s", S_db_emptyArray, rec); sprintf(fieldname, "%s.PROC", rec); - testdbPutFieldFail(S_db_badField, fieldname, DBF_LONG, 1); + testdbPutFieldFail(S_db_emptyArray, fieldname, DBF_LONG, 1); sprintf(fieldname, "%s.SEVR", rec); testdbGetFieldEqual(fieldname, DBF_LONG, INVALID_ALARM); sprintf(fieldname, "%s.STAT", rec); From 275c4c7cf42c3a5bad3c38a4ef0a2d6d8e6bf98a Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 7 May 2020 16:14:16 +0200 Subject: [PATCH 36/43] remove needless pointer access --- modules/database/src/ioc/db/dbAccess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index d338d7665..36b307a6c 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -957,7 +957,7 @@ long dbGet(DBADDR *paddr, short dbrType, } else { DBADDR localAddr = *paddr; /* Structure copy */ - if (pfl->no_elements < 1) { + if (no_elements < 1) { status = S_db_emptyArray; goto done; } From 14b9ac327771f97db267e16113965bc0819faf58 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 7 May 2020 16:35:55 +0200 Subject: [PATCH 37/43] remove unnecessary check --- modules/database/src/ioc/db/dbDbLink.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index ba5294c18..88f32d6ca 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -204,10 +204,6 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, /* filter, array, or special */ ppv_link->getCvt = NULL; - /* For the moment, empty arrays are not supported by EPICS */ - if (dbChannelFinalElements(chan) <= 0) /* empty array request */ - return S_db_emptyArray; - if (ellCount(&chan->filters)) { /* If filters are involved in a read, create field log and run filters */ pfl = db_create_read_log(chan); @@ -226,7 +222,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, if (status) return status; - if (pnRequest && *pnRequest <= 0) /* empty array result */ + if (pnRequest && *pnRequest <= 0) return S_db_emptyArray; } From 4ad98d5b4f0c7a07c2dd0a555d8cfe9827ec0e92 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 7 May 2020 16:44:05 +0200 Subject: [PATCH 38/43] make db_init_event_freelists private --- modules/database/src/ioc/db/dbChannel.c | 2 ++ modules/database/src/ioc/db/dbEvent.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c index 34a3de45f..b6f53f797 100644 --- a/modules/database/src/ioc/db/dbChannel.c +++ b/modules/database/src/ioc/db/dbChannel.c @@ -18,6 +18,8 @@ #include #include +#define EPICS_PRIVATE_API + #include "cantProceed.h" #include "epicsAssert.h" #include "epicsString.h" diff --git a/modules/database/src/ioc/db/dbEvent.h b/modules/database/src/ioc/db/dbEvent.h index d7ebdf805..2c6feca11 100644 --- a/modules/database/src/ioc/db/dbEvent.h +++ b/modules/database/src/ioc/db/dbEvent.h @@ -51,7 +51,6 @@ epicsShareFunc int db_post_events ( typedef void * dbEventCtx; typedef void EXTRALABORFUNC (void *extralabor_arg); -void db_init_event_freelists (void); epicsShareFunc dbEventCtx db_init_events (void); epicsShareFunc int db_start_events ( dbEventCtx ctx, const char *taskname, void (*init_func)(void *), @@ -67,6 +66,7 @@ epicsShareFunc void db_event_change_priority ( dbEventCtx ctx, unsigned epicsPri #ifdef EPICS_PRIVATE_API epicsShareFunc void db_cleanup_events(void); +epicsShareFunc void db_init_event_freelists (void); #endif typedef void EVENTFUNC (void *user_arg, struct dbChannel *chan, From 30d8febb0b1efe5f0363932e5f666385c43518bd Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 7 May 2020 16:48:06 +0200 Subject: [PATCH 39/43] test code beautification --- modules/database/test/std/rec/linkFilterTest.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c index 12eba74ca..c400d1aac 100644 --- a/modules/database/test/std/rec/linkFilterTest.c +++ b/modules/database/test/std/rec/linkFilterTest.c @@ -69,7 +69,7 @@ static void changeRange(long start, long stop, long step) testdbPutFieldOk("wf.INP", DBF_STRING, linkstring); } -static double buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 2, 4, 6}; +static const double buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 2, 4, 6}; static void expectRange(long start, long end) { @@ -79,15 +79,6 @@ static void expectRange(long start, long end) testdbGetArrFieldEqual("wf.VAL", DBF_DOUBLE, n+2, n, buf+start); } -#if 0 -static void expectEmptyArray(void) -{ - /* empty arrays are now allowed at the moment */ - testDiag("expecting empty array"); - testdbGetFieldEqual("wf.NORD", DBF_LONG, 0); -} -#endif - MAIN(linkFilterTest) { testPlan(98); From 3b3261c877a8b66cb68203bbc399dff08b03a751 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 22 May 2020 11:54:47 +0200 Subject: [PATCH 40/43] Revert "new error code for empty arrays" This reverts commit d51b5513fda5ca7c6058990396d315cfff81cbfe. Conflicts: modules/database/src/ioc/db/dbAccess.c modules/database/src/ioc/db/dbDbLink.c --- modules/database/src/ioc/db/dbAccess.c | 2 +- modules/database/src/ioc/db/dbAccessDefs.h | 1 - modules/database/src/ioc/db/dbDbLink.c | 2 +- modules/database/test/std/rec/linkFilterTest.c | 4 ++-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index 36b307a6c..d7e5d0890 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -958,7 +958,7 @@ long dbGet(DBADDR *paddr, short dbrType, DBADDR localAddr = *paddr; /* Structure copy */ if (no_elements < 1) { - status = S_db_emptyArray; + status = S_db_badField; goto done; } diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h index 16bc471fc..5715df35d 100644 --- a/modules/database/src/ioc/db/dbAccessDefs.h +++ b/modules/database/src/ioc/db/dbAccessDefs.h @@ -205,7 +205,6 @@ struct dbr_alDouble {DBRalDouble}; #define S_db_noMemory (M_dbAccess|66) /*unable to allocate data structure from pool*/ #define S_db_notInit (M_dbAccess|67) /*Not initialized*/ #define S_db_bufFull (M_dbAccess|68) /*Buffer full*/ -#define S_db_emptyArray (M_dbAccess|69) /*Array has no elements*/ struct dbEntry; diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index 88f32d6ca..4cd87374e 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -223,7 +223,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, return status; if (pnRequest && *pnRequest <= 0) - return S_db_emptyArray; + return S_db_badField; } if (!status && precord != dbChannelRecord(chan)) diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c index c400d1aac..870a25cb5 100644 --- a/modules/database/test/std/rec/linkFilterTest.c +++ b/modules/database/test/std/rec/linkFilterTest.c @@ -46,9 +46,9 @@ static void expectProcSuccess(const char *rec) static void expectProcFailure(const char *rec) { char fieldname[20]; - testDiag("expecting failure S_db_emptyArray %#x from %s", S_db_emptyArray, rec); + testDiag("expecting failure S_db_badField %#x from %s", S_db_badField, rec); sprintf(fieldname, "%s.PROC", rec); - testdbPutFieldFail(S_db_emptyArray, fieldname, DBF_LONG, 1); + testdbPutFieldFail(S_db_badField, fieldname, DBF_LONG, 1); sprintf(fieldname, "%s.SEVR", rec); testdbGetFieldEqual(fieldname, DBF_LONG, INVALID_ALARM); sprintf(fieldname, "%s.STAT", rec); From 6b5abf76c854937c5442cf22f4453306094a66d1 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 1 Jun 2020 10:50:11 +0200 Subject: [PATCH 41/43] do not handle empty arrays (undefined behavior) --- modules/database/src/ioc/db/dbDbLink.c | 3 --- modules/database/test/std/rec/linkFilterTest.c | 7 +------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index 4cd87374e..b2813145c 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -221,9 +221,6 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, if (status) return status; - - if (pnRequest && *pnRequest <= 0) - return S_db_badField; } if (!status && precord != dbChannelRecord(chan)) diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c index 870a25cb5..7891bff12 100644 --- a/modules/database/test/std/rec/linkFilterTest.c +++ b/modules/database/test/std/rec/linkFilterTest.c @@ -81,7 +81,7 @@ static void expectRange(long start, long end) MAIN(linkFilterTest) { - testPlan(98); + testPlan(83); startTestIoc("linkFilterTest.db"); testDiag("PINI"); @@ -96,7 +96,6 @@ MAIN(linkFilterTest) testDiag("backward range"); changeRange(5,3,0); expectProcFailure("ai"); - expectProcFailure("wf"); testDiag("step 2"); changeRange(1,6,2); @@ -107,7 +106,6 @@ MAIN(linkFilterTest) testDiag("range start beyond src.NORD"); changeRange(8,9,0); expectProcFailure("ai"); - expectProcFailure("wf"); testDiag("range end beyond src.NORD"); changeRange(3,9,0); @@ -118,7 +116,6 @@ MAIN(linkFilterTest) testDiag("range start beyond src.NELM"); changeRange(11,12,0); expectProcFailure("ai"); - expectProcFailure("wf"); testDiag("range end beyond src.NELM"); changeRange(4,12,0); @@ -129,7 +126,6 @@ MAIN(linkFilterTest) testDiag("single value beyond src.NORD"); changeRange(8,0,0); expectProcFailure("ai"); - expectProcFailure("wf"); testDiag("single value"); changeRange(5,0,0); @@ -140,7 +136,6 @@ MAIN(linkFilterTest) testDiag("single beyond rec.NELM"); changeRange(12,0,0); expectProcFailure("ai"); - expectProcFailure("wf"); testIocShutdownOk(); testdbCleanup(); From cf56a0e08e5384495ad228678b1b88584a1e7e87 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 30 Oct 2020 17:43:03 -0500 Subject: [PATCH 42/43] Fix linkFilterTest, move Release Notes to the right place --- documentation/RELEASE_NOTES.md | 20 +++++++++---------- .../database/test/std/rec/linkFilterTest.c | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index a7f2806f9..c6ec174f3 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -17,6 +17,16 @@ should also be read to understand what has changed since earlier releases. +### Filters in database input links + +Input database links can now use channel filters, it is not necessary to +make them CA links for the filters to work. + +### ai Soft Channel support + +The Soft Channel device support for ai records now returns failure when +fetching the INP link fails. + ### Support for zero-length arrays Several modifications have been made to properly support zero-length @@ -373,16 +383,6 @@ As long as all support modules and IOCs are rebuilt from source after updating them to use this release of EPICS Base, these changes should not have any affect. -### Filters in database links - -Input links can now use filters, most importantly array element and sub array -access, even if they are not channel access links. - -### ai Soft Channel support - -The Soft Channel device support for ai records now returns failure when -fetching the INP link fails. - ### logClient reliability On supported targets (Linux, Mac, Windows) logClient will attempt to avoid dropping diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c index 7891bff12..80891851d 100644 --- a/modules/database/test/std/rec/linkFilterTest.c +++ b/modules/database/test/std/rec/linkFilterTest.c @@ -48,7 +48,7 @@ static void expectProcFailure(const char *rec) char fieldname[20]; testDiag("expecting failure S_db_badField %#x from %s", S_db_badField, rec); sprintf(fieldname, "%s.PROC", rec); - testdbPutFieldFail(S_db_badField, fieldname, DBF_LONG, 1); + testdbPutFieldFail(S_db_onlyOne, fieldname, DBF_LONG, 1); sprintf(fieldname, "%s.SEVR", rec); testdbGetFieldEqual(fieldname, DBF_LONG, INVALID_ALARM); sprintf(fieldname, "%s.STAT", rec); From 9048e998fbb155d75d363a2522a5ee13b79d76e4 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 17 Nov 2020 15:21:12 +0100 Subject: [PATCH 43/43] add tests for empty array filter results --- modules/database/test/std/rec/linkFilterTest.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/database/test/std/rec/linkFilterTest.c b/modules/database/test/std/rec/linkFilterTest.c index 80891851d..1996c8c11 100644 --- a/modules/database/test/std/rec/linkFilterTest.c +++ b/modules/database/test/std/rec/linkFilterTest.c @@ -79,9 +79,15 @@ static void expectRange(long start, long end) testdbGetArrFieldEqual("wf.VAL", DBF_DOUBLE, n+2, n, buf+start); } +static void expectEmptyArray(void) +{ + testDiag("expecting empty array"); + testdbGetFieldEqual("wf.NORD", DBF_LONG, 0); +} + MAIN(linkFilterTest) { - testPlan(83); + testPlan(102); startTestIoc("linkFilterTest.db"); testDiag("PINI"); @@ -96,6 +102,8 @@ MAIN(linkFilterTest) testDiag("backward range"); changeRange(5,3,0); expectProcFailure("ai"); + expectProcSuccess("wf"); + expectEmptyArray(); testDiag("step 2"); changeRange(1,6,2); @@ -106,6 +114,8 @@ MAIN(linkFilterTest) testDiag("range start beyond src.NORD"); changeRange(8,9,0); expectProcFailure("ai"); + expectProcSuccess("wf"); + expectEmptyArray(); testDiag("range end beyond src.NORD"); changeRange(3,9,0); @@ -116,6 +126,7 @@ MAIN(linkFilterTest) testDiag("range start beyond src.NELM"); changeRange(11,12,0); expectProcFailure("ai"); + expectProcSuccess("wf"); testDiag("range end beyond src.NELM"); changeRange(4,12,0); @@ -126,6 +137,8 @@ MAIN(linkFilterTest) testDiag("single value beyond src.NORD"); changeRange(8,0,0); expectProcFailure("ai"); + expectProcSuccess("wf"); + expectEmptyArray(); testDiag("single value"); changeRange(5,0,0); @@ -136,6 +149,8 @@ MAIN(linkFilterTest) testDiag("single beyond rec.NELM"); changeRange(12,0,0); expectProcFailure("ai"); + expectProcSuccess("wf"); + expectEmptyArray(); testIocShutdownOk(); testdbCleanup();