feat(scheduler-core): extract xce_sched_core_t from monolithic scheduler struct (task 4.1)

Splits xce_scheduler_t along the controller-I/O seam: xce_sched_core_t
(new core.h/core.c) now holds all scheduling state (jobs/queues/workers/
users/projects/resource managers/plugins/three-thread-cbuf pipeline),
while xce_scheduler_t shrinks to {core; apictx; cluster_cfg}. Proven via
an nm-based link-time isolation test (zero netio/xcecli/idgen/delaymgr
symbols reachable from core.c) plus a reproducible wall-clock-call guard.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019dw5WnN3DXStaEaPYfACU1
This commit is contained in:
Rongsong Shen 2026-08-03 10:55:58 +00:00
parent 604a564904
commit 7c229284e2
Signed by: rshen
SSH key fingerprint: SHA256:8Vf2pZ3h6rJa7EhGK+WaAQ9Mta/apZvteW6g9U/WKKk
26 changed files with 1233 additions and 661 deletions

View file

@ -12,12 +12,25 @@
* This also brings in scheduler.h (with the real xce_scheduler_t) and all deps. */
#include "../src/resource.c"
/* Stubs for symbols used by resource.c functions not under test */
xce_sched_worker_t* sched_get_worker(xce_scheduler_t* s, char* n) { (void)s; (void)n; return NULL; }
/* Stubs for symbols used by resource.c functions not under test.
* sched_get_worker/sched_get_queue relocated to core.h (scheduler-core
* spec, task 4.1) -- now operate on xce_sched_core_t*, not
* xce_scheduler_t*. */
xce_sched_worker_t* sched_get_worker(xce_sched_core_t* s, char* n)
{
(void)s;
(void)n;
return NULL;
}
int plugin_prepare_allocation(void* s, xce_sched_ctx_t* c) { (void)s; (void)c; return 0; }
int sched_ctx_get_uncomit_host(xvec_t** v, xce_sched_ctx_t* c, int i) { (void)v; (void)c; (void)i; return 0; }
int plugin_queue_update(void* s, xce_sched_queue_t* q) { (void)s; (void)q; return 0; }
xce_sched_queue_t* sched_get_queue(xce_scheduler_t* s, char* n) { (void)s; (void)n; return NULL; }
xce_sched_queue_t* sched_get_queue(xce_sched_core_t* s, char* n)
{
(void)s;
(void)n;
return NULL;
}
/* Stub: sched_update_existing_queue returns -EINVAL when updated is NULL,
* -ENOENT when queue not found (sched_get_queue stub returns NULL).
@ -27,7 +40,7 @@ int sched_update_existing_queue(xce_scheduler_t* sched, xce_queue_t* updated)
if (updated == NULL) {
return -EINVAL;
}
if (sched_get_queue(sched, updated->queue) == NULL) {
if (sched_get_queue(&(sched->core), updated->queue) == NULL) {
return -ENOENT;
}
return 0;
@ -127,9 +140,9 @@ test_scheduler_t_has_atomic_gc_fields(void)
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.session_active));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.eval_in_progress));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.staging_merge_complete));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.core.session_active));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.core.eval_in_progress));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.core.staging_merge_complete));
}
static void
@ -222,15 +235,15 @@ test_gc_predicates_idle_invariant(void)
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
/* staging_merge_complete=1 means GC is safe to run */
atomic_store(&sched.staging_merge_complete, 1);
atomic_store(&sched.core.staging_merge_complete, 1);
sel_optimize_set(1);
sel_node_table_init();
/* Verify the atomic fields are readable — testing the predicate contract */
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.session_active));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.eval_in_progress));
TEST_ASSERT_EQUAL_INT(1, atomic_load(&sched.staging_merge_complete));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.core.session_active));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.core.eval_in_progress));
TEST_ASSERT_EQUAL_INT(1, atomic_load(&sched.core.staging_merge_complete));
/* GC operations on an empty node table must not crash */
gc_clear_marks();
@ -256,19 +269,20 @@ test_staging_merge_complete_gc_guard(void)
memset(&sched, 0, sizeof(sched));
/* Simulate the post-begin_session state the fix produces */
atomic_store(&sched.session_active, 0); /* session ended */
atomic_store(&sched.eval_in_progress, 0);
atomic_store(&sched.staging_merge_complete, 1); /* fix: begin_session sets this */
atomic_store(&sched.core.session_active, 0); /* session ended */
atomic_store(&sched.core.eval_in_progress, 0);
atomic_store(&sched.core.staging_merge_complete,
1); /* fix: begin_session sets this */
/* The GC guard in sched_end_session: must NOT fire */
int guard_fires = atomic_load(&sched.eval_in_progress)
|| !atomic_load(&sched.staging_merge_complete);
int guard_fires = atomic_load(&sched.core.eval_in_progress)
|| !atomic_load(&sched.core.staging_merge_complete);
TEST_ASSERT_EQUAL_INT(0, guard_fires);
/* Confirm the unfixed state (staging=0) would block GC */
atomic_store(&sched.staging_merge_complete, 0);
int guard_fires_unfixed = atomic_load(&sched.eval_in_progress)
|| !atomic_load(&sched.staging_merge_complete);
atomic_store(&sched.core.staging_merge_complete, 0);
int guard_fires_unfixed = atomic_load(&sched.core.eval_in_progress)
|| !atomic_load(&sched.core.staging_merge_complete);
TEST_ASSERT_EQUAL_INT(1, guard_fires_unfixed);
}