feat(scheduler-sim-live-allocation): add unsatisfiable-cache callback seam (task 1.1)

Adds a NULL-by-default lookup/populate callback seam on xce_sched_core_t,
mirroring cache_clear_cb's proven shape, so the allocation dispatch
chokepoint can consult the unsatisfiable-resreq cache without a
normal-mode-only field access in shared code. Adapters live in
alloc_cache.c and are registered only during normal-mode startup;
simulation mode leaves both callbacks NULL and always computes fresh.

Adds the scheduler-sim-live-allocation spec (requirements/design/
research/tasks) as the planning artifact this and subsequent tasks
implement against.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
Rongsong Shen 2026-09-18 10:14:04 +00:00
parent 487e7b9419
commit 89297dc825
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
10 changed files with 1130 additions and 0 deletions

View file

@ -109,6 +109,61 @@ typedef int (*xce_core_decision_cb_t)(void*, xce_decision_t*);
*/
typedef void (*xce_core_cache_clear_cb_t)(xce_sched_core_t*);
/*
* xce_unsatisfiable_lookup_cb_t / xce_unsatisfiable_populate_cb_t -
* driver-registered, OPTIONAL per-session unsatisfiable-resreq cache
* consult/populate seam (scheduler-sim-live-allocation spec, task 1.1,
* Req 5.2). Mirrors xce_core_cache_clear_cb_t's own shape and rationale
* immediately above exactly: a bare function pointer taking
* xce_sched_core_t* directly, no wrapper struct, no opaque "impl"/
* user-data pointer -- single registrant (main.c's normal-mode branch),
* no foreseeable second implementation.
*
* Parameter shape mirrors the REAL cache's own existing entry points
* (alloc_cache.c's cache_lookup()/cache_insert(), design.md's
* "Unsatisfiable-Cache Callback Seam" component) rather than inventing a
* new bare-integer "fingerprint" type: xce_sched_obj_t* is the exact
* object cache_lookup()/cache_insert() already key on (its embedded
* xce_resreq_fingerprint_t plus user/queue/project identity, sched/
* alloc_cache.h) -- this seam adds no new type, only a NULL-safe
* indirection over the existing one. xce_sched_obj_t is already visible
* here as a forward-declared pointer type (sched/types.h, included
* above); only a pointer, never the full struct, is needed in this
* header.
*
* WHY a hook instead of a direct call (mirrors xce_core_cache_clear_cb_t's
* own documented incident above): the allocation dispatch chokepoint this
* seam wraps (sched_make_primary_alloc_(), scheduler.c) is reached
* identically by both drivers once a later task's Core-Safe Allocation
* Chain migration lands -- but the real cache
* (xce_scheduler_t.unsatisfiable_cache) exists ONLY on a normal-mode
* xce_scheduler_t, never on a simulation-mode xce_sched_sim_driver_t. A
* NULL-checked, driver-registered callback keeps that difference behind
* an explicit seam instead of a struct-prefix cast into mode-specific
* state from code both drivers share -- the same anti-pattern
* xce_core_cache_clear_cb_t's own doc comment documents a real, prior
* memory-corruption incident from.
*
* NULL (the xce_sched_core_init() memset() default, unless a driver
* registers one) means "always compute fresh" for BOTH callbacks -- never
* an error, never a degraded-correctness path, only a degraded-
* performance one. Wiring the actual consult/populate call sites into
* sched_make_primary_alloc_() is a LATER task's job (tasks.md 2.2,
* boundary "Core-Safe Allocation Chain, Unsatisfiable-Cache Callback
* Seam") -- this task adds only the seam (these two fields, their
* adapters in alloc_cache.c, and main.c's normal-mode-only registration),
* not that call site. Every simulation-mode core leaves both fields NULL
* forever (sim_loop.c never registers them, Req 5 AC 17 "Simulation
* isolation"), so a simulation-mode session never touches the real cache
* implementation at all.
*/
typedef int (*xce_unsatisfiable_lookup_cb_t)(xce_sched_core_t* core,
xce_sched_obj_t* obj,
int* out_reason_category);
typedef void (*xce_unsatisfiable_populate_cb_t)(xce_sched_core_t* core,
xce_sched_obj_t* obj,
int reason_category);
struct xce_sched_core_s {
mylock_t lock;
int stop;
@ -159,6 +214,18 @@ struct xce_sched_core_s {
* call -- true for every simulation-mode xce_sched_core_t, which never
* registers this hook. */
xce_core_cache_clear_cb_t cache_clear_cb;
/* Req 5.2 (scheduler-sim-live-allocation spec, task 1.1): driver-
* registered, OPTIONAL unsatisfiable-resreq cache consult/populate
* seam -- see xce_unsatisfiable_lookup_cb_t/xce_unsatisfiable_
* populate_cb_t's own doc comment above for the full rationale. NULL
* (xce_sched_core_init()'s memset() default) until main.c's
* normal-mode branch registers both, alongside cache_clear_cb just
* above. A simulation-mode core never registers either, so
* consulting/populating this seam is always a true no-op for sim
* mode ("always compute fresh"), never a fallback onto real cache
* state. */
xce_unsatisfiable_lookup_cb_t unsatisfiable_lookup_cb;
xce_unsatisfiable_populate_cb_t unsatisfiable_populate_cb;
_Atomic int session_active; /* 1 during scheduling session, 0 between */
_Atomic int eval_in_progress; /* 1 while eval_select loop runs */
_Atomic int staging_merge_complete; /* 1 after staging merge completes */