The watchdog API is typically used to monitor the progress of an application. In the following example, a thread is dedicated to the watchdog.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <util/chKnTimeVal.h>
#include <sched/chSched.h>
#include <exec/chExec.h>
#include <sync/chSem.h>
#include <cx/wdt.h>
#define WDT_RUNS 10 /* number of times the test is run */
#define WDT_PAT_TIMES 3 /* number of times the timer
* is patted at each interval
*/
#define WDT_PAT_INC 10 /* timeout interval increment */
#define WDT_PAT_MIN 5 /* initial, minimum timeout interval */
#define WDT_STACKSIZE 1000 /* worker thread stack size */
#define WDT_THREAD_PRIO 3 /* worker thread (very high) priority */
#define WDT_EXTRA_SLEEP 10 /* extra delay to check that the timer
* is truly disarmed
*/
wdt_handle_t wdt_handle = 0; /* watchdog timer handle */
/*
* sleep for 'secs' seconds, displaying a symbol at each elapsed second
*/
void
wdt_sleep(int secs)
{
KnTimeVal tv = { 1, 0 };
int i;
putchar('[');
for (i = 0; i < secs; i++) {
threadDelay(&tv);
if (i && (i % 60 == 59))
putchar('#');
else
if (i && (i % 10 == 9))
putchar('|');
else
putchar('.');
fflush(stdout);
}
printf("]\n");
}
/*
* check the return value of a system call
*/
void
wdt_check(char* fn, int res)
{
printf("%s: ", fn);
if (res < 0)
printf("ERROR: res = %d, errno = %d (%s)\n", res,
errno, strerror(errno));
else
printf("SUCCESS: res = %d\n", res);
}
/*
* arm, pat, then disarm the watchdog timer
*/
int
wdt_arm_pat_disarm(int min, int max, int inc, int times, int armed)
{
timespec_t tv;
int res, intv, wait, pats;
intv = min;
while (intv < max) {
tv.tv_sec = intv;
tv.tv_nsec = 0;
/* timer already armed? */
if (!armed) {
printf("\nsetting interval to %d s...\n", intv);
res = wdt_set_interval(wdt_handle, &tv);
wdt_check("wdt_set_interval()", res);
if (res < 0)
return res;
printf("arming...\n");
res = wdt_arm(wdt_handle);
wdt_check("wdt_arm()", res);
if (res < 0)
return res;
}
printf("patting...\n");
for (pats = 0; pats < times; pats++) {
/* pat 1 sec before the timer expires */
wait = intv - 1;
printf("sleeping for %d s\n", wait);
wdt_sleep(wait);
res = wdt_pat(wdt_handle);
wdt_check("wdt_pat()", res);
if (res < 0)
return res;
}
printf("disarming...\n");
res = wdt_disarm(wdt_handle);
wdt_check("wdt_disarm()", res);
if (res < 0)
return res;
armed = 0;
/* wait 'WDT_EXTRA_SLEEP' secs more than last configured interval */
wait = intv + WDT_EXTRA_SLEEP;
printf("checking that the watchdog timer is stopped...\n");
printf("sleeping for %d s...\n", wait);
wdt_sleep(wait);
intv += inc;
}
return 0;
}
static KnSem wdt_sem;
/*
* worker thread
*/
void
wdt_thread()
{
timespec_t max;
int armed, i, res, min;
for (i = 0; i < WDT_RUNS; i++) {
/* Allocate watchdog timer. If timer is already allocated (busy),
a previous instance of the actor was killed. In this case,
reallocate timer and, if it is armed, continue the test
without disarming it */
res = wdt_alloc(&wdt_handle);
wdt_check("wdt_alloc()", res);
if (res < 0 && errno == EBUSY) {
printf("watchdog timer busy -> reallocating it\n");
/* Reallocate timer, using the magic handle (as we have
no knowledge of the actual handle previously allocated).
this call also pats the timer if it is armed */
wdt_handle = WDT_MAGIC_HANDLE;
res = wdt_realloc(&wdt_handle);
wdt_check("wdt_realloc()", res);
}
printf("--> watchdog handle = %d\n", wdt_handle);
if (res < 0)
break;
/* the timer may be already armed when we reallocate it */
res = armed = wdt_is_armed(wdt_handle);
wdt_check("wdt_is_armed()", res);
if (res < 0)
break;
printf("--> the watchdog timer is currently%s armed\n",
armed ? "" : " NOT");
/* ok, get current timeout interval of the armed timer */
if (armed) {
timespec_t intv;
res = wdt_get_interval(wdt_handle, &intv);
wdt_check("wdt_get_interval()", res);
if (res < 0)
break;
printf("--> current interval = %d.%09d s\n",
intv.tv_sec, intv.tv_nsec);
/* continue test at point where preceding actor was killed */
min = intv.tv_sec;
}
else {
min = WDT_PAT_MIN;
}
/* then get the maximum allowed timeout interval */
res = wdt_get_maxinterval(wdt_handle, &max);
wdt_check("wdt_get_maxinterval()", res);
printf( "--> max interval = %d.%09d s\n", max.tv_sec, max.tv_nsec);
if (res < 0)
break;
res = wdt_arm_pat_disarm(min, max.tv_sec - WDT_EXTRA_SLEEP,
WDT_PAT_INC, WDT_PAT_TIMES, armed);
if (res < 0)
break;
res = wdt_free(wdt_handle);
wdt_check("wdt_free()", res);
if (res < 0)
break;
}
/* test completed */
semV(&wdt_sem);
threadDelete(K_MYACTOR, K_MYSELF);
}
static long wdt_stack[WDT_STACKSIZE]; /* watchdog thread stack */
int
main()
{
KnThreadLid childLid;
KnThreadDefaultSched schedParam;
KnDefaultStartInfo_f startInfo;
KnActorPrivilege actorPriv;
int res;
semInit(&wdt_sem, 0);
actorPrivilege(K_MYACTOR, &actorPriv, NULL);
startInfo.dsPrivilege = (actorPriv == K_SUPACTOR) ?
K_SUPTHREAD : K_USERTHREAD;
startInfo.dsType = K_DEFAULT_START_INFO;
startInfo.dsSystemStackSize = K_DEFAULT_STACK_SIZE;
startInfo.dsUserStackPointer = &wdt_stack[WDT_STACKSIZE];
startInfo.dsEntry = (KnPc) wdt_thread;
/* heavily increase the priority of the worker thread to ensure that
it will not be later delayed by other applicative threads (if any) */
schedParam.tdClass = K_SCHED_DEFAULT;
schedParam.tdPriority = WDT_THREAD_PRIO;
/* create the worker thread */
res = threadCreate(K_MYACTOR, &childLid, K_ACTIVE,
&schedParam, &startInfo);
assert(res == 0);
/* then wait for the completion of the test */
semP(&wdt_sem, K_NOTIMEOUT);
return 0;
}