Merge lp:~ralph-lange/epics-base/thread-hooks into lp:~epics-core/epics-base/3.15

Proposed by Ralph Lange on 2012-06-29
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
Reviewer Review Type Date Requested Status
Andrew Johnson 2012-06-29 Approve on 2012-07-04
Review via email: mp+112806@code.launchpad.net

Description of the change

Adds two interfaces for user hooks that are being called from epicsThread:

epicsThreadAddStartHook(func) and epicsThreadAddExitHook(func) each add a user function to an internal list of hooks that are being called from the thread context, immediately before or after each thread's main routine runs.
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(func) immediately calls the user supplied func for each currently existing thread.

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.

To post a comment you must log in.
mdavidsaver (mdavidsaver) 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.

Andrew Johnson (anj) wrote :

Should the AtThreadExits run before the ExitHooks? You have this in several places:
  + epicsThreadRunExitHooks(pthreadInfo);
    epicsExitCallAtThreadExits ();
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*Hooks() routines belong in the epicsWin32ThreadEntry() routine, and epicsThreadHooksInit() should be called towards the bottom of fetchWin32ThreadGlobal() after the atexit() call but before initCompleted gets set (there doesn't look to be anything in the mutex creation code that would cause problems there).

Then with some tests in libCom/test/epicsThreadTest.cpp and comments in the Release Notes I think this should be good to go. Eventually I'll want the API documenting in the AppDevGuide too, but not for this release.

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 on 2012-07-02

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 on 2012-07-02

libCom/osi: Make vxWorks implementation of epicsThreadMap() safe and dynamic

12308. By Ralph Lange on 2012-07-02

libCom/test: Add test for epicsThreadHooks

12309. By Ralph Lange on 2012-07-02

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 'epicsShowThreadInfo' implies this routine is part of the epicsShow 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 epicsThreadShowPrivate(), which matches the Win32 implementation (although there the routine is static).

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/osdThread.c before epicsThreadHooksInit() is run. The Init() routine cannot call any other epicsThread... routines such as epicsThreadGetIdSelf() because once() hasn't finished yet. Maybe epicsThreadHooksInit() could take main's epicsThreadId as an argument and (if non-zero) call the StartHook for it?

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 'epicsShowThreadInfo' implies this routine is part of the epicsShow
> 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
> epicsThreadShowPrivate(), which matches the Win32 implementation (although
> 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

epicsThreadPrivateCreate
epicsThreadPrivateDelete
epicsThreadPrivateSet
epicsThreadPrivateGet

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 on 2012-07-03

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 on 2012-07-03

libCom/osi: Don't decorate epicsThreadHook functions with epicsShareAPI

12312. By Ralph Lange on 2012-07-03

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 on 2012-07-03

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/osdThread.c before
> epicsThreadHooksInit() is run. The Init() routine cannot call any other
> epicsThread... routines such as epicsThreadGetIdSelf() because once() hasn't
> 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 on 2012-07-03

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 epicsThreadHookAdd(), and I added the test routine to epicsRunLibComTests().

I'll do a test run on vxWorks when I'm back at work tomorrow.

Thanks,

- Andrew

review: Approve

Preview Diff

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

Subscribers

People subscribed via source and target branches