Expand description
taskwd — the IOC’s task watchdog, C libCom/src/taskwd/taskwd.c
(R7.0.10).
One low-priority thread wakes every TASKWD_DELAY and asks each
registered task whether it is still running. When one stops running the
watchdog says so on the console, calls that task’s own callback, and tells
every registered monitor — which is how a C IOC turns a wedged scan thread
into an operator-visible event instead of records that quietly stop
updating.
This is not a thread registry. Nothing here enumerates threads, names them or reports their stacks; a task is here because it asked to be watched, and the list is the watchdog’s, not the process’s.
§What “suspended” means here
C’s watchdog polls epicsThreadIsSuspended(tid) (taskwd.c:99). On
vxWorks that is a real OS state; on POSIX it is a flag epicsThreadSuspendSelf
sets, so a C IOC on Linux only ever reports a thread that suspended
itself — out of memory, cantProceed. Rust has neither: a thread cannot
be suspended by another and never suspends itself.
So the port asks the question the other way round, which is the only way it
can be asked here: a task checks in (TaskwdEntry::check_in) as it goes
round its loop, and a task that stops checking in inside the interval it
declared is this port’s suspended. That covers strictly more than C’s
POSIX build does — a thread wedged on a lock or an unbounded read is
invisible to epicsThreadIsSuspended and visible here — and the operator’s
side of it is unchanged: the same console line, the same callback, the same
taskwdShow state column.
A task that cannot promise to come back — one parked in accept(), or in a
blocking read with no deadline — registers CheckIn::Unbounded and is
listed but never reported, which is exactly what C’s POSIX build does with
every one of its tasks.
§Identity is the registration, not a thread id
C keys everything on epicsThreadId, because the state it polls belongs to
a thread. Half the port’s equivalents of C’s call sites are futures, which
have no thread of their own and may run on a different one after every
await, so a thread id would name the wrong thing for them and the right
thing for the others — one field, two meanings. Registration returns a
TaskwdEntry instead, and TaskwdId is what the monitor API carries.
The handle is also what removes the task: C pairs every taskwdInsert with
a taskwdRemove on each exit path and errlogs when it is passed a thread
that was never inserted (taskwd.c:241-243). Dropping the handle removes
it, on every path including a panic, and there is no way to ask for the
removal of something that was never registered.
§Not ported
taskwdAnyInsert/taskwdAnyRemove(taskwd.c:306-354) — the deprecated pre-3.15 monitor API, which C implements as a monitor whosenotifyfires only on suspension. Nothing in base or in the modules this workspace ports calls it; theTaskwdMonitortrait is what it wraps.- The free-node pool (
taskwd.c:395-430) and the%d free nodesit puts in the report. It is an allocator for three C structs that share a union; Rust drops the entry instead, so there is no pool to count. twdctlDisable(taskwd.c:74) — the enum has the state, nothing in R7.0.10 ever assigns it.
Structs§
- Task
Info - What a monitor is told about, and what
taskwd_showlists. - Taskwd
Entry - A watched task’s registration. Dropping it stops the watch — C
taskwdRemove(taskwd.c:207-244) on every exit path, including the ones C’s callers have to remember. - Taskwd
Id - The watchdog’s name for one registration — C’s
epicsThreadIdin the monitor API, without the claim that a task is a thread. - Taskwd
Monitor Entry - A registered monitor’s handle. Dropping it unregisters the monitor.
Enums§
- CheckIn
- What a task promises about coming back to check in.
Constants§
- TASKWD_
DELAY - How often the watchdog looks at its list — C
TASKWD_DELAY(taskwd.c:80).
Traits§
- Taskwd
Monitor - Something watching every task, rather than one — C’s
taskwdMonitor(taskwd.h:41-45). Every method is optional there (aNULLslot is skipped), so every method here has a do-nothing default.
Functions§
- taskwd_
init - Start the watchdog thread if it is not running — C
taskwdInit(taskwd.c:168-172), whichiocInitcalls early (iocInit.c:151) and every registration calls for itself. - taskwd_
insert - Watch this task — C
taskwdInsert(taskwd.c:177-205). - taskwd_
monitor_ add - Watch every task — C
taskwdMonitorAdd(taskwd.c:249-264). - taskwd_
show - Report the watchdog’s list — C
taskwdShow(taskwd.c:359-390).
Type Aliases§
- Taskwd
Callback - A task’s own answer to being found stuck — C’s
TASKWDFUNCand itsusrpointer collapsed into one closure (taskwd.h:33).