/*************************************************************************\ * Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven * National Laboratory. * SPDX-License-Identifier: EPICS * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* * Author: Michael Davidsaver */ #include #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(); }