feat(libxce-alloc-preason-equality): full-match job_preason_eq with first-difference description (task 2.2)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-09-28 09:49:57 +00:00
parent a6cbb323b0
commit 62d0b8b686
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
5 changed files with 648 additions and 2 deletions

View file

@ -12,4 +12,45 @@ extern void erase_job_preason(xce_job_preason_t*);
extern int add_job_preason(xce_job_preason_t*, int, char*, char*);
extern xce_pendreason_t* make_pendreason(uint64_t);
/*
* Pending-reason equality: "are these two pending-reason records identical".
*
* job_preason_eq() is a FULL structural match. It compares, in this order:
* main_reason; nhost_reasons, then host_based_reasons[i] in order, each by
* host then reason; nres_reasons, then res_based_reasons[i] in order, each
* by resource, host, then reason. Nothing is excluded. Lists are
* order-sensitive: the same entries in a different order are not equal.
*
* The scheduler simulator (--sim-verify, sim_verify.c) deliberately does NOT
* use this function for its PEND outcome check: it compares main_reason only
* (bug-1845), because a recorded alloc-cache-hit PEND carries only the main
* reason while the simulator (which has no alloc cache) always builds the
* full detail lists. That loosening is a replay property, not object
* identity, so it stays simulator-local.
*
* NULL handling: identical pointers (including two NULLs) are equal; exactly
* one NULL argument, list entry or string field is not equal. Precondition:
* a non-zero count with a NULL entry array is a malformed record and is not
* handled. Neither argument is modified.
*
* `why` is optional. When NULL, nothing is allocated. Otherwise *why is set
* to NULL when the arguments are equal, or to a newly allocated,
* NUL-terminated description of the first difference found, in comparison
* order, e.g.
* main_reason: 5 != 6
* nhost_reasons: 2 != 1
* host_based_reasons[1](host=h2).host: "h2" != "h9"
* res_based_reasons[0](resource=mem).reason: 3 != 8
* res_based_reasons[0]: non-NULL != NULL
* job_preason: NULL != non-NULL (a NULL top-level argument)
* Path labels come from `a`, from `b` when a's string is NULL, or are `?`.
* The caller owns the description and releases it with free(). The value
* *why holds on entry is never read or freed. If allocating the description
* fails, *why is NULL and the result is unchanged. Nothing is logged.
*
* Returns non-zero when equal, 0 when not.
*/
extern int job_preason_eq(const xce_job_preason_t* a,
const xce_job_preason_t* b, char** why);
#endif