Merged ioc-shutdown2 branch.

This commit is contained in:
Andrew Johnson
2014-07-23 16:59:12 -05:00
40 changed files with 875 additions and 112 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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 */

View File

@@ -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(

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;

View File

@@ -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
View 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
View 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

View File

@@ -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.
*/

View File

@@ -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
}

View File

@@ -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

View File

@@ -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");

View File

@@ -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");

View File

@@ -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);

View 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();
}

View File

@@ -0,0 +1,4 @@
record(x, "x1") {}
record(x, "x2") {}
record(x, "x3") {}
record(x, "x4") {}

View 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();
}

View File

@@ -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();

View File

@@ -0,0 +1 @@
record(ai, "somename") {}

View File

@@ -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);

View File

@@ -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")
}
}

View File

@@ -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)

View File

@@ -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();
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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))) {

View File

@@ -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 ();
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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;
}