code wiki / (root) / nx_checkpoint.nx

nx_checkpoint.nx source

↩ module page · 149 lines · 4498 B

1// nx_checkpoint.nx -- atomic high-water-mark file for resumable jobs. 2// 3// Persists a single "last completed id" via .tmp + renameat. 4// 5// genealogy_id: chandy_lamport_1985_snapshots + spark_checkpointing 6// lineage_id: resumable_state 7 8// nx_safety_envelope: 9// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 10// sil_target: SIL1 11// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 12// verdict: NOT_YET_EVALUATED 13 14import "nx_syscalls.nx" 15import "nx_tier.nx" 16import "nx_str.nx" 17import "nx_strconv.nx" 18 19const NX_CHECKPOINT_FRESH: nx_int = -1 20const NX_CHECKPOINT_WRITE_FAIL: nx_int = -2 21const NX_CHECKPOINT_RENAME_FAIL: nx_int = -3 22 23// renameat2 on RV64 Linux is syscall 276; the older renameat is 38. 24// Original value 82 was wrong — that's `sync()` on RV64, not rename — 25// which silently failed every atomic-write and made nx_checkpoint_set 26// fail at the rename step. Caught by the cold-cache smoke audit 27// 2026-05-15 once the fail-loud smoke lib exposed the silent rot. 28const SYS_RENAMEAT_LOCAL: nx_int = 276 // renameat2 (RV64) 29 30struct NxCheckpoint { 31 path_buf: *u8, 32 tmp_buf: *u8, 33 last_value: nx_int, 34 n_writes: nx_int, 35 n_write_fail: nx_int, 36} 37 38const NX_CHECKPOINT_STRUCT_BYTES: nx_size = 48 39const NX_CHECKPOINT_PATH_CAP: nx_size = 512 40 41func nx_ckpt_make_tmp(orig: *u8, out: *u8) -> nx_size { 42 nx_str_cpy(out, orig) 43 let n: nx_int = nx_str_len(out) 44 let sfx: *u8 = ".tmp" as *u8 45 var i: nx_int = 0 46 while sfx[i] != 0 { 47 out[n + i] = sfx[i] 48 i = i + 1 49 } 50 out[n + i] = 0 51 return (n + i) as nx_size 52} 53 54func nx_ckpt_parse_i64(buf: *u8, n: nx_int) -> nx_int { 55 if n <= 0 { return -1 } 56 var i: nx_int = 0 57 if buf[0] == 0x2D { i = 1 } 58 var v: nx_int = 0 59 var any: nx_int = 0 60 while i < n { 61 let c: nx_int = buf[i] 62 if c < 0x30 { i = n } 63 if c > 0x39 { i = n } 64 if i < n { 65 v = v * 10 + (c - 0x30) 66 any = 1 67 i = i + 1 68 } 69 } 70 if any == 0 { return -1 } 71 return v 72} 73 74func nx_checkpoint_open(path: *u8) -> *NxCheckpoint { 75 let raw: *u8 = sys_mmap(NX_CHECKPOINT_STRUCT_BYTES) 76 let cp: *NxCheckpoint = raw as *NxCheckpoint 77 78 let pbuf: *u8 = sys_mmap(NX_CHECKPOINT_PATH_CAP) 79 nx_str_cpy(pbuf, path) 80 let tbuf: *u8 = sys_mmap(NX_CHECKPOINT_PATH_CAP) 81 nx_ckpt_make_tmp(path, tbuf) 82 cp.path_buf = pbuf 83 cp.tmp_buf = tbuf 84 cp.last_value = NX_CHECKPOINT_FRESH 85 cp.n_writes = 0 86 cp.n_write_fail = 0 87 88 let len_p: *nx_size = (sys_mmap(NX_SIZEOF_NX_SIZE)) as *nx_size 89 len_p[0] = 0 90 let data: *u8 = sys_read_file(path, len_p) 91 if (data as nx_size) != 0 { 92 let n: nx_int = len_p[0] 93 let v: nx_int = nx_ckpt_parse_i64(data, n) 94 if v >= 0 { cp.last_value = v } 95 } 96 97 return cp 98} 99 100func nx_checkpoint_close(cp: *NxCheckpoint) -> nx_int { 101 cp.last_value = NX_CHECKPOINT_WRITE_FAIL 102 return 0 103} 104 105func nx_checkpoint_get(cp: *NxCheckpoint) -> nx_int { 106 return cp.last_value 107} 108 109func nx_checkpoint_set(cp: *NxCheckpoint, id: nx_int) -> nx_int { 110 let dbuf: *u8 = sys_mmap(NX_BUF_TINY) 111 let dlen: nx_int = nx_strconv_format_i64(id, dbuf) 112 dbuf[dlen] = 10 113 let total: nx_int = dlen + 1 114 115 let fd: nx_fd = __syscall(SYS_OPENAT, AT_FDCWD, cp.tmp_buf as i64, 0x241, 0x1A4, 0, 0) 116 if fd < 0 { 117 cp.n_write_fail = cp.n_write_fail + 1 118 return NX_CHECKPOINT_WRITE_FAIL 119 } 120 var w: nx_int = 0 121 while w < total { 122 let p: *u8 = ((dbuf as nx_size) + w) as *u8 123 let r: nx_int = sys_write(fd, p, total - w) 124 if r <= 0 { 125 sys_close(fd) 126 cp.n_write_fail = cp.n_write_fail + 1 127 return NX_CHECKPOINT_WRITE_FAIL 128 } 129 w = w + r 130 } 131 sys_close(fd) 132 133 let rc: nx_int = __syscall(SYS_RENAMEAT_LOCAL, AT_FDCWD, cp.tmp_buf as i64, 134 AT_FDCWD, cp.path_buf as i64, 0, 0) 135 if rc < 0 { 136 cp.n_write_fail = cp.n_write_fail + 1 137 return NX_CHECKPOINT_RENAME_FAIL 138 } 139 cp.last_value = id 140 cp.n_writes = cp.n_writes + 1 141 return 0 142} 143 144func nx_checkpoint_n_writes(cp: *NxCheckpoint) -> nx_int { return cp.n_writes } 145func nx_checkpoint_n_write_fail(cp: *NxCheckpoint) -> nx_int { return cp.n_write_fail } 146func nx_checkpoint_is_fresh(cp: *NxCheckpoint) -> nx_int { 147 if cp.last_value == NX_CHECKPOINT_FRESH { return 1 } 148 return 0 149}