Files
epics-base/modules/libcom/src/taskwd/taskwd.c
Andrew Johnson 3c99391d93 Added SPDX License ID to all EPICS-original source files
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.
2020-08-03 11:53:01 -05:00

431 lines
10 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*************************************************************************\
* 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);
}