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
=== modified file 'src/Makefile'
--- src/Makefile 2012-06-22 23:16:26 +0000
+++ src/Makefile 2013-12-14 18:55:29 +0000
@@ -62,7 +62,7 @@
62ioc_DEPEND_DIRS = libCom ca/client62ioc_DEPEND_DIRS = libCom ca/client
6363
64DIRS += ioc/db/test64DIRS += ioc/db/test
65ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS65ioc/db/test_DEPEND_DIRS = ioc std libCom/RTEMS
6666
67DIRS += ioc/dbtemplate/test67DIRS += ioc/dbtemplate/test
68ioc/dbtemplate/test_DEPEND_DIRS = ioc68ioc/dbtemplate/test_DEPEND_DIRS = ioc
6969
=== modified file 'src/ioc/as/asDbLib.c'
--- src/ioc/as/asDbLib.c 2012-10-01 05:54:10 +0000
+++ src/ioc/as/asDbLib.c 2013-12-14 18:55:29 +0000
@@ -152,6 +152,15 @@
152 return(asInitCommon());152 return(asInitCommon());
153}153}
154154
155int asShutdown(void) {
156 volatile ASBASE *pbase = pasbase;
157 pasbase = NULL;
158 firstTime = TRUE;
159 if(pbase)
160 asFreeAll((ASBASE*)pbase);
161 return 0;
162}
163
155static void wdCallback(void *arg)164static void wdCallback(void *arg)
156{165{
157 ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg;166 ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg;
158167
=== modified file 'src/ioc/as/asDbLib.h'
--- src/ioc/as/asDbLib.h 2012-07-15 21:08:50 +0000
+++ src/ioc/as/asDbLib.h 2013-12-14 18:55:29 +0000
@@ -32,6 +32,7 @@
32epicsShareFunc int asSetSubstitutions(const char *substitutions);32epicsShareFunc int asSetSubstitutions(const char *substitutions);
33epicsShareFunc int asInit(void);33epicsShareFunc int asInit(void);
34epicsShareFunc int asInitAsyn(ASDBCALLBACK *pcallback);34epicsShareFunc int asInitAsyn(ASDBCALLBACK *pcallback);
35epicsShareFunc int asShutdown(void);
35epicsShareFunc int asDbGetAsl(struct dbChannel *chan);36epicsShareFunc int asDbGetAsl(struct dbChannel *chan);
36epicsShareFunc void * asDbGetMemberPvt(struct dbChannel *chan);37epicsShareFunc void * asDbGetMemberPvt(struct dbChannel *chan);
37epicsShareFunc int asdbdump(void);38epicsShareFunc int asdbdump(void);
3839
=== modified file 'src/ioc/db/Makefile'
--- src/ioc/db/Makefile 2012-11-29 18:53:21 +0000
+++ src/ioc/db/Makefile 2013-12-14 18:55:29 +0000
@@ -37,6 +37,7 @@
37INC += dbState.h37INC += dbState.h
38INC += db_access_routines.h38INC += db_access_routines.h
39INC += db_convert.h39INC += db_convert.h
40INC += dbUnitTest.h
4041
41# Generate menuGlobal.dbd automatically42# Generate menuGlobal.dbd automatically
42DBD += menuGlobal.dbd43DBD += menuGlobal.dbd
@@ -86,4 +87,4 @@
86dbCore_SRCS += dbIocRegister.c87dbCore_SRCS += dbIocRegister.c
87dbCore_SRCS += chfPlugin.c88dbCore_SRCS += chfPlugin.c
88dbCore_SRCS += dbState.c89dbCore_SRCS += dbState.c
8990dbCore_SRCS += dbUnitTest.c
9091
=== modified file 'src/ioc/db/callback.c'
--- src/ioc/db/callback.c 2012-05-04 22:34:48 +0000
+++ src/ioc/db/callback.c 2013-12-14 18:55:29 +0000
@@ -53,6 +53,8 @@
53static epicsTimerQueueId timerQueue;53static epicsTimerQueueId timerQueue;
5454
55/* Shutdown handling */55/* Shutdown handling */
56enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
57static volatile enum ctl cbCtl;
56static epicsEventId startStopEvent;58static epicsEventId startStopEvent;
57static void *exitCallback;59static void *exitCallback;
5860
@@ -101,10 +103,13 @@
101 epicsEventSignal(startStopEvent);103 epicsEventSignal(startStopEvent);
102}104}
103105
104static void callbackShutdown(void *arg)106void callbackShutdown(void)
105{107{
106 int i;108 int i;
107109
110 if (cbCtl == ctlExit) return;
111 cbCtl = ctlExit;
112
108 for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {113 for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
109 int lockKey = epicsInterruptLock();114 int lockKey = epicsInterruptLock();
110 int ok = epicsRingPointerPush(callbackQ[i], &exitCallback);115 int ok = epicsRingPointerPush(callbackQ[i], &exitCallback);
@@ -112,6 +117,13 @@
112 epicsEventSignal(callbackSem[i]);117 epicsEventSignal(callbackSem[i]);
113 if (ok) epicsEventWait(startStopEvent);118 if (ok) epicsEventWait(startStopEvent);
114 }119 }
120 epicsTimerQueueRelease(timerQueue);
121 epicsThreadOnceReset(&callbackOnceFlag);
122}
123
124static void callbackExit(void *arg)
125{
126 callbackShutdown();
115}127}
116128
117static void callbackInitOnce(void *arg)129static void callbackInitOnce(void *arg)
@@ -119,6 +131,7 @@
119 int i;131 int i;
120132
121 startStopEvent = epicsEventMustCreate(epicsEventEmpty);133 startStopEvent = epicsEventMustCreate(epicsEventEmpty);
134 cbCtl = ctlRun;
122 timerQueue = epicsTimerQueueAllocate(0,epicsThreadPriorityScanHigh);135 timerQueue = epicsTimerQueueAllocate(0,epicsThreadPriorityScanHigh);
123 for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {136 for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
124 epicsThreadId tid;137 epicsThreadId tid;
@@ -137,7 +150,7 @@
137 else150 else
138 epicsEventWait(startStopEvent);151 epicsEventWait(startStopEvent);
139 }152 }
140 epicsAtExit(callbackShutdown, NULL);153 epicsAtExit(callbackExit, NULL);
141}154}
142155
143void callbackInit(void)156void callbackInit(void)
144157
=== modified file 'src/ioc/db/callback.h'
--- src/ioc/db/callback.h 2010-10-05 19:27:37 +0000
+++ src/ioc/db/callback.h 2013-12-14 18:55:29 +0000
@@ -57,6 +57,7 @@
5757
58epicsShareFunc void callbackInit(void);58epicsShareFunc void callbackInit(void);
59epicsShareFunc void callbackRequest(CALLBACK *pCallback);59epicsShareFunc void callbackRequest(CALLBACK *pCallback);
60epicsShareFunc void callbackShutdown(void);
60epicsShareFunc void callbackSetProcess(61epicsShareFunc void callbackSetProcess(
61 CALLBACK *pcallback, int Priority, void *pRec);62 CALLBACK *pcallback, int Priority, void *pRec);
62epicsShareFunc void callbackRequestProcessCallback(63epicsShareFunc void callbackRequestProcessCallback(
6364
=== modified file 'src/ioc/db/dbCa.c'
--- src/ioc/db/dbCa.c 2012-06-22 23:16:26 +0000
+++ src/ioc/db/dbCa.c 2013-12-14 18:55:29 +0000
@@ -173,15 +173,22 @@
173 dbScanUnlock(pdbCommon);173 dbScanUnlock(pdbCommon);
174}174}
175175
176static void dbCaShutdown(void *arg)176void dbCaShutdown(void)
177{177{
178 if (dbCaCtl == ctlRun) {178 if (dbCaCtl == ctlRun || dbCaCtl == ctlPause) {
179 dbCaCtl = ctlExit;179 dbCaCtl = ctlExit;
180 epicsEventSignal(workListEvent);180 epicsEventSignal(workListEvent);
181 epicsEventMustWait(startStopEvent);181 epicsEventMustWait(startStopEvent);
182 epicsEventDestroy(startStopEvent);
183 epicsEventDestroy(workListEvent);
182 }184 }
183}185}
184186
187static void dbCaExit(void *arg)
188{
189 dbCaShutdown();
190}
191
185void dbCaLinkInit(void)192void dbCaLinkInit(void)
186{193{
187 dbServiceIOInit();194 dbServiceIOInit();
@@ -194,19 +201,23 @@
194 epicsThreadGetStackSize(epicsThreadStackBig),201 epicsThreadGetStackSize(epicsThreadStackBig),
195 dbCaTask, NULL);202 dbCaTask, NULL);
196 epicsEventMustWait(startStopEvent);203 epicsEventMustWait(startStopEvent);
197 epicsAtExit(dbCaShutdown, NULL);204 epicsAtExit(dbCaExit, NULL);
198}205}
199206
200void dbCaRun(void)207void dbCaRun(void)
201{208{
202 dbCaCtl = ctlRun;209 if (dbCaCtl == ctlPause) {
203 epicsEventSignal(workListEvent);210 dbCaCtl = ctlRun;
211 epicsEventSignal(workListEvent);
212 }
204}213}
205214
206void dbCaPause(void)215void dbCaPause(void)
207{216{
208 dbCaCtl = ctlPause;217 if (dbCaCtl == ctlRun) {
209 epicsEventSignal(workListEvent);218 dbCaCtl = ctlPause;
219 epicsEventSignal(workListEvent);
220 }
210}221}
211222
212void dbCaAddLinkCallback(struct link *plink,223void dbCaAddLinkCallback(struct link *plink,
213224
=== modified file 'src/ioc/db/dbCa.h'
--- src/ioc/db/dbCa.h 2012-04-27 17:21:47 +0000
+++ src/ioc/db/dbCa.h 2013-12-14 18:55:29 +0000
@@ -26,6 +26,7 @@
26epicsShareFunc void dbCaLinkInit(void);26epicsShareFunc void dbCaLinkInit(void);
27epicsShareFunc void dbCaRun(void);27epicsShareFunc void dbCaRun(void);
28epicsShareFunc void dbCaPause(void);28epicsShareFunc void dbCaPause(void);
29epicsShareFunc void dbCaShutdown(void);
2930
30epicsShareFunc void dbCaAddLinkCallback(struct link *plink,31epicsShareFunc void dbCaAddLinkCallback(struct link *plink,
31 dbCaCallback connect, dbCaCallback monitor, void *userPvt);32 dbCaCallback connect, dbCaCallback monitor, void *userPvt);
3233
=== modified file 'src/ioc/db/dbScan.c'
--- src/ioc/db/dbScan.c 2013-04-23 15:38:57 +0000
+++ src/ioc/db/dbScan.c 2013-12-14 18:55:29 +0000
@@ -3,6 +3,8 @@
3* National Laboratory.3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.5* Operator of Los Alamos National Laboratory.
6* Copyright (c) 2013 Helmholtz-Zentrum Berlin
7* für Materialien und Energie GmbH.
6* EPICS BASE is distributed subject to a Software License Agreement found8* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution. 9* in file LICENSE that is included with this distribution.
8\*************************************************************************/10\*************************************************************************/
@@ -129,6 +131,7 @@
129static void initOnce(void);131static void initOnce(void);
130static void periodicTask(void *arg);132static void periodicTask(void *arg);
131static void initPeriodic(void);133static void initPeriodic(void);
134static void deletePeriodic(void);
132static void spawnPeriodic(int ind);135static void spawnPeriodic(int ind);
133static void initEvent(void);136static void initEvent(void);
134static void eventCallback(CALLBACK *pcallback);137static void eventCallback(CALLBACK *pcallback);
@@ -139,10 +142,13 @@
139static void addToList(struct dbCommon *precord, scan_list *psl);142static void addToList(struct dbCommon *precord, scan_list *psl);
140static void deleteFromList(struct dbCommon *precord, scan_list *psl);143static void deleteFromList(struct dbCommon *precord, scan_list *psl);
141144
142145
143static void scanShutdown(void *arg)146void scanShutdown(void)
144{147{
145 int i;148 int i;
146149
150 if (scanCtl == ctlExit) return;
151 scanCtl = ctlExit;
152
147 interruptAccept = FALSE;153 interruptAccept = FALSE;
148154
149 for (i = 0; i < nPeriodic; i++) {155 for (i = 0; i < nPeriodic; i++) {
@@ -153,6 +159,18 @@
153159
154 scanOnce((dbCommon *)&exitOnce);160 scanOnce((dbCommon *)&exitOnce);
155 epicsEventWait(startStopEvent);161 epicsEventWait(startStopEvent);
162
163 deletePeriodic();
164
165 epicsRingPointerDelete(onceQ);
166
167 epicsEventDestroy(startStopEvent);
168 epicsEventDestroy(onceSem);
169 onceSem = startStopEvent = NULL;
170
171 free(periodicTaskId);
172 papPeriodic = NULL;
173 periodicTaskId = NULL;
156}174}
157175
158long scanInit(void)176long scanInit(void)
@@ -169,7 +187,6 @@
169 for (i = 0; i < nPeriodic; i++)187 for (i = 0; i < nPeriodic; i++)
170 spawnPeriodic(i);188 spawnPeriodic(i);
171189
172 epicsAtExit(scanShutdown, NULL);
173 return 0;190 return 0;
174}191}
175192
@@ -672,6 +689,22 @@
672 }689 }
673}690}
674691
692static void deletePeriodic(void)
693{
694 int i;
695
696 for (i = 0; i < nPeriodic; i++) {
697 periodic_scan_list *ppsl = papPeriodic[i];
698 ellFree(&ppsl->scan_list.list);
699 epicsEventDestroy(ppsl->loopEvent);
700 epicsMutexDestroy(ppsl->scan_list.lock);
701 free(ppsl);
702 }
703
704 free(papPeriodic);
705 papPeriodic = NULL;
706}
707
675static void spawnPeriodic(int ind)708static void spawnPeriodic(int ind)
676{709{
677 periodic_scan_list *ppsl;710 periodic_scan_list *ppsl;
@@ -778,23 +811,25 @@
778{811{
779 dbRecordType *pdbRecordType;812 dbRecordType *pdbRecordType;
780813
781 /*Look for first record*/
782 for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);814 for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList);
783 pdbRecordType;815 pdbRecordType;
784 pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {816 pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) {
785 dbRecordNode *pdbRecordNode;817 dbRecordNode *pdbRecordNode;
818
786 for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList);819 for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList);
787 pdbRecordNode;820 pdbRecordNode;
788 pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) {821 pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) {
789 dbCommon *precord = pdbRecordNode->precord;822 dbCommon *precord = pdbRecordNode->precord;
823
790 if (!precord->name[0] ||824 if (!precord->name[0] ||
791 pdbRecordNode->flags & DBRN_FLAGS_ISALIAS)825 pdbRecordNode->flags & DBRN_FLAGS_ISALIAS)
792 continue;826 continue;
827
793 scanAdd(precord);828 scanAdd(precord);
794 }829 }
795 }830 }
796}831}
797
798832
833
799static void addToList(struct dbCommon *precord, scan_list *psl)834static void addToList(struct dbCommon *precord, scan_list *psl)
800{835{
801 scan_element *pse, *ptemp;836 scan_element *pse, *ptemp;
802837
=== modified file 'src/ioc/db/dbScan.h'
--- src/ioc/db/dbScan.h 2012-04-10 22:10:50 +0000
+++ src/ioc/db/dbScan.h 2013-12-14 18:55:29 +0000
@@ -44,6 +44,7 @@
44epicsShareFunc long scanInit(void);44epicsShareFunc long scanInit(void);
45epicsShareFunc void scanRun(void);45epicsShareFunc void scanRun(void);
46epicsShareFunc void scanPause(void);46epicsShareFunc void scanPause(void);
47epicsShareFunc void scanShutdown(void);
4748
48epicsShareFunc EVENTPVT eventNameToHandle(const char* event);49epicsShareFunc EVENTPVT eventNameToHandle(const char* event);
49epicsShareFunc void postEvent(EVENTPVT epvt);50epicsShareFunc void postEvent(EVENTPVT epvt);
5051
=== added file 'src/ioc/db/dbUnitTest.c'
--- src/ioc/db/dbUnitTest.c 1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbUnitTest.c 2013-12-14 18:55:29 +0000
@@ -0,0 +1,123 @@
1/*************************************************************************\
2* Copyright (c) 2013 Brookhaven National Laboratory.
3* Copyright (c) 2013 ITER Organization.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7
8/*
9 * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
10 * Ralph Lange <Ralph.Lange@gmx.de>
11 */
12
13#include <string.h>
14
15#include "epicsUnitTest.h"
16#include "osiFileName.h"
17#include "dbmf.h"
18#include "registry.h"
19#define epicsExportSharedSymbols
20#include "iocInit.h"
21#include "initHooks.h"
22#include "dbBase.h"
23#include "dbAccess.h"
24#include "dbStaticLib.h"
25
26#include "dbUnitTest.h"
27
28void testdbPrepare(void)
29{
30 /* No-op at the moment */
31}
32
33void testdbReadDatabase(const char* file,
34 const char* path,
35 const char* substitutions)
36{
37 if(!path)
38 path = "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
39 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common";
40 if(dbReadDatabase(&pdbbase, file, path, substitutions))
41 testAbort("Failed to load test database\ndbReadDatabase(%s,%s,%s)",
42 file, path, substitutions);
43}
44
45int testiocInit(void)
46{
47 return iocBuildNoCA() || iocRun();
48}
49
50int testiocShutdown(void)
51{
52 return iocShutdown();
53}
54
55void testdbCleanup(void)
56{
57 dbFreeBase(pdbbase);
58 initHookFree();
59 registryFree();
60 pdbbase = NULL;
61 dbmfFreeChunks();
62}
63
64long testdbPutField(const char* pv, short dbrType, ...)
65{
66 long ret;
67 va_list ap;
68 va_start(ap, dbrType);
69 ret = testVdbPutField(pv, dbrType, ap);
70 va_end(ap);
71 return ret;
72}
73
74union anybuf {
75 epicsAny val;
76 char bytes[sizeof(epicsAny)];
77};
78
79long testVdbPutField(const char* pv, short dbrType, va_list ap)
80{
81 DBADDR addr;
82 union anybuf pod;
83
84 if(dbNameToAddr(pv, &addr))
85 testAbort("Missing PV %s", pv);
86
87 switch(dbrType) {
88 case DBR_STRING: {
89 const char *uarg = va_arg(ap,char*);
90 epicsOldString buffer;
91 strncpy(buffer, uarg, sizeof(buffer));
92 buffer[sizeof(buffer)-1] = '\0';
93 return dbPutField(&addr, dbrType, buffer, 1);
94 }
95
96#define OP(DBR,Type,mem) case DBR: {pod.val.mem = va_arg(ap,Type); break;}
97 OP(DBR_CHAR, int, int8);
98 OP(DBR_UCHAR, int, uInt8);
99 OP(DBR_SHORT, int, int16);
100 OP(DBR_USHORT, int, uInt16);
101 OP(DBR_LONG, int, int32);
102 OP(DBR_ULONG, unsigned int, uInt32);
103 OP(DBR_FLOAT, double, float32);
104 OP(DBR_DOUBLE, double, float64);
105 OP(DBR_ENUM, int, enum16);
106#undef OP
107 default:
108 testAbort("invalid DBR: dbPutField(%s, %d, ...)",
109 addr.precord->name, dbrType);
110 }
111
112 return dbPutField(&addr, dbrType, pod.bytes, 1);
113}
114
115dbCommon* testGetRecord(const char* pv)
116{
117 DBADDR addr;
118
119 if(dbNameToAddr(pv, &addr))
120 testAbort("Missing PV %s", pv);
121
122 return addr.precord;
123}
0124
=== added file 'src/ioc/db/dbUnitTest.h'
--- src/ioc/db/dbUnitTest.h 1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbUnitTest.h 2013-12-14 18:55:29 +0000
@@ -0,0 +1,54 @@
1/*************************************************************************\
2* Copyright (c) 2013 Brookhaven National Laboratory.
3* Copyright (c) 2013 ITER Organization.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7
8/*
9 * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
10 * Ralph Lange <Ralph.Lange@gmx.de>
11 */
12
13#ifndef EPICSUNITTESTDB_H
14#define EPICSUNITTESTDB_H
15
16#include <stdarg.h>
17
18#include "epicsUnitTest.h"
19#include "dbAddr.h"
20#include "dbCommon.h"
21
22#include "shareLib.h"
23
24#ifdef __cplusplus
25extern "C" {
26#endif
27
28epicsShareFunc void testdbPrepare(void);
29epicsShareFunc void testdbReadDatabase(const char* file,
30 const char* path,
31 const char* substitutions);
32epicsShareFunc int testiocInit(void);
33epicsShareFunc int testiocShutdown(void);
34epicsShareFunc void testdbCleanup(void);
35
36/* Scalar only version.
37 *
38 * Remember to use the correct argument type!s
39 *
40 * int for DBR_UCHAR, DBR_CHAR, DBR_USHORT, DBR_SHORT, DBR_LONG
41 * unsigned int for DBR_ULONG
42 * double for DBR_FLOAT and DBR_DOUBLE
43 * const char* for DBR_STRING
44 */
45epicsShareFunc long testdbPutField(const char* pv, short dbrType, ...);
46epicsShareFunc long testVdbPutField(const char* pv, short dbrType, va_list ap);
47
48epicsShareFunc dbCommon* testGetRecord(const char* pv);
49
50#ifdef __cplusplus
51}
52#endif
53
54#endif // EPICSUNITTESTDB_H
055
=== modified file 'src/ioc/db/initHooks.c'
--- src/ioc/db/initHooks.c 2010-10-05 19:27:37 +0000
+++ src/ioc/db/initHooks.c 2013-12-14 18:55:29 +0000
@@ -93,6 +93,14 @@
93 }93 }
94}94}
9595
96void initHookFree(void)
97{
98 initHookInit();
99 epicsMutexMustLock(listLock);
100 ellFree(&functionList);
101 epicsMutexUnlock(listLock);
102}
103
96/*104/*
97 * Call any time you want to print out a state name.105 * Call any time you want to print out a state name.
98 */106 */
99107
=== modified file 'src/ioc/db/initHooks.h'
--- src/ioc/db/initHooks.h 2010-10-05 19:27:37 +0000
+++ src/ioc/db/initHooks.h 2013-12-14 18:55:29 +0000
@@ -60,6 +60,7 @@
60epicsShareFunc int initHookRegister(initHookFunction func);60epicsShareFunc int initHookRegister(initHookFunction func);
61epicsShareFunc void initHookAnnounce(initHookState state);61epicsShareFunc void initHookAnnounce(initHookState state);
62epicsShareFunc const char *initHookName(int state);62epicsShareFunc const char *initHookName(int state);
63epicsShareFunc void initHookFree(void);
6364
64#ifdef __cplusplus65#ifdef __cplusplus
65}66}
6667
=== modified file 'src/ioc/db/test/Makefile'
--- src/ioc/db/test/Makefile 2012-07-17 19:33:31 +0000
+++ src/ioc/db/test/Makefile 2013-12-14 18:55:29 +0000
@@ -16,6 +16,15 @@
1616
17PROD_LIBS = xRec dbCore ca Com17PROD_LIBS = xRec dbCore ca Com
1818
19TESTPROD_HOST += dbShutdownTest
20TARGETS += $(COMMON_DIR)/dbShutdownTest.dbd
21dbShutdownTest_SRCS += dbShutdownTest.c
22dbShutdownTest_SRCS += dbShutdownTest_registerRecordDeviceDriver.cpp
23dbShutdownTest_LIBS += $(EPICS_BASE_IOC_LIBS)
24dbShutdownTest_DBD += base.dbd
25TESTFILES += $(COMMON_DIR)/dbShutdownTest.dbd ../sRecord.db
26TESTS += dbShutdownTest
27
19TESTPROD_HOST += callbackTest28TESTPROD_HOST += callbackTest
20callbackTest_SRCS += callbackTest.c29callbackTest_SRCS += callbackTest.c
21testHarness_SRCS += callbackTest.c30testHarness_SRCS += callbackTest.c
2231
=== added file 'src/ioc/db/test/dbShutdownTest.c'
--- src/ioc/db/test/dbShutdownTest.c 1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/dbShutdownTest.c 2013-12-14 18:55:29 +0000
@@ -0,0 +1,94 @@
1/*************************************************************************\
2* Copyright (c) 2013 Brookhaven National Laboratory.
3* Copyright (c) 2013 ITER Organization.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6 \*************************************************************************/
7
8/*
9 * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
10 * Ralph Lange <Ralph.Lange@gmx.de>
11 */
12
13#include <string.h>
14
15#include "dbUnitTest.h"
16#include "epicsThread.h"
17#include "iocInit.h"
18#include "dbBase.h"
19#include "dbAccess.h"
20#include "registry.h"
21#include "dbStaticLib.h"
22#include "osiFileName.h"
23#include "dbmf.h"
24
25#include "testMain.h"
26
27void dbShutdownTest_registerRecordDeviceDriver(struct dbBase *);
28
29static struct threadItem {
30 char *name;
31 char found;
32} commonThreads[] = {
33 { "errlog", 0 },
34 { "taskwd", 0 },
35 { "timerQueue", 0 },
36 { "cbLow", 0 },
37 { "scanOnce", 0 },
38 { NULL, 0 }
39};
40
41static
42void findCommonThread (epicsThreadId id) {
43 struct threadItem *thr;
44 char name[32];
45
46 epicsThreadGetName(id, name, 32);
47
48 for (thr = commonThreads; thr->name; thr++) {
49 if (strcasecmp(thr->name, name) == 0) {
50 thr->found = 1;
51 }
52 }
53}
54
55static
56void checkCommonThreads (void) {
57 struct threadItem *thr;
58
59 for (thr = commonThreads; thr->name; thr++) {
60 testOk(thr->found, "Thread %s is running", thr->name);
61 thr->found = 0;
62 }
63}
64
65static
66void cycle(void) {
67
68 testdbPrepare();
69
70 testdbReadDatabase("dbShutdownTest.dbd", NULL, NULL);
71
72 dbShutdownTest_registerRecordDeviceDriver(pdbbase);
73
74 testdbReadDatabase("sRecord.db", NULL, NULL);
75
76 testOk1(!testiocInit());
77
78 epicsThreadMap(findCommonThread);
79 checkCommonThreads();
80
81 testOk1(testiocShutdown()==0);
82
83 testdbCleanup();
84}
85
86MAIN(dbShutdownTest)
87{
88 testPlan(14);
89
90 cycle();
91 cycle();
92
93 return testDone();
94}
095
=== added file 'src/ioc/db/test/sRecord.db'
--- src/ioc/db/test/sRecord.db 1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/sRecord.db 2013-12-14 18:55:29 +0000
@@ -0,0 +1,1 @@
1record(ai, "somename") {}
02
=== modified file 'src/ioc/misc/dbCore.dbd'
--- src/ioc/misc/dbCore.dbd 2011-02-26 05:56:51 +0000
+++ src/ioc/misc/dbCore.dbd 2013-12-14 18:55:29 +0000
@@ -5,6 +5,9 @@
5# This file provides iocsh access to variables that control some lesser-used5# This file provides iocsh access to variables that control some lesser-used
6# and debugging features of the IOC database code.6# and debugging features of the IOC database code.
77
8# show epicsAtExit callbacks as they are run
9variable(atExitDebug,int)
10
8# Access security subroutines11# Access security subroutines
9variable(asCaDebug,int)12variable(asCaDebug,int)
1013
1114
=== modified file 'src/ioc/misc/iocInit.c'
--- src/ioc/misc/iocInit.c 2013-05-30 20:00:37 +0000
+++ src/ioc/misc/iocInit.c 2013-12-14 18:55:29 +0000
@@ -3,6 +3,8 @@
3* National Laboratory.3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.5* Operator of Los Alamos National Laboratory.
6* Copyright (c) 2013 Helmholtz-Zentrum Berlin
7* für Materialien und Energie GmbH.
6* EPICS BASE is distributed subject to a Software License Agreement found8* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution. 9* in file LICENSE that is included with this distribution.
8\*************************************************************************/10\*************************************************************************/
@@ -31,6 +33,7 @@
31#include "errMdef.h"33#include "errMdef.h"
32#include "taskwd.h"34#include "taskwd.h"
33#include "caeventmask.h"35#include "caeventmask.h"
36#include "iocsh.h"
3437
35#define epicsExportSharedSymbols38#define epicsExportSharedSymbols
36#include "alarm.h"39#include "alarm.h"
@@ -88,12 +91,13 @@
88 return iocBuild() || iocRun();91 return iocBuild() || iocRun();
89}92}
9093
91int iocBuild(void)94static int iocBuild_1(void)
92{95{
93 if (iocState != iocVirgin) {96 if (iocState != iocVirgin && iocState != iocStopped) {
94 errlogPrintf("iocBuild: IOC can only be initialized once\n");97 errlogPrintf("iocBuild: IOC can only be initialized from uninitialized or stopped state\n");
95 return -1;98 return -1;
96 }99 }
100 errlogInit(0);
97 initHookAnnounce(initHookAtIocBuild);101 initHookAnnounce(initHookAtIocBuild);
98102
99 if (!epicsThreadIsOkToBlock()) {103 if (!epicsThreadIsOkToBlock()) {
@@ -109,14 +113,17 @@
109 initHookAnnounce(initHookAtBeginning);113 initHookAnnounce(initHookAtBeginning);
110114
111 coreRelease();115 coreRelease();
112 /* After this point, further calls to iocInit() are disallowed. */
113 iocState = iocBuilding;116 iocState = iocBuilding;
114117
115 taskwdInit();118 taskwdInit();
116 callbackInit();119 callbackInit();
117 initHookAnnounce(initHookAfterCallbackInit);120 initHookAnnounce(initHookAfterCallbackInit);
118121
119 dbCaLinkInit();122 return 0;
123}
124
125static int iocBuild_2(void)
126{
120 initHookAnnounce(initHookAfterCaLinkInit);127 initHookAnnounce(initHookAfterCaLinkInit);
121128
122 initDrvSup();129 initDrvSup();
@@ -147,9 +154,11 @@
147154
148 initialProcess();155 initialProcess();
149 initHookAnnounce(initHookAfterInitialProcess);156 initHookAnnounce(initHookAfterInitialProcess);
157 return 0;
158}
150159
151 /* Start CA server threads */160static int iocBuild_3(void)
152 rsrv_init();161{
153 initHookAnnounce(initHookAfterCaServerInit);162 initHookAnnounce(initHookAfterCaServerInit);
154163
155 iocState = iocBuilt;164 iocState = iocBuilt;
@@ -157,6 +166,39 @@
157 return 0;166 return 0;
158}167}
159168
169int iocBuild(void)
170{
171 int status;
172
173 status = iocBuild_1();
174 if (status) return status;
175
176 dbCaLinkInit();
177
178 status = iocBuild_2();
179 if (status) return status;
180
181 /* Start CA server threads */
182 rsrv_init();
183
184 status = iocBuild_3();
185 return status;
186}
187
188int iocBuildNoCA(void)
189{
190 int status;
191
192 status = iocBuild_1();
193 if (status) return status;
194
195 status = iocBuild_2();
196 if (status) return status;
197
198 status = iocBuild_3();
199 return status;
200}
201
160int iocRun(void)202int iocRun(void)
161{203{
162 if (iocState != iocPaused && iocState != iocBuilt) {204 if (iocState != iocPaused && iocState != iocBuilt) {
@@ -599,8 +641,21 @@
599 }641 }
600}642}
601643
602static void exitDatabase(void *dummy)644int iocShutdown(void)
603{645{
646 if (iocState == iocVirgin || iocState == iocStopped) return 0;
604 iterateRecords(doCloseLinks, NULL);647 iterateRecords(doCloseLinks, NULL);
648 scanShutdown();
649 callbackShutdown();
650 asShutdown();
651 iocshFree();
652 taskwdShutdown();
653 errlogShutdown();
605 iocState = iocStopped;654 iocState = iocStopped;
655 return 0;
656}
657
658static void exitDatabase(void *dummy)
659{
660 iocShutdown();
606}661}
607662
=== modified file 'src/ioc/misc/iocInit.h'
--- src/ioc/misc/iocInit.h 2009-06-10 20:19:32 +0000
+++ src/ioc/misc/iocInit.h 2013-12-14 18:55:29 +0000
@@ -19,8 +19,10 @@
1919
20epicsShareFunc int iocInit(void);20epicsShareFunc int iocInit(void);
21epicsShareFunc int iocBuild(void);21epicsShareFunc int iocBuild(void);
22epicsShareFunc int iocBuildNoCA(void);
22epicsShareFunc int iocRun(void);23epicsShareFunc int iocRun(void);
23epicsShareFunc int iocPause(void);24epicsShareFunc int iocPause(void);
25epicsShareFunc int iocShutdown(void);
2426
25#ifdef __cplusplus27#ifdef __cplusplus
26}28}
2729
=== modified file 'src/libCom/as/asLib.h'
--- src/libCom/as/asLib.h 2010-12-17 16:50:52 +0000
+++ src/libCom/as/asLib.h 2013-12-14 18:55:29 +0000
@@ -222,6 +222,7 @@
222/*following is "friend" function*/222/*following is "friend" function*/
223epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size);223epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size);
224epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str);224epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str);
225epicsShareFunc void asFreeAll(ASBASE *pasbase);
225#ifdef __cplusplus226#ifdef __cplusplus
226}227}
227#endif228#endif
228229
=== modified file 'src/libCom/as/asLibRoutines.c'
--- src/libCom/as/asLibRoutines.c 2012-07-07 18:50:55 +0000
+++ src/libCom/as/asLibRoutines.c 2013-12-14 18:55:29 +0000
@@ -51,7 +51,6 @@
51static long asComputeAllAsgPvt(void);51static long asComputeAllAsgPvt(void);
52static long asComputeAsgPvt(ASG *pasg);52static long asComputeAsgPvt(ASG *pasg);
53static long asComputePvt(ASCLIENTPVT asClientPvt);53static long asComputePvt(ASCLIENTPVT asClientPvt);
54static void asFreeAll(ASBASE *pasbase);
55static UAG *asUagAdd(const char *uagName);54static UAG *asUagAdd(const char *uagName);
56static long asUagAddUser(UAG *puag,const char *user);55static long asUagAddUser(UAG *puag,const char *user);
57static HAG *asHagAdd(const char *hagName);56static HAG *asHagAdd(const char *hagName);
@@ -1015,7 +1014,7 @@
1015 return(0);1014 return(0);
1016}1015}
10171016
10181017
1019static void asFreeAll(ASBASE *pasbase)1018void asFreeAll(ASBASE *pasbase)
1020{1019{
1021 UAG *puag;1020 UAG *puag;
1022 UAGNAME *puagname;1021 UAGNAME *puagname;
10231022
=== modified file 'src/libCom/error/errlog.c'
--- src/libCom/error/errlog.c 2013-06-28 17:35:43 +0000
+++ src/libCom/error/errlog.c 2013-12-14 18:55:29 +0000
@@ -41,9 +41,13 @@
41/*Declare storage for errVerbose */41/*Declare storage for errVerbose */
42epicsShareDef int errVerbose = 0;42epicsShareDef int errVerbose = 0;
4343
44static void exitHandler(void *);44static epicsThreadOnceId errlogOnceFlag = EPICS_THREAD_ONCE_INIT;
45
46static void errlogExitHandler(void *);
45static void errlogThread(void);47static void errlogThread(void);
4648
49static void errlogInit1(int bufsize);
50
47static char *msgbufGetFree(int noConsoleMessage);51static char *msgbufGetFree(int noConsoleMessage);
48static void msgbufSetSize(int size); /* Send 'size' chars plus trailing '\0' */52static void msgbufSetSize(int size); /* Send 'size' chars plus trailing '\0' */
49static char *msgbufGetSend(int *noConsoleMessage);53static char *msgbufGetSend(int *noConsoleMessage);
@@ -70,8 +74,8 @@
70 epicsEventId waitForFlush; /*errlogFlush waits for this*/74 epicsEventId waitForFlush; /*errlogFlush waits for this*/
71 epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/75 epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/
72 epicsMutexId flushLock;76 epicsMutexId flushLock;
73 epicsEventId waitForExit; /*exitHandler waits for this*/77 epicsEventId waitForExit; /*errlogExitHandler waits for this*/
74 int atExit; /*TRUE when exitHandler is active*/78 int atExit; /*TRUE when errlogExitHandler is active*/
75 ELLLIST listenerList;79 ELLLIST listenerList;
76 ELLLIST msgQueue;80 ELLLIST msgQueue;
77 msgNode *pnextSend;81 msgNode *pnextSend;
@@ -116,7 +120,7 @@
116 return 0;120 return 0;
117 }121 }
118122
119 errlogInit(0);123 errlogInit1(0);
120 isOkToBlock = epicsThreadIsOkToBlock();124 isOkToBlock = epicsThreadIsOkToBlock();
121125
122 if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {126 if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
@@ -153,7 +157,7 @@
153 return 0;157 return 0;
154 }158 }
155159
156 errlogInit(0);160 errlogInit1(0);
157 if (pvtData.atExit)161 if (pvtData.atExit)
158 return 0;162 return 0;
159 isOkToBlock = epicsThreadIsOkToBlock();163 isOkToBlock = epicsThreadIsOkToBlock();
@@ -191,7 +195,7 @@
191 return 0;195 return 0;
192 }196 }
193197
194 errlogInit(0);198 errlogInit1(0);
195 va_start(pvar, pFormat);199 va_start(pvar, pFormat);
196 nchar = errlogVprintfNoConsole(pFormat, pvar);200 nchar = errlogVprintfNoConsole(pFormat, pvar);
197 va_end(pvar);201 va_end(pvar);
@@ -210,7 +214,7 @@
210 return 0;214 return 0;
211 }215 }
212216
213 errlogInit(0);217 errlogInit1(0);
214 if (pvtData.atExit)218 if (pvtData.atExit)
215 return 0;219 return 0;
216220
@@ -237,7 +241,7 @@
237 return 0;241 return 0;
238 }242 }
239243
240 errlogInit(0);244 errlogInit1(0);
241 if (pvtData.sevToLog > severity)245 if (pvtData.sevToLog > severity)
242 return 0;246 return 0;
243247
@@ -270,7 +274,7 @@
270 return 0;274 return 0;
271 }275 }
272276
273 errlogInit(0);277 errlogInit1(0);
274 if (pvtData.atExit)278 if (pvtData.atExit)
275 return 0;279 return 0;
276280
@@ -295,7 +299,7 @@
295epicsShareFunc char * epicsShareAPI errlogGetSevEnumString(299epicsShareFunc char * epicsShareAPI errlogGetSevEnumString(
296 const errlogSevEnum severity)300 const errlogSevEnum severity)
297{301{
298 errlogInit(0);302 errlogInit1(0);
299 if (severity > 3)303 if (severity > 3)
300 return "unknown";304 return "unknown";
301 return errlogSevEnumString[severity];305 return errlogSevEnumString[severity];
@@ -304,13 +308,13 @@
304epicsShareFunc void epicsShareAPI errlogSetSevToLog(308epicsShareFunc void epicsShareAPI errlogSetSevToLog(
305 const errlogSevEnum severity)309 const errlogSevEnum severity)
306{310{
307 errlogInit(0);311 errlogInit1(0);
308 pvtData.sevToLog = severity;312 pvtData.sevToLog = severity;
309}313}
310314
311epicsShareFunc errlogSevEnum epicsShareAPI errlogGetSevToLog(void)315epicsShareFunc errlogSevEnum epicsShareAPI errlogGetSevToLog(void)
312{316{
313 errlogInit(0);317 errlogInit1(0);
314 return pvtData.sevToLog;318 return pvtData.sevToLog;
315}319}
316320
@@ -319,7 +323,7 @@
319{323{
320 listenerNode *plistenerNode;324 listenerNode *plistenerNode;
321325
322 errlogInit(0);326 errlogInit1(0);
323 if (pvtData.atExit)327 if (pvtData.atExit)
324 return;328 return;
325329
@@ -338,7 +342,7 @@
338 listenerNode *plistenerNode;342 listenerNode *plistenerNode;
339 int count = 0;343 int count = 0;
340344
341 errlogInit(0);345 errlogInit1(0);
342 if (!pvtData.atExit)346 if (!pvtData.atExit)
343 epicsMutexMustLock(pvtData.listenerLock);347 epicsMutexMustLock(pvtData.listenerLock);
344348
@@ -367,14 +371,14 @@
367371
368epicsShareFunc int epicsShareAPI eltc(int yesno)372epicsShareFunc int epicsShareAPI eltc(int yesno)
369{373{
370 errlogInit(0);374 errlogInit1(0);
371 pvtData.toConsole = yesno;375 pvtData.toConsole = yesno;
372 return 0;376 return 0;
373}377}
374378
375epicsShareFunc int errlogSetConsole(FILE *stream)379epicsShareFunc int errlogSetConsole(FILE *stream)
376{380{
377 errlogInit(0);381 errlogInit1(0);
378 pvtData.console = stream ? stream : stderr;382 pvtData.console = stream ? stream : stderr;
379 return 0;383 return 0;
380}384}
@@ -394,7 +398,7 @@
394 return;398 return;
395 }399 }
396400
397 errlogInit(0);401 errlogInit1(0);
398 isOkToBlock = epicsThreadIsOkToBlock();402 isOkToBlock = epicsThreadIsOkToBlock();
399 if (status == 0)403 if (status == 0)
400 status = errno;404 status = errno;
@@ -447,8 +451,9 @@
447}451}
448452
449453
450static void exitHandler(void *pvt)454void errlogShutdown(void)
451{455{
456 if (pvtData.atExit) return;
452 pvtData.atExit = 1;457 pvtData.atExit = 1;
453 epicsEventSignal(pvtData.waitForWork);458 epicsEventSignal(pvtData.waitForWork);
454 epicsEventMustWait(pvtData.waitForExit);459 epicsEventMustWait(pvtData.waitForExit);
@@ -461,6 +466,13 @@
461 epicsMutexDestroy(pvtData.msgQueueLock);466 epicsMutexDestroy(pvtData.msgQueueLock);
462 epicsEventDestroy(pvtData.waitForWork);467 epicsEventDestroy(pvtData.waitForWork);
463 epicsEventDestroy(pvtData.waitForExit);468 epicsEventDestroy(pvtData.waitForExit);
469
470 epicsThreadOnceReset(&errlogOnceFlag);
471}
472
473static void errlogExitHandler(void *pvt)
474{
475 errlogShutdown();
464}476}
465477
466struct initArgs {478struct initArgs {
@@ -505,7 +517,6 @@
505517
506epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize)518epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize)
507{519{
508 static epicsThreadOnceId errlogOnceFlag = EPICS_THREAD_ONCE_INIT;
509 struct initArgs config;520 struct initArgs config;
510521
511 if (pvtData.atExit)522 if (pvtData.atExit)
@@ -529,9 +540,15 @@
529540
530epicsShareFunc int epicsShareAPI errlogInit(int bufsize)541epicsShareFunc int epicsShareAPI errlogInit(int bufsize)
531{542{
543 pvtData.atExit = 0;
532 return errlogInit2(bufsize, MAX_MESSAGE_SIZE);544 return errlogInit2(bufsize, MAX_MESSAGE_SIZE);
533}545}
534546
547static void errlogInit1(int bufsize)
548{
549 errlogInit2(bufsize, MAX_MESSAGE_SIZE);
550}
551
535epicsShareFunc void epicsShareAPI errlogFlush(void)552epicsShareFunc void epicsShareAPI errlogFlush(void)
536{553{
537 int count;554 int count;
@@ -561,7 +578,7 @@
561 int noConsoleMessage;578 int noConsoleMessage;
562 char *pmessage;579 char *pmessage;
563580
564 epicsAtExit(exitHandler,0);581 epicsAtExit(errlogExitHandler,0);
565 while (TRUE) {582 while (TRUE) {
566 epicsEventMustWait(pvtData.waitForWork);583 epicsEventMustWait(pvtData.waitForWork);
567 while ((pmessage = msgbufGetSend(&noConsoleMessage))) {584 while ((pmessage = msgbufGetSend(&noConsoleMessage))) {
568585
=== modified file 'src/libCom/error/errlog.h'
--- src/libCom/error/errlog.h 2013-06-28 17:35:43 +0000
+++ src/libCom/error/errlog.h 2013-12-14 18:55:29 +0000
@@ -64,6 +64,7 @@
64epicsShareFunc int errlogSetConsole(FILE *stream);64epicsShareFunc int errlogSetConsole(FILE *stream);
6565
66epicsShareFunc int epicsShareAPI errlogInit(int bufsize);66epicsShareFunc int epicsShareAPI errlogInit(int bufsize);
67epicsShareFunc void epicsShareAPI errlogShutdown(void);
67epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize);68epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize);
68epicsShareFunc void epicsShareAPI errlogFlush(void);69epicsShareFunc void epicsShareAPI errlogFlush(void);
6970
7071
=== modified file 'src/libCom/iocsh/iocsh.cpp'
--- src/libCom/iocsh/iocsh.cpp 2012-01-11 21:12:15 +0000
+++ src/libCom/iocsh/iocsh.cpp 2013-12-14 18:55:29 +0000
@@ -198,20 +198,22 @@
198 */198 */
199void epicsShareAPI iocshFree(void) 199void epicsShareAPI iocshFree(void)
200{200{
201 struct iocshCommand *pc, *nc;201 struct iocshCommand *pc;
202 struct iocshVariable *pv, *nv;202 struct iocshVariable *pv;
203203
204 iocshTableLock ();204 iocshTableLock ();
205 for (pc = iocshCommandHead ; pc != NULL ; ) {205 for (pc = iocshCommandHead ; pc != NULL ; ) {
206 nc = pc->next;206 struct iocshCommand * nc = pc->next;
207 free (pc);207 free (pc);
208 pc = nc;208 pc = nc;
209 }209 }
210 for (pv = iocshVariableHead ; pv != NULL ; ) {210 for (pv = iocshVariableHead ; pv != NULL ; ) {
211 nv = pv->next;211 struct iocshVariable *nv = pv->next;
212 free (pv);212 free (pv);
213 pv = nv;213 pv = nv;
214 }214 }
215 iocshCommandHead = NULL;
216 iocshVariableHead = NULL;
215 iocshTableUnlock ();217 iocshTableUnlock ();
216}218}
217219
218220
=== modified file 'src/libCom/misc/epicsExit.c'
--- src/libCom/misc/epicsExit.c 2012-07-17 19:33:31 +0000
+++ src/libCom/misc/epicsExit.c 2013-12-14 18:55:29 +0000
@@ -23,7 +23,9 @@
23 */23 */
2424
25#include <stdlib.h>25#include <stdlib.h>
26#include <stdio.h>
26#include <errno.h>27#include <errno.h>
28#include <string.h>
2729
28#define epicsExportSharedSymbols30#define epicsExportSharedSymbols
29#include "ellLib.h"31#include "ellLib.h"
@@ -36,12 +38,15 @@
36 ELLNODE node;38 ELLNODE node;
37 epicsExitFunc func;39 epicsExitFunc func;
38 void *arg;40 void *arg;
41 char name[1];
39}exitNode;42}exitNode;
4043
41typedef struct exitPvt {44typedef struct exitPvt {
42 ELLLIST list;45 ELLLIST list;
43} exitPvt;46} exitPvt;
4447
48int atExitDebug = 0;
49
45static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT;50static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT;
46static exitPvt * pExitPvtPerProcess = 0;51static exitPvt * pExitPvtPerProcess = 0;
47static epicsMutexId exitPvtLock = 0;52static epicsMutexId exitPvtLock = 0;
@@ -66,15 +71,23 @@
66{71{
67 exitPvtPerThread = epicsThreadPrivateCreate ();72 exitPvtPerThread = epicsThreadPrivateCreate ();
68 assert ( exitPvtPerThread );73 assert ( exitPvtPerThread );
69 pExitPvtPerProcess = createExitPvt ();
70 assert ( pExitPvtPerProcess );
71 exitPvtLock = epicsMutexMustCreate ();74 exitPvtLock = epicsMutexMustCreate ();
72}75}
7376
77static void epicsExitInit(void)
78{
79 epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );
80}
81
74static void epicsExitCallAtExitsPvt(exitPvt *pep)82static void epicsExitCallAtExitsPvt(exitPvt *pep)
75{83{
76 exitNode *pexitNode;84 exitNode *pexitNode;
85
77 while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) {86 while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) {
87 if (atExitDebug && pexitNode->name[0])
88 fprintf(stderr, "atExit %s(%p)\n", pexitNode->name, pexitNode->arg);
89 else if(atExitDebug)
90 fprintf(stderr, "atExit %p(%p)\n", pexitNode->func, pexitNode->arg);
78 pexitNode->func ( pexitNode->arg );91 pexitNode->func ( pexitNode->arg );
79 ellDelete ( & pep->list, & pexitNode->node );92 ellDelete ( & pep->list, & pexitNode->node );
80 free ( pexitNode );93 free ( pexitNode );
@@ -84,7 +97,8 @@
84epicsShareFunc void epicsExitCallAtExits(void)97epicsShareFunc void epicsExitCallAtExits(void)
85{98{
86 exitPvt * pep = 0;99 exitPvt * pep = 0;
87 epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );100
101 epicsExitInit ();
88 epicsMutexMustLock ( exitPvtLock );102 epicsMutexMustLock ( exitPvtLock );
89 if ( pExitPvtPerProcess ) {103 if ( pExitPvtPerProcess ) {
90 pep = pExitPvtPerProcess;104 pep = pExitPvtPerProcess;
@@ -100,7 +114,8 @@
100epicsShareFunc void epicsExitCallAtThreadExits(void)114epicsShareFunc void epicsExitCallAtThreadExits(void)
101{115{
102 exitPvt * pep;116 exitPvt * pep;
103 epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );117
118 epicsExitInit ();
104 pep = epicsThreadPrivateGet ( exitPvtPerThread );119 pep = epicsThreadPrivateGet ( exitPvtPerThread );
105 if ( pep ) {120 if ( pep ) {
106 epicsExitCallAtExitsPvt ( pep );121 epicsExitCallAtExitsPvt ( pep );
@@ -109,14 +124,16 @@
109 }124 }
110}125}
111126
112static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg)127static int epicsAtExitPvt(exitPvt *pep, epicsExitFunc func, void *arg, const char *name)
113{128{
114 int status = -1;129 int status = -1;
115 exitNode * pExitNode130 exitNode * pExitNode = calloc ( 1, sizeof( *pExitNode ) + (name?strlen(name):0) );
116 = calloc ( 1, sizeof( *pExitNode ) );131
117 if ( pExitNode ) {132 if ( pExitNode ) {
118 pExitNode->func = func;133 pExitNode->func = func;
119 pExitNode->arg = arg;134 pExitNode->arg = arg;
135 if(name)
136 strcpy(pExitNode->name, name);
120 ellAdd ( & pep->list, & pExitNode->node );137 ellAdd ( & pep->list, & pExitNode->node );
121 status = 0;138 status = 0;
122 }139 }
@@ -126,7 +143,8 @@
126epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg)143epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg)
127{144{
128 exitPvt * pep;145 exitPvt * pep;
129 epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );146
147 epicsExitInit ();
130 pep = epicsThreadPrivateGet ( exitPvtPerThread );148 pep = epicsThreadPrivateGet ( exitPvtPerThread );
131 if ( ! pep ) {149 if ( ! pep ) {
132 pep = createExitPvt ();150 pep = createExitPvt ();
@@ -135,16 +153,20 @@
135 }153 }
136 epicsThreadPrivateSet ( exitPvtPerThread, pep );154 epicsThreadPrivateSet ( exitPvtPerThread, pep );
137 }155 }
138 return epicsAtExitPvt ( pep, func, arg );156 return epicsAtExitPvt ( pep, func, arg, NULL );
139}157}
140158
141epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg)159epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name)
142{160{
143 int status = -1;161 int status = -1;
144 epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 );162
163 epicsExitInit ();
145 epicsMutexMustLock ( exitPvtLock );164 epicsMutexMustLock ( exitPvtLock );
165 if ( !pExitPvtPerProcess ) {
166 pExitPvtPerProcess = createExitPvt ();
167 }
146 if ( pExitPvtPerProcess ) {168 if ( pExitPvtPerProcess ) {
147 status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg );169 status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg, name );
148 }170 }
149 epicsMutexUnlock ( exitPvtLock );171 epicsMutexUnlock ( exitPvtLock );
150 return status;172 return status;
@@ -153,6 +175,10 @@
153epicsShareFunc void epicsExit(int status)175epicsShareFunc void epicsExit(int status)
154{176{
155 epicsExitCallAtExits();177 epicsExitCallAtExits();
156 epicsThreadSleep(1.0);178 epicsThreadSleep(0.1);
157 exit(status);179 exit(status);
158}180}
181
182#include "epicsExport.h"
183
184epicsExportAddress(int,atExitDebug);
159185
=== modified file 'src/libCom/misc/epicsExit.h'
--- src/libCom/misc/epicsExit.h 2012-07-17 19:33:31 +0000
+++ src/libCom/misc/epicsExit.h 2013-12-14 18:55:29 +0000
@@ -19,7 +19,8 @@
1919
20epicsShareFunc void epicsExit(int status);20epicsShareFunc void epicsExit(int status);
21epicsShareFunc void epicsExitCallAtExits(void);21epicsShareFunc void epicsExitCallAtExits(void);
22epicsShareFunc int epicsAtExit(epicsExitFunc func, void *arg);22epicsShareFunc int epicsAtExit3(epicsExitFunc func, void *arg, const char* name);
23#define epicsAtExit(F,A) epicsAtExit3(F,A,#F)
2324
24epicsShareFunc void epicsExitCallAtThreadExits(void);25epicsShareFunc void epicsExitCallAtThreadExits(void);
25epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg);26epicsShareFunc int epicsAtThreadExit(epicsExitFunc func, void *arg);
2627
=== modified file 'src/libCom/osi/epicsThread.h'
--- src/libCom/osi/epicsThread.h 2012-07-06 21:33:10 +0000
+++ src/libCom/osi/epicsThread.h 2013-12-14 18:55:29 +0000
@@ -57,6 +57,9 @@
57epicsShareFunc void epicsShareAPI epicsThreadOnce(57epicsShareFunc void epicsShareAPI epicsThreadOnce(
58 epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg);58 epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg);
5959
60epicsShareFunc void epicsShareAPI epicsThreadOnceReset(
61 epicsThreadOnceId *id);
62
60epicsShareFunc void epicsShareAPI epicsThreadExitMain(void);63epicsShareFunc void epicsShareAPI epicsThreadExitMain(void);
6164
62epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (65epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (
6366
=== modified file 'src/libCom/osi/os/RTEMS/osdThread.c'
--- src/libCom/osi/os/RTEMS/osdThread.c 2012-07-31 19:04:38 +0000
+++ src/libCom/osi/os/RTEMS/osdThread.c 2013-12-14 18:55:29 +0000
@@ -505,6 +505,15 @@
505 epicsMutexUnlock(onceMutex);505 epicsMutexUnlock(onceMutex);
506}506}
507507
508epicsShareFunc void epicsShareAPI epicsThreadOnceReset(
509 epicsThreadOnceId *id)
510{
511 if (!initialized) epicsThreadInit();
512 epicsMutexMustLock(onceMutex);
513 *id = EPICS_THREAD_ONCE_INIT;
514 epicsMutexUnlock(onceMutex);
515}
516
508/*517/*
509 * Thread private storage implementation based on the vxWorks518 * Thread private storage implementation based on the vxWorks
510 * implementation by Andrew Johnson APS/ASD.519 * implementation by Andrew Johnson APS/ASD.
511520
=== modified file 'src/libCom/osi/os/WIN32/osdThread.c'
--- src/libCom/osi/os/WIN32/osdThread.c 2013-06-07 23:08:38 +0000
+++ src/libCom/osi/os/WIN32/osdThread.c 2013-12-14 18:55:29 +0000
@@ -1058,6 +1058,17 @@
1058 LeaveCriticalSection ( & pGbl->mutex );1058 LeaveCriticalSection ( & pGbl->mutex );
1059}1059}
10601060
1061
1062epicsShareFunc void epicsShareAPI epicsThreadOnceReset(
1063 epicsThreadOnceId *id)
1064{
1065 win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal ();
1066 assert ( pGbl );
1067 EnterCriticalSection ( & pGbl->mutex );
1068 *id = EPICS_THREAD_ONCE_INIT;
1069 LeaveCriticalSection ( & pGbl->mutex );
1070}
1071
1061/*1072/*
1062 * epicsThreadPrivateCreate ()1073 * epicsThreadPrivateCreate ()
1063 */1074 */
10641075
=== modified file 'src/libCom/osi/os/posix/osdThread.c'
--- src/libCom/osi/os/posix/osdThread.c 2012-09-20 19:55:32 +0000
+++ src/libCom/osi/os/posix/osdThread.c 2013-12-14 18:55:29 +0000
@@ -465,6 +465,25 @@
465 checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnce");465 checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnce");
466}466}
467467
468epicsShareFunc void epicsShareAPI epicsThreadOnceReset(
469 epicsThreadOnceId *id)
470{
471 int status;
472
473 epicsThreadInit();
474 status = mutexLock(&onceLock);
475 if(status) {
476 fprintf(stderr,"epicsThreadOnceReset: pthread_mutex_lock returned %s.\n",
477 strerror(status));
478 exit(-1);
479 }
480
481 *id = EPICS_THREAD_ONCE_INIT;
482
483 status = pthread_mutex_unlock(&onceLock);
484 checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnceReset");
485}
486
468epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate(const char *name,487epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate(const char *name,
469 unsigned int priority, unsigned int stackSize,488 unsigned int priority, unsigned int stackSize,
470 EPICSTHREADFUNC funptr,void *parm)489 EPICSTHREADFUNC funptr,void *parm)
471490
=== modified file 'src/libCom/osi/os/vxWorks/osdThread.c'
--- src/libCom/osi/os/vxWorks/osdThread.c 2013-06-07 23:08:38 +0000
+++ src/libCom/osi/os/vxWorks/osdThread.c 2013-12-14 18:55:29 +0000
@@ -167,7 +167,19 @@
167 }167 }
168 semGive(epicsThreadOnceMutex);168 semGive(epicsThreadOnceMutex);
169}169}
170
171170
171
172epicsShareFunc void epicsShareAPI epicsThreadOnceReset(
173 epicsThreadOnceId *id)
174{
175 int result;
176
177 epicsThreadInit();
178 result = semTake(epicsThreadOnceMutex, WAIT_FOREVER);
179 assert(result == OK);
180 *id = EPICS_THREAD_ONCE_INIT;
181 semGive(epicsThreadOnceMutex);
182}
183
172static void createFunction(EPICSTHREADFUNC func, void *parm)184static void createFunction(EPICSTHREADFUNC func, void *parm)
173{185{
174 int tid = taskIdSelf();186 int tid = taskIdSelf();
175187
=== modified file 'src/libCom/taskwd/taskwd.c'
--- src/libCom/taskwd/taskwd.c 2012-05-04 18:38:59 +0000
+++ src/libCom/taskwd/taskwd.c 2013-12-14 18:55:29 +0000
@@ -70,10 +70,11 @@
7070
71/* Watchdog task control */71/* Watchdog task control */
72static volatile enum {72static volatile enum {
73 twdctlInit, twdctlRun, twdctlDisable, twdctlExit73 ctlInit, ctlRun, ctlDisable, ctlExit
74} twdCtl;74} twdCtl;
75static epicsEventId loopEvent;75static epicsEventId loopEvent;
76static epicsEventId exitEvent;76static epicsEventId exitEvent;
77static epicsThreadOnceId twdOnceFlag = EPICS_THREAD_ONCE_INIT;
7778
78/* Task delay times (seconds) */79/* Task delay times (seconds) */
79#define TASKWD_DELAY 6.080#define TASKWD_DELAY 6.0
@@ -90,8 +91,8 @@
90 struct tNode *pt;91 struct tNode *pt;
91 struct mNode *pm;92 struct mNode *pm;
9293
93 while (twdCtl != twdctlExit) {94 while (twdCtl != ctlExit) {
94 if (twdCtl == twdctlRun) {95 if (twdCtl == ctlRun) {
95 epicsMutexMustLock(tLock);96 epicsMutexMustLock(tLock);
96 pt = (struct tNode *)ellFirst(&tList);97 pt = (struct tNode *)ellFirst(&tList);
97 while (pt) {98 while (pt) {
@@ -127,12 +128,26 @@
127 epicsEventSignal(exitEvent);128 epicsEventSignal(exitEvent);
128}129}
129130
130131void taskwdShutdown(void)
131static void twdShutdown(void *arg)
132{132{
133 twdCtl = twdctlExit;133 if (twdCtl == ctlExit) return;
134 twdCtl = ctlExit;
135
134 epicsEventSignal(loopEvent);136 epicsEventSignal(loopEvent);
135 epicsEventWait(exitEvent);137 epicsEventWait(exitEvent);
138
139 epicsEventDestroy(exitEvent);
140 epicsEventDestroy(loopEvent);
141 epicsMutexDestroy(fLock);
142 epicsMutexDestroy(mLock);
143 epicsMutexDestroy(tLock);
144
145 epicsThreadOnceReset(&twdOnceFlag);
146}
147
148static void twdExit(void *arg)
149{
150 taskwdShutdown();
136}151}
137152
138static void twdInitOnce(void *arg)153static void twdInitOnce(void *arg)
@@ -143,7 +158,7 @@
143 mLock = epicsMutexMustCreate();158 mLock = epicsMutexMustCreate();
144 fLock = epicsMutexMustCreate();159 fLock = epicsMutexMustCreate();
145160
146 twdCtl = twdctlRun;161 twdCtl = ctlRun;
147 loopEvent = epicsEventMustCreate(epicsEventEmpty);162 loopEvent = epicsEventMustCreate(epicsEventEmpty);
148 exitEvent = epicsEventMustCreate(epicsEventEmpty);163 exitEvent = epicsEventMustCreate(epicsEventEmpty);
149164
@@ -153,12 +168,11 @@
153 if (tid == 0)168 if (tid == 0)
154 cantProceed("Failed to spawn task watchdog thread\n");169 cantProceed("Failed to spawn task watchdog thread\n");
155170
156 epicsAtExit(twdShutdown, NULL);171 epicsAtExit(twdExit, NULL);
157}172}
158173
159void taskwdInit(void)174void taskwdInit(void)
160{175{
161 static epicsThreadOnceId twdOnceFlag = EPICS_THREAD_ONCE_INIT;
162 epicsThreadOnce(&twdOnceFlag, twdInitOnce, NULL);176 epicsThreadOnce(&twdOnceFlag, twdInitOnce, NULL);
163}177}
164178
165179
=== modified file 'src/libCom/taskwd/taskwd.h'
--- src/libCom/taskwd/taskwd.h 2010-10-05 19:27:37 +0000
+++ src/libCom/taskwd/taskwd.h 2013-12-14 18:55:29 +0000
@@ -28,6 +28,7 @@
2828
29/* Initialization, optional */29/* Initialization, optional */
30epicsShareFunc void taskwdInit(void);30epicsShareFunc void taskwdInit(void);
31epicsShareFunc void taskwdShutdown(void);
3132
3233
33/* For tasks to be monitored */34/* For tasks to be monitored */
3435
=== modified file 'src/libCom/test/epicsExitTest.c'
--- src/libCom/test/epicsExitTest.c 2009-04-08 22:39:27 +0000
+++ src/libCom/test/epicsExitTest.c 2013-12-14 18:55:29 +0000
@@ -59,12 +59,20 @@
59 testDiag("%s starting", pinfo->name);59 testDiag("%s starting", pinfo->name);
60 pinfo->terminate = epicsEventMustCreate(epicsEventEmpty);60 pinfo->terminate = epicsEventMustCreate(epicsEventEmpty);
61 pinfo->terminated = epicsEventMustCreate(epicsEventEmpty);61 pinfo->terminated = epicsEventMustCreate(epicsEventEmpty);
62 epicsAtExit(atExit, pinfo);62 testOk(!epicsAtExit(atExit, pinfo), "Registered atExit(%p)", pinfo);
63 epicsAtThreadExit(atThreadExit, pinfo);63 testOk(!epicsAtThreadExit(atThreadExit, pinfo),
64 "Registered atThreadExit(%p)", pinfo);
64 testDiag("%s waiting for atExit", pinfo->name);65 testDiag("%s waiting for atExit", pinfo->name);
65 epicsEventMustWait(pinfo->terminate);66 epicsEventMustWait(pinfo->terminate);
66}67}
6768
69int count;
70
71static void counter(void *pvt)
72{
73 count++;
74}
75
68static void mainExit(void *pvt)76static void mainExit(void *pvt)
69{77{
70 testPass("Reached mainExit");78 testPass("Reached mainExit");
@@ -77,16 +85,23 @@
77 info *pinfoA = (info *)calloc(1, sizeof(info));85 info *pinfoA = (info *)calloc(1, sizeof(info));
78 info *pinfoB = (info *)calloc(1, sizeof(info));86 info *pinfoB = (info *)calloc(1, sizeof(info));
7987
80 testPlan(7);88 testPlan(15);
8189
82 epicsAtExit(mainExit, NULL);90 testOk(!epicsAtExit(counter, NULL), "Registered counter()");
91 count = 0;
92 epicsExitCallAtExits();
93 testOk(count == 1, "counter() called once");
94 epicsExitCallAtExits();
95 testOk(count == 1, "unregistered counter() not called");
96
97 testOk(!epicsAtExit(mainExit, NULL), "Registered mainExit()");
8398
84 epicsThreadCreate("threadA", 50, stackSize, thread, pinfoA);99 epicsThreadCreate("threadA", 50, stackSize, thread, pinfoA);
85 epicsThreadSleep(0.1);100 epicsThreadSleep(0.1);
86 epicsThreadCreate("threadB", 50, stackSize, thread, pinfoB);101 epicsThreadCreate("threadB", 50, stackSize, thread, pinfoB);
87 epicsThreadSleep(1.0);102 epicsThreadSleep(1.0);
88103
89 testDiag("Calling epicsExit\n");104 testDiag("Calling epicsExit");
90 epicsExit(0);105 epicsExit(0);
91 return 0;106 return 0;
92}107}
93108
=== modified file 'src/libCom/test/epicsThreadOnceTest.c'
--- src/libCom/test/epicsThreadOnceTest.c 2012-07-06 22:47:56 +0000
+++ src/libCom/test/epicsThreadOnceTest.c 2013-12-14 18:55:29 +0000
@@ -21,6 +21,7 @@
2121
22epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT;22epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT;
23epicsThreadOnceId twiceFlag = EPICS_THREAD_ONCE_INIT;23epicsThreadOnceId twiceFlag = EPICS_THREAD_ONCE_INIT;
24epicsThreadOnceId resetFlag = EPICS_THREAD_ONCE_INIT;
24epicsMutexId lock;25epicsMutexId lock;
25epicsEventId go;26epicsEventId go;
26epicsEventId done;27epicsEventId done;
@@ -80,7 +81,7 @@
80 int i;81 int i;
81 epicsThreadId tid;82 epicsThreadId tid;
8283
83 testPlan(3 + NUM_ONCE_THREADS);84 testPlan(7 + NUM_ONCE_THREADS);
8485
85 go = epicsEventMustCreate(epicsEventEmpty);86 go = epicsEventMustCreate(epicsEventEmpty);
86 done = epicsEventMustCreate(epicsEventEmpty);87 done = epicsEventMustCreate(epicsEventEmpty);
@@ -112,6 +113,17 @@
112 } while (!epicsThreadIsSuspended(tid));113 } while (!epicsThreadIsSuspended(tid));
113 testPass("Recursive epicsThreadOnce() detected");114 testPass("Recursive epicsThreadOnce() detected");
114115
116 initCount = 0;
117 epicsThreadOnce(&resetFlag, &onceInit, NULL);
118 testOk1(initCount==1);
119 epicsThreadOnce(&resetFlag, &onceInit, NULL);
120 testOk1(initCount==1);
121 epicsThreadOnceReset(&resetFlag);
122 epicsThreadOnce(&resetFlag, &onceInit, NULL);
123 testOk1(initCount==2);
124 epicsThreadOnce(&resetFlag, &onceInit, NULL);
125 testOk1(initCount==2);
126
115 eltc(1);127 eltc(1);
116 return testDone();128 return testDone();
117}129}

Subscribers

People subscribed via source and target branches

to all changes: