feat(scheduler-allocation): clear unsatisfiable-resreq cache per session
Clear the cache inside core_begin_session_internal() so a job cached as unsatisfiable in one session is re-evaluated fresh in the next (Req 7.2). An initial weak-symbol cast-based approach was rejected: it corrupted xce_sched_sim_driver_t.alt_events_file, since core_begin_session_internal() is shared between driver.c (real xce_scheduler_t) and sim_loop.c (a structurally different, deliberately isolated sim driver struct with no cache field at the same byte offset) -- the strong cache_clear() symbol won unconditionally for both, invisible to ASAN and untested by any test_sim_*.c file. Fixed via a driver-registered callback: xce_sched_core_t gains a cache_clear_cb hook (NULL by default, per xce_sched_core_init()'s full-struct memset), set only in main.c's driver-mode branch -- mirroring the existing on_decision/xce_core_set_decision_cb() pattern. --sim mode returns before that registration is ever reached, so its hook stays NULL and core_begin_session_internal()'s guarded call is a no-op. The real xce_scheduler_t-casting adapter lives in alloc_cache.c rather than driver.c, since ~11 tests white-box-include driver.c without linking alloc_cache.c. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
This commit is contained in:
parent
5c022d0b4f
commit
e8c2087d5e
9 changed files with 614 additions and 4 deletions
|
|
@ -69,6 +69,46 @@ typedef struct xce_sched_core_s xce_sched_core_t;
|
|||
*/
|
||||
typedef int (*xce_core_decision_cb_t)(void*, xce_decision_t*);
|
||||
|
||||
/*
|
||||
* xce_core_cache_clear_cb_t - driver-registered per-session cache-clear
|
||||
* hook (scheduler-allocation spec, task 5.2 round 2, Req 7.2):
|
||||
* `void (*)(xce_sched_core_t*)`. Mirrors xce_core_decision_cb_t's
|
||||
* driver-registered-hook idiom just above, but generic on purpose -- no
|
||||
* `void* driver_ctx` companion field, because the sole real
|
||||
* implementation (alloc_cache.c's xce_alloc_cache_clear_cb() -- see that
|
||||
* function's own doc comment for why it lives in alloc_cache.c rather
|
||||
* than driver.c) recovers its owning xce_scheduler_t* via the
|
||||
* pointer-to-first-member idiom this codebase already uses for this exact
|
||||
* struct (scheduler.h: `xce_sched_core_t core` is xce_scheduler_t's first
|
||||
* member), not via an opaque context pointer.
|
||||
*
|
||||
* WHY a hook instead of a direct call (task 5.2 round 1's CRITICAL defect):
|
||||
* core_begin_session_internal() (core_session.c) is reached from BOTH
|
||||
* driver.c's xce_sched_driver_run_normal_mode_round() (a real
|
||||
* xce_scheduler_t*) and sim_loop.c's sched_sim_driver_run() (a
|
||||
* structurally different xce_sched_sim_driver_t*, sim.h -- Req 5 AC 17
|
||||
* "Simulation isolation" -- which has NO unsatisfiable_cache field at
|
||||
* all). Round 1 called alloc_cache.c's real, strong
|
||||
* cache_clear(xce_scheduler_t*) unconditionally for both callers (routed
|
||||
* through a same-named weak fallback that the strong definition always
|
||||
* overrides at link time in the production binary) -- since link-time
|
||||
* weak/strong resolution picks ONE winner process-wide, not per call
|
||||
* site, every simulation-mode session reinterpreted whatever field sits
|
||||
* at unsatisfiable_cache's byte offset in xce_sched_sim_driver_t
|
||||
* (alt_events_file, a live FILE*) as a kvtab_t* and clobbered it.
|
||||
*
|
||||
* A per-core-instance function pointer fixes this at the data-ownership
|
||||
* level: each xce_sched_core_t only ever carries the hook ITS OWN driver
|
||||
* installed via xce_core_set_cache_clear_cb() below. main.c installs
|
||||
* alloc_cache.c's xce_alloc_cache_clear_cb() on the real xce_scheduler_t's
|
||||
* core; sim_loop.c never calls the setter at all, so a simulation-mode core's
|
||||
* hook stays NULL (xce_sched_core_init()'s memset() default) and
|
||||
* core_begin_session_internal() treats NULL as "this driver has no cache
|
||||
* to clear" and skips the call entirely -- a true no-op, not an
|
||||
* accidental type-confused one.
|
||||
*/
|
||||
typedef void (*xce_core_cache_clear_cb_t)(xce_sched_core_t*);
|
||||
|
||||
struct xce_sched_core_s {
|
||||
mylock_t lock;
|
||||
int stop;
|
||||
|
|
@ -110,6 +150,15 @@ struct xce_sched_core_s {
|
|||
* reads these fields today. */
|
||||
void* driver_ctx;
|
||||
xce_core_decision_cb_t on_decision;
|
||||
/* Req 7.2 (scheduler-allocation spec, task 5.2 round 2): driver-
|
||||
* registered per-session cache-clear hook -- see
|
||||
* xce_core_cache_clear_cb_t's own doc comment above for the full
|
||||
* rationale. NULL until a driver registers one (xce_core_set_
|
||||
* cache_clear_cb()); core_begin_session_internal() (core_session.c)
|
||||
* treats NULL as "this driver has no cache to clear" and skips the
|
||||
* call -- true for every simulation-mode xce_sched_core_t, which never
|
||||
* registers this hook. */
|
||||
xce_core_cache_clear_cb_t cache_clear_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 */
|
||||
|
|
@ -424,6 +473,21 @@ extern int xce_core_push_events(xce_sched_core_t*, xce_core_event_t*, int);
|
|||
extern int xce_core_set_decision_cb(xce_sched_core_t*, xce_core_decision_cb_t,
|
||||
void*);
|
||||
|
||||
/*
|
||||
* xce_core_set_cache_clear_cb - registers the driver's per-session
|
||||
* cache-clear hook (scheduler-allocation spec, task 5.2 round 2, Req 7.2).
|
||||
* Same calling convention as xce_core_set_decision_cb() above (may be
|
||||
* called at any time, typically once at driver startup before the first
|
||||
* xce_core_begin_session(); returns -EINVAL if core is NULL) -- no
|
||||
* `driver_ctx` companion parameter, since xce_core_cache_clear_cb_t's own
|
||||
* doc comment (above) explains why the real implementation needs none.
|
||||
* Never called by sim_loop.c -- see that typedef's doc comment for why
|
||||
* leaving this hook unset is the correct, safe default for simulation
|
||||
* mode.
|
||||
*/
|
||||
extern int xce_core_set_cache_clear_cb(xce_sched_core_t*,
|
||||
xce_core_cache_clear_cb_t);
|
||||
|
||||
/*
|
||||
* xce_core_begin_session / xce_core_signal_go - Req 5 AC 5 (task 4.4) split
|
||||
* into two exported halves (task 5.2, Req 1 AC 7 / Req 5 AC 9) so a driver
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue