Files
epics-base/modules/database/test/ioc/db/dbLockTest.c
2018-06-19 11:31:13 +02:00

432 lines
12 KiB
C

/*************************************************************************\
* Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven
* National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
*/
#include <stdlib.h>
#include "epicsSpin.h"
#include "epicsMutex.h"
#include "dbCommon.h"
#include "epicsThread.h"
#include "dbLockPvt.h"
#include "dbStaticLib.h"
#include "dbUnitTest.h"
#include "testMain.h"
#include "dbAccess.h"
#include "errlog.h"
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
static
void compareSets(int match, const char *A, const char *B)
{
int actual;
dbCommon *rA, *rB;
rA = testdbRecordPtr(A);
rB = testdbRecordPtr(B);
actual = rA->lset->plockSet==rB->lset->plockSet;
testOk(match==actual, "dbLockGetLockId(\"%s\")%c=dbLockGetLockId(\"%s\")",
A, match?'=':'!', B);
}
#define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B);
#define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B);
static
void testSets(void) {
DBENTRY entry;
long status;
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
testDiag("Check that all records have initialized lockRecord and lockSet");
dbInitEntry(pdbbase, &entry);
for(status = dbFirstRecordType(&entry);
!status;
status = dbNextRecordType(&entry)) {
for(status = dbFirstRecord(&entry);
!status;
status = dbNextRecord(&entry)) {
dbCommon *prec = entry.precnode->precord;
testOk(prec->lset!=NULL, "%s.LSET != NULL", prec->name);
if(prec->lset!=NULL)
testOk(prec->lset->plockSet!=NULL, "%s.LSET.plockSet != NULL", prec->name);
else
testSkip(1, "lockRecord missing");
}
}
dbFinishEntry(&entry);
testDiag("Check initial creation of DB links");
/* reca is by itself */
compareSets(0, "reca", "recb");
compareSets(0, "reca", "recc");
compareSets(0, "reca", "recd");
compareSets(0, "reca", "rece");
compareSets(0, "reca", "recf");
/* recb and recc should be in a lockset */
compareSets(1, "recb", "recc");
compareSets(0, "recb", "recd");
compareSets(0, "recb", "rece");
compareSets(0, "recb", "recf");
compareSets(0, "recc", "recd");
compareSets(0, "recc", "rece");
compareSets(0, "recc", "recf");
/* recd, e, and f should be in a lockset */
compareSets(1, "recd", "rece");
compareSets(1, "recd", "recf");
compareSets(1, "rece", "recf");
testOk1(testdbRecordPtr("reca")->lset->plockSet->refcount==1);
testOk1(testdbRecordPtr("recb")->lset->plockSet->refcount==2);
testOk1(testdbRecordPtr("recd")->lset->plockSet->refcount==3);
testIocShutdownOk();
testdbCleanup();
}
static void testSingleLock(void)
{
dbCommon *prec;
testDiag("testing dbScanLock()/dbScanUnlock()");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
prec = testdbRecordPtr("reca");
testOk1(prec->lset->plockSet->refcount==1);
dbScanLock(prec);
/* scan lock does not keep a reference to the lockSet */
testOk1(prec->lset->plockSet->refcount==1);
dbScanUnlock(prec);
dbScanLock(prec);
dbScanLock(prec);
/* scan lock can be recursive */
testOk1(prec->lset->plockSet->refcount==1);
dbScanUnlock(prec);
dbScanUnlock(prec);
testIocShutdownOk();
testdbCleanup();
}
static void testMultiLock(void)
{
dbCommon *prec[8];
dbLocker *plockA;
#ifdef LOCKSET_DEBUG
epicsThreadId myself = epicsThreadGetIdSelf();
#endif
testDiag("Test multi-locker function (lock everything)");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
prec[0] = testdbRecordPtr("reca");
prec[1] = testdbRecordPtr("recb");
prec[2] = testdbRecordPtr("recc");
prec[3] = NULL;
prec[4] = testdbRecordPtr("recd");
prec[5] = testdbRecordPtr("rece");
prec[6] = testdbRecordPtr("recf");
prec[7] = testdbRecordPtr("recg");
testDiag("Test init refcounts");
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1);
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2);
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3);
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1);
plockA = dbLockerAlloc(prec, 8, 0);
if(!plockA)
testAbort("dbLockerAlloc() failed");
testDiag("After locker created");
/* locker takes 7 references, one for each lockRecord. */
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2);
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4);
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6);
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2);
#ifdef LOCKSET_DEBUG
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL);
#endif
dbScanLockMany(plockA);
testDiag("After locker locked");
/* locker takes 4 references, one for each lockSet. */
testIntOk1(ellCount(&plockA->locked),==,4);
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3);
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,5);
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,7);
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,3);
#ifdef LOCKSET_DEBUG
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,myself);
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,myself);
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,myself);
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,myself);
#endif
/* recursive locking of individual records is allowed */
dbScanLock(prec[0]);
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,3);
dbScanUnlock(prec[0]);
/* recursive locking with dbScanLockMany() isn't
* dbScanLockMany(plockA); <-- would fail
*/
dbScanUnlockMany(plockA);
testDiag("After locker unlocked");
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,2);
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,4);
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,6);
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,2);
#ifdef LOCKSET_DEBUG
testPtrOk1(testdbRecordPtr("reca")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recb")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recd")->lset->plockSet->owner,==,NULL);
testPtrOk1(testdbRecordPtr("recg")->lset->plockSet->owner,==,NULL);
#endif
dbLockerFree(plockA);
testDiag("After locker free'd");
testIntOk1(testdbRecordPtr("reca")->lset->plockSet->refcount,==,1);
testIntOk1(testdbRecordPtr("recb")->lset->plockSet->refcount,==,2);
testIntOk1(testdbRecordPtr("recd")->lset->plockSet->refcount,==,3);
testIntOk1(testdbRecordPtr("recg")->lset->plockSet->refcount,==,1);
testIocShutdownOk();
testdbCleanup();
}
static void testLinkBreak(void)
{
dbCommon *precB, *precC;
testDiag("Test break link");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
precB = testdbRecordPtr("recb");
precC = testdbRecordPtr("recc");
testOk1(precB->lset->plockSet==precC->lset->plockSet);
testOk1(precB->lset->plockSet->refcount==2);
/* break the link between B and C */
testdbPutFieldOk("recb.SDIS", DBR_STRING, "");
testOk1(precB->lset->plockSet!=precC->lset->plockSet);
testIntOk1(precB->lset->plockSet->refcount, ==, 1);
testIntOk1(precC->lset->plockSet->refcount, ==, 1);
testIocShutdownOk();
testdbCleanup();
}
static void testLinkMake(void)
{
dbCommon *precA, *precG;
lockSet *lA, *lG;
testDiag("Test make link");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
precA = testdbRecordPtr("reca");
lA = dbLockGetRef(precA->lset);
precG = testdbRecordPtr("recg");
lG = dbLockGetRef(precG->lset);
testPtrOk1(precA->lset->plockSet, !=, precG->lset->plockSet);
testIntOk1(precA->lset->plockSet->refcount, ==, 2);
testIntOk1(precG->lset->plockSet->refcount, ==, 2);
/* make a link between A and G */
testdbPutFieldOk("reca.SDIS", DBR_STRING, "recg");
testPtrOk1(precA->lset->plockSet, ==, precG->lset->plockSet);
testIntOk1(precA->lset->plockSet->refcount, ==, 3);
if(precA->lset->plockSet==lG) {
testIntOk1(lA->refcount, ==, 1);
testIntOk1(lG->refcount, ==, 3);
} else {
testIntOk1(lA->refcount, ==, 3);
testIntOk1(lG->refcount, ==, 1);
}
dbLockDecRef(lG);
dbLockDecRef(lA);
testIocShutdownOk();
testdbCleanup();
}
static void testLinkChange(void)
{
dbCommon *precB, *precC, *precG;
lockSet *lB, *lG;
testDiag("Test re-target link");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
precB = testdbRecordPtr("recb");
precC = testdbRecordPtr("recc");
precG = testdbRecordPtr("recg");
lB = dbLockGetRef(precB->lset);
lG = dbLockGetRef(precG->lset);
testPtrOk1(lB,==,precC->lset->plockSet);
testPtrOk1(lB,!=,lG);
testIntOk1(lB->refcount,==,3);
testIntOk1(lG->refcount,==,2);
/* break the link between B and C and replace it
* with a link between B and G
*/
testdbPutFieldOk("recb.SDIS", DBR_STRING, "recg");
testPtrOk1(precB->lset->plockSet,==,lB);
testPtrOk1(precG->lset->plockSet,==,lB);
testPtrOk1(precC->lset->plockSet,!=,lB);
testPtrOk1(precC->lset->plockSet,!=,lG);
testIntOk1(lB->refcount,==,3);
testIntOk1(lG->refcount,==,1);
testIntOk1(precC->lset->plockSet->refcount,==,1);
dbLockDecRef(lB);
dbLockDecRef(lG);
testIocShutdownOk();
testdbCleanup();
}
static void testLinkNOP(void)
{
dbCommon *precB, *precC;
testDiag("Test re-target link to the same destination");
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("dbLockTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
precB = testdbRecordPtr("recb");
precC = testdbRecordPtr("recc");
testOk1(precB->lset->plockSet==precC->lset->plockSet);
testOk1(precB->lset->plockSet->refcount==2);
/* renew link between B and C */
testdbPutFieldOk("recb.SDIS", DBR_STRING, "recc");
testOk1(precB->lset->plockSet==precC->lset->plockSet);
testOk1(precB->lset->plockSet->refcount==2);
testIocShutdownOk();
testdbCleanup();
}
MAIN(dbLockTest)
{
#ifdef LOCKSET_DEBUG
testPlan(100);
#else
testPlan(88);
#endif
testSets();
testSingleLock();
testMultiLock();
testLinkBreak();
testLinkMake();
testLinkChange();
testLinkNOP();
return testDone();
}