In some cases the license-identification header was missing, so I added that as well. Replaced the remaining headers that specifically identified "Versions 3.13.7 and higher". Makefiles and the build system were deliberately excluded.
431 lines
10 KiB
C
431 lines
10 KiB
C
/*************************************************************************\
|
||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||
* National Laboratory.
|
||
* Copyright (c) 2002 The Regents of the University of California, as
|
||
* Operator of Los Alamos National Laboratory.
|
||
* SPDX-License-Identifier: EPICS
|
||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||
* in file LICENSE that is included with this distribution.
|
||
\*************************************************************************/
|
||
/* taskwd.c */
|
||
|
||
/* tasks and subroutines for a general purpose task watchdog */
|
||
/*
|
||
* Original Author: Marty Kraimer
|
||
* Date: 07-18-91
|
||
*/
|
||
|
||
#include <stddef.h>
|
||
#include <stdlib.h>
|
||
|
||
#include "cantProceed.h"
|
||
#include "dbDefs.h"
|
||
#include "epicsEvent.h"
|
||
#include "epicsExit.h"
|
||
#include "epicsStdioRedirect.h"
|
||
#include "epicsThread.h"
|
||
#include "epicsMutex.h"
|
||
#include "valgrind/valgrind.h"
|
||
#include "errlog.h"
|
||
#include "ellLib.h"
|
||
#include "errMdef.h"
|
||
#include "taskwd.h"
|
||
|
||
struct tNode {
|
||
ELLNODE node;
|
||
epicsThreadId tid;
|
||
TASKWDFUNC callback;
|
||
void *usr;
|
||
int suspended;
|
||
};
|
||
|
||
struct mNode {
|
||
ELLNODE node;
|
||
const taskwdMonitor *funcs;
|
||
void *usr;
|
||
};
|
||
|
||
struct aNode {
|
||
void *key;
|
||
TASKWDANYFUNC callback;
|
||
void *usr;
|
||
};
|
||
|
||
union twdNode {
|
||
struct tNode t;
|
||
struct mNode m;
|
||
struct aNode a;
|
||
};
|
||
|
||
/* Registered Tasks */
|
||
static epicsMutexId tLock;
|
||
static ELLLIST tList = ELLLIST_INIT;
|
||
|
||
/* Active Monitors */
|
||
static epicsMutexId mLock;
|
||
static ELLLIST mList = ELLLIST_INIT;
|
||
|
||
/* Free List */
|
||
static epicsMutexId fLock;
|
||
static ELLLIST fList = ELLLIST_INIT;
|
||
|
||
/* Watchdog task control */
|
||
static volatile enum {
|
||
twdctlInit, twdctlRun, twdctlDisable, twdctlExit
|
||
} twdCtl;
|
||
static epicsEventId loopEvent;
|
||
static epicsEventId exitEvent;
|
||
|
||
/* Task delay times (seconds) */
|
||
#define TASKWD_DELAY 6.0
|
||
|
||
|
||
/* forward definitions */
|
||
static union twdNode *allocNode(void);
|
||
static void freeNode(union twdNode *);
|
||
|
||
/* Initialization, lazy */
|
||
|
||
static void twdTask(void *arg)
|
||
{
|
||
struct tNode *pt;
|
||
struct mNode *pm;
|
||
|
||
while (twdCtl != twdctlExit) {
|
||
if (twdCtl == twdctlRun) {
|
||
epicsMutexMustLock(tLock);
|
||
pt = (struct tNode *)ellFirst(&tList);
|
||
while (pt) {
|
||
int susp = epicsThreadIsSuspended(pt->tid);
|
||
if (susp != pt->suspended) {
|
||
epicsMutexMustLock(mLock);
|
||
pm = (struct mNode *)ellFirst(&mList);
|
||
while (pm) {
|
||
if (pm->funcs->notify) {
|
||
pm->funcs->notify(pm->usr, pt->tid, susp);
|
||
}
|
||
pm = (struct mNode *)ellNext(&pm->node);
|
||
}
|
||
epicsMutexUnlock(mLock);
|
||
|
||
if (susp) {
|
||
char tName[40];
|
||
epicsThreadGetName(pt->tid, tName, sizeof(tName));
|
||
errlogPrintf("Thread %s (%p) suspended\n",
|
||
tName, (void *)pt->tid);
|
||
if (pt->callback) {
|
||
pt->callback(pt->usr);
|
||
}
|
||
}
|
||
pt->suspended = susp;
|
||
}
|
||
pt = (struct tNode *)ellNext(&pt->node);
|
||
}
|
||
epicsMutexUnlock(tLock);
|
||
}
|
||
epicsEventWaitWithTimeout(loopEvent, TASKWD_DELAY);
|
||
}
|
||
epicsEventSignal(exitEvent);
|
||
}
|
||
|
||
|
||
static void twdShutdown(void *arg)
|
||
{
|
||
ELLNODE *cur;
|
||
twdCtl = twdctlExit;
|
||
epicsEventSignal(loopEvent);
|
||
epicsEventWait(exitEvent);
|
||
while ((cur = ellGet(&fList)) != NULL) {
|
||
VALGRIND_MEMPOOL_FREE(&fList, cur);
|
||
free(cur);
|
||
}
|
||
VALGRIND_DESTROY_MEMPOOL(&fList);
|
||
}
|
||
|
||
static void twdInitOnce(void *arg)
|
||
{
|
||
epicsThreadId tid;
|
||
|
||
tLock = epicsMutexMustCreate();
|
||
mLock = epicsMutexMustCreate();
|
||
fLock = epicsMutexMustCreate();
|
||
ellInit(&fList);
|
||
VALGRIND_CREATE_MEMPOOL(&fList, 0, 0);
|
||
|
||
twdCtl = twdctlRun;
|
||
loopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||
exitEvent = epicsEventMustCreate(epicsEventEmpty);
|
||
|
||
tid = epicsThreadCreate("taskwd", epicsThreadPriorityLow,
|
||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||
twdTask, NULL);
|
||
if (tid == 0)
|
||
cantProceed("Failed to spawn task watchdog thread\n");
|
||
|
||
epicsAtExit(twdShutdown, NULL);
|
||
}
|
||
|
||
void taskwdInit(void)
|
||
{
|
||
static epicsThreadOnceId twdOnceFlag = EPICS_THREAD_ONCE_INIT;
|
||
epicsThreadOnce(&twdOnceFlag, twdInitOnce, NULL);
|
||
}
|
||
|
||
|
||
/* For tasks to be monitored */
|
||
|
||
void taskwdInsert(epicsThreadId tid, TASKWDFUNC callback, void *usr)
|
||
{
|
||
struct tNode *pt;
|
||
struct mNode *pm;
|
||
|
||
taskwdInit();
|
||
if (tid == 0)
|
||
tid = epicsThreadGetIdSelf();
|
||
|
||
pt = &allocNode()->t;
|
||
pt->tid = tid;
|
||
pt->callback = callback;
|
||
pt->usr = usr;
|
||
pt->suspended = FALSE;
|
||
|
||
epicsMutexMustLock(mLock);
|
||
pm = (struct mNode *)ellFirst(&mList);
|
||
while (pm) {
|
||
if (pm->funcs->insert) {
|
||
pm->funcs->insert(pm->usr, tid);
|
||
}
|
||
pm = (struct mNode *)ellNext(&pm->node);
|
||
}
|
||
epicsMutexUnlock(mLock);
|
||
|
||
epicsMutexMustLock(tLock);
|
||
ellAdd(&tList, (void *)pt);
|
||
epicsMutexUnlock(tLock);
|
||
}
|
||
|
||
void taskwdRemove(epicsThreadId tid)
|
||
{
|
||
struct tNode *pt;
|
||
struct mNode *pm;
|
||
char tName[40];
|
||
|
||
taskwdInit();
|
||
|
||
if (tid == 0)
|
||
tid = epicsThreadGetIdSelf();
|
||
|
||
epicsMutexMustLock(tLock);
|
||
pt = (struct tNode *)ellFirst(&tList);
|
||
while (pt != NULL) {
|
||
if (tid == pt->tid) {
|
||
ellDelete(&tList, (void *)pt);
|
||
epicsMutexUnlock(tLock);
|
||
freeNode((union twdNode *)pt);
|
||
|
||
epicsMutexMustLock(mLock);
|
||
pm = (struct mNode *)ellFirst(&mList);
|
||
while (pm) {
|
||
if (pm->funcs->remove) {
|
||
pm->funcs->remove(pm->usr, tid);
|
||
}
|
||
pm = (struct mNode *)ellNext(&pm->node);
|
||
}
|
||
epicsMutexUnlock(mLock);
|
||
return;
|
||
}
|
||
pt = (struct tNode *)ellNext(&pt->node);
|
||
}
|
||
epicsMutexUnlock(tLock);
|
||
|
||
epicsThreadGetName(tid, tName, sizeof(tName));
|
||
errlogPrintf("taskwdRemove: Thread %s (%p) not registered!\n",
|
||
tName, (void *)tid);
|
||
}
|
||
|
||
|
||
/* Monitoring API */
|
||
|
||
void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr)
|
||
{
|
||
struct mNode *pm;
|
||
|
||
if (funcs == NULL) return;
|
||
|
||
taskwdInit();
|
||
|
||
pm = &allocNode()->m;
|
||
pm->funcs = funcs;
|
||
pm->usr = usr;
|
||
|
||
epicsMutexMustLock(mLock);
|
||
ellAdd(&mList, (void *)pm);
|
||
epicsMutexUnlock(mLock);
|
||
}
|
||
|
||
void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr)
|
||
{
|
||
struct mNode *pm;
|
||
|
||
if (funcs == NULL) return;
|
||
|
||
taskwdInit();
|
||
|
||
epicsMutexMustLock(mLock);
|
||
pm = (struct mNode *)ellFirst(&mList);
|
||
while (pm) {
|
||
if (pm->funcs == funcs && pm->usr == usr) {
|
||
ellDelete(&mList, (void *)pm);
|
||
freeNode((union twdNode *)pm);
|
||
epicsMutexUnlock(mLock);
|
||
return;
|
||
}
|
||
pm = (struct mNode *)ellNext(&pm->node);
|
||
}
|
||
epicsMutexUnlock(mLock);
|
||
|
||
errlogPrintf("taskwdMonitorDel: Unregistered!\n");
|
||
}
|
||
|
||
|
||
/* Support old API for backwards compatibility */
|
||
|
||
static void anyNotify(void *usr, epicsThreadId tid, int suspended)
|
||
{
|
||
struct aNode *pa = (struct aNode *)usr;
|
||
|
||
if (suspended) {
|
||
pa->callback(pa->usr, tid);
|
||
}
|
||
}
|
||
|
||
static taskwdMonitor anyFuncs = {
|
||
NULL, anyNotify, NULL
|
||
};
|
||
|
||
void taskwdAnyInsert(void *key, TASKWDANYFUNC callback, void *usr)
|
||
{
|
||
struct mNode *pm;
|
||
struct aNode *pa;
|
||
|
||
if (callback == NULL) return;
|
||
|
||
taskwdInit();
|
||
|
||
pa = &allocNode()->a;
|
||
pa->key = key;
|
||
pa->callback = callback;
|
||
pa->usr = usr;
|
||
|
||
pm = &allocNode()->m;
|
||
pm->funcs = &anyFuncs;
|
||
pm->usr = pa;
|
||
|
||
epicsMutexMustLock(mLock);
|
||
ellAdd(&mList, (void *)pm);
|
||
epicsMutexUnlock(mLock);
|
||
}
|
||
|
||
void taskwdAnyRemove(void *key)
|
||
{
|
||
struct mNode *pm;
|
||
struct aNode *pa;
|
||
|
||
taskwdInit();
|
||
|
||
epicsMutexMustLock(mLock);
|
||
pm = (struct mNode *)ellFirst(&mList);
|
||
while (pm) {
|
||
if (pm->funcs == &anyFuncs) {
|
||
pa = (struct aNode *)pm->usr;
|
||
if (pa->key == key) {
|
||
ellDelete(&mList, (void *)pm);
|
||
freeNode((union twdNode *)pa);
|
||
freeNode((union twdNode *)pm);
|
||
epicsMutexUnlock(mLock);
|
||
return;
|
||
}
|
||
}
|
||
pm = (struct mNode *)ellNext(&pm->node);
|
||
}
|
||
epicsMutexUnlock(mLock);
|
||
|
||
errlogPrintf("taskwdAnyRemove: Unregistered key %p\n", key);
|
||
}
|
||
|
||
|
||
/* Report function */
|
||
|
||
LIBCOM_API void taskwdShow(int level)
|
||
{
|
||
struct tNode *pt;
|
||
int mCount, fCount, tCount;
|
||
char tName[40];
|
||
|
||
epicsMutexMustLock(mLock);
|
||
mCount = ellCount(&mList);
|
||
epicsMutexUnlock(mLock);
|
||
|
||
epicsMutexMustLock(fLock);
|
||
fCount = ellCount(&fList);
|
||
epicsMutexUnlock(fLock);
|
||
|
||
epicsMutexMustLock(tLock);
|
||
tCount = ellCount(&tList);
|
||
printf("%d monitors, %d threads registered, %d free nodes\n",
|
||
mCount, tCount, fCount);
|
||
if (level) {
|
||
printf("%16.16s %9s %12s %12s %12s\n",
|
||
"THREAD NAME", "STATE", "EPICS TID", "epicsCallback", "USR ARG");
|
||
pt = (struct tNode *)ellFirst(&tList);
|
||
while (pt != NULL) {
|
||
epicsThreadGetName(pt->tid, tName, sizeof(tName));
|
||
printf("%16.16s %9s %12p %12p %12p\n",
|
||
tName, pt->suspended ? "Suspended" : "Ok ",
|
||
(void *)pt->tid, (void *)pt->callback, pt->usr);
|
||
pt = (struct tNode *)ellNext(&pt->node);
|
||
}
|
||
}
|
||
epicsMutexUnlock(tLock);
|
||
}
|
||
|
||
|
||
/* Free list management */
|
||
|
||
static union twdNode *newNode(void)
|
||
{
|
||
union twdNode *pn;
|
||
|
||
epicsMutexMustLock(fLock);
|
||
pn = (union twdNode *)ellGet(&fList);
|
||
if (pn) {
|
||
VALGRIND_MEMPOOL_FREE(&fList, pn);
|
||
}
|
||
epicsMutexUnlock(fLock);
|
||
if (!pn)
|
||
pn = calloc(1, sizeof(union twdNode));
|
||
if (pn)
|
||
VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(*pn));
|
||
return pn;
|
||
}
|
||
|
||
static union twdNode *allocNode(void)
|
||
{
|
||
union twdNode *pn = newNode();
|
||
while (!pn) {
|
||
errlogPrintf("Thread taskwd suspending: out of memory\n");
|
||
epicsThreadSuspendSelf();
|
||
pn = newNode();
|
||
}
|
||
return pn;
|
||
}
|
||
|
||
static void freeNode(union twdNode *pn)
|
||
{
|
||
VALGRIND_MEMPOOL_FREE(&fList, pn);
|
||
VALGRIND_MEMPOOL_ALLOC(&fList, pn, sizeof(ELLNODE));
|
||
epicsMutexMustLock(fLock);
|
||
ellAdd(&fList, (void *)pn);
|
||
epicsMutexUnlock(fLock);
|
||
}
|