From 7d50f62aed5a60caf58d143dd0da63f5c7d4071d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:47 -0500 Subject: [PATCH 1/7] dbScan: allow direct scan of I/O Intr list add scanIoImmediate() to allow drivers to implement I/O Intr scanning without the overhead of maintaining their own scan lists. --- src/ioc/db/dbScan.c | 22 ++++++++++++++++++++++ src/ioc/db/dbScan.h | 1 + 2 files changed, 23 insertions(+) diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 08c5c4af3..272b185b0 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -580,6 +580,28 @@ unsigned int scanIoRequest(IOSCANPVT piosh) return queued; } +unsigned int scanIoImmediate(IOSCANPVT piosh, int prio) +{ + io_scan_list *piosl; + + if (prio<0 || prio>=NUM_CALLBACK_PRIORITIES) + return S_db_errArg; + else if (scanCtl != ctlRun) + return 0; + + piosl = &piosh->iosl[prio]; + + if (ellCount(&piosl->scan_list.list) == 0) + return 0; + + scanList(&piosl->scan_list); + + if (piosh->cb) + piosh->cb(piosh->arg, piosh, prio); + + return 1 << prio; +} + /* May not be called while a scan request is queued or running */ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg) { diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h index cd9666348..8bd2bba22 100644 --- a/src/ioc/db/dbScan.h +++ b/src/ioc/db/dbScan.h @@ -68,6 +68,7 @@ epicsShareFunc int scanpiol(void); epicsShareFunc void scanIoInit(IOSCANPVT *ppios); epicsShareFunc unsigned int scanIoRequest(IOSCANPVT pios); +epicsShareFunc unsigned int scanIoImmediate(IOSCANPVT pios, int prio); epicsShareFunc void scanIoSetComplete(IOSCANPVT, io_scan_complete, void *usr); #ifdef __cplusplus From 2ba2b90b06b21ed0dbcf0fa29278104b7efc9d3c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:47 -0500 Subject: [PATCH 2/7] dbScan: add scanOnce3() w/ completion callback --- src/ioc/db/dbScan.c | 55 ++++++++++++++++++++++++++++++++------------- src/ioc/db/dbScan.h | 8 ++++--- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 272b185b0..3cd93da1a 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -26,10 +26,9 @@ #include "ellLib.h" #include "epicsEvent.h" #include "epicsExit.h" -#include "epicsInterrupt.h" #include "epicsMutex.h" #include "epicsPrint.h" -#include "epicsRingPointer.h" +#include "epicsRingBytes.h" #include "epicsStdio.h" #include "epicsStdlib.h" #include "epicsString.h" @@ -64,7 +63,7 @@ static volatile enum ctl scanCtl; static int onceQueueSize = 1000; static epicsEventId onceSem; -static epicsRingPointerId onceQ; +static epicsRingBytesId onceQ; static epicsThreadId onceTaskId; static void *exitOnce; @@ -172,7 +171,7 @@ void scanShutdown(void) deletePeriodic(); ioscanDestroy(); - epicsRingPointerDelete(onceQ); + epicsRingBytesDelete(onceQ); epicsEventDestroy(startStopEvent); epicsEventDestroy(onceSem); @@ -609,15 +608,27 @@ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg) piosh->arg = arg; } -void scanOnce(struct dbCommon *precord) +int scanOnce(struct dbCommon *precord) { + return scanOnce3(precord, NULL, NULL); +} + +typedef struct { + struct dbCommon *prec; + once_complete cb; + void *usr; +} onceEntry; + +int scanOnce3(struct dbCommon *precord, once_complete cb, void *usr) { static int newOverflow = TRUE; - int lockKey; + onceEntry ent; int pushOK; - lockKey = epicsInterruptLock(); - pushOK = epicsRingPointerPush(onceQ, precord); - epicsInterruptUnlock(lockKey); + ent.prec = precord; + ent.cb = cb; + ent.usr = usr; + + pushOK = epicsRingBytesPut(onceQ, (void*)&ent, sizeof(ent)); if (!pushOK) { if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n"); @@ -626,6 +637,8 @@ void scanOnce(struct dbCommon *precord) newOverflow = TRUE; } epicsEventSignal(onceSem); + + return !pushOK; } static void onceTask(void *arg) @@ -634,14 +647,24 @@ static void onceTask(void *arg) epicsEventSignal(startStopEvent); while (TRUE) { - void *precord; epicsEventMustWait(onceSem); - while ((precord = epicsRingPointerPop(onceQ))) { - if (precord == &exitOnce) goto shutdown; - dbScanLock(precord); - dbProcess(precord); - dbScanUnlock(precord); + while(1) { + onceEntry ent; + int bytes = epicsRingBytesGet(onceQ, (void*)&ent, sizeof(ent)); + if(bytes==0) + break; + if(bytes!=sizeof(ent)) { + errlogPrintf("onceTask: received incomplete %d of %u\n", + bytes, (unsigned)sizeof(ent)); + continue; /* what to do? */ + } else if (ent.prec == (void*)&exitOnce) goto shutdown; + + dbScanLock(ent.prec); + dbProcess(ent.prec); + dbScanUnlock(ent.prec); + if(ent.cb) + ent.cb(ent.usr, ent.prec); } } @@ -658,7 +681,7 @@ int scanOnceSetQueueSize(int size) static void initOnce(void) { - if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) { + if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) { cantProceed("initOnce: Ring buffer create failed\n"); } onceSem = epicsEventMustCreate(epicsEventEmpty); diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h index 8bd2bba22..fffae23d1 100644 --- a/src/ioc/db/dbScan.h +++ b/src/ioc/db/dbScan.h @@ -39,10 +39,11 @@ struct ioscan_head; typedef struct ioscan_head *IOSCANPVT; typedef struct event_list *EVENTPVT; -typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio); - struct dbCommon; +typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio); +typedef void (*once_complete)(void *usr, struct dbCommon*); + epicsShareFunc long scanInit(void); epicsShareFunc void scanRun(void); epicsShareFunc void scanPause(void); @@ -54,7 +55,8 @@ epicsShareFunc void post_event(int event) EPICS_DEPRECATED; epicsShareFunc void scanAdd(struct dbCommon *); epicsShareFunc void scanDelete(struct dbCommon *); epicsShareFunc double scanPeriod(int scan); -epicsShareFunc void scanOnce(struct dbCommon *); +epicsShareFunc int scanOnce(struct dbCommon *); +epicsShareFunc int scanOnce3(struct dbCommon *, once_complete cb, void *usr); epicsShareFunc int scanOnceSetQueueSize(int size); /*print periodic lists*/ From ff14d811a723c16f1e6950c7e085cce346d8fbc6 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:47 -0500 Subject: [PATCH 3/7] dbCaPvt.h: normalize indentation --- src/ioc/db/dbCaPvt.h | 100 +++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index cab596dba..584c53da3 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -38,56 +38,56 @@ typedef struct caLink { - ELLNODE node; - epicsMutexId lock; - struct link *plink; - char *pvname; - chid chid; - short link_action; - /* The following have new values after each data event*/ - epicsEnum16 sevr; - epicsEnum16 stat; - epicsTimeStamp timeStamp; - /* The following have values after connection*/ - short dbrType; - long nelements; - char hasReadAccess; - char hasWriteAccess; - char isConnected; - char gotFirstConnection; - /* The following are for dbCaAddLinkCallback */ - dbCaCallback connect; - dbCaCallback monitor; - void *userPvt; - /* The following are for write request */ - short putType; - dbCaCallback putCallback; - void *putUserPvt; - struct link *plinkPutCallback; - /* The following are for access to additional attributes*/ - char gotAttributes; - dbCaCallback getAttributes; - void *getAttributesPvt; - /* The following have values after getAttribEventCallback*/ - double controlLimits[2]; - double displayLimits[2]; - double alarmLimits[4]; - short precision; - char units[MAX_UNITS_SIZE]; /* units of value */ - /* The following are for handling data*/ - void *pgetNative; - char *pgetString; - void *pputNative; - char *pputString; - char gotInNative; - char gotInString; - char gotOutNative; - char gotOutString; - char newOutNative; - char newOutString; - /* The following are for dbcar*/ - unsigned long nDisconnect; - unsigned long nNoWrite; /*only modified by dbCaPutLink*/ + ELLNODE node; + epicsMutexId lock; + struct link *plink; + char *pvname; + chid chid; + short link_action; + /* The following have new values after each data event*/ + epicsEnum16 sevr; + epicsEnum16 stat; + epicsTimeStamp timeStamp; + /* The following have values after connection*/ + short dbrType; + long nelements; + char hasReadAccess; + char hasWriteAccess; + char isConnected; + char gotFirstConnection; + /* The following are for dbCaAddLinkCallback */ + dbCaCallback connect; + dbCaCallback monitor; + void *userPvt; + /* The following are for write request */ + short putType; + dbCaCallback putCallback; + void *putUserPvt; + struct link *plinkPutCallback; + /* The following are for access to additional attributes*/ + char gotAttributes; + dbCaCallback getAttributes; + void *getAttributesPvt; + /* The following have values after getAttribEventCallback*/ + double controlLimits[2]; + double displayLimits[2]; + double alarmLimits[4]; + short precision; + char units[MAX_UNITS_SIZE]; /* units of value */ + /* The following are for handling data*/ + void *pgetNative; + char *pgetString; + void *pputNative; + char *pputString; + char gotInNative; + char gotInString; + char gotOutNative; + char gotOutString; + char newOutNative; + char newOutString; + /* The following are for dbcar*/ + unsigned long nDisconnect; + unsigned long nNoWrite; /*only modified by dbCaPutLink*/ }caLink; #endif /* INC_dbCaPvt_H */ From d897c9b68616c11e3f431df1b9589059d61bb86c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:47 -0500 Subject: [PATCH 4/7] dbCa: use scanOnce3() to prevent once queue overflow Prevent CP links to high rate records from overflowing the once queue. --- src/ioc/db/dbCa.c | 31 ++++++++++++++++++++++++++++--- src/ioc/db/dbCaPvt.h | 1 + 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index e3ce5efac..2e1bf2ebf 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -629,6 +629,31 @@ long dbCaGetUnits(const struct link *plink, return gotAttributes ? 0 : -1; } +static void scanComplete(void *raw, dbCommon *prec) +{ + caLink *pca = raw; + epicsMutexMustLock(pca->lock); + if(pca->scanningOnce==0) + errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n"); + else if(--pca->scanningOnce){ + /* another scan is queued */ + if(scanOnce3(prec, scanComplete, raw)) { + errlogPrintf("dbCa.c failed to re-queue scanOnce\n"); + } + } + epicsMutexUnlock(pca->lock); +} + +/* must be called with pca->lock held */ +static void scanLinkOnce(dbCommon *prec, caLink *pca) { + if(pca->scanningOnce==0 && scanOnce3(prec, scanComplete, pca)) { + errlogPrintf("dbCa.c failed to queue scanOnce\n"); + } + if(pca->scanningOnce<5) + pca->scanningOnce++; + /* else too many scans queued */ +} + static void connectionCallback(struct connection_handler_args arg) { caLink *pca; @@ -649,7 +674,7 @@ static void connectionCallback(struct connection_handler_args arg) if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) - scanOnce(precord); + scanLinkOnce(precord, pca); goto done; } pca->hasReadAccess = ca_read_access(arg.chid); @@ -762,7 +787,7 @@ static void eventCallback(struct event_handler_args arg) if ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)) - scanOnce(precord); + scanLinkOnce(precord, pca); } done: epicsMutexUnlock(pca->lock); @@ -835,7 +860,7 @@ static void accessRightsCallback(struct access_rights_handler_args arg) if (precord && ((ppv_link->pvlMask & pvlOptCP) || ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) - scanOnce(precord); + scanLinkOnce(precord, pca); done: epicsMutexUnlock(pca->lock); } diff --git a/src/ioc/db/dbCaPvt.h b/src/ioc/db/dbCaPvt.h index 584c53da3..219da5a51 100644 --- a/src/ioc/db/dbCaPvt.h +++ b/src/ioc/db/dbCaPvt.h @@ -85,6 +85,7 @@ typedef struct caLink char gotOutString; char newOutNative; char newOutString; + unsigned char scanningOnce; /* The following are for dbcar*/ unsigned long nDisconnect; unsigned long nNoWrite; /*only modified by dbCaPutLink*/ From 6d7a0327e03289978b4708a8b7e2b557b5352959 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:47 -0500 Subject: [PATCH 5/7] dbScanTest: start with test of scanOnce3() --- src/ioc/db/test/Makefile | 6 +++ src/ioc/db/test/dbScanTest.c | 73 +++++++++++++++++++++++++++++++ src/ioc/db/test/epicsRunDbTests.c | 2 + 3 files changed, 81 insertions(+) create mode 100644 src/ioc/db/test/dbScanTest.c diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index ef0927c26..f1bdbf33e 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -28,6 +28,12 @@ testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp PROD_LIBS = dbTestIoc dbCore ca Com +TESTPROD_HOST += dbScanTest +dbScanTest_SRCS += dbScanTest.c +dbScanTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += dbScanTest.c +TESTS += dbScanTest + TESTPROD_HOST += dbShutdownTest dbShutdownTest_SRCS += dbShutdownTest.c dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp diff --git a/src/ioc/db/test/dbScanTest.c b/src/ioc/db/test/dbScanTest.c new file mode 100644 index 000000000..f9f1f8e44 --- /dev/null +++ b/src/ioc/db/test/dbScanTest.c @@ -0,0 +1,73 @@ +/*************************************************************************\ +* Copyright (c) 2015 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 + */ + +#include + +#include "dbScan.h" +#include "epicsEvent.h" + +#include "dbUnitTest.h" +#include "testMain.h" + +#include "dbAccess.h" +#include "errlog.h" + +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static epicsEventId waiter; +static int called; +static dbCommon *prec; + +static void onceComp(void *junk, dbCommon *prec) +{ + testOk1(junk==(void*)&waiter); + testOk1(strcmp(prec->name, "reca")==0); + called = 1; + epicsEventMustTrigger(waiter); +} + +static void testOnce(void) +{ + testDiag("check scanOnce3() callback"); + waiter = epicsEventMustCreate(epicsEventError); + + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + dbTestIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("dbLockTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + prec = testdbRecordPtr("reca"); + + testDiag("scanOnce %s", prec->name); + scanOnce3(prec, onceComp, &waiter); + testDiag("Waiting"); + epicsEventMustWait(waiter); + testOk1(called==1); + if(!called) + testSkip(2, "callback failed to run"); + + testIocShutdownOk(); + + testdbCleanup(); + epicsEventDestroy(waiter); +} + +MAIN(dbScanTest) +{ + testPlan(3); + testOnce(); + return testDone(); +} diff --git a/src/ioc/db/test/epicsRunDbTests.c b/src/ioc/db/test/epicsRunDbTests.c index 05fedeb51..0ee631686 100644 --- a/src/ioc/db/test/epicsRunDbTests.c +++ b/src/ioc/db/test/epicsRunDbTests.c @@ -22,6 +22,7 @@ int callbackParallelTest(void); int dbStateTest(void); int dbCaStatsTest(void); int dbShutdownTest(void); +int dbScanTest(void); int scanIoTest(void); int dbLockTest(void); int dbPutLinkTest(void); @@ -40,6 +41,7 @@ void epicsRunDbTests(void) runTest(dbStateTest); runTest(dbCaStatsTest); runTest(dbShutdownTest); + runTest(dbScanTest); runTest(scanIoTest); runTest(dbLockTest); runTest(dbPutLinkTest); From b9b8cde5f6aba8e47c068ca579bed7df78142ec1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 5 Jan 2015 16:26:48 -0500 Subject: [PATCH 6/7] dbScan: avoid mustAlloc in eventNameToHandle() Can already return NULL for other error conditions --- src/ioc/db/dbScan.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 3cd93da1a..9dfeec0bc 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -455,7 +455,9 @@ event_list *eventNameToHandle(const char *eventname) if (strcmp(pel->event_name, eventname) == 0) break; } if (pel == NULL) { - pel = dbCalloc(1, sizeof(event_list)); + pel = calloc(1, sizeof(event_list)); + if (!pel) + goto done; strcpy(pel->event_name, eventname); for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]); @@ -473,6 +475,7 @@ event_list *eventNameToHandle(const char *eventname) pevent_list[e] = pel; } } +done: epicsMutexUnlock(event_lock); return pel; } From bbbf05414da5bcc6ec9a3a897b329588003676db Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 17 Feb 2015 11:31:05 -0500 Subject: [PATCH 7/7] dbScan: rename scanOnce3 to scanOnceCallback --- src/ioc/db/dbCa.c | 4 ++-- src/ioc/db/dbScan.c | 4 ++-- src/ioc/db/dbScan.h | 2 +- src/ioc/db/test/dbScanTest.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 2e1bf2ebf..f6422873b 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -637,7 +637,7 @@ static void scanComplete(void *raw, dbCommon *prec) errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n"); else if(--pca->scanningOnce){ /* another scan is queued */ - if(scanOnce3(prec, scanComplete, raw)) { + if(scanOnceCallback(prec, scanComplete, raw)) { errlogPrintf("dbCa.c failed to re-queue scanOnce\n"); } } @@ -646,7 +646,7 @@ static void scanComplete(void *raw, dbCommon *prec) /* must be called with pca->lock held */ static void scanLinkOnce(dbCommon *prec, caLink *pca) { - if(pca->scanningOnce==0 && scanOnce3(prec, scanComplete, pca)) { + if(pca->scanningOnce==0 && scanOnceCallback(prec, scanComplete, pca)) { errlogPrintf("dbCa.c failed to queue scanOnce\n"); } if(pca->scanningOnce<5) diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 9dfeec0bc..e0f2b3603 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -612,7 +612,7 @@ void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg) } int scanOnce(struct dbCommon *precord) { - return scanOnce3(precord, NULL, NULL); + return scanOnceCallback(precord, NULL, NULL); } typedef struct { @@ -621,7 +621,7 @@ typedef struct { void *usr; } onceEntry; -int scanOnce3(struct dbCommon *precord, once_complete cb, void *usr) +int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr) { static int newOverflow = TRUE; onceEntry ent; diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h index fffae23d1..422864a0a 100644 --- a/src/ioc/db/dbScan.h +++ b/src/ioc/db/dbScan.h @@ -56,7 +56,7 @@ epicsShareFunc void scanAdd(struct dbCommon *); epicsShareFunc void scanDelete(struct dbCommon *); epicsShareFunc double scanPeriod(int scan); epicsShareFunc int scanOnce(struct dbCommon *); -epicsShareFunc int scanOnce3(struct dbCommon *, once_complete cb, void *usr); +epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr); epicsShareFunc int scanOnceSetQueueSize(int size); /*print periodic lists*/ diff --git a/src/ioc/db/test/dbScanTest.c b/src/ioc/db/test/dbScanTest.c index f9f1f8e44..0815827e3 100644 --- a/src/ioc/db/test/dbScanTest.c +++ b/src/ioc/db/test/dbScanTest.c @@ -36,7 +36,7 @@ static void onceComp(void *junk, dbCommon *prec) static void testOnce(void) { - testDiag("check scanOnce3() callback"); + testDiag("check scanOnceCallback() callback"); waiter = epicsEventMustCreate(epicsEventError); testdbPrepare(); @@ -52,7 +52,7 @@ static void testOnce(void) prec = testdbRecordPtr("reca"); testDiag("scanOnce %s", prec->name); - scanOnce3(prec, onceComp, &waiter); + scanOnceCallback(prec, onceComp, &waiter); testDiag("Waiting"); epicsEventMustWait(waiter); testOk1(called==1);