Merge lp:~epics-core/epics-base/ioc-shutdown2 into lp:~epics-core/epics-base/3.15
- ioc-shutdown2
- Merge into 3.15
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 |
Related bugs: |
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 |
Commit message
Description of the change
As discussed, I re-based the changes from ioc-shutdown with some additional cleanup, and removed the epicsThreadOnce
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().
Andrew Johnson (anj) wrote : | # |
Committed my changes as described in 1st section above.
mdavidsaver (mdavidsaver) wrote : | # |
> In dbUnitTest.c shouldn't the Type arguments to OP use the
> epics{Int,
> 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(
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
mdavidsaver (mdavidsaver) wrote : | # |
Function names changed as requested.
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,
>> 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(
> "%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
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:
testdbReadD
testIocInit
epicsThread
checkCommon
testIocShut
testdbClean
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
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.
Andrew Johnson (anj) wrote : | # |
Ok, sorry. If you're interested, https:/
- 12510. By mdavidsaver
-
dbUnitTest: testIocInitOk/
testIocShutdown Ok testAbort() on failure - 12511. By mdavidsaver
-
dbUnitTest: replace testdbPutField()
add testdbPutFieldOk() and testdbPutFieldF
ail()
which include calls to testPass() or testFail()Leave testdbVPutField() as a building block.
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()?
mdavidsaver (mdavidsaver) wrote : | # |
> Ok, sorry.
np. These changes should now be complete.
> If you're interested, https:/
Oh yes :)
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
mdavidsaver (mdavidsaver) wrote : | # |
FYI I'm working on a few more changes. Fixing a mistake in dbLockCleanupRe
Also, I have some test code for dbPutFieldLink(). Should I include the present (working) version in this branch?
- 12512. By mdavidsaver
-
dbLock: fix dbLockCleanupRe
cords all lockRecord s allocated in one block. oops.
- 12513. By mdavidsaver
-
dbUnitTest: add testdbGetFieldE
qual() - 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
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.
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 dbLockCleanupRe
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 dbLockCleanupRe
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
J. Lewis Muir (jlmuir) wrote : | # |
On 7/11/14, 5:09 PM, Andrew Johnson wrote:
> The dbLockCleanupRe
Is "Cleanup" in the function name "dbLockCleanupR
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. "dbLockCleanUpR
is a verb.
Or maybe there's already a precedent spelling in existing code, and
"dbLockCleanupR
Lewis
- 12516. By mdavidsaver
-
dbLock: another fix to dbLockCleanupRe
cords() - 12517. By mdavidsaver
-
dbLock: free lockSets
mdavidsaver (mdavidsaver) wrote : | # |
> On 07/11/2014 04:06 PM, mdavidsaver wrote:
> > Ok, thats it. Barring bugs or futher requests, this is done.
>
> The dbLockCleanupRe
That's because I copy+pasted the wrong function. Replaced dbNextRecordType() with dbFirstRecord().
...
> It seems to me that dbLockCleanupRe
> lockSetList[] lists, which contain lockSet objects.
Added.
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 dbLockCleanupRe
>
> 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 dbLockCleanupRe
cords() track the lockRecord allocation
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
Andrew Johnson (anj) wrote : | # |
Ralph, any comments before we merge this?
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...")
- 12520. By Andrew Johnson
-
Suppress errlog output of expected warning messages.
Preview Diff
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 | } |
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; est.c was calling the non-universal strcasecmp()
dbShutdownT
instead of epicsStrCaseCmp().
I would prefer that ioc/db/test not depend on std. I modified est() to epicsRunDbTests().
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
dbShutdownT
------- ------- ------- ------- ------- ------- ------- ------- ------
I would like to see these issues addressed:
I don't like the public name iocBuildNoCA(), if anything the thesaurus. com/browse/ isolated for some alternative
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://
adjectives, Sequestered would also be good but it's even
longer).
In dbUnitTest.c shouldn't the Type arguments to OP use the Int,UInt, Float}< n> names? If not I suspect there
epics{
should at least be a couple more 'unsigned' keywords in
there.
The dbUnitTest.h API can extend the epicsUnitTest.h one and add downOk( ) [note capitalization and name change]
its own Ok() routines. I'd suggest testIocInitOk() and
testIocShut
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 dPtr() maybe?), and should say "record" in its
testdbRecor
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 checkNoCommonTh reads() 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