Merge lp:~epics-core/epics-base/ioc-shutdown into lp:~epics-core/epics-base/3.15
- ioc-shutdown
- Merge into 3.15
Status: | Rejected |
---|---|
Rejected by: | Andrew Johnson |
Proposed branch: | lp:~epics-core/epics-base/ioc-shutdown |
Merge into: | lp:~epics-core/epics-base/3.15 |
Diff against target: |
1578 lines (+646/-80) 36 files modified
src/Makefile (+1/-1) 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 (+15/-2) src/ioc/db/callback.h (+1/-0) src/ioc/db/dbCa.c (+18/-7) src/ioc/db/dbCa.h (+1/-0) src/ioc/db/dbScan.c (+39/-4) src/ioc/db/dbScan.h (+1/-0) src/ioc/db/dbUnitTest.c (+123/-0) src/ioc/db/dbUnitTest.h (+54/-0) src/ioc/db/initHooks.c (+8/-0) src/ioc/db/initHooks.h (+1/-0) src/ioc/db/test/Makefile (+9/-0) src/ioc/db/test/dbShutdownTest.c (+94/-0) src/ioc/db/test/sRecord.db (+1/-0) src/ioc/misc/dbCore.dbd (+3/-0) src/ioc/misc/iocInit.c (+63/-8) 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 (+37/-20) src/libCom/error/errlog.h (+1/-0) src/libCom/iocsh/iocsh.cpp (+6/-4) src/libCom/misc/epicsExit.c (+39/-13) src/libCom/misc/epicsExit.h (+2/-1) src/libCom/osi/epicsThread.h (+3/-0) src/libCom/osi/os/RTEMS/osdThread.c (+9/-0) src/libCom/osi/os/WIN32/osdThread.c (+11/-0) src/libCom/osi/os/posix/osdThread.c (+19/-0) src/libCom/osi/os/vxWorks/osdThread.c (+13/-1) src/libCom/taskwd/taskwd.c (+23/-9) src/libCom/taskwd/taskwd.h (+1/-0) src/libCom/test/epicsExitTest.c (+21/-6) src/libCom/test/epicsThreadOnceTest.c (+13/-1) |
To merge this branch: | bzr merge lp:~epics-core/epics-base/ioc-shutdown |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Johnson | Disapprove | ||
Review via email: mp+190512@code.launchpad.net |
Commit message
Description of the change
Add iocBuildNoCA() and iocShutdown() to iocInit API to allow running an IOC from withing a test program that is part of a test harness.
Doc changes in https:/
mdavidsaver (mdavidsaver) wrote : | # |
mdavidsaver (mdavidsaver) wrote : | # |
> static volatile enum ctl cbCtl;
Also, while I don't think this case will cause any problem, do we want to start using atomic ops for global flags like this as a matter of good practice?
mdavidsaver (mdavidsaver) wrote : | # |
Looks like re-init does quite work. Remaining issues include:
> Warning: Registration already done.
From the generated registrar code. Only a warning.
> Warning -- iocshRegisterVa
From iocsh.c. The lists for registered shell functions and variables need to be cleared.
> Access security is NOT enabled. Was asSetFilename specified before iocInit?
> iocBuild: asInit Failed.
The 'firstTime' flag (at least) in asDbLib.c also needs to be reset.
mdavidsaver (mdavidsaver) wrote : | # |
> Looks like re-init does quite work.
doesn't
- 12446. By mdavidsaver
-
dbShutdownTest
- 12447. By mdavidsaver
-
asShutdown
- 12448. By mdavidsaver
-
cleanup iocsh
- 12449. By mdavidsaver
-
cleanup dbScan
mdavidsaver (mdavidsaver) wrote : | # |
re-init crashes and errors are fixed (I think). Still leaking memory in dbReadCOM().
I've added a new test dbShutdownTest which does the complete startup and shutdown sequence twice. This is added in src/ioc/db/test despite the fact that it depends on src/std (ie base.dbd) because I'm too lazy to create src/std/test (or similar).
- 12450. By Ralph Lange
-
ioc/db/test: Add tests for common IOC threads to dbShutdownTest.c
Ralph Lange (ralph-lange) wrote : | # |
Seems that only the scan threads are restarted.
errlog, timerQueue, taskwd, and callback threads are missing in the second cycle().
Added a test that shows this.
- 12451. By Ralph Lange
-
ioc/db: Fix restart issue for callback threads.
- 12452. By Ralph Lange
-
libCom/error: Fix restart issue for errlog facility
- 12453. By Ralph Lange
-
libCom/taskwd: Fix restart issue.
- 12454. By Ralph Lange
-
ioc/db/test: Add testPlan number for dbShutdownTest
Ralph Lange (ralph-lange) wrote : | # |
Fixed the restart issues - the extended dbShutdownTests pass now.
- 12455. By Ralph Lange
-
ioc/misc: add missing include in iocInit.c
Andrew Johnson (anj) wrote : | # |
For future reference: With almost 2**10 lines of diff this branch mixes together (admittedly related) changes to several different subsystems, which makes it hard to review (Jeff's 3.15_libcom_
The documentation branch does not cover all the changes made here, it only talks about the modifications to the callback subsystem.
Is it wise to provide public APIs to all these Shutdown functions? I'm not so much concerned that this code doesn't work as whether by making them available we're letting ourselves in for a maintenance nightmare in the future. Do all these subsystems *need* to be shut down and restarted? What are the criteria for choosing which ones should and shouldn't be included? Could we shut everything down by calling epicsExitCallAt
The idea of resetting OnceFlags makes me cringe; they were never designed for that, and I'm not convinced that doing so is completely safe. The state change from EPICS_THREAD_
I'm not convinced that restarting an IOC inside the same process is a good idea, given the code we have (I know you're trying to change that, but I think it needs a properly planned solution, which this isn't [yet]). Just making the shutdown APIs available will mean that someone will want/try to use them on a running hard IOC, which is not going to work since we can't communicate the shutdown request to any device support or driver threads (other than by calling their epicsExitCallAt
IMHO if you need to test two independent IOCs, they need to be in two separate processes. You could split them into two test programs, or write the test as a Perl script which starts, controls and stops the IOCs as sub-processes; I have a Perl module which can do that on a host OS and could be extended to control an embedded IOC.
This isn't a definite NO, but I will need convincing that this is the right way to go.
- Andrew
mdavidsaver (mdavidsaver) wrote : | # |
On 12/03/2013 02:35 PM, Andrew Johnson wrote:
> For future reference: With almost 2**10 lines of diff this branch mixes together (admittedly related) changes to several different subsystems,
??? The merge proposal shows:
961 lines (+337/-53) 21 files modified
Also, the branch consists of many small, focused, changesets.
> The documentation branch does not cover all the changes made here, it only talks about the modifications to the callback subsystem.
Agreed, this needs to change.
> Is it wise to provide public APIs to all these Shutdown functions?
I would like to wrap all of this up into a single function (say
"testdbCleanup"). We could then make private header(s) for the many
shutdown calls. Would this make the change more acceptable.
> Could we shut everything down by calling epicsExitCallAt
This is an option I suppose.
> The idea of resetting OnceFlags makes me cringe;
A good point.
> I'm not convinced that restarting an IOC inside the same process is a good idea, given the code we have (I know you're trying to change that, but I think it needs a properly planned solution, which this isn't [yet]). Just making the shutdown APIs available will mean that someone will want/try to use them on a running hard IOC, which is not going to work since we can't communicate the shutdown request to any device support or driver threads (other than by calling their epicsExitCallAt
The goal is unittests. It it would help in your mind we can only allow
shutdown/cleanup between calling testPlan() and testDone().
Andrew Johnson (anj) wrote : | # |
On 12/03/2013 03:39 PM, mdavidsaver wrote:
> On 12/03/2013 02:35 PM, Andrew Johnson wrote:
>> For future reference: With almost 2**10 lines of diff this branch mixes
> together (admittedly related) changes to several different subsystems,
>
> ??? The merge proposal shows:
>
> 961 lines (+337/-53) 21 files modified
961 ≈ 1024
I'm just saying that the longer the diff is, the more reviewers will be
put off looking at it. If the changes were split into smaller dependent
branches, reviewing them might look like less work and could be done in
smaller chunks (this was a human psychology comment, not a programming
issue at all).
>> Is it wise to provide public APIs to all these Shutdown functions?
>
> I would like to wrap all of this up into a single function (say
> "testdbCleanup"). We could then make private header(s) for the many
> shutdown calls. Would this make the change more acceptable.
Hmm, maybe. How about an alternative API to iocInit, for test programs
only, and implemented outside of iocInit.c? I would be quite happy for
the various init*Sup() and related routines to be moved out of there and
into ioc/db somewhere, dbInit.c maybe?
>> Could we shut everything down by calling epicsExitCallAt
> and just ensure that each system's registered shutdown function leaves
> the subsystem in a state that can be properly restarted?
>
> This is an option I suppose.
The advantage of using epicsExitCallAt
expose the various Shutdown symbols at all, and we automatically stop
everything that we've started, in the (correct) reverse order.
The disadvantage is this will slow down IOC shutdown unnecessarily,
since we only need the 'clean up ready to start up again' actions in
those exit routines when we're running tests that need restart.
I'm getting visions of a subsystem management API; subsystems would
register both a fast exit and a slow clean-up routine with it. It could
even incorporate the taskwd, and provide subsystem/task status & health
info on request. This could also somewhere to centralize all of the
init/run/pause/exit enum task controls that are proliferating (which I
agree should probably become atomics).
Too much work?
- Andrew
--
Advertising may be described as the science of arresting the human
intelligence long enough to get money from it. -- Stephen Leacock
Ralph Lange (ralph-lange) wrote : | # |
> On 12/03/2013 03:39 PM, mdavidsaver wrote:
> > On 12/03/2013 02:35 PM, Andrew Johnson wrote:
> >> For future reference: With almost 2**10 lines of diff this branch mixes
> > together (admittedly related) changes to several different subsystems,
> >
> > ??? The merge proposal shows:
> >
> > 961 lines (+337/-53) 21 files modified
>
> 961 ≈ 1024
>
> I'm just saying that the longer the diff is, the more reviewers will be
> put off looking at it. If the changes were split into smaller dependent
> branches, reviewing them might look like less work and could be done in
> smaller chunks (this was a human psychology comment, not a programming
> issue at all).
I was considering this, but did not find a useful subset of the changes that would have added a functionality that could be documented and tested for.
Which in my imagination would have led to rejection of each individual branch and you commenting that a single functionality should have been presented as a single branch instead of multiple dependent branches that don't add anything useful by itself. ;-)
Admittedly, I might have been able to split the callback related things off. But still with a high change of not being able to add the mandatory tests.
Ralph Lange (ralph-lange) wrote : | # |
> The documentation branch does not cover all the changes made here, it only
> talks about the modifications to the callback subsystem.
If you are talking about the Application Developer's Guide, this change is IMHO the only one that is part of an API that Application Developers use.
Base development and internal APIs are not supposed to be covered by this document, correct?
Ralph Lange (ralph-lange) wrote : | # |
> The advantage of using epicsExitCallAt
> expose the various Shutdown symbols at all, and we automatically stop
> everything that we've started, in the (correct) reverse order.
>
> The disadvantage is this will slow down IOC shutdown unnecessarily,
> since we only need the 'clean up ready to start up again' actions in
> those exit routines when we're running tests that need restart.
>
> I'm getting visions of a subsystem management API; subsystems would
> register both a fast exit and a slow clean-up routine with it. It could
> even incorporate the taskwd, and provide subsystem/task status & health
> info on request. This could also somewhere to centralize all of the
> init/run/pause/exit enum task controls that are proliferating (which I
> agree should probably become atomics).
>
> Too much work?
If you think further, that becomes a major design change of iocCore.
It basically means going from somewhat modular spaghetti toward a component model where we have a mostly administrative core framework, and everything else becomes a component, possibly with separate versioning, providing a component API that handles component admin (version), compatibility (components requiring certain versions of other components), status/health, init/run/
The lists of available RSETs and DSETs would basically become extension points.
Existing support modules should have the choice of registering as a component. Plus maybe some generic (generated) code that handles existing record and device support to become (pseudo) components.
That would be a lot of work.
But I like the idea.
Andrew Johnson (anj) wrote : | # |
On 12/04/2013 03:49 AM, Ralph Lange wrote:
>> The documentation branch does not cover all the changes made here,
>> it only talks about the modifications to the callback subsystem.
>
> If you are talking about the Application Developer's Guide, this
> change is IMHO the only one that is part of an API that Application
> Developers use.
8.6.4 documents asInit(), so should mention asShutdown() nearby.
15.8.1 documents dbCaLinkInit(), so should mention dbCaShutdown(). Mea
culpa that it doesn't cover dbCaRun() or dbCaPause(), although they are
mentioned in section 7.
17.3 documents scanInit(), so should mention scanShutdown() nearby.
10.5 documents errlogInit(), so should mention errlogShutdown() nearby.
> Base development and internal APIs are not supposed to be covered by
> this document, correct?
Maybe, but as demonstrated above the documentation already covers
several internal APIs. If we don't modify those sections when we're
updating the code, the existing docs could start to contain wrong
information, which is a Bad Thing™ and may lead to Broken Window Syndrome.
--
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 12/04/2013 04:56 AM, Ralph Lange wrote:
>> Too much work?
>
> If you think further, that becomes a major design change of iocCore.
I agree; maybe a Codeathon project for someone to look at?
> That would be a lot of work. But I like the idea.
I created a Blueprint on LP to keep the idea alive.
--
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 : | # |
So, where to go with this from here?
My preference would be to look further at the epicsExitCallAt
Andrew Johnson (anj) wrote : | # |
epicsExitCallAt
- 12456. By mdavidsaver
-
epicsThreadOnce
Reset - 12457. By mdavidsaver
-
use epicsThreadOnce
Reset - 12458. By mdavidsaver
-
cleanup initHook
- 12459. By mdavidsaver
-
add dbUnitTest.h
hide boilerplate of tests using the PDB
mdavidsaver (mdavidsaver) wrote : | # |
I've added epicsThreadOnce
Also I introduce dbUnitTest.h to provide the boilerplate which unittests running in an IOC should use.
- 12460. By Andrew Johnson
-
Make epicsExit subsystem reusable.
Calling epicsAtExit() after epicsExitCallAt
Exits() now
recreates the per-process list and registers the routine. - 12461. By mdavidsaver
-
deletePeriodic: fix list free
Can't iterate on free'd node
- 12462. By mdavidsaver
-
dbScan: avoid double shutdown
scanShutdown will be called (later) by iocShutdown
via exitDatabase() - 12463. By mdavidsaver
-
epicsExit: optional debug printing
Add a flag to cause a string to be printed
before each handler is run to show the order. - 12464. By mdavidsaver
-
errlog: nicer exit handler name
Ralph Lange (ralph-lange) wrote : | # |
Where are we on this?
I would like to get this branch into a mergeable state, but I have lost track a bit, and I'm not sure about the line between long-term goals and mandatory features for this to be merged.
Ralph Lange (ralph-lange) wrote : | # |
OK ... I think hiding the additional shutdown calls is what is left to do. I will
- move the various init*Sup() and related routines to ioc/db/dbInit.c
- create a private header dbInit.h
- call appropriately from iocInit.c and dbUnitTest.c through the dbInit.h API
- document dbUnitTest.h
That will not preclude us from doing more sophisticated module start/shutdown management later on, and be sufficient for the merge.
Correct?
Andrew Johnson (anj) wrote : | # |
Just merged the replacement ioc-shutdown2 branch, this merge is no longer applicable.
Unmerged revisions
- 12464. By mdavidsaver
-
errlog: nicer exit handler name
- 12463. By mdavidsaver
-
epicsExit: optional debug printing
Add a flag to cause a string to be printed
before each handler is run to show the order. - 12462. By mdavidsaver
-
dbScan: avoid double shutdown
scanShutdown will be called (later) by iocShutdown
via exitDatabase() - 12461. By mdavidsaver
-
deletePeriodic: fix list free
Can't iterate on free'd node
- 12460. By Andrew Johnson
-
Make epicsExit subsystem reusable.
Calling epicsAtExit() after epicsExitCallAt
Exits() now
recreates the per-process list and registers the routine. - 12459. By mdavidsaver
-
add dbUnitTest.h
hide boilerplate of tests using the PDB
- 12458. By mdavidsaver
-
cleanup initHook
- 12457. By mdavidsaver
-
use epicsThreadOnce
Reset - 12456. By mdavidsaver
-
epicsThreadOnce
Reset - 12455. By Ralph Lange
-
ioc/misc: add missing include in iocInit.c
Preview Diff
1 | === modified file 'src/Makefile' |
2 | --- src/Makefile 2012-06-22 23:16:26 +0000 |
3 | +++ src/Makefile 2013-12-14 18:55:29 +0000 |
4 | @@ -62,7 +62,7 @@ |
5 | ioc_DEPEND_DIRS = libCom ca/client |
6 | |
7 | DIRS += ioc/db/test |
8 | -ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS |
9 | +ioc/db/test_DEPEND_DIRS = ioc std libCom/RTEMS |
10 | |
11 | DIRS += ioc/dbtemplate/test |
12 | ioc/dbtemplate/test_DEPEND_DIRS = ioc |
13 | |
14 | === modified file 'src/ioc/as/asDbLib.c' |
15 | --- src/ioc/as/asDbLib.c 2012-10-01 05:54:10 +0000 |
16 | +++ src/ioc/as/asDbLib.c 2013-12-14 18:55:29 +0000 |
17 | @@ -152,6 +152,15 @@ |
18 | return(asInitCommon()); |
19 | } |
20 | |
21 | +int asShutdown(void) { |
22 | + volatile ASBASE *pbase = pasbase; |
23 | + pasbase = NULL; |
24 | + firstTime = TRUE; |
25 | + if(pbase) |
26 | + asFreeAll((ASBASE*)pbase); |
27 | + return 0; |
28 | +} |
29 | + |
30 | static void wdCallback(void *arg) |
31 | { |
32 | ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg; |
33 | |
34 | === modified file 'src/ioc/as/asDbLib.h' |
35 | --- src/ioc/as/asDbLib.h 2012-07-15 21:08:50 +0000 |
36 | +++ src/ioc/as/asDbLib.h 2013-12-14 18:55:29 +0000 |
37 | @@ -32,6 +32,7 @@ |
38 | epicsShareFunc int asSetSubstitutions(const char *substitutions); |
39 | epicsShareFunc int asInit(void); |
40 | epicsShareFunc int asInitAsyn(ASDBCALLBACK *pcallback); |
41 | +epicsShareFunc int asShutdown(void); |
42 | epicsShareFunc int asDbGetAsl(struct dbChannel *chan); |
43 | epicsShareFunc void * asDbGetMemberPvt(struct dbChannel *chan); |
44 | epicsShareFunc int asdbdump(void); |
45 | |
46 | === modified file 'src/ioc/db/Makefile' |
47 | --- src/ioc/db/Makefile 2012-11-29 18:53:21 +0000 |
48 | +++ src/ioc/db/Makefile 2013-12-14 18:55:29 +0000 |
49 | @@ -37,6 +37,7 @@ |
50 | INC += dbState.h |
51 | INC += db_access_routines.h |
52 | INC += db_convert.h |
53 | +INC += dbUnitTest.h |
54 | |
55 | # Generate menuGlobal.dbd automatically |
56 | DBD += menuGlobal.dbd |
57 | @@ -86,4 +87,4 @@ |
58 | dbCore_SRCS += dbIocRegister.c |
59 | dbCore_SRCS += chfPlugin.c |
60 | dbCore_SRCS += dbState.c |
61 | - |
62 | +dbCore_SRCS += dbUnitTest.c |
63 | |
64 | === modified file 'src/ioc/db/callback.c' |
65 | --- src/ioc/db/callback.c 2012-05-04 22:34:48 +0000 |
66 | +++ src/ioc/db/callback.c 2013-12-14 18:55:29 +0000 |
67 | @@ -53,6 +53,8 @@ |
68 | static epicsTimerQueueId timerQueue; |
69 | |
70 | /* Shutdown handling */ |
71 | +enum ctl {ctlInit, ctlRun, ctlPause, ctlExit}; |
72 | +static volatile enum ctl cbCtl; |
73 | static epicsEventId startStopEvent; |
74 | static void *exitCallback; |
75 | |
76 | @@ -101,10 +103,13 @@ |
77 | epicsEventSignal(startStopEvent); |
78 | } |
79 | |
80 | -static void callbackShutdown(void *arg) |
81 | +void callbackShutdown(void) |
82 | { |
83 | int i; |
84 | |
85 | + if (cbCtl == ctlExit) return; |
86 | + cbCtl = ctlExit; |
87 | + |
88 | for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { |
89 | int lockKey = epicsInterruptLock(); |
90 | int ok = epicsRingPointerPush(callbackQ[i], &exitCallback); |
91 | @@ -112,6 +117,13 @@ |
92 | epicsEventSignal(callbackSem[i]); |
93 | if (ok) epicsEventWait(startStopEvent); |
94 | } |
95 | + epicsTimerQueueRelease(timerQueue); |
96 | + epicsThreadOnceReset(&callbackOnceFlag); |
97 | +} |
98 | + |
99 | +static void callbackExit(void *arg) |
100 | +{ |
101 | + callbackShutdown(); |
102 | } |
103 | |
104 | static void callbackInitOnce(void *arg) |
105 | @@ -119,6 +131,7 @@ |
106 | int i; |
107 | |
108 | startStopEvent = epicsEventMustCreate(epicsEventEmpty); |
109 | + cbCtl = ctlRun; |
110 | timerQueue = epicsTimerQueueAllocate(0,epicsThreadPriorityScanHigh); |
111 | for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { |
112 | epicsThreadId tid; |
113 | @@ -137,7 +150,7 @@ |
114 | else |
115 | epicsEventWait(startStopEvent); |
116 | } |
117 | - epicsAtExit(callbackShutdown, NULL); |
118 | + epicsAtExit(callbackExit, NULL); |
119 | } |
120 | |
121 | void callbackInit(void) |
122 | |
123 | === modified file 'src/ioc/db/callback.h' |
124 | --- src/ioc/db/callback.h 2010-10-05 19:27:37 +0000 |
125 | +++ src/ioc/db/callback.h 2013-12-14 18:55:29 +0000 |
126 | @@ -57,6 +57,7 @@ |
127 | |
128 | epicsShareFunc void callbackInit(void); |
129 | epicsShareFunc void callbackRequest(CALLBACK *pCallback); |
130 | +epicsShareFunc void callbackShutdown(void); |
131 | epicsShareFunc void callbackSetProcess( |
132 | CALLBACK *pcallback, int Priority, void *pRec); |
133 | epicsShareFunc void callbackRequestProcessCallback( |
134 | |
135 | === modified file 'src/ioc/db/dbCa.c' |
136 | --- src/ioc/db/dbCa.c 2012-06-22 23:16:26 +0000 |
137 | +++ src/ioc/db/dbCa.c 2013-12-14 18:55:29 +0000 |
138 | @@ -173,15 +173,22 @@ |
139 | dbScanUnlock(pdbCommon); |
140 | } |
141 | |
142 | -static void dbCaShutdown(void *arg) |
143 | +void dbCaShutdown(void) |
144 | { |
145 | - if (dbCaCtl == ctlRun) { |
146 | + if (dbCaCtl == ctlRun || dbCaCtl == ctlPause) { |
147 | dbCaCtl = ctlExit; |
148 | epicsEventSignal(workListEvent); |
149 | epicsEventMustWait(startStopEvent); |
150 | + epicsEventDestroy(startStopEvent); |
151 | + epicsEventDestroy(workListEvent); |
152 | } |
153 | } |
154 | |
155 | +static void dbCaExit(void *arg) |
156 | +{ |
157 | + dbCaShutdown(); |
158 | +} |
159 | + |
160 | void dbCaLinkInit(void) |
161 | { |
162 | dbServiceIOInit(); |
163 | @@ -194,19 +201,23 @@ |
164 | epicsThreadGetStackSize(epicsThreadStackBig), |
165 | dbCaTask, NULL); |
166 | epicsEventMustWait(startStopEvent); |
167 | - epicsAtExit(dbCaShutdown, NULL); |
168 | + epicsAtExit(dbCaExit, NULL); |
169 | } |
170 | |
171 | void dbCaRun(void) |
172 | { |
173 | - dbCaCtl = ctlRun; |
174 | - epicsEventSignal(workListEvent); |
175 | + if (dbCaCtl == ctlPause) { |
176 | + dbCaCtl = ctlRun; |
177 | + epicsEventSignal(workListEvent); |
178 | + } |
179 | } |
180 | |
181 | void dbCaPause(void) |
182 | { |
183 | - dbCaCtl = ctlPause; |
184 | - epicsEventSignal(workListEvent); |
185 | + if (dbCaCtl == ctlRun) { |
186 | + dbCaCtl = ctlPause; |
187 | + epicsEventSignal(workListEvent); |
188 | + } |
189 | } |
190 | |
191 | void dbCaAddLinkCallback(struct link *plink, |
192 | |
193 | === modified file 'src/ioc/db/dbCa.h' |
194 | --- src/ioc/db/dbCa.h 2012-04-27 17:21:47 +0000 |
195 | +++ src/ioc/db/dbCa.h 2013-12-14 18:55:29 +0000 |
196 | @@ -26,6 +26,7 @@ |
197 | epicsShareFunc void dbCaLinkInit(void); |
198 | epicsShareFunc void dbCaRun(void); |
199 | epicsShareFunc void dbCaPause(void); |
200 | +epicsShareFunc void dbCaShutdown(void); |
201 | |
202 | epicsShareFunc void dbCaAddLinkCallback(struct link *plink, |
203 | dbCaCallback connect, dbCaCallback monitor, void *userPvt); |
204 | |
205 | === modified file 'src/ioc/db/dbScan.c' |
206 | --- src/ioc/db/dbScan.c 2013-04-23 15:38:57 +0000 |
207 | +++ src/ioc/db/dbScan.c 2013-12-14 18:55:29 +0000 |
208 | @@ -3,6 +3,8 @@ |
209 | * National Laboratory. |
210 | * Copyright (c) 2002 The Regents of the University of California, as |
211 | * Operator of Los Alamos National Laboratory. |
212 | +* Copyright (c) 2013 Helmholtz-Zentrum Berlin |
213 | +* für Materialien und Energie GmbH. |
214 | * EPICS BASE is distributed subject to a Software License Agreement found |
215 | * in file LICENSE that is included with this distribution. |
216 | \*************************************************************************/ |
217 | @@ -129,6 +131,7 @@ |
218 | static void initOnce(void); |
219 | static void periodicTask(void *arg); |
220 | static void initPeriodic(void); |
221 | +static void deletePeriodic(void); |
222 | static void spawnPeriodic(int ind); |
223 | static void initEvent(void); |
224 | static void eventCallback(CALLBACK *pcallback); |
225 | @@ -139,10 +142,13 @@ |
226 | static void addToList(struct dbCommon *precord, scan_list *psl); |
227 | static void deleteFromList(struct dbCommon *precord, scan_list *psl); |
228 | |
229 | |
230 | -static void scanShutdown(void *arg) |
231 | +void scanShutdown(void) |
232 | { |
233 | int i; |
234 | |
235 | + if (scanCtl == ctlExit) return; |
236 | + scanCtl = ctlExit; |
237 | + |
238 | interruptAccept = FALSE; |
239 | |
240 | for (i = 0; i < nPeriodic; i++) { |
241 | @@ -153,6 +159,18 @@ |
242 | |
243 | scanOnce((dbCommon *)&exitOnce); |
244 | epicsEventWait(startStopEvent); |
245 | + |
246 | + deletePeriodic(); |
247 | + |
248 | + epicsRingPointerDelete(onceQ); |
249 | + |
250 | + epicsEventDestroy(startStopEvent); |
251 | + epicsEventDestroy(onceSem); |
252 | + onceSem = startStopEvent = NULL; |
253 | + |
254 | + free(periodicTaskId); |
255 | + papPeriodic = NULL; |
256 | + periodicTaskId = NULL; |
257 | } |
258 | |
259 | long scanInit(void) |
260 | @@ -169,7 +187,6 @@ |
261 | for (i = 0; i < nPeriodic; i++) |
262 | spawnPeriodic(i); |
263 | |
264 | - epicsAtExit(scanShutdown, NULL); |
265 | return 0; |
266 | } |
267 | |
268 | @@ -672,6 +689,22 @@ |
269 | } |
270 | } |
271 | |
272 | +static void deletePeriodic(void) |
273 | +{ |
274 | + int i; |
275 | + |
276 | + for (i = 0; i < nPeriodic; i++) { |
277 | + periodic_scan_list *ppsl = papPeriodic[i]; |
278 | + ellFree(&ppsl->scan_list.list); |
279 | + epicsEventDestroy(ppsl->loopEvent); |
280 | + epicsMutexDestroy(ppsl->scan_list.lock); |
281 | + free(ppsl); |
282 | + } |
283 | + |
284 | + free(papPeriodic); |
285 | + papPeriodic = NULL; |
286 | +} |
287 | + |
288 | static void spawnPeriodic(int ind) |
289 | { |
290 | periodic_scan_list *ppsl; |
291 | @@ -778,23 +811,25 @@ |
292 | { |
293 | dbRecordType *pdbRecordType; |
294 | |
295 | - /*Look for first record*/ |
296 | for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); |
297 | pdbRecordType; |
298 | pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { |
299 | dbRecordNode *pdbRecordNode; |
300 | + |
301 | for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); |
302 | pdbRecordNode; |
303 | pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { |
304 | dbCommon *precord = pdbRecordNode->precord; |
305 | + |
306 | if (!precord->name[0] || |
307 | pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) |
308 | continue; |
309 | + |
310 | scanAdd(precord); |
311 | } |
312 | } |
313 | } |
314 | - |
315 | |
316 | + |
317 | static void addToList(struct dbCommon *precord, scan_list *psl) |
318 | { |
319 | scan_element *pse, *ptemp; |
320 | |
321 | === modified file 'src/ioc/db/dbScan.h' |
322 | --- src/ioc/db/dbScan.h 2012-04-10 22:10:50 +0000 |
323 | +++ src/ioc/db/dbScan.h 2013-12-14 18:55:29 +0000 |
324 | @@ -44,6 +44,7 @@ |
325 | epicsShareFunc long scanInit(void); |
326 | epicsShareFunc void scanRun(void); |
327 | epicsShareFunc void scanPause(void); |
328 | +epicsShareFunc void scanShutdown(void); |
329 | |
330 | epicsShareFunc EVENTPVT eventNameToHandle(const char* event); |
331 | epicsShareFunc void postEvent(EVENTPVT epvt); |
332 | |
333 | === added file 'src/ioc/db/dbUnitTest.c' |
334 | --- src/ioc/db/dbUnitTest.c 1970-01-01 00:00:00 +0000 |
335 | +++ src/ioc/db/dbUnitTest.c 2013-12-14 18:55:29 +0000 |
336 | @@ -0,0 +1,123 @@ |
337 | +/*************************************************************************\ |
338 | +* Copyright (c) 2013 Brookhaven National Laboratory. |
339 | +* Copyright (c) 2013 ITER Organization. |
340 | +* EPICS BASE is distributed subject to a Software License Agreement found |
341 | +* in file LICENSE that is included with this distribution. |
342 | + \*************************************************************************/ |
343 | + |
344 | +/* |
345 | + * Author: Michael Davidsaver <mdavidsaver@bnl.gov> |
346 | + * Ralph Lange <Ralph.Lange@gmx.de> |
347 | + */ |
348 | + |
349 | +#include <string.h> |
350 | + |
351 | +#include "epicsUnitTest.h" |
352 | +#include "osiFileName.h" |
353 | +#include "dbmf.h" |
354 | +#include "registry.h" |
355 | +#define epicsExportSharedSymbols |
356 | +#include "iocInit.h" |
357 | +#include "initHooks.h" |
358 | +#include "dbBase.h" |
359 | +#include "dbAccess.h" |
360 | +#include "dbStaticLib.h" |
361 | + |
362 | +#include "dbUnitTest.h" |
363 | + |
364 | +void testdbPrepare(void) |
365 | +{ |
366 | + /* No-op at the moment */ |
367 | +} |
368 | + |
369 | +void testdbReadDatabase(const char* file, |
370 | + const char* path, |
371 | + const char* substitutions) |
372 | +{ |
373 | + if(!path) |
374 | + path = "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR |
375 | + "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common"; |
376 | + if(dbReadDatabase(&pdbbase, file, path, substitutions)) |
377 | + testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)", |
378 | + file, path, substitutions); |
379 | +} |
380 | + |
381 | +int testiocInit(void) |
382 | +{ |
383 | + return iocBuildNoCA() || iocRun(); |
384 | +} |
385 | + |
386 | +int testiocShutdown(void) |
387 | +{ |
388 | + return iocShutdown(); |
389 | +} |
390 | + |
391 | +void testdbCleanup(void) |
392 | +{ |
393 | + dbFreeBase(pdbbase); |
394 | + initHookFree(); |
395 | + registryFree(); |
396 | + pdbbase = NULL; |
397 | + dbmfFreeChunks(); |
398 | +} |
399 | + |
400 | +long testdbPutField(const char* pv, short dbrType, ...) |
401 | +{ |
402 | + long ret; |
403 | + va_list ap; |
404 | + va_start(ap, dbrType); |
405 | + ret = testVdbPutField(pv, dbrType, ap); |
406 | + va_end(ap); |
407 | + return ret; |
408 | +} |
409 | + |
410 | +union anybuf { |
411 | + epicsAny val; |
412 | + char bytes[sizeof(epicsAny)]; |
413 | +}; |
414 | + |
415 | +long testVdbPutField(const char* pv, short dbrType, va_list ap) |
416 | +{ |
417 | + DBADDR addr; |
418 | + union anybuf pod; |
419 | + |
420 | + if(dbNameToAddr(pv, &addr)) |
421 | + testAbort("Missing PV %s", pv); |
422 | + |
423 | + switch(dbrType) { |
424 | + case DBR_STRING: { |
425 | + const char *uarg = va_arg(ap,char*); |
426 | + epicsOldString buffer; |
427 | + strncpy(buffer, uarg, sizeof(buffer)); |
428 | + buffer[sizeof(buffer)-1] = '\0'; |
429 | + return dbPutField(&addr, dbrType, buffer, 1); |
430 | + } |
431 | + |
432 | +#define OP(DBR,Type,mem) case DBR: {pod.val.mem = va_arg(ap,Type); break;} |
433 | + OP(DBR_CHAR, int, int8); |
434 | + OP(DBR_UCHAR, int, uInt8); |
435 | + OP(DBR_SHORT, int, int16); |
436 | + OP(DBR_USHORT, int, uInt16); |
437 | + OP(DBR_LONG, int, int32); |
438 | + OP(DBR_ULONG, unsigned int, uInt32); |
439 | + OP(DBR_FLOAT, double, float32); |
440 | + OP(DBR_DOUBLE, double, float64); |
441 | + OP(DBR_ENUM, int, enum16); |
442 | +#undef OP |
443 | + default: |
444 | + testAbort("invalid DBR: dbPutField(%s, %d, ...)", |
445 | + addr.precord->name, dbrType); |
446 | + } |
447 | + |
448 | + return dbPutField(&addr, dbrType, pod.bytes, 1); |
449 | +} |
450 | + |
451 | +dbCommon* testGetRecord(const char* pv) |
452 | +{ |
453 | + DBADDR addr; |
454 | + |
455 | + if(dbNameToAddr(pv, &addr)) |
456 | + testAbort("Missing PV %s", pv); |
457 | + |
458 | + return addr.precord; |
459 | +} |
460 | |
461 | === added file 'src/ioc/db/dbUnitTest.h' |
462 | --- src/ioc/db/dbUnitTest.h 1970-01-01 00:00:00 +0000 |
463 | +++ src/ioc/db/dbUnitTest.h 2013-12-14 18:55:29 +0000 |
464 | @@ -0,0 +1,54 @@ |
465 | +/*************************************************************************\ |
466 | +* Copyright (c) 2013 Brookhaven National Laboratory. |
467 | +* Copyright (c) 2013 ITER Organization. |
468 | +* EPICS BASE is distributed subject to a Software License Agreement found |
469 | +* in file LICENSE that is included with this distribution. |
470 | + \*************************************************************************/ |
471 | + |
472 | +/* |
473 | + * Author: Michael Davidsaver <mdavidsaver@bnl.gov> |
474 | + * Ralph Lange <Ralph.Lange@gmx.de> |
475 | + */ |
476 | + |
477 | +#ifndef EPICSUNITTESTDB_H |
478 | +#define EPICSUNITTESTDB_H |
479 | + |
480 | +#include <stdarg.h> |
481 | + |
482 | +#include "epicsUnitTest.h" |
483 | +#include "dbAddr.h" |
484 | +#include "dbCommon.h" |
485 | + |
486 | +#include "shareLib.h" |
487 | + |
488 | +#ifdef __cplusplus |
489 | +extern "C" { |
490 | +#endif |
491 | + |
492 | +epicsShareFunc void testdbPrepare(void); |
493 | +epicsShareFunc void testdbReadDatabase(const char* file, |
494 | + const char* path, |
495 | + const char* substitutions); |
496 | +epicsShareFunc int testiocInit(void); |
497 | +epicsShareFunc int testiocShutdown(void); |
498 | +epicsShareFunc void testdbCleanup(void); |
499 | + |
500 | +/* Scalar only version. |
501 | + * |
502 | + * Remember to use the correct argument type!s |
503 | + * |
504 | + * int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG |
505 | + * unsigned int for DBR_ULONG |
506 | + * double for DBR_FLOAT and DBR_DOUBLE |
507 | + * const char* for DBR_STRING |
508 | + */ |
509 | +epicsShareFunc long testdbPutField(const char* pv, short dbrType, ...); |
510 | +epicsShareFunc long testVdbPutField(const char* pv, short dbrType, va_list ap); |
511 | + |
512 | +epicsShareFunc dbCommon* testGetRecord(const char* pv); |
513 | + |
514 | +#ifdef __cplusplus |
515 | +} |
516 | +#endif |
517 | + |
518 | +#endif // EPICSUNITTESTDB_H |
519 | |
520 | === modified file 'src/ioc/db/initHooks.c' |
521 | --- src/ioc/db/initHooks.c 2010-10-05 19:27:37 +0000 |
522 | +++ src/ioc/db/initHooks.c 2013-12-14 18:55:29 +0000 |
523 | @@ -93,6 +93,14 @@ |
524 | } |
525 | } |
526 | |
527 | +void initHookFree(void) |
528 | +{ |
529 | + initHookInit(); |
530 | + epicsMutexMustLock(listLock); |
531 | + ellFree(&functionList); |
532 | + epicsMutexUnlock(listLock); |
533 | +} |
534 | + |
535 | /* |
536 | * Call any time you want to print out a state name. |
537 | */ |
538 | |
539 | === modified file 'src/ioc/db/initHooks.h' |
540 | --- src/ioc/db/initHooks.h 2010-10-05 19:27:37 +0000 |
541 | +++ src/ioc/db/initHooks.h 2013-12-14 18:55:29 +0000 |
542 | @@ -60,6 +60,7 @@ |
543 | epicsShareFunc int initHookRegister(initHookFunction func); |
544 | epicsShareFunc void initHookAnnounce(initHookState state); |
545 | epicsShareFunc const char *initHookName(int state); |
546 | +epicsShareFunc void initHookFree(void); |
547 | |
548 | #ifdef __cplusplus |
549 | } |
550 | |
551 | === modified file 'src/ioc/db/test/Makefile' |
552 | --- src/ioc/db/test/Makefile 2012-07-17 19:33:31 +0000 |
553 | +++ src/ioc/db/test/Makefile 2013-12-14 18:55:29 +0000 |
554 | @@ -16,6 +16,15 @@ |
555 | |
556 | PROD_LIBS = xRec dbCore ca Com |
557 | |
558 | +TESTPROD_HOST += dbShutdownTest |
559 | +TARGETS += $(COMMON_DIR)/dbShutdownTest.dbd |
560 | +dbShutdownTest_SRCS += dbShutdownTest.c |
561 | +dbShutdownTest_SRCS += dbShutdownTest_registerRecordDeviceDriver.cpp |
562 | +dbShutdownTest_LIBS += $(EPICS_BASE_IOC_LIBS) |
563 | +dbShutdownTest_DBD += base.dbd |
564 | +TESTFILES += $(COMMON_DIR)/dbShutdownTest.dbd ../sRecord.db |
565 | +TESTS += dbShutdownTest |
566 | + |
567 | TESTPROD_HOST += callbackTest |
568 | callbackTest_SRCS += callbackTest.c |
569 | testHarness_SRCS += callbackTest.c |
570 | |
571 | === added file 'src/ioc/db/test/dbShutdownTest.c' |
572 | --- src/ioc/db/test/dbShutdownTest.c 1970-01-01 00:00:00 +0000 |
573 | +++ src/ioc/db/test/dbShutdownTest.c 2013-12-14 18:55:29 +0000 |
574 | @@ -0,0 +1,94 @@ |
575 | +/*************************************************************************\ |
576 | +* Copyright (c) 2013 Brookhaven National Laboratory. |
577 | +* Copyright (c) 2013 ITER Organization. |
578 | +* EPICS BASE is distributed subject to a Software License Agreement found |
579 | +* in file LICENSE that is included with this distribution. |
580 | + \*************************************************************************/ |
581 | + |
582 | +/* |
583 | + * Author: Michael Davidsaver <mdavidsaver@bnl.gov> |
584 | + * Ralph Lange <Ralph.Lange@gmx.de> |
585 | + */ |
586 | + |
587 | +#include <string.h> |
588 | + |
589 | +#include "dbUnitTest.h" |
590 | +#include "epicsThread.h" |
591 | +#include "iocInit.h" |
592 | +#include "dbBase.h" |
593 | +#include "dbAccess.h" |
594 | +#include "registry.h" |
595 | +#include "dbStaticLib.h" |
596 | +#include "osiFileName.h" |
597 | +#include "dbmf.h" |
598 | + |
599 | +#include "testMain.h" |
600 | + |
601 | +void dbShutdownTest_registerRecordDeviceDriver(struct dbBase *); |
602 | + |
603 | +static struct threadItem { |
604 | + char *name; |
605 | + char found; |
606 | +} commonThreads[] = { |
607 | + { "errlog", 0 }, |
608 | + { "taskwd", 0 }, |
609 | + { "timerQueue", 0 }, |
610 | + { "cbLow", 0 }, |
611 | + { "scanOnce", 0 }, |
612 | + { NULL, 0 } |
613 | +}; |
614 | + |
615 | +static |
616 | +void findCommonThread (epicsThreadId id) { |
617 | + struct threadItem *thr; |
618 | + char name[32]; |
619 | + |
620 | + epicsThreadGetName(id, name, 32); |
621 | + |
622 | + for (thr = commonThreads; thr->name; thr++) { |
623 | + if (strcasecmp(thr->name, name) == 0) { |
624 | + thr->found = 1; |
625 | + } |
626 | + } |
627 | +} |
628 | + |
629 | +static |
630 | +void checkCommonThreads (void) { |
631 | + struct threadItem *thr; |
632 | + |
633 | + for (thr = commonThreads; thr->name; thr++) { |
634 | + testOk(thr->found, "Thread %s is running", thr->name); |
635 | + thr->found = 0; |
636 | + } |
637 | +} |
638 | + |
639 | +static |
640 | +void cycle(void) { |
641 | + |
642 | + testdbPrepare(); |
643 | + |
644 | + testdbReadDatabase("dbShutdownTest.dbd", NULL, NULL); |
645 | + |
646 | + dbShutdownTest_registerRecordDeviceDriver(pdbbase); |
647 | + |
648 | + testdbReadDatabase("sRecord.db", NULL, NULL); |
649 | + |
650 | + testOk1(!testiocInit()); |
651 | + |
652 | + epicsThreadMap(findCommonThread); |
653 | + checkCommonThreads(); |
654 | + |
655 | + testOk1(testiocShutdown()==0); |
656 | + |
657 | + testdbCleanup(); |
658 | +} |
659 | + |
660 | +MAIN(dbShutdownTest) |
661 | +{ |
662 | + testPlan(14); |
663 | + |
664 | + cycle(); |
665 | + cycle(); |
666 | + |
667 | + return testDone(); |
668 | +} |
669 | |
670 | === added file 'src/ioc/db/test/sRecord.db' |
671 | --- src/ioc/db/test/sRecord.db 1970-01-01 00:00:00 +0000 |
672 | +++ src/ioc/db/test/sRecord.db 2013-12-14 18:55:29 +0000 |
673 | @@ -0,0 +1,1 @@ |
674 | +record(ai, "somename") {} |
675 | |
676 | === modified file 'src/ioc/misc/dbCore.dbd' |
677 | --- src/ioc/misc/dbCore.dbd 2011-02-26 05:56:51 +0000 |
678 | +++ src/ioc/misc/dbCore.dbd 2013-12-14 18:55:29 +0000 |
679 | @@ -5,6 +5,9 @@ |
680 | # This file provides iocsh access to variables that control some lesser-used |
681 | # and debugging features of the IOC database code. |
682 | |
683 | +# show epicsAtExit callbacks as they are run |
684 | +variable(atExitDebug,int) |
685 | + |
686 | # Access security subroutines |
687 | variable(asCaDebug,int) |
688 | |
689 | |
690 | === modified file 'src/ioc/misc/iocInit.c' |
691 | --- src/ioc/misc/iocInit.c 2013-05-30 20:00:37 +0000 |
692 | +++ src/ioc/misc/iocInit.c 2013-12-14 18:55:29 +0000 |
693 | @@ -3,6 +3,8 @@ |
694 | * National Laboratory. |
695 | * Copyright (c) 2002 The Regents of the University of California, as |
696 | * Operator of Los Alamos National Laboratory. |
697 | +* Copyright (c) 2013 Helmholtz-Zentrum Berlin |
698 | +* für Materialien und Energie GmbH. |
699 | * EPICS BASE is distributed subject to a Software License Agreement found |
700 | * in file LICENSE that is included with this distribution. |
701 | \*************************************************************************/ |
702 | @@ -31,6 +33,7 @@ |
703 | #include "errMdef.h" |
704 | #include "taskwd.h" |
705 | #include "caeventmask.h" |
706 | +#include "iocsh.h" |
707 | |
708 | #define epicsExportSharedSymbols |
709 | #include "alarm.h" |
710 | @@ -88,12 +91,13 @@ |
711 | return iocBuild() || iocRun(); |
712 | } |
713 | |
714 | -int iocBuild(void) |
715 | +static int iocBuild_1(void) |
716 | { |
717 | - if (iocState != iocVirgin) { |
718 | - errlogPrintf("iocBuild: IOC can only be initialized once\n"); |
719 | + if (iocState != iocVirgin && iocState != iocStopped) { |
720 | + errlogPrintf("iocBuild: IOC can only be initialized from uninitialized or stopped state\n"); |
721 | return -1; |
722 | } |
723 | + errlogInit(0); |
724 | initHookAnnounce(initHookAtIocBuild); |
725 | |
726 | if (!epicsThreadIsOkToBlock()) { |
727 | @@ -109,14 +113,17 @@ |
728 | initHookAnnounce(initHookAtBeginning); |
729 | |
730 | coreRelease(); |
731 | - /* After this point, further calls to iocInit() are disallowed. */ |
732 | iocState = iocBuilding; |
733 | |
734 | taskwdInit(); |
735 | callbackInit(); |
736 | initHookAnnounce(initHookAfterCallbackInit); |
737 | |
738 | - dbCaLinkInit(); |
739 | + return 0; |
740 | +} |
741 | + |
742 | +static int iocBuild_2(void) |
743 | +{ |
744 | initHookAnnounce(initHookAfterCaLinkInit); |
745 | |
746 | initDrvSup(); |
747 | @@ -147,9 +154,11 @@ |
748 | |
749 | initialProcess(); |
750 | initHookAnnounce(initHookAfterInitialProcess); |
751 | + return 0; |
752 | +} |
753 | |
754 | - /* Start CA server threads */ |
755 | - rsrv_init(); |
756 | +static int iocBuild_3(void) |
757 | +{ |
758 | initHookAnnounce(initHookAfterCaServerInit); |
759 | |
760 | iocState = iocBuilt; |
761 | @@ -157,6 +166,39 @@ |
762 | return 0; |
763 | } |
764 | |
765 | +int iocBuild(void) |
766 | +{ |
767 | + int status; |
768 | + |
769 | + status = iocBuild_1(); |
770 | + if (status) return status; |
771 | + |
772 | + dbCaLinkInit(); |
773 | + |
774 | + status = iocBuild_2(); |
775 | + if (status) return status; |
776 | + |
777 | + /* Start CA server threads */ |
778 | + rsrv_init(); |
779 | + |
780 | + status = iocBuild_3(); |
781 | + return status; |
782 | +} |
783 | + |
784 | +int iocBuildNoCA(void) |
785 | +{ |
786 | + int status; |
787 | + |
788 | + status = iocBuild_1(); |
789 | + if (status) return status; |
790 | + |
791 | + status = iocBuild_2(); |
792 | + if (status) return status; |
793 | + |
794 | + status = iocBuild_3(); |
795 | + return status; |
796 | +} |
797 | + |
798 | int iocRun(void) |
799 | { |
800 | if (iocState != iocPaused && iocState != iocBuilt) { |
801 | @@ -599,8 +641,21 @@ |
802 | } |
803 | } |
804 | |
805 | -static void exitDatabase(void *dummy) |
806 | +int iocShutdown(void) |
807 | { |
808 | + if (iocState == iocVirgin || iocState == iocStopped) return 0; |
809 | iterateRecords(doCloseLinks, NULL); |
810 | + scanShutdown(); |
811 | + callbackShutdown(); |
812 | + asShutdown(); |
813 | + iocshFree(); |
814 | + taskwdShutdown(); |
815 | + errlogShutdown(); |
816 | iocState = iocStopped; |
817 | + return 0; |
818 | +} |
819 | + |
820 | +static void exitDatabase(void *dummy) |
821 | +{ |
822 | + iocShutdown(); |
823 | } |
824 | |
825 | === modified file 'src/ioc/misc/iocInit.h' |
826 | --- src/ioc/misc/iocInit.h 2009-06-10 20:19:32 +0000 |
827 | +++ src/ioc/misc/iocInit.h 2013-12-14 18:55:29 +0000 |
828 | @@ -19,8 +19,10 @@ |
829 | |
830 | epicsShareFunc int iocInit(void); |
831 | epicsShareFunc int iocBuild(void); |
832 | +epicsShareFunc int iocBuildNoCA(void); |
833 | epicsShareFunc int iocRun(void); |
834 | epicsShareFunc int iocPause(void); |
835 | +epicsShareFunc int iocShutdown(void); |
836 | |
837 | #ifdef __cplusplus |
838 | } |
839 | |
840 | === modified file 'src/libCom/as/asLib.h' |
841 | --- src/libCom/as/asLib.h 2010-12-17 16:50:52 +0000 |
842 | +++ src/libCom/as/asLib.h 2013-12-14 18:55:29 +0000 |
843 | @@ -222,6 +222,7 @@ |
844 | /*following is "friend" function*/ |
845 | epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size); |
846 | epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str); |
847 | +epicsShareFunc void asFreeAll(ASBASE *pasbase); |
848 | #ifdef __cplusplus |
849 | } |
850 | #endif |
851 | |
852 | === modified file 'src/libCom/as/asLibRoutines.c' |
853 | --- src/libCom/as/asLibRoutines.c 2012-07-07 18:50:55 +0000 |
854 | +++ src/libCom/as/asLibRoutines.c 2013-12-14 18:55:29 +0000 |
855 | @@ -51,7 +51,6 @@ |
856 | static long asComputeAllAsgPvt(void); |
857 | static long asComputeAsgPvt(ASG *pasg); |
858 | static long asComputePvt(ASCLIENTPVT asClientPvt); |
859 | -static void asFreeAll(ASBASE *pasbase); |
860 | static UAG *asUagAdd(const char *uagName); |
861 | static long asUagAddUser(UAG *puag,const char *user); |
862 | static HAG *asHagAdd(const char *hagName); |
863 | @@ -1015,7 +1014,7 @@ |
864 | return(0); |
865 | } |
866 | |
867 | |
868 | -static void asFreeAll(ASBASE *pasbase) |
869 | +void asFreeAll(ASBASE *pasbase) |
870 | { |
871 | UAG *puag; |
872 | UAGNAME *puagname; |
873 | |
874 | === modified file 'src/libCom/error/errlog.c' |
875 | --- src/libCom/error/errlog.c 2013-06-28 17:35:43 +0000 |
876 | +++ src/libCom/error/errlog.c 2013-12-14 18:55:29 +0000 |
877 | @@ -41,9 +41,13 @@ |
878 | /*Declare storage for errVerbose */ |
879 | epicsShareDef int errVerbose = 0; |
880 | |
881 | -static void exitHandler(void *); |
882 | +static epicsThreadOnceId errlogOnceFlag = EPICS_THREAD_ONCE_INIT; |
883 | + |
884 | +static void errlogExitHandler(void *); |
885 | static void errlogThread(void); |
886 | |
887 | +static void errlogInit1(int bufsize); |
888 | + |
889 | static char *msgbufGetFree(int noConsoleMessage); |
890 | static void msgbufSetSize(int size); /* Send 'size' chars plus trailing '\0' */ |
891 | static char *msgbufGetSend(int *noConsoleMessage); |
892 | @@ -70,8 +74,8 @@ |
893 | epicsEventId waitForFlush; /*errlogFlush waits for this*/ |
894 | epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/ |
895 | epicsMutexId flushLock; |
896 | - epicsEventId waitForExit; /*exitHandler waits for this*/ |
897 | - int atExit; /*TRUE when exitHandler is active*/ |
898 | + epicsEventId waitForExit; /*errlogExitHandler waits for this*/ |
899 | + int atExit; /*TRUE when errlogExitHandler is active*/ |
900 | ELLLIST listenerList; |
901 | ELLLIST msgQueue; |
902 | msgNode *pnextSend; |
903 | @@ -116,7 +120,7 @@ |
904 | return 0; |
905 | } |
906 | |
907 | - errlogInit(0); |
908 | + errlogInit1(0); |
909 | isOkToBlock = epicsThreadIsOkToBlock(); |
910 | |
911 | if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { |
912 | @@ -153,7 +157,7 @@ |
913 | return 0; |
914 | } |
915 | |
916 | - errlogInit(0); |
917 | + errlogInit1(0); |
918 | if (pvtData.atExit) |
919 | return 0; |
920 | isOkToBlock = epicsThreadIsOkToBlock(); |
921 | @@ -191,7 +195,7 @@ |
922 | return 0; |
923 | } |
924 | |
925 | - errlogInit(0); |
926 | + errlogInit1(0); |
927 | va_start(pvar, pFormat); |
928 | nchar = errlogVprintfNoConsole(pFormat, pvar); |
929 | va_end(pvar); |
930 | @@ -210,7 +214,7 @@ |
931 | return 0; |
932 | } |
933 | |
934 | - errlogInit(0); |
935 | + errlogInit1(0); |
936 | if (pvtData.atExit) |
937 | return 0; |
938 | |
939 | @@ -237,7 +241,7 @@ |
940 | return 0; |
941 | } |
942 | |
943 | - errlogInit(0); |
944 | + errlogInit1(0); |
945 | if (pvtData.sevToLog > severity) |
946 | return 0; |
947 | |
948 | @@ -270,7 +274,7 @@ |
949 | return 0; |
950 | } |
951 | |
952 | - errlogInit(0); |
953 | + errlogInit1(0); |
954 | if (pvtData.atExit) |
955 | return 0; |
956 | |
957 | @@ -295,7 +299,7 @@ |
958 | epicsShareFunc char * epicsShareAPI errlogGetSevEnumString( |
959 | const errlogSevEnum severity) |
960 | { |
961 | - errlogInit(0); |
962 | + errlogInit1(0); |
963 | if (severity > 3) |
964 | return "unknown"; |
965 | return errlogSevEnumString[severity]; |
966 | @@ -304,13 +308,13 @@ |
967 | epicsShareFunc void epicsShareAPI errlogSetSevToLog( |
968 | const errlogSevEnum severity) |
969 | { |
970 | - errlogInit(0); |
971 | + errlogInit1(0); |
972 | pvtData.sevToLog = severity; |
973 | } |
974 | |
975 | epicsShareFunc errlogSevEnum epicsShareAPI errlogGetSevToLog(void) |
976 | { |
977 | - errlogInit(0); |
978 | + errlogInit1(0); |
979 | return pvtData.sevToLog; |
980 | } |
981 | |
982 | @@ -319,7 +323,7 @@ |
983 | { |
984 | listenerNode *plistenerNode; |
985 | |
986 | - errlogInit(0); |
987 | + errlogInit1(0); |
988 | if (pvtData.atExit) |
989 | return; |
990 | |
991 | @@ -338,7 +342,7 @@ |
992 | listenerNode *plistenerNode; |
993 | int count = 0; |
994 | |
995 | - errlogInit(0); |
996 | + errlogInit1(0); |
997 | if (!pvtData.atExit) |
998 | epicsMutexMustLock(pvtData.listenerLock); |
999 | |
1000 | @@ -367,14 +371,14 @@ |
1001 | |
1002 | epicsShareFunc int epicsShareAPI eltc(int yesno) |
1003 | { |
1004 | - errlogInit(0); |
1005 | + errlogInit1(0); |
1006 | pvtData.toConsole = yesno; |
1007 | return 0; |
1008 | } |
1009 | |
1010 | epicsShareFunc int errlogSetConsole(FILE *stream) |
1011 | { |
1012 | - errlogInit(0); |
1013 | + errlogInit1(0); |
1014 | pvtData.console = stream ? stream : stderr; |
1015 | return 0; |
1016 | } |
1017 | @@ -394,7 +398,7 @@ |
1018 | return; |
1019 | } |
1020 | |
1021 | - errlogInit(0); |
1022 | + errlogInit1(0); |
1023 | isOkToBlock = epicsThreadIsOkToBlock(); |
1024 | if (status == 0) |
1025 | status = errno; |
1026 | @@ -447,8 +451,9 @@ |
1027 | } |
1028 | |
1029 | |
1030 | -static void exitHandler(void *pvt) |
1031 | +void errlogShutdown(void) |
1032 | { |
1033 | + if (pvtData.atExit) return; |
1034 | pvtData.atExit = 1; |
1035 | epicsEventSignal(pvtData.waitForWork); |
1036 | epicsEventMustWait(pvtData.waitForExit); |
1037 | @@ -461,6 +466,13 @@ |
1038 | epicsMutexDestroy(pvtData.msgQueueLock); |
1039 | epicsEventDestroy(pvtData.waitForWork); |
1040 | epicsEventDestroy(pvtData.waitForExit); |
1041 | + |
1042 | + epicsThreadOnceReset(&errlogOnceFlag); |
1043 | +} |
1044 | + |
1045 | +static void errlogExitHandler(void *pvt) |
1046 | +{ |
1047 | + errlogShutdown(); |
1048 | } |
1049 | |
1050 | struct initArgs { |
1051 | @@ -505,7 +517,6 @@ |
1052 | |
1053 | epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize) |
1054 | { |
1055 | - static epicsThreadOnceId errlogOnceFlag = EPICS_THREAD_ONCE_INIT; |
1056 | struct initArgs config; |
1057 | |
1058 | if (pvtData.atExit) |
1059 | @@ -529,9 +540,15 @@ |
1060 | |
1061 | epicsShareFunc int epicsShareAPI errlogInit(int bufsize) |
1062 | { |
1063 | + pvtData.atExit = 0; |
1064 | return errlogInit2(bufsize, MAX_MESSAGE_SIZE); |
1065 | } |
1066 | |
1067 | +static void errlogInit1(int bufsize) |
1068 | +{ |
1069 | + errlogInit2(bufsize, MAX_MESSAGE_SIZE); |
1070 | +} |
1071 | + |
1072 | epicsShareFunc void epicsShareAPI errlogFlush(void) |
1073 | { |
1074 | int count; |
1075 | @@ -561,7 +578,7 @@ |
1076 | int noConsoleMessage; |
1077 | char *pmessage; |
1078 | |
1079 | - epicsAtExit(exitHandler,0); |
1080 | + epicsAtExit(errlogExitHandler,0); |
1081 | while (TRUE) { |
1082 | epicsEventMustWait(pvtData.waitForWork); |
1083 | while ((pmessage = msgbufGetSend(&noConsoleMessage))) { |
1084 | |
1085 | === modified file 'src/libCom/error/errlog.h' |
1086 | --- src/libCom/error/errlog.h 2013-06-28 17:35:43 +0000 |
1087 | +++ src/libCom/error/errlog.h 2013-12-14 18:55:29 +0000 |
1088 | @@ -64,6 +64,7 @@ |
1089 | epicsShareFunc int errlogSetConsole(FILE *stream); |
1090 | |
1091 | epicsShareFunc int epicsShareAPI errlogInit(int bufsize); |
1092 | +epicsShareFunc void epicsShareAPI errlogShutdown(void); |
1093 | epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize); |
1094 | epicsShareFunc void epicsShareAPI errlogFlush(void); |
1095 | |
1096 | |
1097 | === modified file 'src/libCom/iocsh/iocsh.cpp' |
1098 | --- src/libCom/iocsh/iocsh.cpp 2012-01-11 21:12:15 +0000 |
1099 | +++ src/libCom/iocsh/iocsh.cpp 2013-12-14 18:55:29 +0000 |
1100 | @@ -198,20 +198,22 @@ |
1101 | */ |
1102 | void epicsShareAPI iocshFree(void) |
1103 | { |
1104 | - struct iocshCommand *pc, *nc; |
1105 | - struct iocshVariable *pv, *nv; |
1106 | + struct iocshCommand *pc; |
1107 | + struct iocshVariable *pv; |
1108 | |
1109 | iocshTableLock (); |
1110 | for (pc = iocshCommandHead ; pc != NULL ; ) { |
1111 | - nc = pc->next; |
1112 | + struct iocshCommand * nc = pc->next; |
1113 | free (pc); |
1114 | pc = nc; |
1115 | } |
1116 | for (pv = iocshVariableHead ; pv != NULL ; ) { |
1117 | - nv = pv->next; |
1118 | + struct iocshVariable *nv = pv->next; |
1119 | free (pv); |
1120 | pv = nv; |
1121 | } |
1122 | + iocshCommandHead = NULL; |
1123 | + iocshVariableHead = NULL; |
1124 | iocshTableUnlock (); |
1125 | } |
1126 | |
1127 | |
1128 | === modified file 'src/libCom/misc/epicsExit.c' |
1129 | --- src/libCom/misc/epicsExit.c 2012-07-17 19:33:31 +0000 |
1130 | +++ src/libCom/misc/epicsExit.c 2013-12-14 18:55:29 +0000 |
1131 | @@ -23,7 +23,9 @@ |
1132 | */ |
1133 | |
1134 | #include <stdlib.h> |
1135 | +#include <stdio.h> |
1136 | #include <errno.h> |
1137 | +#include <string.h> |
1138 | |
1139 | #define epicsExportSharedSymbols |
1140 | #include "ellLib.h" |
1141 | @@ -36,12 +38,15 @@ |
1142 | ELLNODE node; |
1143 | epicsExitFunc func; |
1144 | void *arg; |
1145 | + char name[1]; |
1146 | }exitNode; |
1147 | |
1148 | typedef struct exitPvt { |
1149 | ELLLIST list; |
1150 | } exitPvt; |
1151 | |
1152 | +int atExitDebug = 0; |
1153 | + |
1154 | static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT; |
1155 | static exitPvt * pExitPvtPerProcess = 0; |
1156 | static epicsMutexId exitPvtLock = 0; |
1157 | @@ -66,15 +71,23 @@ |
1158 | { |
1159 | exitPvtPerThread = epicsThreadPrivateCreate (); |
1160 | assert ( exitPvtPerThread ); |
1161 | - pExitPvtPerProcess = createExitPvt (); |
1162 | - assert ( pExitPvtPerProcess ); |
1163 | exitPvtLock = epicsMutexMustCreate (); |
1164 | } |
1165 | |
1166 | +static void epicsExitInit(void) |
1167 | +{ |
1168 | + epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); |
1169 | +} |
1170 | + |
1171 | static void epicsExitCallAtExitsPvt(exitPvt *pep) |
1172 | { |
1173 | exitNode *pexitNode; |
1174 | + |
1175 | while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) { |
1176 | + if (atExitDebug && pexitNode->name[0]) |
1177 | + fprintf(stderr, "atExit %s(%p)\n", pexitNode->name, pexitNode->arg); |
1178 | + else if(atExitDebug) |
1179 | + fprintf(stderr, "atExit %p(%p)\n", pexitNode->func, pexitNode->arg); |
1180 | pexitNode->func ( pexitNode->arg ); |
1181 | ellDelete ( & pep->list, & pexitNode->node ); |
1182 | free ( pexitNode ); |
1183 | @@ -84,7 +97,8 @@ |
1184 | epicsShareFunc void epicsExitCallAtExits(void) |
1185 | { |
1186 | exitPvt * pep = 0; |
1187 | - epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); |
1188 | + |
1189 | + epicsExitInit (); |
1190 | epicsMutexMustLock ( exitPvtLock ); |
1191 | if ( pExitPvtPerProcess ) { |
1192 | pep = pExitPvtPerProcess; |
1193 | @@ -100,7 +114,8 @@ |
1194 | epicsShareFunc void epicsExitCallAtThreadExits(void) |
1195 | { |
1196 | exitPvt * pep; |
1197 | - epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); |
1198 | + |
1199 | + epicsExitInit (); |
1200 | pep = epicsThreadPrivateGet ( exitPvtPerThread ); |
1201 | if ( pep ) { |
1202 | epicsExitCallAtExitsPvt ( pep ); |
1203 | @@ -109,14 +124,16 @@ |
1204 | } |
1205 | } |
1206 | |
1207 | -static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg) |
1208 | +static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const char *name) |
1209 | { |
1210 | int status = -1; |
1211 | - exitNode * pExitNode |
1212 | - = calloc ( 1, sizeof( *pExitNode ) ); |
1213 | + exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) ); |
1214 | + |
1215 | if ( pExitNode ) { |
1216 | pExitNode->func = func; |
1217 | pExitNode->arg = arg; |
1218 | + if(name) |
1219 | + strcpy(pExitNode->name, name); |
1220 | ellAdd ( & pep->list, & pExitNode->node ); |
1221 | status = 0; |
1222 | } |
1223 | @@ -126,7 +143,8 @@ |
1224 | epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg) |
1225 | { |
1226 | exitPvt * pep; |
1227 | - epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); |
1228 | + |
1229 | + epicsExitInit (); |
1230 | pep = epicsThreadPrivateGet ( exitPvtPerThread ); |
1231 | if ( ! pep ) { |
1232 | pep = createExitPvt (); |
1233 | @@ -135,16 +153,20 @@ |
1234 | } |
1235 | epicsThreadPrivateSet ( exitPvtPerThread, pep ); |
1236 | } |
1237 | - return epicsAtExitPvt ( pep, func, arg ); |
1238 | + return epicsAtExitPvt ( pep, func, arg, NULL ); |
1239 | } |
1240 | |
1241 | -epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg) |
1242 | +epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name) |
1243 | { |
1244 | int status = -1; |
1245 | - epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); |
1246 | + |
1247 | + epicsExitInit (); |
1248 | epicsMutexMustLock ( exitPvtLock ); |
1249 | + if ( !pExitPvtPerProcess ) { |
1250 | + pExitPvtPerProcess = createExitPvt (); |
1251 | + } |
1252 | if ( pExitPvtPerProcess ) { |
1253 | - status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg ); |
1254 | + status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg, name ); |
1255 | } |
1256 | epicsMutexUnlock ( exitPvtLock ); |
1257 | return status; |
1258 | @@ -153,6 +175,10 @@ |
1259 | epicsShareFunc void epicsExit(int status) |
1260 | { |
1261 | epicsExitCallAtExits(); |
1262 | - epicsThreadSleep(1.0); |
1263 | + epicsThreadSleep(0.1); |
1264 | exit(status); |
1265 | } |
1266 | + |
1267 | +#include "epicsExport.h" |
1268 | + |
1269 | +epicsExportAddress(int,atExitDebug); |
1270 | |
1271 | === modified file 'src/libCom/misc/epicsExit.h' |
1272 | --- src/libCom/misc/epicsExit.h 2012-07-17 19:33:31 +0000 |
1273 | +++ src/libCom/misc/epicsExit.h 2013-12-14 18:55:29 +0000 |
1274 | @@ -19,7 +19,8 @@ |
1275 | |
1276 | epicsShareFunc void epicsExit(int status); |
1277 | epicsShareFunc void epicsExitCallAtExits(void); |
1278 | -epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg); |
1279 | +epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name); |
1280 | +#define epicsAtExit(F,A) epicsAtExit3(F,A,#F) |
1281 | |
1282 | epicsShareFunc void epicsExitCallAtThreadExits(void); |
1283 | epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg); |
1284 | |
1285 | === modified file 'src/libCom/osi/epicsThread.h' |
1286 | --- src/libCom/osi/epicsThread.h 2012-07-06 21:33:10 +0000 |
1287 | +++ src/libCom/osi/epicsThread.h 2013-12-14 18:55:29 +0000 |
1288 | @@ -57,6 +57,9 @@ |
1289 | epicsShareFunc void epicsShareAPI epicsThreadOnce( |
1290 | epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg); |
1291 | |
1292 | +epicsShareFunc void epicsShareAPI epicsThreadOnceReset( |
1293 | + epicsThreadOnceId *id); |
1294 | + |
1295 | epicsShareFunc void epicsShareAPI epicsThreadExitMain(void); |
1296 | |
1297 | epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate ( |
1298 | |
1299 | === modified file 'src/libCom/osi/os/RTEMS/osdThread.c' |
1300 | --- src/libCom/osi/os/RTEMS/osdThread.c 2012-07-31 19:04:38 +0000 |
1301 | +++ src/libCom/osi/os/RTEMS/osdThread.c 2013-12-14 18:55:29 +0000 |
1302 | @@ -505,6 +505,15 @@ |
1303 | epicsMutexUnlock(onceMutex); |
1304 | } |
1305 | |
1306 | +epicsShareFunc void epicsShareAPI epicsThreadOnceReset( |
1307 | + epicsThreadOnceId *id) |
1308 | +{ |
1309 | + if (!initialized) epicsThreadInit(); |
1310 | + epicsMutexMustLock(onceMutex); |
1311 | + *id = EPICS_THREAD_ONCE_INIT; |
1312 | + epicsMutexUnlock(onceMutex); |
1313 | +} |
1314 | + |
1315 | /* |
1316 | * Thread private storage implementation based on the vxWorks |
1317 | * implementation by Andrew Johnson APS/ASD. |
1318 | |
1319 | === modified file 'src/libCom/osi/os/WIN32/osdThread.c' |
1320 | --- src/libCom/osi/os/WIN32/osdThread.c 2013-06-07 23:08:38 +0000 |
1321 | +++ src/libCom/osi/os/WIN32/osdThread.c 2013-12-14 18:55:29 +0000 |
1322 | @@ -1058,6 +1058,17 @@ |
1323 | LeaveCriticalSection ( & pGbl->mutex ); |
1324 | } |
1325 | |
1326 | + |
1327 | +epicsShareFunc void epicsShareAPI epicsThreadOnceReset( |
1328 | + epicsThreadOnceId *id) |
1329 | +{ |
1330 | + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); |
1331 | + assert ( pGbl ); |
1332 | + EnterCriticalSection ( & pGbl->mutex ); |
1333 | + *id = EPICS_THREAD_ONCE_INIT; |
1334 | + LeaveCriticalSection ( & pGbl->mutex ); |
1335 | +} |
1336 | + |
1337 | /* |
1338 | * epicsThreadPrivateCreate () |
1339 | */ |
1340 | |
1341 | === modified file 'src/libCom/osi/os/posix/osdThread.c' |
1342 | --- src/libCom/osi/os/posix/osdThread.c 2012-09-20 19:55:32 +0000 |
1343 | +++ src/libCom/osi/os/posix/osdThread.c 2013-12-14 18:55:29 +0000 |
1344 | @@ -465,6 +465,25 @@ |
1345 | checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnce"); |
1346 | } |
1347 | |
1348 | +epicsShareFunc void epicsShareAPI epicsThreadOnceReset( |
1349 | + epicsThreadOnceId *id) |
1350 | +{ |
1351 | + int status; |
1352 | + |
1353 | + epicsThreadInit(); |
1354 | + status = mutexLock(&onceLock); |
1355 | + if(status) { |
1356 | + fprintf(stderr,"epicsThreadOnceReset: pthread_mutex_lock returned %s.\n", |
1357 | + strerror(status)); |
1358 | + exit(-1); |
1359 | + } |
1360 | + |
1361 | + *id = EPICS_THREAD_ONCE_INIT; |
1362 | + |
1363 | + status = pthread_mutex_unlock(&onceLock); |
1364 | + checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnceReset"); |
1365 | +} |
1366 | + |
1367 | epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate(const char *name, |
1368 | unsigned int priority, unsigned int stackSize, |
1369 | EPICSTHREADFUNC funptr,void *parm) |
1370 | |
1371 | === modified file 'src/libCom/osi/os/vxWorks/osdThread.c' |
1372 | --- src/libCom/osi/os/vxWorks/osdThread.c 2013-06-07 23:08:38 +0000 |
1373 | +++ src/libCom/osi/os/vxWorks/osdThread.c 2013-12-14 18:55:29 +0000 |
1374 | @@ -167,7 +167,19 @@ |
1375 | } |
1376 | semGive(epicsThreadOnceMutex); |
1377 | } |
1378 | - |
1379 | |
1380 | + |
1381 | +epicsShareFunc void epicsShareAPI epicsThreadOnceReset( |
1382 | + epicsThreadOnceId *id) |
1383 | +{ |
1384 | + int result; |
1385 | + |
1386 | + epicsThreadInit(); |
1387 | + result = semTake(epicsThreadOnceMutex, WAIT_FOREVER); |
1388 | + assert(result == OK); |
1389 | + *id = EPICS_THREAD_ONCE_INIT; |
1390 | + semGive(epicsThreadOnceMutex); |
1391 | +} |
1392 | + |
1393 | static void createFunction(EPICSTHREADFUNC func, void *parm) |
1394 | { |
1395 | int tid = taskIdSelf(); |
1396 | |
1397 | === modified file 'src/libCom/taskwd/taskwd.c' |
1398 | --- src/libCom/taskwd/taskwd.c 2012-05-04 18:38:59 +0000 |
1399 | +++ src/libCom/taskwd/taskwd.c 2013-12-14 18:55:29 +0000 |
1400 | @@ -70,10 +70,11 @@ |
1401 | |
1402 | /* Watchdog task control */ |
1403 | static volatile enum { |
1404 | - twdctlInit, twdctlRun, twdctlDisable, twdctlExit |
1405 | + ctlInit, ctlRun, ctlDisable, ctlExit |
1406 | } twdCtl; |
1407 | static epicsEventId loopEvent; |
1408 | static epicsEventId exitEvent; |
1409 | +static epicsThreadOnceId twdOnceFlag = EPICS_THREAD_ONCE_INIT; |
1410 | |
1411 | /* Task delay times (seconds) */ |
1412 | #define TASKWD_DELAY 6.0 |
1413 | @@ -90,8 +91,8 @@ |
1414 | struct tNode *pt; |
1415 | struct mNode *pm; |
1416 | |
1417 | - while (twdCtl != twdctlExit) { |
1418 | - if (twdCtl == twdctlRun) { |
1419 | + while (twdCtl != ctlExit) { |
1420 | + if (twdCtl == ctlRun) { |
1421 | epicsMutexMustLock(tLock); |
1422 | pt = (struct tNode *)ellFirst(&tList); |
1423 | while (pt) { |
1424 | @@ -127,12 +128,26 @@ |
1425 | epicsEventSignal(exitEvent); |
1426 | } |
1427 | |
1428 | - |
1429 | -static void twdShutdown(void *arg) |
1430 | +void taskwdShutdown(void) |
1431 | { |
1432 | - twdCtl = twdctlExit; |
1433 | + if (twdCtl == ctlExit) return; |
1434 | + twdCtl = ctlExit; |
1435 | + |
1436 | epicsEventSignal(loopEvent); |
1437 | epicsEventWait(exitEvent); |
1438 | + |
1439 | + epicsEventDestroy(exitEvent); |
1440 | + epicsEventDestroy(loopEvent); |
1441 | + epicsMutexDestroy(fLock); |
1442 | + epicsMutexDestroy(mLock); |
1443 | + epicsMutexDestroy(tLock); |
1444 | + |
1445 | + epicsThreadOnceReset(&twdOnceFlag); |
1446 | +} |
1447 | + |
1448 | +static void twdExit(void *arg) |
1449 | +{ |
1450 | + taskwdShutdown(); |
1451 | } |
1452 | |
1453 | static void twdInitOnce(void *arg) |
1454 | @@ -143,7 +158,7 @@ |
1455 | mLock = epicsMutexMustCreate(); |
1456 | fLock = epicsMutexMustCreate(); |
1457 | |
1458 | - twdCtl = twdctlRun; |
1459 | + twdCtl = ctlRun; |
1460 | loopEvent = epicsEventMustCreate(epicsEventEmpty); |
1461 | exitEvent = epicsEventMustCreate(epicsEventEmpty); |
1462 | |
1463 | @@ -153,12 +168,11 @@ |
1464 | if (tid == 0) |
1465 | cantProceed("Failed to spawn task watchdog thread\n"); |
1466 | |
1467 | - epicsAtExit(twdShutdown, NULL); |
1468 | + epicsAtExit(twdExit, NULL); |
1469 | } |
1470 | |
1471 | void taskwdInit(void) |
1472 | { |
1473 | - static epicsThreadOnceId twdOnceFlag = EPICS_THREAD_ONCE_INIT; |
1474 | epicsThreadOnce(&twdOnceFlag, twdInitOnce, NULL); |
1475 | } |
1476 | |
1477 | |
1478 | === modified file 'src/libCom/taskwd/taskwd.h' |
1479 | --- src/libCom/taskwd/taskwd.h 2010-10-05 19:27:37 +0000 |
1480 | +++ src/libCom/taskwd/taskwd.h 2013-12-14 18:55:29 +0000 |
1481 | @@ -28,6 +28,7 @@ |
1482 | |
1483 | /* Initialization, optional */ |
1484 | epicsShareFunc void taskwdInit(void); |
1485 | +epicsShareFunc void taskwdShutdown(void); |
1486 | |
1487 | |
1488 | /* For tasks to be monitored */ |
1489 | |
1490 | === modified file 'src/libCom/test/epicsExitTest.c' |
1491 | --- src/libCom/test/epicsExitTest.c 2009-04-08 22:39:27 +0000 |
1492 | +++ src/libCom/test/epicsExitTest.c 2013-12-14 18:55:29 +0000 |
1493 | @@ -59,12 +59,20 @@ |
1494 | testDiag("%s starting", pinfo->name); |
1495 | pinfo->terminate = epicsEventMustCreate(epicsEventEmpty); |
1496 | pinfo->terminated = epicsEventMustCreate(epicsEventEmpty); |
1497 | - epicsAtExit(atExit, pinfo); |
1498 | - epicsAtThreadExit(atThreadExit, pinfo); |
1499 | + testOk(!epicsAtExit(atExit, pinfo), "Registered atExit(%p)", pinfo); |
1500 | + testOk(!epicsAtThreadExit(atThreadExit, pinfo), |
1501 | + "Registered atThreadExit(%p)", pinfo); |
1502 | testDiag("%s waiting for atExit", pinfo->name); |
1503 | epicsEventMustWait(pinfo->terminate); |
1504 | } |
1505 | |
1506 | +int count; |
1507 | + |
1508 | +static void counter(void *pvt) |
1509 | +{ |
1510 | + count++; |
1511 | +} |
1512 | + |
1513 | static void mainExit(void *pvt) |
1514 | { |
1515 | testPass("Reached mainExit"); |
1516 | @@ -77,16 +85,23 @@ |
1517 | info *pinfoA = (info *)calloc(1, sizeof(info)); |
1518 | info *pinfoB = (info *)calloc(1, sizeof(info)); |
1519 | |
1520 | - testPlan(7); |
1521 | - |
1522 | - epicsAtExit(mainExit, NULL); |
1523 | + testPlan(15); |
1524 | + |
1525 | + testOk(!epicsAtExit(counter, NULL), "Registered counter()"); |
1526 | + count = 0; |
1527 | + epicsExitCallAtExits(); |
1528 | + testOk(count == 1, "counter() called once"); |
1529 | + epicsExitCallAtExits(); |
1530 | + testOk(count == 1, "unregistered counter() not called"); |
1531 | + |
1532 | + testOk(!epicsAtExit(mainExit, NULL), "Registered mainExit()"); |
1533 | |
1534 | epicsThreadCreate("threadA", 50, stackSize, thread, pinfoA); |
1535 | epicsThreadSleep(0.1); |
1536 | epicsThreadCreate("threadB", 50, stackSize, thread, pinfoB); |
1537 | epicsThreadSleep(1.0); |
1538 | |
1539 | - testDiag("Calling epicsExit\n"); |
1540 | + testDiag("Calling epicsExit"); |
1541 | epicsExit(0); |
1542 | return 0; |
1543 | } |
1544 | |
1545 | === modified file 'src/libCom/test/epicsThreadOnceTest.c' |
1546 | --- src/libCom/test/epicsThreadOnceTest.c 2012-07-06 22:47:56 +0000 |
1547 | +++ src/libCom/test/epicsThreadOnceTest.c 2013-12-14 18:55:29 +0000 |
1548 | @@ -21,6 +21,7 @@ |
1549 | |
1550 | epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT; |
1551 | epicsThreadOnceId twiceFlag = EPICS_THREAD_ONCE_INIT; |
1552 | +epicsThreadOnceId resetFlag = EPICS_THREAD_ONCE_INIT; |
1553 | epicsMutexId lock; |
1554 | epicsEventId go; |
1555 | epicsEventId done; |
1556 | @@ -80,7 +81,7 @@ |
1557 | int i; |
1558 | epicsThreadId tid; |
1559 | |
1560 | - testPlan(3 + NUM_ONCE_THREADS); |
1561 | + testPlan(7 + NUM_ONCE_THREADS); |
1562 | |
1563 | go = epicsEventMustCreate(epicsEventEmpty); |
1564 | done = epicsEventMustCreate(epicsEventEmpty); |
1565 | @@ -112,6 +113,17 @@ |
1566 | } while (!epicsThreadIsSuspended(tid)); |
1567 | testPass("Recursive epicsThreadOnce() detected"); |
1568 | |
1569 | + initCount = 0; |
1570 | + epicsThreadOnce(&resetFlag, &onceInit, NULL); |
1571 | + testOk1(initCount==1); |
1572 | + epicsThreadOnce(&resetFlag, &onceInit, NULL); |
1573 | + testOk1(initCount==1); |
1574 | + epicsThreadOnceReset(&resetFlag); |
1575 | + epicsThreadOnce(&resetFlag, &onceInit, NULL); |
1576 | + testOk1(initCount==2); |
1577 | + epicsThreadOnce(&resetFlag, &onceInit, NULL); |
1578 | + testOk1(initCount==2); |
1579 | + |
1580 | eltc(1); |
1581 | return testDone(); |
1582 | } |
Ralph, have you tested re-initialization? I see that callbackOnceFlag in callback.c is not being reset.