Merged ioc-shutdown2 branch.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
@@ -315,7 +326,9 @@ void dbScanUnlock(dbCommon *precord)
|
||||
epicsMutexUnlock(lockSetModifyLock);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static lockRecord *lockRecordAlloc;
|
||||
|
||||
void dbLockInitRecords(dbBase *pdbbase)
|
||||
{
|
||||
int link;
|
||||
@@ -336,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)) {
|
||||
@@ -377,7 +390,34 @@ void dbLockInitRecords(dbBase *pdbbase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dbLockCleanupRecords(dbBase *pdbbase)
|
||||
{
|
||||
ELLNODE *cur;
|
||||
|
||||
free(lockRecordAlloc);
|
||||
lockRecordAlloc = NULL;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
lockRecord *p1lockRecord = pfirst->lset;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
201
src/ioc/db/dbUnitTest.c
Normal file
201
src/ioc/db/dbUnitTest.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*************************************************************************\
|
||||
* 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 <mdavidsaver@bnl.gov>
|
||||
* Ralph Lange <Ralph.Lange@gmx.de>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
void testIocInitOk(void)
|
||||
{
|
||||
if(iocBuildIsolated() || iocRun())
|
||||
testAbort("Failed to start up test database");
|
||||
}
|
||||
|
||||
void testIocShutdownOk(void)
|
||||
{
|
||||
if(iocShutdown())
|
||||
testAbort("Failed to shutdown test database");
|
||||
}
|
||||
|
||||
void testdbCleanup(void)
|
||||
{
|
||||
dbFreeBase(pdbbase);
|
||||
initHookFree();
|
||||
registryFree();
|
||||
pdbbase = NULL;
|
||||
dbmfFreeChunks();
|
||||
}
|
||||
|
||||
union anybuf {
|
||||
epicsAny val;
|
||||
char valStr[MAX_STRING_SIZE];
|
||||
char bytes[sizeof(epicsAny)];
|
||||
};
|
||||
|
||||
long testdbVPutField(const char* pv, short dbrType, va_list ap)
|
||||
{
|
||||
DBADDR addr;
|
||||
union anybuf pod;
|
||||
|
||||
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*);
|
||||
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
|
||||
* 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);
|
||||
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:
|
||||
testFail("invalid DBR: dbPutField(\"%s\", %d, ...)",
|
||||
addr.precord->name, dbrType);
|
||||
return S_db_badDbrtype;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
DBADDR addr;
|
||||
|
||||
if(dbNameToAddr(pv, &addr))
|
||||
testAbort("Missing record %s", pv);
|
||||
|
||||
return addr.precord;
|
||||
}
|
||||
64
src/ioc/db/dbUnitTest.h
Normal file
64
src/ioc/db/dbUnitTest.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*************************************************************************\
|
||||
* 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 <mdavidsaver@bnl.gov>
|
||||
* Ralph Lange <Ralph.Lange@gmx.de>
|
||||
*/
|
||||
|
||||
#ifndef EPICSUNITTESTDB_H
|
||||
#define EPICSUNITTESTDB_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#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 void testIocInitOk(void);
|
||||
epicsShareFunc void testIocShutdownOk(void);
|
||||
epicsShareFunc void testdbCleanup(void);
|
||||
|
||||
/* 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 void testdbPutFieldOk(const char* pv, short dbrType, ...);
|
||||
/* 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
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // EPICSUNITTESTDB_H
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -10,11 +10,31 @@ 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
|
||||
dbShutdownTest_SRCS += dbShutdownTest.c
|
||||
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 += testdbConvert
|
||||
testdbConvert_SRCS += testdbConvert.c
|
||||
@@ -31,34 +51,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
|
||||
|
||||
TESTPROD_HOST += benchdbConvert
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include "osiFileName.h"
|
||||
#include "errlog.h"
|
||||
|
||||
/* Expected call bit definitions */
|
||||
#define e_start 0x00000001
|
||||
@@ -149,7 +150,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 +158,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");
|
||||
@@ -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);
|
||||
|
||||
|
||||
110
src/ioc/db/test/dbPutLinkTest.c
Normal file
110
src/ioc/db/test/dbPutLinkTest.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven
|
||||
* National Laboratory.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
* Author: Michael Davidsaver <mdavidsaver@bnl.gov>
|
||||
*/
|
||||
|
||||
#include "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 "errlog.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);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
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(40);
|
||||
testSet();
|
||||
return testDone();
|
||||
}
|
||||
4
src/ioc/db/test/dbPutLinkTest.db
Normal file
4
src/ioc/db/test/dbPutLinkTest.db
Normal file
@@ -0,0 +1,4 @@
|
||||
record(x, "x1") {}
|
||||
record(x, "x2") {}
|
||||
record(x, "x3") {}
|
||||
record(x, "x4") {}
|
||||
96
src/ioc/db/test/dbShutdownTest.c
Normal file
96
src/ioc/db/test/dbShutdownTest.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/*************************************************************************\
|
||||
* 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 <mdavidsaver@bnl.gov>
|
||||
* Ralph Lange <Ralph.Lange@gmx.de>
|
||||
*/
|
||||
|
||||
#include "epicsString.h"
|
||||
#include "dbUnitTest.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 "errlog.h"
|
||||
|
||||
#include "testMain.h"
|
||||
|
||||
void dbTestIoc_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 (epicsStrCaseCmp(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) {
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
|
||||
|
||||
dbTestIoc_registerRecordDeviceDriver(pdbbase);
|
||||
|
||||
testdbReadDatabase("xRecord.db", NULL, NULL);
|
||||
|
||||
eltc(0);
|
||||
testIocInitOk();
|
||||
eltc(1);
|
||||
|
||||
epicsThreadMap(findCommonThread);
|
||||
checkCommonThreads();
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
}
|
||||
|
||||
MAIN(dbShutdownTest)
|
||||
{
|
||||
testPlan(10);
|
||||
|
||||
cycle();
|
||||
cycle();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
@@ -19,6 +19,8 @@
|
||||
int testdbConvert(void);
|
||||
int callbackTest(void);
|
||||
int dbStateTest(void);
|
||||
int dbShutdownTest(void);
|
||||
int dbPutLinkTest(void);
|
||||
int testDbChannel(void);
|
||||
int chfPluginTest(void);
|
||||
int arrShorthandTest(void);
|
||||
@@ -30,9 +32,11 @@ void epicsRunDbTests(void)
|
||||
runTest(testdbConvert);
|
||||
runTest(callbackTest);
|
||||
runTest(dbStateTest);
|
||||
runTest(dbShutdownTest);
|
||||
runTest(dbPutLinkTest);
|
||||
runTest(testDbChannel);
|
||||
runTest(chfPluginTest);
|
||||
runTest(arrShorthandTest);
|
||||
runTest(chfPluginTest);
|
||||
|
||||
dbmfFreeChunks();
|
||||
|
||||
|
||||
1
src/ioc/db/test/sRecord.db
Normal file
1
src/ioc/db/test/sRecord.db
Normal file
@@ -0,0 +1 @@
|
||||
record(ai, "somename") {}
|
||||
@@ -14,12 +14,24 @@
|
||||
*/
|
||||
|
||||
#include "dbAccessDefs.h"
|
||||
#include <recSup.h>
|
||||
#include "recSup.h"
|
||||
#include "recGbl.h"
|
||||
|
||||
#define GEN_SIZE_OFFSET
|
||||
#include "xRecord.h"
|
||||
|
||||
#include <epicsExport.h>
|
||||
|
||||
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);
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# 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")
|
||||
}
|
||||
field(LNK, DBF_INLINK) {
|
||||
prompt("Link")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,12 +91,13 @@ 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);
|
||||
initHookAnnounce(initHookAtIocBuild);
|
||||
|
||||
if (!epicsThreadIsOkToBlock()) {
|
||||
@@ -109,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();
|
||||
@@ -147,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;
|
||||
@@ -157,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 iocBuildIsolated(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) {
|
||||
@@ -599,8 +641,31 @@ 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);
|
||||
scanShutdown();
|
||||
callbackShutdown();
|
||||
iterateRecords(doFreeRecord, NULL);
|
||||
dbLockCleanupRecords(pdbbase);
|
||||
asShutdown();
|
||||
iocshFree();
|
||||
iocState = iocStopped;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exitDatabase(void *dummy)
|
||||
{
|
||||
iterateRecords(doCloseLinks, NULL);
|
||||
iocState = iocStopped;
|
||||
iocShutdown();
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ extern "C" {
|
||||
|
||||
epicsShareFunc int iocInit(void);
|
||||
epicsShareFunc int iocBuild(void);
|
||||
epicsShareFunc int iocBuildIsolated(void);
|
||||
epicsShareFunc int iocRun(void);
|
||||
epicsShareFunc int iocPause(void);
|
||||
epicsShareFunc int iocShutdown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -368,6 +368,7 @@ epicsShareFunc int epicsShareAPI errlogRemoveListeners(
|
||||
epicsShareFunc int epicsShareAPI eltc(int yesno)
|
||||
{
|
||||
errlogInit(0);
|
||||
errlogFlush();
|
||||
pvtData.toConsole = yesno;
|
||||
return 0;
|
||||
}
|
||||
@@ -447,7 +448,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 +562,7 @@ static void errlogThread(void)
|
||||
int noConsoleMessage;
|
||||
char *pmessage;
|
||||
|
||||
epicsAtExit(exitHandler,0);
|
||||
epicsAtExit(errlogExitHandler,0);
|
||||
while (TRUE) {
|
||||
epicsEventMustWait(pvtData.waitForWork);
|
||||
while ((pmessage = msgbufGetSend(&noConsoleMessage))) {
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
@@ -66,15 +71,23 @@ 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);
|
||||
else if(atExitDebug)
|
||||
fprintf(stderr, "atExit %p(%p)\n", pexitNode->func, pexitNode->arg);
|
||||
pexitNode->func ( pexitNode->arg );
|
||||
ellDelete ( & pep->list, & pexitNode->node );
|
||||
free ( pexitNode );
|
||||
@@ -84,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;
|
||||
@@ -100,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 );
|
||||
@@ -109,14 +124,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
|
||||
= calloc ( 1, sizeof( *pExitNode ) );
|
||||
exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) );
|
||||
|
||||
if ( pExitNode ) {
|
||||
pExitNode->func = func;
|
||||
pExitNode->arg = arg;
|
||||
if(name)
|
||||
strcpy(pExitNode->name, name);
|
||||
ellAdd ( & pep->list, & pExitNode->node );
|
||||
status = 0;
|
||||
}
|
||||
@@ -126,7 +143,8 @@ static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg)
|
||||
epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg)
|
||||
{
|
||||
exitPvt * pep;
|
||||
epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );
|
||||
|
||||
epicsExitInit ();
|
||||
pep = epicsThreadPrivateGet ( exitPvtPerThread );
|
||||
if ( ! pep ) {
|
||||
pep = createExitPvt ();
|
||||
@@ -135,16 +153,20 @@ 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 );
|
||||
|
||||
epicsExitInit ();
|
||||
epicsMutexMustLock ( exitPvtLock );
|
||||
if ( !pExitPvtPerProcess ) {
|
||||
pExitPvtPerProcess = createExitPvt ();
|
||||
}
|
||||
if ( pExitPvtPerProcess ) {
|
||||
status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg );
|
||||
status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg, name );
|
||||
}
|
||||
epicsMutexUnlock ( exitPvtLock );
|
||||
return status;
|
||||
@@ -153,6 +175,10 @@ epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg)
|
||||
epicsShareFunc void epicsExit(int status)
|
||||
{
|
||||
epicsExitCallAtExits();
|
||||
epicsThreadSleep(1.0);
|
||||
epicsThreadSleep(0.1);
|
||||
exit(status);
|
||||
}
|
||||
|
||||
#include "epicsExport.h"
|
||||
|
||||
epicsExportAddress(int,atExitDebug);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
* Author: Andrew Johnson
|
||||
*/
|
||||
|
||||
#ifndef INC_epicsUnitTest_H
|
||||
#define INC_epicsUnitTest_H
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "compilerDependencies.h"
|
||||
@@ -47,3 +50,5 @@ epicsShareFunc void runTestFunc(const char *name, TESTFUNC func);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INC_epicsUnitTest_H */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user