feat(scheduler-sim-live-allocation): make allocation dispatch and decision commit core-safe (task 2.2)
Retypes sched_make_allocation()/sched_make_primary_allocation()/ sched_make_compound_allocation()/sched_make_alter_allocation()/ sched_commit_decision(), plus 3 private helpers found necessary during migration (sched_make_primary_alloc_(), sched_make_seqjob_ allocation(), sched_make_parjob_allocation()) from xce_scheduler_t* to xce_sched_core_t* in scheduler.c. Wires the one genuine normal-mode-only touch point -- the unsatisfiable-resreq cache, inside sched_make_primary_alloc_() -- through the task-1.1 NULL-safe callback seam (core->unsatisfiable_ lookup_cb/populate_cb) instead of a direct field read. No other normal-mode-only state was found in any of the 8 migrated functions, independently confirmed by both the implementer and reviewer against xce_scheduler_t's own field list -- no escalation needed. Minimal &(sched->core) call-site wraps applied inside sched_allocator()/ commit_decision() (task 3.1's home, not yet migrated), matching the pattern established in tasks 1.2/1.3/2.1. TDD: two-part RED -- a compile-error RED from the sched_commit_decision retype, and a behavioral RED using spy callbacks proving the cache seam was genuinely unconsulted before this fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
parent
d52c78e4be
commit
ecdea8bf55
6 changed files with 213 additions and 51 deletions
|
|
@ -326,7 +326,15 @@ extern void sched_end_session(xce_scheduler_t*);
|
|||
extern int sched_update_existing_queue(xce_scheduler_t*, xce_queue_t*);
|
||||
extern int sched_update_existing_job(xce_scheduler_t*, xce_job_t*);
|
||||
extern int sched_apply_job_rusage(xce_scheduler_t*, xce_job_rusages_t*);
|
||||
extern int sched_commit_decision(xce_scheduler_t*, xce_decision_t*);
|
||||
/* scheduler-sim-live-allocation spec, task 2.2 (Core-Safe Allocation
|
||||
* Chain): sched_commit_decision() is now core-typed (xce_sched_core_t*),
|
||||
* not xce_scheduler_t* -- it, plugin_commit_decision(), sched_get_job()
|
||||
* and sched_commit_job_alloc() (the latter two already relocated to
|
||||
* core.h) are all core-safe now, so the whole DISPATCH-commit path is
|
||||
* callable from either driver. Its sole production caller,
|
||||
* commit_decision() (scheduler.c, static), wraps its own still-
|
||||
* xce_scheduler_t*-typed argument as &(sched->core) at the call site. */
|
||||
extern int sched_commit_decision(xce_sched_core_t*, xce_decision_t*);
|
||||
extern int sched_publish_pend_reason(xce_scheduler_t*, xce_preason_set_t*);
|
||||
extern int sched_publish_dispatch(xce_scheduler_t*, xce_job_dispatch_t*);
|
||||
/* bug-1760: exposed (was `static sched_publish_check_rejoin`) so
|
||||
|
|
|
|||
|
|
@ -138,12 +138,20 @@ static void* sched_job_picker(void* arg)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int sched_make_seqjob_allocation(xce_scheduler_t* sched,
|
||||
/* scheduler-sim-live-allocation spec, task 2.2 (Core-Safe Allocation
|
||||
* Chain): both retyped to xce_sched_core_t* -- neither ever touched
|
||||
* anything beyond core->seqjob_allocator/core->parjob_allocator (already
|
||||
* genuinely core-scoped fields, just previously read via sched->core.*).
|
||||
* Their only callers are sched_make_primary_alloc_()'s two branches below,
|
||||
* also migrated in this task -- mirrors task 1.3's "a private static
|
||||
* helper with no other callers besides the functions being migrated
|
||||
* should be migrated alongside them" precedent. */
|
||||
static int sched_make_seqjob_allocation(xce_sched_core_t* core,
|
||||
xce_sched_ctx_t* ctx)
|
||||
{
|
||||
xce_sched_plugin_t* plugin = NULL;
|
||||
|
||||
plugin = sched->core.seqjob_allocator;
|
||||
plugin = core->seqjob_allocator;
|
||||
if (plugin->job_alloc_fn == NULL) {
|
||||
ERROR("%s plugin %s has no allocation function", __func__,
|
||||
plugin->name);
|
||||
|
|
@ -153,12 +161,12 @@ static int sched_make_seqjob_allocation(xce_scheduler_t* sched,
|
|||
return plugin->job_alloc_fn(plugin, ctx);
|
||||
}
|
||||
|
||||
static int sched_make_parjob_allocation(xce_scheduler_t* sched,
|
||||
static int sched_make_parjob_allocation(xce_sched_core_t* core,
|
||||
xce_sched_ctx_t* ctx)
|
||||
{
|
||||
xce_sched_plugin_t* plugin = NULL;
|
||||
|
||||
plugin = sched->core.parjob_allocator;
|
||||
plugin = core->parjob_allocator;
|
||||
if (plugin->job_alloc_fn == NULL) {
|
||||
ERROR("%s plugin %s has no allocation function", __func__,
|
||||
plugin->name);
|
||||
|
|
@ -269,7 +277,7 @@ static void sched_pend_from_cached_reason_(xce_sched_ctx_t* ctx, int category)
|
|||
add_job_preason(ctx->reason->reasons, category, NULL, NULL);
|
||||
}
|
||||
|
||||
static int sched_make_primary_alloc_(xce_scheduler_t* sched,
|
||||
static int sched_make_primary_alloc_(xce_sched_core_t* core,
|
||||
xce_sched_ctx_t* ctx)
|
||||
{
|
||||
xce_primary_resreq_t* primary = ctx->primary;
|
||||
|
|
@ -281,17 +289,24 @@ static int sched_make_primary_alloc_(xce_scheduler_t* sched,
|
|||
|
||||
cache_obj = sched_primary_cache_obj_(ctx, &scratch_primary, &scratch_obj);
|
||||
|
||||
/* Req 7.4: a cache hit pends the job immediately, without invoking
|
||||
* either allocator for this attempt. */
|
||||
if (cache_lookup(sched, cache_obj, &reason_category) == 1) {
|
||||
/* Req 7.4 / scheduler-sim-live-allocation spec, task 2.2
|
||||
* (Unsatisfiable-Cache Callback Seam): consult the driver-registered
|
||||
* lookup hook instead of calling cache_lookup() directly -- a NULL
|
||||
* hook (simulation mode, which never registers one) means "always
|
||||
* compute fresh", never an error, so this is the one primary-resreq
|
||||
* dispatch chokepoint the seam exists for. A cache hit pends the job
|
||||
* immediately, without invoking either allocator for this attempt. */
|
||||
if ((core->unsatisfiable_lookup_cb != NULL)
|
||||
&& (core->unsatisfiable_lookup_cb(core, cache_obj, &reason_category)
|
||||
== 1)) {
|
||||
sched_pend_from_cached_reason_(ctx, reason_category);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (primary->maxTasks == 1) {
|
||||
rc = sched_make_seqjob_allocation(sched, ctx);
|
||||
rc = sched_make_seqjob_allocation(core, ctx);
|
||||
} else {
|
||||
rc = sched_make_parjob_allocation(sched, ctx);
|
||||
rc = sched_make_parjob_allocation(core, ctx);
|
||||
}
|
||||
|
||||
/* Req 7.3: once the chosen allocator has exhausted every candidate
|
||||
|
|
@ -299,18 +314,34 @@ static int sched_make_primary_alloc_(xce_scheduler_t* sched,
|
|||
* this resreq's fingerprint + identity and the exhausted attempt's own
|
||||
* final pend-reason category, before the caller pends the job. A
|
||||
* negative rc is an infrastructure error, not an unsatisfiable
|
||||
* determination, so it is never cached; a positive rc is success. */
|
||||
* determination, so it is never cached; a positive rc is success.
|
||||
* Routed through the same NULL-safe seam as the lookup above -- a NULL
|
||||
* populate hook is simply a no-op, not an error. */
|
||||
if (rc == 0) {
|
||||
int category
|
||||
= (ctx->reason != NULL) ? ctx->reason->reasons->main_reason : 0;
|
||||
|
||||
cache_insert(sched, cache_obj, category);
|
||||
if (core->unsatisfiable_populate_cb != NULL) {
|
||||
core->unsatisfiable_populate_cb(core, cache_obj, category);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sched_make_primary_allocation(xce_scheduler_t* sched,
|
||||
/* scheduler-sim-live-allocation spec, task 2.2 (Core-Safe Allocation
|
||||
* Chain): sched_make_primary_allocation()/sched_make_compound_allocation()/
|
||||
* sched_make_alter_allocation()/sched_make_allocation() below are now all
|
||||
* retyped to xce_sched_core_t* core -- read in full before migrating (per
|
||||
* this task's own escalation clause): each touches nothing beyond
|
||||
* core->*, ctx/obj fields, and (via sched_make_primary_alloc_() above) the
|
||||
* one already-seamed cache touch point. No additional normal-mode-only
|
||||
* state was found, so no escalation is needed. sched_allocator() (this
|
||||
* file, task 3.1's future home) is the sole caller of sched_make_
|
||||
* allocation() and stays xce_scheduler_t*-typed for now -- its call site
|
||||
* below wraps &(sched->core), the same minimal-argument-wrap pattern
|
||||
* task 1.2/2.1 already established. */
|
||||
static int sched_make_primary_allocation(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj)
|
||||
{
|
||||
xce_primary_resreq_t* primary = obj->u.primary->resreq;
|
||||
|
|
@ -321,16 +352,9 @@ static int sched_make_primary_allocation(xce_scheduler_t* sched,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 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_init(&ctx, core, obj);
|
||||
sched_ctx_set_primary(&ctx, primary);
|
||||
rc = sched_make_primary_alloc_(sched, &ctx);
|
||||
rc = sched_make_primary_alloc_(core, &ctx);
|
||||
if (ctx.alloc != NULL) {
|
||||
MOVE_PTR(obj->alloc, ctx.alloc);
|
||||
}
|
||||
|
|
@ -344,7 +368,7 @@ static int sched_make_primary_allocation(xce_scheduler_t* sched,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int sched_make_compound_allocation(xce_scheduler_t* sched,
|
||||
static int sched_make_compound_allocation(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -352,17 +376,14 @@ static int sched_make_compound_allocation(xce_scheduler_t* sched,
|
|||
xce_sched_ctx_t ctx;
|
||||
int rc = 0;
|
||||
|
||||
/* 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);
|
||||
sched_ctx_init(&ctx, core, obj);
|
||||
|
||||
for (i = 0; i < resreq->primaries.count; i++) {
|
||||
xce_primary_resreq_t* primary
|
||||
= (xce_primary_resreq_t*)xlist_get(&(resreq->primaries), i);
|
||||
|
||||
sched_ctx_set_primary(&ctx, primary);
|
||||
rc = sched_make_primary_alloc_(sched, &ctx);
|
||||
rc = sched_make_primary_alloc_(core, &ctx);
|
||||
if (rc <= 0) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -381,7 +402,7 @@ static int sched_make_compound_allocation(xce_scheduler_t* sched,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int sched_make_alter_allocation(xce_scheduler_t* sched,
|
||||
static int sched_make_alter_allocation(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -395,7 +416,7 @@ static int sched_make_alter_allocation(xce_scheduler_t* sched,
|
|||
continue;
|
||||
}
|
||||
|
||||
rc = sched_make_compound_allocation(sched, obj1);
|
||||
rc = sched_make_compound_allocation(core, obj1);
|
||||
if (rc > 0) {
|
||||
if (obj1->alloc != NULL) {
|
||||
MOVE_PTR(obj->alloc, obj1->alloc);
|
||||
|
|
@ -410,19 +431,19 @@ static int sched_make_alter_allocation(xce_scheduler_t* sched,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sched_make_allocation(xce_scheduler_t* sched, xce_sched_obj_t* obj)
|
||||
static int sched_make_allocation(xce_sched_core_t* core, xce_sched_obj_t* obj)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (obj->tag) {
|
||||
case XCE_RESREQ_PRIMARY:
|
||||
rc = sched_make_primary_allocation(sched, obj);
|
||||
rc = sched_make_primary_allocation(core, obj);
|
||||
break;
|
||||
case XCE_RESREQ_COMPOUND:
|
||||
rc = sched_make_compound_allocation(sched, obj);
|
||||
rc = sched_make_compound_allocation(core, obj);
|
||||
break;
|
||||
case XCE_RESREQ_ALTER:
|
||||
rc = sched_make_alter_allocation(sched, obj);
|
||||
rc = sched_make_alter_allocation(core, obj);
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
|
|
@ -431,9 +452,13 @@ static int sched_make_allocation(xce_scheduler_t* sched, xce_sched_obj_t* obj)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* commit_decision() stays xce_scheduler_t*-typed: its sole caller,
|
||||
* sched_allocator() below, is task 3.1's job, not this one. Minimal
|
||||
* call-site wrap into the now-core-typed sched_commit_decision() --
|
||||
* same pattern as every other call site this task touches. */
|
||||
static int commit_decision(xce_scheduler_t* sched, xce_decision_t* decision)
|
||||
{
|
||||
return sched_commit_decision(sched, decision);
|
||||
return sched_commit_decision(&(sched->core), decision);
|
||||
}
|
||||
|
||||
static void* sched_allocator(void* arg)
|
||||
|
|
@ -497,7 +522,12 @@ static void* sched_allocator(void* arg)
|
|||
plugin_job_election(&(sched->core), job);
|
||||
|
||||
/* calculate decision for jobid */
|
||||
alloc_rc = sched_make_allocation(sched, job->schedobj);
|
||||
/* scheduler-sim-live-allocation spec, task 2.2: sched_make_
|
||||
* allocation() is now core-typed -- pass &(sched->core), the same
|
||||
* minimal call-site wrap this file's other driver-agnostic call
|
||||
* sites already use. sched_allocator() itself stays
|
||||
* xce_scheduler_t*-typed (task 3.1's job). */
|
||||
alloc_rc = sched_make_allocation(&(sched->core), job->schedobj);
|
||||
if (alloc_rc < 0) {
|
||||
/* something wrong */
|
||||
continue;
|
||||
|
|
@ -1620,27 +1650,31 @@ void sched_end_session(xce_scheduler_t* sched)
|
|||
memory_order_release);
|
||||
}
|
||||
|
||||
int sched_commit_decision(xce_scheduler_t* sched, xce_decision_t* decision)
|
||||
/* scheduler-sim-live-allocation spec, task 2.2 (Core-Safe Allocation
|
||||
* Chain): retyped to xce_sched_core_t* core -- its body already only
|
||||
* touched plugin_commit_decision()/sched_get_job()/sched_commit_job_alloc()
|
||||
* (all three already core-typed, tasks 1.2/2.1's "original 6-function
|
||||
* migration") via &(sched->core), so this is a pure signature migration at
|
||||
* the top of the function, no body-logic change. Its sole production
|
||||
* caller, commit_decision() (this file, static), now wraps its own
|
||||
* still-xce_scheduler_t*-typed argument at the call site instead. */
|
||||
int sched_commit_decision(xce_sched_core_t* core, xce_decision_t* decision)
|
||||
{
|
||||
int rc = 0;
|
||||
xce_sched_job_t* job = NULL;
|
||||
|
||||
/* scheduler-sim-live-allocation spec, task 1.2: plugin_commit_decision()
|
||||
* is now core-typed -- pass &(sched->core), same minimal call-site
|
||||
* update as plugin_job_election()'s own call site (sched_allocator(),
|
||||
* this file). */
|
||||
rc = plugin_commit_decision(&(sched->core), decision);
|
||||
rc = plugin_commit_decision(core, decision);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (decision->op == XCE_DECISION_DISPATCH) {
|
||||
job = sched_get_job(&(sched->core), decision->u.alloc->jobid);
|
||||
job = sched_get_job(core, decision->u.alloc->jobid);
|
||||
job->allocs = decision->u.alloc;
|
||||
job->accounts = make_job_account(job->allocs);
|
||||
job->state = JOB_STAT_RUN;
|
||||
|
||||
return sched_commit_job_alloc(&(sched->core), job);
|
||||
return sched_commit_job_alloc(core, job);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@
|
|||
#include "preason.h"
|
||||
#include "resobj.h"
|
||||
#include "resreq.h"
|
||||
#include "sched/alloc_cache.h"
|
||||
#include "scheduler.h"
|
||||
#include "xce/cluster.h"
|
||||
#include "xstring.h"
|
||||
|
|
@ -154,6 +155,42 @@ int __wrap_sched_meet_host_selection(xce_sched_core_t* core,
|
|||
return __real_sched_meet_host_selection(core, w, select);
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════════════
|
||||
* spy_unsatisfiable_lookup_cb / spy_unsatisfiable_populate_cb -
|
||||
* scheduler-sim-live-allocation spec, task 2.2 (Unsatisfiable-Cache
|
||||
* Callback Seam consumer wiring): registered on sched->core in place of
|
||||
* the real xce_alloc_cache_lookup_cb()/xce_alloc_cache_populate_cb()
|
||||
* adapters (main.c's own normal-mode registration, sched_init() itself
|
||||
* never registers either), so this fixture can count real invocations
|
||||
* while still delegating to the real cache implementation for correct
|
||||
* hit/miss behavior. Before this task, sched_make_primary_alloc_()
|
||||
* (scheduler.c) read sched->unsatisfiable_cache directly and never
|
||||
* touched core->unsatisfiable_lookup_cb/unsatisfiable_populate_cb at all
|
||||
* -- these counters staying at 0 through both dispatches is the genuine
|
||||
* RED signal proving the seam is not yet wired.
|
||||
* ════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
static int g_lookup_cb_calls = 0;
|
||||
static int g_populate_cb_calls = 0;
|
||||
|
||||
static int spy_unsatisfiable_lookup_cb(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj,
|
||||
int* out_reason_category)
|
||||
{
|
||||
g_lookup_cb_calls++;
|
||||
|
||||
return xce_alloc_cache_lookup_cb(core, obj, out_reason_category);
|
||||
}
|
||||
|
||||
static void spy_unsatisfiable_populate_cb(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj,
|
||||
int reason_category)
|
||||
{
|
||||
g_populate_cb_calls++;
|
||||
|
||||
xce_alloc_cache_populate_cb(core, obj, reason_category);
|
||||
}
|
||||
|
||||
void setUp(void) { return; }
|
||||
void tearDown(void) { return; }
|
||||
|
||||
|
|
@ -248,6 +285,16 @@ static xce_scheduler_t* start_sched(void)
|
|||
* unsatisfiable for any job (see file header). */
|
||||
add_worker_with_slots(&(sched->core), WORKER_NAME, slots_res->idx, 0.0f);
|
||||
|
||||
/* scheduler-sim-live-allocation spec, task 2.2: sched_init() itself
|
||||
* never registers the unsatisfiable-cache seam (main.c's normal-mode
|
||||
* branch is the only production registrant) -- register the spy
|
||||
* adapters here so this fixture's cache-hit proof genuinely exercises
|
||||
* the new seam, not just the underlying cache_lookup()/cache_insert()
|
||||
* calls a pre-migration sched_make_primary_alloc_() would reach some
|
||||
* other way. */
|
||||
sched->core.unsatisfiable_lookup_cb = spy_unsatisfiable_lookup_cb;
|
||||
sched->core.unsatisfiable_populate_cb = spy_unsatisfiable_populate_cb;
|
||||
|
||||
return sched;
|
||||
}
|
||||
|
||||
|
|
@ -372,6 +419,16 @@ static void test_standalone_primary_cache_hit_skips_allocator(void)
|
|||
calls_job1 > 0,
|
||||
"job 1 (cache miss) must genuinely run the allocator's host "
|
||||
"selection check at least once");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
1, g_lookup_cb_calls,
|
||||
"job 1's cache miss must be consulted through core->"
|
||||
"unsatisfiable_lookup_cb (Unsatisfiable-Cache Callback Seam), not "
|
||||
"a direct cache_lookup() call");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
1, g_populate_cb_calls,
|
||||
"job 1's exhausted attempt must populate the cache through "
|
||||
"core->unsatisfiable_populate_cb, not a direct cache_insert() "
|
||||
"call");
|
||||
|
||||
/* Job 2: identical fingerprint + identity -- cache HIT. Must pend
|
||||
* immediately, with zero additional sched_meet_host_selection()
|
||||
|
|
@ -394,6 +451,14 @@ static void test_standalone_primary_cache_hit_skips_allocator(void)
|
|||
decision2->u.reason->reasons->main_reason,
|
||||
"a cache hit must attribute the SAME pend reason category as the "
|
||||
"original unsatisfiable determination (Req 7.5)");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
2, g_lookup_cb_calls,
|
||||
"job 2's cache hit must also be consulted through core->"
|
||||
"unsatisfiable_lookup_cb (one more call than after job 1)");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
1, g_populate_cb_calls,
|
||||
"a cache HIT must never re-populate the cache -- "
|
||||
"unsatisfiable_populate_cb call count stays at job 1's count");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
#include "preason.h"
|
||||
#include "resobj.h"
|
||||
#include "resreq.h"
|
||||
#include "sched/alloc_cache.h"
|
||||
#include "scheduler.h"
|
||||
#include "xce/cluster.h"
|
||||
#include "xstring.h"
|
||||
|
|
@ -119,6 +120,36 @@ int __wrap_sched_meet_host_selection(xce_sched_core_t* core,
|
|||
return __real_sched_meet_host_selection(core, w, select);
|
||||
}
|
||||
|
||||
/* ════════════════════════════════════════════════════════════════════════
|
||||
* spy_unsatisfiable_lookup_cb / spy_unsatisfiable_populate_cb -- see
|
||||
* test_scheduler_cache_dispatch.c's identical spies for the full
|
||||
* rationale (scheduler-sim-live-allocation spec, task 2.2). Delegates to
|
||||
* the real xce_alloc_cache_lookup_cb()/xce_alloc_cache_populate_cb()
|
||||
* adapters so this file's compound-nested-primary cache-hit proof stays
|
||||
* functionally correct while also counting real seam invocations.
|
||||
* ════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
static int g_lookup_cb_calls = 0;
|
||||
static int g_populate_cb_calls = 0;
|
||||
|
||||
static int spy_unsatisfiable_lookup_cb(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj,
|
||||
int* out_reason_category)
|
||||
{
|
||||
g_lookup_cb_calls++;
|
||||
|
||||
return xce_alloc_cache_lookup_cb(core, obj, out_reason_category);
|
||||
}
|
||||
|
||||
static void spy_unsatisfiable_populate_cb(xce_sched_core_t* core,
|
||||
xce_sched_obj_t* obj,
|
||||
int reason_category)
|
||||
{
|
||||
g_populate_cb_calls++;
|
||||
|
||||
xce_alloc_cache_populate_cb(core, obj, reason_category);
|
||||
}
|
||||
|
||||
void setUp(void) { return; }
|
||||
void tearDown(void) { return; }
|
||||
|
||||
|
|
@ -209,6 +240,12 @@ static xce_scheduler_t* start_sched(void)
|
|||
* unsatisfiable for any job. */
|
||||
add_worker_with_slots(&(sched->core), WORKER_NAME, slots_res->idx, 0.0f);
|
||||
|
||||
/* scheduler-sim-live-allocation spec, task 2.2: see
|
||||
* test_scheduler_cache_dispatch.c's identical registration for why
|
||||
* sched_init() alone never wires this seam. */
|
||||
sched->core.unsatisfiable_lookup_cb = spy_unsatisfiable_lookup_cb;
|
||||
sched->core.unsatisfiable_populate_cb = spy_unsatisfiable_populate_cb;
|
||||
|
||||
return sched;
|
||||
}
|
||||
|
||||
|
|
@ -343,6 +380,16 @@ static void test_compound_nested_primary_cache_hit_skips_allocator(void)
|
|||
calls_jobA > 0,
|
||||
"job A (cache miss) must genuinely run the allocator's host "
|
||||
"selection check at least once for its nested primary");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
1, g_lookup_cb_calls,
|
||||
"job A's cache miss must be consulted through core->"
|
||||
"unsatisfiable_lookup_cb (Unsatisfiable-Cache Callback Seam), not "
|
||||
"a direct cache_lookup() call, even for a compound-nested primary");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
1, g_populate_cb_calls,
|
||||
"job A's exhausted attempt must populate the cache through "
|
||||
"core->unsatisfiable_populate_cb, not a direct cache_insert() "
|
||||
"call");
|
||||
|
||||
/* Job B: a different job, but its one nested primary shares an
|
||||
* identical fingerprint (same resreq string) and identity (same
|
||||
|
|
@ -365,6 +412,14 @@ static void test_compound_nested_primary_cache_hit_skips_allocator(void)
|
|||
"a cache hit must attribute the SAME pend reason category as the "
|
||||
"original unsatisfiable determination (Req 7.5), even across a "
|
||||
"compound-nested primary");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
2, g_lookup_cb_calls,
|
||||
"job B's cache hit must also be consulted through core->"
|
||||
"unsatisfiable_lookup_cb (one more call than after job A)");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
1, g_populate_cb_calls,
|
||||
"a cache HIT must never re-populate the cache -- "
|
||||
"unsatisfiable_populate_cb call count stays at job A's count");
|
||||
|
||||
FREEUP(errmsg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ static void test_dispatch_commit_transitions_job_to_run(void)
|
|||
decision = make_dispatch_decision(alloc);
|
||||
TEST_ASSERT_NOT_NULL(decision);
|
||||
|
||||
rc = sched_commit_decision(&sched, decision);
|
||||
rc = sched_commit_decision(&(sched.core), decision);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
|
||||
/* the new behavior this task adds */
|
||||
|
|
@ -218,7 +218,7 @@ static void test_dispatch_commit_survives_round_boundary_before_confirmation(voi
|
|||
decision = make_dispatch_decision(alloc);
|
||||
TEST_ASSERT_NOT_NULL(decision);
|
||||
|
||||
rc = sched_commit_decision(&sched, decision);
|
||||
rc = sched_commit_decision(&(sched.core), decision);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
TEST_ASSERT_EQUAL_UINT(JOB_STAT_RUN, job->state);
|
||||
TEST_ASSERT_NOT_NULL(job->accounts);
|
||||
|
|
@ -264,7 +264,7 @@ static void test_pend_decision_does_not_touch_job_state(void)
|
|||
decision = make_pend_decision(reason);
|
||||
TEST_ASSERT_NOT_NULL(decision);
|
||||
|
||||
rc = sched_commit_decision(&sched, decision);
|
||||
rc = sched_commit_decision(&(sched.core), decision);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT(JOB_STAT_PEND, job->state);
|
||||
|
|
@ -316,7 +316,7 @@ static void test_dispatch_commit_survives_several_rounds_before_confirmation(voi
|
|||
decision = make_dispatch_decision(alloc);
|
||||
TEST_ASSERT_NOT_NULL(decision);
|
||||
|
||||
rc = sched_commit_decision(&sched, decision);
|
||||
rc = sched_commit_decision(&(sched.core), decision);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
TEST_ASSERT_EQUAL_UINT(JOB_STAT_RUN, job->state);
|
||||
|
||||
|
|
@ -396,7 +396,7 @@ static float run_dispatch_then_n_rounds(int nrounds)
|
|||
decision = make_dispatch_decision(alloc);
|
||||
TEST_ASSERT_NOT_NULL(decision);
|
||||
|
||||
rc = sched_commit_decision(&sched, decision);
|
||||
rc = sched_commit_decision(&(sched.core), decision);
|
||||
TEST_ASSERT_TRUE(rc >= 0);
|
||||
TEST_ASSERT_EQUAL_UINT(JOB_STAT_RUN, job->state);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue