From d52c78e4bec31be5a791e841ab6de49c119440f2 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Fri, 18 Sep 2026 13:52:14 +0000 Subject: [PATCH] feat(scheduler-sim-live-allocation): thread core state through allocation context and host search (task 2.1) Retypes xce_sched_ctx_t.sched (sched/types.h) from void* to xce_sched_core_t* outright, and sched_try_alloc_on_host_() (resource.c) to take xce_sched_core_t* directly -- completing what tasks 1.2/1.3 left as temporary &(sched->core) call-site wraps. Removes the two resulting struct-prefix casts entirely (schedobj.c's sched_ctx_set_primary(), host_iter.c's sched_host_iter_next()): both now pass core state through honestly, with no cast. Updates every sched_ctx_init()/sched_try_alloc_on_host_() call site tree-wide (scheduler.c, parjob_allocator.c, seqjob_allocator.c) with zero change to host_iter/parjob/seqjob's own host-iteration/ bucket-construction algorithm. Adds a driver-parity test for sched_try_alloc_on_host_() proving identical grant/deny results from normal-mode-shaped and simulation-mode-shaped core state. Flags (for task 2.3) a confirmed-dead, type-confused write at plugins/limit.c:319 (ctx.sched = plugin->sched) -- never read today, left untouched since limit.c is task 2.3's boundary, not this one's. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC --- .../scheduler-sim-live-allocation/tasks.md | 3 +- src/scheduler/include/sched/types.h | 23 +++- src/scheduler/include/scheduler.h | 11 +- src/scheduler/src/host_iter.c | 22 ++-- src/scheduler/src/plugins/parjob_allocator.c | 9 +- src/scheduler/src/plugins/seqjob_allocator.c | 9 +- src/scheduler/src/resource.c | 30 ++--- src/scheduler/src/schedobj.c | 18 ++- src/scheduler/src/scheduler.c | 14 +- src/scheduler/testsuite/test_host_iter.c | 120 +++++++++++++++++- src/scheduler/testsuite/test_job_update.c | 4 +- .../testsuite/test_parjob_combinatorial.c | 8 +- .../testsuite/test_seqjob_allocator.c | 8 +- .../test_seqjob_rewrap_equivalence.c | 4 +- 14 files changed, 229 insertions(+), 54 deletions(-) diff --git a/.kiro/specs/scheduler-sim-live-allocation/tasks.md b/.kiro/specs/scheduler-sim-live-allocation/tasks.md index fe0b461a..479d84eb 100644 --- a/.kiro/specs/scheduler-sim-live-allocation/tasks.md +++ b/.kiro/specs/scheduler-sim-live-allocation/tasks.md @@ -25,7 +25,7 @@ - _Boundary: Core-Safe Allocation Chain_ - [ ] 2. Core: driver-agnostic allocation search and dispatch -- [ ] 2.1 Thread shared core state through the per-job allocation context and the per-host allocation attempt +- [x] 2.1 Thread shared core state through the per-job allocation context and the per-host allocation attempt - Change the per-job allocation context's driver-reference field to carry shared core state instead of a normal-mode-only pointer, updating its handful of existing cast sites - Re-type the per-host allocation-attempt function (which calls the three capacity checks from 1.3 and the prepare-allocation hook from 1.2) to take shared core state directly - Thread the same change through the existing multi-host allocation search's entry points, preserving their host-iteration and bucket/plan-construction algorithm exactly as-is @@ -47,6 +47,7 @@ - [ ] 2.3 (P) Make global and host-scoped resource-limit matching driver-agnostic - Change the limit plugin's global-limit and host-scoped-limit matching functions to read the new honest core-state reference from 1.2 instead of deriving anything from the plugin's normal-mode-only field + - Also address the confirmed-dead, type-confused write at plugins/limit.c:319 (`ctx.sched = plugin->sched;`, flagged during task 2.1's review — never read back today, but stores an `xce_scheduler_t*`-shaped value into a field that is now genuinely `xce_sched_core_t*`-typed): either populate it honestly from the plugin's new core-state reference, or remove the dead write entirely if nothing in the migrated code path needs it - Keep the existing plugin-hook-registered entry points' external signature unchanged; have them resolve core state from the new field and delegate to the now-driver-agnostic matching logic - TDD: extend the limit plugin's existing test suite with a case that registers the plugin via the simulation-mode entry point and confirms a match still succeeds and still registers the matched limit's usage, where today it would silently no-op - Observable: a limit configured on a job registered via the simulation-mode plugin path is matched and its usage instance created, verifiable by reading the plugin's own configured-limits/usage-instances accessors diff --git a/src/scheduler/include/sched/types.h b/src/scheduler/include/sched/types.h index fe74cdb3..72dd9044 100644 --- a/src/scheduler/include/sched/types.h +++ b/src/scheduler/include/sched/types.h @@ -30,6 +30,16 @@ typedef struct { struct xce_sched_obj_; typedef struct xce_sched_obj_ xce_sched_obj_t; +/* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation + * Chain): forward declaration only -- the full definition lives in core.h. + * Needed here so xce_sched_ctx_t.sched below can be honestly typed + * xce_sched_core_t* instead of a bare void* + cast at every read site. Same + * idiom already used by plugin.h/core.h (an identical repeated typedef is + * valid C11/gnu17 -- see bug-1489 -- so include order relative to core.h/ + * plugin.h doesn't matter here either). */ +struct xce_sched_core_s; +typedef struct xce_sched_core_s xce_sched_core_t; + /* Req 7.1 (scheduler-allocation spec): pre-computed resreq portion of the * unsatisfiable-resreq cache key -- resource-usage shape only, independent * of job identity (user/queue/project, which the full xce_cache_key_t adds @@ -157,7 +167,18 @@ typedef enum { } xce_alloc_mode_t; typedef struct { - void* sched; + /* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation + * Chain): honestly typed as shared core state -- populated by + * sched_ctx_init()'s `arg` parameter (still void* there, since that + * init function is intentionally generic; the implicit void*-to-typed- + * pointer assignment needs no cast). Every caller of sched_ctx_init() + * now passes &(sched->core) (or an already-core-typed pointer, e.g. a + * simulation driver's own &(sim->core)) rather than a bare + * xce_scheduler_t* -- see resource.c's sched_try_alloc_on_host_(), + * host_iter.c's sched_host_iter_next(), and schedobj.c's + * sched_ctx_set_primary() for the leaf/middle consumers this replaces + * a void*+cast with a direct, honest read. */ + xce_sched_core_t* sched; xce_sched_worker_t* worker; xce_sched_obj_t* schedobj; xce_primary_resreq_t* primary; diff --git a/src/scheduler/include/scheduler.h b/src/scheduler/include/scheduler.h index 9a5c25ae..de34d8d5 100644 --- a/src/scheduler/include/scheduler.h +++ b/src/scheduler/include/scheduler.h @@ -360,8 +360,17 @@ extern xce_alloc_t** sched_make_allocs(xce_scheduler_t*, char*, int, int*, * deleted) so both seqjob_allocator.c and parjob_allocator.c can call this * shared Composition-Algorithm try_alloc leaf directly with real * parameters, instead of each carrying its own private copy. */ +/* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation + * Chain): retyped from xce_scheduler_t* to xce_sched_core_t* -- callers + * still owning an xce_scheduler_t* (e.g. seqjob_allocator.c's/parjob_ + * allocator.c's my_alloc()/parjob_try_plan_(), which derive their own + * `sched` from the plugin's separate, untouched xce_sched_plugin_t.sched + * field) pass &(sched->core); both xce_scheduler_t and + * xce_sched_sim_driver_t embed xce_sched_core_t core as their first-class + * field, so either driver's own core state satisfies this signature + * identically. */ extern int sched_try_alloc_on_host_(xce_sched_worker_t*, - xce_scheduler_t*, + xce_sched_core_t*, xce_sched_ctx_t*); extern int get_resvals(zarray_t*, int, int*, float*); extern int put_resvals(zarray_t*, int, int*, float*); diff --git a/src/scheduler/src/host_iter.c b/src/scheduler/src/host_iter.c index 5269ee30..3be6eb90 100644 --- a/src/scheduler/src/host_iter.c +++ b/src/scheduler/src/host_iter.c @@ -86,7 +86,7 @@ xce_sched_worker_t* sched_host_iter_next(xce_host_iter_t* iter) { host_iter_priv_t* priv; xce_sched_ctx_t* ctx; - xce_scheduler_t* sched; + xce_sched_core_t* core; /* Req 3 AC 6: a real cancellation point at least once per invocation * -- scanning/filtering candidate hosts is fast, non-blocking pure @@ -105,8 +105,11 @@ xce_sched_worker_t* sched_host_iter_next(xce_host_iter_t* iter) return NULL; } - ctx = priv->plan->ctx; - sched = (xce_scheduler_t*)ctx->sched; + ctx = priv->plan->ctx; + /* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation + * Chain): ctx->sched is now honestly typed xce_sched_core_t* -- no + * xce_scheduler_t* cast needed to reach it any more. */ + core = ctx->sched; for (;;) { xce_sched_worker_t* w = NULL; @@ -129,14 +132,9 @@ xce_sched_worker_t* sched_host_iter_next(xce_host_iter_t* iter) continue; } - /* 1. Select-expression filter -- existing evaluator, unchanged. - * scheduler-sim-live-allocation task 1.3: sched_meet_host_ - * selection() is now core-typed -- minimal &(sched->core) wrap; - * `sched` itself (derived from ctx->sched) keeps its existing - * xce_scheduler_t* type here, since that field's own migration to - * shared core state is task 2.1's job. */ + /* 1. Select-expression filter -- existing evaluator, unchanged. */ if (!sched_meet_host_selection( - &(sched->core), w, + core, w, (ctx->primary != NULL) ? ctx->primary->select : NULL)) { continue; } @@ -157,9 +155,7 @@ xce_sched_worker_t* sched_host_iter_next(xce_host_iter_t* iter) continue; } - /* task 1.3: sched_check_host_satisfy() is now core-typed too -- - * same minimal &(sched->core) wrap as above. */ - rc = sched_check_host_satisfy(&(sched->core), w, uncomit, + rc = sched_check_host_satisfy(core, w, uncomit, &(ctx->alloc_req.host), &(ctx->unmeet)); if (rc <= 0) { diff --git a/src/scheduler/src/plugins/parjob_allocator.c b/src/scheduler/src/plugins/parjob_allocator.c index 5f769751..646dcbc0 100644 --- a/src/scheduler/src/plugins/parjob_allocator.c +++ b/src/scheduler/src/plugins/parjob_allocator.c @@ -487,7 +487,14 @@ static int parjob_try_plan_(xce_scheduler_t* sched, xce_sched_ctx_t* ctx, ctx->nslots = plan->bucket_slots[b]; ctx->nhosts = 1; - if (sched_try_alloc_on_host_(cand, sched, ctx) > 0) { + /* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe + * Allocation Chain): sched_try_alloc_on_host_() is now + * core-typed -- minimal &(sched->core) wrap; `sched` + * itself keeps its existing xce_scheduler_t* type here + * (derived from plugin->sched via my_alloc(), the + * plugin's own separate, untouched driver-reference + * field), unchanged by this task. */ + if (sched_try_alloc_on_host_(cand, &(sched->core), ctx) > 0) { int rc = parjob_snapshot_host_req_( &(claimed[nclaimed].host_req), &(ctx->alloc_req.host)); diff --git a/src/scheduler/src/plugins/seqjob_allocator.c b/src/scheduler/src/plugins/seqjob_allocator.c index fcd9c127..a0e0a58b 100644 --- a/src/scheduler/src/plugins/seqjob_allocator.c +++ b/src/scheduler/src/plugins/seqjob_allocator.c @@ -304,7 +304,14 @@ static int my_alloc(xce_sched_plugin_t* plugin, xce_sched_ctx_t* ctx) xce_sched_worker_t* cand; while ((cand = host_iter->next(host_iter)) != NULL) { - if (sched_try_alloc_on_host_(cand, sched, ctx) > 0) { + /* scheduler-sim-live-allocation spec, task 2.1 + * (Core-Safe Allocation Chain): sched_try_alloc_on_ + * host_() is now core-typed -- minimal &(sched->core) + * wrap; `sched` itself keeps its existing + * xce_scheduler_t* type here (derived from + * plugin->sched), unchanged by this task. */ + if (sched_try_alloc_on_host_(cand, &(sched->core), ctx) + > 0) { w = cand; break; } diff --git a/src/scheduler/src/resource.c b/src/scheduler/src/resource.c index acce0a3e..ebc1ce1c 100644 --- a/src/scheduler/src/resource.c +++ b/src/scheduler/src/resource.c @@ -494,7 +494,7 @@ static int sched_update_res_accounts(xce_sched_core_t* core, zarray_t* array, * committed want), 0 if this host does not work (a pend reason is * recorded), <0 on infrastructure error. */ -int sched_try_alloc_on_host_(xce_sched_worker_t* w, xce_scheduler_t* sched, +int sched_try_alloc_on_host_(xce_sched_worker_t* w, xce_sched_core_t* core, xce_sched_ctx_t* ctx) { xce_sched_obj_t* schedobj = ctx->schedobj; @@ -502,13 +502,12 @@ int sched_try_alloc_on_host_(xce_sched_worker_t* w, xce_scheduler_t* sched, int rc; xvec_t* uncomit = NULL; - /* scheduler-sim-live-allocation spec, task 1.3: sched_meet_host_ - * selection() is now core-typed -- pass &(sched->core), the same - * minimal call-site update task 1.2 made for plugin_prepare_ - * allocation() just below. sched_try_alloc_on_host_() itself keeps its - * existing xce_scheduler_t* sched parameter unchanged (that retyping - * is task 2.1's job, "Core-Safe Allocation Chain"). */ - rc = sched_meet_host_selection(&(sched->core), w, primary->select); + /* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation + * Chain): sched_try_alloc_on_host_() itself is now core-typed, so its + * calls into sched_meet_host_selection()/plugin_prepare_allocation()/ + * sched_check_host_satisfy() (all already core-typed, tasks 1.2/1.3) + * pass `core` straight through -- no more &(sched->core) wrap. */ + rc = sched_meet_host_selection(core, w, primary->select); if (rc == 0) { if (ctx->reason == NULL) { ctx->reason = make_pendreason(0); @@ -531,23 +530,14 @@ int sched_try_alloc_on_host_(xce_sched_worker_t* w, xce_scheduler_t* sched, ctx->worker = w; - /* scheduler-sim-live-allocation spec, task 1.2: plugin_prepare_ - * allocation() is now core-typed -- pass &(sched->core), not sched, - * the same minimal call-site update plugin_begin_session()/ - * plugin_end_session()'s own earlier migration made at their call - * sites. sched_try_alloc_on_host_() itself keeps its existing - * xce_scheduler_t* sched parameter unchanged (that retyping is a - * separate task's scope, "Core-Safe Allocation Chain"). */ - rc = plugin_prepare_allocation(&(sched->core), ctx); + rc = plugin_prepare_allocation(core, ctx); if (rc < 0) { ctx->worker = NULL; return rc; } - /* task 1.3: sched_check_host_satisfy() is now core-typed too -- same - * minimal &(sched->core) wrap as sched_meet_host_selection() above. */ - rc = sched_check_host_satisfy(&(sched->core), w, uncomit, + rc = sched_check_host_satisfy(core, w, uncomit, &(ctx->alloc_req.host), &(ctx->unmeet)); if (rc < 0) { /* FIXME: */ @@ -561,7 +551,7 @@ int sched_try_alloc_on_host_(xce_sched_worker_t* w, xce_scheduler_t* sched, for (i = 0; i < ctx->unmeet.count; i++) { xce_resource_t* r = NULL; - rc = vget(sched->core.resmgr, ((int*)ctx->unmeet.idxs.data)[i], + rc = vget(core->resmgr, ((int*)ctx->unmeet.idxs.data)[i], (void**)&r); if (rc < 0) { continue; diff --git a/src/scheduler/src/schedobj.c b/src/scheduler/src/schedobj.c index dffe2188..bd13f2e9 100644 --- a/src/scheduler/src/schedobj.c +++ b/src/scheduler/src/schedobj.c @@ -459,6 +459,11 @@ int sched_ctx_init(xce_sched_ctx_t* ctx, void* arg, xce_sched_obj_t* obj) } ctx->schedobj = obj; + /* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation + * Chain): `arg` stays void* here (this init function is intentionally + * generic), but every caller now passes an xce_sched_core_t* (e.g. + * &(sched->core)) rather than a bare xce_scheduler_t* -- the implicit + * void*-to-typed-pointer assignment below needs no cast either way. */ ctx->sched = arg; ctx->uncomitted.shared = (xvec_t*)calloc(1, sizeof(xvec_t)); @@ -503,13 +508,12 @@ void sched_ctx_set_primary(xce_sched_ctx_t* ctx, xce_primary_resreq_t* primary) { ctx->primary = primary; - /* scheduler-sim-live-allocation spec, task 1.2: plugin_prepare_ - * allocation() is now core-typed -- ctx->sched itself keeps its - * existing void* meaning and cast (xce_scheduler_t* today; retyping - * ctx->sched to carry core state directly is a separate task's scope, - * "Core-Safe Allocation Chain", task 2.1); this call site only adds - * the trailing &(...->core) needed for the now-core-typed callee. */ - plugin_prepare_allocation(&(((xce_scheduler_t*)ctx->sched)->core), ctx); + /* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation + * Chain): ctx->sched is now honestly typed xce_sched_core_t* (set by + * sched_ctx_init() above), so this call needs no cast at all -- + * plugin_prepare_allocation() itself was already made core-typed by + * task 1.2. */ + plugin_prepare_allocation(ctx->sched, ctx); return; } diff --git a/src/scheduler/src/scheduler.c b/src/scheduler/src/scheduler.c index 0d33ebaf..b2c2c963 100644 --- a/src/scheduler/src/scheduler.c +++ b/src/scheduler/src/scheduler.c @@ -321,7 +321,14 @@ static int sched_make_primary_allocation(xce_scheduler_t* sched, return -EINVAL; } - sched_ctx_init(&ctx, sched, obj); + /* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation + * Chain): ctx->sched is now honestly typed xce_sched_core_t* -- pass + * &(sched->core), not sched, the same minimal call-site update + * plugin_prepare_allocation()'s own callers made at task 1.2. This + * function's own xce_scheduler_t* sched parameter stays unchanged + * (retyping sched_make_primary_allocation() itself is task 2.2's + * scope, the dispatch chain). */ + sched_ctx_init(&ctx, &(sched->core), obj); sched_ctx_set_primary(&ctx, primary); rc = sched_make_primary_alloc_(sched, &ctx); if (ctx.alloc != NULL) { @@ -345,7 +352,10 @@ static int sched_make_compound_allocation(xce_scheduler_t* sched, xce_sched_ctx_t ctx; int rc = 0; - sched_ctx_init(&ctx, sched, obj); + /* scheduler-sim-live-allocation spec, task 2.1: same minimal + * &(sched->core) call-site update as sched_make_primary_allocation() + * above. */ + sched_ctx_init(&ctx, &(sched->core), obj); for (i = 0; i < resreq->primaries.count; i++) { xce_primary_resreq_t* primary diff --git a/src/scheduler/testsuite/test_host_iter.c b/src/scheduler/testsuite/test_host_iter.c index 35664dd0..be016062 100644 --- a/src/scheduler/testsuite/test_host_iter.c +++ b/src/scheduler/testsuite/test_host_iter.c @@ -49,6 +49,14 @@ #include "sched/alloc_iter.h" #include "scheduler.h" +/* scheduler-sim-live-allocation spec, task 2.1: sim.h only depends on + * core.h (+ stdbool/stdint/stdio, per its own header comment), so it is + * safe to pull in here just for xce_sched_sim_driver_t -- needed to build + * a simulation-mode-shaped container for the driver-parity test added by + * this task (mirrors test.c's own test_core_safe_allocation_chain_driver_ + * parity(), task 1.3). */ +#include "sim.h" + /* Pull in the real xce_sched_core_init()/sched_add_worker() (core.c), * sched_meet_host_selection()/sched_check_host_satisfy()/put_resvals() * (resource.c, the evaluator + availability check under test), the real @@ -178,7 +186,9 @@ static void make_rack_eq_select(xce_select_node_t* resname, static void init_ctx(xce_sched_ctx_t* ctx, xce_scheduler_t* sched, xce_sched_obj_t* obj, xce_primary_resreq_t* primary) { - int rc = sched_ctx_init(ctx, sched, obj); + /* scheduler-sim-live-allocation spec, task 2.1: ctx->sched is now + * honestly typed xce_sched_core_t* -- pass &(sched->core), not sched. */ + int rc = sched_ctx_init(ctx, &(sched->core), obj); TEST_ASSERT_EQUAL_INT(0, rc); ctx->primary = primary; } @@ -610,6 +620,112 @@ static void test_init_rejects_missing_ctx(void) TEST_ASSERT_EQUAL_INT(-EINVAL, host_iter->init(host_iter, NULL, NULL)); } +/* + * scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation + * Chain): sched_try_alloc_on_host_() (resource.c) -- the per-host + * allocation-attempt leaf that both host_iter.c's own capacity checks + * ultimately mirror and that parjob_allocator.c's/seqjob_allocator.c's + * my_alloc() call once per candidate -- is now core-typed. This drives it + * end to end (real sched_ctx_get_uncomit_host()/sched_check_host_satisfy(), + * schedobj.c+resource.c, this file's own plugin_prepare_allocation() no-op + * stub above) against BOTH a normal-mode-shaped (xce_scheduler_t) and a + * simulation-mode-shaped (xce_sched_sim_driver_t) core container, proving + * identical grant/deny results regardless of which driver's core state is + * passed in -- mirrors test.c's own + * test_core_safe_allocation_chain_driver_parity() (task 1.3) for the one + * function that test doesn't itself cover. + * + * RED (before task 2.1): fails to compile -- sched_try_alloc_on_host_() + * still takes xce_scheduler_t*, so passing xce_sched_core_t* directly is + * an incompatible-pointer-type error (this toolchain treats + * -Wincompatible-pointer-types as fatal, not a warning). + */ +static int try_alloc_once(xce_sched_core_t* core, xce_sched_worker_t* w, + float host_wanted) +{ + xce_sched_ctx_t ctx; + xce_sched_obj_t obj; + xce_primary_resreq_t primary; + int rc; + + memset(&primary, 0, sizeof(primary)); + primary.minTasks = 1; + primary.maxTasks = 1; + primary.select = NULL; + + init_primary_obj(&obj); + TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(&ctx, core, &obj)); + ctx.primary = &primary; + + /* sched_try_alloc_on_host_()'s resource-unmet branch assumes + * ctx->reason already exists (unlike its select-mismatch branch, which + * lazily creates one) -- my_alloc() always creates one before ever + * reaching this leaf in production; this test calls the leaf directly, + * bypassing that step, so it must set one up itself. Same pattern + * test_parjob_combinatorial.c's own direct parjob_try_plan_() call + * already established for the identical reason. */ + ctx.reason = make_pendreason(0); + TEST_ASSERT_NOT_NULL(ctx.reason); + + set_wanted_slots(&ctx, host_wanted); + + rc = sched_try_alloc_on_host_(w, core, &ctx); + + sched_ctx_fini(&ctx); + + return rc; +} + +static void +test_try_alloc_on_host_grants_and_denies_identically_for_both_driver_shapes( + void) +{ + xce_scheduler_t sched_normal; + xce_sched_sim_driver_t sim_shape; + xce_sched_core_t* core_normal = &(sched_normal.core); + xce_sched_core_t* core_sim = &(sim_shape.core); + xce_sched_worker_t* w_normal; + xce_sched_worker_t* w_sim; + int rc_normal, rc_sim; + + memset(&sched_normal, 0, sizeof(sched_normal)); + memset(&sim_shape, 0, sizeof(sim_shape)); + TEST_ASSERT_EQUAL_INT(0, xce_sched_core_init(core_normal)); + TEST_ASSERT_EQUAL_INT(0, xce_sched_core_init(core_sim)); + + add_worker(core_normal, "only", 4.0f, 0.0f, 0.0); + add_worker(core_sim, "only", 4.0f, 0.0f, 0.0); + + w_normal = sched_get_worker(core_normal, "only"); + w_sim = sched_get_worker(core_sim, "only"); + TEST_ASSERT_NOT_NULL(w_normal); + TEST_ASSERT_NOT_NULL(w_sim); + + /* Grant: wants 3 of 4 free slots. */ + rc_normal = try_alloc_once(core_normal, w_normal, 3.0f); + rc_sim = try_alloc_once(core_sim, w_sim, 3.0f); + + TEST_ASSERT_EQUAL_INT(1, rc_normal); + TEST_ASSERT_EQUAL_INT_MESSAGE( + rc_normal, rc_sim, + "sched_try_alloc_on_host_() must grant identically for a " + "normal-mode-shaped and a simulation-mode-shaped core"); + + /* Deny: wants 8, only 4 free -- the per-ctx uncommitted debit tracked + * by the grant above never touches the worker's own resInUse, so this + * second, fresh-ctx attempt against the SAME worker still sees all 4 + * free, exactly like test.c's own driver-parity test reuses one + * worker across a grant then a deny sub-case. */ + rc_normal = try_alloc_once(core_normal, w_normal, 8.0f); + rc_sim = try_alloc_once(core_sim, w_sim, 8.0f); + + TEST_ASSERT_EQUAL_INT(0, rc_normal); + TEST_ASSERT_EQUAL_INT_MESSAGE( + rc_normal, rc_sim, + "sched_try_alloc_on_host_() must deny identically for a " + "normal-mode-shaped and a simulation-mode-shaped core"); +} + int main(void) { UNITY_BEGIN(); @@ -620,5 +736,7 @@ int main(void) test_host_too_small_for_larger_search_remains_candidate_for_smaller_search); RUN_TEST(test_next_has_a_real_cancellation_point); RUN_TEST(test_init_rejects_missing_ctx); + RUN_TEST( + test_try_alloc_on_host_grants_and_denies_identically_for_both_driver_shapes); return UNITY_END(); } diff --git a/src/scheduler/testsuite/test_job_update.c b/src/scheduler/testsuite/test_job_update.c index 49c0a635..3c5c09be 100644 --- a/src/scheduler/testsuite/test_job_update.c +++ b/src/scheduler/testsuite/test_job_update.c @@ -394,7 +394,9 @@ test_ctx_init_defaults_alloc_mode_to_normal(void) obj.tag = XCE_RESREQ_PRIMARY; xce_sched_ctx_t ctx; - int rc = sched_ctx_init(&ctx, &sched, &obj); + /* scheduler-sim-live-allocation spec, task 2.1: ctx->sched is now + * honestly typed xce_sched_core_t* -- pass &(sched.core), not &sched. */ + int rc = sched_ctx_init(&ctx, &(sched.core), &obj); TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_EQUAL_INT(XCE_ALLOC_NORMAL, ctx.alloc_mode); diff --git a/src/scheduler/testsuite/test_parjob_combinatorial.c b/src/scheduler/testsuite/test_parjob_combinatorial.c index af7e92a1..76c655d6 100644 --- a/src/scheduler/testsuite/test_parjob_combinatorial.c +++ b/src/scheduler/testsuite/test_parjob_combinatorial.c @@ -304,7 +304,9 @@ static int drive_my_alloc(xce_scheduler_t* sched, xce_sched_obj_t* obj, { xce_sched_plugin_t plugin; - TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(ctx, sched, obj)); + /* scheduler-sim-live-allocation spec, task 2.1: ctx->sched is now + * honestly typed xce_sched_core_t* -- pass &(sched->core), not sched. */ + TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(ctx, &(sched->core), obj)); sched_ctx_set_primary(ctx, primary); memset(&plugin, 0, sizeof(plugin)); @@ -790,7 +792,9 @@ static void test_descending_bucket_order_succeeds_where_ascending_fails(void) primary.select = NULL; init_primary_obj(&obj, &primary_obj, &primary); - TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(&ctx, &sched, &obj)); + /* scheduler-sim-live-allocation spec, task 2.1: ctx->sched is now + * honestly typed xce_sched_core_t* -- pass &(sched.core), not &sched. */ + TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(&ctx, &(sched.core), &obj)); sched_ctx_set_primary(&ctx, &primary); /* my_alloc() always lazily creates ctx->reason before ever reaching diff --git a/src/scheduler/testsuite/test_seqjob_allocator.c b/src/scheduler/testsuite/test_seqjob_allocator.c index ab332aac..d3f2f1cf 100644 --- a/src/scheduler/testsuite/test_seqjob_allocator.c +++ b/src/scheduler/testsuite/test_seqjob_allocator.c @@ -270,7 +270,9 @@ test_my_alloc_allocates_single_matching_host_through_shared_iterators(void) init_primary_obj(&obj, &primary_obj, &primary); - TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(&ctx, &sched, &obj)); + /* scheduler-sim-live-allocation spec, task 2.1: ctx->sched is now + * honestly typed xce_sched_core_t* -- pass &(sched.core), not &sched. */ + TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(&ctx, &(sched.core), &obj)); /* Real call -- exercises plugin_prepare_allocation()'s ctx->worker == * NULL branch exactly as sched_make_primary_allocation() does today, * populating ctx->alloc_req.shared before my_alloc() ever runs. */ @@ -325,7 +327,9 @@ test_seqjob_prepare_allocation_guards_against_maxtasks_gt_1_fanout(void) init_primary_obj(&obj, &primary_obj, &primary); - TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(&ctx, &sched, &obj)); + /* scheduler-sim-live-allocation spec, task 2.1: ctx->sched is now + * honestly typed xce_sched_core_t* -- pass &(sched.core), not &sched. */ + TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(&ctx, &(sched.core), &obj)); ctx.primary = &primary; /* Simulate parjob_prepare_allocation() having already staged this diff --git a/src/scheduler/testsuite/test_seqjob_rewrap_equivalence.c b/src/scheduler/testsuite/test_seqjob_rewrap_equivalence.c index 93d9a4f2..3fee4ddb 100644 --- a/src/scheduler/testsuite/test_seqjob_rewrap_equivalence.c +++ b/src/scheduler/testsuite/test_seqjob_rewrap_equivalence.c @@ -307,7 +307,9 @@ static int drive_my_alloc(xce_scheduler_t* sched, xce_sched_obj_t* obj, { xce_sched_plugin_t plugin; - TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(ctx, sched, obj)); + /* scheduler-sim-live-allocation spec, task 2.1: ctx->sched is now + * honestly typed xce_sched_core_t* -- pass &(sched->core), not sched. */ + TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(ctx, &(sched->core), obj)); sched_ctx_set_primary(ctx, primary); memset(&plugin, 0, sizeof(plugin));