From a28a561d8a3e3eae25c2cfc33adaaf8c4ea7deb7 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:18 -0400 Subject: [PATCH 01/31] epicsExit: optional debug printing Add a flag to cause a string to be printed before each handler is run to show the order. --- src/ioc/misc/dbCore.dbd | 3 +++ src/libCom/misc/epicsExit.c | 25 ++++++++++++++++++++----- src/libCom/misc/epicsExit.h | 3 ++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/ioc/misc/dbCore.dbd b/src/ioc/misc/dbCore.dbd index fac0fd674..3b22f4663 100644 --- a/src/ioc/misc/dbCore.dbd +++ b/src/ioc/misc/dbCore.dbd @@ -5,6 +5,9 @@ # This file provides iocsh access to variables that control some lesser-used # and debugging features of the IOC database code. +# show epicsAtExit callbacks as they are run +variable(atExitDebug,int) + # Access security subroutines variable(asCaDebug,int) diff --git a/src/libCom/misc/epicsExit.c b/src/libCom/misc/epicsExit.c index 453378d36..6ae9dffd5 100644 --- a/src/libCom/misc/epicsExit.c +++ b/src/libCom/misc/epicsExit.c @@ -23,7 +23,9 @@ */ #include +#include #include +#include #define epicsExportSharedSymbols #include "ellLib.h" @@ -36,12 +38,15 @@ typedef struct exitNode { ELLNODE node; epicsExitFunc func; void *arg; + char name[1]; }exitNode; typedef struct exitPvt { ELLLIST list; } exitPvt; +int atExitDebug = 0; + static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT; static exitPvt * pExitPvtPerProcess = 0; static epicsMutexId exitPvtLock = 0; @@ -75,6 +80,10 @@ static void epicsExitCallAtExitsPvt(exitPvt *pep) { exitNode *pexitNode; while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) { + if (atExitDebug && pexitNode->name[0]) + fprintf(stderr, "atExit %s(%p)\n", pexitNode->name, pexitNode->arg); + else if(atExitDebug) + fprintf(stderr, "atExit %p(%p)\n", pexitNode->func, pexitNode->arg); pexitNode->func ( pexitNode->arg ); ellDelete ( & pep->list, & pexitNode->node ); free ( pexitNode ); @@ -109,14 +118,16 @@ epicsShareFunc void epicsExitCallAtThreadExits(void) } } -static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg) +static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const char *name) { int status = -1; - exitNode * pExitNode + exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) ); = calloc ( 1, sizeof( *pExitNode ) ); if ( pExitNode ) { pExitNode->func = func; pExitNode->arg = arg; + if(name) + strcpy(pExitNode->name, name); ellAdd ( & pep->list, & pExitNode->node ); status = 0; } @@ -135,16 +146,16 @@ epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg) } epicsThreadPrivateSet ( exitPvtPerThread, pep ); } - return epicsAtExitPvt ( pep, func, arg ); + return epicsAtExitPvt ( pep, func, arg, NULL ); } -epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg) +epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name) { int status = -1; epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); epicsMutexMustLock ( exitPvtLock ); if ( pExitPvtPerProcess ) { - status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg ); + status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg, name ); } epicsMutexUnlock ( exitPvtLock ); return status; @@ -156,3 +167,7 @@ epicsShareFunc void epicsExit(int status) epicsThreadSleep(1.0); exit(status); } + +#include "epicsExport.h" + +epicsExportAddress(int,atExitDebug); diff --git a/src/libCom/misc/epicsExit.h b/src/libCom/misc/epicsExit.h index 2fb33f129..e64316010 100644 --- a/src/libCom/misc/epicsExit.h +++ b/src/libCom/misc/epicsExit.h @@ -19,7 +19,8 @@ typedef void (*epicsExitFunc)(void *arg); epicsShareFunc void epicsExit(int status); epicsShareFunc void epicsExitCallAtExits(void); -epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg); +epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name); +#define epicsAtExit(F,A) epicsAtExit3(F,A,#F) epicsShareFunc void epicsExitCallAtThreadExits(void); epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg); From f405b444a3ae8f9c40f00a77c53b34f54b9f8e8e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:19 -0400 Subject: [PATCH 02/31] errlog: nicer exit handler name --- src/libCom/error/errlog.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libCom/error/errlog.c b/src/libCom/error/errlog.c index b2714d7a9..fbad162e1 100644 --- a/src/libCom/error/errlog.c +++ b/src/libCom/error/errlog.c @@ -41,7 +41,7 @@ /*Declare storage for errVerbose */ epicsShareDef int errVerbose = 0; -static void exitHandler(void *); +static void errlogExitHandler(void *); static void errlogThread(void); static char *msgbufGetFree(int noConsoleMessage); @@ -70,8 +70,8 @@ static struct { epicsEventId waitForFlush; /*errlogFlush waits for this*/ epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/ epicsMutexId flushLock; - epicsEventId waitForExit; /*exitHandler waits for this*/ - int atExit; /*TRUE when exitHandler is active*/ + epicsEventId waitForExit; /*errlogExitHandler waits for this*/ + int atExit; /*TRUE when errlogExitHandler is active*/ ELLLIST listenerList; ELLLIST msgQueue; msgNode *pnextSend; @@ -447,7 +447,7 @@ epicsShareFunc void errPrintf(long status, const char *pFileName, } -static void exitHandler(void *pvt) +static void errlogExitHandler(void *pvt) { pvtData.atExit = 1; epicsEventSignal(pvtData.waitForWork); @@ -561,7 +561,7 @@ static void errlogThread(void) int noConsoleMessage; char *pmessage; - epicsAtExit(exitHandler,0); + epicsAtExit(errlogExitHandler,0); while (TRUE) { epicsEventMustWait(pvtData.waitForWork); while ((pmessage = msgbufGetSend(&noConsoleMessage))) { From 8a42190e397722effbf4a82b6b322b1cd4c650cf Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:20 -0400 Subject: [PATCH 03/31] ensure the errlog is started with iocInit --- src/ioc/misc/iocInit.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index f13cf6bb3..a98bfea7f 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -94,6 +94,7 @@ int iocBuild(void) errlogPrintf("iocBuild: IOC can only be initialized once\n"); return -1; } + errlogInit(0); initHookAnnounce(initHookAtIocBuild); if (!epicsThreadIsOkToBlock()) { From 564a527489993c20f7fc4ecc245f616f23016a57 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:20 -0400 Subject: [PATCH 04/31] misc/iocInit: add iocBuildNoCA() and iocShutdown() to API, split up iocBuild in three phases --- src/ioc/misc/iocInit.c | 77 +++++++++++++++++++++++++++++++++++++----- src/ioc/misc/iocInit.h | 2 ++ 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index a98bfea7f..e7c9a26ab 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -3,6 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. +* Copyright (c) 2013 Helmholtz-Zentrum Berlin +* für Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ @@ -31,6 +33,7 @@ #include "errMdef.h" #include "taskwd.h" #include "caeventmask.h" +#include "iocsh.h" #define epicsExportSharedSymbols #include "alarm.h" @@ -88,10 +91,10 @@ int iocInit(void) return iocBuild() || iocRun(); } -int iocBuild(void) +static int iocBuild_1(void) { - if (iocState != iocVirgin) { - errlogPrintf("iocBuild: IOC can only be initialized once\n"); + if (iocState != iocVirgin && iocState != iocStopped) { + errlogPrintf("iocBuild: IOC can only be initialized from uninitialized or stopped state\n"); return -1; } errlogInit(0); @@ -110,14 +113,17 @@ int iocBuild(void) initHookAnnounce(initHookAtBeginning); coreRelease(); - /* After this point, further calls to iocInit() are disallowed. */ iocState = iocBuilding; taskwdInit(); callbackInit(); initHookAnnounce(initHookAfterCallbackInit); - dbCaLinkInit(); + return 0; +} + +static int iocBuild_2(void) +{ initHookAnnounce(initHookAfterCaLinkInit); initDrvSup(); @@ -148,9 +154,11 @@ int iocBuild(void) initialProcess(); initHookAnnounce(initHookAfterInitialProcess); + return 0; +} - /* Start CA server threads */ - rsrv_init(); +static int iocBuild_3(void) +{ initHookAnnounce(initHookAfterCaServerInit); iocState = iocBuilt; @@ -158,6 +166,39 @@ int iocBuild(void) return 0; } +int iocBuild(void) +{ + int status; + + status = iocBuild_1(); + if (status) return status; + + dbCaLinkInit(); + + status = iocBuild_2(); + if (status) return status; + + /* Start CA server threads */ + rsrv_init(); + + status = iocBuild_3(); + return status; +} + +int iocBuildNoCA(void) +{ + int status; + + status = iocBuild_1(); + if (status) return status; + + status = iocBuild_2(); + if (status) return status; + + status = iocBuild_3(); + return status; +} + int iocRun(void) { if (iocState != iocPaused && iocState != iocBuilt) { @@ -600,8 +641,26 @@ static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord, } } +static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord, + void *user) +{ + struct rset *prset = pdbRecordType->prset; + + if (!prset) return; /* unlikely */ + + epicsMutexDestroy(precord->mlok); +} + +int iocShutdown(void) +{ + if (iocState == iocVirgin || iocState == iocStopped) return 0; + iterateRecords(doCloseLinks, NULL); + iterateRecords(doFreeRecord, NULL); + iocState = iocStopped; + return 0; +} + static void exitDatabase(void *dummy) { - iterateRecords(doCloseLinks, NULL); - iocState = iocStopped; + iocShutdown(); } diff --git a/src/ioc/misc/iocInit.h b/src/ioc/misc/iocInit.h index 6a2f5682c..a5709f68e 100644 --- a/src/ioc/misc/iocInit.h +++ b/src/ioc/misc/iocInit.h @@ -19,8 +19,10 @@ extern "C" { epicsShareFunc int iocInit(void); epicsShareFunc int iocBuild(void); +epicsShareFunc int iocBuildNoCA(void); epicsShareFunc int iocRun(void); epicsShareFunc int iocPause(void); +epicsShareFunc int iocShutdown(void); #ifdef __cplusplus } From 23e22313f16adaa0cca999714dedc6b6c5defcc5 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Mon, 23 Jun 2014 16:28:21 -0400 Subject: [PATCH 05/31] db/dbScan: add scanShutdown() to API, complete shutdown handling --- src/ioc/db/dbScan.c | 43 ++++++++++++++++++++++++++++++++++++++---- src/ioc/db/dbScan.h | 1 + src/ioc/misc/iocInit.c | 1 + 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index 10dc9c146..a1ba12d9a 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -3,6 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. +* Copyright (c) 2013 Helmholtz-Zentrum Berlin +* für Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ @@ -132,6 +134,7 @@ static void onceTask(void *); static void initOnce(void); static void periodicTask(void *arg); static void initPeriodic(void); +static void deletePeriodic(void); static void spawnPeriodic(int ind); static void initEvent(void); static void eventCallback(CALLBACK *pcallback); @@ -142,10 +145,13 @@ static void buildScanLists(void); static void addToList(struct dbCommon *precord, scan_list *psl); static void deleteFromList(struct dbCommon *precord, scan_list *psl); -static void scanShutdown(void *arg) +void scanShutdown(void) { int i; + if (scanCtl == ctlExit) return; + scanCtl = ctlExit; + interruptAccept = FALSE; for (i = 0; i < nPeriodic; i++) { @@ -156,6 +162,18 @@ static void scanShutdown(void *arg) scanOnce((dbCommon *)&exitOnce); epicsEventWait(startStopEvent); + + deletePeriodic(); + + epicsRingPointerDelete(onceQ); + + epicsEventDestroy(startStopEvent); + epicsEventDestroy(onceSem); + onceSem = startStopEvent = NULL; + + free(periodicTaskId); + papPeriodic = NULL; + periodicTaskId = NULL; } long scanInit(void) @@ -172,7 +190,6 @@ long scanInit(void) for (i = 0; i < nPeriodic; i++) spawnPeriodic(i); - epicsAtExit(scanShutdown, NULL); return 0; } @@ -696,6 +713,22 @@ static void initPeriodic(void) } } +static void deletePeriodic(void) +{ + int i; + + for (i = 0; i < nPeriodic; i++) { + periodic_scan_list *ppsl = papPeriodic[i]; + ellFree(&ppsl->scan_list.list); + epicsEventDestroy(ppsl->loopEvent); + epicsMutexDestroy(ppsl->scan_list.lock); + free(ppsl); + } + + free(papPeriodic); + papPeriodic = NULL; +} + static void spawnPeriodic(int ind) { periodic_scan_list *ppsl; @@ -802,23 +835,25 @@ static void buildScanLists(void) { dbRecordType *pdbRecordType; - /*Look for first record*/ for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { dbRecordNode *pdbRecordNode; + for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); pdbRecordNode; pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { dbCommon *precord = pdbRecordNode->precord; + if (!precord->name[0] || pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) continue; + scanAdd(precord); } } } - + static void addToList(struct dbCommon *precord, scan_list *psl) { scan_element *pse, *ptemp; diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h index eca2c889d..03117f769 100644 --- a/src/ioc/db/dbScan.h +++ b/src/ioc/db/dbScan.h @@ -44,6 +44,7 @@ struct dbCommon; epicsShareFunc long scanInit(void); epicsShareFunc void scanRun(void); epicsShareFunc void scanPause(void); +epicsShareFunc void scanShutdown(void); epicsShareFunc EVENTPVT eventNameToHandle(const char* event); epicsShareFunc void postEvent(EVENTPVT epvt); diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index e7c9a26ab..988b8410c 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -655,6 +655,7 @@ int iocShutdown(void) { if (iocState == iocVirgin || iocState == iocStopped) return 0; iterateRecords(doCloseLinks, NULL); + scanShutdown(); iterateRecords(doFreeRecord, NULL); iocState = iocStopped; return 0; From e83bc13a5ecbb197dcce942fe9bc6ace84331ed3 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Mon, 23 Jun 2014 16:28:21 -0400 Subject: [PATCH 06/31] db/dbCa: add dbCaShutdown() to API, complete shutdown handling --- src/ioc/db/dbCa.c | 25 ++++++++++++++++++------- src/ioc/db/dbCa.h | 1 + 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index e329b4323..c64f5713f 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -173,15 +173,22 @@ void dbCaCallbackProcess(void *usrPvt) dbScanUnlock(pdbCommon); } -static void dbCaShutdown(void *arg) +void dbCaShutdown(void) { - if (dbCaCtl == ctlRun) { + if (dbCaCtl == ctlRun || dbCaCtl == ctlPause) { dbCaCtl = ctlExit; epicsEventSignal(workListEvent); epicsEventMustWait(startStopEvent); + epicsEventDestroy(startStopEvent); + epicsEventDestroy(workListEvent); } } +static void dbCaExit(void *arg) +{ + dbCaShutdown(); +} + void dbCaLinkInit(void) { dbServiceIOInit(); @@ -194,19 +201,23 @@ void dbCaLinkInit(void) epicsThreadGetStackSize(epicsThreadStackBig), dbCaTask, NULL); epicsEventMustWait(startStopEvent); - epicsAtExit(dbCaShutdown, NULL); + epicsAtExit(dbCaExit, NULL); } void dbCaRun(void) { - dbCaCtl = ctlRun; - epicsEventSignal(workListEvent); + if (dbCaCtl == ctlPause) { + dbCaCtl = ctlRun; + epicsEventSignal(workListEvent); + } } void dbCaPause(void) { - dbCaCtl = ctlPause; - epicsEventSignal(workListEvent); + if (dbCaCtl == ctlRun) { + dbCaCtl = ctlPause; + epicsEventSignal(workListEvent); + } } void dbCaAddLinkCallback(struct link *plink, diff --git a/src/ioc/db/dbCa.h b/src/ioc/db/dbCa.h index bf6f2edd9..a147307bf 100644 --- a/src/ioc/db/dbCa.h +++ b/src/ioc/db/dbCa.h @@ -26,6 +26,7 @@ epicsShareFunc void dbCaCallbackProcess(void *usrPvt); epicsShareFunc void dbCaLinkInit(void); epicsShareFunc void dbCaRun(void); epicsShareFunc void dbCaPause(void); +epicsShareFunc void dbCaShutdown(void); epicsShareFunc void dbCaAddLinkCallback(struct link *plink, dbCaCallback connect, dbCaCallback monitor, void *userPvt); From 2d4e143987a2205e6748ad1f22f82b2527a5969f Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Mon, 23 Jun 2014 16:28:21 -0400 Subject: [PATCH 07/31] db/callback: add callbackShutdown() to API, add shutdown handling --- src/ioc/db/callback.c | 27 +++++++++++++++++---------- src/ioc/db/callback.h | 1 + src/ioc/misc/iocInit.c | 1 + 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/ioc/db/callback.c b/src/ioc/db/callback.c index 4e364b181..bd20304a3 100644 --- a/src/ioc/db/callback.c +++ b/src/ioc/db/callback.c @@ -43,7 +43,6 @@ #include "callback.h" -static epicsThreadOnceId callbackOnceFlag = EPICS_THREAD_ONCE_INIT; static int callbackQueueSize = 2000; static epicsEventId callbackSem[NUM_CALLBACK_PRIORITIES]; static epicsRingPointerId callbackQ[NUM_CALLBACK_PRIORITIES]; @@ -53,6 +52,8 @@ static volatile int ringOverflow[NUM_CALLBACK_PRIORITIES]; static epicsTimerQueueId timerQueue; /* Shutdown handling */ +enum ctl {ctlInit, ctlRun, ctlPause, ctlExit}; +static volatile enum ctl cbCtl; static epicsEventId startStopEvent; static void *exitCallback; @@ -70,7 +71,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2}; int callbackSetQueueSize(int size) { - if (callbackOnceFlag != EPICS_THREAD_ONCE_INIT) { + if (startStopEvent) { errlogPrintf("Callback system already initialized\n"); return -1; } @@ -101,24 +102,36 @@ shutdown: epicsEventSignal(startStopEvent); } -static void callbackShutdown(void *arg) +void callbackShutdown(void) { int i; + if (cbCtl == ctlExit) return; + cbCtl = ctlExit; + for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { int lockKey = epicsInterruptLock(); int ok = epicsRingPointerPush(callbackQ[i], &exitCallback); epicsInterruptUnlock(lockKey); epicsEventSignal(callbackSem[i]); if (ok) epicsEventWait(startStopEvent); + epicsEventDestroy(callbackSem[i]); + epicsRingPointerDelete(callbackQ[i]); } + epicsTimerQueueRelease(timerQueue); + epicsEventDestroy(startStopEvent); + startStopEvent = NULL; } -static void callbackInitOnce(void *arg) +void callbackInit(void) { int i; + if(startStopEvent) + return; + startStopEvent = epicsEventMustCreate(epicsEventEmpty); + cbCtl = ctlRun; timerQueue = epicsTimerQueueAllocate(0,epicsThreadPriorityScanHigh); for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { epicsThreadId tid; @@ -137,12 +150,6 @@ static void callbackInitOnce(void *arg) else epicsEventWait(startStopEvent); } - epicsAtExit(callbackShutdown, NULL); -} - -void callbackInit(void) -{ - epicsThreadOnce(&callbackOnceFlag, callbackInitOnce, NULL); } /* This routine can be called from interrupt context */ diff --git a/src/ioc/db/callback.h b/src/ioc/db/callback.h index 1b539c13e..6ce0b1adb 100644 --- a/src/ioc/db/callback.h +++ b/src/ioc/db/callback.h @@ -57,6 +57,7 @@ typedef void (*CALLBACKFUNC)(struct callbackPvt*); epicsShareFunc void callbackInit(void); epicsShareFunc void callbackRequest(CALLBACK *pCallback); +epicsShareFunc void callbackShutdown(void); epicsShareFunc void callbackSetProcess( CALLBACK *pcallback, int Priority, void *pRec); epicsShareFunc void callbackRequestProcessCallback( diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 988b8410c..da32bf67c 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -656,6 +656,7 @@ int iocShutdown(void) if (iocState == iocVirgin || iocState == iocStopped) return 0; iterateRecords(doCloseLinks, NULL); scanShutdown(); + callbackShutdown(); iterateRecords(doFreeRecord, NULL); iocState = iocStopped; return 0; From ad2bb0725db6eae63fb255883294533e6c78abca Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:22 -0400 Subject: [PATCH 08/31] asShutdown --- src/ioc/as/asDbLib.c | 9 +++++++++ src/ioc/as/asDbLib.h | 1 + src/ioc/misc/iocInit.c | 1 + src/libCom/as/asLib.h | 1 + src/libCom/as/asLibRoutines.c | 3 +-- 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ioc/as/asDbLib.c b/src/ioc/as/asDbLib.c index 4c295018c..d0322bfd1 100644 --- a/src/ioc/as/asDbLib.c +++ b/src/ioc/as/asDbLib.c @@ -152,6 +152,15 @@ int asInit(void) return(asInitCommon()); } +int asShutdown(void) { + volatile ASBASE *pbase = pasbase; + pasbase = NULL; + firstTime = TRUE; + if(pbase) + asFreeAll((ASBASE*)pbase); + return 0; +} + static void wdCallback(void *arg) { ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg; diff --git a/src/ioc/as/asDbLib.h b/src/ioc/as/asDbLib.h index 04e35611f..b0c3516f6 100644 --- a/src/ioc/as/asDbLib.h +++ b/src/ioc/as/asDbLib.h @@ -32,6 +32,7 @@ epicsShareFunc int asSetFilename(const char *acf); epicsShareFunc int asSetSubstitutions(const char *substitutions); epicsShareFunc int asInit(void); epicsShareFunc int asInitAsyn(ASDBCALLBACK *pcallback); +epicsShareFunc int asShutdown(void); epicsShareFunc int asDbGetAsl(struct dbChannel *chan); epicsShareFunc void * asDbGetMemberPvt(struct dbChannel *chan); epicsShareFunc int asdbdump(void); diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index da32bf67c..0268801cb 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -658,6 +658,7 @@ int iocShutdown(void) scanShutdown(); callbackShutdown(); iterateRecords(doFreeRecord, NULL); + asShutdown(); iocState = iocStopped; return 0; } diff --git a/src/libCom/as/asLib.h b/src/libCom/as/asLib.h index b3da2f836..af8e0dc30 100644 --- a/src/libCom/as/asLib.h +++ b/src/libCom/as/asLib.h @@ -222,6 +222,7 @@ epicsShareFunc long epicsShareAPI asComputeAsg(ASG *pasg); /*following is "friend" function*/ epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size); epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str); +epicsShareFunc void asFreeAll(ASBASE *pasbase); #ifdef __cplusplus } #endif diff --git a/src/libCom/as/asLibRoutines.c b/src/libCom/as/asLibRoutines.c index 83f6629e6..c8046ebce 100644 --- a/src/libCom/as/asLibRoutines.c +++ b/src/libCom/as/asLibRoutines.c @@ -51,7 +51,6 @@ static long asAddMemberPvt(ASMEMBERPVT *pasMemberPvt,const char *asgName); static long asComputeAllAsgPvt(void); static long asComputeAsgPvt(ASG *pasg); static long asComputePvt(ASCLIENTPVT asClientPvt); -static void asFreeAll(ASBASE *pasbase); static UAG *asUagAdd(const char *uagName); static long asUagAddUser(UAG *puag,const char *user); static HAG *asHagAdd(const char *hagName); @@ -1015,7 +1014,7 @@ next_rule: return(0); } -static void asFreeAll(ASBASE *pasbase) +void asFreeAll(ASBASE *pasbase) { UAG *puag; UAGNAME *puagname; From 3ca42fc838eb6994e41ab2ed40690ad080efa708 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:22 -0400 Subject: [PATCH 09/31] cleanup iocsh --- src/ioc/misc/iocInit.c | 1 + src/libCom/iocsh/iocsh.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 0268801cb..774846848 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -659,6 +659,7 @@ int iocShutdown(void) callbackShutdown(); iterateRecords(doFreeRecord, NULL); asShutdown(); + iocshFree(); iocState = iocStopped; return 0; } diff --git a/src/libCom/iocsh/iocsh.cpp b/src/libCom/iocsh/iocsh.cpp index 81727696b..671239781 100644 --- a/src/libCom/iocsh/iocsh.cpp +++ b/src/libCom/iocsh/iocsh.cpp @@ -203,20 +203,22 @@ void epicsShareAPI iocshRegisterVariable (const iocshVarDef *piocshVarDef) */ void epicsShareAPI iocshFree(void) { - struct iocshCommand *pc, *nc; - struct iocshVariable *pv, *nv; + struct iocshCommand *pc; + struct iocshVariable *pv; iocshTableLock (); for (pc = iocshCommandHead ; pc != NULL ; ) { - nc = pc->next; + struct iocshCommand * nc = pc->next; free (pc); pc = nc; } for (pv = iocshVariableHead ; pv != NULL ; ) { - nv = pv->next; + struct iocshVariable *nv = pv->next; free (pv); pv = nv; } + iocshCommandHead = NULL; + iocshVariableHead = NULL; iocshTableUnlock (); } From 5caaf2d13c88e5bcdeaf0fca64d583978d3879f4 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:23 -0400 Subject: [PATCH 10/31] cleanup initHook --- src/ioc/db/initHooks.c | 8 ++++++++ src/ioc/db/initHooks.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/ioc/db/initHooks.c b/src/ioc/db/initHooks.c index 5dc7f88e7..a6803f56b 100644 --- a/src/ioc/db/initHooks.c +++ b/src/ioc/db/initHooks.c @@ -93,6 +93,14 @@ void initHookAnnounce(initHookState state) } } +void initHookFree(void) +{ + initHookInit(); + epicsMutexMustLock(listLock); + ellFree(&functionList); + epicsMutexUnlock(listLock); +} + /* * Call any time you want to print out a state name. */ diff --git a/src/ioc/db/initHooks.h b/src/ioc/db/initHooks.h index f8606e314..c4c84b7b9 100644 --- a/src/ioc/db/initHooks.h +++ b/src/ioc/db/initHooks.h @@ -60,6 +60,7 @@ typedef void (*initHookFunction)(initHookState state); epicsShareFunc int initHookRegister(initHookFunction func); epicsShareFunc void initHookAnnounce(initHookState state); epicsShareFunc const char *initHookName(int state); +epicsShareFunc void initHookFree(void); #ifdef __cplusplus } From 1ae2ab1de6bfacf339e4dc310f1d80c2741956c3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:24 -0400 Subject: [PATCH 11/31] dbShutdownTest --- src/Makefile | 2 +- src/ioc/db/test/Makefile | 9 +++ src/ioc/db/test/dbShutdownTest.c | 98 ++++++++++++++++++++++++++++++++ src/ioc/db/test/sRecord.db | 1 + 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/ioc/db/test/dbShutdownTest.c create mode 100644 src/ioc/db/test/sRecord.db diff --git a/src/Makefile b/src/Makefile index 3307b8786..8205a427b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -62,7 +62,7 @@ DIRS += ioc ioc_DEPEND_DIRS = libCom ca/client DIRS += ioc/db/test -ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS +ioc/db/test_DEPEND_DIRS = ioc std libCom/RTEMS DIRS += ioc/dbtemplate/test ioc/dbtemplate/test_DEPEND_DIRS = ioc diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 2f428aa5b..dd149c8a0 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -16,6 +16,15 @@ xRec_SRCS = xRecord.c PROD_LIBS = xRec dbCore ca Com +TESTPROD_HOST += dbShutdownTest +TARGETS += $(COMMON_DIR)/dbShutdownTest.dbd +dbShutdownTest_SRCS += dbShutdownTest.c +dbShutdownTest_SRCS += dbShutdownTest_registerRecordDeviceDriver.cpp +dbShutdownTest_LIBS += $(EPICS_BASE_IOC_LIBS) +dbShutdownTest_DBD += base.dbd +TESTFILES += $(COMMON_DIR)/dbShutdownTest.dbd ../sRecord.db +TESTS += dbShutdownTest + TESTPROD_HOST += callbackTest callbackTest_SRCS += callbackTest.c testHarness_SRCS += callbackTest.c diff --git a/src/ioc/db/test/dbShutdownTest.c b/src/ioc/db/test/dbShutdownTest.c new file mode 100644 index 000000000..7d9b3862a --- /dev/null +++ b/src/ioc/db/test/dbShutdownTest.c @@ -0,0 +1,98 @@ +/*************************************************************************\ +* Copyright (c) 2013 Brookhaven National Laboratory. +* Copyright (c) 2013 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Michael Davidsaver + * Ralph Lange + */ + +#include + +#include "epicsUnitTest.h" +#include "epicsThread.h" +#include "iocInit.h" +#include "dbBase.h" +#include "dbAccess.h" +#include "registry.h" +#include "dbStaticLib.h" +#include "osiFileName.h" +#include "dbmf.h" + +#include "testMain.h" + +void dbShutdownTest_registerRecordDeviceDriver(struct dbBase *); + +static struct threadItem { + char *name; + char found; +} commonThreads[] = { + { "errlog", 0 }, + { "taskwd", 0 }, + { "timerQueue", 0 }, + { "cbLow", 0 }, + { "scanOnce", 0 }, + { NULL, 0 } +}; + +static +void findCommonThread (epicsThreadId id) { + struct threadItem *thr; + char name[32]; + + epicsThreadGetName(id, name, 32); + + for (thr = commonThreads; thr->name; thr++) { + if (strcasecmp(thr->name, name) == 0) { + thr->found = 1; + } + } +} + +static +void checkCommonThreads (void) { + struct threadItem *thr; + + for (thr = commonThreads; thr->name; thr++) { + testOk(thr->found, "Thread %s is running", thr->name); + thr->found = 0; + } +} + +static +void cycle(void) { + if (dbReadDatabase(&pdbbase, "dbShutdownTest.dbd", + "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR + "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) + testAbort("Database description 'dbShutdownTest.dbd' not found"); + + dbShutdownTest_registerRecordDeviceDriver(pdbbase); + if (dbReadDatabase(&pdbbase, "sRecord.db", + "." OSI_PATH_LIST_SEPARATOR "..", NULL)) + testAbort("Test database 'sRecord.db' not found"); + + testOk1(!(iocBuildNoCA() || iocRun())); + + epicsThreadMap(findCommonThread); + checkCommonThreads(); + + testOk1(iocShutdown()==0); + + dbFreeBase(pdbbase); + registryFree(); + pdbbase = NULL; + dbmfFreeChunks(); +} + +MAIN(dbShutdownTest) +{ + testPlan(14); + + cycle(); + cycle(); + + return testDone(); +} diff --git a/src/ioc/db/test/sRecord.db b/src/ioc/db/test/sRecord.db new file mode 100644 index 000000000..790a7df7c --- /dev/null +++ b/src/ioc/db/test/sRecord.db @@ -0,0 +1 @@ +record(ai, "somename") {} From 80dd66a58da109d1b8cdb0a48df882c2fd6c1be1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:25 -0400 Subject: [PATCH 12/31] add dbUnitTest.h hide boilerplate of tests using the PDB --- src/ioc/db/Makefile | 3 +- src/ioc/db/dbUnitTest.c | 123 +++++++++++++++++++++++++++++++ src/ioc/db/dbUnitTest.h | 54 ++++++++++++++ src/ioc/db/test/dbShutdownTest.c | 24 +++--- 4 files changed, 189 insertions(+), 15 deletions(-) create mode 100644 src/ioc/db/dbUnitTest.c create mode 100644 src/ioc/db/dbUnitTest.h diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index c957842cb..8788207fe 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -37,6 +37,7 @@ INC += chfPlugin.h INC += dbState.h INC += db_access_routines.h INC += db_convert.h +INC += dbUnitTest.h # Generate menuGlobal.dbd automatically DBD += menuGlobal.dbd @@ -86,4 +87,4 @@ dbCore_SRCS += templateInstances.cpp dbCore_SRCS += dbIocRegister.c dbCore_SRCS += chfPlugin.c dbCore_SRCS += dbState.c - +dbCore_SRCS += dbUnitTest.c diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c new file mode 100644 index 000000000..1a7f74dde --- /dev/null +++ b/src/ioc/db/dbUnitTest.c @@ -0,0 +1,123 @@ +/*************************************************************************\ +* Copyright (c) 2013 Brookhaven National Laboratory. +* Copyright (c) 2013 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Michael Davidsaver + * Ralph Lange + */ + +#include + +#include "epicsUnitTest.h" +#include "osiFileName.h" +#include "dbmf.h" +#include "registry.h" +#define epicsExportSharedSymbols +#include "iocInit.h" +#include "initHooks.h" +#include "dbBase.h" +#include "dbAccess.h" +#include "dbStaticLib.h" + +#include "dbUnitTest.h" + +void testdbPrepare(void) +{ + /* No-op at the moment */ +} + +void testdbReadDatabase(const char* file, + const char* path, + const char* substitutions) +{ + if(!path) + path = "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR + "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common"; + if(dbReadDatabase(&pdbbase, file, path, substitutions)) + testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)", + file, path, substitutions); +} + +int testiocInit(void) +{ + return iocBuildNoCA() || iocRun(); +} + +int testiocShutdown(void) +{ + return iocShutdown(); +} + +void testdbCleanup(void) +{ + dbFreeBase(pdbbase); + initHookFree(); + registryFree(); + pdbbase = NULL; + dbmfFreeChunks(); +} + +long testdbPutField(const char* pv, short dbrType, ...) +{ + long ret; + va_list ap; + va_start(ap, dbrType); + ret = testVdbPutField(pv, dbrType, ap); + va_end(ap); + return ret; +} + +union anybuf { + epicsAny val; + char bytes[sizeof(epicsAny)]; +}; + +long testVdbPutField(const char* pv, short dbrType, va_list ap) +{ + DBADDR addr; + union anybuf pod; + + if(dbNameToAddr(pv, &addr)) + testAbort("Missing PV %s", pv); + + switch(dbrType) { + case DBR_STRING: { + const char *uarg = va_arg(ap,char*); + epicsOldString buffer; + strncpy(buffer, uarg, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + return dbPutField(&addr, dbrType, buffer, 1); + } + +#define OP(DBR,Type,mem) case DBR: {pod.val.mem = va_arg(ap,Type); break;} + OP(DBR_CHAR, int, int8); + OP(DBR_UCHAR, int, uInt8); + OP(DBR_SHORT, int, int16); + OP(DBR_USHORT, int, uInt16); + OP(DBR_LONG, int, int32); + OP(DBR_ULONG, unsigned int, uInt32); + OP(DBR_FLOAT, double, float32); + OP(DBR_DOUBLE, double, float64); + OP(DBR_ENUM, int, enum16); +#undef OP + default: + testAbort("invalid DBR: dbPutField(%s, %d, ...)", + addr.precord->name, dbrType); + } + + return dbPutField(&addr, dbrType, pod.bytes, 1); +} + +dbCommon* testGetRecord(const char* pv) +{ + DBADDR addr; + + if(dbNameToAddr(pv, &addr)) + testAbort("Missing PV %s", pv); + + return addr.precord; +} diff --git a/src/ioc/db/dbUnitTest.h b/src/ioc/db/dbUnitTest.h new file mode 100644 index 000000000..9b58673ed --- /dev/null +++ b/src/ioc/db/dbUnitTest.h @@ -0,0 +1,54 @@ +/*************************************************************************\ +* Copyright (c) 2013 Brookhaven National Laboratory. +* Copyright (c) 2013 ITER Organization. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Michael Davidsaver + * Ralph Lange + */ + +#ifndef EPICSUNITTESTDB_H +#define EPICSUNITTESTDB_H + +#include + +#include "epicsUnitTest.h" +#include "dbAddr.h" +#include "dbCommon.h" + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void testdbPrepare(void); +epicsShareFunc void testdbReadDatabase(const char* file, + const char* path, + const char* substitutions); +epicsShareFunc int testiocInit(void); +epicsShareFunc int testiocShutdown(void); +epicsShareFunc void testdbCleanup(void); + +/* Scalar only version. + * + * Remember to use the correct argument type!s + * + * int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG + * unsigned int for DBR_ULONG + * double for DBR_FLOAT and DBR_DOUBLE + * const char* for DBR_STRING + */ +epicsShareFunc long testdbPutField(const char* pv, short dbrType, ...); +epicsShareFunc long testVdbPutField(const char* pv, short dbrType, va_list ap); + +epicsShareFunc dbCommon* testGetRecord(const char* pv); + +#ifdef __cplusplus +} +#endif + +#endif // EPICSUNITTESTDB_H diff --git a/src/ioc/db/test/dbShutdownTest.c b/src/ioc/db/test/dbShutdownTest.c index 7d9b3862a..d3a993b6e 100644 --- a/src/ioc/db/test/dbShutdownTest.c +++ b/src/ioc/db/test/dbShutdownTest.c @@ -12,7 +12,7 @@ #include -#include "epicsUnitTest.h" +#include "dbUnitTest.h" #include "epicsThread.h" #include "iocInit.h" #include "dbBase.h" @@ -64,27 +64,23 @@ void checkCommonThreads (void) { static void cycle(void) { - if (dbReadDatabase(&pdbbase, "dbShutdownTest.dbd", - "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR - "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) - testAbort("Database description 'dbShutdownTest.dbd' not found"); + + testdbPrepare(); + + testdbReadDatabase("dbShutdownTest.dbd", NULL, NULL); dbShutdownTest_registerRecordDeviceDriver(pdbbase); - if (dbReadDatabase(&pdbbase, "sRecord.db", - "." OSI_PATH_LIST_SEPARATOR "..", NULL)) - testAbort("Test database 'sRecord.db' not found"); - testOk1(!(iocBuildNoCA() || iocRun())); + testdbReadDatabase("sRecord.db", NULL, NULL); + + testOk1(!testiocInit()); epicsThreadMap(findCommonThread); checkCommonThreads(); - testOk1(iocShutdown()==0); + testOk1(testiocShutdown()==0); - dbFreeBase(pdbbase); - registryFree(); - pdbbase = NULL; - dbmfFreeChunks(); + testdbCleanup(); } MAIN(dbShutdownTest) From 60a0c7f181192f1652347685b7f7d2bd541011aa Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 23 Jun 2014 16:28:25 -0400 Subject: [PATCH 13/31] Make epicsExit subsystem reusable. Calling epicsAtExit() after epicsExitCallAtExits() now recreates the per-process list and registers the routine. --- src/libCom/misc/epicsExit.c | 27 +++++++++++++++++++-------- src/libCom/test/epicsExitTest.c | 25 ++++++++++++++++++++----- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/src/libCom/misc/epicsExit.c b/src/libCom/misc/epicsExit.c index 6ae9dffd5..0fbed224b 100644 --- a/src/libCom/misc/epicsExit.c +++ b/src/libCom/misc/epicsExit.c @@ -71,14 +71,18 @@ static void exitPvtOnceFunc(void *pParm) { exitPvtPerThread = epicsThreadPrivateCreate (); assert ( exitPvtPerThread ); - pExitPvtPerProcess = createExitPvt (); - assert ( pExitPvtPerProcess ); exitPvtLock = epicsMutexMustCreate (); } +static void epicsExitInit(void) +{ + epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); +} + static void epicsExitCallAtExitsPvt(exitPvt *pep) { exitNode *pexitNode; + while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) { if (atExitDebug && pexitNode->name[0]) fprintf(stderr, "atExit %s(%p)\n", pexitNode->name, pexitNode->arg); @@ -93,7 +97,8 @@ static void epicsExitCallAtExitsPvt(exitPvt *pep) epicsShareFunc void epicsExitCallAtExits(void) { exitPvt * pep = 0; - epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); + + epicsExitInit (); epicsMutexMustLock ( exitPvtLock ); if ( pExitPvtPerProcess ) { pep = pExitPvtPerProcess; @@ -109,7 +114,8 @@ epicsShareFunc void epicsExitCallAtExits(void) epicsShareFunc void epicsExitCallAtThreadExits(void) { exitPvt * pep; - epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); + + epicsExitInit (); pep = epicsThreadPrivateGet ( exitPvtPerThread ); if ( pep ) { epicsExitCallAtExitsPvt ( pep ); @@ -122,7 +128,7 @@ static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const cha { int status = -1; exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) ); - = calloc ( 1, sizeof( *pExitNode ) ); + if ( pExitNode ) { pExitNode->func = func; pExitNode->arg = arg; @@ -137,7 +143,8 @@ static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const cha epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg) { exitPvt * pep; - epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); + + epicsExitInit (); pep = epicsThreadPrivateGet ( exitPvtPerThread ); if ( ! pep ) { pep = createExitPvt (); @@ -152,8 +159,12 @@ epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg) epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name) { int status = -1; - epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); + + epicsExitInit (); epicsMutexMustLock ( exitPvtLock ); + if ( !pExitPvtPerProcess ) { + pExitPvtPerProcess = createExitPvt (); + } if ( pExitPvtPerProcess ) { status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg, name ); } @@ -164,7 +175,7 @@ epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name) epicsShareFunc void epicsExit(int status) { epicsExitCallAtExits(); - epicsThreadSleep(1.0); + epicsThreadSleep(0.1); exit(status); } diff --git a/src/libCom/test/epicsExitTest.c b/src/libCom/test/epicsExitTest.c index f464b707c..dc901b5ac 100644 --- a/src/libCom/test/epicsExitTest.c +++ b/src/libCom/test/epicsExitTest.c @@ -59,12 +59,20 @@ static void thread(void *arg) testDiag("%s starting", pinfo->name); pinfo->terminate = epicsEventMustCreate(epicsEventEmpty); pinfo->terminated = epicsEventMustCreate(epicsEventEmpty); - epicsAtExit(atExit, pinfo); - epicsAtThreadExit(atThreadExit, pinfo); + testOk(!epicsAtExit(atExit, pinfo), "Registered atExit(%p)", pinfo); + testOk(!epicsAtThreadExit(atThreadExit, pinfo), + "Registered atThreadExit(%p)", pinfo); testDiag("%s waiting for atExit", pinfo->name); epicsEventMustWait(pinfo->terminate); } +int count; + +static void counter(void *pvt) +{ + count++; +} + static void mainExit(void *pvt) { testPass("Reached mainExit"); @@ -77,16 +85,23 @@ MAIN(epicsExitTest) info *pinfoA = (info *)calloc(1, sizeof(info)); info *pinfoB = (info *)calloc(1, sizeof(info)); - testPlan(7); + testPlan(15); - epicsAtExit(mainExit, NULL); + testOk(!epicsAtExit(counter, NULL), "Registered counter()"); + count = 0; + epicsExitCallAtExits(); + testOk(count == 1, "counter() called once"); + epicsExitCallAtExits(); + testOk(count == 1, "unregistered counter() not called"); + + testOk(!epicsAtExit(mainExit, NULL), "Registered mainExit()"); epicsThreadCreate("threadA", 50, stackSize, thread, pinfoA); epicsThreadSleep(0.1); epicsThreadCreate("threadB", 50, stackSize, thread, pinfoB); epicsThreadSleep(1.0); - testDiag("Calling epicsExit\n"); + testDiag("Calling epicsExit"); epicsExit(0); return 0; } From ddaa6e4eb6647545db7a43c9b83ca7e2c497f3b8 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:26 -0400 Subject: [PATCH 14/31] dbNotify: cleanup atexit --- src/ioc/db/dbNotify.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ioc/db/dbNotify.c b/src/ioc/db/dbNotify.c index 020f6d7c7..66253736c 100644 --- a/src/ioc/db/dbNotify.c +++ b/src/ioc/db/dbNotify.c @@ -44,6 +44,7 @@ #include "recGbl.h" #include "dbNotify.h" #include "epicsTime.h" +#include "epicsExit.h" #include "cantProceed.h" /*notify state values */ @@ -298,6 +299,14 @@ static void notifyCallback(CALLBACK *pcallback) callDone(precord, ppn); } +static void dbProcessNotifyExit(void* junk) +{ + assert(ellCount(&pnotifyGlobal->freeList)==0); + epicsMutexDestroy(pnotifyGlobal->lock); + free(pnotifyGlobal); + pnotifyGlobal = NULL; +} + void dbProcessNotifyInit(void) { if (pnotifyGlobal) @@ -305,6 +314,7 @@ void dbProcessNotifyInit(void) pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal)); pnotifyGlobal->lock = epicsMutexMustCreate(); ellInit(&pnotifyGlobal->freeList); + epicsAtExit(dbProcessNotifyExit, NULL); } void dbProcessNotify(processNotify *ppn) From c838d5d8701ef75a5982153d70c0e57f5e884597 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:26 -0400 Subject: [PATCH 15/31] dbBkpt: atexit --- src/ioc/db/dbBkpt.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbBkpt.c b/src/ioc/db/dbBkpt.c index dba5136b9..86a198ace 100644 --- a/src/ioc/db/dbBkpt.c +++ b/src/ioc/db/dbBkpt.c @@ -61,6 +61,7 @@ #include "errMdef.h" #include "recSup.h" #include "special.h" +#include "epicsExit.h" #define epicsExportSharedSymbols #include "dbAddr.h" #include "dbAccessDefs.h" @@ -250,7 +251,11 @@ static long FIND_CONT_NODE( return(0); } - +static void dbBkptExit(void *junk) { + epicsMutexDestroy(bkpt_stack_sem); + bkpt_stack_sem = NULL; +} + /* * Initialise the breakpoint stack */ @@ -259,6 +264,7 @@ void dbBkptInit(void) if (! bkpt_stack_sem) { bkpt_stack_sem = epicsMutexMustCreate(); lset_stack_count = 0; + epicsAtExit(dbBkptExit, NULL); } } From c8d9d5e9524fc77a82a9e0a93ba93f46b6f7d050 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:27 -0400 Subject: [PATCH 16/31] dbLock: atexit --- src/ioc/db/dbLock.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index eb2e113f7..d9bf09dca 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -54,6 +54,7 @@ since this will delay all other threads. #include "epicsMutex.h" #include "epicsThread.h" #include "epicsAssert.h" +#include "epicsExit.h" #include "cantProceed.h" #include "ellLib.h" #define epicsExportSharedSymbols @@ -107,7 +108,16 @@ typedef struct lockRecord { lockSet *plockSet; dbCommon *precord; } lockRecord; - + +static void dbLockExit(void *junk) +{ + epicsMutexDestroy(globalLock); + epicsMutexDestroy(lockSetModifyLock); + globalLock = NULL; + lockSetModifyLock = NULL; + dbLockIsInitialized = FALSE; +} + /*private routines */ static void dbLockInitialize(void) { @@ -118,6 +128,7 @@ static void dbLockInitialize(void) globalLock = epicsMutexMustCreate(); lockSetModifyLock = epicsMutexMustCreate(); dbLockIsInitialized = TRUE; + epicsAtExit(dbLockExit,NULL); } static lockSet * allocLockSet( From 0c73bb9448b5a6558824d3c62fecb3d12ed51e45 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:27 -0400 Subject: [PATCH 17/31] dbChannel: atexit --- src/ioc/db/dbChannel.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index d555c5f2e..86f23627b 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -20,6 +20,7 @@ #include "cantProceed.h" #include "epicsAssert.h" #include "epicsString.h" +#include "epicsExit.h" #include "errlog.h" #include "freeList.h" #include "gpHash.h" @@ -52,16 +53,23 @@ static void *dbChannelFreeList; static void *chFilterFreeList; static void *dbchStringFreeList; +static void dbChannelExit(void* junk) +{ + freeListCleanup(dbChannelFreeList); + freeListCleanup(chFilterFreeList); + freeListCleanup(dbchStringFreeList); + dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL; +} + void dbChannelInit (void) { - static int done = 0; + if(dbChannelFreeList) + return; - if (!done) { - done = 1; - freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128); - freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64); - freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128); - } + freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128); + freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64); + freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128); + epicsAtExit(dbChannelExit, NULL); } static void chf_value(parseContext *parser, parse_result *presult) From a9d4204d43c4d08d5076c447f1646f79a94999a1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 23 Jun 2014 16:28:28 -0400 Subject: [PATCH 18/31] dbLock: free LSET --- src/ioc/db/dbLock.c | 18 +++++++++++++++++- src/ioc/db/dbLock.h | 1 + src/ioc/misc/iocInit.c | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index d9bf09dca..5014523b2 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -388,7 +388,23 @@ void dbLockInitRecords(dbBase *pdbbase) } } } - + +void dbLockCleanupRecords(dbBase *pdbbase) +{ + DBENTRY ent; + long status; + + dbInitEntry(pdbbase, &ent); + for(status=dbFirstRecordType(&ent); !status; status=dbNextRecordType(&ent)) { + for(status=dbFirstRecord(&ent); !status; status=dbNextRecord(&ent)) { + dbCommon *prec = ent.precnode->precord; + free(prec->lset); + } + } + + dbFinishEntry(&ent); +} + void dbLockSetMerge(dbCommon *pfirst,dbCommon *psecond) { lockRecord *p1lockRecord = pfirst->lset; diff --git a/src/ioc/db/dbLock.h b/src/ioc/db/dbLock.h index 062996601..e13ce38b1 100644 --- a/src/ioc/db/dbLock.h +++ b/src/ioc/db/dbLock.h @@ -28,6 +28,7 @@ epicsShareFunc unsigned long dbLockGetLockId( struct dbCommon *precord); epicsShareFunc void dbLockInitRecords(struct dbBase *pdbbase); +epicsShareFunc void dbLockCleanupRecords(struct dbBase *pdbbase); epicsShareFunc void dbLockSetMerge( struct dbCommon *pfirst,struct dbCommon *psecond); epicsShareFunc void dbLockSetSplit(struct dbCommon *psource); diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 774846848..67438bf4f 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -658,6 +658,7 @@ int iocShutdown(void) scanShutdown(); callbackShutdown(); iterateRecords(doFreeRecord, NULL); + dbLockCleanupRecords(pdbbase); asShutdown(); iocshFree(); iocState = iocStopped; From 3ff49fc9b294aa295272c75d98c0d73f1628a0fb Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 10 Jul 2014 14:27:44 -0500 Subject: [PATCH 19/31] Fixed build errors, remove dependency on std. Build errors: Include guard missing from epicsUnitTest.h; dbShutdownTest.c was calling the non-universal strcasecmp() instead of epicsStrCaseCmp(). ioc/db/test no longer depends on std. I modified xRecord to make it a working record type, and simplified the other test programs so they all use the same new expanded dbd file rather than each making their own. I also added dbShutdownTest() to epicsRunDbTests(). --- src/Makefile | 2 +- src/ioc/db/test/Makefile | 39 ++++++++++++------------------ src/ioc/db/test/arrShorthandTest.c | 8 +++--- src/ioc/db/test/chfPluginTest.c | 8 +++--- src/ioc/db/test/dbChannelTest.c | 8 +++--- src/ioc/db/test/dbShutdownTest.c | 13 +++++----- src/ioc/db/test/epicsRunDbTests.c | 4 ++- src/ioc/db/test/xRecord.c | 16 ++++++++++-- src/ioc/db/test/xRecord.dbd | 8 ++---- src/libCom/misc/epicsUnitTest.h | 5 ++++ 10 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/Makefile b/src/Makefile index 8205a427b..3307b8786 100644 --- a/src/Makefile +++ b/src/Makefile @@ -62,7 +62,7 @@ DIRS += ioc ioc_DEPEND_DIRS = libCom ca/client DIRS += ioc/db/test -ioc/db/test_DEPEND_DIRS = ioc std libCom/RTEMS +ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS DIRS += ioc/dbtemplate/test ioc/dbtemplate/test_DEPEND_DIRS = ioc diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index dd149c8a0..4f1bc973b 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -10,19 +10,24 @@ TOP=../../../.. include $(TOP)/configure/CONFIG -TESTLIBRARY = xRec +TESTLIBRARY = dbTestIoc -xRec_SRCS = xRecord.c +dbTestIoc_SRCS = xRecord.c -PROD_LIBS = xRec dbCore ca Com +TARGETS += $(COMMON_DIR)/dbTestIoc.dbd +dbTestIoc_DBD += menuGlobal.dbd +dbTestIoc_DBD += menuConvert.dbd +dbTestIoc_DBD += xRecord.dbd +TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db + +testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp + +PROD_LIBS = dbTestIoc dbCore ca Com TESTPROD_HOST += dbShutdownTest -TARGETS += $(COMMON_DIR)/dbShutdownTest.dbd dbShutdownTest_SRCS += dbShutdownTest.c -dbShutdownTest_SRCS += dbShutdownTest_registerRecordDeviceDriver.cpp -dbShutdownTest_LIBS += $(EPICS_BASE_IOC_LIBS) -dbShutdownTest_DBD += base.dbd -TESTFILES += $(COMMON_DIR)/dbShutdownTest.dbd ../sRecord.db +dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += dbShutdownTest.c TESTS += dbShutdownTest TESTPROD_HOST += callbackTest @@ -35,34 +40,22 @@ dbStateTest_SRCS += dbStateTest.c testHarness_SRCS += dbStateTest.c TESTS += dbStateTest -TARGETS += $(COMMON_DIR)/dbChannelTest.dbd -dbChannelTest_DBD += xRecord.dbd TESTPROD_HOST += dbChannelTest dbChannelTest_SRCS += dbChannelTest.c -dbChannelTest_SRCS += dbChannelTest_registerRecordDeviceDriver.cpp +dbChannelTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbChannelTest.c -testHarness_SRCS += dbChannelTest_registerRecordDeviceDriver.cpp -TESTFILES += $(COMMON_DIR)/dbChannelTest.dbd ../xRecord.db TESTS += dbChannelTest -TARGETS += $(COMMON_DIR)/chfPluginTest.dbd -chfPluginTest_DBD += xRecord.dbd TESTPROD_HOST += chfPluginTest chfPluginTest_SRCS += chfPluginTest.c -chfPluginTest_SRCS += chfPluginTest_registerRecordDeviceDriver.cpp +chfPluginTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += chfPluginTest.c -testHarness_SRCS += chfPluginTest_registerRecordDeviceDriver.cpp -TESTFILES += $(COMMON_DIR)/chfPluginTest.dbd TESTS += chfPluginTest -TARGETS += $(COMMON_DIR)/arrShorthandTest.dbd -arrShorthandTest_DBD += xRecord.dbd TESTPROD_HOST += arrShorthandTest arrShorthandTest_SRCS += arrShorthandTest.c -arrShorthandTest_SRCS += arrShorthandTest_registerRecordDeviceDriver.cpp +arrShorthandTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += arrShorthandTest.c -testHarness_SRCS += arrShorthandTest_registerRecordDeviceDriver.cpp -TESTFILES += $(COMMON_DIR)/arrShorthandTest.dbd TESTS += arrShorthandTest # The testHarness runs all the test programs in a known working order. diff --git a/src/ioc/db/test/arrShorthandTest.c b/src/ioc/db/test/arrShorthandTest.c index dafa9e6e8..afb37e577 100644 --- a/src/ioc/db/test/arrShorthandTest.c +++ b/src/ioc/db/test/arrShorthandTest.c @@ -77,7 +77,7 @@ static void testHead (char* title) { testDiag("--------------------------------------------------------"); } -void arrShorthandTest_registerRecordDeviceDriver(struct dbBase *); +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(arrShorthandTest) { @@ -88,12 +88,12 @@ MAIN(arrShorthandTest) db_init_events(); dbChannelInit(); - if (dbReadDatabase(&pdbbase, "arrShorthandTest.dbd", + if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd", "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) - testAbort("Database description 'arrShorthandTest.dbd' not found"); + testAbort("Database description 'dbTestIoc.dbd' not found"); - arrShorthandTest_registerRecordDeviceDriver(pdbbase); + dbTestIoc_registerRecordDeviceDriver(pdbbase); if (dbReadDatabase(&pdbbase, "xRecord.db", "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database 'xRecord.db' not found"); diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index de34b989a..028ecceb2 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -481,7 +481,7 @@ static void testHead (char* title) { testDiag("--------------------------------------------------------"); } -void chfPluginTest_registerRecordDeviceDriver(struct dbBase *); +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(chfPluginTest) { @@ -508,12 +508,12 @@ MAIN(chfPluginTest) testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, "Enum to string: invalid index"); - if (dbReadDatabase(&pdbbase, "chfPluginTest.dbd", + if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd", "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) - testAbort("Database description 'chfPluginTest.dbd' not found"); + testAbort("Database description 'dbTestIoc.dbd' not found"); - chfPluginTest_registerRecordDeviceDriver(pdbbase); + dbTestIoc_registerRecordDeviceDriver(pdbbase); if (dbReadDatabase(&pdbbase, "xRecord.db", "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database 'xRecord.db' not found"); diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index a4011f646..5eb431f3e 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -149,7 +149,7 @@ chFilterIf testIf = p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array, c_open, c_reg_pre, c_reg_post, c_report, c_close }; -void dbChannelTest_registerRecordDeviceDriver(struct dbBase *); +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(testDbChannel) /* dbChannelTest is an API routine... */ { @@ -157,12 +157,12 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */ testPlan(66); - if (dbReadDatabase(&pdbbase, "dbChannelTest.dbd", + if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd", "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)) - testAbort("Database description 'dbChannelTest.dbd' not found"); + testAbort("Database description 'dbTestIoc.dbd' not found"); - dbChannelTest_registerRecordDeviceDriver(pdbbase); + dbTestIoc_registerRecordDeviceDriver(pdbbase); if (dbReadDatabase(&pdbbase, "xRecord.db", "." OSI_PATH_LIST_SEPARATOR "..", NULL)) testAbort("Test database 'xRecord.db' not found"); diff --git a/src/ioc/db/test/dbShutdownTest.c b/src/ioc/db/test/dbShutdownTest.c index d3a993b6e..69f43814f 100644 --- a/src/ioc/db/test/dbShutdownTest.c +++ b/src/ioc/db/test/dbShutdownTest.c @@ -10,8 +10,7 @@ * Ralph Lange */ -#include - +#include "epicsString.h" #include "dbUnitTest.h" #include "epicsThread.h" #include "iocInit.h" @@ -24,7 +23,7 @@ #include "testMain.h" -void dbShutdownTest_registerRecordDeviceDriver(struct dbBase *); +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); static struct threadItem { char *name; @@ -46,7 +45,7 @@ void findCommonThread (epicsThreadId id) { epicsThreadGetName(id, name, 32); for (thr = commonThreads; thr->name; thr++) { - if (strcasecmp(thr->name, name) == 0) { + if (epicsStrCaseCmp(thr->name, name) == 0) { thr->found = 1; } } @@ -67,11 +66,11 @@ void cycle(void) { testdbPrepare(); - testdbReadDatabase("dbShutdownTest.dbd", NULL, NULL); + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); - dbShutdownTest_registerRecordDeviceDriver(pdbbase); + dbTestIoc_registerRecordDeviceDriver(pdbbase); - testdbReadDatabase("sRecord.db", NULL, NULL); + testdbReadDatabase("xRecord.db", NULL, NULL); testOk1(!testiocInit()); diff --git a/src/ioc/db/test/epicsRunDbTests.c b/src/ioc/db/test/epicsRunDbTests.c index d8633702e..ef759d9d7 100644 --- a/src/ioc/db/test/epicsRunDbTests.c +++ b/src/ioc/db/test/epicsRunDbTests.c @@ -18,6 +18,7 @@ int callbackTest(void); int dbStateTest(void); +int dbShutdownTest(void); int testDbChannel(void); int chfPluginTest(void); int arrShorthandTest(void); @@ -28,9 +29,10 @@ void epicsRunDbTests(void) runTest(callbackTest); runTest(dbStateTest); + runTest(dbShutdownTest); runTest(testDbChannel); - runTest(chfPluginTest); runTest(arrShorthandTest); + runTest(chfPluginTest); dbmfFreeChunks(); diff --git a/src/ioc/db/test/xRecord.c b/src/ioc/db/test/xRecord.c index 568fbb838..dcc1f776c 100644 --- a/src/ioc/db/test/xRecord.c +++ b/src/ioc/db/test/xRecord.c @@ -14,12 +14,24 @@ */ #include "dbAccessDefs.h" -#include +#include "recSup.h" +#include "recGbl.h" #define GEN_SIZE_OFFSET #include "xRecord.h" #include -static rset xRSET; +static long process(xRecord *prec) +{ + prec->pact = TRUE; + recGblGetTimeStamp(prec); + recGblFwdLink(prec); + prec->pact = FALSE; + return 0; +} + +static rset xRSET = { + RSETNUMBER, NULL, NULL, NULL, process +}; epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/xRecord.dbd b/src/ioc/db/test/xRecord.dbd index 4837871a4..77e67147a 100644 --- a/src/ioc/db/test/xRecord.dbd +++ b/src/ioc/db/test/xRecord.dbd @@ -1,11 +1,7 @@ -# This is a combined minimal DBD and DB file +# This is a minimal record definition recordtype(x) { - field(NAME, DBF_STRING) { - prompt("Record Name") - special(SPC_NOMOD) - size(61) - } + include "dbCommon.dbd" field(VAL, DBF_LONG) { prompt("Value") } diff --git a/src/libCom/misc/epicsUnitTest.h b/src/libCom/misc/epicsUnitTest.h index c1f0a5939..0489752a1 100644 --- a/src/libCom/misc/epicsUnitTest.h +++ b/src/libCom/misc/epicsUnitTest.h @@ -9,6 +9,9 @@ * Author: Andrew Johnson */ +#ifndef INC_epicsUnitTest_H +#define INC_epicsUnitTest_H + #include #include "compilerDependencies.h" @@ -47,3 +50,5 @@ epicsShareFunc void runTestFunc(const char *name, TESTFUNC func); #ifdef __cplusplus } #endif + +#endif /* INC_epicsUnitTest_H */ From f85c3249abd28d2c1a2eea8c4224ef162d590943 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 10 Jul 2014 18:23:49 -0400 Subject: [PATCH 20/31] dbUnitTest: rename --- src/ioc/db/dbUnitTest.c | 14 +++++++------- src/ioc/db/dbUnitTest.h | 10 +++++----- src/ioc/db/test/dbShutdownTest.c | 4 ++-- src/ioc/misc/iocInit.c | 2 +- src/ioc/misc/iocInit.h | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c index 1a7f74dde..df9f5ac99 100644 --- a/src/ioc/db/dbUnitTest.c +++ b/src/ioc/db/dbUnitTest.c @@ -42,12 +42,12 @@ void testdbReadDatabase(const char* file, file, path, substitutions); } -int testiocInit(void) +int testIocInitOk(void) { - return iocBuildNoCA() || iocRun(); + return iocBuildIsolated() || iocRun(); } -int testiocShutdown(void) +int testIocShutdownOk(void) { return iocShutdown(); } @@ -66,7 +66,7 @@ long testdbPutField(const char* pv, short dbrType, ...) long ret; va_list ap; va_start(ap, dbrType); - ret = testVdbPutField(pv, dbrType, ap); + ret = testdbVPutField(pv, dbrType, ap); va_end(ap); return ret; } @@ -76,7 +76,7 @@ union anybuf { char bytes[sizeof(epicsAny)]; }; -long testVdbPutField(const char* pv, short dbrType, va_list ap) +long testdbVPutField(const char* pv, short dbrType, va_list ap) { DBADDR addr; union anybuf pod; @@ -112,12 +112,12 @@ long testVdbPutField(const char* pv, short dbrType, va_list ap) return dbPutField(&addr, dbrType, pod.bytes, 1); } -dbCommon* testGetRecord(const char* pv) +dbCommon* testdbRecordPtr(const char* pv) { DBADDR addr; if(dbNameToAddr(pv, &addr)) - testAbort("Missing PV %s", pv); + testAbort("Missing record %s", pv); return addr.precord; } diff --git a/src/ioc/db/dbUnitTest.h b/src/ioc/db/dbUnitTest.h index 9b58673ed..8a1b28463 100644 --- a/src/ioc/db/dbUnitTest.h +++ b/src/ioc/db/dbUnitTest.h @@ -29,13 +29,13 @@ epicsShareFunc void testdbPrepare(void); epicsShareFunc void testdbReadDatabase(const char* file, const char* path, const char* substitutions); -epicsShareFunc int testiocInit(void); -epicsShareFunc int testiocShutdown(void); +epicsShareFunc int testIocInitOk(void); +epicsShareFunc int testIocShutdownOk(void); epicsShareFunc void testdbCleanup(void); /* Scalar only version. * - * Remember to use the correct argument type!s + * Remember to use the correct argument types! * * int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG * unsigned int for DBR_ULONG @@ -43,9 +43,9 @@ epicsShareFunc void testdbCleanup(void); * const char* for DBR_STRING */ epicsShareFunc long testdbPutField(const char* pv, short dbrType, ...); -epicsShareFunc long testVdbPutField(const char* pv, short dbrType, va_list ap); +epicsShareFunc long testdbVPutField(const char* pv, short dbrType, va_list ap); -epicsShareFunc dbCommon* testGetRecord(const char* pv); +epicsShareFunc dbCommon* testdbRecordPtr(const char* pv); #ifdef __cplusplus } diff --git a/src/ioc/db/test/dbShutdownTest.c b/src/ioc/db/test/dbShutdownTest.c index 69f43814f..850b22989 100644 --- a/src/ioc/db/test/dbShutdownTest.c +++ b/src/ioc/db/test/dbShutdownTest.c @@ -72,12 +72,12 @@ void cycle(void) { testdbReadDatabase("xRecord.db", NULL, NULL); - testOk1(!testiocInit()); + testOk1(!testIocInitOk()); epicsThreadMap(findCommonThread); checkCommonThreads(); - testOk1(testiocShutdown()==0); + testOk1(testIocShutdownOk()==0); testdbCleanup(); } diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 67438bf4f..5f3d505d5 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -185,7 +185,7 @@ int iocBuild(void) return status; } -int iocBuildNoCA(void) +int iocBuildIsolated(void) { int status; diff --git a/src/ioc/misc/iocInit.h b/src/ioc/misc/iocInit.h index a5709f68e..24ae45e06 100644 --- a/src/ioc/misc/iocInit.h +++ b/src/ioc/misc/iocInit.h @@ -19,7 +19,7 @@ extern "C" { epicsShareFunc int iocInit(void); epicsShareFunc int iocBuild(void); -epicsShareFunc int iocBuildNoCA(void); +epicsShareFunc int iocBuildIsolated(void); epicsShareFunc int iocRun(void); epicsShareFunc int iocPause(void); epicsShareFunc int iocShutdown(void); From ebc3834661a760935293aba710f058ab8465577a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jul 2014 10:28:54 -0400 Subject: [PATCH 21/31] dbUnitTest: testIocInitOk/testIocShutdownOk testAbort() on failure --- src/ioc/db/dbUnitTest.c | 10 ++++++---- src/ioc/db/dbUnitTest.h | 4 ++-- src/ioc/db/test/dbShutdownTest.c | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c index df9f5ac99..2965c436b 100644 --- a/src/ioc/db/dbUnitTest.c +++ b/src/ioc/db/dbUnitTest.c @@ -42,14 +42,16 @@ void testdbReadDatabase(const char* file, file, path, substitutions); } -int testIocInitOk(void) +void testIocInitOk(void) { - return iocBuildIsolated() || iocRun(); + if(iocBuildIsolated() || iocRun()) + testAbort("Failed to start up test database"); } -int testIocShutdownOk(void) +void testIocShutdownOk(void) { - return iocShutdown(); + if(iocShutdown()) + testAbort("Failed to shutdown test database"); } void testdbCleanup(void) diff --git a/src/ioc/db/dbUnitTest.h b/src/ioc/db/dbUnitTest.h index 8a1b28463..eccdd8a14 100644 --- a/src/ioc/db/dbUnitTest.h +++ b/src/ioc/db/dbUnitTest.h @@ -29,8 +29,8 @@ epicsShareFunc void testdbPrepare(void); epicsShareFunc void testdbReadDatabase(const char* file, const char* path, const char* substitutions); -epicsShareFunc int testIocInitOk(void); -epicsShareFunc int testIocShutdownOk(void); +epicsShareFunc void testIocInitOk(void); +epicsShareFunc void testIocShutdownOk(void); epicsShareFunc void testdbCleanup(void); /* Scalar only version. diff --git a/src/ioc/db/test/dbShutdownTest.c b/src/ioc/db/test/dbShutdownTest.c index 850b22989..b74b68d08 100644 --- a/src/ioc/db/test/dbShutdownTest.c +++ b/src/ioc/db/test/dbShutdownTest.c @@ -72,19 +72,19 @@ void cycle(void) { testdbReadDatabase("xRecord.db", NULL, NULL); - testOk1(!testIocInitOk()); + testIocInitOk(); epicsThreadMap(findCommonThread); checkCommonThreads(); - testOk1(testIocShutdownOk()==0); + testIocShutdownOk(); testdbCleanup(); } MAIN(dbShutdownTest) { - testPlan(14); + testPlan(10); cycle(); cycle(); From 3f11fd16658ad97a26eb3cf680971ac4bccc06ab Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jul 2014 10:30:31 -0400 Subject: [PATCH 22/31] dbUnitTest: replace testdbPutField() add testdbPutFieldOk() and testdbPutFieldFail() which include calls to testPass() or testFail() Leave testdbVPutField() as a building block. --- src/ioc/db/dbUnitTest.c | 41 +++++++++++++++++++++++++++++++---------- src/ioc/db/dbUnitTest.h | 13 +++++++++++-- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c index 2965c436b..432cac852 100644 --- a/src/ioc/db/dbUnitTest.c +++ b/src/ioc/db/dbUnitTest.c @@ -63,16 +63,6 @@ void testdbCleanup(void) dbmfFreeChunks(); } -long testdbPutField(const char* pv, short dbrType, ...) -{ - long ret; - va_list ap; - va_start(ap, dbrType); - ret = testdbVPutField(pv, dbrType, ap); - va_end(ap); - return ret; -} - union anybuf { epicsAny val; char bytes[sizeof(epicsAny)]; @@ -95,6 +85,10 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap) return dbPutField(&addr, dbrType, buffer, 1); } + /* The Type parameter takes into consideration + * the C language rules for promotion of argument types + * in variadic functions. + */ #define OP(DBR,Type,mem) case DBR: {pod.val.mem = va_arg(ap,Type); break;} OP(DBR_CHAR, int, int8); OP(DBR_UCHAR, int, uInt8); @@ -114,6 +108,33 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap) return dbPutField(&addr, dbrType, pod.bytes, 1); } +void testdbPutFieldOk(const char* pv, short dbrType, ...) +{ + long ret; + va_list ap; + + va_start(ap, dbrType); + ret = testdbVPutField(pv, dbrType, ap); + va_end(ap); + + testOk(ret==0, "dbPutField(%s, %d, ...) == %ld", pv, dbrType, ret); +} + +void testdbPutFieldFail(long status, const char* pv, short dbrType, ...) +{ + long ret; + va_list ap; + + va_start(ap, dbrType); + ret = testdbVPutField(pv, dbrType, ap); + va_end(ap); + + if(ret==status) + testPass("dbPutField(%s, %d, ...) == %ld", pv, dbrType, status); + else + testFail("dbPutField(%s, %d, ...) != %ld (%ld)", pv, dbrType, status, ret); +} + dbCommon* testdbRecordPtr(const char* pv) { DBADDR addr; diff --git a/src/ioc/db/dbUnitTest.h b/src/ioc/db/dbUnitTest.h index eccdd8a14..e4a76ec2e 100644 --- a/src/ioc/db/dbUnitTest.h +++ b/src/ioc/db/dbUnitTest.h @@ -35,14 +35,23 @@ epicsShareFunc void testdbCleanup(void); /* Scalar only version. * - * Remember to use the correct argument types! + * Correct argument types must be used with this var-arg function! + * Doing otherwise will result in corruption of argument values! * * int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG * unsigned int for DBR_ULONG * double for DBR_FLOAT and DBR_DOUBLE * const char* for DBR_STRING + * + * eg. + * testdbPutFieldOk("pvname", DBF_ULONG, (unsigned int)5); + * testdbPutFieldOk("pvname", DBF_FLOAT, (double)4.1); + * testdbPutFieldOk("pvname", DBF_STRING, "hello world"); */ -epicsShareFunc long testdbPutField(const char* pv, short dbrType, ...); +epicsShareFunc void testdbPutFieldOk(const char* pv, short dbrType, ...); +/* the inverse of testdbPutFieldOk(). Tests for put failure */ +epicsShareFunc void testdbPutFieldFail(long status, const char* pv, short dbrType, ...); + epicsShareFunc long testdbVPutField(const char* pv, short dbrType, va_list ap); epicsShareFunc dbCommon* testdbRecordPtr(const char* pv); From a218bfd75c6a02b22aa2f40aa84d550542d30979 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jul 2014 16:27:41 -0400 Subject: [PATCH 23/31] dbLock: fix dbLockCleanupRecords all lockRecord s allocated in one block. oops. --- src/ioc/db/dbLock.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 5014523b2..a30080ad6 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -392,16 +392,18 @@ void dbLockInitRecords(dbBase *pdbbase) void dbLockCleanupRecords(dbBase *pdbbase) { DBENTRY ent; + dbCommon *prec; long status; dbInitEntry(pdbbase, &ent); - for(status=dbFirstRecordType(&ent); !status; status=dbNextRecordType(&ent)) { - for(status=dbFirstRecord(&ent); !status; status=dbNextRecord(&ent)) { - dbCommon *prec = ent.precnode->precord; - free(prec->lset); - } - } - + status=dbFirstRecordType(&ent); + if(status) + return; + status=dbNextRecordType(&ent); + if(status) + return; + prec = ent.precnode->precord; + free(prec->lset); dbFinishEntry(&ent); } From f14bfaab24faf8b79d6991fc518620e8f9e30782 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jul 2014 16:47:55 -0400 Subject: [PATCH 24/31] dbUnitTest: add testdbGetFieldEqual() --- src/ioc/db/dbUnitTest.c | 73 ++++++++++++++++++++++++++++++++++++----- src/ioc/db/dbUnitTest.h | 9 ++--- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/ioc/db/dbUnitTest.c b/src/ioc/db/dbUnitTest.c index 432cac852..634993e20 100644 --- a/src/ioc/db/dbUnitTest.c +++ b/src/ioc/db/dbUnitTest.c @@ -65,6 +65,7 @@ void testdbCleanup(void) union anybuf { epicsAny val; + char valStr[MAX_STRING_SIZE]; char bytes[sizeof(epicsAny)]; }; @@ -73,16 +74,17 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap) DBADDR addr; union anybuf pod; - if(dbNameToAddr(pv, &addr)) - testAbort("Missing PV %s", pv); + if(dbNameToAddr(pv, &addr)) { + testFail("Missing PV %s", pv); + return S_dbLib_recNotFound; + } switch(dbrType) { case DBR_STRING: { const char *uarg = va_arg(ap,char*); - epicsOldString buffer; - strncpy(buffer, uarg, sizeof(buffer)); - buffer[sizeof(buffer)-1] = '\0'; - return dbPutField(&addr, dbrType, buffer, 1); + strncpy(pod.valStr, uarg, sizeof(pod.valStr)); + pod.valStr[sizeof(pod.valStr)-1] = '\0'; + return dbPutField(&addr, dbrType, pod.valStr, 1); } /* The Type parameter takes into consideration @@ -101,8 +103,9 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap) OP(DBR_ENUM, int, enum16); #undef OP default: - testAbort("invalid DBR: dbPutField(%s, %d, ...)", + testFail("invalid DBR: dbPutField(\"%s\", %d, ...)", addr.precord->name, dbrType); + return S_db_badDbrtype; } return dbPutField(&addr, dbrType, pod.bytes, 1); @@ -130,9 +133,61 @@ void testdbPutFieldFail(long status, const char* pv, short dbrType, ...) va_end(ap); if(ret==status) - testPass("dbPutField(%s, %d, ...) == %ld", pv, dbrType, status); + testPass("dbPutField(\"%s\", %d, ...) == %ld", pv, dbrType, status); else - testFail("dbPutField(%s, %d, ...) != %ld (%ld)", pv, dbrType, status, ret); + testFail("dbPutField(\"%s\", %d, ...) != %ld (%ld)", pv, dbrType, status, ret); +} + +void testdbGetFieldEqual(const char* pv, short dbrType, ...) +{ + va_list ap; + + va_start(ap, dbrType); + testdbVGetFieldEqual(pv, dbrType, ap); + va_end(ap); +} + +void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) +{ + DBADDR addr; + long nReq = 1; + union anybuf pod; + long status; + + if(dbNameToAddr(pv, &addr)) { + testFail("Missing PV %s", pv); + return; + } + + status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL); + if(status) { + testFail("dbGetField(\"%s\",%d,...) returns %ld", pv, dbrType, status); + return; + } + + switch(dbrType) { + case DBR_STRING: { + const char *expect = va_arg(ap, char*); + testOk(strcmp(expect, pod.valStr)==0, + "dbGetField(\"%s\", %d) -> \"%s\" == \"%s\"", + pv, dbrType, expect, pod.valStr); + break; + } +#define OP(DBR,Type,mem,pat) case DBR: {Type expect = va_arg(ap,Type); \ + testOk(expect==pod.val.mem, "dbGetField(\"%s\", %d) -> " pat " == " pat, \ + pv, dbrType, expect, (Type)pod.val.mem); break;} + + OP(DBR_CHAR, int, int8, "%d"); + OP(DBR_UCHAR, int, uInt8, "%d"); + OP(DBR_SHORT, int, int16, "%d"); + OP(DBR_USHORT, int, uInt16, "%d"); + OP(DBR_LONG, int, int32, "%d"); + OP(DBR_ULONG, unsigned int, uInt32, "%u"); + OP(DBR_FLOAT, double, float32, "%e"); + OP(DBR_DOUBLE, double, float64, "%e"); + OP(DBR_ENUM, int, enum16, "%d"); +#undef OP + } } dbCommon* testdbRecordPtr(const char* pv) diff --git a/src/ioc/db/dbUnitTest.h b/src/ioc/db/dbUnitTest.h index e4a76ec2e..80867dc9d 100644 --- a/src/ioc/db/dbUnitTest.h +++ b/src/ioc/db/dbUnitTest.h @@ -33,9 +33,7 @@ epicsShareFunc void testIocInitOk(void); epicsShareFunc void testIocShutdownOk(void); epicsShareFunc void testdbCleanup(void); -/* Scalar only version. - * - * Correct argument types must be used with this var-arg function! +/* Correct argument types must be used with this var-arg function! * Doing otherwise will result in corruption of argument values! * * int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG @@ -49,11 +47,14 @@ epicsShareFunc void testdbCleanup(void); * testdbPutFieldOk("pvname", DBF_STRING, "hello world"); */ epicsShareFunc void testdbPutFieldOk(const char* pv, short dbrType, ...); -/* the inverse of testdbPutFieldOk(). Tests for put failure */ +/* Tests for put failure */ epicsShareFunc void testdbPutFieldFail(long status, const char* pv, short dbrType, ...); epicsShareFunc long testdbVPutField(const char* pv, short dbrType, va_list ap); +epicsShareFunc void testdbGetFieldEqual(const char* pv, short dbrType, ...); +epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap); + epicsShareFunc dbCommon* testdbRecordPtr(const char* pv); #ifdef __cplusplus From fd7a934ce359077e8d837fbaf17789d68b233e92 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jul 2014 16:51:32 -0400 Subject: [PATCH 25/31] dbPutLinkTest: test link string parsing Test parsing to CONTANT and DB_LINK. dbCa isn't initialized, so no test for CA_LINK. --- src/ioc/db/test/Makefile | 6 ++ src/ioc/db/test/dbPutLinkTest.c | 107 ++++++++++++++++++++++++++++++ src/ioc/db/test/dbPutLinkTest.db | 4 ++ src/ioc/db/test/epicsRunDbTests.c | 2 + src/ioc/db/test/xRecord.dbd | 3 + 5 files changed, 122 insertions(+) create mode 100644 src/ioc/db/test/dbPutLinkTest.c create mode 100644 src/ioc/db/test/dbPutLinkTest.db diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 4f1bc973b..88e0c105f 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -30,6 +30,12 @@ dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbShutdownTest.c TESTS += dbShutdownTest +TESTPROD_HOST += dbPutLinkTest +dbPutLinkTest_SRCS += dbPutLinkTest.c +dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += dbPutLinkTest.c +TESTS += dbPutLinkTest + TESTPROD_HOST += callbackTest callbackTest_SRCS += callbackTest.c testHarness_SRCS += callbackTest.c diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c new file mode 100644 index 000000000..cab27c637 --- /dev/null +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -0,0 +1,107 @@ +/*************************************************************************\ +* 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 + */ + +#include "string.h" + +#include "epicsString.h" +#include "dbUnitTest.h" +#include "epicsThread.h" +#include "iocInit.h" +#include "dbBase.h" +#include "link.h" +#include "dbAccess.h" +#include "registry.h" +#include "dbStaticLib.h" +#include "osiFileName.h" +#include "dbmf.h" + +#include "xRecord.h" + +#include "testMain.h" + +void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static const struct testDataT { + const char *linkstring; + short linkType; + unsigned int pvlMask; + const char *linkback; +} testSetData[] = { + {"", CONSTANT, 0}, + {"0", CONSTANT, 0}, + {"42", CONSTANT, 0}, + {"x1", DB_LINK, 0, "x1 NPP NMS"}, + {"x1.VAL", DB_LINK, 0, "x1.VAL NPP NMS"}, + {"x1.TIME", DB_LINK, 0, "x1.TIME NPP NMS"}, + {"x1 PP", DB_LINK, pvlOptPP, "x1 PP NMS"}, + {"x1 PP MSS", DB_LINK, pvlOptPP|pvlOptMSS, "x1 PP MSS"}, + {"x1 PPMSS", DB_LINK, pvlOptPP|pvlOptMSS, "x1 PP MSS"}, + {"x1 PPMSI", DB_LINK, pvlOptPP|pvlOptMSI, "x1 PP MSI"}, + /*TODO: testing doesn't support CA_LINK yet */ + {NULL} +}; + +static void testSet(void) +{ + const struct testDataT *td = testSetData; + xRecord *prec; + DBLINK *plink; + testdbPrepare(); + + testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); + + dbTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); + + testIocInitOk(); + + prec = (xRecord*)testdbRecordPtr("x1"); + plink = &prec->lnk; + + for(;td->linkstring;td++) { + + testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring); + if(td->linkback) + testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback); + else + testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring); + testOk1(plink->type==td->linkType); + + if(plink->type==td->linkType) { + switch(td->linkType) { + case CONSTANT: + if(plink->value.constantStr) + testOk1(strcmp(plink->value.constantStr,td->linkstring)==0); + else if(td->linkstring[0]=='\0') + testPass("Empty String"); + else + testFail("oops"); + break; + + case DB_LINK: + testOk1(plink->value.pv_link.pvlMask==td->pvlMask); + break; + } + } + } + + testIocShutdownOk(); + + testdbCleanup(); +} + +MAIN(dbPutLinkTest) +{ + testPlan(0); + testSet(); + return testDone(); +} diff --git a/src/ioc/db/test/dbPutLinkTest.db b/src/ioc/db/test/dbPutLinkTest.db new file mode 100644 index 000000000..940ea2e7f --- /dev/null +++ b/src/ioc/db/test/dbPutLinkTest.db @@ -0,0 +1,4 @@ +record(x, "x1") {} +record(x, "x2") {} +record(x, "x3") {} +record(x, "x4") {} diff --git a/src/ioc/db/test/epicsRunDbTests.c b/src/ioc/db/test/epicsRunDbTests.c index ef759d9d7..d13ae545f 100644 --- a/src/ioc/db/test/epicsRunDbTests.c +++ b/src/ioc/db/test/epicsRunDbTests.c @@ -19,6 +19,7 @@ int callbackTest(void); int dbStateTest(void); int dbShutdownTest(void); +int dbPutLinkTest(void); int testDbChannel(void); int chfPluginTest(void); int arrShorthandTest(void); @@ -30,6 +31,7 @@ void epicsRunDbTests(void) runTest(callbackTest); runTest(dbStateTest); runTest(dbShutdownTest); + runTest(dbPutLinkTest); runTest(testDbChannel); runTest(arrShorthandTest); runTest(chfPluginTest); diff --git a/src/ioc/db/test/xRecord.dbd b/src/ioc/db/test/xRecord.dbd index 77e67147a..8f4b2156c 100644 --- a/src/ioc/db/test/xRecord.dbd +++ b/src/ioc/db/test/xRecord.dbd @@ -5,4 +5,7 @@ recordtype(x) { field(VAL, DBF_LONG) { prompt("Value") } + field(LNK, DBF_INLINK) { + prompt("Link") + } } From bb51281d9ca69af74ed6b21ded374a78dc71484f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jul 2014 16:59:22 -0400 Subject: [PATCH 26/31] dbPutLinkTest: set # of tests --- src/ioc/db/test/dbPutLinkTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index cab27c637..fc5ad7a27 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -101,7 +101,7 @@ static void testSet(void) MAIN(dbPutLinkTest) { - testPlan(0); + testPlan(40); testSet(); return testDone(); } From edf1ad036207ca2d0f1da443bef1ac2873f68beb Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jul 2014 18:49:41 -0400 Subject: [PATCH 27/31] dbLock: another fix to dbLockCleanupRecords() --- src/ioc/db/dbLock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index a30080ad6..c758fca46 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -399,7 +399,7 @@ void dbLockCleanupRecords(dbBase *pdbbase) status=dbFirstRecordType(&ent); if(status) return; - status=dbNextRecordType(&ent); + status=dbFirstRecord(&ent); if(status) return; prec = ent.precnode->precord; From 3755b9eaad648ed950a8280adb0c96bf8d47cea1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 11 Jul 2014 18:49:41 -0400 Subject: [PATCH 28/31] dbLock: free lockSets --- src/ioc/db/dbLock.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index c758fca46..0a5b878d1 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -394,7 +394,9 @@ void dbLockCleanupRecords(dbBase *pdbbase) DBENTRY ent; dbCommon *prec; long status; + ELLNODE *cur; + /* free lockRecord */ dbInitEntry(pdbbase, &ent); status=dbFirstRecordType(&ent); if(status) @@ -405,6 +407,25 @@ void dbLockCleanupRecords(dbBase *pdbbase) prec = ent.precnode->precord; free(prec->lset); dbFinishEntry(&ent); + + /* free lockSets */ + /* ensure no lockSets are locked for re-compute */ + assert(ellCount(&lockSetList[listTypeRecordLock])==0); + /* move allocated locks back to the free list */ + while((cur=ellGet(&lockSetList[listTypeScanLock]))!=NULL) + { + lockSet *pset = CONTAINER(cur, lockSet, node); + assert(pset->state == lockSetStateFree); /* lock not held */ + pset->type = listTypeFree; + ellAdd(&lockSetList[listTypeFree],&pset->node); + } + /* clean up free list */ + while((cur=ellGet(&lockSetList[listTypeFree]))!=NULL) + { + lockSet *pset = CONTAINER(cur, lockSet, node); + epicsMutexDestroy(pset->lock); + free(pset); + } } void dbLockSetMerge(dbCommon *pfirst,dbCommon *psecond) From 34f0374ca5fe9c1a626d25f9f7e598e68ceef9b3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 14 Jul 2014 17:57:17 -0400 Subject: [PATCH 29/31] dbLock: yet another fix to dbLockCleanupRecords() track the lockRecord allocation --- src/ioc/db/dbLock.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 0a5b878d1..19ddc1f32 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -326,7 +326,9 @@ void dbScanUnlock(dbCommon *precord) epicsMutexUnlock(lockSetModifyLock); return; } - + +static lockRecord *lockRecordAlloc; + void dbLockInitRecords(dbBase *pdbbase) { int link; @@ -347,7 +349,7 @@ void dbLockInitRecords(dbBase *pdbbase) - pdbRecordType->no_aliases; } /*Allocate all of them at once */ - plockRecord = dbCalloc(nrecords,sizeof(lockRecord)); + lockRecordAlloc = plockRecord = dbCalloc(nrecords,sizeof(lockRecord)); for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { @@ -396,17 +398,8 @@ void dbLockCleanupRecords(dbBase *pdbbase) long status; ELLNODE *cur; - /* free lockRecord */ - dbInitEntry(pdbbase, &ent); - status=dbFirstRecordType(&ent); - if(status) - return; - status=dbFirstRecord(&ent); - if(status) - return; - prec = ent.precnode->precord; - free(prec->lset); - dbFinishEntry(&ent); + free(lockRecordAlloc); + lockRecordAlloc = NULL; /* free lockSets */ /* ensure no lockSets are locked for re-compute */ From 0bcfbd0ffc54a3a1ab504183826494af7c88de6e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 14 Jul 2014 18:16:30 -0400 Subject: [PATCH 30/31] remove unused --- src/ioc/db/dbLock.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ioc/db/dbLock.c b/src/ioc/db/dbLock.c index 19ddc1f32..5774f7ae1 100644 --- a/src/ioc/db/dbLock.c +++ b/src/ioc/db/dbLock.c @@ -393,9 +393,6 @@ void dbLockInitRecords(dbBase *pdbbase) void dbLockCleanupRecords(dbBase *pdbbase) { - DBENTRY ent; - dbCommon *prec; - long status; ELLNODE *cur; free(lockRecordAlloc); From 6bdb8f911a3b8f58bcafc094bf40663f706c094d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 23 Jul 2014 16:18:32 -0500 Subject: [PATCH 31/31] Suppress errlog output of expected warning messages. --- src/ioc/db/test/dbChannelTest.c | 3 +++ src/ioc/db/test/dbPutLinkTest.c | 3 +++ src/ioc/db/test/dbShutdownTest.c | 3 +++ src/libCom/error/errlog.c | 1 + 4 files changed, 10 insertions(+) diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 5eb431f3e..75d3185aa 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -21,6 +21,7 @@ #include "epicsUnitTest.h" #include "testMain.h" #include "osiFileName.h" +#include "errlog.h" /* Expected call bit definitions */ #define e_start 0x00000001 @@ -199,7 +200,9 @@ MAIN(testDbChannel) /* dbChannelTest is an API routine... */ testOk(!dbChannelCreate("y"), "Create, bad record"); testOk(!dbChannelCreate("x.NOFIELD"), "Create, bad field"); testOk(!dbChannelCreate("x.{not-json}"), "Create, bad JSON"); + eltc(0); testOk(!dbChannelCreate("x.{\"none\":null}"), "Create, bad filter"); + eltc(1); dbRegisterFilter("any", &testIf, NULL); diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index fc5ad7a27..a5a4e8084 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -22,6 +22,7 @@ #include "dbStaticLib.h" #include "osiFileName.h" #include "dbmf.h" +#include "errlog.h" #include "xRecord.h" @@ -62,7 +63,9 @@ static void testSet(void) testdbReadDatabase("dbPutLinkTest.db", NULL, NULL); + eltc(0); testIocInitOk(); + eltc(1); prec = (xRecord*)testdbRecordPtr("x1"); plink = &prec->lnk; diff --git a/src/ioc/db/test/dbShutdownTest.c b/src/ioc/db/test/dbShutdownTest.c index b74b68d08..55a8f3e0c 100644 --- a/src/ioc/db/test/dbShutdownTest.c +++ b/src/ioc/db/test/dbShutdownTest.c @@ -20,6 +20,7 @@ #include "dbStaticLib.h" #include "osiFileName.h" #include "dbmf.h" +#include "errlog.h" #include "testMain.h" @@ -72,7 +73,9 @@ void cycle(void) { testdbReadDatabase("xRecord.db", NULL, NULL); + eltc(0); testIocInitOk(); + eltc(1); epicsThreadMap(findCommonThread); checkCommonThreads(); diff --git a/src/libCom/error/errlog.c b/src/libCom/error/errlog.c index fbad162e1..680a1316a 100644 --- a/src/libCom/error/errlog.c +++ b/src/libCom/error/errlog.c @@ -368,6 +368,7 @@ epicsShareFunc int epicsShareAPI errlogRemoveListeners( epicsShareFunc int epicsShareAPI eltc(int yesno) { errlogInit(0); + errlogFlush(); pvtData.toConsole = yesno; return 0; }