diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index 77d9266307..23a6448d86 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -103,6 +103,7 @@ long dbDbInitLink(struct link *plink, short dbfType) plink->lset = &dbDb_lset; plink->type = DB_LINK; plink->value.pv_link.pvt = chan; + plink->flags &= ~(DBLINK_FLAG_VISITED|DBLINK_FLAG_LOOPS|DBLINK_FLAG_LOOPFREE); ellAdd(&precord->bklnk, &plink->value.pv_link.backlinknode); /* merging into the same lockset is deferred to the caller. * cf. initPVLinks() @@ -232,19 +233,48 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, return status; } -static long dbDbGetControlLimits(const struct link *plink, double *low, - double *high) +/* Some records get options (precsision, units, ...) for some fields + * from an input link. We need to catch the case that this link + * points back to the same field or we will end in an infinite recursion. +*/ +static long dbGetLoopSafe(const struct link *plink, short dbrType, + void *pbuffer, long option) { + /* We need to cast away const. + That's ok because we know that plink is never actually readonly. + */ + struct link *mutable_plink = (struct link *)plink; + long status = S_dbLib_badLink; dbChannel *chan = linkChannel(plink); DBADDR *paddr = &chan->addr; + long number_elements = 0; + + if (plink->flags & DBLINK_FLAG_LOOPS) + return status; + if (!(plink->flags & DBLINK_FLAG_LOOPFREE)) + return dbGet(paddr, dbrType, pbuffer, &option, &number_elements, NULL); + dbScanLock(paddr->precord); + if (mutable_plink->flags & DBLINK_FLAG_VISITED) { + mutable_plink->flags |= DBLINK_FLAG_LOOPS; + } else { + mutable_plink->flags |= DBLINK_FLAG_VISITED; + status = dbGet(paddr, dbrType, pbuffer, &option, &number_elements, NULL); + if (!(mutable_plink->flags & DBLINK_FLAG_LOOPS)) + mutable_plink->flags |= DBLINK_FLAG_LOOPFREE; + mutable_plink->flags &= ~DBLINK_FLAG_VISITED; + } + dbScanUnlock(paddr->precord); + return status; +} + +static long dbDbGetControlLimits(const struct link *plink, double *low, + double *high) +{ struct buffer { DBRctrlDouble double value; } buffer; - long options = DBR_CTRL_DOUBLE; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - NULL); + long status = dbGetLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_CTRL_DOUBLE); if (status) return status; @@ -257,16 +287,11 @@ static long dbDbGetControlLimits(const struct link *plink, double *low, static long dbDbGetGraphicLimits(const struct link *plink, double *low, double *high) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRgrDouble double value; } buffer; - long options = DBR_GR_DOUBLE; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - NULL); + long status = dbGetLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_GR_DOUBLE); if (status) return status; @@ -279,16 +304,11 @@ static long dbDbGetGraphicLimits(const struct link *plink, double *low, static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, double *low, double *high, double *hihi) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRalDouble double value; } buffer; - long options = DBR_AL_DOUBLE; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - 0); + long status = dbGetLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_AL_DOUBLE); if (status) return status; @@ -302,16 +322,11 @@ static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, static long dbDbGetPrecision(const struct link *plink, short *precision) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRprecision double value; } buffer; - long options = DBR_PRECISION; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - 0); + long status = dbGetLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_PRECISION); if (status) return status; @@ -322,16 +337,11 @@ static long dbDbGetPrecision(const struct link *plink, short *precision) static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize) { - dbChannel *chan = linkChannel(plink); - DBADDR *paddr = &chan->addr; struct buffer { DBRunits double value; } buffer; - long options = DBR_UNITS; - long number_elements = 0; - long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, - 0); + long status = dbGetLoopSafe(plink, DBR_DOUBLE, &buffer, DBR_UNITS); if (status) return status; diff --git a/modules/database/src/ioc/dbStatic/link.h b/modules/database/src/ioc/dbStatic/link.h index a4aab8dd88..a7e2008be7 100644 --- a/modules/database/src/ioc/dbStatic/link.h +++ b/modules/database/src/ioc/dbStatic/link.h @@ -72,6 +72,9 @@ DBCORE_API extern const maplinkType pamaplinkType[LINK_NTYPES]; /* DBLINK Flag bits */ #define DBLINK_FLAG_INITIALIZED 1 /* dbInitLink() called */ #define DBLINK_FLAG_TSELisTIME 2 /* Use TSEL to get timeStamp */ +#define DBLINK_FLAG_VISITED 4 /* Used in loop detection */ +#define DBLINK_FLAG_LOOPS 8 /* Used in loop detection */ +#define DBLINK_FLAG_LOOPFREE 16 /* Used in loop detection */ struct macro_link { char *macroStr;