diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 3b223be89..be954be12 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -684,6 +684,17 @@ static long getUnits(const struct link *plink, return gotAttributes ? 0 : -1; } +static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) +{ + caLink *pca; + long status; + + pcaGetCheck + status = rtn(plink, priv); + epicsMutexUnlock(pca->lock); + return status; +} + static void scanComplete(void *raw, dbCommon *prec) { caLink *pca = raw; @@ -727,7 +738,7 @@ static lset dbCa_lset = { getPrecision, getUnits, getAlarm, getTimeStamp, dbCaPutLink, dbCaPutAsync, - scanForward + scanForward, doLocked }; static void connectionCallback(struct connection_handler_args arg) diff --git a/src/ioc/db/dbConstLink.c b/src/ioc/db/dbConstLink.c index 7d196ec88..00eeb8e17 100644 --- a/src/ioc/db/dbConstLink.c +++ b/src/ioc/db/dbConstLink.c @@ -135,6 +135,5 @@ static lset dbConst_lset = { NULL, NULL, NULL, NULL, NULL, NULL, - NULL + NULL, NULL }; - diff --git a/src/ioc/db/dbDbLink.c b/src/ioc/db/dbDbLink.c index 87e78b1b0..8263293e9 100644 --- a/src/ioc/db/dbDbLink.c +++ b/src/ioc/db/dbDbLink.c @@ -337,6 +337,11 @@ static void dbDbScanFwdLink(struct link *plink) dbScanPassive(precord, paddr->precord); } +static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) +{ + return rtn(plink, priv); +} + static lset dbDb_lset = { 0, 0, /* not Constant, not Volatile */ NULL, dbDbRemoveLink, @@ -348,6 +353,5 @@ static lset dbDb_lset = { dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, dbDbPutValue, NULL, - dbDbScanFwdLink + dbDbScanFwdLink, doLocked }; - diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index bb74f91c6..d383a3cbe 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -414,6 +414,18 @@ void dbScanFwdLink(struct link *plink) plset->scanForward(plink); } +long dbLinkDoLocked(struct link *plink, dbLinkUserCallback rtn, + void *priv) +{ + lset *plset = plink->lset; + + if (!rtn || !plset || !plset->doLocked) + return S_db_noLSET; + + return plset->doLocked(plink, rtn, priv); +} + + /* Helper functions for long string support */ long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size, diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index cf7ff0433..6f5858b65 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -27,6 +27,8 @@ extern "C" { struct dbLocker; +typedef long (*dbLinkUserCallback)(struct link *plink, void *priv); + typedef struct lset { /* Characteristics of the link type */ const unsigned isConstant:1; @@ -71,6 +73,9 @@ typedef struct lset { /* Process */ void (*scanForward)(struct link *plink); + + /* Atomicity */ + long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv); } lset; #define dbGetSevr(link, sevr) \ @@ -117,6 +122,9 @@ epicsShareFunc long dbPutLinkAsync(struct link *plink, short dbrType, const void *pbuffer, long nRequest); epicsShareFunc void dbScanFwdLink(struct link *plink); +epicsShareFunc long dbLinkDoLocked(struct link *plink, dbLinkUserCallback rtn, + void *priv); + epicsShareFunc long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size, epicsUInt32 *plen); epicsShareFunc long dbGetLinkLS(struct link *plink, char *pbuffer, diff --git a/src/ioc/db/test/dbCaLinkTest.c b/src/ioc/db/test/dbCaLinkTest.c index e1955a5ad..8d563a076 100644 --- a/src/ioc/db/test/dbCaLinkTest.c +++ b/src/ioc/db/test/dbCaLinkTest.c @@ -107,6 +107,39 @@ void putLink(DBLINK *plink, short dbr, const void*buf, long nReq) waitEvent = NULL; } +static long getTwice(struct link *psrclnk, void *dummy) +{ + epicsInt32 val1, val2; + long status = dbGetLink(psrclnk, DBR_LONG, &val1, 0, 0); + + if (status) return status; + + epicsThreadSleep(0.5); + status = dbGetLink(psrclnk, DBR_LONG, &val2, 0, 0); + if (status) return status; + + testDiag("val1 = %d, val2 = %d", val1, val2); + return (val1 == val2) ? 0 : -1; +} + +static void countUp(void *parm) +{ + xRecord *ptarg = (xRecord *)parm; + epicsInt32 val; + + for (val = 1; val < 10; val++) { + dbScanLock((dbCommon*)ptarg); + ptarg->val = val; + db_post_events(ptarg, &ptarg->val, DBE_VALUE|DBE_ALARM|DBE_ARCHIVE); + dbScanUnlock((dbCommon*)ptarg); + + epicsThreadSleep(0.1); + } + + if (waitEvent) + epicsEventMustTrigger(waitEvent); +} + static void testNativeLink(void) { xRecord *psrc, *ptarg; @@ -166,6 +199,27 @@ static void testNativeLink(void) testOk1(ptarg->val==1010); dbScanUnlock((dbCommon*)ptarg); + assert(!waitEvent); + waitEvent = epicsEventMustCreate(epicsEventEmpty); + + /* Start counter */ + epicsThreadCreate("countUp", epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackSmall), countUp, ptarg); + + dbScanLock((dbCommon*)psrc); + /* Check that unlocked gets change */ + temp = getTwice(psrclnk, NULL); + testOk(temp == -1, "unlocked, getTwice returned %d (-1)", temp); + + /* Check locked gets are atomic */ + temp = dbLinkDoLocked(psrclnk, getTwice, NULL); + testOk(temp == 0, "locked, getTwice returned %d (0)", temp); + dbScanUnlock((dbCommon*)psrc); + + epicsEventMustWait(waitEvent); + epicsEventDestroy(waitEvent); + waitEvent = NULL; + testIocShutdownOk(); testdbCleanup(); @@ -598,7 +652,7 @@ static void testCAC(void) MAIN(dbCaLinkTest) { - testPlan(99); + testPlan(101); testNativeLink(); testStringLink(); testCP(); diff --git a/src/ioc/db/test/jlinkz.c b/src/ioc/db/test/jlinkz.c index f283f814e..7bb8b831e 100644 --- a/src/ioc/db/test/jlinkz.c +++ b/src/ioc/db/test/jlinkz.c @@ -146,6 +146,7 @@ static lset lsetZ = { &z_putval, NULL, /* putasync */ NULL, /* forward */ + NULL, /* doLocked */ }; static diff --git a/src/ioc/db/test/xLink.c b/src/ioc/db/test/xLink.c index f6dfab95c..12175f44e 100644 --- a/src/ioc/db/test/xLink.c +++ b/src/ioc/db/test/xLink.c @@ -73,7 +73,7 @@ static lset xlink_lset = { NULL, NULL, NULL, NULL, NULL, NULL, - NULL + NULL, NULL }; static jlif xlinkIf = { diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c index 1b3222a34..a7ec9126a 100644 --- a/src/std/link/lnkCalc.c +++ b/src/std/link/lnkCalc.c @@ -597,6 +597,11 @@ static long lnkCalc_getAlarm(const struct link *plink, epicsEnum16 *status, return 0; } +static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) +{ + return rtn(plink, priv); +} + /************************* Interface Tables *************************/ @@ -610,7 +615,7 @@ static lset lnkCalc_lset = { lnkCalc_getPrecision, lnkCalc_getUnits, lnkCalc_getAlarm, NULL, NULL, NULL, - NULL + NULL, doLocked }; static jlif lnkCalcIf = { diff --git a/src/std/link/lnkConst.c b/src/std/link/lnkConst.c index 7062ff1f9..f3ff5d004 100644 --- a/src/std/link/lnkConst.c +++ b/src/std/link/lnkConst.c @@ -551,7 +551,7 @@ static lset lnkConst_lset = { NULL, NULL, NULL, NULL, NULL, NULL, - NULL + NULL, NULL }; static jlif lnkConstIf = {