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

Proposed by Ralph Lange on 2013-10-11
Status: Rejected
Rejected by: Andrew Johnson on 2014-07-23
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
Reviewer Review Type Date Requested Status
Andrew Johnson 2013-10-11 Disapprove on 2014-07-23
Review via email: mp+190512@code.launchpad.net

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://code.launchpad.net/~epics-documenters/epics-appdev/ioc-shutdown

To post a comment you must log in.
mdavidsaver (mdavidsaver) wrote :

Ralph, have you tested re-initialization? I see that callbackOnceFlag in callback.c is not being reset.

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 -- iocshRegisterVariable redefining asCaDebug.

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 on 2013-10-11

dbShutdownTest

12447. By mdavidsaver on 2013-10-11

asShutdown

12448. By mdavidsaver on 2013-10-11

cleanup iocsh

12449. By mdavidsaver on 2013-10-11

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 on 2013-10-22

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 on 2013-10-22

ioc/db: Fix restart issue for callback threads.

12452. By Ralph Lange on 2013-10-22

libCom/error: Fix restart issue for errlog facility

12453. By Ralph Lange on 2013-10-22

libCom/taskwd: Fix restart issue.

12454. By Ralph Lange on 2013-10-22

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 on 2013-10-22

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_from_cvs_trunk branch has the same problem). The easier you make it to review a branch, the sooner and quicker it will likely be reviewed.

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 epicsExitCallAtExits() instead, and just ensure that each system's registered shutdown function leaves the subsystem in a state that can be properly restarted?

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_ONCE_INIT through <active> to EPICS_THREAD_ONCE_DONE happens within the protection of a mutex inside the epicsThreadOnce() routines, the mutex providing a memory barrier on SMP. The reversals here are unprotected by either, and don't check that the subsystems have even been initialized either — could some other thread such as a time provider on a different CPU call a subsystem function at the wrong time while the subsystem is shutting down and crash it?

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 epicsExitCallAtExits() routines, but existing support don't use that to prepare themselves for a restart).

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

review: Needs Fixing
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 epicsExitCallAtExits() instead, 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 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 epicsExitCallAtExits() routines, but existing support don't use that to prepare themselves for a restart).

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 epicsExitCallAtExits() instead,
> 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 epicsExitCallAtExits() is that we don't have to
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 epicsExitCallAtExits() is that we don't have to
> 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/pause/exit, etc.
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 epicsExitCallAtExits() solution, instead of adding all the public Shutdown routines (which also reduces the documentation changes needed). Maybe we could add a global flag to epicsExit.c which the registered routines can check to decide whether they need to do a full clean-up ready for a restart, or just a quick shutdown (I want to avoid them all wasting time freeing memory if the process is about to exit or VxWorks is about to reboot).

Andrew Johnson (anj) wrote :

epicsExitCallAtExits() is not currently re-usable, but can be made so relatively easily, I have the necessary changes here.

12456. By mdavidsaver on 2013-12-05

epicsThreadOnceReset

12457. By mdavidsaver on 2013-12-05

use epicsThreadOnceReset

12458. By mdavidsaver on 2013-12-05

cleanup initHook

12459. By mdavidsaver on 2013-12-05

add dbUnitTest.h

hide boilerplate of tests using the PDB

mdavidsaver (mdavidsaver) wrote :

I've added epicsThreadOnceReset() to provide a safe way to reset once flags.

Also I introduce dbUnitTest.h to provide the boilerplate which unittests running in an IOC should use.

12460. By Andrew Johnson on 2013-12-05

Make epicsExit subsystem reusable.

Calling epicsAtExit() after epicsExitCallAtExits() now
recreates the per-process list and registers the routine.

12461. By mdavidsaver on 2013-12-14

deletePeriodic: fix list free

Can't iterate on free'd node

12462. By mdavidsaver on 2013-12-14

dbScan: avoid double shutdown

scanShutdown will be called (later) by iocShutdown
via exitDatabase()

12463. By mdavidsaver on 2013-12-14

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 on 2013-12-14

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.

review: Disapprove

Unmerged revisions

12464. By mdavidsaver on 2013-12-14

errlog: nicer exit handler name

12463. By mdavidsaver on 2013-12-14

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 on 2013-12-14

dbScan: avoid double shutdown

scanShutdown will be called (later) by iocShutdown
via exitDatabase()

12461. By mdavidsaver on 2013-12-14

deletePeriodic: fix list free

Can't iterate on free'd node

12460. By Andrew Johnson on 2013-12-05

Make epicsExit subsystem reusable.

Calling epicsAtExit() after epicsExitCallAtExits() now
recreates the per-process list and registers the routine.

12459. By mdavidsaver on 2013-12-05

add dbUnitTest.h

hide boilerplate of tests using the PDB

12458. By mdavidsaver on 2013-12-05

cleanup initHook

12457. By mdavidsaver on 2013-12-05

use epicsThreadOnceReset

12456. By mdavidsaver on 2013-12-05

epicsThreadOnceReset

12455. By Ralph Lange on 2013-10-22

ioc/misc: add missing include in iocInit.c

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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 }

Subscribers

People subscribed via source and target branches

to all changes: