EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: [Merge] lp:~epics-core/epics-base/ioc-shutdown2 into lp:epics-base
From: mdavidsaver <[email protected]>
To: [email protected]
Date: Mon, 23 Jun 2014 20:40:31 -0000
mdavidsaver has proposed merging lp:~epics-core/epics-base/ioc-shutdown2 into lp:epics-base.

Requested reviews:
  EPICS Core Developers (epics-core)

For more details, see:
https://code.launchpad.net/~epics-core/epics-base/ioc-shutdown2/+merge/224213

As discussed, I re-based the changes from ioc-shutdown with some additional cleanup, and removed the epicsThreadOnceReset function.
-- 
https://code.launchpad.net/~epics-core/epics-base/ioc-shutdown2/+merge/224213
Your team EPICS Core Developers is requested to review the proposed merge of lp:~epics-core/epics-base/ioc-shutdown2 into lp:epics-base.
=== modified file 'src/Makefile'
--- src/Makefile	2012-06-22 23:16:26 +0000
+++ src/Makefile	2014-06-23 20:39:40 +0000
@@ -62,7 +62,7 @@
 ioc_DEPEND_DIRS = libCom ca/client
 
 DIRS += ioc/db/test
-ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS
+ioc/db/test_DEPEND_DIRS = ioc std libCom/RTEMS
 
 DIRS += ioc/dbtemplate/test
 ioc/dbtemplate/test_DEPEND_DIRS = ioc

=== modified file 'src/ioc/as/asDbLib.c'
--- src/ioc/as/asDbLib.c	2012-10-01 05:54:10 +0000
+++ src/ioc/as/asDbLib.c	2014-06-23 20:39:40 +0000
@@ -152,6 +152,15 @@
     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;

=== modified file 'src/ioc/as/asDbLib.h'
--- src/ioc/as/asDbLib.h	2012-07-15 21:08:50 +0000
+++ src/ioc/as/asDbLib.h	2014-06-23 20:39:40 +0000
@@ -32,6 +32,7 @@
 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);

=== modified file 'src/ioc/db/Makefile'
--- src/ioc/db/Makefile	2012-11-29 18:53:21 +0000
+++ src/ioc/db/Makefile	2014-06-23 20:39:40 +0000
@@ -37,6 +37,7 @@
 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 += dbIocRegister.c
 dbCore_SRCS += chfPlugin.c
 dbCore_SRCS += dbState.c
-
+dbCore_SRCS += dbUnitTest.c

=== modified file 'src/ioc/db/callback.c'
--- src/ioc/db/callback.c	2012-05-04 22:34:48 +0000
+++ src/ioc/db/callback.c	2014-06-23 20:39:40 +0000
@@ -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 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 @@
 
 int callbackSetQueueSize(int size)
 {
-    if (callbackOnceFlag != EPICS_THREAD_ONCE_INIT) {
+    if (startStopEvent) {
         errlogPrintf("Callback system already initialized\n");
         return -1;
     }
@@ -101,24 +102,36 @@
     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 @@
         else
             epicsEventWait(startStopEvent);
     }
-    epicsAtExit(callbackShutdown, NULL);
-}
-
-void callbackInit(void)
-{
-    epicsThreadOnce(&callbackOnceFlag, callbackInitOnce, NULL);
 }
 
 /* This routine can be called from interrupt context */

=== modified file 'src/ioc/db/callback.h'
--- src/ioc/db/callback.h	2010-10-05 19:27:37 +0000
+++ src/ioc/db/callback.h	2014-06-23 20:39:40 +0000
@@ -57,6 +57,7 @@
 
 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(

=== modified file 'src/ioc/db/dbBkpt.c'
--- src/ioc/db/dbBkpt.c	2014-06-04 19:18:43 +0000
+++ src/ioc/db/dbBkpt.c	2014-06-23 20:39:40 +0000
@@ -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 @@
   return(0);
 }
 
-
+static void dbBkptExit(void *junk) {
+    epicsMutexDestroy(bkpt_stack_sem);
+    bkpt_stack_sem = NULL;
+}
+
 /*
  *  Initialise the breakpoint stack
  */
@@ -259,6 +264,7 @@
     if (! bkpt_stack_sem) {
         bkpt_stack_sem = epicsMutexMustCreate();
         lset_stack_count = 0;
+        epicsAtExit(dbBkptExit, NULL);
     }
 }
 

=== modified file 'src/ioc/db/dbCa.c'
--- src/ioc/db/dbCa.c	2014-06-04 19:18:43 +0000
+++ src/ioc/db/dbCa.c	2014-06-23 20:39:40 +0000
@@ -173,15 +173,22 @@
     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 @@
         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,

=== modified file 'src/ioc/db/dbCa.h'
--- src/ioc/db/dbCa.h	2012-04-27 17:21:47 +0000
+++ src/ioc/db/dbCa.h	2014-06-23 20:39:40 +0000
@@ -26,6 +26,7 @@
 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);

=== modified file 'src/ioc/db/dbChannel.c'
--- src/ioc/db/dbChannel.c	2014-06-04 13:56:51 +0000
+++ src/ioc/db/dbChannel.c	2014-06-23 20:39:40 +0000
@@ -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 *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)

=== modified file 'src/ioc/db/dbLock.c'
--- src/ioc/db/dbLock.c	2012-07-15 21:08:50 +0000
+++ src/ioc/db/dbLock.c	2014-06-23 20:39:40 +0000
@@ -54,6 +54,7 @@
 #include "epicsMutex.h"
 #include "epicsThread.h"
 #include "epicsAssert.h"
+#include "epicsExit.h"
 #include "cantProceed.h"
 #include "ellLib.h"
 #define epicsExportSharedSymbols
@@ -107,7 +108,16 @@
     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 @@
     globalLock = epicsMutexMustCreate();
     lockSetModifyLock = epicsMutexMustCreate();
     dbLockIsInitialized = TRUE;
+    epicsAtExit(dbLockExit,NULL);
 }
 
 static lockSet * allocLockSet(
@@ -377,7 +388,23 @@
         }
     }
 }
-
+
+void dbLockCleanupRecords(dbBase *pdbbase)
+{
+    DBENTRY ent;
+    long status;
+
+    dbInitEntry(pdbbase, &ent);
+    for(status=dbFirstRecordType(&ent); !status; status=dbNextRecordType(&ent)) {
+        for(status=dbFirstRecord(&ent); !status; status=dbNextRecord(&ent)) {
+            dbCommon *prec = ent.precnode->precord;
+            free(prec->lset);
+        }
+    }
+
+    dbFinishEntry(&ent);
+}
+
 void dbLockSetMerge(dbCommon *pfirst,dbCommon *psecond)
 {
     lockRecord	*p1lockRecord = pfirst->lset;

=== modified file 'src/ioc/db/dbLock.h'
--- src/ioc/db/dbLock.h	2012-07-15 21:08:50 +0000
+++ src/ioc/db/dbLock.h	2014-06-23 20:39:40 +0000
@@ -28,6 +28,7 @@
     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);

=== modified file 'src/ioc/db/dbNotify.c'
--- src/ioc/db/dbNotify.c	2012-07-15 21:08:50 +0000
+++ src/ioc/db/dbNotify.c	2014-06-23 20:39:40 +0000
@@ -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 @@
     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 @@
     pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal));
     pnotifyGlobal->lock = epicsMutexMustCreate();
     ellInit(&pnotifyGlobal->freeList);
+    epicsAtExit(dbProcessNotifyExit, NULL);
 }
 
 void dbProcessNotify(processNotify *ppn)

=== modified file 'src/ioc/db/dbScan.c'
--- src/ioc/db/dbScan.c	2013-12-17 18:54:04 +0000
+++ src/ioc/db/dbScan.c	2014-06-23 20:39:40 +0000
@@ -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 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 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 @@
 
     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 @@
     for (i = 0; i < nPeriodic; i++)
         spawnPeriodic(i);
 
-    epicsAtExit(scanShutdown, NULL);
     return 0;
 }
 
@@ -696,6 +713,22 @@
     }
 }
 
+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 @@
 {
     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;

=== modified file 'src/ioc/db/dbScan.h'
--- src/ioc/db/dbScan.h	2012-04-10 22:10:50 +0000
+++ src/ioc/db/dbScan.h	2014-06-23 20:39:40 +0000
@@ -44,6 +44,7 @@
 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);

=== added file 'src/ioc/db/dbUnitTest.c'
--- src/ioc/db/dbUnitTest.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbUnitTest.c	2014-06-23 20:39:40 +0000
@@ -0,0 +1,123 @@
+/*************************************************************************\
+* Copyright (c) 2013 Brookhaven National Laboratory.
+* Copyright (c) 2013 ITER Organization.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+ \*************************************************************************/
+
+/*
+ *  Author: Michael Davidsaver <[email protected]>
+ *          Ralph Lange <[email protected]>
+ */
+
+#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);
+}
+
+int testiocInit(void)
+{
+    return iocBuildNoCA() || iocRun();
+}
+
+int testiocShutdown(void)
+{
+    return iocShutdown();
+}
+
+void testdbCleanup(void)
+{
+    dbFreeBase(pdbbase);
+    initHookFree();
+    registryFree();
+    pdbbase = NULL;
+    dbmfFreeChunks();
+}
+
+long testdbPutField(const char* pv, short dbrType, ...)
+{
+    long ret;
+    va_list ap;
+    va_start(ap, dbrType);
+    ret = testVdbPutField(pv, dbrType, ap);
+    va_end(ap);
+    return ret;
+}
+
+union anybuf {
+    epicsAny val;
+    char bytes[sizeof(epicsAny)];
+};
+
+long testVdbPutField(const char* pv, short dbrType, va_list ap)
+{
+    DBADDR addr;
+    union anybuf pod;
+
+    if(dbNameToAddr(pv, &addr))
+        testAbort("Missing PV %s", pv);
+
+    switch(dbrType) {
+    case DBR_STRING: {
+        const char *uarg = va_arg(ap,char*);
+        epicsOldString buffer;
+        strncpy(buffer, uarg, sizeof(buffer));
+        buffer[sizeof(buffer)-1] = '\0';
+        return dbPutField(&addr, dbrType, buffer, 1);
+    }
+
+#define OP(DBR,Type,mem) case DBR: {pod.val.mem = va_arg(ap,Type); break;}
+    OP(DBR_CHAR, int, int8);
+    OP(DBR_UCHAR, int, uInt8);
+    OP(DBR_SHORT, int, int16);
+    OP(DBR_USHORT, int, uInt16);
+    OP(DBR_LONG, int, int32);
+    OP(DBR_ULONG, unsigned int, uInt32);
+    OP(DBR_FLOAT, double, float32);
+    OP(DBR_DOUBLE, double, float64);
+    OP(DBR_ENUM, int, enum16);
+#undef OP
+    default:
+        testAbort("invalid DBR: dbPutField(%s, %d, ...)",
+                  addr.precord->name, dbrType);
+    }
+
+    return dbPutField(&addr, dbrType, pod.bytes, 1);
+}
+
+dbCommon* testGetRecord(const char* pv)
+{
+    DBADDR addr;
+
+    if(dbNameToAddr(pv, &addr))
+        testAbort("Missing PV %s", pv);
+
+    return addr.precord;
+}

=== added file 'src/ioc/db/dbUnitTest.h'
--- src/ioc/db/dbUnitTest.h	1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbUnitTest.h	2014-06-23 20:39:40 +0000
@@ -0,0 +1,54 @@
+/*************************************************************************\
+* Copyright (c) 2013 Brookhaven National Laboratory.
+* Copyright (c) 2013 ITER Organization.
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+ \*************************************************************************/
+
+/*
+ *  Author: Michael Davidsaver <[email protected]>
+ *          Ralph Lange <[email protected]>
+ */
+
+#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 int testiocInit(void);
+epicsShareFunc int testiocShutdown(void);
+epicsShareFunc void testdbCleanup(void);
+
+/* Scalar only version.
+ *
+ * Remember to use the correct argument type!s
+ *
+ * int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG
+ * unsigned int for DBR_ULONG
+ * double for DBR_FLOAT and DBR_DOUBLE
+ * const char* for DBR_STRING
+ */
+epicsShareFunc long testdbPutField(const char* pv, short dbrType, ...);
+epicsShareFunc long testVdbPutField(const char* pv, short dbrType, va_list ap);
+
+epicsShareFunc dbCommon* testGetRecord(const char* pv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // EPICSUNITTESTDB_H

=== modified file 'src/ioc/db/initHooks.c'
--- src/ioc/db/initHooks.c	2010-10-05 19:27:37 +0000
+++ src/ioc/db/initHooks.c	2014-06-23 20:39:40 +0000
@@ -93,6 +93,14 @@
     }
 }
 
+void initHookFree(void)
+{
+    initHookInit();
+    epicsMutexMustLock(listLock);
+    ellFree(&functionList);
+    epicsMutexUnlock(listLock);
+}
+
 /*
  * Call any time you want to print out a state name.
  */

=== modified file 'src/ioc/db/initHooks.h'
--- src/ioc/db/initHooks.h	2010-10-05 19:27:37 +0000
+++ src/ioc/db/initHooks.h	2014-06-23 20:39:40 +0000
@@ -60,6 +60,7 @@
 epicsShareFunc int initHookRegister(initHookFunction func);
 epicsShareFunc void initHookAnnounce(initHookState state);
 epicsShareFunc const char *initHookName(int state);
+epicsShareFunc void initHookFree(void);
 
 #ifdef __cplusplus
 }

=== modified file 'src/ioc/db/test/Makefile'
--- src/ioc/db/test/Makefile	2014-06-13 19:37:12 +0000
+++ src/ioc/db/test/Makefile	2014-06-23 20:39:40 +0000
@@ -16,11 +16,23 @@
 
 PROD_LIBS = xRec dbCore ca Com
 
+<<<<<<< TREE
 TESTPROD_HOST += testdbConvert
 testdbConvert_SRCS += testdbConvert.c
 testHarness_SRCS += testdbConvert.c
 TESTS += testdbConvert
 
+=======
+TESTPROD_HOST += dbShutdownTest
+TARGETS += $(COMMON_DIR)/dbShutdownTest.dbd
+dbShutdownTest_SRCS += dbShutdownTest.c
+dbShutdownTest_SRCS += dbShutdownTest_registerRecordDeviceDriver.cpp
+dbShutdownTest_LIBS += $(EPICS_BASE_IOC_LIBS)
+dbShutdownTest_DBD += base.dbd
+TESTFILES += $(COMMON_DIR)/dbShutdownTest.dbd ../sRecord.db
+TESTS += dbShutdownTest
+
+>>>>>>> MERGE-SOURCE
 TESTPROD_HOST += callbackTest
 callbackTest_SRCS += callbackTest.c
 testHarness_SRCS += callbackTest.c

=== added file 'src/ioc/db/test/dbShutdownTest.c'
--- src/ioc/db/test/dbShutdownTest.c	1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/dbShutdownTest.c	2014-06-23 20:39:40 +0000
@@ -0,0 +1,94 @@
+/*************************************************************************\
+* 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 <[email protected]>
+ *          Ralph Lange <[email protected]>
+ */
+
+#include <string.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 "testMain.h"
+
+void dbShutdownTest_registerRecordDeviceDriver(struct dbBase *);
+
+static struct threadItem {
+    char *name;
+    char found;
+} commonThreads[] = {
+    { "errlog", 0 },
+    { "taskwd", 0 },
+    { "timerQueue", 0 },
+    { "cbLow", 0 },
+    { "scanOnce", 0 },
+    { NULL, 0 }
+};
+
+static
+void findCommonThread (epicsThreadId id) {
+    struct threadItem *thr;
+    char name[32];
+
+    epicsThreadGetName(id, name, 32);
+
+    for (thr = commonThreads; thr->name; thr++) {
+        if (strcasecmp(thr->name, name) == 0) {
+            thr->found = 1;
+        }
+    }
+}
+
+static
+void checkCommonThreads (void) {
+    struct threadItem *thr;
+
+    for (thr = commonThreads; thr->name; thr++) {
+        testOk(thr->found, "Thread %s is running", thr->name);
+        thr->found = 0;
+    }
+}
+
+static
+void cycle(void) {
+
+    testdbPrepare();
+
+    testdbReadDatabase("dbShutdownTest.dbd", NULL, NULL);
+
+    dbShutdownTest_registerRecordDeviceDriver(pdbbase);
+
+    testdbReadDatabase("sRecord.db", NULL, NULL);
+
+    testOk1(!testiocInit());
+
+    epicsThreadMap(findCommonThread);
+    checkCommonThreads();
+
+    testOk1(testiocShutdown()==0);
+
+    testdbCleanup();
+}
+
+MAIN(dbShutdownTest)
+{
+    testPlan(14);
+
+    cycle();
+    cycle();
+
+    return testDone();
+}

=== added file 'src/ioc/db/test/sRecord.db'
--- src/ioc/db/test/sRecord.db	1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/sRecord.db	2014-06-23 20:39:40 +0000
@@ -0,0 +1,1 @@
+record(ai, "somename") {}

=== modified file 'src/ioc/misc/dbCore.dbd'
--- src/ioc/misc/dbCore.dbd	2013-12-17 18:54:04 +0000
+++ src/ioc/misc/dbCore.dbd	2014-06-23 20:39:40 +0000
@@ -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)
 

=== modified file 'src/ioc/misc/iocInit.c'
--- src/ioc/misc/iocInit.c	2013-05-30 20:00:37 +0000
+++ src/ioc/misc/iocInit.c	2014-06-23 20:39:40 +0000
@@ -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 @@
     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 @@
     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 @@
 
     initialProcess();
     initHookAnnounce(initHookAfterInitialProcess);
+    return 0;
+}
 
-    /* Start CA server threads */
-    rsrv_init();
+static int iocBuild_3(void)
+{
     initHookAnnounce(initHookAfterCaServerInit);
 
     iocState = iocBuilt;
@@ -157,6 +166,39 @@
     return 0;
 }
 
+int iocBuild(void)
+{
+    int status;
+
+    status = iocBuild_1();
+    if (status) return status;
+
+    dbCaLinkInit();
+
+    status = iocBuild_2();
+    if (status) return status;
+
+    /* Start CA server threads */
+    rsrv_init();
+
+    status = iocBuild_3();
+    return status;
+}
+
+int iocBuildNoCA(void)
+{
+    int status;
+
+    status = iocBuild_1();
+    if (status) return status;
+
+    status = iocBuild_2();
+    if (status) return status;
+
+    status = iocBuild_3();
+    return status;
+}
+
 int iocRun(void)
 {
     if (iocState != iocPaused && iocState != iocBuilt) {
@@ -599,8 +641,31 @@
     }
 }
 
-static void exitDatabase(void *dummy)
-{
+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)
+{
+    iocShutdown();
 }

=== modified file 'src/ioc/misc/iocInit.h'
--- src/ioc/misc/iocInit.h	2009-06-10 20:19:32 +0000
+++ src/ioc/misc/iocInit.h	2014-06-23 20:39:40 +0000
@@ -19,8 +19,10 @@
 
 epicsShareFunc int iocInit(void);
 epicsShareFunc int iocBuild(void);
+epicsShareFunc int iocBuildNoCA(void);
 epicsShareFunc int iocRun(void);
 epicsShareFunc int iocPause(void);
+epicsShareFunc int iocShutdown(void);
 
 #ifdef __cplusplus
 }

=== modified file 'src/libCom/as/asLib.h'
--- src/libCom/as/asLib.h	2010-12-17 16:50:52 +0000
+++ src/libCom/as/asLib.h	2014-06-23 20:39:40 +0000
@@ -222,6 +222,7 @@
 /*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

=== modified file 'src/libCom/as/asLibRoutines.c'
--- src/libCom/as/asLibRoutines.c	2012-07-07 18:50:55 +0000
+++ src/libCom/as/asLibRoutines.c	2014-06-23 20:39:40 +0000
@@ -51,7 +51,6 @@
 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 @@
     return(0);
 }
 
-static void asFreeAll(ASBASE *pasbase)
+void asFreeAll(ASBASE *pasbase)
 {
     UAG		*puag;
     UAGNAME	*puagname;

=== modified file 'src/libCom/error/errlog.c'
--- src/libCom/error/errlog.c	2013-06-28 17:35:43 +0000
+++ src/libCom/error/errlog.c	2014-06-23 20:39:40 +0000
@@ -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 @@
     epicsEventId waitForFlush; /*errlogFlush waits for this*/
     epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/
     epicsMutexId flushLock;
-    epicsEventId waitForExit; /*exitHandler waits for this*/
-    int          atExit;      /*TRUE when exitHandler is active*/
+    epicsEventId waitForExit; /*errlogExitHandler waits for this*/
+    int          atExit;      /*TRUE when errlogExitHandler is active*/
     ELLLIST      listenerList;
     ELLLIST      msgQueue;
     msgNode      *pnextSend;
@@ -447,7 +447,7 @@
 }
 
 
-static void exitHandler(void *pvt)
+static void errlogExitHandler(void *pvt)
 {
     pvtData.atExit = 1;
     epicsEventSignal(pvtData.waitForWork);
@@ -561,7 +561,7 @@
     int noConsoleMessage;
     char *pmessage;
 
-    epicsAtExit(exitHandler,0);
+    epicsAtExit(errlogExitHandler,0);
     while (TRUE) {
         epicsEventMustWait(pvtData.waitForWork);
         while ((pmessage = msgbufGetSend(&noConsoleMessage))) {

=== modified file 'src/libCom/iocsh/iocsh.cpp'
--- src/libCom/iocsh/iocsh.cpp	2014-02-07 23:19:28 +0000
+++ src/libCom/iocsh/iocsh.cpp	2014-06-23 20:39:40 +0000
@@ -203,20 +203,22 @@
  */
 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 ();
 }
 

=== modified file 'src/libCom/misc/epicsExit.c'
--- src/libCom/misc/epicsExit.c	2013-12-17 18:54:04 +0000
+++ src/libCom/misc/epicsExit.c	2014-06-23 20:39:40 +0000
@@ -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 @@
     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 @@
 {
     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 @@
 epicsShareFunc void epicsExitCallAtExits(void)
 {
     exitPvt * pep = 0;
-    epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );
+
+    epicsExitInit ();
     epicsMutexMustLock ( exitPvtLock );
     if ( pExitPvtPerProcess ) {
         pep = pExitPvtPerProcess;
@@ -100,7 +114,8 @@
 epicsShareFunc void epicsExitCallAtThreadExits(void)
 {
     exitPvt * pep;
-    epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );
+
+    epicsExitInit ();
     pep = epicsThreadPrivateGet ( exitPvtPerThread );
     if ( pep ) {
         epicsExitCallAtExitsPvt ( pep );
@@ -109,14 +124,16 @@
     }
 }
 
-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 @@
 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 @@
         }
         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 void epicsExit(int status)
 {
     epicsExitCallAtExits();
-    epicsThreadSleep(1.0);
+    epicsThreadSleep(0.1);
     exit(status);
 }
+
+#include "epicsExport.h"
+
+epicsExportAddress(int,atExitDebug);

=== modified file 'src/libCom/misc/epicsExit.h'
--- src/libCom/misc/epicsExit.h	2013-12-17 18:54:04 +0000
+++ src/libCom/misc/epicsExit.h	2014-06-23 20:39:40 +0000
@@ -19,7 +19,8 @@
 
 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);

=== modified file 'src/libCom/test/epicsExitTest.c'
--- src/libCom/test/epicsExitTest.c	2009-04-08 22:39:27 +0000
+++ src/libCom/test/epicsExitTest.c	2014-06-23 20:39:40 +0000
@@ -59,12 +59,20 @@
     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 @@
     info *pinfoA = (info *)calloc(1, sizeof(info));
     info *pinfoB = (info *)calloc(1, sizeof(info));
 
-    testPlan(7);
-
-    epicsAtExit(mainExit, NULL);
+    testPlan(15);
+
+    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;
 }


Replies:
Re: [Merge] lp:~epics-core/epics-base/ioc-shutdown2 into lp:epics-base Andrew Johnson
Re: [Merge] lp:~epics-core/epics-base/ioc-shutdown2 into lp:epics-base Andrew Johnson
Re: [Merge] lp:~epics-core/epics-base/ioc-shutdown2 into lp:epics-base mdavidsaver
Re: [Merge] lp:~epics-core/epics-base/ioc-shutdown2 into lp:epics-base Andrew Johnson
Re: [Merge] lp:~epics-core/epics-base/ioc-shutdown2 into lp:epics-base mdavidsaver
Re: [Merge] lp:~epics-core/epics-base/ioc-shutdown2 into lp:epics-base mdavidsaver

Navigate by Date:
Prev: Jenkins build is back to normal : epics-base-3.15-win64s #14 APS Jenkins
Next: epicsThreadShowAll Benjamin Franksen
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Jenkins build is back to normal : epics-base-3.15-win64s #14 APS Jenkins
Next: Re: [Merge] lp:~epics-core/epics-base/ioc-shutdown2 into lp:epics-base Andrew Johnson
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 11 Jul 2014 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·