dbLockTest and dbStressLock
This commit is contained in:
@@ -10,6 +10,9 @@ TOP=../../../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# Allow access to private headers in db/
|
||||
USR_CPPFLAGS = -I ../..
|
||||
|
||||
TESTLIBRARY = dbTestIoc
|
||||
|
||||
dbTestIoc_SRCS += xRecord.c
|
||||
@@ -54,6 +57,11 @@ testHarness_SRCS += dbLockTest.c
|
||||
TESTS += dbLockTest
|
||||
TESTFILES += ../dbLockTest.db
|
||||
|
||||
TESTPROD_HOST += dbStressTest
|
||||
dbStressTest_SRCS += dbStressLock.c
|
||||
dbStressTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
|
||||
TESTS += dbStressTest
|
||||
|
||||
TESTPROD_HOST += testdbConvert
|
||||
testdbConvert_SRCS += testdbConvert.c
|
||||
testHarness_SRCS += testdbConvert.c
|
||||
|
||||
@@ -9,7 +9,15 @@
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include "dbLock.h"
|
||||
#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"
|
||||
@@ -28,15 +36,19 @@ void compareSets(int match, const char *A, const char *B)
|
||||
rA = testdbRecordPtr(A);
|
||||
rB = testdbRecordPtr(B);
|
||||
|
||||
actual = dbLockGetLockId(rA)==dbLockGetLockId(rB);
|
||||
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) {
|
||||
testDiag("Check initial creation of DB links");
|
||||
DBENTRY entry;
|
||||
long status;
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
@@ -48,6 +60,27 @@ void testSets(void) {
|
||||
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");
|
||||
@@ -71,6 +104,299 @@ void testSets(void) {
|
||||
|
||||
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;
|
||||
epicsThreadId myself = epicsThreadGetIdSelf();
|
||||
|
||||
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
|
||||
|
||||
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();
|
||||
@@ -78,7 +404,13 @@ void testSets(void) {
|
||||
|
||||
MAIN(dbLockTest)
|
||||
{
|
||||
testPlan(15);
|
||||
testPlan(99);
|
||||
testSets();
|
||||
testSingleLock();
|
||||
testMultiLock();
|
||||
testLinkBreak();
|
||||
testLinkMake();
|
||||
testLinkChange();
|
||||
testLinkNOP();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -19,3 +19,6 @@ record(x, "rece") {
|
||||
|
||||
record(x, "recf") {
|
||||
}
|
||||
|
||||
record(x, "recg") {
|
||||
}
|
||||
|
||||
311
src/ioc/db/test/dbStressLock.c
Normal file
311
src/ioc/db/test/dbStressLock.c
Normal file
@@ -0,0 +1,311 @@
|
||||
/*************************************************************************\
|
||||
* 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.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Lockset stress test.
|
||||
*
|
||||
* The test stratagy is for N threads to contend for M records.
|
||||
* Each thread will perform one of three operations:
|
||||
* 1) Lock a single record.
|
||||
* 2) Lock several records.
|
||||
* 3) Retarget the TSEL link of a record
|
||||
*
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "envDefs.h"
|
||||
#include "epicsStdlib.h"
|
||||
#include "epicsSpin.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsMutex.h"
|
||||
#include "dbCommon.h"
|
||||
|
||||
#include "dbLockPvt.h"
|
||||
#include "dbStaticLib.h"
|
||||
|
||||
#include "dbUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
#include "dbAccess.h"
|
||||
#include "errlog.h"
|
||||
|
||||
#include "xRecord.h"
|
||||
|
||||
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
/* number of seconds for the test to run */
|
||||
static double runningtime = 18.0;
|
||||
|
||||
/* number of worker threads */
|
||||
static unsigned int nworkers = 5;
|
||||
|
||||
static unsigned int nrecords;
|
||||
|
||||
#define MAXLOCK 20
|
||||
|
||||
static dbCommon **precords;
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
unsigned long N[3];
|
||||
double X[3];
|
||||
double X2[3];
|
||||
|
||||
unsigned int done;
|
||||
epicsEventId donevent;
|
||||
|
||||
dbCommon *prec[MAXLOCK];
|
||||
} workerPriv;
|
||||
|
||||
/* hopefully a uniform random number in [0.0, 1.0] */
|
||||
static
|
||||
double getRand(void)
|
||||
{
|
||||
return rand()/(double)RAND_MAX;
|
||||
}
|
||||
|
||||
static
|
||||
void doSingle(workerPriv *p)
|
||||
{
|
||||
size_t recn = (size_t)(getRand()*(nrecords-1));
|
||||
dbCommon *prec = precords[recn];
|
||||
xRecord *px = (xRecord*)prec;
|
||||
|
||||
dbScanLock(prec);
|
||||
px->val++;
|
||||
dbScanUnlock(prec);
|
||||
}
|
||||
|
||||
static volatile int bitbucket;
|
||||
|
||||
static
|
||||
void doMulti(workerPriv *p)
|
||||
{
|
||||
int sum = 0;
|
||||
size_t i;
|
||||
size_t nlock = 2 + (size_t)(getRand()*(MAXLOCK-3));
|
||||
size_t nrec = (size_t)(getRand()*(nrecords-1));
|
||||
dbLocker *locker;
|
||||
|
||||
assert(nlock>=2);
|
||||
assert(nlock<nrecords);
|
||||
|
||||
for(i=0; i<nlock; i++, nrec=(nrec+1)%nrecords) {
|
||||
p->prec[i] = precords[nrec];
|
||||
}
|
||||
|
||||
locker = dbLockerAlloc(p->prec, nlock, 0);
|
||||
if(!locker)
|
||||
testAbort("locker allocation fails");
|
||||
|
||||
dbScanLockMany(locker);
|
||||
for(i=0; i<nlock; i++) {
|
||||
xRecord *px = (xRecord*)p->prec[i];
|
||||
sum += px->val;
|
||||
}
|
||||
dbScanUnlockMany(locker);
|
||||
|
||||
dbLockerFree(locker);
|
||||
}
|
||||
|
||||
static
|
||||
void doreTarget(workerPriv *p)
|
||||
{
|
||||
char scratchsrc[60];
|
||||
char scratchdst[MAX_STRING_SIZE];
|
||||
long ret;
|
||||
DBADDR dbaddr;
|
||||
double action = getRand();
|
||||
size_t nsrc = (size_t)(getRand()*(nrecords-1));
|
||||
size_t ntarg = (size_t)(getRand()*(nrecords-1));
|
||||
xRecord *psrc = (xRecord*)precords[nsrc];
|
||||
xRecord *ptarg = (xRecord*)precords[ntarg];
|
||||
|
||||
strcpy(scratchsrc, psrc->name);
|
||||
strcat(scratchsrc, ".TSEL");
|
||||
|
||||
ret = dbNameToAddr(scratchsrc, &dbaddr);
|
||||
if(ret)
|
||||
testAbort("bad record name? %ld", ret);
|
||||
|
||||
if(action<0.25) {
|
||||
scratchdst[0] = '\0';
|
||||
} else {
|
||||
strcpy(scratchdst, ptarg->name);
|
||||
}
|
||||
|
||||
ret = dbPutField(&dbaddr, DBR_STRING, ptarg->name, 1);
|
||||
if(ret)
|
||||
testAbort("put fails with %ld", ret);
|
||||
}
|
||||
|
||||
static
|
||||
void worker(void *raw)
|
||||
{
|
||||
struct timespec before;
|
||||
workerPriv *priv = raw;
|
||||
|
||||
testDiag("worker %d is %p", priv->id, epicsThreadGetIdSelf());
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &before);
|
||||
|
||||
while(!priv->done) {
|
||||
double sel = getRand();
|
||||
struct timespec after;
|
||||
double duration;
|
||||
|
||||
int act;
|
||||
if(sel<0.33) {
|
||||
doSingle(priv);
|
||||
act = 0;
|
||||
} else if(sel<0.66) {
|
||||
doMulti(priv);
|
||||
act = 1;
|
||||
} else {
|
||||
doreTarget(priv);
|
||||
act = 2;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &after);
|
||||
|
||||
duration = (double)((long)after.tv_nsec - (long)before.tv_nsec);
|
||||
duration *= 1e-9;
|
||||
duration += (double)(after.tv_sec - before.tv_sec);
|
||||
|
||||
priv->N[act]++;
|
||||
priv->X[act] += duration;
|
||||
priv->X2[act] += duration*duration;
|
||||
}
|
||||
|
||||
epicsEventMustTrigger(priv->donevent);
|
||||
}
|
||||
|
||||
MAIN(dbStressTest)
|
||||
{
|
||||
DBENTRY ent;
|
||||
long status;
|
||||
unsigned int i;
|
||||
workerPriv *priv;
|
||||
char *nwork=getenv("NWORK");
|
||||
struct timespec seed;
|
||||
|
||||
testPlan(0);
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &seed);
|
||||
srand(seed.tv_nsec);
|
||||
|
||||
if(nwork) {
|
||||
long val = 0;
|
||||
epicsParseLong(nwork, &val, 0, NULL);
|
||||
if(val>2)
|
||||
nworkers = val;
|
||||
}
|
||||
|
||||
priv = callocMustSucceed(nworkers, sizeof(*priv), "no memory");
|
||||
|
||||
testDiag("lock set stress test");
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
testdbReadDatabase("dbStressLock.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
/* collect an array of all records */
|
||||
dbInitEntry(pdbbase, &ent);
|
||||
for(status = dbFirstRecordType(&ent);
|
||||
!status;
|
||||
status = dbNextRecordType(&ent))
|
||||
{
|
||||
for(status = dbFirstRecord(&ent);
|
||||
!status;
|
||||
status = dbNextRecord(&ent))
|
||||
{
|
||||
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
|
||||
continue;
|
||||
nrecords++;
|
||||
}
|
||||
|
||||
}
|
||||
if(nrecords<2)
|
||||
testAbort("where are the records!");
|
||||
precords = callocMustSucceed(nrecords, sizeof(*precords), "no mem");
|
||||
for(status = dbFirstRecordType(&ent), i = 0;
|
||||
!status;
|
||||
status = dbNextRecordType(&ent))
|
||||
{
|
||||
for(status = dbFirstRecord(&ent);
|
||||
!status;
|
||||
status = dbNextRecord(&ent))
|
||||
{
|
||||
if(ent.precnode->flags&DBRN_FLAGS_ISALIAS)
|
||||
continue;
|
||||
precords[i++] = ent.precnode->precord;
|
||||
}
|
||||
|
||||
}
|
||||
dbFinishEntry(&ent);
|
||||
|
||||
testDiag("Running with %u workers and %u records",
|
||||
nworkers, nrecords);
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
priv[i].id = i;
|
||||
priv[i].donevent = epicsEventMustCreate(epicsEventEmpty);
|
||||
}
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
epicsThreadMustCreate("runner", epicsThreadPriorityMedium,
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
&worker, &priv[i]);
|
||||
}
|
||||
|
||||
testDiag("All started");
|
||||
|
||||
epicsThreadSleep(runningtime);
|
||||
|
||||
testDiag("Stopping");
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
priv[i].done = 1;
|
||||
}
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
epicsEventMustWait(priv[i].donevent);
|
||||
epicsEventDestroy(priv[i].donevent);
|
||||
}
|
||||
|
||||
testDiag("All stopped");
|
||||
|
||||
for(i=0; i<nworkers; i++) {
|
||||
testDiag("Worker %u", i);
|
||||
testDiag("N = %lu %lu %lu", priv[i].N[0], priv[i].N[1], priv[i].N[2]);
|
||||
testDiag("X = %g %g %g", priv[i].X[0], priv[i].X[1], priv[i].X[2]);
|
||||
testDiag("X2= %g %g %g", priv[i].X2[0], priv[i].X2[1], priv[i].X2[2]);
|
||||
|
||||
testOk1(priv[i].N[0]>0);
|
||||
testOk1(priv[i].N[1]>0);
|
||||
testOk1(priv[i].N[2]>0);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
free(priv);
|
||||
free(precords);
|
||||
|
||||
return testDone();
|
||||
}
|
||||
40
src/ioc/db/test/dbStressLock.db
Normal file
40
src/ioc/db/test/dbStressLock.db
Normal file
@@ -0,0 +1,40 @@
|
||||
record(x, "rec01") {}
|
||||
record(x, "rec02") {}
|
||||
record(x, "rec03") {}
|
||||
record(x, "rec04") {}
|
||||
record(x, "rec05") {}
|
||||
record(x, "rec06") {}
|
||||
record(x, "rec07") {}
|
||||
record(x, "rec08") {}
|
||||
record(x, "rec09") {}
|
||||
record(x, "rec10") {}
|
||||
record(x, "rec11") {}
|
||||
record(x, "rec12") {}
|
||||
record(x, "rec13") {}
|
||||
record(x, "rec14") {}
|
||||
record(x, "rec15") {}
|
||||
record(x, "rec16") {}
|
||||
record(x, "rec17") {}
|
||||
record(x, "rec18") {}
|
||||
record(x, "rec19") {}
|
||||
record(x, "rec20") {}
|
||||
record(x, "rec21") {}
|
||||
record(x, "rec22") {}
|
||||
record(x, "rec23") {}
|
||||
record(x, "rec24") {}
|
||||
record(x, "rec25") {}
|
||||
record(x, "rec26") {}
|
||||
record(x, "rec27") {}
|
||||
record(x, "rec28") {}
|
||||
record(x, "rec29") {}
|
||||
record(x, "rec30") {}
|
||||
record(x, "rec31") {}
|
||||
record(x, "rec32") {}
|
||||
record(x, "rec33") {}
|
||||
record(x, "rec34") {}
|
||||
record(x, "rec35") {}
|
||||
record(x, "rec36") {}
|
||||
record(x, "rec37") {}
|
||||
record(x, "rec38") {}
|
||||
record(x, "rec39") {}
|
||||
record(x, "rec40") {}
|
||||
Reference in New Issue
Block a user