code wiki / _hdl_build / nx_jobcancel_gate.nx
nx_jobcancel_gate.nx source
↩ module page · 103 lines · 7094 B
1// nx_jobcancel_gate.nx -- referee for nx_jobcancel_lib (engineshift ES29, 2026-09-15): a REAL child of this gate
2// sleeps as the job under test, the launch row is planted in a /tmp fixture journal, and the cancel verb is
3// driven through every named outcome: REFUSED-ABSENT (no row), REFUSED-MISMATCH (row names another elf: the child
4// must survive), CANCELLED (TERM suffices, the child is reaped with signal 15), ALREADY-GONE (a second cancel of
5// the same row). The KILL escalation is NOT exercised here (a child that ignores TERM needs a signal mask this
6// gate does not install) and the receipt says so. Fixture dir /tmp/nx_jobcancel_gate/, journal truncated at SETUP.
7// license_tier: ORIGINAL
8import "nx_jobcancel_lib.nx"
9import "nx_syscalls.nx"
10import "nx_gate_verdict.nx"
11
12const G_DIR: *u8 = "/tmp/nx_jobcancel_gate/"
13const G_JRNL: *u8 = "/tmp/nx_jobcancel_gate/jobrun.jrnl"
14const G_CONF: *u8 = "/tmp/nx_jobcancel_gate/jobrun.conf"
15const G_CONF_ROWS: *u8 = "# planted\ncancel_grace_ms 250\n"
16const G_OUT_A: *u8 = "/tmp/nx_jobcancel_gate/job_a.out"
17const G_OUT_B: *u8 = "/tmp/nx_jobcancel_gate/job_b.out"
18const G_OUT_NONE: *u8 = "/tmp/nx_jobcancel_gate/never_launched.out"
19const G_WRONG_ELF: *u8 = "/tmp/nx_jobcancel_gate/not_this.elf"
20const G_CHILD_SLEEP_MS: i64 = 30000
21const G_GRACE_MS: i64 = 2000
22const G_POLL_MS: i64 = 50
23const G_EPOCH: i64 = 1789482000
24const G_MODE_644: i64 = 420
25const G_SIG_MASK: i64 = 128 // the low 7 bits of a wait4 status word carry the terminating signal
26const G_WAIT_WORDS: i64 = 2
27const G_ROW_CAP: i64 = 2048
28
29func g_fresh(path: *u8) -> i64 { let fd: i64 = sys_openat_wr(path, G_MODE_644); if fd >= 0 { sys_close(fd); return 1 } return 0 }
30func g_write(path: *u8, body: *u8) -> i64 { let fd: i64 = sys_openat_wr(path, G_MODE_644); if fd < 0 { return 0 } sys_write(fd, body, jc_slen(body)); sys_close(fd); return 1 }
31func g_child() -> i64 { let pid: i64 = sys_fork(); if pid == 0 { sys_sleep_ms(G_CHILD_SLEEP_MS); sys_exit(0) } return pid }
32func g_count_rows(path: *u8, kind: *u8) -> i64 {
33 let nb: *i64 = sys_mmap(16) as *i64
34 nb[0] = 0
35 let buf: *u8 = sys_read_file(path, nb)
36 if (buf as i64) == 0 { return 0 }
37 let n: i64 = nb[0]
38 let off: *i64 = sys_mmap(16) as *i64
39 let len: *i64 = sys_mmap(16) as *i64
40 var c: i64 = 0
41 var p: i64 = 0
42 while p < n {
43 var e: i64 = p
44 while e < n { if buf[e] == (JC_LF as u8) { break } e = e + 1 }
45 let line: *u8 = (buf as i64 + p) as *u8
46 if jc_field(line, e - p, JC_F_KIND, off, len) == 1 { if jc_eq_n(kind, (line as i64 + off[0]) as *u8, len[0]) == 1 { c = c + 1 } }
47 p = e + 1
48 }
49 return c
50}
51
52func main(argc: i64, argv: *i64) -> i64 {
53 let ctr: *i64 = gv_ctr()
54 gv_head("=== NX-JOBCANCEL GATE -- the launcher's cancel verb on a real sleeping child, every named outcome ===" as *u8)
55 let self: *u8 = argv[0] as *u8
56 sys_mkdir(G_DIR, JC_MODE_644 + 73) // 0755
57 gv_check("S0-fixture-journal-truncated-at-setup" as *u8, g_fresh(G_JRNL), ctr)
58 let pidp: *i64 = sys_mmap(8) as *i64
59 // T1: no launch row for this out-file -> REFUSED-ABSENT, nothing signalled
60 gv_check_eq("T1-NEG-unknown-out-file-is-REFUSED-ABSENT" as *u8, jc_cancel(G_JRNL, G_OUT_NONE, G_GRACE_MS, G_POLL_MS, pidp), JC_REFUSED_ABSENT, ctr)
61 gv_check_eq("T1a-no-pid-acted-on" as *u8, pidp[0], 0, ctr)
62 // T2: a real child, journaled under the WRONG elf -> REFUSED-MISMATCH and the child is still alive
63 let kid_b: i64 = g_child()
64 gv_check("T2-fixture-child-forked" as *u8, (kid_b > 0) as i64, ctr)
65 gv_check("T2a-fixture-child-alive-before" as *u8, jc_alive(kid_b), ctr)
66 gv_check_eq("T2b-fixture-child-cmdline-is-this-gate" as *u8, jc_verify(kid_b, self), 1, ctr)
67 gv_check("T2c-launch-row-planted-with-wrong-elf" as *u8, jc_journal(G_JRNL, JC_KIND_LAUNCH, G_EPOCH, kid_b, G_WRONG_ELF, G_OUT_B, "planted" as *u8), ctr)
68 gv_check_eq("T2d-NEG-elf-mismatch-is-REFUSED-MISMATCH" as *u8, jc_cancel(G_JRNL, G_OUT_B, G_GRACE_MS, G_POLL_MS, pidp), JC_REFUSED_MISMATCH, ctr)
69 gv_check_eq("T2e-refusal-named-the-journaled-pid" as *u8, pidp[0], kid_b, ctr)
70 gv_check("T2f-NEG-stranger-survives-a-refused-cancel" as *u8, jc_alive(kid_b), ctr)
71 gv_check_eq("T2g-still-the-same-process" as *u8, jc_verify(kid_b, self), 1, ctr)
72 // T3: the same child journaled under its REAL elf on another out-file (the LAST row wins) -> CANCELLED by TERM
73 gv_check("T3-launch-row-planted-with-the-real-elf" as *u8, jc_journal(G_JRNL, JC_KIND_LAUNCH, G_EPOCH + 1, kid_b, self, G_OUT_A, "planted" as *u8), ctr)
74 let elfbuf: *u8 = sys_mmap(G_ROW_CAP)
75 gv_check_eq("T3a-find-resolves-the-row" as *u8, jc_find(G_JRNL, G_OUT_A, pidp, elfbuf, G_ROW_CAP), 1, ctr)
76 gv_check_eq("T3b-find-carries-the-pid" as *u8, pidp[0], kid_b, ctr)
77 gv_check("T3c-find-carries-the-elf" as *u8, jc_streq(elfbuf, self), ctr)
78 let rc: i64 = jc_cancel(G_JRNL, G_OUT_A, G_GRACE_MS, G_POLL_MS, pidp)
79 gv_check_eq("T3d-cancel-is-CANCELLED-by-TERM" as *u8, rc, JC_OK_TERM, ctr)
80 let st: *i64 = sys_mmap(G_WAIT_WORDS * 8) as *i64
81 st[0] = 0
82 let reaped: i64 = sys_wait4(kid_b, st, 0)
83 gv_check_eq("T3e-child-reaped-by-the-gate" as *u8, reaped, kid_b, ctr)
84 gv_check_eq("T3f-child-died-of-SIGTERM" as *u8, st[0] % G_SIG_MASK, JC_SIGTERM, ctr)
85 gv_check_eq("T3g-NEG-child-gone-after-reap" as *u8, jc_alive(kid_b), 0, ctr)
86 // T4: cancelling the same row again -> ALREADY-GONE (never a second signal at a dead pid)
87 gv_check_eq("T4-second-cancel-is-ALREADY-GONE" as *u8, jc_cancel(G_JRNL, G_OUT_A, G_GRACE_MS, G_POLL_MS, pidp), JC_ALREADY_GONE, ctr)
88 // T5: the journal reads launch -> cancel -> outcome
89 gv_check_eq("T5-two-launch-rows-journaled" as *u8, g_count_rows(G_JRNL, JC_KIND_LAUNCH), 2, ctr)
90 gv_check_eq("T5a-three-cancel-outcomes-journaled" as *u8, g_count_rows(G_JRNL, JC_KIND_CANCEL), 3, ctr)
91 // T6: the grace conf -- planted row, absent file, the bootstrap default
92 gv_check("T6-conf-planted" as *u8, g_write(G_CONF, G_CONF_ROWS), ctr)
93 gv_check_eq("T6a-conf-row-read" as *u8, jc_conf_grace(G_CONF, JC_GRACE_MS_DEFAULT), 250, ctr)
94 gv_check_eq("T6b-NEG-absent-conf-is-the-default" as *u8, jc_conf_grace("/tmp/nx_jobcancel_gate/absent.conf" as *u8, JC_GRACE_MS_DEFAULT), JC_GRACE_MS_DEFAULT, ctr)
95 gv_check_eq("T6c-default-is-named" as *u8, JC_GRACE_MS_DEFAULT, 3000, ctr)
96 // T7: the outcome names
97 gv_check("T7-outcome-names-resolve" as *u8, jc_streq(jc_outcome_name(JC_REFUSED_MISMATCH), "REFUSED-MISMATCH" as *u8) * jc_streq(jc_outcome_name(JC_OK_KILL), "CANCELLED-KILLED" as *u8), ctr)
98 gv_kv("cancelled_pid" as *u8, kid_b)
99 gv_kv("wait_status" as *u8, st[0])
100 gv_kv("grace_ms_used" as *u8, G_GRACE_MS)
101 gv_kv("kill_escalation_exercised" as *u8, 0)
102 return gv_verdict("nx_jobcancel_gate" as *u8, ctr, "a real child of this gate cancelled through the journal-verified verb: absent row refused, wrong elf refused with the child alive, right elf cancelled by TERM and reaped, second cancel already-gone; KILL escalation unexercised and said so" as *u8)
103}