Merge lp:~epics-core/epics-base/ioc-shutdown2 into lp:~epics-core/epics-base/3.15

Proposed by mdavidsaver
Status: Merged
Approved by: Andrew Johnson
Approved revision: 12520
Merged at revision: 12496
Proposed branch: lp:~epics-core/epics-base/ioc-shutdown2
Merge into: lp:~epics-core/epics-base/3.15
Diff against target: 1877 lines (+878/-115)
40 files modified
src/ioc/as/asDbLib.c (+9/-0)
src/ioc/as/asDbLib.h (+1/-0)
src/ioc/db/Makefile (+2/-1)
src/ioc/db/callback.c (+17/-10)
src/ioc/db/callback.h (+1/-0)
src/ioc/db/dbBkpt.c (+7/-1)
src/ioc/db/dbCa.c (+18/-7)
src/ioc/db/dbCa.h (+1/-0)
src/ioc/db/dbChannel.c (+15/-7)
src/ioc/db/dbLock.c (+44/-4)
src/ioc/db/dbLock.h (+1/-0)
src/ioc/db/dbNotify.c (+10/-0)
src/ioc/db/dbScan.c (+39/-4)
src/ioc/db/dbScan.h (+1/-0)
src/ioc/db/dbUnitTest.c (+201/-0)
src/ioc/db/dbUnitTest.h (+64/-0)
src/ioc/db/initHooks.c (+8/-0)
src/ioc/db/initHooks.h (+1/-0)
src/ioc/db/test/Makefile (+28/-20)
src/ioc/db/test/arrShorthandTest.c (+4/-4)
src/ioc/db/test/chfPluginTest.c (+4/-4)
src/ioc/db/test/dbChannelTest.c (+7/-4)
src/ioc/db/test/dbPutLinkTest.c (+110/-0)
src/ioc/db/test/dbPutLinkTest.db (+4/-0)
src/ioc/db/test/dbShutdownTest.c (+96/-0)
src/ioc/db/test/epicsRunDbTests.c (+5/-1)
src/ioc/db/test/sRecord.db (+1/-0)
src/ioc/db/test/xRecord.c (+14/-2)
src/ioc/db/test/xRecord.dbd (+5/-6)
src/ioc/misc/dbCore.dbd (+3/-0)
src/ioc/misc/iocInit.c (+74/-9)
src/ioc/misc/iocInit.h (+2/-0)
src/libCom/as/asLib.h (+1/-0)
src/libCom/as/asLibRoutines.c (+1/-2)
src/libCom/error/errlog.c (+6/-5)
src/libCom/iocsh/iocsh.cpp (+6/-4)
src/libCom/misc/epicsExit.c (+39/-13)
src/libCom/misc/epicsExit.h (+2/-1)
src/libCom/misc/epicsUnitTest.h (+5/-0)
src/libCom/test/epicsExitTest.c (+21/-6)
To merge this branch: bzr merge lp:~epics-core/epics-base/ioc-shutdown2
Reviewer Review Type Date Requested Status
Andrew Johnson Approve
Ralph Lange close look at diff Approve
mdavidsaver Approve
Review via email: mp+224213@code.launchpad.net

Description of the change

As discussed, I re-based the changes from ioc-shutdown with some additional cleanup, and removed the epicsThreadOnceReset function.

To post a comment you must log in.
Revision history for this message
Andrew Johnson (anj) wrote :

Hi Michael,

This looks much better. There are still a few minor things to clean up and we will eventually need appDevGuide text for the new dbUnitTest API, but I will accept this approach. Some comments:

--------------------------------------------------------------

A patch for the following fixes exists, or I can commit the
changes if you prefer. I tested the result on Linux & VxWorks,
builds Ok for RTEMS:

Build errors: Include guard missing from epicsUnitTest.h;
    dbShutdownTest.c was calling the non-universal strcasecmp()
    instead of epicsStrCaseCmp().

I would prefer that ioc/db/test not depend on std. I modified
    xRecord to make it a working record type, and simplified the
    other test programs so they all use the same new expanded
    dbd file rather than each making their own. I also added
    dbShutdownTest() to epicsRunDbTests().

--------------------------------------------------------------

I would like to see these issues addressed:

I don't like the public name iocBuildNoCA(), if anything the
    iocBuild routine should have "WithCA" in its name instead,
    as eventually merging pvAccess will invalidate a ...NoCA
    name, but that may be going a little too far. How about
    using iocBuildIsolated() instead of ...NoCA (or see
    http://thesaurus.com/browse/isolated for some alternative
    adjectives, Sequestered would also be good but it's even
    longer).

In dbUnitTest.c shouldn't the Type arguments to OP use the
    epics{Int,UInt,Float}<n> names? If not I suspect there
    should at least be a couple more 'unsigned' keywords in
    there.

The dbUnitTest.h API can extend the epicsUnitTest.h one and add
    its own Ok() routines. I'd suggest testIocInitOk() and
    testIocShutdownOk() [note capitalization and name change]
    which should call testOk() directly (with a suitable
    description string) instead of making the user remember to
    invert the return values.

The name testVdbPutField() should probably be testdbVPutField()
    since most of the other routine names start 'testdb'.

The name testGetRecord() doesn't match the other names (use
    testdbRecordPtr() maybe?), and should say "record" in its
    abort message instead of "PV" (usually records are PVs, but
    not all PVs are records...).

The testdbPrepare() routine could call eltc(0) to turn off
    errlog warning messages which otherwise get sent to stderr
    and appear in the test output.

--------------------------------------------------------------

These are minor details which don't have to be fixed now but
should be eventually:

The variable(atExitDebug,int) declaration should not appear in
    dbCore.dbd, but libCom doesn't currently have its own .dbd
    file where it should really belong.

src/ioc/misc/iocInit.c routine names (static):
  * Change the iocBuild_<n>() names to be more descriptive?

dbShutdownTest does not have a checkNoCommonThreads() test, so
    it's not testing the full operation of iocShutdown().

These ioc/db/test programs need converting to use dbUnitTest.h:
    dbChannelTest.c chfPluginTest.c and arrShorthandTest.c

--------------------------------------------------------------

- Andrew

review: Needs Fixing
Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> ...
> A patch for the following fixes exists, or I can commit the
> changes if you prefer.

Go ahead.

12508. By Andrew Johnson

Fixed build errors, remove dependency on std.

Build errors: Include guard missing from epicsUnitTest.h;
    dbShutdownTest.c was calling the non-universal strcasecmp()
    instead of epicsStrCaseCmp().

ioc/db/test no longer depends on std. I modified
    xRecord to make it a working record type, and simplified the
    other test programs so they all use the same new expanded
    dbd file rather than each making their own. I also added
    dbShutdownTest() to epicsRunDbTests().

Revision history for this message
Andrew Johnson (anj) wrote :

Committed my changes as described in 1st section above.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> In dbUnitTest.c shouldn't the Type arguments to OP use the
> epics{Int,UInt,Float}<n> names? If not I suspect there
> should at least be a couple more 'unsigned' keywords in
> there.

Before I can answer this question I think you will need to educate me a bit about argument type promotion in vararg functions. Does va_arg() have knowledge of this? Will va_arg(X,short) actually return an 'int'?

My thinking at the time was that since these rules are a bit obscure, that it would be better to explicitly document a mapping of variable type to DBF_*. You will find such a mapping in a comment in dbUnitTest.h. I'm not too attached to this approach though, the point is that a put operation should be one line.

Just to throw out another idea. I have considered using printf style syntax just to get a compile time check. eg. testdbPutField("pvname", DBF_LONG, "%d", 0) where exactly "%d" must be used for DBF_LONG. As a side effect this would work well for DBF_STRING.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> The testdbPrepare() routine could call eltc(0) to turn off
> errlog warning messages which otherwise get sent to stderr
> and appear in the test output.

Ok. Perhaps it would be better still to do this only inside the test*() functions which will generate known warnings? eltc() could be changed to return the previous value of the toConsole flag.

12509. By mdavidsaver

dbUnitTest: rename

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Function names changed as requested.

Revision history for this message
Andrew Johnson (anj) wrote :

On 07/10/2014 05:14 PM, mdavidsaver wrote:
>> In dbUnitTest.c shouldn't the Type arguments to OP use the
>> epics{Int,UInt,Float}<n> names? If not I suspect there
>> should at least be a couple more 'unsigned' keywords in
>> there.
>
> Before I can answer this question I think you will need to
> educate me a bit about argument type promotion in vararg
> functions. Does va_arg() have knowledge of this? Will
> va_arg(X,short) actually return an 'int'?

No, you just educated me, I withdraw the above comment. It would be nice
to have a source-file comment nearby saying something like "See the C
integer type promotion rules" for human readers, and maybe use that
language in the header file as well.

> Just to throw out another idea. I have considered using
> printf style syntax just to get a compile time check. eg.
> testdbPutField("pvname", DBF_LONG, "%d", 0) where exactly
> "%d" must be used for DBF_LONG. As a side effect this
> would work well for DBF_STRING.

True, but it might cause problems on Windows when we add 64-bit integer
fields ("%ld" vs "%lld"). I'm about to publish a branch for comments
that introduces DBF_INT64 and DBF_UINT64 types to the database.
Unfortunately it will probably break device support for waveform and
other record types that can choose field types, because I inserted the
64-bit type IDs between ULONG and FLOAT.

- Andrew
--
Advertising may be described as the science of arresting the human
intelligence long enough to get money from it. -- Stephen Leacock

Revision history for this message
Andrew Johnson (anj) wrote :

On 07/10/2014 05:40 PM, mdavidsaver wrote:
> Function names changed as requested.

You misunderstood a couple of my suggestions; I meant dbShutdownTest to
look like this:

    testdbReadDatabase("xRecord.db", NULL, NULL);

    testIocInitOk();

    epicsThreadMap(findCommonThread);
    checkCommonThreads();

    testIocShutdownOk();

    testdbCleanup();

The ...Ok() functions should incorporate calls to testOk() so the user
doesn't have to remember whether she needs to invert the result or not,
and should also use explicit text such as "IOC Initialized".

- Andrew
--
Advertising may be described as the science of arresting the human
intelligence long enough to get money from it. -- Stephen Leacock

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> On 07/10/2014 05:40 PM, mdavidsaver wrote:
> > Function names changed as requested.
>
> You misunderstood a couple of my suggestions; I meant dbShutdownTest to
> look like this:

Actually not, I did the renames seperately. The functional changes will follow.

Revision history for this message
Andrew Johnson (anj) wrote :
12510. By mdavidsaver

dbUnitTest: testIocInitOk/testIocShutdownOk testAbort() on failure

12511. By mdavidsaver

dbUnitTest: replace testdbPutField()

add testdbPutFieldOk() and testdbPutFieldFail()
which include calls to testPass() or testFail()

Leave testdbVPutField() as a building block.

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> > The testdbPrepare() routine could call eltc(0) ...
> Ok. ...

I'm reconsidering this. Do we really want to hide all messages?

Also, if this were done, shouldn't this be done in testPlan()?

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> Ok, sorry.

np. These changes should now be complete.

> If you're interested, https://code.launchpad.net/~epics-core/epics-base/int64

Oh yes :)

Revision history for this message
Andrew Johnson (anj) wrote :

On 07/11/2014 09:49 AM, mdavidsaver wrote:
>>> The testdbPrepare() routine could call eltc(0) ...
>> Ok. ...
>
> I'm reconsidering this. Do we really want to hide all messages?

Maybe not; several of our tests do trigger errlog warning messages from
the facilities they are testing, but I guess when that's expected the
test program should be using eltc to suppress them – there are three
programs in libCom/test and one in ioc/db/test that currently do that.

> Also, if this were done, shouldn't this be done in testPlan()?

That would couple the epicsUnitTest code directly to errlog which would
be a bad thing. Currently only some of the test programs will pull in
errlog, whereas the IOC definitely requires it to be present in the
executable.

You're right, that was probably not a good idea.

- Andrew
--
Advertising may be described as the science of arresting the human
intelligence long enough to get money from it. -- Stephen Leacock

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

FYI I'm working on a few more changes. Fixing a mistake in dbLockCleanupRecords() and adding testdbGetFieldEqual() to complement testdbPutFieldOk(). I'll mark as Approved when I'm done.

Also, I have some test code for dbPutFieldLink(). Should I include the present (working) version in this branch?

review: Needs Fixing
12512. By mdavidsaver

dbLock: fix dbLockCleanupRecords

all lockRecord s allocated in one block. oops.

12513. By mdavidsaver

dbUnitTest: add testdbGetFieldEqual()

12514. By mdavidsaver

dbPutLinkTest: test link string parsing

Test parsing to CONTANT and DB_LINK.
dbCa isn't initialized, so no test for CA_LINK.

12515. By mdavidsaver

dbPutLinkTest: set # of tests

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Ok, thats it. Barring bugs or futher requests, this is done.

I decided to include dbPutLinkTest, which is stripped down since testing of CA links won't work yet. dbCa isn't being initialized, and probably shouldn't be as this will start a CA client.

review: Approve
Revision history for this message
Andrew Johnson (anj) wrote :

On 07/11/2014 04:06 PM, mdavidsaver wrote:
> Ok, thats it. Barring bugs or futher requests, this is done.

The dbLockCleanupRecords() routine looks a little strange; it appears to
call free() once, on prec->lset of first record of the /second/ record
type. Now I know that dbLockInitRecords() only calls dbCalloc() once, so
all of the lockRecord (prec->lset) objects get allocated in a single
block, but the address of that big block is going to be the first record
instance of the first record type that actually has any records loaded.
I think you still need to loop through the record types, but you can
exit it as soon as you find the first record instance.

It seems to me that dbLockCleanupRecords() should also be emptying the
lockSetList[] lists, which contain lockSet objects. I admit I have no
idea what the differences are between the three different lists, but I
can see that there are calls to dbCalloc() in dbLock.c that don't appear
to an have associated call to free() anywhere.

> I decided to include dbPutLinkTest, which is stripped down since
> testing of CA links won't work yet. dbCa isn't being initialized,
> and probably shouldn't be as this will start a CA client.

That's fine. There are several input links in dbCommon.dbd that you
could have used for testing (e.g. SDIS) though, you didn't have to add
another link to the xRecord type.

- Andrew
--
Advertising may be described as the science of arresting the human
intelligence long enough to get money from it. -- Stephen Leacock

Revision history for this message
J. Lewis Muir (jlmuir) wrote :

On 7/11/14, 5:09 PM, Andrew Johnson wrote:
> The dbLockCleanupRecords() routine looks a little strange; it appears

Is "Cleanup" in the function name "dbLockCleanupRecords" a noun? The
camel case suggests that, but looking briefly at the function, it looks
like its a verb? If its a verb, I think the "u" should be upper case
(i.e. "dbLockCleanUpRecords") since "cleanup" is a noun, and "clean up"
is a verb.

Or maybe there's already a precedent spelling in existing code, and
"dbLockCleanupRecords" is just following that?

Lewis

12516. By mdavidsaver

dbLock: another fix to dbLockCleanupRecords()

12517. By mdavidsaver

dbLock: free lockSets

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> On 07/11/2014 04:06 PM, mdavidsaver wrote:
> > Ok, thats it. Barring bugs or futher requests, this is done.
>
> The dbLockCleanupRecords() routine looks a little strange;

That's because I copy+pasted the wrong function. Replaced dbNextRecordType() with dbFirstRecord().

...
> It seems to me that dbLockCleanupRecords() should also be emptying the
> lockSetList[] lists, which contain lockSet objects.

Added.

Revision history for this message
Andrew Johnson (anj) wrote :

On 07/11/2014 06:01 PM, mdavidsaver wrote:
>> On 07/11/2014 04:06 PM, mdavidsaver wrote:
>>> Ok, thats it. Barring bugs or futher requests, this is done.
>>
>> The dbLockCleanupRecords() routine looks a little strange;
>
> That's because I copy+pasted the wrong function. Replaced dbNextRecordType() with dbFirstRecord().

Ok but don't you still need the loop? There may be no instances of the
first record type present...

--
Advertising may be described as the science of arresting the human
intelligence long enough to get money from it. -- Stephen Leacock

12518. By mdavidsaver

dbLock: yet another fix to dbLockCleanupRecords()

track the lockRecord allocation

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> Ok but don't you still need the loop? There may be no instances of the
> first record type present...

Right, or it could be an alias. So this is clearly an error prone strategy. It is simpler just to keep track of the originally allocated pointer.

12519. By mdavidsaver

remove unused

Revision history for this message
Andrew Johnson (anj) wrote :

Ralph, any comments before we merge this?

Revision history for this message
Ralph Lange (ralph-lange) wrote :

No issues - I like the current state of this patch.

(Especially the "iocVirgin" state - can't get the Madonna song out of my head now: "Run for the very first time...")

review: Approve (close look at diff)
12520. By Andrew Johnson

Suppress errlog output of expected warning messages.

Revision history for this message
Andrew Johnson (anj) wrote :

Merging...

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/ioc/as/asDbLib.c'
2--- src/ioc/as/asDbLib.c 2012-10-01 05:54:10 +0000
3+++ src/ioc/as/asDbLib.c 2014-07-23 21:19:10 +0000
4@@ -152,6 +152,15 @@
5 return(asInitCommon());
6 }
7
8+int asShutdown(void) {
9+ volatile ASBASE *pbase = pasbase;
10+ pasbase = NULL;
11+ firstTime = TRUE;
12+ if(pbase)
13+ asFreeAll((ASBASE*)pbase);
14+ return 0;
15+}
16+
17 static void wdCallback(void *arg)
18 {
19 ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg;
20
21=== modified file 'src/ioc/as/asDbLib.h'
22--- src/ioc/as/asDbLib.h 2012-07-15 21:08:50 +0000
23+++ src/ioc/as/asDbLib.h 2014-07-23 21:19:10 +0000
24@@ -32,6 +32,7 @@
25 epicsShareFunc int asSetSubstitutions(const char *substitutions);
26 epicsShareFunc int asInit(void);
27 epicsShareFunc int asInitAsyn(ASDBCALLBACK *pcallback);
28+epicsShareFunc int asShutdown(void);
29 epicsShareFunc int asDbGetAsl(struct dbChannel *chan);
30 epicsShareFunc void * asDbGetMemberPvt(struct dbChannel *chan);
31 epicsShareFunc int asdbdump(void);
32
33=== modified file 'src/ioc/db/Makefile'
34--- src/ioc/db/Makefile 2012-11-29 18:53:21 +0000
35+++ src/ioc/db/Makefile 2014-07-23 21:19:10 +0000
36@@ -37,6 +37,7 @@
37 INC += dbState.h
38 INC += db_access_routines.h
39 INC += db_convert.h
40+INC += dbUnitTest.h
41
42 # Generate menuGlobal.dbd automatically
43 DBD += menuGlobal.dbd
44@@ -86,4 +87,4 @@
45 dbCore_SRCS += dbIocRegister.c
46 dbCore_SRCS += chfPlugin.c
47 dbCore_SRCS += dbState.c
48-
49+dbCore_SRCS += dbUnitTest.c
50
51=== modified file 'src/ioc/db/callback.c'
52--- src/ioc/db/callback.c 2012-05-04 22:34:48 +0000
53+++ src/ioc/db/callback.c 2014-07-23 21:19:10 +0000
54@@ -43,7 +43,6 @@
55 #include "callback.h"
56
57
58-static epicsThreadOnceId callbackOnceFlag = EPICS_THREAD_ONCE_INIT;
59 static int callbackQueueSize = 2000;
60 static epicsEventId callbackSem[NUM_CALLBACK_PRIORITIES];
61 static epicsRingPointerId callbackQ[NUM_CALLBACK_PRIORITIES];
62@@ -53,6 +52,8 @@
63 static epicsTimerQueueId timerQueue;
64
65 /* Shutdown handling */
66+enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
67+static volatile enum ctl cbCtl;
68 static epicsEventId startStopEvent;
69 static void *exitCallback;
70
71@@ -70,7 +71,7 @@
72
73 int callbackSetQueueSize(int size)
74 {
75- if (callbackOnceFlag != EPICS_THREAD_ONCE_INIT) {
76+ if (startStopEvent) {
77 errlogPrintf("Callback system already initialized\n");
78 return -1;
79 }
80@@ -101,24 +102,36 @@
81 epicsEventSignal(startStopEvent);
82 }
83
84-static void callbackShutdown(void *arg)
85+void callbackShutdown(void)
86 {
87 int i;
88
89+ if (cbCtl == ctlExit) return;
90+ cbCtl = ctlExit;
91+
92 for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
93 int lockKey = epicsInterruptLock();
94 int ok = epicsRingPointerPush(callbackQ[i], &exitCallback);
95 epicsInterruptUnlock(lockKey);
96 epicsEventSignal(callbackSem[i]);
97 if (ok) epicsEventWait(startStopEvent);
98+ epicsEventDestroy(callbackSem[i]);
99+ epicsRingPointerDelete(callbackQ[i]);
100 }
101+ epicsTimerQueueRelease(timerQueue);
102+ epicsEventDestroy(startStopEvent);
103+ startStopEvent = NULL;
104 }
105
106-static void callbackInitOnce(void *arg)
107+void callbackInit(void)
108 {
109 int i;
110
111+ if(startStopEvent)
112+ return;
113+
114 startStopEvent = epicsEventMustCreate(epicsEventEmpty);
115+ cbCtl = ctlRun;
116 timerQueue = epicsTimerQueueAllocate(0,epicsThreadPriorityScanHigh);
117 for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
118 epicsThreadId tid;
119@@ -137,12 +150,6 @@
120 else
121 epicsEventWait(startStopEvent);
122 }
123- epicsAtExit(callbackShutdown, NULL);
124-}
125-
126-void callbackInit(void)
127-{
128- epicsThreadOnce(&callbackOnceFlag, callbackInitOnce, NULL);
129 }
130
131 /* This routine can be called from interrupt context */
132
133=== modified file 'src/ioc/db/callback.h'
134--- src/ioc/db/callback.h 2010-10-05 19:27:37 +0000
135+++ src/ioc/db/callback.h 2014-07-23 21:19:10 +0000
136@@ -57,6 +57,7 @@
137
138 epicsShareFunc void callbackInit(void);
139 epicsShareFunc void callbackRequest(CALLBACK *pCallback);
140+epicsShareFunc void callbackShutdown(void);
141 epicsShareFunc void callbackSetProcess(
142 CALLBACK *pcallback, int Priority, void *pRec);
143 epicsShareFunc void callbackRequestProcessCallback(
144
145=== modified file 'src/ioc/db/dbBkpt.c'
146--- src/ioc/db/dbBkpt.c 2014-06-04 19:18:43 +0000
147+++ src/ioc/db/dbBkpt.c 2014-07-23 21:19:10 +0000
148@@ -61,6 +61,7 @@
149 #include "errMdef.h"
150 #include "recSup.h"
151 #include "special.h"
152+#include "epicsExit.h"
153 #define epicsExportSharedSymbols
154 #include "dbAddr.h"
155 #include "dbAccessDefs.h"
156@@ -250,7 +251,11 @@
157 return(0);
158 }
159
160-
161
162+static void dbBkptExit(void *junk) {
163+ epicsMutexDestroy(bkpt_stack_sem);
164+ bkpt_stack_sem = NULL;
165+}
166+
167 /*
168 * Initialise the breakpoint stack
169 */
170@@ -259,6 +264,7 @@
171 if (! bkpt_stack_sem) {
172 bkpt_stack_sem = epicsMutexMustCreate();
173 lset_stack_count = 0;
174+ epicsAtExit(dbBkptExit, NULL);
175 }
176 }
177
178
179=== modified file 'src/ioc/db/dbCa.c'
180--- src/ioc/db/dbCa.c 2014-06-04 19:18:43 +0000
181+++ src/ioc/db/dbCa.c 2014-07-23 21:19:10 +0000
182@@ -173,15 +173,22 @@
183 dbScanUnlock(pdbCommon);
184 }
185
186-static void dbCaShutdown(void *arg)
187+void dbCaShutdown(void)
188 {
189- if (dbCaCtl == ctlRun) {
190+ if (dbCaCtl == ctlRun || dbCaCtl == ctlPause) {
191 dbCaCtl = ctlExit;
192 epicsEventSignal(workListEvent);
193 epicsEventMustWait(startStopEvent);
194+ epicsEventDestroy(startStopEvent);
195+ epicsEventDestroy(workListEvent);
196 }
197 }
198
199+static void dbCaExit(void *arg)
200+{
201+ dbCaShutdown();
202+}
203+
204 void dbCaLinkInit(void)
205 {
206 dbServiceIOInit();
207@@ -194,19 +201,23 @@
208 epicsThreadGetStackSize(epicsThreadStackBig),
209 dbCaTask, NULL);
210 epicsEventMustWait(startStopEvent);
211- epicsAtExit(dbCaShutdown, NULL);
212+ epicsAtExit(dbCaExit, NULL);
213 }
214
215 void dbCaRun(void)
216 {
217- dbCaCtl = ctlRun;
218- epicsEventSignal(workListEvent);
219+ if (dbCaCtl == ctlPause) {
220+ dbCaCtl = ctlRun;
221+ epicsEventSignal(workListEvent);
222+ }
223 }
224
225 void dbCaPause(void)
226 {
227- dbCaCtl = ctlPause;
228- epicsEventSignal(workListEvent);
229+ if (dbCaCtl == ctlRun) {
230+ dbCaCtl = ctlPause;
231+ epicsEventSignal(workListEvent);
232+ }
233 }
234
235 void dbCaAddLinkCallback(struct link *plink,
236
237=== modified file 'src/ioc/db/dbCa.h'
238--- src/ioc/db/dbCa.h 2012-04-27 17:21:47 +0000
239+++ src/ioc/db/dbCa.h 2014-07-23 21:19:10 +0000
240@@ -26,6 +26,7 @@
241 epicsShareFunc void dbCaLinkInit(void);
242 epicsShareFunc void dbCaRun(void);
243 epicsShareFunc void dbCaPause(void);
244+epicsShareFunc void dbCaShutdown(void);
245
246 epicsShareFunc void dbCaAddLinkCallback(struct link *plink,
247 dbCaCallback connect, dbCaCallback monitor, void *userPvt);
248
249=== modified file 'src/ioc/db/dbChannel.c'
250--- src/ioc/db/dbChannel.c 2014-06-04 13:56:51 +0000
251+++ src/ioc/db/dbChannel.c 2014-07-23 21:19:10 +0000
252@@ -20,6 +20,7 @@
253 #include "cantProceed.h"
254 #include "epicsAssert.h"
255 #include "epicsString.h"
256+#include "epicsExit.h"
257 #include "errlog.h"
258 #include "freeList.h"
259 #include "gpHash.h"
260@@ -52,16 +53,23 @@
261 static void *chFilterFreeList;
262 static void *dbchStringFreeList;
263
264+static void dbChannelExit(void* junk)
265+{
266+ freeListCleanup(dbChannelFreeList);
267+ freeListCleanup(chFilterFreeList);
268+ freeListCleanup(dbchStringFreeList);
269+ dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL;
270+}
271+
272 void dbChannelInit (void)
273 {
274- static int done = 0;
275+ if(dbChannelFreeList)
276+ return;
277
278- if (!done) {
279- done = 1;
280- freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);
281- freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);
282- freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
283- }
284+ freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);
285+ freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);
286+ freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
287+ epicsAtExit(dbChannelExit, NULL);
288 }
289
290 static void chf_value(parseContext *parser, parse_result *presult)
291
292=== modified file 'src/ioc/db/dbLock.c'
293--- src/ioc/db/dbLock.c 2012-07-15 21:08:50 +0000
294+++ src/ioc/db/dbLock.c 2014-07-23 21:19:10 +0000
295@@ -54,6 +54,7 @@
296 #include "epicsMutex.h"
297 #include "epicsThread.h"
298 #include "epicsAssert.h"
299+#include "epicsExit.h"
300 #include "cantProceed.h"
301 #include "ellLib.h"
302 #define epicsExportSharedSymbols
303@@ -107,7 +108,16 @@
304 lockSet *plockSet;
305 dbCommon *precord;
306 } lockRecord;
307-
308
309+
310+static void dbLockExit(void *junk)
311+{
312+ epicsMutexDestroy(globalLock);
313+ epicsMutexDestroy(lockSetModifyLock);
314+ globalLock = NULL;
315+ lockSetModifyLock = NULL;
316+ dbLockIsInitialized = FALSE;
317+}
318+
319 /*private routines */
320 static void dbLockInitialize(void)
321 {
322@@ -118,6 +128,7 @@
323 globalLock = epicsMutexMustCreate();
324 lockSetModifyLock = epicsMutexMustCreate();
325 dbLockIsInitialized = TRUE;
326+ epicsAtExit(dbLockExit,NULL);
327 }
328
329 static lockSet * allocLockSet(
330@@ -315,7 +326,9 @@
331 epicsMutexUnlock(lockSetModifyLock);
332 return;
333 }
334-
335
336+
337+static lockRecord *lockRecordAlloc;
338+
339 void dbLockInitRecords(dbBase *pdbbase)
340 {
341 int link;
342@@ -336,7 +349,7 @@
343 - pdbRecordType->no_aliases;
344 }
345 /*Allocate all of them at once */
346- plockRecord = dbCalloc(nrecords,sizeof(lockRecord));
347+ lockRecordAlloc = plockRecord = dbCalloc(nrecords,sizeof(lockRecord));
348 for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
349 pdbRecordType;
350 pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
351@@ -377,7 +390,34 @@
352 }
353 }
354 }
355-
356
357+
358+void dbLockCleanupRecords(dbBase *pdbbase)
359+{
360+ ELLNODE *cur;
361+
362+ free(lockRecordAlloc);
363+ lockRecordAlloc = NULL;
364+
365+ /* free lockSets */
366+ /* ensure no lockSets are locked for re-compute */
367+ assert(ellCount(&lockSetList[listTypeRecordLock])==0);
368+ /* move allocated locks back to the free list */
369+ while((cur=ellGet(&lockSetList[listTypeScanLock]))!=NULL)
370+ {
371+ lockSet *pset = CONTAINER(cur, lockSet, node);
372+ assert(pset->state == lockSetStateFree); /* lock not held */
373+ pset->type = listTypeFree;
374+ ellAdd(&lockSetList[listTypeFree],&pset->node);
375+ }
376+ /* clean up free list */
377+ while((cur=ellGet(&lockSetList[listTypeFree]))!=NULL)
378+ {
379+ lockSet *pset = CONTAINER(cur, lockSet, node);
380+ epicsMutexDestroy(pset->lock);
381+ free(pset);
382+ }
383+}
384+
385 void dbLockSetMerge(dbCommon *pfirst,dbCommon *psecond)
386 {
387 lockRecord *p1lockRecord = pfirst->lset;
388
389=== modified file 'src/ioc/db/dbLock.h'
390--- src/ioc/db/dbLock.h 2012-07-15 21:08:50 +0000
391+++ src/ioc/db/dbLock.h 2014-07-23 21:19:10 +0000
392@@ -28,6 +28,7 @@
393 struct dbCommon *precord);
394
395 epicsShareFunc void dbLockInitRecords(struct dbBase *pdbbase);
396+epicsShareFunc void dbLockCleanupRecords(struct dbBase *pdbbase);
397 epicsShareFunc void dbLockSetMerge(
398 struct dbCommon *pfirst,struct dbCommon *psecond);
399 epicsShareFunc void dbLockSetSplit(struct dbCommon *psource);
400
401=== modified file 'src/ioc/db/dbNotify.c'
402--- src/ioc/db/dbNotify.c 2012-07-15 21:08:50 +0000
403+++ src/ioc/db/dbNotify.c 2014-07-23 21:19:10 +0000
404@@ -44,6 +44,7 @@
405 #include "recGbl.h"
406 #include "dbNotify.h"
407 #include "epicsTime.h"
408+#include "epicsExit.h"
409 #include "cantProceed.h"
410
411 /*notify state values */
412@@ -298,6 +299,14 @@
413 callDone(precord, ppn);
414 }
415
416+static void dbProcessNotifyExit(void* junk)
417+{
418+ assert(ellCount(&pnotifyGlobal->freeList)==0);
419+ epicsMutexDestroy(pnotifyGlobal->lock);
420+ free(pnotifyGlobal);
421+ pnotifyGlobal = NULL;
422+}
423+
424 void dbProcessNotifyInit(void)
425 {
426 if (pnotifyGlobal)
427@@ -305,6 +314,7 @@
428 pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal));
429 pnotifyGlobal->lock = epicsMutexMustCreate();
430 ellInit(&pnotifyGlobal->freeList);
431+ epicsAtExit(dbProcessNotifyExit, NULL);
432 }
433
434 void dbProcessNotify(processNotify *ppn)
435
436=== modified file 'src/ioc/db/dbScan.c'
437--- src/ioc/db/dbScan.c 2013-12-17 18:54:04 +0000
438+++ src/ioc/db/dbScan.c 2014-07-23 21:19:10 +0000
439@@ -3,6 +3,8 @@
440 * National Laboratory.
441 * Copyright (c) 2002 The Regents of the University of California, as
442 * Operator of Los Alamos National Laboratory.
443+* Copyright (c) 2013 Helmholtz-Zentrum Berlin
444+* für Materialien und Energie GmbH.
445 * EPICS BASE is distributed subject to a Software License Agreement found
446 * in file LICENSE that is included with this distribution.
447 \*************************************************************************/
448@@ -132,6 +134,7 @@
449 static void initOnce(void);
450 static void periodicTask(void *arg);
451 static void initPeriodic(void);
452+static void deletePeriodic(void);
453 static void spawnPeriodic(int ind);
454 static void initEvent(void);
455 static void eventCallback(CALLBACK *pcallback);
456@@ -142,10 +145,13 @@
457 static void addToList(struct dbCommon *precord, scan_list *psl);
458 static void deleteFromList(struct dbCommon *precord, scan_list *psl);
459
460
461-static void scanShutdown(void *arg)
462+void scanShutdown(void)
463 {
464 int i;
465
466+ if (scanCtl == ctlExit) return;
467+ scanCtl = ctlExit;
468+
469 interruptAccept = FALSE;
470
471 for (i = 0; i < nPeriodic; i++) {
472@@ -156,6 +162,18 @@
473
474 scanOnce((dbCommon *)&exitOnce);
475 epicsEventWait(startStopEvent);
476+
477+ deletePeriodic();
478+
479+ epicsRingPointerDelete(onceQ);
480+
481+ epicsEventDestroy(startStopEvent);
482+ epicsEventDestroy(onceSem);
483+ onceSem = startStopEvent = NULL;
484+
485+ free(periodicTaskId);
486+ papPeriodic = NULL;
487+ periodicTaskId = NULL;
488 }
489
490 long scanInit(void)
491@@ -172,7 +190,6 @@
492 for (i = 0; i < nPeriodic; i++)
493 spawnPeriodic(i);
494
495- epicsAtExit(scanShutdown, NULL);
496 return 0;
497 }
498
499@@ -696,6 +713,22 @@
500 }
501 }
502
503+static void deletePeriodic(void)
504+{
505+ int i;
506+
507+ for (i = 0; i < nPeriodic; i++) {
508+ periodic_scan_list *ppsl = papPeriodic[i];
509+ ellFree(&ppsl->scan_list.list);
510+ epicsEventDestroy(ppsl->loopEvent);
511+ epicsMutexDestroy(ppsl->scan_list.lock);
512+ free(ppsl);
513+ }
514+
515+ free(papPeriodic);
516+ papPeriodic = NULL;
517+}
518+
519 static void spawnPeriodic(int ind)
520 {
521 periodic_scan_list *ppsl;
522@@ -802,23 +835,25 @@
523 {
524 dbRecordType *pdbRecordType;
525
526- /*Look for first record*/
527 for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
528 pdbRecordType;
529 pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
530 dbRecordNode *pdbRecordNode;
531+
532 for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList);
533 pdbRecordNode;
534 pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) {
535 dbCommon *precord = pdbRecordNode->precord;
536+
537 if (!precord->name[0] ||
538 pdbRecordNode->flags & DBRN_FLAGS_ISALIAS)
539 continue;
540+
541 scanAdd(precord);
542 }
543 }
544 }
545-
546
547+
548 static void addToList(struct dbCommon *precord, scan_list *psl)
549 {
550 scan_element *pse, *ptemp;
551
552=== modified file 'src/ioc/db/dbScan.h'
553--- src/ioc/db/dbScan.h 2012-04-10 22:10:50 +0000
554+++ src/ioc/db/dbScan.h 2014-07-23 21:19:10 +0000
555@@ -44,6 +44,7 @@
556 epicsShareFunc long scanInit(void);
557 epicsShareFunc void scanRun(void);
558 epicsShareFunc void scanPause(void);
559+epicsShareFunc void scanShutdown(void);
560
561 epicsShareFunc EVENTPVT eventNameToHandle(const char* event);
562 epicsShareFunc void postEvent(EVENTPVT epvt);
563
564=== added file 'src/ioc/db/dbUnitTest.c'
565--- src/ioc/db/dbUnitTest.c 1970-01-01 00:00:00 +0000
566+++ src/ioc/db/dbUnitTest.c 2014-07-23 21:19:10 +0000
567@@ -0,0 +1,201 @@
568+/*************************************************************************\
569+* Copyright (c) 2013 Brookhaven National Laboratory.
570+* Copyright (c) 2013 ITER Organization.
571+* EPICS BASE is distributed subject to a Software License Agreement found
572+* in file LICENSE that is included with this distribution.
573+ \*************************************************************************/
574+
575+/*
576+ * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
577+ * Ralph Lange <Ralph.Lange@gmx.de>
578+ */
579+
580+#include <string.h>
581+
582+#include "epicsUnitTest.h"
583+#include "osiFileName.h"
584+#include "dbmf.h"
585+#include "registry.h"
586+#define epicsExportSharedSymbols
587+#include "iocInit.h"
588+#include "initHooks.h"
589+#include "dbBase.h"
590+#include "dbAccess.h"
591+#include "dbStaticLib.h"
592+
593+#include "dbUnitTest.h"
594+
595+void testdbPrepare(void)
596+{
597+ /* No-op at the moment */
598+}
599+
600+void testdbReadDatabase(const char* file,
601+ const char* path,
602+ const char* substitutions)
603+{
604+ if(!path)
605+ path = "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
606+ "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common";
607+ if(dbReadDatabase(&pdbbase, file, path, substitutions))
608+ testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)",
609+ file, path, substitutions);
610+}
611+
612+void testIocInitOk(void)
613+{
614+ if(iocBuildIsolated() || iocRun())
615+ testAbort("Failed to start up test database");
616+}
617+
618+void testIocShutdownOk(void)
619+{
620+ if(iocShutdown())
621+ testAbort("Failed to shutdown test database");
622+}
623+
624+void testdbCleanup(void)
625+{
626+ dbFreeBase(pdbbase);
627+ initHookFree();
628+ registryFree();
629+ pdbbase = NULL;
630+ dbmfFreeChunks();
631+}
632+
633+union anybuf {
634+ epicsAny val;
635+ char valStr[MAX_STRING_SIZE];
636+ char bytes[sizeof(epicsAny)];
637+};
638+
639+long testdbVPutField(const char* pv, short dbrType, va_list ap)
640+{
641+ DBADDR addr;
642+ union anybuf pod;
643+
644+ if(dbNameToAddr(pv, &addr)) {
645+ testFail("Missing PV %s", pv);
646+ return S_dbLib_recNotFound;
647+ }
648+
649+ switch(dbrType) {
650+ case DBR_STRING: {
651+ const char *uarg = va_arg(ap,char*);
652+ strncpy(pod.valStr, uarg, sizeof(pod.valStr));
653+ pod.valStr[sizeof(pod.valStr)-1] = '\0';
654+ return dbPutField(&addr, dbrType, pod.valStr, 1);
655+ }
656+
657+ /* The Type parameter takes into consideration
658+ * the C language rules for promotion of argument types
659+ * in variadic functions.
660+ */
661+#define OP(DBR,Type,mem) case DBR: {pod.val.mem = va_arg(ap,Type); break;}
662+ OP(DBR_CHAR, int, int8);
663+ OP(DBR_UCHAR, int, uInt8);
664+ OP(DBR_SHORT, int, int16);
665+ OP(DBR_USHORT, int, uInt16);
666+ OP(DBR_LONG, int, int32);
667+ OP(DBR_ULONG, unsigned int, uInt32);
668+ OP(DBR_FLOAT, double, float32);
669+ OP(DBR_DOUBLE, double, float64);
670+ OP(DBR_ENUM, int, enum16);
671+#undef OP
672+ default:
673+ testFail("invalid DBR: dbPutField(\"%s\", %d, ...)",
674+ addr.precord->name, dbrType);
675+ return S_db_badDbrtype;
676+ }
677+
678+ return dbPutField(&addr, dbrType, pod.bytes, 1);
679+}
680+
681+void testdbPutFieldOk(const char* pv, short dbrType, ...)
682+{
683+ long ret;
684+ va_list ap;
685+
686+ va_start(ap, dbrType);
687+ ret = testdbVPutField(pv, dbrType, ap);
688+ va_end(ap);
689+
690+ testOk(ret==0, "dbPutField(%s, %d, ...) == %ld", pv, dbrType, ret);
691+}
692+
693+void testdbPutFieldFail(long status, const char* pv, short dbrType, ...)
694+{
695+ long ret;
696+ va_list ap;
697+
698+ va_start(ap, dbrType);
699+ ret = testdbVPutField(pv, dbrType, ap);
700+ va_end(ap);
701+
702+ if(ret==status)
703+ testPass("dbPutField(\"%s\", %d, ...) == %ld", pv, dbrType, status);
704+ else
705+ testFail("dbPutField(\"%s\", %d, ...) != %ld (%ld)", pv, dbrType, status, ret);
706+}
707+
708+void testdbGetFieldEqual(const char* pv, short dbrType, ...)
709+{
710+ va_list ap;
711+
712+ va_start(ap, dbrType);
713+ testdbVGetFieldEqual(pv, dbrType, ap);
714+ va_end(ap);
715+}
716+
717+void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
718+{
719+ DBADDR addr;
720+ long nReq = 1;
721+ union anybuf pod;
722+ long status;
723+
724+ if(dbNameToAddr(pv, &addr)) {
725+ testFail("Missing PV %s", pv);
726+ return;
727+ }
728+
729+ status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
730+ if(status) {
731+ testFail("dbGetField(\"%s\",%d,...) returns %ld", pv, dbrType, status);
732+ return;
733+ }
734+
735+ switch(dbrType) {
736+ case DBR_STRING: {
737+ const char *expect = va_arg(ap, char*);
738+ testOk(strcmp(expect, pod.valStr)==0,
739+ "dbGetField(\"%s\", %d) -> \"%s\" == \"%s\"",
740+ pv, dbrType, expect, pod.valStr);
741+ break;
742+ }
743+#define OP(DBR,Type,mem,pat) case DBR: {Type expect = va_arg(ap,Type); \
744+ testOk(expect==pod.val.mem, "dbGetField(\"%s\", %d) -> " pat " == " pat, \
745+ pv, dbrType, expect, (Type)pod.val.mem); break;}
746+
747+ OP(DBR_CHAR, int, int8, "%d");
748+ OP(DBR_UCHAR, int, uInt8, "%d");
749+ OP(DBR_SHORT, int, int16, "%d");
750+ OP(DBR_USHORT, int, uInt16, "%d");
751+ OP(DBR_LONG, int, int32, "%d");
752+ OP(DBR_ULONG, unsigned int, uInt32, "%u");
753+ OP(DBR_FLOAT, double, float32, "%e");
754+ OP(DBR_DOUBLE, double, float64, "%e");
755+ OP(DBR_ENUM, int, enum16, "%d");
756+#undef OP
757+ }
758+}
759+
760+dbCommon* testdbRecordPtr(const char* pv)
761+{
762+ DBADDR addr;
763+
764+ if(dbNameToAddr(pv, &addr))
765+ testAbort("Missing record %s", pv);
766+
767+ return addr.precord;
768+}
769
770=== added file 'src/ioc/db/dbUnitTest.h'
771--- src/ioc/db/dbUnitTest.h 1970-01-01 00:00:00 +0000
772+++ src/ioc/db/dbUnitTest.h 2014-07-23 21:19:10 +0000
773@@ -0,0 +1,64 @@
774+/*************************************************************************\
775+* Copyright (c) 2013 Brookhaven National Laboratory.
776+* Copyright (c) 2013 ITER Organization.
777+* EPICS BASE is distributed subject to a Software License Agreement found
778+* in file LICENSE that is included with this distribution.
779+ \*************************************************************************/
780+
781+/*
782+ * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
783+ * Ralph Lange <Ralph.Lange@gmx.de>
784+ */
785+
786+#ifndef EPICSUNITTESTDB_H
787+#define EPICSUNITTESTDB_H
788+
789+#include <stdarg.h>
790+
791+#include "epicsUnitTest.h"
792+#include "dbAddr.h"
793+#include "dbCommon.h"
794+
795+#include "shareLib.h"
796+
797+#ifdef __cplusplus
798+extern "C" {
799+#endif
800+
801+epicsShareFunc void testdbPrepare(void);
802+epicsShareFunc void testdbReadDatabase(const char* file,
803+ const char* path,
804+ const char* substitutions);
805+epicsShareFunc void testIocInitOk(void);
806+epicsShareFunc void testIocShutdownOk(void);
807+epicsShareFunc void testdbCleanup(void);
808+
809+/* Correct argument types must be used with this var-arg function!
810+ * Doing otherwise will result in corruption of argument values!
811+ *
812+ * int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG
813+ * unsigned int for DBR_ULONG
814+ * double for DBR_FLOAT and DBR_DOUBLE
815+ * const char* for DBR_STRING
816+ *
817+ * eg.
818+ * testdbPutFieldOk("pvname", DBF_ULONG, (unsigned int)5);
819+ * testdbPutFieldOk("pvname", DBF_FLOAT, (double)4.1);
820+ * testdbPutFieldOk("pvname", DBF_STRING, "hello world");
821+ */
822+epicsShareFunc void testdbPutFieldOk(const char* pv, short dbrType, ...);
823+/* Tests for put failure */
824+epicsShareFunc void testdbPutFieldFail(long status, const char* pv, short dbrType, ...);
825+
826+epicsShareFunc long testdbVPutField(const char* pv, short dbrType, va_list ap);
827+
828+epicsShareFunc void testdbGetFieldEqual(const char* pv, short dbrType, ...);
829+epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap);
830+
831+epicsShareFunc dbCommon* testdbRecordPtr(const char* pv);
832+
833+#ifdef __cplusplus
834+}
835+#endif
836+
837+#endif // EPICSUNITTESTDB_H
838
839=== modified file 'src/ioc/db/initHooks.c'
840--- src/ioc/db/initHooks.c 2010-10-05 19:27:37 +0000
841+++ src/ioc/db/initHooks.c 2014-07-23 21:19:10 +0000
842@@ -93,6 +93,14 @@
843 }
844 }
845
846+void initHookFree(void)
847+{
848+ initHookInit();
849+ epicsMutexMustLock(listLock);
850+ ellFree(&functionList);
851+ epicsMutexUnlock(listLock);
852+}
853+
854 /*
855 * Call any time you want to print out a state name.
856 */
857
858=== modified file 'src/ioc/db/initHooks.h'
859--- src/ioc/db/initHooks.h 2010-10-05 19:27:37 +0000
860+++ src/ioc/db/initHooks.h 2014-07-23 21:19:10 +0000
861@@ -60,6 +60,7 @@
862 epicsShareFunc int initHookRegister(initHookFunction func);
863 epicsShareFunc void initHookAnnounce(initHookState state);
864 epicsShareFunc const char *initHookName(int state);
865+epicsShareFunc void initHookFree(void);
866
867 #ifdef __cplusplus
868 }
869
870=== modified file 'src/ioc/db/test/Makefile'
871--- src/ioc/db/test/Makefile 2014-06-13 19:37:12 +0000
872+++ src/ioc/db/test/Makefile 2014-07-23 21:19:10 +0000
873@@ -10,11 +10,31 @@
874
875 include $(TOP)/configure/CONFIG
876
877-TESTLIBRARY = xRec
878-
879-xRec_SRCS = xRecord.c
880-
881-PROD_LIBS = xRec dbCore ca Com
882+TESTLIBRARY = dbTestIoc
883+
884+dbTestIoc_SRCS = xRecord.c
885+
886+TARGETS += $(COMMON_DIR)/dbTestIoc.dbd
887+dbTestIoc_DBD += menuGlobal.dbd
888+dbTestIoc_DBD += menuConvert.dbd
889+dbTestIoc_DBD += xRecord.dbd
890+TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
891+
892+testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
893+
894+PROD_LIBS = dbTestIoc dbCore ca Com
895+
896+TESTPROD_HOST += dbShutdownTest
897+dbShutdownTest_SRCS += dbShutdownTest.c
898+dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
899+testHarness_SRCS += dbShutdownTest.c
900+TESTS += dbShutdownTest
901+
902+TESTPROD_HOST += dbPutLinkTest
903+dbPutLinkTest_SRCS += dbPutLinkTest.c
904+dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
905+testHarness_SRCS += dbPutLinkTest.c
906+TESTS += dbPutLinkTest
907
908 TESTPROD_HOST += testdbConvert
909 testdbConvert_SRCS += testdbConvert.c
910@@ -31,34 +51,22 @@
911 testHarness_SRCS += dbStateTest.c
912 TESTS += dbStateTest
913
914-TARGETS += $(COMMON_DIR)/dbChannelTest.dbd
915-dbChannelTest_DBD += xRecord.dbd
916 TESTPROD_HOST += dbChannelTest
917 dbChannelTest_SRCS += dbChannelTest.c
918-dbChannelTest_SRCS += dbChannelTest_registerRecordDeviceDriver.cpp
919+dbChannelTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
920 testHarness_SRCS += dbChannelTest.c
921-testHarness_SRCS += dbChannelTest_registerRecordDeviceDriver.cpp
922-TESTFILES += $(COMMON_DIR)/dbChannelTest.dbd ../xRecord.db
923 TESTS += dbChannelTest
924
925-TARGETS += $(COMMON_DIR)/chfPluginTest.dbd
926-chfPluginTest_DBD += xRecord.dbd
927 TESTPROD_HOST += chfPluginTest
928 chfPluginTest_SRCS += chfPluginTest.c
929-chfPluginTest_SRCS += chfPluginTest_registerRecordDeviceDriver.cpp
930+chfPluginTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
931 testHarness_SRCS += chfPluginTest.c
932-testHarness_SRCS += chfPluginTest_registerRecordDeviceDriver.cpp
933-TESTFILES += $(COMMON_DIR)/chfPluginTest.dbd
934 TESTS += chfPluginTest
935
936-TARGETS += $(COMMON_DIR)/arrShorthandTest.dbd
937-arrShorthandTest_DBD += xRecord.dbd
938 TESTPROD_HOST += arrShorthandTest
939 arrShorthandTest_SRCS += arrShorthandTest.c
940-arrShorthandTest_SRCS += arrShorthandTest_registerRecordDeviceDriver.cpp
941+arrShorthandTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
942 testHarness_SRCS += arrShorthandTest.c
943-testHarness_SRCS += arrShorthandTest_registerRecordDeviceDriver.cpp
944-TESTFILES += $(COMMON_DIR)/arrShorthandTest.dbd
945 TESTS += arrShorthandTest
946
947 TESTPROD_HOST += benchdbConvert
948
949=== modified file 'src/ioc/db/test/arrShorthandTest.c'
950--- src/ioc/db/test/arrShorthandTest.c 2012-07-13 22:48:31 +0000
951+++ src/ioc/db/test/arrShorthandTest.c 2014-07-23 21:19:10 +0000
952@@ -77,7 +77,7 @@
953 testDiag("--------------------------------------------------------");
954 }
955
956-void arrShorthandTest_registerRecordDeviceDriver(struct dbBase *);
957+void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
958
959 MAIN(arrShorthandTest)
960 {
961@@ -88,12 +88,12 @@
962 db_init_events();
963 dbChannelInit();
964
965- if (dbReadDatabase(&pdbbase, "arrShorthandTest.dbd",
966+ if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
967 "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
968 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
969- testAbort("Database description 'arrShorthandTest.dbd' not found");
970+ testAbort("Database description 'dbTestIoc.dbd' not found");
971
972- arrShorthandTest_registerRecordDeviceDriver(pdbbase);
973+ dbTestIoc_registerRecordDeviceDriver(pdbbase);
974 if (dbReadDatabase(&pdbbase, "xRecord.db",
975 "." OSI_PATH_LIST_SEPARATOR "..", NULL))
976 testAbort("Test database 'xRecord.db' not found");
977
978=== modified file 'src/ioc/db/test/chfPluginTest.c'
979--- src/ioc/db/test/chfPluginTest.c 2014-06-04 09:55:40 +0000
980+++ src/ioc/db/test/chfPluginTest.c 2014-07-23 21:19:10 +0000
981@@ -481,7 +481,7 @@
982 testDiag("--------------------------------------------------------");
983 }
984
985-void chfPluginTest_registerRecordDeviceDriver(struct dbBase *);
986+void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
987
988 MAIN(chfPluginTest)
989 {
990@@ -508,12 +508,12 @@
991 testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0,
992 "Enum to string: invalid index");
993
994- if (dbReadDatabase(&pdbbase, "chfPluginTest.dbd",
995+ if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
996 "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
997 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
998- testAbort("Database description 'chfPluginTest.dbd' not found");
999+ testAbort("Database description 'dbTestIoc.dbd' not found");
1000
1001- chfPluginTest_registerRecordDeviceDriver(pdbbase);
1002+ dbTestIoc_registerRecordDeviceDriver(pdbbase);
1003 if (dbReadDatabase(&pdbbase, "xRecord.db",
1004 "." OSI_PATH_LIST_SEPARATOR "..", NULL))
1005 testAbort("Test database 'xRecord.db' not found");
1006
1007=== modified file 'src/ioc/db/test/dbChannelTest.c'
1008--- src/ioc/db/test/dbChannelTest.c 2012-07-13 22:48:31 +0000
1009+++ src/ioc/db/test/dbChannelTest.c 2014-07-23 21:19:10 +0000
1010@@ -21,6 +21,7 @@
1011 #include "epicsUnitTest.h"
1012 #include "testMain.h"
1013 #include "osiFileName.h"
1014+#include "errlog.h"
1015
1016 /* Expected call bit definitions */
1017 #define e_start 0x00000001
1018@@ -149,7 +150,7 @@
1019 p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array,
1020 c_open, c_reg_pre, c_reg_post, c_report, c_close };
1021
1022-void dbChannelTest_registerRecordDeviceDriver(struct dbBase *);
1023+void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
1024
1025 MAIN(testDbChannel) /* dbChannelTest is an API routine... */
1026 {
1027@@ -157,12 +158,12 @@
1028
1029 testPlan(66);
1030
1031- if (dbReadDatabase(&pdbbase, "dbChannelTest.dbd",
1032+ if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
1033 "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
1034 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
1035- testAbort("Database description 'dbChannelTest.dbd' not found");
1036+ testAbort("Database description 'dbTestIoc.dbd' not found");
1037
1038- dbChannelTest_registerRecordDeviceDriver(pdbbase);
1039+ dbTestIoc_registerRecordDeviceDriver(pdbbase);
1040 if (dbReadDatabase(&pdbbase, "xRecord.db",
1041 "." OSI_PATH_LIST_SEPARATOR "..", NULL))
1042 testAbort("Test database 'xRecord.db' not found");
1043@@ -199,7 +200,9 @@
1044 testOk(!dbChannelCreate("y"), "Create, bad record");
1045 testOk(!dbChannelCreate("x.NOFIELD"), "Create, bad field");
1046 testOk(!dbChannelCreate("x.{not-json}"), "Create, bad JSON");
1047+ eltc(0);
1048 testOk(!dbChannelCreate("x.{\"none\":null}"), "Create, bad filter");
1049+ eltc(1);
1050
1051 dbRegisterFilter("any", &testIf, NULL);
1052
1053
1054=== added file 'src/ioc/db/test/dbPutLinkTest.c'
1055--- src/ioc/db/test/dbPutLinkTest.c 1970-01-01 00:00:00 +0000
1056+++ src/ioc/db/test/dbPutLinkTest.c 2014-07-23 21:19:10 +0000
1057@@ -0,0 +1,110 @@
1058+/*************************************************************************\
1059+* Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven
1060+* National Laboratory.
1061+* EPICS BASE is distributed subject to a Software License Agreement found
1062+* in file LICENSE that is included with this distribution.
1063+ \*************************************************************************/
1064+
1065+/*
1066+ * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
1067+ */
1068+
1069+#include "string.h"
1070+
1071+#include "epicsString.h"
1072+#include "dbUnitTest.h"
1073+#include "epicsThread.h"
1074+#include "iocInit.h"
1075+#include "dbBase.h"
1076+#include "link.h"
1077+#include "dbAccess.h"
1078+#include "registry.h"
1079+#include "dbStaticLib.h"
1080+#include "osiFileName.h"
1081+#include "dbmf.h"
1082+#include "errlog.h"
1083+
1084+#include "xRecord.h"
1085+
1086+#include "testMain.h"
1087+
1088+void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
1089+
1090+static const struct testDataT {
1091+ const char *linkstring;
1092+ short linkType;
1093+ unsigned int pvlMask;
1094+ const char *linkback;
1095+} testSetData[] = {
1096+ {"", CONSTANT, 0},
1097+ {"0", CONSTANT, 0},
1098+ {"42", CONSTANT, 0},
1099+ {"x1", DB_LINK, 0, "x1 NPP NMS"},
1100+ {"x1.VAL", DB_LINK, 0, "x1.VAL NPP NMS"},
1101+ {"x1.TIME", DB_LINK, 0, "x1.TIME NPP NMS"},
1102+ {"x1 PP", DB_LINK, pvlOptPP, "x1 PP NMS"},
1103+ {"x1 PP MSS", DB_LINK, pvlOptPP|pvlOptMSS, "x1 PP MSS"},
1104+ {"x1 PPMSS", DB_LINK, pvlOptPP|pvlOptMSS, "x1 PP MSS"},
1105+ {"x1 PPMSI", DB_LINK, pvlOptPP|pvlOptMSI, "x1 PP MSI"},
1106+ /*TODO: testing doesn't support CA_LINK yet */
1107+ {NULL}
1108+};
1109+
1110+static void testSet(void)
1111+{
1112+ const struct testDataT *td = testSetData;
1113+ xRecord *prec;
1114+ DBLINK *plink;
1115+ testdbPrepare();
1116+
1117+ testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
1118+
1119+ dbTestIoc_registerRecordDeviceDriver(pdbbase);
1120+
1121+ testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
1122+
1123+ eltc(0);
1124+ testIocInitOk();
1125+ eltc(1);
1126+
1127+ prec = (xRecord*)testdbRecordPtr("x1");
1128+ plink = &prec->lnk;
1129+
1130+ for(;td->linkstring;td++) {
1131+
1132+ testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring);
1133+ if(td->linkback)
1134+ testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback);
1135+ else
1136+ testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring);
1137+ testOk1(plink->type==td->linkType);
1138+
1139+ if(plink->type==td->linkType) {
1140+ switch(td->linkType) {
1141+ case CONSTANT:
1142+ if(plink->value.constantStr)
1143+ testOk1(strcmp(plink->value.constantStr,td->linkstring)==0);
1144+ else if(td->linkstring[0]=='\0')
1145+ testPass("Empty String");
1146+ else
1147+ testFail("oops");
1148+ break;
1149+
1150+ case DB_LINK:
1151+ testOk1(plink->value.pv_link.pvlMask==td->pvlMask);
1152+ break;
1153+ }
1154+ }
1155+ }
1156+
1157+ testIocShutdownOk();
1158+
1159+ testdbCleanup();
1160+}
1161+
1162+MAIN(dbPutLinkTest)
1163+{
1164+ testPlan(40);
1165+ testSet();
1166+ return testDone();
1167+}
1168
1169=== added file 'src/ioc/db/test/dbPutLinkTest.db'
1170--- src/ioc/db/test/dbPutLinkTest.db 1970-01-01 00:00:00 +0000
1171+++ src/ioc/db/test/dbPutLinkTest.db 2014-07-23 21:19:10 +0000
1172@@ -0,0 +1,4 @@
1173+record(x, "x1") {}
1174+record(x, "x2") {}
1175+record(x, "x3") {}
1176+record(x, "x4") {}
1177
1178=== added file 'src/ioc/db/test/dbShutdownTest.c'
1179--- src/ioc/db/test/dbShutdownTest.c 1970-01-01 00:00:00 +0000
1180+++ src/ioc/db/test/dbShutdownTest.c 2014-07-23 21:19:10 +0000
1181@@ -0,0 +1,96 @@
1182+/*************************************************************************\
1183+* Copyright (c) 2013 Brookhaven National Laboratory.
1184+* Copyright (c) 2013 ITER Organization.
1185+* EPICS BASE is distributed subject to a Software License Agreement found
1186+* in file LICENSE that is included with this distribution.
1187+ \*************************************************************************/
1188+
1189+/*
1190+ * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
1191+ * Ralph Lange <Ralph.Lange@gmx.de>
1192+ */
1193+
1194+#include "epicsString.h"
1195+#include "dbUnitTest.h"
1196+#include "epicsThread.h"
1197+#include "iocInit.h"
1198+#include "dbBase.h"
1199+#include "dbAccess.h"
1200+#include "registry.h"
1201+#include "dbStaticLib.h"
1202+#include "osiFileName.h"
1203+#include "dbmf.h"
1204+#include "errlog.h"
1205+
1206+#include "testMain.h"
1207+
1208+void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
1209+
1210+static struct threadItem {
1211+ char *name;
1212+ char found;
1213+} commonThreads[] = {
1214+ { "errlog", 0 },
1215+ { "taskwd", 0 },
1216+ { "timerQueue", 0 },
1217+ { "cbLow", 0 },
1218+ { "scanOnce", 0 },
1219+ { NULL, 0 }
1220+};
1221+
1222+static
1223+void findCommonThread (epicsThreadId id) {
1224+ struct threadItem *thr;
1225+ char name[32];
1226+
1227+ epicsThreadGetName(id, name, 32);
1228+
1229+ for (thr = commonThreads; thr->name; thr++) {
1230+ if (epicsStrCaseCmp(thr->name, name) == 0) {
1231+ thr->found = 1;
1232+ }
1233+ }
1234+}
1235+
1236+static
1237+void checkCommonThreads (void) {
1238+ struct threadItem *thr;
1239+
1240+ for (thr = commonThreads; thr->name; thr++) {
1241+ testOk(thr->found, "Thread %s is running", thr->name);
1242+ thr->found = 0;
1243+ }
1244+}
1245+
1246+static
1247+void cycle(void) {
1248+
1249+ testdbPrepare();
1250+
1251+ testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
1252+
1253+ dbTestIoc_registerRecordDeviceDriver(pdbbase);
1254+
1255+ testdbReadDatabase("xRecord.db", NULL, NULL);
1256+
1257+ eltc(0);
1258+ testIocInitOk();
1259+ eltc(1);
1260+
1261+ epicsThreadMap(findCommonThread);
1262+ checkCommonThreads();
1263+
1264+ testIocShutdownOk();
1265+
1266+ testdbCleanup();
1267+}
1268+
1269+MAIN(dbShutdownTest)
1270+{
1271+ testPlan(10);
1272+
1273+ cycle();
1274+ cycle();
1275+
1276+ return testDone();
1277+}
1278
1279=== modified file 'src/ioc/db/test/epicsRunDbTests.c'
1280--- src/ioc/db/test/epicsRunDbTests.c 2014-06-13 19:37:11 +0000
1281+++ src/ioc/db/test/epicsRunDbTests.c 2014-07-23 21:19:10 +0000
1282@@ -19,6 +19,8 @@
1283 int testdbConvert(void);
1284 int callbackTest(void);
1285 int dbStateTest(void);
1286+int dbShutdownTest(void);
1287+int dbPutLinkTest(void);
1288 int testDbChannel(void);
1289 int chfPluginTest(void);
1290 int arrShorthandTest(void);
1291@@ -30,9 +32,11 @@
1292 runTest(testdbConvert);
1293 runTest(callbackTest);
1294 runTest(dbStateTest);
1295+ runTest(dbShutdownTest);
1296+ runTest(dbPutLinkTest);
1297 runTest(testDbChannel);
1298+ runTest(arrShorthandTest);
1299 runTest(chfPluginTest);
1300- runTest(arrShorthandTest);
1301
1302 dbmfFreeChunks();
1303
1304
1305=== added file 'src/ioc/db/test/sRecord.db'
1306--- src/ioc/db/test/sRecord.db 1970-01-01 00:00:00 +0000
1307+++ src/ioc/db/test/sRecord.db 2014-07-23 21:19:10 +0000
1308@@ -0,0 +1,1 @@
1309+record(ai, "somename") {}
1310
1311=== modified file 'src/ioc/db/test/xRecord.c'
1312--- src/ioc/db/test/xRecord.c 2012-06-29 15:09:56 +0000
1313+++ src/ioc/db/test/xRecord.c 2014-07-23 21:19:10 +0000
1314@@ -14,12 +14,24 @@
1315 */
1316
1317 #include "dbAccessDefs.h"
1318-#include <recSup.h>
1319+#include "recSup.h"
1320+#include "recGbl.h"
1321
1322 #define GEN_SIZE_OFFSET
1323 #include "xRecord.h"
1324
1325 #include <epicsExport.h>
1326
1327-static rset xRSET;
1328+static long process(xRecord *prec)
1329+{
1330+ prec->pact = TRUE;
1331+ recGblGetTimeStamp(prec);
1332+ recGblFwdLink(prec);
1333+ prec->pact = FALSE;
1334+ return 0;
1335+}
1336+
1337+static rset xRSET = {
1338+ RSETNUMBER, NULL, NULL, NULL, process
1339+};
1340 epicsExportAddress(rset,xRSET);
1341
1342=== modified file 'src/ioc/db/test/xRecord.dbd'
1343--- src/ioc/db/test/xRecord.dbd 2012-06-29 15:09:56 +0000
1344+++ src/ioc/db/test/xRecord.dbd 2014-07-23 21:19:10 +0000
1345@@ -1,12 +1,11 @@
1346-# This is a combined minimal DBD and DB file
1347+# This is a minimal record definition
1348
1349 recordtype(x) {
1350- field(NAME, DBF_STRING) {
1351- prompt("Record Name")
1352- special(SPC_NOMOD)
1353- size(61)
1354- }
1355+ include "dbCommon.dbd"
1356 field(VAL, DBF_LONG) {
1357 prompt("Value")
1358 }
1359+ field(LNK, DBF_INLINK) {
1360+ prompt("Link")
1361+ }
1362 }
1363
1364=== modified file 'src/ioc/misc/dbCore.dbd'
1365--- src/ioc/misc/dbCore.dbd 2013-12-17 18:54:04 +0000
1366+++ src/ioc/misc/dbCore.dbd 2014-07-23 21:19:10 +0000
1367@@ -5,6 +5,9 @@
1368 # This file provides iocsh access to variables that control some lesser-used
1369 # and debugging features of the IOC database code.
1370
1371+# show epicsAtExit callbacks as they are run
1372+variable(atExitDebug,int)
1373+
1374 # Access security subroutines
1375 variable(asCaDebug,int)
1376
1377
1378=== modified file 'src/ioc/misc/iocInit.c'
1379--- src/ioc/misc/iocInit.c 2013-05-30 20:00:37 +0000
1380+++ src/ioc/misc/iocInit.c 2014-07-23 21:19:10 +0000
1381@@ -3,6 +3,8 @@
1382 * National Laboratory.
1383 * Copyright (c) 2002 The Regents of the University of California, as
1384 * Operator of Los Alamos National Laboratory.
1385+* Copyright (c) 2013 Helmholtz-Zentrum Berlin
1386+* für Materialien und Energie GmbH.
1387 * EPICS BASE is distributed subject to a Software License Agreement found
1388 * in file LICENSE that is included with this distribution.
1389 \*************************************************************************/
1390@@ -31,6 +33,7 @@
1391 #include "errMdef.h"
1392 #include "taskwd.h"
1393 #include "caeventmask.h"
1394+#include "iocsh.h"
1395
1396 #define epicsExportSharedSymbols
1397 #include "alarm.h"
1398@@ -88,12 +91,13 @@
1399 return iocBuild() || iocRun();
1400 }
1401
1402-int iocBuild(void)
1403+static int iocBuild_1(void)
1404 {
1405- if (iocState != iocVirgin) {
1406- errlogPrintf("iocBuild: IOC can only be initialized once\n");
1407+ if (iocState != iocVirgin && iocState != iocStopped) {
1408+ errlogPrintf("iocBuild: IOC can only be initialized from uninitialized or stopped state\n");
1409 return -1;
1410 }
1411+ errlogInit(0);
1412 initHookAnnounce(initHookAtIocBuild);
1413
1414 if (!epicsThreadIsOkToBlock()) {
1415@@ -109,14 +113,17 @@
1416 initHookAnnounce(initHookAtBeginning);
1417
1418 coreRelease();
1419- /* After this point, further calls to iocInit() are disallowed. */
1420 iocState = iocBuilding;
1421
1422 taskwdInit();
1423 callbackInit();
1424 initHookAnnounce(initHookAfterCallbackInit);
1425
1426- dbCaLinkInit();
1427+ return 0;
1428+}
1429+
1430+static int iocBuild_2(void)
1431+{
1432 initHookAnnounce(initHookAfterCaLinkInit);
1433
1434 initDrvSup();
1435@@ -147,9 +154,11 @@
1436
1437 initialProcess();
1438 initHookAnnounce(initHookAfterInitialProcess);
1439+ return 0;
1440+}
1441
1442- /* Start CA server threads */
1443- rsrv_init();
1444+static int iocBuild_3(void)
1445+{
1446 initHookAnnounce(initHookAfterCaServerInit);
1447
1448 iocState = iocBuilt;
1449@@ -157,6 +166,39 @@
1450 return 0;
1451 }
1452
1453+int iocBuild(void)
1454+{
1455+ int status;
1456+
1457+ status = iocBuild_1();
1458+ if (status) return status;
1459+
1460+ dbCaLinkInit();
1461+
1462+ status = iocBuild_2();
1463+ if (status) return status;
1464+
1465+ /* Start CA server threads */
1466+ rsrv_init();
1467+
1468+ status = iocBuild_3();
1469+ return status;
1470+}
1471+
1472+int iocBuildIsolated(void)
1473+{
1474+ int status;
1475+
1476+ status = iocBuild_1();
1477+ if (status) return status;
1478+
1479+ status = iocBuild_2();
1480+ if (status) return status;
1481+
1482+ status = iocBuild_3();
1483+ return status;
1484+}
1485+
1486 int iocRun(void)
1487 {
1488 if (iocState != iocPaused && iocState != iocBuilt) {
1489@@ -599,8 +641,31 @@
1490 }
1491 }
1492
1493-static void exitDatabase(void *dummy)
1494-{
1495+static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord,
1496+ void *user)
1497+{
1498+ struct rset *prset = pdbRecordType->prset;
1499+
1500+ if (!prset) return; /* unlikely */
1501+
1502+ epicsMutexDestroy(precord->mlok);
1503+}
1504+
1505+int iocShutdown(void)
1506+{
1507+ if (iocState == iocVirgin || iocState == iocStopped) return 0;
1508 iterateRecords(doCloseLinks, NULL);
1509+ scanShutdown();
1510+ callbackShutdown();
1511+ iterateRecords(doFreeRecord, NULL);
1512+ dbLockCleanupRecords(pdbbase);
1513+ asShutdown();
1514+ iocshFree();
1515 iocState = iocStopped;
1516+ return 0;
1517+}
1518+
1519+static void exitDatabase(void *dummy)
1520+{
1521+ iocShutdown();
1522 }
1523
1524=== modified file 'src/ioc/misc/iocInit.h'
1525--- src/ioc/misc/iocInit.h 2009-06-10 20:19:32 +0000
1526+++ src/ioc/misc/iocInit.h 2014-07-23 21:19:10 +0000
1527@@ -19,8 +19,10 @@
1528
1529 epicsShareFunc int iocInit(void);
1530 epicsShareFunc int iocBuild(void);
1531+epicsShareFunc int iocBuildIsolated(void);
1532 epicsShareFunc int iocRun(void);
1533 epicsShareFunc int iocPause(void);
1534+epicsShareFunc int iocShutdown(void);
1535
1536 #ifdef __cplusplus
1537 }
1538
1539=== modified file 'src/libCom/as/asLib.h'
1540--- src/libCom/as/asLib.h 2010-12-17 16:50:52 +0000
1541+++ src/libCom/as/asLib.h 2014-07-23 21:19:10 +0000
1542@@ -222,6 +222,7 @@
1543 /*following is "friend" function*/
1544 epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size);
1545 epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str);
1546+epicsShareFunc void asFreeAll(ASBASE *pasbase);
1547 #ifdef __cplusplus
1548 }
1549 #endif
1550
1551=== modified file 'src/libCom/as/asLibRoutines.c'
1552--- src/libCom/as/asLibRoutines.c 2012-07-07 18:50:55 +0000
1553+++ src/libCom/as/asLibRoutines.c 2014-07-23 21:19:10 +0000
1554@@ -51,7 +51,6 @@
1555 static long asComputeAllAsgPvt(void);
1556 static long asComputeAsgPvt(ASG *pasg);
1557 static long asComputePvt(ASCLIENTPVT asClientPvt);
1558-static void asFreeAll(ASBASE *pasbase);
1559 static UAG *asUagAdd(const char *uagName);
1560 static long asUagAddUser(UAG *puag,const char *user);
1561 static HAG *asHagAdd(const char *hagName);
1562@@ -1015,7 +1014,7 @@
1563 return(0);
1564 }
1565
1566
1567-static void asFreeAll(ASBASE *pasbase)
1568+void asFreeAll(ASBASE *pasbase)
1569 {
1570 UAG *puag;
1571 UAGNAME *puagname;
1572
1573=== modified file 'src/libCom/error/errlog.c'
1574--- src/libCom/error/errlog.c 2013-06-28 17:35:43 +0000
1575+++ src/libCom/error/errlog.c 2014-07-23 21:19:10 +0000
1576@@ -41,7 +41,7 @@
1577 /*Declare storage for errVerbose */
1578 epicsShareDef int errVerbose = 0;
1579
1580-static void exitHandler(void *);
1581+static void errlogExitHandler(void *);
1582 static void errlogThread(void);
1583
1584 static char *msgbufGetFree(int noConsoleMessage);
1585@@ -70,8 +70,8 @@
1586 epicsEventId waitForFlush; /*errlogFlush waits for this*/
1587 epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/
1588 epicsMutexId flushLock;
1589- epicsEventId waitForExit; /*exitHandler waits for this*/
1590- int atExit; /*TRUE when exitHandler is active*/
1591+ epicsEventId waitForExit; /*errlogExitHandler waits for this*/
1592+ int atExit; /*TRUE when errlogExitHandler is active*/
1593 ELLLIST listenerList;
1594 ELLLIST msgQueue;
1595 msgNode *pnextSend;
1596@@ -368,6 +368,7 @@
1597 epicsShareFunc int epicsShareAPI eltc(int yesno)
1598 {
1599 errlogInit(0);
1600+ errlogFlush();
1601 pvtData.toConsole = yesno;
1602 return 0;
1603 }
1604@@ -447,7 +448,7 @@
1605 }
1606
1607
1608-static void exitHandler(void *pvt)
1609+static void errlogExitHandler(void *pvt)
1610 {
1611 pvtData.atExit = 1;
1612 epicsEventSignal(pvtData.waitForWork);
1613@@ -561,7 +562,7 @@
1614 int noConsoleMessage;
1615 char *pmessage;
1616
1617- epicsAtExit(exitHandler,0);
1618+ epicsAtExit(errlogExitHandler,0);
1619 while (TRUE) {
1620 epicsEventMustWait(pvtData.waitForWork);
1621 while ((pmessage = msgbufGetSend(&noConsoleMessage))) {
1622
1623=== modified file 'src/libCom/iocsh/iocsh.cpp'
1624--- src/libCom/iocsh/iocsh.cpp 2014-02-07 23:19:28 +0000
1625+++ src/libCom/iocsh/iocsh.cpp 2014-07-23 21:19:10 +0000
1626@@ -203,20 +203,22 @@
1627 */
1628 void epicsShareAPI iocshFree(void)
1629 {
1630- struct iocshCommand *pc, *nc;
1631- struct iocshVariable *pv, *nv;
1632+ struct iocshCommand *pc;
1633+ struct iocshVariable *pv;
1634
1635 iocshTableLock ();
1636 for (pc = iocshCommandHead ; pc != NULL ; ) {
1637- nc = pc->next;
1638+ struct iocshCommand * nc = pc->next;
1639 free (pc);
1640 pc = nc;
1641 }
1642 for (pv = iocshVariableHead ; pv != NULL ; ) {
1643- nv = pv->next;
1644+ struct iocshVariable *nv = pv->next;
1645 free (pv);
1646 pv = nv;
1647 }
1648+ iocshCommandHead = NULL;
1649+ iocshVariableHead = NULL;
1650 iocshTableUnlock ();
1651 }
1652
1653
1654=== modified file 'src/libCom/misc/epicsExit.c'
1655--- src/libCom/misc/epicsExit.c 2013-12-17 18:54:04 +0000
1656+++ src/libCom/misc/epicsExit.c 2014-07-23 21:19:10 +0000
1657@@ -23,7 +23,9 @@
1658 */
1659
1660 #include <stdlib.h>
1661+#include <stdio.h>
1662 #include <errno.h>
1663+#include <string.h>
1664
1665 #define epicsExportSharedSymbols
1666 #include "ellLib.h"
1667@@ -36,12 +38,15 @@
1668 ELLNODE node;
1669 epicsExitFunc func;
1670 void *arg;
1671+ char name[1];
1672 }exitNode;
1673
1674 typedef struct exitPvt {
1675 ELLLIST list;
1676 } exitPvt;
1677
1678+int atExitDebug = 0;
1679+
1680 static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT;
1681 static exitPvt * pExitPvtPerProcess = 0;
1682 static epicsMutexId exitPvtLock = 0;
1683@@ -66,15 +71,23 @@
1684 {
1685 exitPvtPerThread = epicsThreadPrivateCreate ();
1686 assert ( exitPvtPerThread );
1687- pExitPvtPerProcess = createExitPvt ();
1688- assert ( pExitPvtPerProcess );
1689 exitPvtLock = epicsMutexMustCreate ();
1690 }
1691
1692+static void epicsExitInit(void)
1693+{
1694+ epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );
1695+}
1696+
1697 static void epicsExitCallAtExitsPvt(exitPvt *pep)
1698 {
1699 exitNode *pexitNode;
1700+
1701 while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) {
1702+ if (atExitDebug && pexitNode->name[0])
1703+ fprintf(stderr, "atExit %s(%p)\n", pexitNode->name, pexitNode->arg);
1704+ else if(atExitDebug)
1705+ fprintf(stderr, "atExit %p(%p)\n", pexitNode->func, pexitNode->arg);
1706 pexitNode->func ( pexitNode->arg );
1707 ellDelete ( & pep->list, & pexitNode->node );
1708 free ( pexitNode );
1709@@ -84,7 +97,8 @@
1710 epicsShareFunc void epicsExitCallAtExits(void)
1711 {
1712 exitPvt * pep = 0;
1713- epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );
1714+
1715+ epicsExitInit ();
1716 epicsMutexMustLock ( exitPvtLock );
1717 if ( pExitPvtPerProcess ) {
1718 pep = pExitPvtPerProcess;
1719@@ -100,7 +114,8 @@
1720 epicsShareFunc void epicsExitCallAtThreadExits(void)
1721 {
1722 exitPvt * pep;
1723- epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );
1724+
1725+ epicsExitInit ();
1726 pep = epicsThreadPrivateGet ( exitPvtPerThread );
1727 if ( pep ) {
1728 epicsExitCallAtExitsPvt ( pep );
1729@@ -109,14 +124,16 @@
1730 }
1731 }
1732
1733-static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg)
1734+static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const char *name)
1735 {
1736 int status = -1;
1737- exitNode * pExitNode
1738- = calloc ( 1, sizeof( *pExitNode ) );
1739+ exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) );
1740+
1741 if ( pExitNode ) {
1742 pExitNode->func = func;
1743 pExitNode->arg = arg;
1744+ if(name)
1745+ strcpy(pExitNode->name, name);
1746 ellAdd ( & pep->list, & pExitNode->node );
1747 status = 0;
1748 }
1749@@ -126,7 +143,8 @@
1750 epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg)
1751 {
1752 exitPvt * pep;
1753- epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );
1754+
1755+ epicsExitInit ();
1756 pep = epicsThreadPrivateGet ( exitPvtPerThread );
1757 if ( ! pep ) {
1758 pep = createExitPvt ();
1759@@ -135,16 +153,20 @@
1760 }
1761 epicsThreadPrivateSet ( exitPvtPerThread, pep );
1762 }
1763- return epicsAtExitPvt ( pep, func, arg );
1764+ return epicsAtExitPvt ( pep, func, arg, NULL );
1765 }
1766
1767-epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg)
1768+epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name)
1769 {
1770 int status = -1;
1771- epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );
1772+
1773+ epicsExitInit ();
1774 epicsMutexMustLock ( exitPvtLock );
1775+ if ( !pExitPvtPerProcess ) {
1776+ pExitPvtPerProcess = createExitPvt ();
1777+ }
1778 if ( pExitPvtPerProcess ) {
1779- status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg );
1780+ status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg, name );
1781 }
1782 epicsMutexUnlock ( exitPvtLock );
1783 return status;
1784@@ -153,6 +175,10 @@
1785 epicsShareFunc void epicsExit(int status)
1786 {
1787 epicsExitCallAtExits();
1788- epicsThreadSleep(1.0);
1789+ epicsThreadSleep(0.1);
1790 exit(status);
1791 }
1792+
1793+#include "epicsExport.h"
1794+
1795+epicsExportAddress(int,atExitDebug);
1796
1797=== modified file 'src/libCom/misc/epicsExit.h'
1798--- src/libCom/misc/epicsExit.h 2013-12-17 18:54:04 +0000
1799+++ src/libCom/misc/epicsExit.h 2014-07-23 21:19:10 +0000
1800@@ -19,7 +19,8 @@
1801
1802 epicsShareFunc void epicsExit(int status);
1803 epicsShareFunc void epicsExitCallAtExits(void);
1804-epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg);
1805+epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name);
1806+#define epicsAtExit(F,A) epicsAtExit3(F,A,#F)
1807
1808 epicsShareFunc void epicsExitCallAtThreadExits(void);
1809 epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg);
1810
1811=== modified file 'src/libCom/misc/epicsUnitTest.h'
1812--- src/libCom/misc/epicsUnitTest.h 2010-10-05 19:27:37 +0000
1813+++ src/libCom/misc/epicsUnitTest.h 2014-07-23 21:19:10 +0000
1814@@ -9,6 +9,9 @@
1815 * Author: Andrew Johnson
1816 */
1817
1818+#ifndef INC_epicsUnitTest_H
1819+#define INC_epicsUnitTest_H
1820+
1821 #include <stdarg.h>
1822
1823 #include "compilerDependencies.h"
1824@@ -47,3 +50,5 @@
1825 #ifdef __cplusplus
1826 }
1827 #endif
1828+
1829+#endif /* INC_epicsUnitTest_H */
1830
1831=== modified file 'src/libCom/test/epicsExitTest.c'
1832--- src/libCom/test/epicsExitTest.c 2009-04-08 22:39:27 +0000
1833+++ src/libCom/test/epicsExitTest.c 2014-07-23 21:19:10 +0000
1834@@ -59,12 +59,20 @@
1835 testDiag("%s starting", pinfo->name);
1836 pinfo->terminate = epicsEventMustCreate(epicsEventEmpty);
1837 pinfo->terminated = epicsEventMustCreate(epicsEventEmpty);
1838- epicsAtExit(atExit, pinfo);
1839- epicsAtThreadExit(atThreadExit, pinfo);
1840+ testOk(!epicsAtExit(atExit, pinfo), "Registered atExit(%p)", pinfo);
1841+ testOk(!epicsAtThreadExit(atThreadExit, pinfo),
1842+ "Registered atThreadExit(%p)", pinfo);
1843 testDiag("%s waiting for atExit", pinfo->name);
1844 epicsEventMustWait(pinfo->terminate);
1845 }
1846
1847+int count;
1848+
1849+static void counter(void *pvt)
1850+{
1851+ count++;
1852+}
1853+
1854 static void mainExit(void *pvt)
1855 {
1856 testPass("Reached mainExit");
1857@@ -77,16 +85,23 @@
1858 info *pinfoA = (info *)calloc(1, sizeof(info));
1859 info *pinfoB = (info *)calloc(1, sizeof(info));
1860
1861- testPlan(7);
1862-
1863- epicsAtExit(mainExit, NULL);
1864+ testPlan(15);
1865+
1866+ testOk(!epicsAtExit(counter, NULL), "Registered counter()");
1867+ count = 0;
1868+ epicsExitCallAtExits();
1869+ testOk(count == 1, "counter() called once");
1870+ epicsExitCallAtExits();
1871+ testOk(count == 1, "unregistered counter() not called");
1872+
1873+ testOk(!epicsAtExit(mainExit, NULL), "Registered mainExit()");
1874
1875 epicsThreadCreate("threadA", 50, stackSize, thread, pinfoA);
1876 epicsThreadSleep(0.1);
1877 epicsThreadCreate("threadB", 50, stackSize, thread, pinfoB);
1878 epicsThreadSleep(1.0);
1879
1880- testDiag("Calling epicsExit\n");
1881+ testDiag("Calling epicsExit");
1882 epicsExit(0);
1883 return 0;
1884 }

Subscribers

People subscribed via source and target branches

to all changes: