diff --git a/src/libCom/iocsh/libComRegister.c b/src/libCom/iocsh/libComRegister.c index 87acc4432..fc07eb5fb 100644 --- a/src/libCom/iocsh/libComRegister.c +++ b/src/libCom/iocsh/libComRegister.c @@ -20,6 +20,7 @@ #include "osiUnistd.h" #include "logClient.h" #include "errlog.h" +#include "taskwd.h" #include "epicsGeneralTime.h" #include "libComRegister.h" @@ -228,6 +229,16 @@ static void threadCallFunc(const iocshArgBuf *args) } } +/* taskwdShow */ +static const iocshArg taskwdShowArg0 = { "level",iocshArgInt}; +static const iocshArg * const taskwdShowArgs[1] = {&taskwdShowArg0}; +static const iocshFuncDef taskwdShowFuncDef = + {"taskwdShow",1,taskwdShowArgs}; +static void taskwdShowCallFunc(const iocshArgBuf *args) +{ + taskwdShow(args[0].ival); +} + /* epicsMutexShowAll */ static const iocshArg epicsMutexShowAllArg0 = { "onlyLocked",iocshArgInt}; static const iocshArg epicsMutexShowAllArg1 = { "level",iocshArgInt}; @@ -328,6 +339,7 @@ void epicsShareAPI libComRegister(void) iocshRegister(&epicsThreadShowAllFuncDef,epicsThreadShowAllCallFunc); iocshRegister(&threadFuncDef, threadCallFunc); + iocshRegister(&taskwdShowFuncDef,taskwdShowCallFunc); iocshRegister(&epicsMutexShowAllFuncDef,epicsMutexShowAllCallFunc); iocshRegister(&epicsThreadSleepFuncDef,epicsThreadSleepCallFunc); iocshRegister(&epicsThreadResumeFuncDef,epicsThreadResumeCallFunc); diff --git a/src/libCom/taskwd/taskwd.c b/src/libCom/taskwd/taskwd.c index 1fdc2992b..16f75e751 100644 --- a/src/libCom/taskwd/taskwd.c +++ b/src/libCom/taskwd/taskwd.c @@ -1,14 +1,13 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* 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. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* taskwd.c */ -/* share/src/db @(#)taskwd.c 1.7 7/11/94 */ + /* tasks and subroutines for a general purpose task watchdog */ /* * Original Author: Marty Kraimer @@ -17,10 +16,12 @@ #include #include -#include - + #define epicsExportSharedSymbols #include "dbDefs.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsStdio.h" #include "epicsThread.h" #include "epicsMutex.h" #include "errlog.h" @@ -28,206 +29,385 @@ #include "errMdef.h" #include "taskwd.h" -typedef void (*MYFUNCPTR)(); - -struct task_list { - ELLNODE node; - MYFUNCPTR callback; - void *arg; - union { - epicsThreadId tid; - void *userpvt; - } id; +struct tNode { + ELLNODE node; + epicsThreadId tid; + TASKWDFUNC callback; + void *usr; int suspended; }; -static ELLLIST list; -static ELLLIST anylist; -static epicsMutexId lock; -static epicsMutexId anylock; -static epicsMutexId alloclock; -static epicsThreadId taskwdid=0; -volatile int taskwdOn=TRUE; -struct freeList{ - struct freeList *next; +struct mNode { + ELLNODE node; + const taskwdMonitor *funcs; + void *usr; }; -static struct freeList *freeHead=NULL; + +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; + +/* Active Monitors */ +static epicsMutexId mLock; +static ELLLIST mList; + +/* Free List */ +static epicsMutexId fLock; +static ELLLIST fList; + +/* Watchdog task control */ +static enum { + twdctlRun, twdctlDisable, twdctlExit +} twdCtl; +static epicsEventId loopEvent; +static epicsEventId exitEvent; + /* Task delay times (seconds) */ #define TASKWD_DELAY 6.0 -/*forward definitions*/ -static void taskwdTask(void); -static struct task_list *allocList(void); -static void freeList(struct task_list *pt); -static void taskwdInitPvt(void *); + +/* forward definitions */ +static union twdNode *allocNode(void); +static void freeNode(union twdNode *); -static void taskwdInitPvt(void *arg) +/* Initialization, lazy */ + +static void twdTask(void) { - lock = epicsMutexMustCreate(); - anylock = epicsMutexMustCreate(); - alloclock = epicsMutexMustCreate(); - ellInit(&list); - ellInit(&anylist); - taskwdid = epicsThreadCreate( - "taskwd",epicsThreadPriorityLow, - epicsThreadGetStackSize(epicsThreadStackSmall), - (EPICSTHREADFUNC)taskwdTask,0); -} -void epicsShareAPI taskwdInit() -{ - static epicsThreadOnceId taskwdOnceFlag = EPICS_THREAD_ONCE_INIT; - void *arg = 0; - epicsThreadOnce(&taskwdOnceFlag,taskwdInitPvt,arg); -} + struct tNode *pt; + struct mNode *pm; -void epicsShareAPI taskwdInsert(epicsThreadId tid,TASKWDFUNCPRR callback,void *arg) -{ - struct task_list *pt; - - taskwdInit(); - if(tid==0) { - errlogPrintf("taskwdInsert called with null tid\n"); - return; - } - epicsMutexMustLock(lock); - pt = allocList(); - ellAdd(&list,(void *)pt); - pt->suspended = FALSE; - pt->id.tid = tid; - pt->callback = callback; - pt->arg = arg; - epicsMutexUnlock(lock); -} - -void epicsShareAPI taskwdAnyInsert(void *userpvt,TASKWDANYFUNCPRR callback,void *arg) -{ - struct task_list *pt; - - taskwdInit(); - epicsMutexMustLock(anylock); - pt = allocList(); - ellAdd(&anylist,(void *)pt); - pt->id.userpvt = userpvt; - pt->callback = callback; - pt->arg = arg; - epicsMutexUnlock(anylock); -} - -void epicsShareAPI taskwdRemove(epicsThreadId tid) -{ - struct task_list *pt; - char thrName[40]; - - taskwdInit(); - epicsMutexMustLock(lock); - pt = (struct task_list *)ellFirst(&list); - while(pt!=NULL) { - if (tid == pt->id.tid) { - ellDelete(&list,(void *)pt); - freeList(pt); - epicsMutexUnlock(lock); - return; - } - pt = (struct task_list *)ellNext(&pt->node); - } - epicsMutexUnlock(lock); - epicsThreadGetName(tid, thrName, 40); - errlogPrintf("taskwdRemove: Thread %s (%p) not registered!\n", - thrName, tid); -} - -void epicsShareAPI taskwdAnyRemove(void *userpvt) -{ - struct task_list *pt; - - taskwdInit(); - epicsMutexMustLock(anylock); - pt = (struct task_list *)ellFirst(&anylist); - while(pt!=NULL) { - if (userpvt == pt->id.userpvt) { - ellDelete(&anylist,(void *)pt); - freeList(pt); - epicsMutexUnlock(anylock); - return; - } - pt = (struct task_list *)ellNext(&pt->node); - } - epicsMutexUnlock(anylock); - errlogPrintf("taskwdAnyRemove: Userpvt %p not registered!\n", userpvt); -} - -static void taskwdTask(void) -{ - struct task_list *pt,*next; - - while(TRUE) { - if(taskwdOn) { - epicsMutexMustLock(lock); - pt = (struct task_list *)ellFirst(&list); - while(pt) { - next = (struct task_list *)ellNext(&pt->node); - if(epicsThreadIsSuspended(pt->id.tid)) { - if(!pt->suspended) { - struct task_list *ptany; - char thrName[40]; - - pt->suspended = TRUE; - epicsThreadGetName(pt->id.tid, thrName, 40); - errlogPrintf("Thread %s (%p) suspended\n", - thrName, (void *)pt->id.tid); - ptany = (struct task_list *)ellFirst(&anylist); - while(ptany) { - if(ptany->callback) { - TASKWDANYFUNCPRR pcallback = pt->callback; - (pcallback)(ptany->arg,pt->id.tid); - } - ptany = (struct task_list *)ellNext(&ptany->node); + 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); } - if(pt->callback) { - TASKWDFUNCPRR pcallback = pt->callback; - void *arg = pt->arg; + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); - /*Must allow callback to call taskwdRemove*/ - epicsMutexUnlock(lock); - (pcallback)(arg); - /*skip rest because we have unlocked*/ - break; + 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); } } - } else { - pt->suspended = FALSE; + pt->suspended = susp; } - pt = next; + pt = (struct tNode *)ellNext(&pt->node); } - epicsMutexUnlock(lock); + epicsMutexUnlock(tLock); } - epicsThreadSleep(TASKWD_DELAY); + epicsEventWaitWithTimeout(loopEvent, TASKWD_DELAY); + } + epicsEventSignal(exitEvent); +} + + +static void twdShutdown(void *arg) +{ + twdCtl = twdctlExit; + epicsEventSignal(loopEvent); + epicsEventWait(exitEvent); +} + +static void twdInitPvt(void *arg) +{ + tLock = epicsMutexMustCreate(); + ellInit(&tList); + + mLock = epicsMutexMustCreate(); + ellInit(&mList); + + fLock = epicsMutexMustCreate(); + ellInit(&fList); + + twdCtl = twdctlRun; + loopEvent = epicsEventMustCreate(epicsEventEmpty); + exitEvent = epicsEventMustCreate(epicsEventEmpty); + + epicsThreadCreate("taskwd", epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackSmall), + (EPICSTHREADFUNC)twdTask, 0); + epicsAtExit(twdShutdown, NULL); +} + +void taskwdInit() +{ + static epicsThreadOnceId twdOnceFlag = EPICS_THREAD_ONCE_INIT; + epicsThreadOnce(&twdOnceFlag, twdInitPvt, 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, 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 struct task_list *allocList(void) -{ - struct task_list *pt; - epicsMutexMustLock(alloclock); - if(freeHead) { - pt = (struct task_list *)freeHead; - freeHead = freeHead->next; - } else pt = calloc(1,sizeof(struct task_list)); - while (pt==NULL) { +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 */ + +epicsShareFunc 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", "CALLBACK", "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 ", + pt->tid, pt->callback, pt->usr); + pt = (struct tNode *)ellNext(&pt->node); + } + } + epicsMutexUnlock(tLock); +} + +static union twdNode *newNode(void) +{ + union twdNode *pn; + + epicsMutexMustLock(fLock); + pn = (union twdNode *)ellFirst(&fList); + if (pn) { + ellDelete(&fList, (void *)pn); + epicsMutexUnlock(fLock); + return pn; + } + epicsMutexUnlock(fLock); + return calloc(1, sizeof(union twdNode)); +} + +static union twdNode *allocNode(void) +{ + union twdNode *pn = newNode(); + while (!pn) { errlogPrintf("Thread taskwd suspending: out of memory\n"); epicsThreadSuspendSelf(); - pt = calloc(1,sizeof(struct task_list)); + pn = newNode(); } - epicsMutexUnlock(alloclock); - return(pt); + return pn; } -static void freeList(struct task_list *pt) +static void freeNode(union twdNode *pn) { - - epicsMutexMustLock(alloclock); - ((struct freeList *)pt)->next = freeHead; - freeHead = (struct freeList *)pt; - epicsMutexUnlock(alloclock); + epicsMutexMustLock(fLock); + ellAdd(&fList, (void *)pn); + epicsMutexUnlock(fLock); } diff --git a/src/libCom/taskwd/taskwd.h b/src/libCom/taskwd/taskwd.h index 7187b0e83..4d3ac9436 100644 --- a/src/libCom/taskwd/taskwd.h +++ b/src/libCom/taskwd/taskwd.h @@ -1,45 +1,68 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* 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. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* taskwd.h */ -/* share/epicsH/taskwd.h $Id$ */ -/* includes for general purpose taskwd tasks */ +/* $Id$ */ + +/* General purpose task watchdog */ /* * Original Author: Marty Kraimer * Date: 07-18-91 */ - -#ifndef INCtaskwdh -#define INCtaskwdh 1 +#ifndef INC_taskwd_H +#define INC_taskwd_H #include "epicsThread.h" #include "shareLib.h" -typedef void (*TASKWDFUNCPRR)(void *parm); -typedef void (*TASKWDANYFUNCPRR)(void *parm,epicsThreadId tid); - #ifdef __cplusplus extern "C" { #endif -epicsShareFunc void epicsShareAPI taskwdInit(void); -epicsShareFunc void epicsShareAPI taskwdInsert( - epicsThreadId tid, TASKWDFUNCPRR callback,void *arg); -epicsShareFunc void epicsShareAPI taskwdAnyInsert( - void *userpvt, TASKWDANYFUNCPRR callback,void *arg); -epicsShareFunc void epicsShareAPI taskwdRemove(epicsThreadId tid); -epicsShareFunc void epicsShareAPI taskwdAnyRemove(void *userpvt); + +/* Initialization, optional */ +epicsShareFunc void taskwdInit(void); + + +/* For tasks to be monitored */ +typedef void (*TASKWDFUNC)(void *usr); + +epicsShareFunc void taskwdInsert(epicsThreadId tid, + TASKWDFUNC callback, void *usr); +epicsShareFunc void taskwdRemove(epicsThreadId tid); + + +/* Monitoring API */ +typedef struct { + void (*insert)(void *usr, epicsThreadId tid); + void (*notify)(void *usr, epicsThreadId tid, int suspended); + void (*remove)(void *usr, epicsThreadId tid); +} taskwdMonitor; + +epicsShareFunc void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr); +epicsShareFunc void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr); + + +/* Old monitoring API, deprecated */ +typedef void (*TASKWDANYFUNC)(void *usr, epicsThreadId tid); + +epicsShareFunc void taskwdAnyInsert(void *key, + TASKWDANYFUNC callback, void *usr); +epicsShareFunc void taskwdAnyRemove(void *key); + + +/* Report function */ +epicsShareFunc void taskwdShow(int level); + #ifdef __cplusplus } #endif -#endif /*INCtaskwdh*/ +#endif /* INC_taskwd_H */ diff --git a/src/libCom/test/Makefile b/src/libCom/test/Makefile index e644a978d..567a27ab4 100644 --- a/src/libCom/test/Makefile +++ b/src/libCom/test/Makefile @@ -107,6 +107,11 @@ macLibTest_SRCS += macLibTest.c testHarness_SRCS += macLibTest.c TESTS += macLibTest +TESTPROD_HOST += taskwdTest +taskwdTest_SRCS += taskwdTest.c +testHarness_SRCS += taskwdTest.c +TESTS += taskwdTest + TESTPROD_HOST += blockingSockTest blockingSockTest_SRCS += blockingSockTest.cpp testHarness_SRCS += blockingSockTest.cpp diff --git a/src/libCom/test/taskwdTest.c b/src/libCom/test/taskwdTest.c new file mode 100644 index 000000000..d0156797c --- /dev/null +++ b/src/libCom/test/taskwdTest.c @@ -0,0 +1,92 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* $Id$ */ + +/* Author: Andrew Johnson + * Date: May 21, 2008 + */ + +#include + +#include "taskwd.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +void monInsert(void *usr, epicsThreadId tid) +{ + testPass("monInsert(tid=%p)", (void *)tid); +} + +void monNotify(void *usr, epicsThreadId tid, int suspended) +{ + testPass("monNotify(tid=%p, suspended=%d)", (void *)tid, suspended); + epicsThreadResume(tid); +} + +void monRemove(void *usr, epicsThreadId tid) +{ + testPass("monRemove(tid=%p)", (void *)tid); +} + +taskwdMonitor monitor = {monInsert, monNotify, monRemove}; + +void anyNotify(void *usr, epicsThreadId tid) +{ + testPass("anyNotify(tid=%p)", (void *)tid); +} + +void taskNotify(void *usr) +{ + testPass("taskNotify"); +} + +void testTask1(void *arg) +{ + taskwdInsert(0, taskNotify, NULL); + epicsThreadSleep(10.0); + taskwdRemove(0); +} + +void testTask2(void *arg) +{ + taskwdInsert(0, taskNotify, NULL); + testDiag("Task suspending"); + epicsThreadSuspendSelf(); + epicsThreadSleep(1.0); + testDiag("Alive again"); + epicsThreadSleep(10.0); + taskwdRemove(0); +} + +MAIN(epicsThreadPrivateTest) +{ + testPlan(8); + + taskwdInit(); + taskwdMonitorAdd(&monitor, NULL); + taskwdAnyInsert(NULL, anyNotify, NULL); + + epicsThreadCreate("testTask1", epicsThreadPriorityMax, + epicsThreadGetStackSize(epicsThreadStackSmall), + testTask1, NULL); + epicsThreadSleep(1.0); + + epicsThreadCreate("testTask2", epicsThreadPriorityMax, + epicsThreadGetStackSize(epicsThreadStackSmall), + testTask2, NULL); + + /* taskwd checks tasks every 6 seconds */ + epicsThreadSleep(18.0); + + taskwdMonitorDel(&monitor, NULL); + taskwdAnyRemove(NULL); + + return testDone(); +} +