fix(scheduler): cache hits keep the full pend reason; sim uses the cache (bug-1932)
The session-level unsatisfiable-resreq cache stored only the exhausted attempt's main reason (an int). The allocators record shortfalls as per-host/per-resource entries and leave main_reason at 0, so every cache hit in the live scheduler pended its job with an empty reason. The simulator registered no cache at all (its table lived on xce_scheduler_t), so it searched every identical pending job again and its traces differed from production. - The cache table moves to xce_sched_core_t; cache_lookup/insert/clear and the hook adapters take the core, with no cast to a driver struct. sched_init() builds it, cache_clear() rebuilds it per session, xce_sched_core_fini() destroys it. - A cache entry is a deep copy of the attempt's full xce_job_preason_t plus the job that proved the request unsatisfiable (core->alloc_jobid, set by sched_allocator()). A hit moves a copy into ctx->reason; a miss leaves ctx->reason untouched. - main.c passes the same three adapters to the simulator through new sched_sim_args_t fields; sched_sim_driver_start() sets them on the sim core (plain field writes: white-box tests include sim.c alone). - Decision trace: a hit renders "no plan generated: skipped, same request as <job> proved unsatisfiable earlier this session" (new XCE_TRACE_NOPLAN_KNOWN_UNSAT via sched_trace_known_unsat()); the outcome shows the restored reasons. - sim_verify keeps its main-only PEND compare (old recordings carry main-only hits); comments and the scheduler-sim-live-allocation design note updated. Tests: full-reason assertions in the cache dispatch tests (RED before: "a cache hit must restore the original attempt's FULL pend reason"), test_alloc_cache.c ported to the new API (deep copy, clear frees, -EEXIST frees), a sim-registered clear test, a renderer test, and a sim e2e scenario with two identical unsatisfiable jobs (204, 205) where 205 is skipped with 204's full reasons (RED before: no skip line). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cff3304f5d
commit
0317c7af98
27 changed files with 982 additions and 737 deletions
|
|
@ -73,97 +73,53 @@ 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.
|
||||
* `void (*)(xce_sched_core_t*)`, called by core_begin_session_internal()
|
||||
* (core_session.c) when non-NULL. Both drivers register alloc_cache.c's
|
||||
* xce_alloc_cache_clear_cb() (main.c; sched_sim_driver_start() from the
|
||||
* caller's sched_sim_args_t), which empties THIS core's own
|
||||
* unsatisfiable_cache (bug-1932).
|
||||
*
|
||||
* 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.
|
||||
* WHY a hook instead of a direct call: task 5.2 round 1 called a
|
||||
* cache_clear(xce_scheduler_t*) unconditionally for both drivers, so every
|
||||
* simulation-mode session reinterpreted xce_sched_sim_driver_t's
|
||||
* alt_events_file FILE* as the cache and clobbered it. The table now lives
|
||||
* on the core and no hook casts the core to a driver struct, but the hook
|
||||
* stays: white-box tests that link core_session.c without alloc_cache.c
|
||||
* leave it NULL, and NULL means "no cache to clear".
|
||||
*/
|
||||
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.
|
||||
* driver-registered, OPTIONAL unsatisfiable-resreq cache consult/populate
|
||||
* seam (scheduler-sim-live-allocation spec, task 1.1, Req 5.2) around the
|
||||
* allocation dispatch chokepoint, sched_make_primary_alloc_()
|
||||
* (scheduler.c). `obj` is the object cache_lookup()/cache_insert()
|
||||
* (sched/alloc_cache.h) key on: its resreq fingerprint plus user/queue/
|
||||
* project.
|
||||
*
|
||||
* 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.
|
||||
* bug-1932: a hit must report the original attempt's FULL pend reason,
|
||||
* not only its main reason (in production the main reason is usually 0
|
||||
* and the real cause is per-host, so a main-only hit pended later jobs
|
||||
* with an empty reason). Lookup returns 1 on a hit and replaces
|
||||
* *out_reasons with a copy of the stored reason, *out_jobid with the job
|
||||
* whose search proved the request unsatisfiable; 0 on a miss (outputs
|
||||
* untouched). Populate stores a copy of `reasons` (NULL: empty) for
|
||||
* `jobid`.
|
||||
*
|
||||
* 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.
|
||||
* Both drivers register alloc_cache.c's adapters (bug-1932: the
|
||||
* simulator used to register none, so it searched every identical pending
|
||||
* job again and its traces differed from production). NULL (the
|
||||
* xce_sched_core_init() memset() default) means "always compute fresh" --
|
||||
* a performance difference only, never a correctness one.
|
||||
*/
|
||||
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);
|
||||
typedef int (*xce_unsatisfiable_lookup_cb_t)(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj,
|
||||
xce_job_preason_t* out_reasons,
|
||||
uint64_t* out_jobid);
|
||||
typedef void (*xce_unsatisfiable_populate_cb_t)(
|
||||
xce_sched_core_t* core, xce_sched_obj_t* obj,
|
||||
const xce_job_preason_t* reasons, uint64_t jobid);
|
||||
|
||||
/* Reserved jbuf sentinel for the Recorded-Decision Reconciliation
|
||||
* allocator-thread relay (bug-1837) -- see xce_sched_core_s's reconcile_req/
|
||||
|
|
@ -238,25 +194,25 @@ struct xce_sched_core_s {
|
|||
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 above. NULL until a driver registers one
|
||||
* (xce_core_set_cache_clear_cb()). */
|
||||
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. */
|
||||
/* Req 5.2 (scheduler-sim-live-allocation spec, task 1.1): the
|
||||
* unsatisfiable-resreq cache consult/populate seam -- see
|
||||
* xce_unsatisfiable_lookup_cb_t above. NULL until a driver registers
|
||||
* both (both drivers do, bug-1932). */
|
||||
xce_unsatisfiable_lookup_cb_t unsatisfiable_lookup_cb;
|
||||
xce_unsatisfiable_populate_cb_t unsatisfiable_populate_cb;
|
||||
/* bug-1932: the session-level unsatisfiable-resreq cache itself
|
||||
* (sched/alloc_cache.h), owned by the core so both drivers have one.
|
||||
* Built and emptied by cache_clear() at every session start, destroyed
|
||||
* by xce_sched_core_fini(); NULL until the first clear. Allocator
|
||||
* thread only, plus the clear between sessions. */
|
||||
var unsatisfiable_cache;
|
||||
/* bug-1932: the job sched_make_allocation() is working on, set by
|
||||
* sched_allocator() (sched_election.c) so a cache populate can record
|
||||
* which job proved a request unsatisfiable. 0 when unknown. */
|
||||
uint64_t alloc_jobid;
|
||||
_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 */
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@
|
|||
*
|
||||
* Key shape: xce_resreq_fingerprint_t (sched/types.h, already populated on
|
||||
* xce_primary_schedobj_t.resreq_fp at job-ingest time -- see task 1.2)
|
||||
* composed with job identity (user/queue/project). Value: the ORIGINAL
|
||||
* exhausted attempt's pend-reason category (an int code), not a bare
|
||||
* present/absent marker -- Req 7.5 requires a cache hit to report the same
|
||||
* reason category the original miss determined, so operators are not
|
||||
* misled about why a later job was skipped.
|
||||
* composed with job identity (user/queue/project). Value: a deep copy of
|
||||
* the ORIGINAL exhausted attempt's full pend reason (main, per-host and
|
||||
* per-resource entries) plus the job whose search proved the request
|
||||
* unsatisfiable -- Req 7.5 requires a cache hit to report the same reason
|
||||
* the original miss determined, and bug-1932 found that keeping only the
|
||||
* main-reason int pended later jobs with an empty reason whenever the real
|
||||
* cause was per-host.
|
||||
*
|
||||
* Backed by this codebase's existing keyed-table infrastructure (kv.h's
|
||||
* kvtab_type/key_find()/key_add()) via a THIRD kv_key_ops_t instance
|
||||
|
|
@ -28,23 +30,16 @@
|
|||
* (cache_key_assign_(), below) and returns >=0 on success, -EEXIST if the
|
||||
* exact key is already present.
|
||||
*
|
||||
* This header intentionally exposes only what design.md's Service
|
||||
* Interface specifies (cache_lookup/cache_insert/cache_clear) plus the
|
||||
* key type and key ops -- no separate cache-construct/-destroy wrapper.
|
||||
* xce_scheduler_t.unsatisfiable_cache (scheduler.h) is constructed and
|
||||
* destroyed directly by sched_init()/sched_fini() (scheduler.c) via
|
||||
* makeInstance(&kvtab_type, ¶ms)/destroy(), using the exported
|
||||
* cache_key_ops below and the XCE_UNSAT_CACHE_NSLOTS slot count -- the
|
||||
* exact same convention xce_select.c's sel_node_table_init() already
|
||||
* establishes for its own composite-struct-keyed kvtab_t
|
||||
* (sel_node_key_ops). This cache's own unit test (test_alloc_cache.c)
|
||||
* constructs/destroys its fixture's table identically, so the two never
|
||||
* drift.
|
||||
* The table is xce_sched_core_t.unsatisfiable_cache (core.h, bug-1932:
|
||||
* moved off xce_scheduler_t so the simulation driver has one too).
|
||||
* cache_clear() builds and empties it at every session start, through the
|
||||
* per-session clear hook both drivers register; xce_sched_core_fini()
|
||||
* destroys it. Its datakiller frees each entry.
|
||||
*/
|
||||
|
||||
#include <kv.h>
|
||||
|
||||
#include "scheduler.h"
|
||||
#include "core.h"
|
||||
|
||||
/*
|
||||
* xce_cache_key_t - full cache key: the pre-computed resreq fingerprint
|
||||
|
|
@ -63,100 +58,87 @@ typedef struct {
|
|||
|
||||
/*
|
||||
* cache_key_ops - the kv_key_ops_t (kv.h) binding hash/assign/cmp/destroy
|
||||
* for xce_cache_key_t. Exported so scheduler.c's sched_init()/sched_fini()
|
||||
* (the real construct/destroy call sites for xce_scheduler_t.
|
||||
* unsatisfiable_cache) and this cache's own test can build an identical
|
||||
* kvtab_t independently.
|
||||
* for xce_cache_key_t; cache_clear() builds the table with it.
|
||||
*/
|
||||
extern kv_key_ops_t cache_key_ops;
|
||||
|
||||
/*
|
||||
* XCE_UNSAT_CACHE_NSLOTS - slot count for the kvtab_t backing
|
||||
* xce_scheduler_t.unsatisfiable_cache. Shared by every construction call
|
||||
* site (sched_init(), cache_clear()'s destroy+recreate, and this cache's
|
||||
* own test) so they never drift apart. kv.c's kvtab_init() requires
|
||||
* xce_sched_core_t.unsatisfiable_cache. kv.c's kvtab_init() requires
|
||||
* nslots > 1.
|
||||
*/
|
||||
#define XCE_UNSAT_CACHE_NSLOTS 257
|
||||
|
||||
/*
|
||||
* cache_lookup - Req 7.4: before starting full allocation for a job, look
|
||||
* up its resreq fingerprint + identity in the cache.
|
||||
* up its resreq fingerprint + identity in core's cache.
|
||||
*
|
||||
* Preconditions: obj->tag == XCE_RESREQ_PRIMARY; obj->u.primary->resreq_fp
|
||||
* already populated (job-ingest time, core_build_primary_schedobj(),
|
||||
* core_events.c -- task 1.2).
|
||||
*
|
||||
* Returns:
|
||||
* 1 - hit; *out_reason_category is set to the reason category the
|
||||
* ORIGINAL exhausted attempt stored at cache_insert() time (Req
|
||||
* 7.5). Caller pends the job using this category, without
|
||||
* invoking the plan/host iterators or preemption for this attempt.
|
||||
* 0 - miss (including: cache not yet constructed).
|
||||
* 1 - hit; *out is erased and refilled with a copy of the reason the
|
||||
* ORIGINAL exhausted attempt stored (Req 7.5, bug-1932) and
|
||||
* *out_jobid set to that attempt's job. Caller pends the job with
|
||||
* it, without invoking the plan/host iterators for this attempt.
|
||||
* A copy that runs out of memory part way keeps the entries copied
|
||||
* so far.
|
||||
* 0 - miss (including: cache not yet built); *out untouched.
|
||||
* <0 - error (NULL argument, or obj is not a populated primary resreq
|
||||
* scheduling object).
|
||||
*/
|
||||
extern int cache_lookup(xce_scheduler_t* sched, xce_sched_obj_t* obj,
|
||||
int* out_reason_category);
|
||||
extern int cache_lookup(xce_sched_core_t* core, xce_sched_obj_t* obj,
|
||||
xce_job_preason_t* out, uint64_t* out_jobid);
|
||||
|
||||
/*
|
||||
* cache_insert - Req 7.3: when a job's primary resreq's plan iterator is
|
||||
* exhausted without a successful allocation, populate the cache with that
|
||||
* resreq's fingerprint + identity and the exhausted attempt's own final
|
||||
* pend-reason category (the exhausted plan iterator's own final pend
|
||||
* reason's main_reason), so a later cache hit for the same key reports the
|
||||
* same reason (Req 7.5).
|
||||
* resreq's fingerprint + identity, a deep copy of the attempt's pend
|
||||
* reason (`reasons`; NULL stores an empty one) and `jobid`, so a later hit
|
||||
* for the same key reports the same reason (Req 7.5).
|
||||
*
|
||||
* Returns 0 on success, <0 on error (NULL argument; obj is not a populated
|
||||
* primary resreq scheduling object; -EEXIST if this exact key is already
|
||||
* cached; the cache is not yet constructed).
|
||||
* Returns 0 on success, <0 on error (NULL core/obj; obj is not a
|
||||
* populated primary resreq scheduling object; -EEXIST if this exact key
|
||||
* is already cached; -ENOMEM; the cache is not yet built). Nothing is
|
||||
* stored on error.
|
||||
*/
|
||||
extern int cache_insert(xce_scheduler_t* sched, xce_sched_obj_t* obj,
|
||||
int reason_category);
|
||||
extern int cache_insert(xce_sched_core_t* core, xce_sched_obj_t* obj,
|
||||
const xce_job_preason_t* reasons, uint64_t jobid);
|
||||
|
||||
/*
|
||||
* cache_clear - Req 7.2 (wiring into core_begin_session_internal() is a
|
||||
* later task -- tasks.md 5.2, not this one): empty every entry from the
|
||||
* cache. Called once at the start of every scheduling session.
|
||||
* cache_clear - Req 7.2: empty every entry from core's cache, building the
|
||||
* table on first use. Called once at the start of every scheduling
|
||||
* session through xce_alloc_cache_clear_cb().
|
||||
*
|
||||
* kv.h's intf_kv_t exposes only add_key/del_key/find_key -- no batch
|
||||
* clear-all primitive exists on kvtab_t. This implementation destroys the
|
||||
* existing backing table and constructs a fresh, empty one with the same
|
||||
* cache_key_ops/XCE_UNSAT_CACHE_NSLOTS in its place, which is functionally
|
||||
* equivalent to clearing every entry. If reconstruction fails (e.g.
|
||||
* existing backing table (its datakiller frees every entry) and constructs
|
||||
* a fresh, empty one in its place. If reconstruction fails (e.g.
|
||||
* makeInstance() returns NULL on -ENOMEM), the cache is left NULL:
|
||||
* cache_lookup() treats a NULL cache as an unconditional miss and
|
||||
* cache_insert() returns an error, so a failed clear degrades to "cache
|
||||
* always misses" rather than crashing or operating on a stale table.
|
||||
*/
|
||||
extern void cache_clear(xce_scheduler_t* sched);
|
||||
extern void cache_clear(xce_sched_core_t* core);
|
||||
|
||||
/*
|
||||
* xce_alloc_cache_clear_cb - the xce_core_cache_clear_cb_t (core.h)
|
||||
* adapter for cache_clear() above (scheduler-allocation spec, task 5.2
|
||||
* round 2, Req 7.2). Registered on the real xce_scheduler_t's core via
|
||||
* xce_core_set_cache_clear_cb() (main.c). See alloc_cache.c's own doc
|
||||
* comment on this function for the full rationale, including why it lives
|
||||
* xce_alloc_cache_clear_cb / xce_alloc_cache_lookup_cb /
|
||||
* xce_alloc_cache_populate_cb - the xce_core_cache_clear_cb_t /
|
||||
* xce_unsatisfiable_lookup_cb_t / xce_unsatisfiable_populate_cb_t (core.h)
|
||||
* adapters for cache_clear()/cache_lookup()/cache_insert() on the given
|
||||
* core. Both drivers register all three (main.c directly; the simulation
|
||||
* driver from its sched_sim_args_t). See alloc_cache.c for why they live
|
||||
* here rather than in driver.c.
|
||||
*/
|
||||
extern void xce_alloc_cache_clear_cb(xce_sched_core_t* core);
|
||||
|
||||
/*
|
||||
* xce_alloc_cache_lookup_cb / xce_alloc_cache_populate_cb - the
|
||||
* xce_unsatisfiable_lookup_cb_t / xce_unsatisfiable_populate_cb_t (core.h)
|
||||
* adapters for cache_lookup()/cache_insert() above (scheduler-sim-live-
|
||||
* allocation spec, task 1.1, Req 5.2). Registered on the real
|
||||
* xce_scheduler_t's core via direct field assignment in main.c's
|
||||
* normal-mode-only startup branch, alongside xce_alloc_cache_clear_cb()
|
||||
* just above. See alloc_cache.c's own doc comment on these functions for
|
||||
* the full rationale, including why they live here rather than in
|
||||
* driver.c.
|
||||
*/
|
||||
extern int xce_alloc_cache_lookup_cb(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj,
|
||||
int* out_reason_category);
|
||||
extern void xce_alloc_cache_populate_cb(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj,
|
||||
int reason_category);
|
||||
extern int xce_alloc_cache_lookup_cb(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj,
|
||||
xce_job_preason_t* out_reasons,
|
||||
uint64_t* out_jobid);
|
||||
extern void xce_alloc_cache_populate_cb(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj,
|
||||
const xce_job_preason_t* reasons,
|
||||
uint64_t jobid);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -132,7 +132,8 @@ typedef struct {
|
|||
typedef enum {
|
||||
XCE_TRACE_NOPLAN_DECLINED, /* allocator accepts() declined */
|
||||
XCE_TRACE_NOPLAN_GENERATOR_FAILED, /* plan_init failed */
|
||||
XCE_TRACE_NOPLAN_NONE_GENERATED /* generator produced no plan */
|
||||
XCE_TRACE_NOPLAN_NONE_GENERATED, /* generator produced no plan */
|
||||
XCE_TRACE_NOPLAN_KNOWN_UNSAT /* bug-1932: unsatisfiable-cache hit */
|
||||
} xce_sched_trace_noplan_t;
|
||||
|
||||
typedef enum {
|
||||
|
|
@ -314,6 +315,7 @@ typedef struct sched_trace_entry_s {
|
|||
} check_end;
|
||||
struct { /* NO_PLAN */
|
||||
xce_sched_trace_noplan_t why;
|
||||
uint64_t src_jobid; /* KNOWN_UNSAT: the job that proved it */
|
||||
} no_plan;
|
||||
struct { /* PLAN_BEGIN, PLAN_DISCARDED, PLAN_NOT_TRIED */
|
||||
int plan_no; /* 1-based, in generation order per requirement */
|
||||
|
|
@ -630,6 +632,10 @@ extern void sched_trace_check_end(xce_sched_trace_rec_t* rec, int rc,
|
|||
* later in the same requirement makes no statement and counts nothing. */
|
||||
extern void sched_trace_no_plan(xce_sched_trace_rec_t* rec,
|
||||
xce_sched_trace_noplan_t why);
|
||||
/* bug-1932: the same, for a request src_jobid (0: unknown) proved
|
||||
* unsatisfiable earlier this session -- the cache skipped the search. */
|
||||
extern void sched_trace_known_unsat(xce_sched_trace_rec_t* rec,
|
||||
uint64_t src_jobid);
|
||||
|
||||
/* Plans, numbered in generation order within the current requirement. */
|
||||
extern void sched_trace_plan_begin(xce_sched_trace_rec_t* rec,
|
||||
|
|
|
|||
|
|
@ -175,27 +175,6 @@ typedef struct {
|
|||
typedef struct {
|
||||
xce_sched_core_t core;
|
||||
xcecli_ctx_t apictx;
|
||||
/* Req 7.1 (scheduler-allocation spec, task 2.2): the per-session
|
||||
* unsatisfiable-resreq fingerprint cache -- a kvtab_t (kv.h, RTTI `var`)
|
||||
* keyed by xce_cache_key_t (sched/alloc_cache.h), value = a pend-reason
|
||||
* category boxed as (void*)(intptr_t)category (Req 7.5). Constructed in
|
||||
* sched_init(), destroyed in sched_fini() (scheduler.c) -- both minimal
|
||||
* construct/destroy calls only, using the exported cache_key_ops
|
||||
* (sched/alloc_cache.h), mirroring xce_select.c's sel_node_table_init()
|
||||
* convention for a composite-struct-keyed kvtab_t. Lookup/insert
|
||||
* (cache_lookup()/cache_insert(), alloc_cache.c) are confined to the
|
||||
* allocator thread; NULL until sched_init() constructs it.
|
||||
*
|
||||
* NOTE (task 2.2 CONCERN, for a later task's attention): Req 7.2
|
||||
* requires cache_clear() to run inside core_begin_session_internal()
|
||||
* (core_session.c), which only receives xce_sched_core_t* core, not
|
||||
* xce_scheduler_t* -- this field lives on the outer driver struct per
|
||||
* this task's own scope, not on `core` above. Wiring AC 2's clear call
|
||||
* is explicitly a later task (tasks.md 5.2), not this one; whoever
|
||||
* implements it will need to either reach the owning xce_scheduler_t
|
||||
* from `core` (core is this struct's first member, so the standard
|
||||
* pointer-to-first-member idiom applies) or relocate this field. */
|
||||
var unsatisfiable_cache;
|
||||
xce_cluster_config_t
|
||||
cluster_cfg; /* HA candidate list. Empty (candidates_count==0) only
|
||||
* transiently before sched_init() runs, when no
|
||||
|
|
|
|||
|
|
@ -164,6 +164,15 @@ typedef struct {
|
|||
bool interactive; /* --interactive / -i (task 12) */
|
||||
const char* log_path; /* --logfile FILE (optional; NULL means
|
||||
* SCHED_DEFAULT_LOG_PATH) */
|
||||
/* bug-1932: the unsatisfiable-resreq cache hooks (core.h), registered
|
||||
* on the simulation core exactly as main.c registers them on the
|
||||
* normal-mode one, so the simulator skips a request already proved
|
||||
* unsatisfiable this session the way production does. main.c passes
|
||||
* alloc_cache.c's adapters; NULL (white-box tests that do not link
|
||||
* alloc_cache.c) leaves the core computing every attempt fresh. */
|
||||
xce_core_cache_clear_cb_t cache_clear_cb;
|
||||
xce_unsatisfiable_lookup_cb_t unsat_lookup_cb;
|
||||
xce_unsatisfiable_populate_cb_t unsat_populate_cb;
|
||||
} sched_sim_args_t;
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue