Merge lp:~ralph-lange/epics-base/thread-hooks into lp:~epics-core/epics-base/3.15
- thread-hooks
- Merge into 3.15
Status: | Merged |
---|---|
Merged at revision: | 12303 |
Proposed branch: | lp:~ralph-lange/epics-base/thread-hooks |
Merge into: | lp:~epics-core/epics-base/3.15 |
Diff against target: |
967 lines (+553/-66) 15 files modified
documentation/RELEASE_NOTES.html (+11/-0) src/libCom/osi/Makefile (+2/-0) src/libCom/osi/epicsThread.h (+8/-2) src/libCom/osi/os/Linux/osdThread.h (+46/-0) src/libCom/osi/os/Linux/osdThreadExtra.c (+66/-0) src/libCom/osi/os/RTEMS/osdThread.c (+28/-9) src/libCom/osi/os/WIN32/osdThread.c (+34/-9) src/libCom/osi/os/default/osdThreadExtra.c (+19/-0) src/libCom/osi/os/default/osdThreadHooks.c (+74/-0) src/libCom/osi/os/posix/osdThread.c (+24/-40) src/libCom/osi/os/posix/osdThread.h (+19/-1) src/libCom/osi/os/posix/osdThreadExtra.c (+48/-0) src/libCom/osi/os/vxWorks/osdThread.c (+47/-5) src/libCom/test/Makefile (+5/-0) src/libCom/test/epicsThreadHooksTest.c (+122/-0) |
To merge this branch: | bzr merge lp:~ralph-lange/epics-base/thread-hooks |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Johnson | Approve | ||
Review via email: mp+112806@code.launchpad.net |
Commit message
Description of the change
Adds two interfaces for user hooks that are being called from epicsThread:
epicsThreadAddS
For Linux, the default start hook adds the thread's Linux LWP ID to the thread info block and sets the Linux name to match the epics thread name, and epicsShowThread() is overwritten to print this LWP ID instead of the POSIX thread ID.
epicsThreadMap(
Caveat: The WIN32 implementation needs the start/exit hooks calls added - I simply do not understand this implementation well enough to do that.
The vxWorks implementation of epicsThreadMap() only works for up to 2048 threads, and needs to be made dynamic.
mdavidsaver (mdavidsaver) wrote : | # |
Andrew Johnson (anj) wrote : | # |
Should the AtThreadExits run before the ExitHooks? You have this in several places:
+ epicsThreadRunE
epicsExitCa
My feeling is that task-specific clean-up should happen before the generic task clean-up, so things nest nicely.
I think the Win32 calls to the epicsThreadRun*
Then with some tests in libCom/
Thanks,
- Andrew
Ralph Lange (ralph-lange) wrote : | # |
> > /* As we're only ever inserting hooks at the head of the list, forward
> traversing is safe */
> > pHook = (epicsThreadHook *) ellFirst(list);
>
> It can be unlocked to traverse, but the mutex should be locked when fetching
> the head pointer.
That's because the pointer modifying operations (in elllib.c) are not atomic?
Other than that, I would assume the map function could either get or miss the element currently being inserted or added, and both would be ok.
- 12306. By Ralph Lange
-
libCom/osi: Fix nesting for epicsThreadHooks, add WIN32 implementation, lock traversal
- call (generic) exit hook after calling the (specific) epicsExitCallAtThreadExits( )
- for start hooks added as 1-2-3, run exit hooks in opposite order: 3-2-1
- add calls to hooks module to WIN32 osdThread.c
- add lock/unlock to hook list traversal - 12307. By Ralph Lange
-
libCom/osi: Make vxWorks implementation of epicsThreadMap() safe and dynamic
- 12308. By Ralph Lange
-
libCom/test: Add test for epicsThreadHooks
- 12309. By Ralph Lange
-
documentation: Add epicsThreadHooks info to RELEASE NOTES
Ralph Lange (ralph-lange) wrote : | # |
I think I pretty much worked down the list.
Andrew Johnson (anj) wrote : | # |
Hi Ralph,
Thanks.
Sorry, a couple more issues, then I think I'm done:
The name 'epicsShowThrea
If you don't intend that routine to be called by user code the declaration for it doesn't have to appear in any header files either, it could just appear in the posix/osdThread.c file where it's used. I would also prefer that it take an epicsThreadId argument instead of the epicsThreadOSD* pointer; that should remain for internal use only (epicsThread.h has a typedef making the two identical).
- Andrew
Andrew Johnson (anj) wrote : | # |
Sorry, a few more, although they're minor.
Please don't mark new API routines with epicsShareAPI; I'm trying to phase that out since almost nobody needs to use Pascal calling conventions on Windows any more. It's really only needed for routines that may be called by Visual Basic (if there are any users of that left), so it may still be necessary on CA API routines but not really on most others.
This may be more of a discussion point. Here's my output from epicsThreadShowAll:
epics> epicsThreadShowAll
NAME EPICS ID LWP ID OSIPRI OSSPRI STATE
_main_ 0x10b1060 0 0 0 OK
errlog 0x113c830 20849 10 0 OK
taskwd 0x113cc80 20850 10 0 OK
timerQueue 0x113d5e0 20851 70 0 OK
cbLow 0x113d8c0 20852 59 0 OK
cbMedium 0x113db90 20853 64 0 OK
cbHigh 0x113de60 20854 71 0 OK
dbCaLink 0x113e300 20855 50 0 OK
scanOnce 0x113ea10 20856 70 0 OK
scan-10 0x114d670 20857 60 0 OK
scan-5 0x114d8b0 20858 61 0 OK
scan-2 0x114daf0 20859 62 0 OK
scan-1 0x114dd30 20860 63 0 OK
scan-0.5 0x114df70 20861 64 0 OK
scan-0.2 0x114e1b0 20862 65 0 OK
scan-0.1 0x114e3f0 20863 66 0 OK
CAS-TCP 0x114ebc0 20864 18 0 OK
CAS-beacon 0x7f043c000990 20865 17 0 OK
CAS-UDP 0x7f0440000970 20866 16 0 OK
The _main_ thread is not started using epicsThreadCreate() and thus never runs the thread_hook, so we don't know its LWP ID. It would be nice if we could see it here. The _main_ thread gets its epicsThreadOSD structure created and added to pthreadList inside the once() routine in od/posix/
I think we can safely forget about calling _main_'s ExitHook, which brings up the interesting question of whether ExitHook is actually needed -- any StartHook can register an epicsAtThreadExit() routine if it needs to do clean-up. If we were to remove the ExitHooks we would never have problems with an ExitHook being called for a thread for which where there was no corresponding StartHook. It also means that the ExitHook routines would be run in the reverse order of the StartHook routines, whereas at the moment the order is the same.
- Andrew
Ralph Lange (ralph-lange) wrote : | # |
Hi Andrew,
> The name 'epicsShowThrea
> subsystem. The original name 'showThreadInfo' was static and thus only used
> internally to the posix implementation, but if you're making it visible I
> think it needs to match the other epicsThread... names. Maybe name it
> epicsThreadShow
> there the routine is static).
Will do.
On a closely related issue, I am quite unhappy with the confusing names of the public API functions
epicsThreadPriv
epicsThreadPriv
epicsThreadPriv
epicsThreadPriv
that are - unlike the other funtions that contain "Private" - *not* private, but public functions.
Also, they implement what is more commonly called "thread local storage".
Do you think it would be worthwhile renaming them to *Local* and deprecate the existing names, adding macros to continue supporting them?!
> If you don't intend that routine to be called by user code the declaration for
> it doesn't have to appear in any header files either, it could just appear in
> the posix/osdThread.c file where it's used.
True. Will do.
> I would also prefer that it take
> an epicsThreadId argument instead of the epicsThreadOSD* pointer; that should
> remain for internal use only (epicsThread.h has a typedef making the two
> identical).
If (after the mentioned change) the function is private, not in any header file, and any implementation would have to cast the argument to an epicsThreadOSD* pointer anyway, I don't see the point in using the opaque type.
~Ralph
- 12310. By Ralph Lange
-
libCom/osi: Streamline epicsThreadShowInfo functions between implementations
- epicsThreadShowInfo: private function to print headers or task line,
not in header file, using internal pointer type
Ralph Lange (ralph-lange) wrote : | # |
> Please don't mark new API routines with epicsShareAPI; I'm trying to phase
> that out [...]
Will do.
> This may be more of a discussion point. Here's my output from
> epicsThreadShowAll:
>
> epics> epicsThreadShowAll
> NAME EPICS ID LWP ID OSIPRI OSSPRI STATE
> _main_ 0x10b1060 0 0 0 OK
> errlog 0x113c830 20849 10 0 OK
> [...]
>
> The _main_ thread is not started using epicsThreadCreate() and thus never runs
> the thread_hook, so we don't know its LWP ID. It would be nice if we could
> see it here.
I had considered this, then decided against, as the POSIX implementation shows the POSIX thread id as 0, and I wanted to stay minimally invasive.
If you think this is a good idea (I do), I will add that.
> I think we can safely forget about calling _main_'s ExitHook, which brings up
> the interesting question of whether ExitHook is actually needed -- any
> StartHook can register an epicsAtThreadExit() routine if it needs to do clean-
> up. If we were to remove the ExitHooks we would never have problems with an
> ExitHook being called for a thread for which where there was no corresponding
> StartHook.
Good! That was my initial approach, and I added the ExitHook routines only out of a gut feeling that I wold have to justify if I didn't.
The argument about an exit hook being called where the start hook wasn't added at thread creation time is an excellent one.
> It also means that the ExitHook routines would be run in the
> reverse order of the StartHook routines, whereas at the moment the order is
> the same.
Not true. They are run in correct (reverse) order, which is covered by the test. Never mind.
~Ralph
- 12311. By Ralph Lange
-
libCom/osi: Don't decorate epicsThreadHook functions with epicsShareAPI
- 12312. By Ralph Lange
-
libCom/osi: Clean up epicsThreadHooks API
- remove exit hooks completely
- remove non-public functions fom header files
- add test for epicsThreadMap
- fix bugs in RTEMS and vxWorks implementation of epicsThreadMap - 12313. By Ralph Lange
-
libCom/osi: Add default start hook for _main_ thread
Ralph Lange (ralph-lange) wrote : | # |
> [...] The _main_ thread gets its epicsThreadOSD structure created and
> added to pthreadList inside the once() routine in od/posix/
> epicsThreadHook
> epicsThread... routines such as epicsThreadGetI
> finished yet.
I don't like all thread start hooks to inherit this limitation, so I implemented this using a second default hook, that is exclusively used for _main_.
So - if I did not miss anything - the list seems to be worked through, again.
~Ralph
- 12314. By Ralph Lange
-
documentation: Updated RELEASE_NOTES
Andrew Johnson (anj) wrote : | # |
Hi Ralph,
I made some minor changes while merging this, the most important being to properly fix the vxWorks epicsThreadInit() code so it is only run once. I also renamed a few things so the hooking API is now called epicsThreadHook
I'll do a test run on vxWorks when I'm back at work tomorrow.
Thanks,
- Andrew
Preview Diff
1 | === modified file 'documentation/RELEASE_NOTES.html' |
2 | --- documentation/RELEASE_NOTES.html 2012-06-27 21:43:15 +0000 |
3 | +++ documentation/RELEASE_NOTES.html 2012-07-03 14:42:25 +0000 |
4 | @@ -15,6 +15,17 @@ |
5 | <h2 align="center">Changes between 3.14.x and 3.15.0.x</h2> |
6 | <!-- Insert new items immediately below here ... --> |
7 | |
8 | +<h3>New API to hook into thread creation</h3> |
9 | + |
10 | +<p>A hook API has been added allowing user-supplied functions to be called |
11 | +whenever a thread starts. The calls are made from the thread's context, |
12 | +and can be used to control additional thread properties not handled inside |
13 | +EPICS base, e.g. setting the scheduling policy or CPU affinity (on SMP |
14 | +systems).</p> |
15 | + |
16 | +<p>The API also supports a mapping operation, calling a user-supplied function |
17 | +for every thread that is currently running.</p> |
18 | + |
19 | <h3>New scan rate units</h3> |
20 | |
21 | <p>Scan rates defined in the menuScan.dbd file may now be specified in seconds, |
22 | |
23 | === modified file 'src/libCom/osi/Makefile' |
24 | --- src/libCom/osi/Makefile 2012-05-03 17:19:34 +0000 |
25 | +++ src/libCom/osi/Makefile 2012-07-03 14:42:25 +0000 |
26 | @@ -104,6 +104,8 @@ |
27 | osdThread_CPPFLAGS += $(THREAD_CPPFLAGS_$(USE_POSIX_THREAD_PRIORITY_SCHEDULING)) |
28 | |
29 | Com_SRCS += osdThread.c |
30 | +Com_SRCS += osdThreadExtra.c |
31 | +Com_SRCS += osdThreadHooks.c |
32 | Com_SRCS += osdMutex.c |
33 | Com_SRCS += osdEvent.c |
34 | Com_SRCS += osdTime.cpp |
35 | |
36 | === modified file 'src/libCom/osi/epicsThread.h' |
37 | --- src/libCom/osi/epicsThread.h 2010-04-26 20:38:11 +0000 |
38 | +++ src/libCom/osi/epicsThread.h 2012-07-03 14:42:25 +0000 |
39 | @@ -3,8 +3,8 @@ |
40 | * National Laboratory. |
41 | * Copyright (c) 2002 The Regents of the University of California, as |
42 | * Operator of Los Alamos National Laboratory. |
43 | -* EPICS BASE Versions 3.13.7 |
44 | -* and higher are distributed subject to a Software License Agreement found |
45 | +* Copyright (c) 2012 ITER Organization |
46 | +* EPICS BASE is distributed subject to a Software License Agreement found |
47 | * in file LICENSE that is included with this distribution. |
48 | \*************************************************************************/ |
49 | #ifndef epicsThreadh |
50 | @@ -101,6 +101,12 @@ |
51 | epicsShareFunc void epicsShareAPI epicsThreadShow( |
52 | epicsThreadId id,unsigned int level); |
53 | |
54 | +/* Hooks called when a thread starts, map function called once for every thread */ |
55 | +typedef void (*EPICS_THREAD_HOOK_ROUTINE)(epicsThreadId id); |
56 | +epicsShareFunc void epicsThreadHooksInit(epicsThreadId id); |
57 | +epicsShareFunc void epicsThreadAddStartHook(EPICS_THREAD_HOOK_ROUTINE hook); |
58 | +epicsShareFunc void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func); |
59 | + |
60 | typedef struct epicsThreadPrivateOSD * epicsThreadPrivateId; |
61 | epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void); |
62 | epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete(epicsThreadPrivateId id); |
63 | |
64 | === added file 'src/libCom/osi/os/Linux/osdThread.h' |
65 | --- src/libCom/osi/os/Linux/osdThread.h 1970-01-01 00:00:00 +0000 |
66 | +++ src/libCom/osi/os/Linux/osdThread.h 2012-07-03 14:42:25 +0000 |
67 | @@ -0,0 +1,46 @@ |
68 | +/*************************************************************************\ |
69 | +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
70 | +* National Laboratory. |
71 | +* Copyright (c) 2002 The Regents of the University of California, as |
72 | +* Operator of Los Alamos National Laboratory. |
73 | +* EPICS BASE Versions 3.13.7 |
74 | +* and higher are distributed subject to a Software License Agreement found |
75 | +* in file LICENSE that is included with this distribution. |
76 | +\*************************************************************************/ |
77 | +#ifndef osdThreadh |
78 | +#define osdThreadh |
79 | + |
80 | +#include <pthread.h> |
81 | + |
82 | +#include "shareLib.h" |
83 | +#include "ellLib.h" |
84 | +#include "epicsEvent.h" |
85 | + |
86 | +#ifdef __cplusplus |
87 | +extern "C" { |
88 | +#endif |
89 | + |
90 | +typedef struct epicsThreadOSD { |
91 | + ELLNODE node; |
92 | + pthread_t tid; |
93 | + pid_t lwpId; |
94 | + pthread_attr_t attr; |
95 | + struct sched_param schedParam; |
96 | + EPICSTHREADFUNC createFunc; |
97 | + void *createArg; |
98 | + epicsEventId suspendEvent; |
99 | + int isSuspended; |
100 | + int isEpicsThread; |
101 | + int isFifoScheduled; |
102 | + int isOnThreadList; |
103 | + unsigned int osiPriority; |
104 | + char name[1]; /* actually larger */ |
105 | +} epicsThreadOSD; |
106 | + |
107 | +epicsShareFunc pthread_t epicsShareAPI epicsThreadGetPosixThreadId(epicsThreadId id); |
108 | + |
109 | +#ifdef __cplusplus |
110 | +} |
111 | +#endif |
112 | + |
113 | +#endif /* osdThreadh */ |
114 | |
115 | === added file 'src/libCom/osi/os/Linux/osdThreadExtra.c' |
116 | --- src/libCom/osi/os/Linux/osdThreadExtra.c 1970-01-01 00:00:00 +0000 |
117 | +++ src/libCom/osi/os/Linux/osdThreadExtra.c 2012-07-03 14:42:25 +0000 |
118 | @@ -0,0 +1,66 @@ |
119 | +/*************************************************************************\ |
120 | +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
121 | +* National Laboratory. |
122 | +* Copyright (c) 2002 The Regents of the University of California, as |
123 | +* Operator of Los Alamos National Laboratory. |
124 | +* Copyright (c) 2012 ITER Organization |
125 | +* EPICS BASE is distributed subject to a Software License Agreement found |
126 | +* in file LICENSE that is included with this distribution. |
127 | +\*************************************************************************/ |
128 | + |
129 | +/* Author: Marty Kraimer Date: 18JAN2000 */ |
130 | + |
131 | +/* This differs from the posix implementation of epicsThread by: |
132 | + * - printing the Linux LWP ID instead of the POSIX thread ID in the show routines |
133 | + * - installing a default thread start hook, that sets the Linux thread name to the |
134 | + * EPICS thread name to make it visible on OS level, and discovers the LWP ID */ |
135 | + |
136 | +#include <unistd.h> |
137 | +#include <signal.h> |
138 | +#include <sys/syscall.h> |
139 | +#include <sys/types.h> |
140 | +#include <sys/prctl.h> |
141 | + |
142 | +#define epicsExportSharedSymbols |
143 | +#include "epicsStdio.h" |
144 | +#include "ellLib.h" |
145 | +#include "epicsEvent.h" |
146 | +#include "epicsThread.h" |
147 | + |
148 | +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadDefaultStartHook; |
149 | +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadMainStartHook; |
150 | + |
151 | +void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level) |
152 | +{ |
153 | + if(!pthreadInfo) { |
154 | + fprintf(epicsGetStdout()," NAME EPICS ID " |
155 | + "LWP ID OSIPRI OSSPRI STATE\n"); |
156 | + } else { |
157 | + struct sched_param param; |
158 | + int policy; |
159 | + int priority = 0; |
160 | + |
161 | + if(pthreadInfo->tid) { |
162 | + int status; |
163 | + status = pthread_getschedparam(pthreadInfo->tid,&policy,¶m); |
164 | + if(!status) priority = param.sched_priority; |
165 | + } |
166 | + fprintf(epicsGetStdout(),"%16.16s %12p %8lu %3d%8d %8.8s\n", |
167 | + pthreadInfo->name,(void *) |
168 | + pthreadInfo,(unsigned long)pthreadInfo->lwpId, |
169 | + pthreadInfo->osiPriority,priority, |
170 | + pthreadInfo->isSuspended?"SUSPEND":"OK"); |
171 | + } |
172 | +} |
173 | + |
174 | +static void thread_hook(epicsThreadOSD *pthreadInfo) |
175 | +{ |
176 | + /* Set the name of the thread's process. Limited to 16 characters. */ |
177 | + char comm[16]; |
178 | + snprintf(comm, sizeof(comm), "%s", pthreadInfo->name); |
179 | + prctl(PR_SET_NAME, comm, 0l, 0l, 0l); |
180 | + pthreadInfo->lwpId = syscall(SYS_gettid); |
181 | +} |
182 | + |
183 | +EPICS_THREAD_HOOK_ROUTINE epicsThreadDefaultStartHook = thread_hook; |
184 | +EPICS_THREAD_HOOK_ROUTINE epicsThreadMainStartHook = thread_hook; |
185 | |
186 | === modified file 'src/libCom/osi/os/RTEMS/osdThread.c' |
187 | --- src/libCom/osi/os/RTEMS/osdThread.c 2010-10-05 19:27:37 +0000 |
188 | +++ src/libCom/osi/os/RTEMS/osdThread.c 2012-07-03 14:42:25 +0000 |
189 | @@ -39,6 +39,8 @@ |
190 | #include "osdInterrupt.h" |
191 | #include "epicsExit.h" |
192 | |
193 | +epicsShareFunc void epicsThreadRunStartHooks(epicsThreadId id); |
194 | + |
195 | /* |
196 | * Per-task variables |
197 | */ |
198 | @@ -164,6 +166,7 @@ |
199 | { |
200 | struct taskVar *v = (struct taskVar *)arg; |
201 | |
202 | + epicsThreadRunStartHooks(v->id); |
203 | (*v->funptr)(v->parm); |
204 | epicsExitCallAtThreadExits (); |
205 | taskVarLock (); |
206 | @@ -235,6 +238,7 @@ |
207 | taskVarMutex = epicsMutexMustCreate (); |
208 | rtems_task_ident (RTEMS_SELF, 0, &tid); |
209 | setThreadInfo (tid, "_main_", NULL, NULL); |
210 | + epicsThreadHooksInit(tid); |
211 | initialized = 1; |
212 | epicsThreadCreate ("ImsgDaemon", 99, |
213 | epicsThreadGetStackSize (epicsThreadStackSmall), |
214 | @@ -640,19 +644,17 @@ |
215 | } |
216 | |
217 | static void |
218 | -epicsThreadShowHeader (void) |
219 | -{ |
220 | - fprintf(epicsGetStdout()," PRIORITY\n"); |
221 | - fprintf(epicsGetStdout()," ID EPICS RTEMS STATE WAIT NAME\n"); |
222 | - fprintf(epicsGetStdout(),"+--------+-----------+--------+--------+---------------------+\n"); |
223 | -} |
224 | - |
225 | -static void |
226 | epicsThreadShowInfo (struct taskVar *v, unsigned int level) |
227 | { |
228 | + if (!v) { |
229 | + fprintf(epicsGetStdout()," PRIORITY\n"); |
230 | + fprintf(epicsGetStdout()," ID EPICS RTEMS STATE WAIT NAME\n"); |
231 | + fprintf(epicsGetStdout(),"+--------+-----------+--------+--------+---------------------+\n"); |
232 | + } else { |
233 | fprintf(epicsGetStdout(),"%9.8x", (int)v->id); |
234 | showInternalTaskInfo (v->id); |
235 | fprintf(epicsGetStdout()," %s\n", v->name); |
236 | + } |
237 | } |
238 | |
239 | void epicsThreadShow (epicsThreadId id, unsigned int level) |
240 | @@ -660,7 +662,7 @@ |
241 | struct taskVar *v; |
242 | |
243 | if (!id) { |
244 | - epicsThreadShowHeader (); |
245 | + epicsThreadShowInfo (NULL, level); |
246 | return; |
247 | } |
248 | taskVarLock (); |
249 | @@ -674,6 +676,23 @@ |
250 | fprintf(epicsGetStdout(),"*** Thread %x does not exist.\n", (unsigned int)id); |
251 | } |
252 | |
253 | +void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func) |
254 | +{ |
255 | + struct taskVar *v; |
256 | + |
257 | + taskVarLock (); |
258 | + /* |
259 | + * Map tasks in the order of creation (backwards through list) |
260 | + */ |
261 | + for (v = taskVarHead ; v != NULL && v->forw != NULL ; v = v->forw) |
262 | + continue; |
263 | + while (v) { |
264 | + func ((epicsThreadId)v->id); |
265 | + v = v->back; |
266 | + } |
267 | + taskVarUnlock (); |
268 | +} |
269 | + |
270 | void epicsThreadShowAll (unsigned int level) |
271 | { |
272 | struct taskVar *v; |
273 | |
274 | === modified file 'src/libCom/osi/os/WIN32/osdThread.c' |
275 | --- src/libCom/osi/os/WIN32/osdThread.c 2012-02-03 00:14:01 +0000 |
276 | +++ src/libCom/osi/os/WIN32/osdThread.c 2012-07-03 14:42:25 +0000 |
277 | @@ -38,6 +38,8 @@ |
278 | #include "ellLib.h" |
279 | #include "epicsExit.h" |
280 | |
281 | +epicsShareFunc void epicsThreadRunStartHooks(epicsThreadId id); |
282 | + |
283 | void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ); |
284 | static void threadCleanupWIN32 ( void ); |
285 | |
286 | @@ -226,6 +228,7 @@ |
287 | pWin32ThreadGlobal = 0; |
288 | return 0; |
289 | } |
290 | + epicsThreadHooksInit (NULL); |
291 | |
292 | InterlockedExchange ( & initCompleted, 1 ); |
293 | |
294 | @@ -496,6 +499,7 @@ |
295 | |
296 | success = TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, pParm ); |
297 | if ( success ) { |
298 | + epicsThreadRunStartHooks ( ( epicsThreadId ) pParm ); |
299 | /* printf ( "starting thread %d\n", pParm->id ); */ |
300 | ( *pParm->funptr ) ( pParm->parm ); |
301 | /* printf ( "terminating thread %d\n", pParm->id ); */ |
302 | @@ -510,7 +514,6 @@ |
303 | } |
304 | |
305 | epicsExitCallAtThreadExits (); |
306 | - |
307 | /* |
308 | * CAUTION: !!!! the thread id might continue to be used after this thread exits !!!! |
309 | */ |
310 | @@ -947,9 +950,9 @@ |
311 | } |
312 | |
313 | /* |
314 | - * epicsThreadShowPrivate () |
315 | + * epicsThreadShowInfo () |
316 | */ |
317 | -static void epicsThreadShowPrivate ( epicsThreadId id, unsigned level ) |
318 | +static void epicsThreadShowInfo ( epicsThreadId id, unsigned level ) |
319 | { |
320 | win32ThreadParam * pParm = ( win32ThreadParam * ) id; |
321 | |
322 | @@ -975,6 +978,28 @@ |
323 | } |
324 | |
325 | /* |
326 | + * epicsThreadMap () |
327 | + */ |
328 | +epicsShareFunc void epicsShareAPI epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func ) |
329 | +{ |
330 | + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); |
331 | + win32ThreadParam * pParm; |
332 | + |
333 | + if ( ! pGbl ) { |
334 | + return; |
335 | + } |
336 | + |
337 | + EnterCriticalSection ( & pGbl->mutex ); |
338 | + |
339 | + for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); |
340 | + pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { |
341 | + func ( ( epicsThreadId ) pParm ); |
342 | + } |
343 | + |
344 | + LeaveCriticalSection ( & pGbl->mutex ); |
345 | +} |
346 | + |
347 | +/* |
348 | * epicsThreadShowAll () |
349 | */ |
350 | epicsShareFunc void epicsShareAPI epicsThreadShowAll ( unsigned level ) |
351 | @@ -987,11 +1012,11 @@ |
352 | } |
353 | |
354 | EnterCriticalSection ( & pGbl->mutex ); |
355 | - |
356 | - epicsThreadShowPrivate ( 0, level ); |
357 | - for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); |
358 | + |
359 | + epicsThreadShowInfo ( 0, level ); |
360 | + for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); |
361 | pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { |
362 | - epicsThreadShowPrivate ( ( epicsThreadId ) pParm, level ); |
363 | + epicsThreadShowInfo ( ( epicsThreadId ) pParm, level ); |
364 | } |
365 | |
366 | LeaveCriticalSection ( & pGbl->mutex ); |
367 | @@ -1002,8 +1027,8 @@ |
368 | */ |
369 | epicsShareFunc void epicsShareAPI epicsThreadShow ( epicsThreadId id, unsigned level ) |
370 | { |
371 | - epicsThreadShowPrivate ( 0, level ); |
372 | - epicsThreadShowPrivate ( id, level ); |
373 | + epicsThreadShowInfo ( 0, level ); |
374 | + epicsThreadShowInfo ( id, level ); |
375 | } |
376 | |
377 | /* |
378 | |
379 | === added file 'src/libCom/osi/os/default/osdThreadExtra.c' |
380 | --- src/libCom/osi/os/default/osdThreadExtra.c 1970-01-01 00:00:00 +0000 |
381 | +++ src/libCom/osi/os/default/osdThreadExtra.c 2012-07-03 14:42:25 +0000 |
382 | @@ -0,0 +1,19 @@ |
383 | +/*************************************************************************\ |
384 | +* Copyright (c) 2012 ITER Organization |
385 | +* |
386 | +* EPICS BASE is distributed subject to a Software License Agreement found |
387 | +* in file LICENSE that is included with this distribution. |
388 | +\*************************************************************************/ |
389 | + |
390 | +/* Author: Ralph Lange Date: 26 Jun 2012 */ |
391 | + |
392 | +/* Null default thread hooks for all platforms that do not do anything special */ |
393 | + |
394 | +#define epicsExportSharedSymbols |
395 | +#include "shareLib.h" |
396 | + |
397 | +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadDefaultStartHook; |
398 | +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadMainStartHook; |
399 | + |
400 | +EPICS_THREAD_HOOK_ROUTINE epicsThreadDefaultStartHook; |
401 | +EPICS_THREAD_HOOK_ROUTINE epicsThreadMainStartHook; |
402 | |
403 | === added file 'src/libCom/osi/os/default/osdThreadHooks.c' |
404 | --- src/libCom/osi/os/default/osdThreadHooks.c 1970-01-01 00:00:00 +0000 |
405 | +++ src/libCom/osi/os/default/osdThreadHooks.c 2012-07-03 14:42:25 +0000 |
406 | @@ -0,0 +1,74 @@ |
407 | +/*************************************************************************\ |
408 | +* Copyright (c) 2012 ITER Organization |
409 | +* |
410 | +* EPICS BASE is distributed subject to a Software License Agreement found |
411 | +* in file LICENSE that is included with this distribution. |
412 | +\*************************************************************************/ |
413 | + |
414 | +/* Author: Ralph Lange Date: 28 Jun 2012 */ |
415 | + |
416 | +/* Secure hooks for epicsThread */ |
417 | + |
418 | +#include <stdlib.h> |
419 | +#include <stdio.h> |
420 | +#include <errno.h> |
421 | +#include <string.h> |
422 | + |
423 | +#define epicsExportSharedSymbols |
424 | +#include "ellLib.h" |
425 | +#include "epicsMutex.h" |
426 | +#include "epicsThread.h" |
427 | + |
428 | +epicsShareFunc void epicsThreadRunStartHooks(epicsThreadId id); |
429 | +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadDefaultStartHook; |
430 | +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadMainStartHook; |
431 | + |
432 | +#define checkStatusOnceReturn(status, message, method) \ |
433 | +if((status)) { \ |
434 | + fprintf(stderr,"%s error %s\n",(message),strerror((status))); \ |
435 | + fprintf(stderr," %s\n",(method)); \ |
436 | + return; } |
437 | + |
438 | +typedef struct epicsThreadHook { |
439 | + ELLNODE node; |
440 | + EPICS_THREAD_HOOK_ROUTINE func; |
441 | +} epicsThreadHook; |
442 | + |
443 | +static ELLLIST startHooks = ELLLIST_INIT; |
444 | + |
445 | +/* Locking could probably be avoided, if elllist implementation was using atomic ops */ |
446 | +static epicsMutexId hookLock; |
447 | + |
448 | +epicsShareFunc void epicsThreadAddStartHook(EPICS_THREAD_HOOK_ROUTINE hook) |
449 | +{ |
450 | + epicsThreadHook *pHook; |
451 | + |
452 | + pHook = calloc(1, sizeof(epicsThreadHook)); |
453 | + if (!pHook) checkStatusOnceReturn(errno,"calloc","epicsThreadAddStartHook"); |
454 | + pHook->func = hook; |
455 | + epicsMutexLock(hookLock); |
456 | + ellAdd(&startHooks, &pHook->node); |
457 | + epicsMutexUnlock(hookLock); |
458 | +} |
459 | + |
460 | +epicsShareFunc void epicsThreadHooksInit(epicsThreadId id) |
461 | +{ |
462 | + if (!hookLock) { |
463 | + hookLock = epicsMutexMustCreate(); |
464 | + if (epicsThreadDefaultStartHook) epicsThreadAddStartHook(epicsThreadDefaultStartHook); |
465 | + } |
466 | + if (id && epicsThreadMainStartHook) epicsThreadMainStartHook(id); |
467 | +} |
468 | + |
469 | +epicsShareFunc void epicsThreadRunStartHooks(epicsThreadId id) |
470 | +{ |
471 | + epicsThreadHook *pHook; |
472 | + |
473 | + epicsMutexLock(hookLock); |
474 | + pHook = (epicsThreadHook *) ellFirst(&startHooks); |
475 | + while (pHook) { |
476 | + pHook->func(id); |
477 | + pHook = (epicsThreadHook *) ellNext(&pHook->node); |
478 | + } |
479 | + epicsMutexUnlock(hookLock); |
480 | +} |
481 | |
482 | === modified file 'src/libCom/osi/os/posix/osdThread.c' |
483 | --- src/libCom/osi/os/posix/osdThread.c 2012-06-19 19:28:26 +0000 |
484 | +++ src/libCom/osi/os/posix/osdThread.c 2012-07-03 14:42:25 +0000 |
485 | @@ -3,6 +3,7 @@ |
486 | * National Laboratory. |
487 | * Copyright (c) 2002 The Regents of the University of California, as |
488 | * Operator of Los Alamos National Laboratory. |
489 | +* Copyright (c) 2012 ITER Organization |
490 | * EPICS BASE is distributed subject to a Software License Agreement found |
491 | * in file LICENSE that is included with this distribution. |
492 | \*************************************************************************/ |
493 | @@ -34,6 +35,9 @@ |
494 | #include "epicsAssert.h" |
495 | #include "epicsExit.h" |
496 | |
497 | +epicsShareFunc void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level); |
498 | +epicsShareFunc void epicsThreadRunStartHooks(epicsThreadId id); |
499 | + |
500 | static int mutexLock(pthread_mutex_t *id) |
501 | { |
502 | int status; |
503 | @@ -57,22 +61,6 @@ |
504 | int schedPolicy; |
505 | } commonAttr; |
506 | |
507 | -typedef struct epicsThreadOSD { |
508 | - ELLNODE node; |
509 | - pthread_t tid; |
510 | - pthread_attr_t attr; |
511 | - struct sched_param schedParam; |
512 | - EPICSTHREADFUNC createFunc; |
513 | - void *createArg; |
514 | - epicsEventId suspendEvent; |
515 | - int isSuspended; |
516 | - int isEpicsThread; |
517 | - int isFifoScheduled; |
518 | - int isOnThreadList; |
519 | - unsigned int osiPriority; |
520 | - char name[1]; /* actually larger */ |
521 | -} epicsThreadOSD; |
522 | - |
523 | #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING |
524 | typedef struct { |
525 | int min_pri, max_pri; |
526 | @@ -366,6 +354,7 @@ |
527 | checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadInit"); |
528 | status = atexit(epicsExitCallAtExits); |
529 | checkStatusOnce(status,"atexit"); |
530 | + epicsThreadHooksInit(pthreadInfo); |
531 | epicsThreadOnceCalled = 1; |
532 | } |
533 | |
534 | |
535 | @@ -375,7 +364,7 @@ |
536 | int status; |
537 | int oldtype; |
538 | sigset_t blockAllSig; |
539 | - |
540 | + |
541 | sigfillset(&blockAllSig); |
542 | pthread_sigmask(SIG_SETMASK,&blockAllSig,NULL); |
543 | status = pthread_setspecific(getpthreadInfo,arg); |
544 | @@ -388,11 +377,11 @@ |
545 | pthreadInfo->isOnThreadList = 1; |
546 | status = pthread_mutex_unlock(&listLock); |
547 | checkStatusQuit(status,"pthread_mutex_unlock","start_routine"); |
548 | + epicsThreadRunStartHooks(pthreadInfo); |
549 | |
550 | (*pthreadInfo->createFunc)(pthreadInfo->createArg); |
551 | |
552 | epicsExitCallAtThreadExits (); |
553 | - |
554 | free_threadInfo(pthreadInfo); |
555 | return(0); |
556 | } |
557 | @@ -747,27 +736,23 @@ |
558 | name[size-1] = '\0'; |
559 | } |
560 | |
561 | |
562 | -static void showThreadInfo(epicsThreadOSD *pthreadInfo,unsigned int level) |
563 | +epicsShareFunc void epicsThreadMap(EPICS_THREAD_HOOK_ROUTINE func) |
564 | { |
565 | - if(!pthreadInfo) { |
566 | - fprintf(epicsGetStdout()," NAME EPICS ID " |
567 | - "PTHREAD ID OSIPRI OSSPRI STATE\n"); |
568 | - } else { |
569 | - struct sched_param param; |
570 | - int policy; |
571 | - int priority = 0; |
572 | + epicsThreadOSD *pthreadInfo; |
573 | + int status; |
574 | |
575 | - if(pthreadInfo->tid) { |
576 | - int status; |
577 | - status = pthread_getschedparam(pthreadInfo->tid,&policy,¶m); |
578 | - if(!status) priority = param.sched_priority; |
579 | - } |
580 | - fprintf(epicsGetStdout(),"%16.16s %12p %12lu %3d%8d %8.8s\n", |
581 | - pthreadInfo->name,(void *) |
582 | - pthreadInfo,(unsigned long)pthreadInfo->tid, |
583 | - pthreadInfo->osiPriority,priority, |
584 | - pthreadInfo->isSuspended?"SUSPEND":"OK"); |
585 | + epicsThreadInit(); |
586 | + status = mutexLock(&listLock); |
587 | + checkStatus(status, "pthread_mutex_lock epicsThreadMap"); |
588 | + if (status) |
589 | + return; |
590 | + pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); |
591 | + while (pthreadInfo) { |
592 | + func(pthreadInfo); |
593 | + pthreadInfo = (epicsThreadOSD *)ellNext(&pthreadInfo->node); |
594 | } |
595 | + status = pthread_mutex_unlock(&listLock); |
596 | + checkStatus(status, "pthread_mutex_unlock epicsThreadMap"); |
597 | } |
598 | |
599 | epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level) |
600 | @@ -783,7 +768,7 @@ |
601 | return; |
602 | pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); |
603 | while(pthreadInfo) { |
604 | - showThreadInfo(pthreadInfo,level); |
605 | + epicsThreadShowInfo(pthreadInfo,level); |
606 | pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); |
607 | } |
608 | status = pthread_mutex_unlock(&listLock); |
609 | @@ -798,7 +783,7 @@ |
610 | |
611 | epicsThreadInit(); |
612 | if(!showThread) { |
613 | - showThreadInfo(0,level); |
614 | + epicsThreadShowInfo(0,level); |
615 | return; |
616 | } |
617 | status = mutexLock(&listLock); |
618 | @@ -810,7 +795,7 @@ |
619 | if (((epicsThreadId)pthreadInfo == showThread) |
620 | || ((epicsThreadId)pthreadInfo->tid == showThread)) { |
621 | found = 1; |
622 | - showThreadInfo(pthreadInfo,level); |
623 | + epicsThreadShowInfo(pthreadInfo,level); |
624 | } |
625 | pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); |
626 | } |
627 | @@ -820,7 +805,6 @@ |
628 | if (!found) |
629 | printf("Thread %#lx (%lu) not found.\n", (unsigned long)showThread, (unsigned long)showThread); |
630 | } |
631 | - |
632 | |
633 | |
634 | epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void) |
635 | { |
636 | |
637 | === modified file 'src/libCom/osi/os/posix/osdThread.h' |
638 | --- src/libCom/osi/os/posix/osdThread.h 2008-08-06 16:54:25 +0000 |
639 | +++ src/libCom/osi/os/posix/osdThread.h 2012-07-03 14:42:25 +0000 |
640 | @@ -13,12 +13,30 @@ |
641 | #include <pthread.h> |
642 | |
643 | #include "shareLib.h" |
644 | +#include "ellLib.h" |
645 | +#include "epicsEvent.h" |
646 | |
647 | #ifdef __cplusplus |
648 | extern "C" { |
649 | #endif |
650 | |
651 | -epicsShareFunc pthread_t epicsShareAPI epicsThreadGetPosixThreadId ( epicsThreadId id ); |
652 | +typedef struct epicsThreadOSD { |
653 | + ELLNODE node; |
654 | + pthread_t tid; |
655 | + pthread_attr_t attr; |
656 | + struct sched_param schedParam; |
657 | + EPICSTHREADFUNC createFunc; |
658 | + void *createArg; |
659 | + epicsEventId suspendEvent; |
660 | + int isSuspended; |
661 | + int isEpicsThread; |
662 | + int isFifoScheduled; |
663 | + int isOnThreadList; |
664 | + unsigned int osiPriority; |
665 | + char name[1]; /* actually larger */ |
666 | +} epicsThreadOSD; |
667 | + |
668 | +epicsShareFunc pthread_t epicsShareAPI epicsThreadGetPosixThreadId(epicsThreadId id); |
669 | |
670 | #ifdef __cplusplus |
671 | } |
672 | |
673 | === added file 'src/libCom/osi/os/posix/osdThreadExtra.c' |
674 | --- src/libCom/osi/os/posix/osdThreadExtra.c 1970-01-01 00:00:00 +0000 |
675 | +++ src/libCom/osi/os/posix/osdThreadExtra.c 2012-07-03 14:42:25 +0000 |
676 | @@ -0,0 +1,48 @@ |
677 | +/*************************************************************************\ |
678 | +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
679 | +* National Laboratory. |
680 | +* Copyright (c) 2002 The Regents of the University of California, as |
681 | +* Operator of Los Alamos National Laboratory. |
682 | +* EPICS BASE is distributed subject to a Software License Agreement found |
683 | +* in file LICENSE that is included with this distribution. |
684 | +\*************************************************************************/ |
685 | + |
686 | +/* Author: Marty Kraimer Date: 18JAN2000 */ |
687 | + |
688 | +/* This is part of the posix implementation of epicsThread */ |
689 | + |
690 | +#define epicsExportSharedSymbols |
691 | +#include "epicsStdio.h" |
692 | +#include "ellLib.h" |
693 | +#include "epicsEvent.h" |
694 | +#include "epicsThread.h" |
695 | + |
696 | +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadDefaultStartHook; |
697 | +epicsShareExtern EPICS_THREAD_HOOK_ROUTINE epicsThreadMainStartHook; |
698 | + |
699 | +EPICS_THREAD_HOOK_ROUTINE epicsThreadDefaultStartHook; |
700 | +EPICS_THREAD_HOOK_ROUTINE epicsThreadMainStartHook; |
701 | + |
702 | +void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level) |
703 | +{ |
704 | + if(!pthreadInfo) { |
705 | + fprintf(epicsGetStdout()," NAME EPICS ID " |
706 | + "PTHREAD ID OSIPRI OSSPRI STATE\n"); |
707 | + } else { |
708 | + struct sched_param param; |
709 | + int policy; |
710 | + int priority = 0; |
711 | + |
712 | + if(pthreadInfo->tid) { |
713 | + int status; |
714 | + status = pthread_getschedparam(pthreadInfo->tid,&policy,¶m); |
715 | + if(!status) priority = param.sched_priority; |
716 | + } |
717 | + fprintf(epicsGetStdout(),"%16.16s %12p %12lu %3d%8d %8.8s\n", |
718 | + pthreadInfo->name,(void *) |
719 | + pthreadInfo,(unsigned long)pthreadInfo->tid, |
720 | + pthreadInfo->osiPriority,priority, |
721 | + pthreadInfo->isSuspended?"SUSPEND":"OK"); |
722 | + } |
723 | +} |
724 | + |
725 | |
726 | === modified file 'src/libCom/osi/os/vxWorks/osdThread.c' |
727 | --- src/libCom/osi/os/vxWorks/osdThread.c 2010-08-11 15:45:17 +0000 |
728 | +++ src/libCom/osi/os/vxWorks/osdThread.c 2012-07-03 14:42:25 +0000 |
729 | @@ -3,6 +3,7 @@ |
730 | * National Laboratory. |
731 | * Copyright (c) 2002 The Regents of the University of California, as |
732 | * Operator of Los Alamos National Laboratory. |
733 | +* Copyright (c) 2012 ITER Organization |
734 | * EPICS BASE is distributed subject to a Software License Agreement found |
735 | * in file LICENSE that is included with this distribution. |
736 | \*************************************************************************/ |
737 | @@ -32,6 +33,8 @@ |
738 | #include "vxLib.h" |
739 | #include "epicsExit.h" |
740 | |
741 | +epicsShareFunc void epicsThreadRunStartHooks(epicsThreadId id); |
742 | + |
743 | #if CPU_FAMILY == MC680X0 |
744 | #define ARCH_STACK_FACTOR 1 |
745 | #elif CPU_FAMILY == SPARC |
746 | @@ -42,6 +45,12 @@ |
747 | static const unsigned stackSizeTable[epicsThreadStackBig+1] = |
748 | {4000*ARCH_STACK_FACTOR, 6000*ARCH_STACK_FACTOR, 11000*ARCH_STACK_FACTOR}; |
749 | |
750 | +/* Table and lock for epicsThreadMap() */ |
751 | +#define ID_LIST_CHUNK 512 |
752 | +static int *taskIdList = 0; |
753 | +int taskIdListSize = 0; |
754 | +static SEM_ID epicsThreadListMutex = 0; |
755 | + |
756 | /*The following forces atReboot to be loaded*/ |
757 | extern int atRebootExtern; |
758 | static struct pext { |
759 | @@ -83,16 +92,25 @@ |
760 | } |
761 | } |
762 | |
763 | +static void mutexInit(SEM_ID lock) |
764 | +{ |
765 | + if (lock == 0) { |
766 | + lock = semMCreate(SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY); |
767 | + assert(lock); |
768 | + } |
769 | +} |
770 | + |
771 | static void epicsThreadInit(void) |
772 | { |
773 | static int lock = 0; |
774 | |
775 | while(!vxTas(&lock)) taskDelay(1); |
776 | - if(epicsThreadOnceMutex==0) { |
777 | - epicsThreadOnceMutex = semMCreate( |
778 | - SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY); |
779 | - assert(epicsThreadOnceMutex); |
780 | - } |
781 | + epicsThreadHooksInit(NULL); |
782 | + mutexInit(epicsThreadOnceMutex); |
783 | + mutexInit(epicsThreadListMutex); |
784 | + taskIdList = calloc(ID_LIST_CHUNK, sizeof(int)); |
785 | + assert(taskIdList); |
786 | + taskIdListSize = ID_LIST_CHUNK; |
787 | lock = 0; |
788 | } |
789 | |
790 | @@ -157,6 +175,7 @@ |
791 | taskVarAdd(tid,(int *)(char *)&papTSD); |
792 | /*Make sure that papTSD is still 0 after that call to taskVarAdd*/ |
793 | papTSD = 0; |
794 | + epicsThreadRunStartHooks((epicsThreadId)tid); |
795 | (*func)(parm); |
796 | epicsExitCallAtThreadExits (); |
797 | free(papTSD); |
798 | @@ -312,6 +331,29 @@ |
799 | name[size-1] = '\0'; |
800 | } |
801 | |
802 | +epicsShareFunc void epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func ) |
803 | +{ |
804 | + int noTasks = 0; |
805 | + int i; |
806 | + int result; |
807 | + |
808 | + result = semTake(epicsThreadListMutex, WAIT_FOREVER); |
809 | + assert(result == OK); |
810 | + while (noTasks == 0) { |
811 | + noTasks = taskIdListGet(taskIdList, taskIdListSize); |
812 | + if (noTasks == taskIdListSize) { |
813 | + taskIdList = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int)); |
814 | + assert(taskIdList); |
815 | + taskIdListSize += ID_LIST_CHUNK; |
816 | + noTasks = 0; |
817 | + } |
818 | + } |
819 | + for (i = 0; i < noTasks; i++) { |
820 | + func ((epicsThreadId)taskIdList[i]); |
821 | + } |
822 | + semGive(epicsThreadListMutex); |
823 | +} |
824 | + |
825 | void epicsThreadShowAll(unsigned int level) |
826 | { |
827 | taskShow(0,2); |
828 | |
829 | === modified file 'src/libCom/test/Makefile' |
830 | --- src/libCom/test/Makefile 2012-06-22 19:06:52 +0000 |
831 | +++ src/libCom/test/Makefile 2012-07-03 14:42:25 +0000 |
832 | @@ -89,6 +89,11 @@ |
833 | testHarness_SRCS += epicsThreadPrivateTest.cpp |
834 | TESTS += epicsThreadPrivateTest |
835 | |
836 | +TESTPROD_HOST += epicsThreadHooksTest |
837 | +epicsThreadHooksTest_SRCS += epicsThreadHooksTest.c |
838 | +testHarness_SRCS += epicsThreadHooksTest.c |
839 | +TESTS += epicsThreadHooksTest |
840 | + |
841 | TESTPROD_HOST += epicsExitTest |
842 | epicsExitTest_SRCS += epicsExitTest.c |
843 | testHarness_SRCS += epicsExitTest.c |
844 | |
845 | === added file 'src/libCom/test/epicsThreadHooksTest.c' |
846 | --- src/libCom/test/epicsThreadHooksTest.c 1970-01-01 00:00:00 +0000 |
847 | +++ src/libCom/test/epicsThreadHooksTest.c 2012-07-03 14:42:25 +0000 |
848 | @@ -0,0 +1,122 @@ |
849 | +/*************************************************************************\ |
850 | +* Copyright (c) 2012 ITER Organization |
851 | +* |
852 | +* EPICS BASE is distributed subject to a Software License Agreement found |
853 | +* in file LICENSE that is included with this distribution. |
854 | +\*************************************************************************/ |
855 | + |
856 | +/* epicsThreadHooksTest.c */ |
857 | + |
858 | +#include <stdio.h> |
859 | +#include <stdint.h> |
860 | + |
861 | +#include "epicsThread.h" |
862 | +#include "epicsExit.h" |
863 | +#include "epicsEvent.h" |
864 | +#include "errlog.h" |
865 | +#include "epicsUnitTest.h" |
866 | +#include "testMain.h" |
867 | + |
868 | +#define THREAD_NO 6 |
869 | +#define HOOKS_NO 4 |
870 | + |
871 | +epicsThreadPrivateId threadNo; |
872 | +static int order[THREAD_NO][HOOKS_NO]; |
873 | +static int cnt[THREAD_NO]; |
874 | +static int mine[THREAD_NO]; |
875 | +static int called[THREAD_NO]; |
876 | +static epicsThreadId tid[THREAD_NO]; |
877 | +epicsEventId shutdown[THREAD_NO]; |
878 | + |
879 | +static int newThreadIndex(epicsThreadId id) |
880 | +{ |
881 | + int i = 0; |
882 | + while (tid[i] != 0 && i < THREAD_NO) i++; |
883 | + tid[i] = id; |
884 | + return i; |
885 | +} |
886 | + |
887 | +static int findThreadIndex(epicsThreadId id) |
888 | +{ |
889 | + int i = 0; |
890 | + while (tid[i] != id && i < THREAD_NO) i++; |
891 | + return i; |
892 | +} |
893 | + |
894 | +static void atExitHook1 (void *arg) |
895 | +{ |
896 | + int no = findThreadIndex(epicsThreadGetIdSelf()); |
897 | + order[no][3] = cnt[no]++; |
898 | +} |
899 | + |
900 | +static void atExitHook2 (void *arg) |
901 | +{ |
902 | + int no = findThreadIndex(epicsThreadGetIdSelf()); |
903 | + order[no][2] = cnt[no]++; |
904 | +} |
905 | + |
906 | +static void startHook1 (epicsThreadId id) |
907 | +{ |
908 | + int no = newThreadIndex(id); |
909 | + order[no][0] = cnt[no]++; |
910 | + epicsAtThreadExit(atExitHook1, NULL); |
911 | +} |
912 | + |
913 | +static void startHook2 (epicsThreadId id) |
914 | +{ |
915 | + int no = findThreadIndex(id); |
916 | + order[no][1] = cnt[no]++; |
917 | + epicsAtThreadExit(atExitHook2, NULL); |
918 | +} |
919 | + |
920 | +static void my_thread (void *arg) |
921 | +{ |
922 | + int no = findThreadIndex(epicsThreadGetIdSelf()); |
923 | + mine[no] = 1; |
924 | + epicsEventMustWait((epicsEventId) arg); |
925 | +} |
926 | + |
927 | +static void mapper (epicsThreadId id) |
928 | +{ |
929 | + called[findThreadIndex(id)]++; |
930 | +} |
931 | + |
932 | +MAIN(epicsThreadHooksTest) |
933 | +{ |
934 | + int i; |
935 | + int ok; |
936 | + |
937 | + testPlan(THREAD_NO); |
938 | + epicsThreadAddStartHook(startHook1); |
939 | + epicsThreadAddStartHook(startHook2); |
940 | + |
941 | + for (i = 0; i < THREAD_NO-1; i++) { |
942 | + shutdown[i] = epicsEventCreate(epicsEventEmpty); |
943 | + char name[10]; |
944 | + sprintf(name, "t%d", (int) i); |
945 | + epicsThreadCreate(name, epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), my_thread, shutdown[i]); |
946 | + } |
947 | + |
948 | + epicsThreadMap(mapper); |
949 | + |
950 | + ok = 1; |
951 | + for (i = 0; i < THREAD_NO-1; i++) { |
952 | + if (mine[i] && called[i] != 1) ok = 0; |
953 | + } |
954 | + testOk(ok, "All tasks covered once by epicsThreadMap"); |
955 | + |
956 | + for (i = 0; i < THREAD_NO-1; i++) { |
957 | + epicsEventSignal(shutdown[i]); |
958 | + } |
959 | + |
960 | + epicsThreadSleep(1.0); |
961 | + |
962 | + for (i = 0; i < THREAD_NO; i++) { |
963 | + int j; |
964 | + for (j = 0; j < HOOKS_NO; j++) { |
965 | + if (mine[i] && order[i][j]!=j) ok = 0; |
966 | + } |
967 | + if (mine[i]) testOk(ok, "All hooks for task %d called in correct order", (int)i); |
968 | + } |
969 | + return testDone(); |
970 | +} |
> /* As we're only ever inserting hooks at the head of the list, forward traversing is safe */
> pHook = (epicsThreadHook *) ellFirst(list);
It can be unlocked to traverse, but the mutex should be locked when fetching the head pointer.