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
=== 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-07-23 21:19:10 +0000
@@ -152,6 +152,15 @@
152 return(asInitCommon());152 return(asInitCommon());
153}153}
154154
155int asShutdown(void) {
156 volatile ASBASE *pbase = pasbase;
157 pasbase = NULL;
158 firstTime = TRUE;
159 if(pbase)
160 asFreeAll((ASBASE*)pbase);
161 return 0;
162}
163
155static void wdCallback(void *arg)164static void wdCallback(void *arg)
156{165{
157 ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg;166 ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg;
158167
=== 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-07-23 21:19:10 +0000
@@ -32,6 +32,7 @@
32epicsShareFunc int asSetSubstitutions(const char *substitutions);32epicsShareFunc int asSetSubstitutions(const char *substitutions);
33epicsShareFunc int asInit(void);33epicsShareFunc int asInit(void);
34epicsShareFunc int asInitAsyn(ASDBCALLBACK *pcallback);34epicsShareFunc int asInitAsyn(ASDBCALLBACK *pcallback);
35epicsShareFunc int asShutdown(void);
35epicsShareFunc int asDbGetAsl(struct dbChannel *chan);36epicsShareFunc int asDbGetAsl(struct dbChannel *chan);
36epicsShareFunc void * asDbGetMemberPvt(struct dbChannel *chan);37epicsShareFunc void * asDbGetMemberPvt(struct dbChannel *chan);
37epicsShareFunc int asdbdump(void);38epicsShareFunc int asdbdump(void);
3839
=== modified file 'src/ioc/db/Makefile'
--- src/ioc/db/Makefile 2012-11-29 18:53:21 +0000
+++ src/ioc/db/Makefile 2014-07-23 21:19:10 +0000
@@ -37,6 +37,7 @@
37INC += dbState.h37INC += dbState.h
38INC += db_access_routines.h38INC += db_access_routines.h
39INC += db_convert.h39INC += db_convert.h
40INC += dbUnitTest.h
4041
41# Generate menuGlobal.dbd automatically42# Generate menuGlobal.dbd automatically
42DBD += menuGlobal.dbd43DBD += menuGlobal.dbd
@@ -86,4 +87,4 @@
86dbCore_SRCS += dbIocRegister.c87dbCore_SRCS += dbIocRegister.c
87dbCore_SRCS += chfPlugin.c88dbCore_SRCS += chfPlugin.c
88dbCore_SRCS += dbState.c89dbCore_SRCS += dbState.c
8990dbCore_SRCS += dbUnitTest.c
9091
=== 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-07-23 21:19:10 +0000
@@ -43,7 +43,6 @@
43#include "callback.h"43#include "callback.h"
4444
4545
46static epicsThreadOnceId callbackOnceFlag = EPICS_THREAD_ONCE_INIT;
47static int callbackQueueSize = 2000;46static int callbackQueueSize = 2000;
48static epicsEventId callbackSem[NUM_CALLBACK_PRIORITIES];47static epicsEventId callbackSem[NUM_CALLBACK_PRIORITIES];
49static epicsRingPointerId callbackQ[NUM_CALLBACK_PRIORITIES];48static epicsRingPointerId callbackQ[NUM_CALLBACK_PRIORITIES];
@@ -53,6 +52,8 @@
53static epicsTimerQueueId timerQueue;52static epicsTimerQueueId timerQueue;
5453
55/* Shutdown handling */54/* Shutdown handling */
55enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
56static volatile enum ctl cbCtl;
56static epicsEventId startStopEvent;57static epicsEventId startStopEvent;
57static void *exitCallback;58static void *exitCallback;
5859
@@ -70,7 +71,7 @@
7071
71int callbackSetQueueSize(int size)72int callbackSetQueueSize(int size)
72{73{
73 if (callbackOnceFlag != EPICS_THREAD_ONCE_INIT) {74 if (startStopEvent) {
74 errlogPrintf("Callback system already initialized\n");75 errlogPrintf("Callback system already initialized\n");
75 return -1;76 return -1;
76 }77 }
@@ -101,24 +102,36 @@
101 epicsEventSignal(startStopEvent);102 epicsEventSignal(startStopEvent);
102}103}
103104
104static void callbackShutdown(void *arg)105void callbackShutdown(void)
105{106{
106 int i;107 int i;
107108
109 if (cbCtl == ctlExit) return;
110 cbCtl = ctlExit;
111
108 for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {112 for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
109 int lockKey = epicsInterruptLock();113 int lockKey = epicsInterruptLock();
110 int ok = epicsRingPointerPush(callbackQ[i], &exitCallback);114 int ok = epicsRingPointerPush(callbackQ[i], &exitCallback);
111 epicsInterruptUnlock(lockKey);115 epicsInterruptUnlock(lockKey);
112 epicsEventSignal(callbackSem[i]);116 epicsEventSignal(callbackSem[i]);
113 if (ok) epicsEventWait(startStopEvent);117 if (ok) epicsEventWait(startStopEvent);
118 epicsEventDestroy(callbackSem[i]);
119 epicsRingPointerDelete(callbackQ[i]);
114 }120 }
121 epicsTimerQueueRelease(timerQueue);
122 epicsEventDestroy(startStopEvent);
123 startStopEvent = NULL;
115}124}
116125
117static void callbackInitOnce(void *arg)126void callbackInit(void)
118{127{
119 int i;128 int i;
120129
130 if(startStopEvent)
131 return;
132
121 startStopEvent = epicsEventMustCreate(epicsEventEmpty);133 startStopEvent = epicsEventMustCreate(epicsEventEmpty);
134 cbCtl = ctlRun;
122 timerQueue = epicsTimerQueueAllocate(0,epicsThreadPriorityScanHigh);135 timerQueue = epicsTimerQueueAllocate(0,epicsThreadPriorityScanHigh);
123 for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {136 for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
124 epicsThreadId tid;137 epicsThreadId tid;
@@ -137,12 +150,6 @@
137 else150 else
138 epicsEventWait(startStopEvent);151 epicsEventWait(startStopEvent);
139 }152 }
140 epicsAtExit(callbackShutdown, NULL);
141}
142
143void callbackInit(void)
144{
145 epicsThreadOnce(&callbackOnceFlag, callbackInitOnce, NULL);
146}153}
147154
148/* This routine can be called from interrupt context */155/* This routine can be called from interrupt context */
149156
=== 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-07-23 21:19:10 +0000
@@ -57,6 +57,7 @@
5757
58epicsShareFunc void callbackInit(void);58epicsShareFunc void callbackInit(void);
59epicsShareFunc void callbackRequest(CALLBACK *pCallback);59epicsShareFunc void callbackRequest(CALLBACK *pCallback);
60epicsShareFunc void callbackShutdown(void);
60epicsShareFunc void callbackSetProcess(61epicsShareFunc void callbackSetProcess(
61 CALLBACK *pcallback, int Priority, void *pRec);62 CALLBACK *pcallback, int Priority, void *pRec);
62epicsShareFunc void callbackRequestProcessCallback(63epicsShareFunc void callbackRequestProcessCallback(
6364
=== 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-07-23 21:19:10 +0000
@@ -61,6 +61,7 @@
61#include "errMdef.h"61#include "errMdef.h"
62#include "recSup.h"62#include "recSup.h"
63#include "special.h"63#include "special.h"
64#include "epicsExit.h"
64#define epicsExportSharedSymbols65#define epicsExportSharedSymbols
65#include "dbAddr.h"66#include "dbAddr.h"
66#include "dbAccessDefs.h"67#include "dbAccessDefs.h"
@@ -250,7 +251,11 @@
250 return(0);251 return(0);
251}252}
252253
253
254254
255static void dbBkptExit(void *junk) {
256 epicsMutexDestroy(bkpt_stack_sem);
257 bkpt_stack_sem = NULL;
258}
259
255/*260/*
256 * Initialise the breakpoint stack261 * Initialise the breakpoint stack
257 */262 */
@@ -259,6 +264,7 @@
259 if (! bkpt_stack_sem) {264 if (! bkpt_stack_sem) {
260 bkpt_stack_sem = epicsMutexMustCreate();265 bkpt_stack_sem = epicsMutexMustCreate();
261 lset_stack_count = 0;266 lset_stack_count = 0;
267 epicsAtExit(dbBkptExit, NULL);
262 }268 }
263}269}
264270
265271
=== 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-07-23 21:19:10 +0000
@@ -173,15 +173,22 @@
173 dbScanUnlock(pdbCommon);173 dbScanUnlock(pdbCommon);
174}174}
175175
176static void dbCaShutdown(void *arg)176void dbCaShutdown(void)
177{177{
178 if (dbCaCtl == ctlRun) {178 if (dbCaCtl == ctlRun || dbCaCtl == ctlPause) {
179 dbCaCtl = ctlExit;179 dbCaCtl = ctlExit;
180 epicsEventSignal(workListEvent);180 epicsEventSignal(workListEvent);
181 epicsEventMustWait(startStopEvent);181 epicsEventMustWait(startStopEvent);
182 epicsEventDestroy(startStopEvent);
183 epicsEventDestroy(workListEvent);
182 }184 }
183}185}
184186
187static void dbCaExit(void *arg)
188{
189 dbCaShutdown();
190}
191
185void dbCaLinkInit(void)192void dbCaLinkInit(void)
186{193{
187 dbServiceIOInit();194 dbServiceIOInit();
@@ -194,19 +201,23 @@
194 epicsThreadGetStackSize(epicsThreadStackBig),201 epicsThreadGetStackSize(epicsThreadStackBig),
195 dbCaTask, NULL);202 dbCaTask, NULL);
196 epicsEventMustWait(startStopEvent);203 epicsEventMustWait(startStopEvent);
197 epicsAtExit(dbCaShutdown, NULL);204 epicsAtExit(dbCaExit, NULL);
198}205}
199206
200void dbCaRun(void)207void dbCaRun(void)
201{208{
202 dbCaCtl = ctlRun;209 if (dbCaCtl == ctlPause) {
203 epicsEventSignal(workListEvent);210 dbCaCtl = ctlRun;
211 epicsEventSignal(workListEvent);
212 }
204}213}
205214
206void dbCaPause(void)215void dbCaPause(void)
207{216{
208 dbCaCtl = ctlPause;217 if (dbCaCtl == ctlRun) {
209 epicsEventSignal(workListEvent);218 dbCaCtl = ctlPause;
219 epicsEventSignal(workListEvent);
220 }
210}221}
211222
212void dbCaAddLinkCallback(struct link *plink,223void dbCaAddLinkCallback(struct link *plink,
213224
=== 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-07-23 21:19:10 +0000
@@ -26,6 +26,7 @@
26epicsShareFunc void dbCaLinkInit(void);26epicsShareFunc void dbCaLinkInit(void);
27epicsShareFunc void dbCaRun(void);27epicsShareFunc void dbCaRun(void);
28epicsShareFunc void dbCaPause(void);28epicsShareFunc void dbCaPause(void);
29epicsShareFunc void dbCaShutdown(void);
2930
30epicsShareFunc void dbCaAddLinkCallback(struct link *plink,31epicsShareFunc void dbCaAddLinkCallback(struct link *plink,
31 dbCaCallback connect, dbCaCallback monitor, void *userPvt);32 dbCaCallback connect, dbCaCallback monitor, void *userPvt);
3233
=== 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-07-23 21:19:10 +0000
@@ -20,6 +20,7 @@
20#include "cantProceed.h"20#include "cantProceed.h"
21#include "epicsAssert.h"21#include "epicsAssert.h"
22#include "epicsString.h"22#include "epicsString.h"
23#include "epicsExit.h"
23#include "errlog.h"24#include "errlog.h"
24#include "freeList.h"25#include "freeList.h"
25#include "gpHash.h"26#include "gpHash.h"
@@ -52,16 +53,23 @@
52static void *chFilterFreeList;53static void *chFilterFreeList;
53static void *dbchStringFreeList;54static void *dbchStringFreeList;
5455
56static void dbChannelExit(void* junk)
57{
58 freeListCleanup(dbChannelFreeList);
59 freeListCleanup(chFilterFreeList);
60 freeListCleanup(dbchStringFreeList);
61 dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL;
62}
63
55void dbChannelInit (void)64void dbChannelInit (void)
56{65{
57 static int done = 0;66 if(dbChannelFreeList)
67 return;
5868
59 if (!done) {69 freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);
60 done = 1;70 freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);
61 freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128);71 freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
62 freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64);72 epicsAtExit(dbChannelExit, NULL);
63 freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128);
64 }
65}73}
6674
67static void chf_value(parseContext *parser, parse_result *presult)75static void chf_value(parseContext *parser, parse_result *presult)
6876
=== 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-07-23 21:19:10 +0000
@@ -54,6 +54,7 @@
54#include "epicsMutex.h"54#include "epicsMutex.h"
55#include "epicsThread.h"55#include "epicsThread.h"
56#include "epicsAssert.h"56#include "epicsAssert.h"
57#include "epicsExit.h"
57#include "cantProceed.h"58#include "cantProceed.h"
58#include "ellLib.h"59#include "ellLib.h"
59#define epicsExportSharedSymbols60#define epicsExportSharedSymbols
@@ -107,7 +108,16 @@
107 lockSet *plockSet;108 lockSet *plockSet;
108 dbCommon *precord;109 dbCommon *precord;
109} lockRecord;110} lockRecord;
110
111111
112
113static void dbLockExit(void *junk)
114{
115 epicsMutexDestroy(globalLock);
116 epicsMutexDestroy(lockSetModifyLock);
117 globalLock = NULL;
118 lockSetModifyLock = NULL;
119 dbLockIsInitialized = FALSE;
120}
121
112/*private routines */122/*private routines */
113static void dbLockInitialize(void)123static void dbLockInitialize(void)
114{124{
@@ -118,6 +128,7 @@
118 globalLock = epicsMutexMustCreate();128 globalLock = epicsMutexMustCreate();
119 lockSetModifyLock = epicsMutexMustCreate();129 lockSetModifyLock = epicsMutexMustCreate();
120 dbLockIsInitialized = TRUE;130 dbLockIsInitialized = TRUE;
131 epicsAtExit(dbLockExit,NULL);
121}132}
122133
123static lockSet * allocLockSet(134static lockSet * allocLockSet(
@@ -315,7 +326,9 @@
315 epicsMutexUnlock(lockSetModifyLock);326 epicsMutexUnlock(lockSetModifyLock);
316 return;327 return;
317}328}
318
319329
330
331static lockRecord *lockRecordAlloc;
332
320void dbLockInitRecords(dbBase *pdbbase)333void dbLockInitRecords(dbBase *pdbbase)
321{334{
322 int link;335 int link;
@@ -336,7 +349,7 @@
336 - pdbRecordType->no_aliases;349 - pdbRecordType->no_aliases;
337 }350 }
338 /*Allocate all of them at once */351 /*Allocate all of them at once */
339 plockRecord = dbCalloc(nrecords,sizeof(lockRecord));352 lockRecordAlloc = plockRecord = dbCalloc(nrecords,sizeof(lockRecord));
340 for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);353 for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
341 pdbRecordType;354 pdbRecordType;
342 pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {355 pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
@@ -377,7 +390,34 @@
377 }390 }
378 }391 }
379}392}
380
381393
394
395void dbLockCleanupRecords(dbBase *pdbbase)
396{
397 ELLNODE *cur;
398
399 free(lockRecordAlloc);
400 lockRecordAlloc = NULL;
401
402 /* free lockSets */
403 /* ensure no lockSets are locked for re-compute */
404 assert(ellCount(&lockSetList[listTypeRecordLock])==0);
405 /* move allocated locks back to the free list */
406 while((cur=ellGet(&lockSetList[listTypeScanLock]))!=NULL)
407 {
408 lockSet *pset = CONTAINER(cur, lockSet, node);
409 assert(pset->state == lockSetStateFree); /* lock not held */
410 pset->type = listTypeFree;
411 ellAdd(&lockSetList[listTypeFree],&pset->node);
412 }
413 /* clean up free list */
414 while((cur=ellGet(&lockSetList[listTypeFree]))!=NULL)
415 {
416 lockSet *pset = CONTAINER(cur, lockSet, node);
417 epicsMutexDestroy(pset->lock);
418 free(pset);
419 }
420}
421
382void dbLockSetMerge(dbCommon *pfirst,dbCommon *psecond)422void dbLockSetMerge(dbCommon *pfirst,dbCommon *psecond)
383{423{
384 lockRecord *p1lockRecord = pfirst->lset;424 lockRecord *p1lockRecord = pfirst->lset;
385425
=== 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-07-23 21:19:10 +0000
@@ -28,6 +28,7 @@
28 struct dbCommon *precord);28 struct dbCommon *precord);
2929
30epicsShareFunc void dbLockInitRecords(struct dbBase *pdbbase);30epicsShareFunc void dbLockInitRecords(struct dbBase *pdbbase);
31epicsShareFunc void dbLockCleanupRecords(struct dbBase *pdbbase);
31epicsShareFunc void dbLockSetMerge(32epicsShareFunc void dbLockSetMerge(
32 struct dbCommon *pfirst,struct dbCommon *psecond);33 struct dbCommon *pfirst,struct dbCommon *psecond);
33epicsShareFunc void dbLockSetSplit(struct dbCommon *psource);34epicsShareFunc void dbLockSetSplit(struct dbCommon *psource);
3435
=== 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-07-23 21:19:10 +0000
@@ -44,6 +44,7 @@
44#include "recGbl.h"44#include "recGbl.h"
45#include "dbNotify.h"45#include "dbNotify.h"
46#include "epicsTime.h"46#include "epicsTime.h"
47#include "epicsExit.h"
47#include "cantProceed.h"48#include "cantProceed.h"
4849
49/*notify state values */50/*notify state values */
@@ -298,6 +299,14 @@
298 callDone(precord, ppn);299 callDone(precord, ppn);
299}300}
300301
302static void dbProcessNotifyExit(void* junk)
303{
304 assert(ellCount(&pnotifyGlobal->freeList)==0);
305 epicsMutexDestroy(pnotifyGlobal->lock);
306 free(pnotifyGlobal);
307 pnotifyGlobal = NULL;
308}
309
301void dbProcessNotifyInit(void)310void dbProcessNotifyInit(void)
302{311{
303 if (pnotifyGlobal)312 if (pnotifyGlobal)
@@ -305,6 +314,7 @@
305 pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal));314 pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal));
306 pnotifyGlobal->lock = epicsMutexMustCreate();315 pnotifyGlobal->lock = epicsMutexMustCreate();
307 ellInit(&pnotifyGlobal->freeList);316 ellInit(&pnotifyGlobal->freeList);
317 epicsAtExit(dbProcessNotifyExit, NULL);
308}318}
309319
310void dbProcessNotify(processNotify *ppn)320void dbProcessNotify(processNotify *ppn)
311321
=== 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-07-23 21:19:10 +0000
@@ -3,6 +3,8 @@
3* National Laboratory.3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.5* Operator of Los Alamos National Laboratory.
6* Copyright (c) 2013 Helmholtz-Zentrum Berlin
7* für Materialien und Energie GmbH.
6* EPICS BASE is distributed subject to a Software License Agreement found8* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution. 9* in file LICENSE that is included with this distribution.
8\*************************************************************************/10\*************************************************************************/
@@ -132,6 +134,7 @@
132static void initOnce(void);134static void initOnce(void);
133static void periodicTask(void *arg);135static void periodicTask(void *arg);
134static void initPeriodic(void);136static void initPeriodic(void);
137static void deletePeriodic(void);
135static void spawnPeriodic(int ind);138static void spawnPeriodic(int ind);
136static void initEvent(void);139static void initEvent(void);
137static void eventCallback(CALLBACK *pcallback);140static void eventCallback(CALLBACK *pcallback);
@@ -142,10 +145,13 @@
142static void addToList(struct dbCommon *precord, scan_list *psl);145static void addToList(struct dbCommon *precord, scan_list *psl);
143static void deleteFromList(struct dbCommon *precord, scan_list *psl);146static void deleteFromList(struct dbCommon *precord, scan_list *psl);
144147
145148
146static void scanShutdown(void *arg)149void scanShutdown(void)
147{150{
148 int i;151 int i;
149152
153 if (scanCtl == ctlExit) return;
154 scanCtl = ctlExit;
155
150 interruptAccept = FALSE;156 interruptAccept = FALSE;
151157
152 for (i = 0; i < nPeriodic; i++) {158 for (i = 0; i < nPeriodic; i++) {
@@ -156,6 +162,18 @@
156162
157 scanOnce((dbCommon *)&exitOnce);163 scanOnce((dbCommon *)&exitOnce);
158 epicsEventWait(startStopEvent);164 epicsEventWait(startStopEvent);
165
166 deletePeriodic();
167
168 epicsRingPointerDelete(onceQ);
169
170 epicsEventDestroy(startStopEvent);
171 epicsEventDestroy(onceSem);
172 onceSem = startStopEvent = NULL;
173
174 free(periodicTaskId);
175 papPeriodic = NULL;
176 periodicTaskId = NULL;
159}177}
160178
161long scanInit(void)179long scanInit(void)
@@ -172,7 +190,6 @@
172 for (i = 0; i < nPeriodic; i++)190 for (i = 0; i < nPeriodic; i++)
173 spawnPeriodic(i);191 spawnPeriodic(i);
174192
175 epicsAtExit(scanShutdown, NULL);
176 return 0;193 return 0;
177}194}
178195
@@ -696,6 +713,22 @@
696 }713 }
697}714}
698715
716static void deletePeriodic(void)
717{
718 int i;
719
720 for (i = 0; i < nPeriodic; i++) {
721 periodic_scan_list *ppsl = papPeriodic[i];
722 ellFree(&ppsl->scan_list.list);
723 epicsEventDestroy(ppsl->loopEvent);
724 epicsMutexDestroy(ppsl->scan_list.lock);
725 free(ppsl);
726 }
727
728 free(papPeriodic);
729 papPeriodic = NULL;
730}
731
699static void spawnPeriodic(int ind)732static void spawnPeriodic(int ind)
700{733{
701 periodic_scan_list *ppsl;734 periodic_scan_list *ppsl;
@@ -802,23 +835,25 @@
802{835{
803 dbRecordType *pdbRecordType;836 dbRecordType *pdbRecordType;
804837
805 /*Look for first record*/
806 for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);838 for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
807 pdbRecordType;839 pdbRecordType;
808 pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {840 pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
809 dbRecordNode *pdbRecordNode;841 dbRecordNode *pdbRecordNode;
842
810 for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList);843 for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList);
811 pdbRecordNode;844 pdbRecordNode;
812 pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) {845 pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) {
813 dbCommon *precord = pdbRecordNode->precord;846 dbCommon *precord = pdbRecordNode->precord;
847
814 if (!precord->name[0] ||848 if (!precord->name[0] ||
815 pdbRecordNode->flags & DBRN_FLAGS_ISALIAS)849 pdbRecordNode->flags & DBRN_FLAGS_ISALIAS)
816 continue;850 continue;
851
817 scanAdd(precord);852 scanAdd(precord);
818 }853 }
819 }854 }
820}855}
821
822856
857
823static void addToList(struct dbCommon *precord, scan_list *psl)858static void addToList(struct dbCommon *precord, scan_list *psl)
824{859{
825 scan_element *pse, *ptemp;860 scan_element *pse, *ptemp;
826861
=== 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-07-23 21:19:10 +0000
@@ -44,6 +44,7 @@
44epicsShareFunc long scanInit(void);44epicsShareFunc long scanInit(void);
45epicsShareFunc void scanRun(void);45epicsShareFunc void scanRun(void);
46epicsShareFunc void scanPause(void);46epicsShareFunc void scanPause(void);
47epicsShareFunc void scanShutdown(void);
4748
48epicsShareFunc EVENTPVT eventNameToHandle(const char* event);49epicsShareFunc EVENTPVT eventNameToHandle(const char* event);
49epicsShareFunc void postEvent(EVENTPVT epvt);50epicsShareFunc void postEvent(EVENTPVT epvt);
5051
=== 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-07-23 21:19:10 +0000
@@ -0,0 +1,201 @@
1/*************************************************************************\
2* Copyright (c) 2013 Brookhaven National Laboratory.
3* Copyright (c) 2013 ITER Organization.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7
8/*
9 * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
10 * Ralph Lange <Ralph.Lange@gmx.de>
11 */
12
13#include <string.h>
14
15#include "epicsUnitTest.h"
16#include "osiFileName.h"
17#include "dbmf.h"
18#include "registry.h"
19#define epicsExportSharedSymbols
20#include "iocInit.h"
21#include "initHooks.h"
22#include "dbBase.h"
23#include "dbAccess.h"
24#include "dbStaticLib.h"
25
26#include "dbUnitTest.h"
27
28void testdbPrepare(void)
29{
30 /* No-op at the moment */
31}
32
33void testdbReadDatabase(const char* file,
34 const char* path,
35 const char* substitutions)
36{
37 if(!path)
38 path = "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
39 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common";
40 if(dbReadDatabase(&pdbbase, file, path, substitutions))
41 testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)",
42 file, path, substitutions);
43}
44
45void testIocInitOk(void)
46{
47 if(iocBuildIsolated() || iocRun())
48 testAbort("Failed to start up test database");
49}
50
51void testIocShutdownOk(void)
52{
53 if(iocShutdown())
54 testAbort("Failed to shutdown test database");
55}
56
57void testdbCleanup(void)
58{
59 dbFreeBase(pdbbase);
60 initHookFree();
61 registryFree();
62 pdbbase = NULL;
63 dbmfFreeChunks();
64}
65
66union anybuf {
67 epicsAny val;
68 char valStr[MAX_STRING_SIZE];
69 char bytes[sizeof(epicsAny)];
70};
71
72long testdbVPutField(const char* pv, short dbrType, va_list ap)
73{
74 DBADDR addr;
75 union anybuf pod;
76
77 if(dbNameToAddr(pv, &addr)) {
78 testFail("Missing PV %s", pv);
79 return S_dbLib_recNotFound;
80 }
81
82 switch(dbrType) {
83 case DBR_STRING: {
84 const char *uarg = va_arg(ap,char*);
85 strncpy(pod.valStr, uarg, sizeof(pod.valStr));
86 pod.valStr[sizeof(pod.valStr)-1] = '\0';
87 return dbPutField(&addr, dbrType, pod.valStr, 1);
88 }
89
90 /* The Type parameter takes into consideration
91 * the C language rules for promotion of argument types
92 * in variadic functions.
93 */
94#define OP(DBR,Type,mem) case DBR: {pod.val.mem = va_arg(ap,Type); break;}
95 OP(DBR_CHAR, int, int8);
96 OP(DBR_UCHAR, int, uInt8);
97 OP(DBR_SHORT, int, int16);
98 OP(DBR_USHORT, int, uInt16);
99 OP(DBR_LONG, int, int32);
100 OP(DBR_ULONG, unsigned int, uInt32);
101 OP(DBR_FLOAT, double, float32);
102 OP(DBR_DOUBLE, double, float64);
103 OP(DBR_ENUM, int, enum16);
104#undef OP
105 default:
106 testFail("invalid DBR: dbPutField(\"%s\", %d, ...)",
107 addr.precord->name, dbrType);
108 return S_db_badDbrtype;
109 }
110
111 return dbPutField(&addr, dbrType, pod.bytes, 1);
112}
113
114void testdbPutFieldOk(const char* pv, short dbrType, ...)
115{
116 long ret;
117 va_list ap;
118
119 va_start(ap, dbrType);
120 ret = testdbVPutField(pv, dbrType, ap);
121 va_end(ap);
122
123 testOk(ret==0, "dbPutField(%s, %d, ...) == %ld", pv, dbrType, ret);
124}
125
126void testdbPutFieldFail(long status, const char* pv, short dbrType, ...)
127{
128 long ret;
129 va_list ap;
130
131 va_start(ap, dbrType);
132 ret = testdbVPutField(pv, dbrType, ap);
133 va_end(ap);
134
135 if(ret==status)
136 testPass("dbPutField(\"%s\", %d, ...) == %ld", pv, dbrType, status);
137 else
138 testFail("dbPutField(\"%s\", %d, ...) != %ld (%ld)", pv, dbrType, status, ret);
139}
140
141void testdbGetFieldEqual(const char* pv, short dbrType, ...)
142{
143 va_list ap;
144
145 va_start(ap, dbrType);
146 testdbVGetFieldEqual(pv, dbrType, ap);
147 va_end(ap);
148}
149
150void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap)
151{
152 DBADDR addr;
153 long nReq = 1;
154 union anybuf pod;
155 long status;
156
157 if(dbNameToAddr(pv, &addr)) {
158 testFail("Missing PV %s", pv);
159 return;
160 }
161
162 status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
163 if(status) {
164 testFail("dbGetField(\"%s\",%d,...) returns %ld", pv, dbrType, status);
165 return;
166 }
167
168 switch(dbrType) {
169 case DBR_STRING: {
170 const char *expect = va_arg(ap, char*);
171 testOk(strcmp(expect, pod.valStr)==0,
172 "dbGetField(\"%s\", %d) -> \"%s\" == \"%s\"",
173 pv, dbrType, expect, pod.valStr);
174 break;
175 }
176#define OP(DBR,Type,mem,pat) case DBR: {Type expect = va_arg(ap,Type); \
177 testOk(expect==pod.val.mem, "dbGetField(\"%s\", %d) -> " pat " == " pat, \
178 pv, dbrType, expect, (Type)pod.val.mem); break;}
179
180 OP(DBR_CHAR, int, int8, "%d");
181 OP(DBR_UCHAR, int, uInt8, "%d");
182 OP(DBR_SHORT, int, int16, "%d");
183 OP(DBR_USHORT, int, uInt16, "%d");
184 OP(DBR_LONG, int, int32, "%d");
185 OP(DBR_ULONG, unsigned int, uInt32, "%u");
186 OP(DBR_FLOAT, double, float32, "%e");
187 OP(DBR_DOUBLE, double, float64, "%e");
188 OP(DBR_ENUM, int, enum16, "%d");
189#undef OP
190 }
191}
192
193dbCommon* testdbRecordPtr(const char* pv)
194{
195 DBADDR addr;
196
197 if(dbNameToAddr(pv, &addr))
198 testAbort("Missing record %s", pv);
199
200 return addr.precord;
201}
0202
=== 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-07-23 21:19:10 +0000
@@ -0,0 +1,64 @@
1/*************************************************************************\
2* Copyright (c) 2013 Brookhaven National Laboratory.
3* Copyright (c) 2013 ITER Organization.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7
8/*
9 * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
10 * Ralph Lange <Ralph.Lange@gmx.de>
11 */
12
13#ifndef EPICSUNITTESTDB_H
14#define EPICSUNITTESTDB_H
15
16#include <stdarg.h>
17
18#include "epicsUnitTest.h"
19#include "dbAddr.h"
20#include "dbCommon.h"
21
22#include "shareLib.h"
23
24#ifdef __cplusplus
25extern "C" {
26#endif
27
28epicsShareFunc void testdbPrepare(void);
29epicsShareFunc void testdbReadDatabase(const char* file,
30 const char* path,
31 const char* substitutions);
32epicsShareFunc void testIocInitOk(void);
33epicsShareFunc void testIocShutdownOk(void);
34epicsShareFunc void testdbCleanup(void);
35
36/* Correct argument types must be used with this var-arg function!
37 * Doing otherwise will result in corruption of argument values!
38 *
39 * int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG
40 * unsigned int for DBR_ULONG
41 * double for DBR_FLOAT and DBR_DOUBLE
42 * const char* for DBR_STRING
43 *
44 * eg.
45 * testdbPutFieldOk("pvname", DBF_ULONG, (unsigned int)5);
46 * testdbPutFieldOk("pvname", DBF_FLOAT, (double)4.1);
47 * testdbPutFieldOk("pvname", DBF_STRING, "hello world");
48 */
49epicsShareFunc void testdbPutFieldOk(const char* pv, short dbrType, ...);
50/* Tests for put failure */
51epicsShareFunc void testdbPutFieldFail(long status, const char* pv, short dbrType, ...);
52
53epicsShareFunc long testdbVPutField(const char* pv, short dbrType, va_list ap);
54
55epicsShareFunc void testdbGetFieldEqual(const char* pv, short dbrType, ...);
56epicsShareFunc void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap);
57
58epicsShareFunc dbCommon* testdbRecordPtr(const char* pv);
59
60#ifdef __cplusplus
61}
62#endif
63
64#endif // EPICSUNITTESTDB_H
065
=== 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-07-23 21:19:10 +0000
@@ -93,6 +93,14 @@
93 }93 }
94}94}
9595
96void initHookFree(void)
97{
98 initHookInit();
99 epicsMutexMustLock(listLock);
100 ellFree(&functionList);
101 epicsMutexUnlock(listLock);
102}
103
96/*104/*
97 * Call any time you want to print out a state name.105 * Call any time you want to print out a state name.
98 */106 */
99107
=== 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-07-23 21:19:10 +0000
@@ -60,6 +60,7 @@
60epicsShareFunc int initHookRegister(initHookFunction func);60epicsShareFunc int initHookRegister(initHookFunction func);
61epicsShareFunc void initHookAnnounce(initHookState state);61epicsShareFunc void initHookAnnounce(initHookState state);
62epicsShareFunc const char *initHookName(int state);62epicsShareFunc const char *initHookName(int state);
63epicsShareFunc void initHookFree(void);
6364
64#ifdef __cplusplus65#ifdef __cplusplus
65}66}
6667
=== 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-07-23 21:19:10 +0000
@@ -10,11 +10,31 @@
1010
11include $(TOP)/configure/CONFIG11include $(TOP)/configure/CONFIG
1212
13TESTLIBRARY = xRec13TESTLIBRARY = dbTestIoc
1414
15xRec_SRCS = xRecord.c15dbTestIoc_SRCS = xRecord.c
1616
17PROD_LIBS = xRec dbCore ca Com17TARGETS += $(COMMON_DIR)/dbTestIoc.dbd
18dbTestIoc_DBD += menuGlobal.dbd
19dbTestIoc_DBD += menuConvert.dbd
20dbTestIoc_DBD += xRecord.dbd
21TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
22
23testHarness_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
24
25PROD_LIBS = dbTestIoc dbCore ca Com
26
27TESTPROD_HOST += dbShutdownTest
28dbShutdownTest_SRCS += dbShutdownTest.c
29dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
30testHarness_SRCS += dbShutdownTest.c
31TESTS += dbShutdownTest
32
33TESTPROD_HOST += dbPutLinkTest
34dbPutLinkTest_SRCS += dbPutLinkTest.c
35dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
36testHarness_SRCS += dbPutLinkTest.c
37TESTS += dbPutLinkTest
1838
19TESTPROD_HOST += testdbConvert39TESTPROD_HOST += testdbConvert
20testdbConvert_SRCS += testdbConvert.c40testdbConvert_SRCS += testdbConvert.c
@@ -31,34 +51,22 @@
31testHarness_SRCS += dbStateTest.c51testHarness_SRCS += dbStateTest.c
32TESTS += dbStateTest52TESTS += dbStateTest
3353
34TARGETS += $(COMMON_DIR)/dbChannelTest.dbd
35dbChannelTest_DBD += xRecord.dbd
36TESTPROD_HOST += dbChannelTest54TESTPROD_HOST += dbChannelTest
37dbChannelTest_SRCS += dbChannelTest.c55dbChannelTest_SRCS += dbChannelTest.c
38dbChannelTest_SRCS += dbChannelTest_registerRecordDeviceDriver.cpp56dbChannelTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
39testHarness_SRCS += dbChannelTest.c57testHarness_SRCS += dbChannelTest.c
40testHarness_SRCS += dbChannelTest_registerRecordDeviceDriver.cpp
41TESTFILES += $(COMMON_DIR)/dbChannelTest.dbd ../xRecord.db
42TESTS += dbChannelTest58TESTS += dbChannelTest
4359
44TARGETS += $(COMMON_DIR)/chfPluginTest.dbd
45chfPluginTest_DBD += xRecord.dbd
46TESTPROD_HOST += chfPluginTest60TESTPROD_HOST += chfPluginTest
47chfPluginTest_SRCS += chfPluginTest.c61chfPluginTest_SRCS += chfPluginTest.c
48chfPluginTest_SRCS += chfPluginTest_registerRecordDeviceDriver.cpp62chfPluginTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
49testHarness_SRCS += chfPluginTest.c63testHarness_SRCS += chfPluginTest.c
50testHarness_SRCS += chfPluginTest_registerRecordDeviceDriver.cpp
51TESTFILES += $(COMMON_DIR)/chfPluginTest.dbd
52TESTS += chfPluginTest64TESTS += chfPluginTest
5365
54TARGETS += $(COMMON_DIR)/arrShorthandTest.dbd
55arrShorthandTest_DBD += xRecord.dbd
56TESTPROD_HOST += arrShorthandTest66TESTPROD_HOST += arrShorthandTest
57arrShorthandTest_SRCS += arrShorthandTest.c67arrShorthandTest_SRCS += arrShorthandTest.c
58arrShorthandTest_SRCS += arrShorthandTest_registerRecordDeviceDriver.cpp68arrShorthandTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
59testHarness_SRCS += arrShorthandTest.c69testHarness_SRCS += arrShorthandTest.c
60testHarness_SRCS += arrShorthandTest_registerRecordDeviceDriver.cpp
61TESTFILES += $(COMMON_DIR)/arrShorthandTest.dbd
62TESTS += arrShorthandTest70TESTS += arrShorthandTest
6371
64TESTPROD_HOST += benchdbConvert72TESTPROD_HOST += benchdbConvert
6573
=== modified file 'src/ioc/db/test/arrShorthandTest.c'
--- src/ioc/db/test/arrShorthandTest.c 2012-07-13 22:48:31 +0000
+++ src/ioc/db/test/arrShorthandTest.c 2014-07-23 21:19:10 +0000
@@ -77,7 +77,7 @@
77 testDiag("--------------------------------------------------------");77 testDiag("--------------------------------------------------------");
78}78}
7979
80void arrShorthandTest_registerRecordDeviceDriver(struct dbBase *);80void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
8181
82MAIN(arrShorthandTest)82MAIN(arrShorthandTest)
83{83{
@@ -88,12 +88,12 @@
88 db_init_events();88 db_init_events();
89 dbChannelInit();89 dbChannelInit();
9090
91 if (dbReadDatabase(&pdbbase, "arrShorthandTest.dbd",91 if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
92 "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR92 "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
93 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))93 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
94 testAbort("Database description 'arrShorthandTest.dbd' not found");94 testAbort("Database description 'dbTestIoc.dbd' not found");
9595
96 arrShorthandTest_registerRecordDeviceDriver(pdbbase);96 dbTestIoc_registerRecordDeviceDriver(pdbbase);
97 if (dbReadDatabase(&pdbbase, "xRecord.db",97 if (dbReadDatabase(&pdbbase, "xRecord.db",
98 "." OSI_PATH_LIST_SEPARATOR "..", NULL))98 "." OSI_PATH_LIST_SEPARATOR "..", NULL))
99 testAbort("Test database 'xRecord.db' not found");99 testAbort("Test database 'xRecord.db' not found");
100100
=== modified file 'src/ioc/db/test/chfPluginTest.c'
--- src/ioc/db/test/chfPluginTest.c 2014-06-04 09:55:40 +0000
+++ src/ioc/db/test/chfPluginTest.c 2014-07-23 21:19:10 +0000
@@ -481,7 +481,7 @@
481 testDiag("--------------------------------------------------------");481 testDiag("--------------------------------------------------------");
482}482}
483483
484void chfPluginTest_registerRecordDeviceDriver(struct dbBase *);484void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
485485
486MAIN(chfPluginTest)486MAIN(chfPluginTest)
487{487{
@@ -508,12 +508,12 @@
508 testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0,508 testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0,
509 "Enum to string: invalid index");509 "Enum to string: invalid index");
510510
511 if (dbReadDatabase(&pdbbase, "chfPluginTest.dbd",511 if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
512 "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR512 "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
513 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))513 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
514 testAbort("Database description 'chfPluginTest.dbd' not found");514 testAbort("Database description 'dbTestIoc.dbd' not found");
515515
516 chfPluginTest_registerRecordDeviceDriver(pdbbase);516 dbTestIoc_registerRecordDeviceDriver(pdbbase);
517 if (dbReadDatabase(&pdbbase, "xRecord.db",517 if (dbReadDatabase(&pdbbase, "xRecord.db",
518 "." OSI_PATH_LIST_SEPARATOR "..", NULL))518 "." OSI_PATH_LIST_SEPARATOR "..", NULL))
519 testAbort("Test database 'xRecord.db' not found");519 testAbort("Test database 'xRecord.db' not found");
520520
=== modified file 'src/ioc/db/test/dbChannelTest.c'
--- src/ioc/db/test/dbChannelTest.c 2012-07-13 22:48:31 +0000
+++ src/ioc/db/test/dbChannelTest.c 2014-07-23 21:19:10 +0000
@@ -21,6 +21,7 @@
21#include "epicsUnitTest.h"21#include "epicsUnitTest.h"
22#include "testMain.h"22#include "testMain.h"
23#include "osiFileName.h"23#include "osiFileName.h"
24#include "errlog.h"
2425
25/* Expected call bit definitions */26/* Expected call bit definitions */
26#define e_start 0x0000000127#define e_start 0x00000001
@@ -149,7 +150,7 @@
149 p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array,150 p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array,
150 c_open, c_reg_pre, c_reg_post, c_report, c_close };151 c_open, c_reg_pre, c_reg_post, c_report, c_close };
151152
152void dbChannelTest_registerRecordDeviceDriver(struct dbBase *);153void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
153154
154MAIN(testDbChannel) /* dbChannelTest is an API routine... */155MAIN(testDbChannel) /* dbChannelTest is an API routine... */
155{156{
@@ -157,12 +158,12 @@
157158
158 testPlan(66);159 testPlan(66);
159160
160 if (dbReadDatabase(&pdbbase, "dbChannelTest.dbd",161 if (dbReadDatabase(&pdbbase, "dbTestIoc.dbd",
161 "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR162 "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
162 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))163 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL))
163 testAbort("Database description 'dbChannelTest.dbd' not found");164 testAbort("Database description 'dbTestIoc.dbd' not found");
164165
165 dbChannelTest_registerRecordDeviceDriver(pdbbase);166 dbTestIoc_registerRecordDeviceDriver(pdbbase);
166 if (dbReadDatabase(&pdbbase, "xRecord.db",167 if (dbReadDatabase(&pdbbase, "xRecord.db",
167 "." OSI_PATH_LIST_SEPARATOR "..", NULL))168 "." OSI_PATH_LIST_SEPARATOR "..", NULL))
168 testAbort("Test database 'xRecord.db' not found");169 testAbort("Test database 'xRecord.db' not found");
@@ -199,7 +200,9 @@
199 testOk(!dbChannelCreate("y"), "Create, bad record");200 testOk(!dbChannelCreate("y"), "Create, bad record");
200 testOk(!dbChannelCreate("x.NOFIELD"), "Create, bad field");201 testOk(!dbChannelCreate("x.NOFIELD"), "Create, bad field");
201 testOk(!dbChannelCreate("x.{not-json}"), "Create, bad JSON");202 testOk(!dbChannelCreate("x.{not-json}"), "Create, bad JSON");
203 eltc(0);
202 testOk(!dbChannelCreate("x.{\"none\":null}"), "Create, bad filter");204 testOk(!dbChannelCreate("x.{\"none\":null}"), "Create, bad filter");
205 eltc(1);
203206
204 dbRegisterFilter("any", &testIf, NULL);207 dbRegisterFilter("any", &testIf, NULL);
205208
206209
=== added file 'src/ioc/db/test/dbPutLinkTest.c'
--- src/ioc/db/test/dbPutLinkTest.c 1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/dbPutLinkTest.c 2014-07-23 21:19:10 +0000
@@ -0,0 +1,110 @@
1/*************************************************************************\
2* Copyright (c) 2014 Brookhaven Science Assoc. as operator of Brookhaven
3* National Laboratory.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7
8/*
9 * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
10 */
11
12#include "string.h"
13
14#include "epicsString.h"
15#include "dbUnitTest.h"
16#include "epicsThread.h"
17#include "iocInit.h"
18#include "dbBase.h"
19#include "link.h"
20#include "dbAccess.h"
21#include "registry.h"
22#include "dbStaticLib.h"
23#include "osiFileName.h"
24#include "dbmf.h"
25#include "errlog.h"
26
27#include "xRecord.h"
28
29#include "testMain.h"
30
31void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
32
33static const struct testDataT {
34 const char *linkstring;
35 short linkType;
36 unsigned int pvlMask;
37 const char *linkback;
38} testSetData[] = {
39 {"", CONSTANT, 0},
40 {"0", CONSTANT, 0},
41 {"42", CONSTANT, 0},
42 {"x1", DB_LINK, 0, "x1 NPP NMS"},
43 {"x1.VAL", DB_LINK, 0, "x1.VAL NPP NMS"},
44 {"x1.TIME", DB_LINK, 0, "x1.TIME NPP NMS"},
45 {"x1 PP", DB_LINK, pvlOptPP, "x1 PP NMS"},
46 {"x1 PP MSS", DB_LINK, pvlOptPP|pvlOptMSS, "x1 PP MSS"},
47 {"x1 PPMSS", DB_LINK, pvlOptPP|pvlOptMSS, "x1 PP MSS"},
48 {"x1 PPMSI", DB_LINK, pvlOptPP|pvlOptMSI, "x1 PP MSI"},
49 /*TODO: testing doesn't support CA_LINK yet */
50 {NULL}
51};
52
53static void testSet(void)
54{
55 const struct testDataT *td = testSetData;
56 xRecord *prec;
57 DBLINK *plink;
58 testdbPrepare();
59
60 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
61
62 dbTestIoc_registerRecordDeviceDriver(pdbbase);
63
64 testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
65
66 eltc(0);
67 testIocInitOk();
68 eltc(1);
69
70 prec = (xRecord*)testdbRecordPtr("x1");
71 plink = &prec->lnk;
72
73 for(;td->linkstring;td++) {
74
75 testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring);
76 if(td->linkback)
77 testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback);
78 else
79 testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring);
80 testOk1(plink->type==td->linkType);
81
82 if(plink->type==td->linkType) {
83 switch(td->linkType) {
84 case CONSTANT:
85 if(plink->value.constantStr)
86 testOk1(strcmp(plink->value.constantStr,td->linkstring)==0);
87 else if(td->linkstring[0]=='\0')
88 testPass("Empty String");
89 else
90 testFail("oops");
91 break;
92
93 case DB_LINK:
94 testOk1(plink->value.pv_link.pvlMask==td->pvlMask);
95 break;
96 }
97 }
98 }
99
100 testIocShutdownOk();
101
102 testdbCleanup();
103}
104
105MAIN(dbPutLinkTest)
106{
107 testPlan(40);
108 testSet();
109 return testDone();
110}
0111
=== added file 'src/ioc/db/test/dbPutLinkTest.db'
--- src/ioc/db/test/dbPutLinkTest.db 1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/dbPutLinkTest.db 2014-07-23 21:19:10 +0000
@@ -0,0 +1,4 @@
1record(x, "x1") {}
2record(x, "x2") {}
3record(x, "x3") {}
4record(x, "x4") {}
05
=== 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-07-23 21:19:10 +0000
@@ -0,0 +1,96 @@
1/*************************************************************************\
2* Copyright (c) 2013 Brookhaven National Laboratory.
3* Copyright (c) 2013 ITER Organization.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7
8/*
9 * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
10 * Ralph Lange <Ralph.Lange@gmx.de>
11 */
12
13#include "epicsString.h"
14#include "dbUnitTest.h"
15#include "epicsThread.h"
16#include "iocInit.h"
17#include "dbBase.h"
18#include "dbAccess.h"
19#include "registry.h"
20#include "dbStaticLib.h"
21#include "osiFileName.h"
22#include "dbmf.h"
23#include "errlog.h"
24
25#include "testMain.h"
26
27void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
28
29static struct threadItem {
30 char *name;
31 char found;
32} commonThreads[] = {
33 { "errlog", 0 },
34 { "taskwd", 0 },
35 { "timerQueue", 0 },
36 { "cbLow", 0 },
37 { "scanOnce", 0 },
38 { NULL, 0 }
39};
40
41static
42void findCommonThread (epicsThreadId id) {
43 struct threadItem *thr;
44 char name[32];
45
46 epicsThreadGetName(id, name, 32);
47
48 for (thr = commonThreads; thr->name; thr++) {
49 if (epicsStrCaseCmp(thr->name, name) == 0) {
50 thr->found = 1;
51 }
52 }
53}
54
55static
56void checkCommonThreads (void) {
57 struct threadItem *thr;
58
59 for (thr = commonThreads; thr->name; thr++) {
60 testOk(thr->found, "Thread %s is running", thr->name);
61 thr->found = 0;
62 }
63}
64
65static
66void cycle(void) {
67
68 testdbPrepare();
69
70 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
71
72 dbTestIoc_registerRecordDeviceDriver(pdbbase);
73
74 testdbReadDatabase("xRecord.db", NULL, NULL);
75
76 eltc(0);
77 testIocInitOk();
78 eltc(1);
79
80 epicsThreadMap(findCommonThread);
81 checkCommonThreads();
82
83 testIocShutdownOk();
84
85 testdbCleanup();
86}
87
88MAIN(dbShutdownTest)
89{
90 testPlan(10);
91
92 cycle();
93 cycle();
94
95 return testDone();
96}
097
=== modified file 'src/ioc/db/test/epicsRunDbTests.c'
--- src/ioc/db/test/epicsRunDbTests.c 2014-06-13 19:37:11 +0000
+++ src/ioc/db/test/epicsRunDbTests.c 2014-07-23 21:19:10 +0000
@@ -19,6 +19,8 @@
19int testdbConvert(void);19int testdbConvert(void);
20int callbackTest(void);20int callbackTest(void);
21int dbStateTest(void);21int dbStateTest(void);
22int dbShutdownTest(void);
23int dbPutLinkTest(void);
22int testDbChannel(void);24int testDbChannel(void);
23int chfPluginTest(void);25int chfPluginTest(void);
24int arrShorthandTest(void);26int arrShorthandTest(void);
@@ -30,9 +32,11 @@
30 runTest(testdbConvert);32 runTest(testdbConvert);
31 runTest(callbackTest);33 runTest(callbackTest);
32 runTest(dbStateTest);34 runTest(dbStateTest);
35 runTest(dbShutdownTest);
36 runTest(dbPutLinkTest);
33 runTest(testDbChannel);37 runTest(testDbChannel);
38 runTest(arrShorthandTest);
34 runTest(chfPluginTest);39 runTest(chfPluginTest);
35 runTest(arrShorthandTest);
3640
37 dbmfFreeChunks();41 dbmfFreeChunks();
3842
3943
=== 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-07-23 21:19:10 +0000
@@ -0,0 +1,1 @@
1record(ai, "somename") {}
02
=== modified file 'src/ioc/db/test/xRecord.c'
--- src/ioc/db/test/xRecord.c 2012-06-29 15:09:56 +0000
+++ src/ioc/db/test/xRecord.c 2014-07-23 21:19:10 +0000
@@ -14,12 +14,24 @@
14 */14 */
1515
16#include "dbAccessDefs.h"16#include "dbAccessDefs.h"
17#include <recSup.h>17#include "recSup.h"
18#include "recGbl.h"
1819
19#define GEN_SIZE_OFFSET20#define GEN_SIZE_OFFSET
20#include "xRecord.h"21#include "xRecord.h"
2122
22#include <epicsExport.h>23#include <epicsExport.h>
2324
24static rset xRSET;25static long process(xRecord *prec)
26{
27 prec->pact = TRUE;
28 recGblGetTimeStamp(prec);
29 recGblFwdLink(prec);
30 prec->pact = FALSE;
31 return 0;
32}
33
34static rset xRSET = {
35 RSETNUMBER, NULL, NULL, NULL, process
36};
25epicsExportAddress(rset,xRSET);37epicsExportAddress(rset,xRSET);
2638
=== modified file 'src/ioc/db/test/xRecord.dbd'
--- src/ioc/db/test/xRecord.dbd 2012-06-29 15:09:56 +0000
+++ src/ioc/db/test/xRecord.dbd 2014-07-23 21:19:10 +0000
@@ -1,12 +1,11 @@
1# This is a combined minimal DBD and DB file1# This is a minimal record definition
22
3recordtype(x) {3recordtype(x) {
4 field(NAME, DBF_STRING) {4 include "dbCommon.dbd"
5 prompt("Record Name")
6 special(SPC_NOMOD)
7 size(61)
8 }
9 field(VAL, DBF_LONG) {5 field(VAL, DBF_LONG) {
10 prompt("Value")6 prompt("Value")
11 }7 }
8 field(LNK, DBF_INLINK) {
9 prompt("Link")
10 }
12}11}
1312
=== 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-07-23 21:19:10 +0000
@@ -5,6 +5,9 @@
5# This file provides iocsh access to variables that control some lesser-used5# This file provides iocsh access to variables that control some lesser-used
6# and debugging features of the IOC database code.6# and debugging features of the IOC database code.
77
8# show epicsAtExit callbacks as they are run
9variable(atExitDebug,int)
10
8# Access security subroutines11# Access security subroutines
9variable(asCaDebug,int)12variable(asCaDebug,int)
1013
1114
=== 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-07-23 21:19:10 +0000
@@ -3,6 +3,8 @@
3* National Laboratory.3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.5* Operator of Los Alamos National Laboratory.
6* Copyright (c) 2013 Helmholtz-Zentrum Berlin
7* für Materialien und Energie GmbH.
6* EPICS BASE is distributed subject to a Software License Agreement found8* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution. 9* in file LICENSE that is included with this distribution.
8\*************************************************************************/10\*************************************************************************/
@@ -31,6 +33,7 @@
31#include "errMdef.h"33#include "errMdef.h"
32#include "taskwd.h"34#include "taskwd.h"
33#include "caeventmask.h"35#include "caeventmask.h"
36#include "iocsh.h"
3437
35#define epicsExportSharedSymbols38#define epicsExportSharedSymbols
36#include "alarm.h"39#include "alarm.h"
@@ -88,12 +91,13 @@
88 return iocBuild() || iocRun();91 return iocBuild() || iocRun();
89}92}
9093
91int iocBuild(void)94static int iocBuild_1(void)
92{95{
93 if (iocState != iocVirgin) {96 if (iocState != iocVirgin && iocState != iocStopped) {
94 errlogPrintf("iocBuild: IOC can only be initialized once\n");97 errlogPrintf("iocBuild: IOC can only be initialized from uninitialized or stopped state\n");
95 return -1;98 return -1;
96 }99 }
100 errlogInit(0);
97 initHookAnnounce(initHookAtIocBuild);101 initHookAnnounce(initHookAtIocBuild);
98102
99 if (!epicsThreadIsOkToBlock()) {103 if (!epicsThreadIsOkToBlock()) {
@@ -109,14 +113,17 @@
109 initHookAnnounce(initHookAtBeginning);113 initHookAnnounce(initHookAtBeginning);
110114
111 coreRelease();115 coreRelease();
112 /* After this point, further calls to iocInit() are disallowed. */
113 iocState = iocBuilding;116 iocState = iocBuilding;
114117
115 taskwdInit();118 taskwdInit();
116 callbackInit();119 callbackInit();
117 initHookAnnounce(initHookAfterCallbackInit);120 initHookAnnounce(initHookAfterCallbackInit);
118121
119 dbCaLinkInit();122 return 0;
123}
124
125static int iocBuild_2(void)
126{
120 initHookAnnounce(initHookAfterCaLinkInit);127 initHookAnnounce(initHookAfterCaLinkInit);
121128
122 initDrvSup();129 initDrvSup();
@@ -147,9 +154,11 @@
147154
148 initialProcess();155 initialProcess();
149 initHookAnnounce(initHookAfterInitialProcess);156 initHookAnnounce(initHookAfterInitialProcess);
157 return 0;
158}
150159
151 /* Start CA server threads */160static int iocBuild_3(void)
152 rsrv_init();161{
153 initHookAnnounce(initHookAfterCaServerInit);162 initHookAnnounce(initHookAfterCaServerInit);
154163
155 iocState = iocBuilt;164 iocState = iocBuilt;
@@ -157,6 +166,39 @@
157 return 0;166 return 0;
158}167}
159168
169int iocBuild(void)
170{
171 int status;
172
173 status = iocBuild_1();
174 if (status) return status;
175
176 dbCaLinkInit();
177
178 status = iocBuild_2();
179 if (status) return status;
180
181 /* Start CA server threads */
182 rsrv_init();
183
184 status = iocBuild_3();
185 return status;
186}
187
188int iocBuildIsolated(void)
189{
190 int status;
191
192 status = iocBuild_1();
193 if (status) return status;
194
195 status = iocBuild_2();
196 if (status) return status;
197
198 status = iocBuild_3();
199 return status;
200}
201
160int iocRun(void)202int iocRun(void)
161{203{
162 if (iocState != iocPaused && iocState != iocBuilt) {204 if (iocState != iocPaused && iocState != iocBuilt) {
@@ -599,8 +641,31 @@
599 }641 }
600}642}
601643
602static void exitDatabase(void *dummy)644static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord,
603{645 void *user)
646{
647 struct rset *prset = pdbRecordType->prset;
648
649 if (!prset) return; /* unlikely */
650
651 epicsMutexDestroy(precord->mlok);
652}
653
654int iocShutdown(void)
655{
656 if (iocState == iocVirgin || iocState == iocStopped) return 0;
604 iterateRecords(doCloseLinks, NULL);657 iterateRecords(doCloseLinks, NULL);
658 scanShutdown();
659 callbackShutdown();
660 iterateRecords(doFreeRecord, NULL);
661 dbLockCleanupRecords(pdbbase);
662 asShutdown();
663 iocshFree();
605 iocState = iocStopped;664 iocState = iocStopped;
665 return 0;
666}
667
668static void exitDatabase(void *dummy)
669{
670 iocShutdown();
606}671}
607672
=== 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-07-23 21:19:10 +0000
@@ -19,8 +19,10 @@
1919
20epicsShareFunc int iocInit(void);20epicsShareFunc int iocInit(void);
21epicsShareFunc int iocBuild(void);21epicsShareFunc int iocBuild(void);
22epicsShareFunc int iocBuildIsolated(void);
22epicsShareFunc int iocRun(void);23epicsShareFunc int iocRun(void);
23epicsShareFunc int iocPause(void);24epicsShareFunc int iocPause(void);
25epicsShareFunc int iocShutdown(void);
2426
25#ifdef __cplusplus27#ifdef __cplusplus
26}28}
2729
=== 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-07-23 21:19:10 +0000
@@ -222,6 +222,7 @@
222/*following is "friend" function*/222/*following is "friend" function*/
223epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size);223epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size);
224epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str);224epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str);
225epicsShareFunc void asFreeAll(ASBASE *pasbase);
225#ifdef __cplusplus226#ifdef __cplusplus
226}227}
227#endif228#endif
228229
=== 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-07-23 21:19:10 +0000
@@ -51,7 +51,6 @@
51static long asComputeAllAsgPvt(void);51static long asComputeAllAsgPvt(void);
52static long asComputeAsgPvt(ASG *pasg);52static long asComputeAsgPvt(ASG *pasg);
53static long asComputePvt(ASCLIENTPVT asClientPvt);53static long asComputePvt(ASCLIENTPVT asClientPvt);
54static void asFreeAll(ASBASE *pasbase);
55static UAG *asUagAdd(const char *uagName);54static UAG *asUagAdd(const char *uagName);
56static long asUagAddUser(UAG *puag,const char *user);55static long asUagAddUser(UAG *puag,const char *user);
57static HAG *asHagAdd(const char *hagName);56static HAG *asHagAdd(const char *hagName);
@@ -1015,7 +1014,7 @@
1015 return(0);1014 return(0);
1016}1015}
10171016
10181017
1019static void asFreeAll(ASBASE *pasbase)1018void asFreeAll(ASBASE *pasbase)
1020{1019{
1021 UAG *puag;1020 UAG *puag;
1022 UAGNAME *puagname;1021 UAGNAME *puagname;
10231022
=== 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-07-23 21:19:10 +0000
@@ -41,7 +41,7 @@
41/*Declare storage for errVerbose */41/*Declare storage for errVerbose */
42epicsShareDef int errVerbose = 0;42epicsShareDef int errVerbose = 0;
4343
44static void exitHandler(void *);44static void errlogExitHandler(void *);
45static void errlogThread(void);45static void errlogThread(void);
4646
47static char *msgbufGetFree(int noConsoleMessage);47static char *msgbufGetFree(int noConsoleMessage);
@@ -70,8 +70,8 @@
70 epicsEventId waitForFlush; /*errlogFlush waits for this*/70 epicsEventId waitForFlush; /*errlogFlush waits for this*/
71 epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/71 epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/
72 epicsMutexId flushLock;72 epicsMutexId flushLock;
73 epicsEventId waitForExit; /*exitHandler waits for this*/73 epicsEventId waitForExit; /*errlogExitHandler waits for this*/
74 int atExit; /*TRUE when exitHandler is active*/74 int atExit; /*TRUE when errlogExitHandler is active*/
75 ELLLIST listenerList;75 ELLLIST listenerList;
76 ELLLIST msgQueue;76 ELLLIST msgQueue;
77 msgNode *pnextSend;77 msgNode *pnextSend;
@@ -368,6 +368,7 @@
368epicsShareFunc int epicsShareAPI eltc(int yesno)368epicsShareFunc int epicsShareAPI eltc(int yesno)
369{369{
370 errlogInit(0);370 errlogInit(0);
371 errlogFlush();
371 pvtData.toConsole = yesno;372 pvtData.toConsole = yesno;
372 return 0;373 return 0;
373}374}
@@ -447,7 +448,7 @@
447}448}
448449
449450
450static void exitHandler(void *pvt)451static void errlogExitHandler(void *pvt)
451{452{
452 pvtData.atExit = 1;453 pvtData.atExit = 1;
453 epicsEventSignal(pvtData.waitForWork);454 epicsEventSignal(pvtData.waitForWork);
@@ -561,7 +562,7 @@
561 int noConsoleMessage;562 int noConsoleMessage;
562 char *pmessage;563 char *pmessage;
563564
564 epicsAtExit(exitHandler,0);565 epicsAtExit(errlogExitHandler,0);
565 while (TRUE) {566 while (TRUE) {
566 epicsEventMustWait(pvtData.waitForWork);567 epicsEventMustWait(pvtData.waitForWork);
567 while ((pmessage = msgbufGetSend(&noConsoleMessage))) {568 while ((pmessage = msgbufGetSend(&noConsoleMessage))) {
568569
=== 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-07-23 21:19:10 +0000
@@ -203,20 +203,22 @@
203 */203 */
204void epicsShareAPI iocshFree(void) 204void epicsShareAPI iocshFree(void)
205{205{
206 struct iocshCommand *pc, *nc;206 struct iocshCommand *pc;
207 struct iocshVariable *pv, *nv;207 struct iocshVariable *pv;
208208
209 iocshTableLock ();209 iocshTableLock ();
210 for (pc = iocshCommandHead ; pc != NULL ; ) {210 for (pc = iocshCommandHead ; pc != NULL ; ) {
211 nc = pc->next;211 struct iocshCommand * nc = pc->next;
212 free (pc);212 free (pc);
213 pc = nc;213 pc = nc;
214 }214 }
215 for (pv = iocshVariableHead ; pv != NULL ; ) {215 for (pv = iocshVariableHead ; pv != NULL ; ) {
216 nv = pv->next;216 struct iocshVariable *nv = pv->next;
217 free (pv);217 free (pv);
218 pv = nv;218 pv = nv;
219 }219 }
220 iocshCommandHead = NULL;
221 iocshVariableHead = NULL;
220 iocshTableUnlock ();222 iocshTableUnlock ();
221}223}
222224
223225
=== 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-07-23 21:19:10 +0000
@@ -23,7 +23,9 @@
23 */23 */
2424
25#include <stdlib.h>25#include <stdlib.h>
26#include <stdio.h>
26#include <errno.h>27#include <errno.h>
28#include <string.h>
2729
28#define epicsExportSharedSymbols30#define epicsExportSharedSymbols
29#include "ellLib.h"31#include "ellLib.h"
@@ -36,12 +38,15 @@
36 ELLNODE node;38 ELLNODE node;
37 epicsExitFunc func;39 epicsExitFunc func;
38 void *arg;40 void *arg;
41 char name[1];
39}exitNode;42}exitNode;
4043
41typedef struct exitPvt {44typedef struct exitPvt {
42 ELLLIST list;45 ELLLIST list;
43} exitPvt;46} exitPvt;
4447
48int atExitDebug = 0;
49
45static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT;50static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT;
46static exitPvt * pExitPvtPerProcess = 0;51static exitPvt * pExitPvtPerProcess = 0;
47static epicsMutexId exitPvtLock = 0;52static epicsMutexId exitPvtLock = 0;
@@ -66,15 +71,23 @@
66{71{
67 exitPvtPerThread = epicsThreadPrivateCreate ();72 exitPvtPerThread = epicsThreadPrivateCreate ();
68 assert ( exitPvtPerThread );73 assert ( exitPvtPerThread );
69 pExitPvtPerProcess = createExitPvt ();
70 assert ( pExitPvtPerProcess );
71 exitPvtLock = epicsMutexMustCreate ();74 exitPvtLock = epicsMutexMustCreate ();
72}75}
7376
77static void epicsExitInit(void)
78{
79 epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );
80}
81
74static void epicsExitCallAtExitsPvt(exitPvt *pep)82static void epicsExitCallAtExitsPvt(exitPvt *pep)
75{83{
76 exitNode *pexitNode;84 exitNode *pexitNode;
85
77 while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) {86 while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) {
87 if (atExitDebug && pexitNode->name[0])
88 fprintf(stderr, "atExit %s(%p)\n", pexitNode->name, pexitNode->arg);
89 else if(atExitDebug)
90 fprintf(stderr, "atExit %p(%p)\n", pexitNode->func, pexitNode->arg);
78 pexitNode->func ( pexitNode->arg );91 pexitNode->func ( pexitNode->arg );
79 ellDelete ( & pep->list, & pexitNode->node );92 ellDelete ( & pep->list, & pexitNode->node );
80 free ( pexitNode );93 free ( pexitNode );
@@ -84,7 +97,8 @@
84epicsShareFunc void epicsExitCallAtExits(void)97epicsShareFunc void epicsExitCallAtExits(void)
85{98{
86 exitPvt * pep = 0;99 exitPvt * pep = 0;
87 epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );100
101 epicsExitInit ();
88 epicsMutexMustLock ( exitPvtLock );102 epicsMutexMustLock ( exitPvtLock );
89 if ( pExitPvtPerProcess ) {103 if ( pExitPvtPerProcess ) {
90 pep = pExitPvtPerProcess;104 pep = pExitPvtPerProcess;
@@ -100,7 +114,8 @@
100epicsShareFunc void epicsExitCallAtThreadExits(void)114epicsShareFunc void epicsExitCallAtThreadExits(void)
101{115{
102 exitPvt * pep;116 exitPvt * pep;
103 epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );117
118 epicsExitInit ();
104 pep = epicsThreadPrivateGet ( exitPvtPerThread );119 pep = epicsThreadPrivateGet ( exitPvtPerThread );
105 if ( pep ) {120 if ( pep ) {
106 epicsExitCallAtExitsPvt ( pep );121 epicsExitCallAtExitsPvt ( pep );
@@ -109,14 +124,16 @@
109 }124 }
110}125}
111126
112static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg)127static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const char *name)
113{128{
114 int status = -1;129 int status = -1;
115 exitNode * pExitNode130 exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) );
116 = calloc ( 1, sizeof( *pExitNode ) );131
117 if ( pExitNode ) {132 if ( pExitNode ) {
118 pExitNode->func = func;133 pExitNode->func = func;
119 pExitNode->arg = arg;134 pExitNode->arg = arg;
135 if(name)
136 strcpy(pExitNode->name, name);
120 ellAdd ( & pep->list, & pExitNode->node );137 ellAdd ( & pep->list, & pExitNode->node );
121 status = 0;138 status = 0;
122 }139 }
@@ -126,7 +143,8 @@
126epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg)143epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg)
127{144{
128 exitPvt * pep;145 exitPvt * pep;
129 epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );146
147 epicsExitInit ();
130 pep = epicsThreadPrivateGet ( exitPvtPerThread );148 pep = epicsThreadPrivateGet ( exitPvtPerThread );
131 if ( ! pep ) {149 if ( ! pep ) {
132 pep = createExitPvt ();150 pep = createExitPvt ();
@@ -135,16 +153,20 @@
135 }153 }
136 epicsThreadPrivateSet ( exitPvtPerThread, pep );154 epicsThreadPrivateSet ( exitPvtPerThread, pep );
137 }155 }
138 return epicsAtExitPvt ( pep, func, arg );156 return epicsAtExitPvt ( pep, func, arg, NULL );
139}157}
140158
141epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg)159epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name)
142{160{
143 int status = -1;161 int status = -1;
144 epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );162
163 epicsExitInit ();
145 epicsMutexMustLock ( exitPvtLock );164 epicsMutexMustLock ( exitPvtLock );
165 if ( !pExitPvtPerProcess ) {
166 pExitPvtPerProcess = createExitPvt ();
167 }
146 if ( pExitPvtPerProcess ) {168 if ( pExitPvtPerProcess ) {
147 status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg );169 status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg, name );
148 }170 }
149 epicsMutexUnlock ( exitPvtLock );171 epicsMutexUnlock ( exitPvtLock );
150 return status;172 return status;
@@ -153,6 +175,10 @@
153epicsShareFunc void epicsExit(int status)175epicsShareFunc void epicsExit(int status)
154{176{
155 epicsExitCallAtExits();177 epicsExitCallAtExits();
156 epicsThreadSleep(1.0);178 epicsThreadSleep(0.1);
157 exit(status);179 exit(status);
158}180}
181
182#include "epicsExport.h"
183
184epicsExportAddress(int,atExitDebug);
159185
=== 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-07-23 21:19:10 +0000
@@ -19,7 +19,8 @@
1919
20epicsShareFunc void epicsExit(int status);20epicsShareFunc void epicsExit(int status);
21epicsShareFunc void epicsExitCallAtExits(void);21epicsShareFunc void epicsExitCallAtExits(void);
22epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg);22epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name);
23#define epicsAtExit(F,A) epicsAtExit3(F,A,#F)
2324
24epicsShareFunc void epicsExitCallAtThreadExits(void);25epicsShareFunc void epicsExitCallAtThreadExits(void);
25epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg);26epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg);
2627
=== modified file 'src/libCom/misc/epicsUnitTest.h'
--- src/libCom/misc/epicsUnitTest.h 2010-10-05 19:27:37 +0000
+++ src/libCom/misc/epicsUnitTest.h 2014-07-23 21:19:10 +0000
@@ -9,6 +9,9 @@
9 * Author: Andrew Johnson9 * Author: Andrew Johnson
10 */10 */
1111
12#ifndef INC_epicsUnitTest_H
13#define INC_epicsUnitTest_H
14
12#include <stdarg.h>15#include <stdarg.h>
1316
14#include "compilerDependencies.h"17#include "compilerDependencies.h"
@@ -47,3 +50,5 @@
47#ifdef __cplusplus50#ifdef __cplusplus
48}51}
49#endif52#endif
53
54#endif /* INC_epicsUnitTest_H */
5055
=== 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-07-23 21:19:10 +0000
@@ -59,12 +59,20 @@
59 testDiag("%s starting", pinfo->name);59 testDiag("%s starting", pinfo->name);
60 pinfo->terminate = epicsEventMustCreate(epicsEventEmpty);60 pinfo->terminate = epicsEventMustCreate(epicsEventEmpty);
61 pinfo->terminated = epicsEventMustCreate(epicsEventEmpty);61 pinfo->terminated = epicsEventMustCreate(epicsEventEmpty);
62 epicsAtExit(atExit, pinfo);62 testOk(!epicsAtExit(atExit, pinfo), "Registered atExit(%p)", pinfo);
63 epicsAtThreadExit(atThreadExit, pinfo);63 testOk(!epicsAtThreadExit(atThreadExit, pinfo),
64 "Registered atThreadExit(%p)", pinfo);
64 testDiag("%s waiting for atExit", pinfo->name);65 testDiag("%s waiting for atExit", pinfo->name);
65 epicsEventMustWait(pinfo->terminate);66 epicsEventMustWait(pinfo->terminate);
66}67}
6768
69int count;
70
71static void counter(void *pvt)
72{
73 count++;
74}
75
68static void mainExit(void *pvt)76static void mainExit(void *pvt)
69{77{
70 testPass("Reached mainExit");78 testPass("Reached mainExit");
@@ -77,16 +85,23 @@
77 info *pinfoA = (info *)calloc(1, sizeof(info));85 info *pinfoA = (info *)calloc(1, sizeof(info));
78 info *pinfoB = (info *)calloc(1, sizeof(info));86 info *pinfoB = (info *)calloc(1, sizeof(info));
7987
80 testPlan(7);88 testPlan(15);
8189
82 epicsAtExit(mainExit, NULL);90 testOk(!epicsAtExit(counter, NULL), "Registered counter()");
91 count = 0;
92 epicsExitCallAtExits();
93 testOk(count == 1, "counter() called once");
94 epicsExitCallAtExits();
95 testOk(count == 1, "unregistered counter() not called");
96
97 testOk(!epicsAtExit(mainExit, NULL), "Registered mainExit()");
8398
84 epicsThreadCreate("threadA", 50, stackSize, thread, pinfoA);99 epicsThreadCreate("threadA", 50, stackSize, thread, pinfoA);
85 epicsThreadSleep(0.1);100 epicsThreadSleep(0.1);
86 epicsThreadCreate("threadB", 50, stackSize, thread, pinfoB);101 epicsThreadCreate("threadB", 50, stackSize, thread, pinfoB);
87 epicsThreadSleep(1.0);102 epicsThreadSleep(1.0);
88103
89 testDiag("Calling epicsExit\n");104 testDiag("Calling epicsExit");
90 epicsExit(0);105 epicsExit(0);
91 return 0;106 return 0;
92}107}

Subscribers

People subscribed via source and target branches

to all changes: