nx_progress_watchdog.nx source
↩ module page · 65 lines · 2332 B
1// nx_progress_watchdog.nx -- detects stalled phases in long ingest runs.
2//
3// A heartbeat-based liveness monitor: caller heartbeats at progress
4// points; check() reports STALLED if too much wall-clock has elapsed
5// since the last heartbeat. Lightweight: zero syscalls per heartbeat
6// (just stores the timestamp); one clock read per check.
7//
8// genealogy_id: ibm_360_watchdog_timer + os_softdog_1990s
9// lineage_id: liveness_monitor
10
11// nx_safety_envelope:
12// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
13// sil_target: SIL1
14// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
15// verdict: NOT_YET_EVALUATED
16
17import "nx_syscalls.nx"
18import "nx_tier.nx"
19import "nx_clock.nx"
20
21const NX_WATCHDOG_OK: nx_int = 0
22const NX_WATCHDOG_STALLED: nx_int = 1
23
24struct NxWatchdog {
25 stall_timeout_ns: nx_int,
26 last_beat_ns: nx_int,
27 n_heartbeats: nx_int,
28 n_stalls_detected: nx_int,
29}
30
31func nx_progress_watchdog_new(stall_timeout_ms: nx_int) -> *NxWatchdog {
32 let raw: *nx_byte = sys_mmap(NX_BUF_TINY)
33 let w: *NxWatchdog = raw as *NxWatchdog
34 w.stall_timeout_ns = stall_timeout_ms * NX_NS_PER_MS
35 w.last_beat_ns = nx_clock_monotonic_ns()
36 w.n_heartbeats = 0
37 w.n_stalls_detected = 0
38 return w
39}
40
41func nx_progress_watchdog_heartbeat(w: *NxWatchdog) -> nx_int {
42 w.last_beat_ns = nx_clock_monotonic_ns()
43 w.n_heartbeats = w.n_heartbeats + 1
44 return 0
45}
46
47func nx_progress_watchdog_check(w: *NxWatchdog) -> nx_int {
48 let now_ns: nx_int = nx_clock_monotonic_ns()
49 let elapsed_ns: nx_int = now_ns - w.last_beat_ns
50 if elapsed_ns > w.stall_timeout_ns {
51 w.n_stalls_detected = w.n_stalls_detected + 1
52 return NX_WATCHDOG_STALLED
53 }
54 return NX_WATCHDOG_OK
55}
56
57func nx_progress_watchdog_elapsed_ms(w: *NxWatchdog) -> nx_int {
58 let now_ns: nx_int = nx_clock_monotonic_ns()
59 let dt_ns: nx_int = now_ns - w.last_beat_ns
60 return dt_ns / NX_NS_PER_MS
61}
62
63func nx_progress_watchdog_n_heartbeats(w: *NxWatchdog) -> nx_int { return w.n_heartbeats }
64func nx_progress_watchdog_n_stalls(w: *NxWatchdog) -> nx_int { return w.n_stalls_detected }
65func nx_progress_watchdog_stall_timeout_ms(w: *NxWatchdog) -> nx_int { return w.stall_timeout_ns / NX_NS_PER_MS }