nx_jobclaim_lib.nx
buildroot/runtime/nx_jobclaim_lib.nx
about
nx_jobclaim_lib.nx -- ONE CONTRACT shared by the _jobs claim WRITER and the _jobs claim READER.
WHY THIS IS A LIB AND NOT TWO ORGANS THAT EACH KNOW THE FORMAT.
nx_jobclaim_reap marks a dead job claim by APPENDING a tombstone. nx_joblost_gate decides what a
claim IS by looking for markers inside it. Those two are a PRODUCER and a CONSUMER of one wire,
and this estate has already measured what that costs when each is written separately: a producer
and a consumer each correct in isolation can still disagree on the wire.
THE SPECIFIC TRAP THIS FILE MAKES IMPOSSIBLE.
History here is additive (rule 13): the tombstone is APPENDED, so a reaped claim STILL CONTAINS
its original `state=CLAIMED` line. The census classified by substring over the whole file and
tested CLAIMED before anything else -- so a reader written on its own sees every reaped claim as
still pending. The tombstone lands, the count does not move, the partition tooth breaks, and the
reaper reads as a fix while being a no-op.
A REAPER SHIPPED ALONE IS A NO-OP THAT LOOKS LIKE A FIX.
The ORDER is therefore not each organ's private business. There is ONE classifier -- jr_state_of --
and both organs import it, so the ordering cannot be right in one and wrong in the other.
WHY A TOMBSTONE AND NOT AN UNLINK. The claims store is a DIRECTORY OF FILES, not a last-wins
seg-store, so the clock-plane's tombstone-row pattern does not transfer unchanged. `sys_unlinkat`
exists and would work -- and it would also destroy the only record that the job was ever claimed,
which is the evidence any later investigation of the loss needs. Rule 13: soft-delete, never
DELETE. The precedent is nx_claims' own `reap` verb: release ONLY provably-expired claims, leave
an audit note, additive-only, idempotent. This is that shape applied to a different store.
license_tier: ORIGINAL
dependencies 1 imports · 11 importers
diagram shows first 10 each side; +0 more imports, +1 more importers in the complete lists below.
imports: nx_syscalls.nx
imported by: nx_job_concurrent_canonical_gate_t182.nxnx_job_reservation_canonical_gate_t182.nxnx_jobclaim_reap.nxnx_jobclaim_reap_gate.nxnx_joblost_gate.nxnx_tools_api.nxnx_tools_api_canonical_candidate_t182.nxnx_tools_api_idem_candidate_t190.nxnx_tools_api_recovery_candidate_t316.nxnx_tools_api_recovery_candidate_v2_t316.nxnx_tools_api_recovery_candidate_v3_t316.nx
structs
| none |
consts
| 34 | const JR_MARK_REAPED: *u8 = "state=REAPED" as *u8 |
| 35 | const JR_MARK_DONE: *u8 = "state=DONE" as *u8 |
| 36 | const JR_MARK_CLAIMED: *u8 = "state=CLAIMED" as *u8 |
| 37 | const JR_SELF: *u8 = "nx_jobclaim_reap" as *u8 |
| 38 | const JR_EXT_CLAIM: *u8 = ".claim" as *u8 |
| 39 | const JR_EXT_OUT: *u8 = ".out" as *u8 |
| 40 | const JR_PREFIX: *u8 = "job_" as *u8 |
| 41 | const JR_JOBS: *u8 = "_jobs" as *u8 |
| 42 | const JR_TMP: *u8 = "/tmp/" as *u8 |
| 43 | const JR_DOTDOT: *u8 = ".." as *u8 |
| 49 | const JR_ST_EMPTY: i64 = 0 |
| 50 | const JR_ST_REAPED: i64 = 1 |
| 51 | const JR_ST_DONE: i64 = 2 |
| 52 | const JR_ST_CLAIMED: i64 = 3 |
| 53 | const JR_ST_UNKNOWN: i64 = 4 |
| 57 | const JR_D_REAP: i64 = 0 |
| 58 | const JR_D_ALREADY: i64 = 1 |
| 59 | const JR_D_ABSENT: i64 = 2 |
| 60 | const JR_D_DONE: i64 = 3 |
| 61 | const JR_D_HAS_OUT: i64 = 4 |
| 62 | const JR_D_TOO_YOUNG: i64 = 5 |
| 63 | const JR_D_UNPARSED: i64 = 6 |
| 64 | const JR_D_WRITEFAIL: i64 = 7 |
| 65 | const JR_D_PATHLONG: i64 = 8 |
| 66 | const JR_D_BADID: i64 = 9 |
| 69 | const JR_I_STATE: i64 = 0 |
| 70 | const JR_I_AGE: i64 = 1 |
| 71 | const JR_I_HASOUT: i64 = 2 |
| 72 | const JR_I_BYTES: i64 = 3 |
| 73 | const JR_I_NEEDNL: i64 = 4 |
| 74 | const JR_I_SLOTS: i64 = 5 |
| 78 | const JR_I64_BYTES: i64 = 8 |
| 85 | const JR_MAXAGE_CONF: *u8 = "knowledge/status/jobclaim_maxage.conf" as *u8 |
| 86 | const JR_DEFAULT_MAX_AGE_SEC: i64 = 3600 |
| 88 | const JR_CH_NL: i64 = 10 |
| 89 | const JR_CH_MINUS: i64 = 45 |
| 90 | const JR_CH_DOT: i64 = 46 |
| 91 | const JR_CH_SLASH: i64 = 47 |
| 92 | const JR_CH_ZERO: i64 = 48 |
| 93 | const JR_CH_NINE: i64 = 57 |
| 94 | const JR_B10: i64 = 10 |
| 95 | const JR_SCRATCH: i64 = 16 |
| 102 | const JR_I64_DIGITS: i64 = 20 |
| 103 | const JR_TOMB_JOIN: i64 = 24 |
| 104 | const JR_TOMB_MAX: i64 = 128 |
| 105 | const JR_LINE: i64 = 256 |
| 106 | const JR_PATH: i64 = 1024 |
| 408 | const JR_ID_MAX:i64=9223372036854775807 |
| 409 | const JR_ERR_EXIST:i64=0-17 |
| 410 | const JR_ERR_INTR:i64=0-4 |
| 411 | const JR_ERR_INVALID:i64=0-22 |
| 412 | const JR_ERR_OVERFLOW:i64=0-75 |
| 413 | const JR_ERR_DEADLINE:i64=0-110 |
| 414 | const JR_ERR_PATH:i64=0-36 |
| 415 | const JR_ERR_IO:i64=0-5 |
functions
| 108 | func jr_slen(p: *u8) -> i64 |
| 116 | func jr_has(buf: *u8, n: i64, pat: *u8) -> i64 |
| 130 | func jr_prefix(s: *u8, pre: *u8) -> i64 |
| 139 | func jr_streq(a: *u8, b: *u8) -> i64 |
| 154 | func jr_state_of(buf: *u8, n: i64) -> i64 |
| 165 | func jr_state_of_path(path: *u8, sc: *i64) -> i64 |
| 174 | func jr_state_name(st: i64) -> *u8 |
| 183 | func jr_decision_name(d: i64) -> *u8 |
| 197 | func jr_decision_why(d: i64) -> *u8 called by 1: main |
| 215 | func jr_num_from(buf: *u8, n: i64, seen: *i64) -> i64 |
| 229 | func jr_atoi_strict(s: *u8) -> i64 |
| 249 | func jr_maxage(defaulted: *i64, scratch: *i64) -> i64 |
| 271 | func jr_dir_allowed(dir: *u8) -> i64 |
| 280 | func jr_cat(d: *u8, o: i64, s: *u8) -> i64 |
| 287 | func jr_catnum(d: *u8, o: i64, v: i64) -> i64 |
| 300 | func jr_mkpath(buf: *u8, cap: i64, dir: *u8, id: i64, ext: *u8) -> i64 called by 14: jc_childmainmainjr_examinejr_reservejr_slot_occupied+8 calls 3: jr_slenjr_catjr_catnum |
| 327 | func jr_tombstone(path: *u8, now: i64, age: i64, need_nl: i64, line: *u8, lcap: i64) -> i64 |
| 353 | func jr_decide(state: i64, age: i64, maxage: i64, has_out: i64) -> i64 called by 1: jr_examine |
| 364 | func jr_examine(dir: *u8, id: i64, now: i64, maxage: i64, info: *i64, pbuf: *u8, pcap: i64, sc: *i64) -> i64 called by 2: jr_reap_onemain calls 7: jr_mkpathsys_read_filejr_state_ofsys_free_filesys_openat_rdsys_close+1 |
| 395 | func jr_reap_one(dir: *u8, id: i64, now: i64, maxage: i64, dry: i64, |
| 416 | func jr_reserve(dir:*u8,first:i64,deadline_ms:i64,path:*u8,pathcap:i64,line:*u8,linecap:i64,out:*i64)->i64 |
| 464 | func jr_slot_occupied(dir:*u8,id:i64,path:*u8,cap:i64,ext:*u8)->i64 |