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}