code wiki / _hdl_build / nx_supervised_dispatch.nx

nx_supervised_dispatch.nx

buildroot/runtime/_hdl_build/nx_supervised_dispatch.nx

16625 B305 linesdepth 5pulls 7 transitivereach 1 importersview sourcekind tooltopic supervised
docsdependenciesstructsconstsfunctions

about

nx_supervised_dispatch.nx -- WMS rung M5: OS / KERNEL SUPERVISOR BINDING. module: nishi-core.autonomy.supervised_dispatch capability: BIND_DISPATCH_TO_SUPERVISED_JOB (a dispatched rung becomes a real, resource-LIMITED, reaped, RESTART-on-death supervised job) WHAT THIS UNIFIES (the M5 payoff): M4 (nx_dispatch_lease) LEASES the next ready rung but never RUNS it; M1 (nx_heartbeat_monitor) detects a stalled stream but never ACTS; the supervisor policy (nx_supervisor) decides RESTART/ESCALATE but never forks. M5 is the missing seam: it FORKS the leased rung as a governed child (with a kernel resource limit = prlimit64 RLIMIT_AS, the Linux analog of a Job-Object memory limit), beats its heartbeat, and -- when M1 sees a missing heartbeat (a crashed job) -- it RESTARTS/reassigns (a second supervised run). Conductor loop + kernel scheduler + workstream manager collapse into ONE control plane. REUSE / lineage (compose, do not reinvent -- check-registries-before-building): - dl_dispatch / dl_try_lease / dl_release <- nx_dispatch_lease.nx (M4): lease the rung - an_newcx / an_load / an_find / an_id_at <- nx_assign_core (surfaced via M4) - hb_beat_at / hbm_scan / HB_STALLED/HB_ALIVE <- nx_heartbeat_monitor.nx (M1) - fa_cat / fa_catn / fa_appendz <- nx_framed_append.nx (R0b): the locked single-write status discipline (every record = ONE buffer -> ONE atomic write) - the fork -> child-setup -> sys_wait4 status-decode model is the nx_container.nx idiom (lo = status & 0xFF; exited iff lo==0; code = (status>>8)&0xFF) -- reimplemented HERE (NOT imported) because nx_container.nx imports nx_syscalls_x86_64.nx and that would surface a SECOND syscall table against M1/M4's nx_syscalls.nx = the double-import rc=6 trap. M5 keeps a SINGLE syscall surface (nx_syscalls.nx via all three imports). SOVEREIGN PAYLOAD (no /bin dependency): the supervised child does NOT execve a foreign ELF. After fork it caps ITSELF (nx_prlimit) then runs an IN-ORGAN payload selector -- the same organ post-fork executing a payload function (the nx_container fork model minus execve). payload=0 clean work+exit(0); payload=1 "crash" exit(70); payload=2 hang (finite busy then exit WITHOUT beating = the never-beats stall the gate KILLs/detects). SUPERVISOR POLICY (inlined, ~ nx_supervisor's 4-state restart-budget machine): a STALLED job under budget -> RESTART (respawn + restarts++ + fresh beat); over budget -> ESCALATE (bounded, no thrash storm); an ALIVE / clean-exited job -> NONE (the no-thrash neg-ctrl). WRITE DISCIPLINE (the torn-line bug we must NOT reintroduce): every status record is assembled into ONE buffer and emitted with a SINGLE locked fa_appendz (the cn_emit_* discipline) -- never a sequence of sys_write() calls. Additive: new file only; one tiny

dependencies 3 imports · 1 importers

nx_dispatch_lease.nx nx_heartbeat_monitor.nx nx_framed_append.nx nx_supervised_dispatch.nx nx_supervised_dispatch_gate.nx

imports: nx_dispatch_lease.nxnx_heartbeat_monitor.nxnx_framed_append.nx

imported by: nx_supervised_dispatch_gate.nx

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main sd_p sys_now_realtime_ms sys_mmap sys_clock_gettime_real fa_cat fa_catn sys_mmap ↻ sd_job_new sys_now_realtime_sec sys_mmap ↻ sys_clock_gettime_real ↻ sd_spawn_supervised sys_fork sd_child_payload sd_apply_limit nx_prlimit sys_sleep_ms sys_mmap ↻ sys_munmap sd_getpid hb_beat_at sys_mmap ↻ hbm_build fa_cat ↻ fa_catn ↻ fa_append sys_mmap ↻ sys_openat_append sys_flock fa_write_all sys_write sys_close hb_beat_at ↻ sd_getpid ↻ sd_n sd_poll sys_wait4 sys_sleep_ms ↻ sd_emit

structs

none

consts

46const SD_MAGIC_2277: i64 = 2277
47const SD_MAGIC_100000: i64 = 100000
49const SD_REC_CAP: i64 = 256 // bounded status record size (no magic number)
50const SD_SIGKILL: i64 = 9 // the crash signal for the controlled-kill test
51const SD_MAXRESTART: i64 = 3 // restart budget; over -> ESCALATE (no thrash storm)
52const SD_AS_LIMIT: i64 = 0x10000000 // 256 MiB address-space cap = the resource "limit"
55const SD_ACTION_NONE: i64 = 0 // alive / clean-exited -> do nothing (no-thrash path)
56const SD_ACTION_RESTART: i64 = 1 // stalled + under budget -> respawn
57const SD_ACTION_ESCALATE: i64 = 3 // stalled + budget exhausted -> escalate (bounded)
60const SD_RUNNING: i64 = 0
61const SD_EXITED_OK: i64 = 1
62const SD_CRASHED: i64 = 2
63const SD_ESCALATED: i64 = 3
66const SD_PAY_CLEAN: i64 = 0 // compute + exit(0): a job that finishes normally
67const SD_PAY_CRASH: i64 = 1 // exit(70): a job that dies non-zero
68const SD_PAY_HANG: i64 = 2 // finite busy, exit WITHOUT beating (the stall payload)
72const SD_J_WS: i64 = 0
73const SD_J_PID: i64 = 1
74const SD_J_RESTARTS: i64 = 2
75const SD_J_STATE: i64 = 3
76const SD_J_LIMIT: i64 = 4
77const SD_J_EXIT: i64 = 5

functions

79func sd_job_new() -> *i64
called by 2: mainmain
86func sd_getpid() -> i64 { return __syscall(172, 0, 0, 0, 0, 0, 0) } // rv64 getpid=172; raw x86 39 is an RV64 KEY translated to ioctl(16) -> -ENOTTY (debt idx SD_MAGIC_2277)
93func sd_apply_limit(limit_as: i64) -> i64
called by 2: sd_child_payloadmain calls 1: nx_prlimit
115func sd_child_payload(hbpath: *u8, ws: i64, payload: i64, limit_as: i64, beats: i64,
146func sd_spawn_supervised(hbpath: *u8, job: *i64, payload: i64, beat_epoch: i64) -> i64
169func sd_poll(job: *i64) -> i64
called by 1: main calls 1: sys_wait4
194func sd_dispatch_and_supervise(cx: *i64, dir: *u8, hbpath: *u8, job: *i64,
called by 1: main calls 2: dl_dispatchsd_spawn_supervised
215func sd_supervise_tick(hbpath: *u8, now: i64, thresh: i64, job: *i64,
called by 1: main calls 2: hbm_scansd_spawn_supervised
244func sd_emit(path: *u8, verb: *u8, ws: i64, pid: i64, restarts: i64, action: i64,
called by 1: main calls 3: fa_catfa_catnfa_appendz
264func sd_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
called by 1: main
265func sd_n(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m; sys_write(1,"-\x00" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 }
called by 1: main
271func main() -> i64