This is Unofficial EPICS BASE Doxygen Site
taskwd.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2002 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * EPICS BASE is distributed subject to a Software License Agreement found
7 * in file LICENSE that is included with this distribution.
8 \*************************************************************************/
9 /* taskwd.c */
10 
11 /* tasks and subroutines for a general purpose task watchdog */
12 /*
13  * Original Author: Marty Kraimer
14  * Date: 07-18-91
15 */
16 
17 #include <stddef.h>
18 #include <stdlib.h>
19 
20 #include "cantProceed.h"
21 #include "dbDefs.h"
22 #include "epicsEvent.h"
23 #include "epicsExit.h"
24 #include "epicsStdioRedirect.h"
25 #include "epicsThread.h"
26 #include "epicsMutex.h"
27 #include "valgrind/valgrind.h"
28 #include "errlog.h"
29 #include "ellLib.h"
30 #include "errMdef.h"
31 #include "taskwd.h"
32 
33 struct tNode {
37  void *usr;
38  int suspended;
39 };
40 
41 struct mNode {
44  void *usr;
45 };
46 
47 struct aNode {
48  void *key;
50  void *usr;
51 };
52 
53 union twdNode {
54  struct tNode t;
55  struct mNode m;
56  struct aNode a;
57 };
58 
59 /* Registered Tasks */
60 static epicsMutexId tLock;
61 static ELLLIST tList = ELLLIST_INIT;
62 
63 /* Active Monitors */
64 static epicsMutexId mLock;
65 static ELLLIST mList = ELLLIST_INIT;
66 
67 /* Free List */
68 static epicsMutexId fLock;
69 static ELLLIST fList = ELLLIST_INIT;
70 
71 /* Watchdog task control */
72 static volatile enum {
74 } twdCtl;
75 static epicsEventId loopEvent;
76 static epicsEventId exitEvent;
77 
78 /* Task delay times (seconds) */
79 #define TASKWD_DELAY 6.0
80 
81 
82 /* forward definitions */
83 static union twdNode *allocNode(void);
84 static void freeNode(union twdNode *);
85 
86 /* Initialization, lazy */
87 
88 static void twdTask(void *arg)
89 {
90  struct tNode *pt;
91  struct mNode *pm;
92 
93  while (twdCtl != twdctlExit) {
94  if (twdCtl == twdctlRun) {
95  epicsMutexMustLock(tLock);
96  pt = (struct tNode *)ellFirst(&tList);
97  while (pt) {
98  int susp = epicsThreadIsSuspended(pt->tid);
99  if (susp != pt->suspended) {
100  epicsMutexMustLock(mLock);
101  pm = (struct mNode *)ellFirst(&mList);
102  while (pm) {
103  if (pm->funcs->notify) {
104  pm->funcs->notify(pm->usr, pt->tid, susp);
105  }
106  pm = (struct mNode *)ellNext(&pm->node);
107  }
108  epicsMutexUnlock(mLock);
109 
110  if (susp) {
111  char tName[40];
112  epicsThreadGetName(pt->tid, tName, sizeof(tName));
113  errlogPrintf("Thread %s (%p) suspended\n",
114  tName, (void *)pt->tid);
115  if (pt->callback) {
116  pt->callback(pt->usr);
117  }
118  }
119  pt->suspended = susp;
120  }
121  pt = (struct tNode *)ellNext(&pt->node);
122  }
123  epicsMutexUnlock(tLock);
124  }
126  }
127  epicsEventSignal(exitEvent);
128 }
129 
130 
131 static void twdShutdown(void *arg)
132 {
133  ELLNODE *cur;
134  twdCtl = twdctlExit;
135  epicsEventSignal(loopEvent);
136  epicsEventWait(exitEvent);
137  while ((cur = ellGet(&fList)) != NULL) {
138  VALGRIND_MEMPOOL_FREE(&fList, cur);
139  free(cur);
140  }
141  VALGRIND_DESTROY_MEMPOOL(&fList);
142 }
143 
144 static void twdInitOnce(void *arg)
145 {
147 
148  tLock = epicsMutexMustCreate();
149  mLock = epicsMutexMustCreate();
150  fLock = epicsMutexMustCreate();
151  ellInit(&fList);
152  VALGRIND_CREATE_MEMPOOL(&fList, 0, 0);
153 
154  twdCtl = twdctlRun;
157 
160  twdTask, NULL);
161  if (tid == 0)
162  cantProceed("Failed to spawn task watchdog thread\n");
163 
164  epicsAtExit(twdShutdown, NULL);
165 }
166 
167 void taskwdInit(void)
168 {
169  static epicsThreadOnceId twdOnceFlag = EPICS_THREAD_ONCE_INIT;
170  epicsThreadOnce(&twdOnceFlag, twdInitOnce, NULL);
171 }
172 
173 
174 /* For tasks to be monitored */
175 
177 {
178  struct tNode *pt;
179  struct mNode *pm;
180 
181  taskwdInit();
182  if (tid == 0)
183  tid = epicsThreadGetIdSelf();
184 
185  pt = &allocNode()->t;
186  pt->tid = tid;
187  pt->callback = callback;
188  pt->usr = usr;
189  pt->suspended = FALSE;
190 
191  epicsMutexMustLock(mLock);
192  pm = (struct mNode *)ellFirst(&mList);
193  while (pm) {
194  if (pm->funcs->insert) {
195  pm->funcs->insert(pm->usr, tid);
196  }
197  pm = (struct mNode *)ellNext(&pm->node);
198  }
199  epicsMutexUnlock(mLock);
200 
201  epicsMutexMustLock(tLock);
202  ellAdd(&tList, (void *)pt);
203  epicsMutexUnlock(tLock);
204 }
205 
207 {
208  struct tNode *pt;
209  struct mNode *pm;
210  char tName[40];
211 
212  taskwdInit();
213 
214  if (tid == 0)
215  tid = epicsThreadGetIdSelf();
216 
217  epicsMutexMustLock(tLock);
218  pt = (struct tNode *)ellFirst(&tList);
219  while (pt != NULL) {
220  if (tid == pt->tid) {
221  ellDelete(&tList, (void *)pt);
222  epicsMutexUnlock(tLock);
223  freeNode((union twdNode *)pt);
224 
225  epicsMutexMustLock(mLock);
226  pm = (struct mNode *)ellFirst(&mList);
227  while (pm) {
228  if (pm->funcs->remove) {
229  pm->funcs->remove(pm->usr, tid);
230  }
231  pm = (struct mNode *)ellNext(&pm->node);
232  }
233  epicsMutexUnlock(mLock);
234  return;
235  }
236  pt = (struct tNode *)ellNext(&pt->node);
237  }
238  epicsMutexUnlock(tLock);
239 
240  epicsThreadGetName(tid, tName, sizeof(tName));
241  errlogPrintf("taskwdRemove: Thread %s (%p) not registered!\n",
242  tName, (void *)tid);
243 }
244 
245 
246 /* Monitoring API */
247 
248 void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr)
249 {
250  struct mNode *pm;
251 
252  if (funcs == NULL) return;
253 
254  taskwdInit();
255 
256  pm = &allocNode()->m;
257  pm->funcs = funcs;
258  pm->usr = usr;
259 
260  epicsMutexMustLock(mLock);
261  ellAdd(&mList, (void *)pm);
262  epicsMutexUnlock(mLock);
263 }
264 
266 {
267  struct mNode *pm;
268 
269  if (funcs == NULL) return;
270 
271  taskwdInit();
272 
273  epicsMutexMustLock(mLock);
274  pm = (struct mNode *)ellFirst(&mList);
275  while (pm) {
276  if (pm->funcs == funcs && pm->usr == usr) {
277  ellDelete(&mList, (void *)pm);
278  freeNode((union twdNode *)pm);
279  epicsMutexUnlock(mLock);
280  return;
281  }
282  pm = (struct mNode *)ellNext(&pm->node);
283  }
284  epicsMutexUnlock(mLock);
285 
286  errlogPrintf("taskwdMonitorDel: Unregistered!\n");
287 }
288 
289 
290 /* Support old API for backwards compatibility */
291 
292 static void anyNotify(void *usr, epicsThreadId tid, int suspended)
293 {
294  struct aNode *pa = (struct aNode *)usr;
295 
296  if (suspended) {
297  pa->callback(pa->usr, tid);
298  }
299 }
300 
301 static taskwdMonitor anyFuncs = {
302  NULL, anyNotify, NULL
303 };
304 
305 void taskwdAnyInsert(void *key, TASKWDANYFUNC callback, void *usr)
306 {
307  struct mNode *pm;
308  struct aNode *pa;
309 
310  if (callback == NULL) return;
311 
312  taskwdInit();
313 
314  pa = &allocNode()->a;
315  pa->key = key;
316  pa->callback = callback;
317  pa->usr = usr;
318 
319  pm = &allocNode()->m;
320  pm->funcs = &anyFuncs;
321  pm->usr = pa;
322 
323  epicsMutexMustLock(mLock);
324  ellAdd(&mList, (void *)pm);
325  epicsMutexUnlock(mLock);
326 }
327 
328 void taskwdAnyRemove(void *key)
329 {
330  struct mNode *pm;
331  struct aNode *pa;
332 
333  taskwdInit();
334 
335  epicsMutexMustLock(mLock);
336  pm = (struct mNode *)ellFirst(&mList);
337  while (pm) {
338  if (pm->funcs == &anyFuncs) {
339  pa = (struct aNode *)pm->usr;
340  if (pa->key == key) {
341  ellDelete(&mList, (void *)pm);
342  freeNode((union twdNode *)pa);
343  freeNode((union twdNode *)pm);
344  epicsMutexUnlock(mLock);
345  return;
346  }
347  }
348  pm = (struct mNode *)ellNext(&pm->node);
349  }
350  epicsMutexUnlock(mLock);
351 
352  errlogPrintf("taskwdAnyRemove: Unregistered key %p\n", key);
353 }
354 
355 
356 /* Report function */
357 
358 LIBCOM_API void taskwdShow(int level)
359 {
360  struct tNode *pt;
361  int mCount, fCount, tCount;
362  char tName[40];
363 
364  epicsMutexMustLock(mLock);
365  mCount = ellCount(&mList);
366  epicsMutexUnlock(mLock);
367 
368  epicsMutexMustLock(fLock);
369  fCount = ellCount(&fList);
370  epicsMutexUnlock(fLock);
371 
372  epicsMutexMustLock(tLock);
373  tCount = ellCount(&tList);
374  printf("%d monitors, %d threads registered, %d free nodes\n",
375  mCount, tCount, fCount);
376  if (level) {
377  printf("%16.16s %9s %12s %12s %12s\n",
378  "THREAD NAME", "STATE", "EPICS TID", "epicsCallback", "USR ARG");
379  pt = (struct tNode *)ellFirst(&tList);
380  while (pt != NULL) {
381  epicsThreadGetName(pt->tid, tName, sizeof(tName));
382  printf("%16.16s %9s %12p %12p %12p\n",
383  tName, pt->suspended ? "Suspended" : "Ok ",
384  (void *)pt->tid, (void *)pt->callback, pt->usr);
385  pt = (struct tNode *)ellNext(&pt->node);
386  }
387  }
388  epicsMutexUnlock(tLock);
389 }
390 
391 
392 /* Free list management */
393 
394 static union twdNode *newNode(void)
395 {
396  union twdNode *pn;
397 
398  epicsMutexMustLock(fLock);
399  pn = (union twdNode *)ellGet(&fList);
400  if (pn) {
401  VALGRIND_MEMPOOL_FREE(&fList, pn);
402  }
403  epicsMutexUnlock(fLock);
404  if (!pn)
405  pn = calloc(1, sizeof(union twdNode));
406  if (pn)
407  VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(*pn));
408  return pn;
409 }
410 
411 static union twdNode *allocNode(void)
412 {
413  union twdNode *pn = newNode();
414  while (!pn) {
415  errlogPrintf("Thread taskwd suspending: out of memory\n");
417  pn = newNode();
418  }
419  return pn;
420 }
421 
422 static void freeNode(union twdNode *pn)
423 {
424  VALGRIND_MEMPOOL_FREE(&fList, pn);
425  VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(ELLNODE));
426  epicsMutexMustLock(fLock);
427  ellAdd(&fList, (void *)pn);
428  epicsMutexUnlock(fLock);
429 }
void taskwdInsert(epicsThreadId tid, TASKWDFUNC callback, void *usr)
Definition: taskwd.c:176
void taskwdAnyInsert(void *key, TASKWDANYFUNC callback, void *usr)
Definition: taskwd.c:305
#define FALSE
Definition: dbDefs.h:32
Definition: taskwd.c:33
void(* insert)(void *usr, epicsThreadId tid)
Definition: taskwd.h:41
#define ellCount(PLIST)
Report the number of nodes in a list.
Definition: ellLib.h:84
ELLNODE node
Definition: taskwd.c:42
struct aNode a
Definition: taskwd.c:56
#define printf
Definition: epicsStdio.h:41
#define epicsEventWait(ID)
Definition: osdEvent.h:19
ELLNODE * ellGet(ELLLIST *pList)
Deletes and returns the first node from a list.
Definition: ellLib.c:147
#define epicsMutexMustCreate()
Create an epicsMutex semaphore for use from C code.
Definition: epicsMutex.h:179
void(* notify)(void *usr, epicsThreadId tid, int suspended)
Definition: taskwd.h:42
#define NULL
Definition: catime.c:38
#define TASKWD_DELAY
Definition: taskwd.c:79
void * usr
Definition: taskwd.c:37
LIBCOM_API epicsEventStatus epicsEventWaitWithTimeout(epicsEventId id, double timeOut)
Wait an the event or until the specified timeout period is over.
Definition: osdEvent.c:117
int suspended
Definition: taskwd.c:38
#define ELLLIST_INIT
Value of an empty list.
Definition: ellLib.h:63
LIBCOM_API epicsEventId epicsEventMustCreate(epicsEventInitialState initialState)
Create an epicsEvent for use from C code.
Definition: epicsEvent.cpp:106
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
Release the semaphore.
Definition: epicsMutex.cpp:140
Miscellaneous macro definitions.
LIBCOM_API unsigned int epicsStdCall epicsThreadGetStackSize(epicsThreadStackSizeClass size)
Definition: osdThread.c:466
void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr)
Definition: taskwd.c:248
epicsThreadId epicsStdCall epicsThreadCreate(const char *name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr, void *parm)
Definition: epicsThread.cpp:33
const taskwdMonitor * funcs
Definition: taskwd.c:43
#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed)
Definition: valgrind.h:6470
struct tNode t
Definition: taskwd.c:54
Definition: taskwd.c:47
A doubly-linked list library.
void ellAdd(ELLLIST *pList, ELLNODE *pNode)
Adds a node to the end of a list.
Definition: ellLib.c:24
#define ellNext(PNODE)
Find the next node in list.
Definition: ellLib.h:99
epicsEventId loopEvent
Definition: osiClockTime.c:32
#define EPICS_THREAD_ONCE_INIT
Definition: epicsThread.h:109
LIBCOM_API void epicsStdCall epicsThreadSuspendSelf(void)
Definition: osdThread.c:664
#define epicsEventSignal(ID)
A synonym for epicsEventTrigger().
Definition: epicsEvent.h:172
#define VALGRIND_DESTROY_MEMPOOL(pool)
Definition: valgrind.h:6475
void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr)
Definition: taskwd.c:265
Definition: taskwd.c:41
APIs for the epicsMutex mutual exclusion semaphore.
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg)
void * key
Definition: taskwd.c:48
void(* remove)(void *usr, epicsThreadId tid)
Definition: taskwd.h:43
#define epicsThreadPriorityLow
Definition: epicsThread.h:75
Extended replacement for the Posix exit and atexit routines.
void * usr
Definition: taskwd.c:50
LIBCOM_API void epicsStdCall epicsThreadGetName(epicsThreadId id, char *name, size_t size)
Definition: osdThread.c:857
#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size)
Definition: valgrind.h:6480
TASKWDFUNC callback
Definition: taskwd.c:36
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
List node type.
Definition: ellLib.h:45
APIs for the epicsEvent binary semaphore.
TASKWDANYFUNC callback
Definition: taskwd.c:49
void * usr
Definition: taskwd.c:44
void taskwdRemove(epicsThreadId tid)
Definition: taskwd.c:206
Definition: taskwd.c:53
ELLNODE node
Definition: taskwd.c:34
void taskwdInit(void)
Definition: taskwd.c:167
#define ellInit(PLIST)
Initialize a list type.
Definition: ellLib.h:76
void taskwdAnyRemove(void *key)
Definition: taskwd.c:328
LIBCOM_API void cantProceed(const char *msg,...)
Definition: cantProceed.c:54
void(* TASKWDANYFUNC)(void *usr, epicsThreadId tid)
Definition: taskwd.h:51
if(yy_init)
Definition: scan.c:972
LIBCOM_API int epicsStdCall epicsThreadIsSuspended(epicsThreadId id)
Definition: osdThread.c:784
Routines for code that can&#39;t continue or return after an error.
void(* TASKWDFUNC)(void *usr)
Definition: taskwd.h:32
#define VALGRIND_MEMPOOL_FREE(pool, addr)
Definition: valgrind.h:6485
#define epicsAtExit(F, A)
Convenience macro to register a function and context value to be run when the process exits...
Definition: epicsExit.h:70
List header type.
Definition: ellLib.h:56
struct mNode m
Definition: taskwd.c:55
C++ and C descriptions for a thread.
#define epicsMutexMustLock(ID)
Claim a semaphore (see epicsMutexLock()).
Definition: epicsMutex.h:214
void ellDelete(ELLLIST *pList, ELLNODE *pNode)
Deletes a node from a list.
Definition: ellLib.c:75
epicsThreadId tid
Definition: taskwd.c:35
LIBCOM_API void taskwdShow(int level)
Definition: taskwd.c:358
#define ellFirst(PLIST)
Find the first node in list.
Definition: ellLib.h:89
LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetIdSelf(void)
Definition: osdThread.c:810