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

@ -0,0 +1,117 @@
#if !defined(_CORE_H_)
#define _CORE_H_ 1
/*
* core.h - xce_sched_core_t: the scheduler's pure scheduling-state
* structure (scheduler-core spec, task 4.1, Req 5.1/5.2).
*
* Boundary (design.md "SchedulerCore" / "Existing Architecture Analysis"):
* `xce_scheduler_t` (scheduler.h) splits along the controller-I/O seam into
* this structure (no `apictx`/`cluster_cfg` -- zero controller I/O, network,
* or wall-clock dependency) and the driver-level fields that remain on
* `xce_scheduler_t` itself. Every field below is the SAME field, same name,
* same type, that lived directly on `xce_scheduler_t` before this task --
* this is a relocation, not a redesign; `xce_scheduler_t` now embeds this
* struct as its `core` member instead of declaring these fields itself.
*
* Allowed dependencies only (design.md "Allowed Dependencies", Req 5 AC 3):
* ordq, dataq, cbuf, xlist, zarray, kvmgr, rtti. This header and core.c
* must NEVER include client.h/netio.h/xceapi.h/delay.h/idgen.h/
* xce/cluster.h, and must never call a wall-clock function (time(),
* clock_gettime(), gettimeofday()) -- enforced by
* testsuite/test_core_isolation.c (source-text scan + an nm(1) undefined-
* symbol audit of core.c's compiled object, the build-time proof this
* task's Observable requires).
*/
#include <pthread.h>
#include <rtti.h>
#include <stdatomic.h>
#include <stdint.h>
#include "dataq.h"
#include "mylock.h"
#include "sched/types.h"
#include "xce/types.h"
#include "zarray.h"
/* xce_sched_core_t is also forward-declared in plugin.h (picker lifecycle
* hook parameter type); this typedef and the full struct definition below
* are the canonical declaration -- same idiom as cluster_t across
* myconf.h/persist.h/ha.h/cluster_def.h (an identical repeated typedef is
* valid C11/gnu17, so include order between core.h and plugin.h doesn't
* matter). */
typedef struct xce_sched_core_s xce_sched_core_t;
struct xce_sched_core_s {
mylock_t lock;
int stop;
uint64_t nextid;
xce_conf4sched_t* config;
uint64_t configver;
/* Req 2 AC 1 (scheduler-core spec, task 1.3): global picker plugin
* name. NULL until sched_set_default_job_pick_plugin() (schedobj.c)
* applies the "builtin_fcfs" default, or a future config-driven
* assignment sets it explicitly. */
char* job_pick_plugin;
var plugins;
void* seqjob_allocator;
void* parjob_allocator;
void* affinity_allocator;
void* gpu_allocator;
dataq_t* pend_updates[2];
var resmgr;
var ugmgr;
var qgmgr;
var pgmgr;
var hgmgr;
zarray_t sharedInst;
zarray_t sharedInUse;
var jobs;
var queues;
var workers;
var users;
var projects;
pthread_t jobpicker;
pthread_t allocator;
cbuf_t wbuf; /* action to trigger scheduling */
cbuf_t jbuf; /* job picked for allocation */
cbuf_t decisions; /* scheduler decisions */
_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 */
select_eval_ctx_t* sel_eval_ctx; /* session-level memoisation context; NULL
between sessions */
};
/*
* xce_sched_core_init/xce_sched_core_fini - allocate/initialize, and tear
* down, exactly the state above. Deliberately narrower than the design's
* eventual Service Interface (`xce_sched_core_init(core, decision_cb,
* driver_ctx, prng_seed)`, design.md "SchedulerCore" component) -- the
* decision callback, driver context, and PRNG seed belong to the event/
* decision protocol tasks 4.3-4.5 build; this task only extracts state
* init/fini, mirroring the non-I/O portion of today's sched_init()/
* sched_fini() (scheduler.c).
*/
extern int xce_sched_core_init(xce_sched_core_t*);
extern void xce_sched_core_fini(xce_sched_core_t*);
/* Relocated state-only accessors/mutators (task 4.1): moved out of
* scheduler.c/schedobj.c, now operating on xce_sched_core_t* -- zero
* controller I/O, exactly like the rest of this header. */
extern xce_sched_job_t* sched_get_job(xce_sched_core_t*, uint64_t);
extern int sched_add_job(xce_sched_core_t*, xce_sched_job_t*);
extern xce_sched_queue_t* sched_get_queue(xce_sched_core_t*, char*);
extern int sched_add_queue(xce_sched_core_t*, xce_sched_queue_t*);
extern xce_sched_worker_t* sched_get_worker(xce_sched_core_t*, char*);
extern int sched_add_worker(xce_sched_core_t*, xce_sched_worker_t*);
extern xce_sched_user_t* sched_get_user(xce_sched_core_t*, char*);
extern int sched_add_user(xce_sched_core_t*, xce_sched_user_t*);
extern xce_sched_project_t* sched_get_project(xce_sched_core_t*, char*);
extern int sched_add_project(xce_sched_core_t*, xce_sched_project_t*);
extern int sched_job_is_ready(xce_sched_job_t*);
extern int sched_queue_is_ready(xce_sched_queue_t*);
#endif

View file

@ -9,6 +9,7 @@
#include <time.h>
#include "client.h"
#include "core.h"
#include "dataq.h"
#include "decision.h"
#include "group.h"
@ -84,49 +85,23 @@ typedef struct {
- offsetof(xce_sched_update_request_t, \
link)))
/*
* xce_scheduler_t - scheduler-core spec, task 4.1 (Req 5.1/5.2): splits
* along the controller-I/O seam into `core` (xce_sched_core_t, core.h --
* all scheduling state, zero controller I/O) and the two fields below
* (controller client context + HA candidate list). Every field that used
* to live directly on this struct is unchanged in name/type/semantics --
* it now lives on `core` (see core.h's field-by-field comment). Existing
* callers holding an `xce_scheduler_t*` reach relocated state through
* `&sched->core` at the specific call sites that need the narrower
* `xce_sched_core_t*` type (the accessors declared in core.h); anything
* still operating on the full driver-level struct is unaffected.
*/
typedef struct {
mylock_t lock;
int stop;
xcecli_ctx_t apictx;
uint64_t nextid;
xce_conf4sched_t* config;
uint64_t configver;
/* Req 2 AC 1 (scheduler-core spec, task 1.3): global picker plugin
* name. NULL until sched_set_default_job_pick_plugin() (schedobj.c)
* applies the "builtin_fcfs" default, or a future config-driven
* assignment sets it explicitly. This field only carries the *name* --
* resolving it against the plugin registry (plugin_register/key_find)
* and failing startup loudly on an unregistered name (Req 2 AC 5) is
* task 2.4's job. */
char* job_pick_plugin;
var plugins;
void* seqjob_allocator;
void* parjob_allocator;
void* affinity_allocator;
void* gpu_allocator;
dataq_t* pend_updates[2];
var resmgr;
var ugmgr;
var qgmgr;
var pgmgr;
var hgmgr;
zarray_t sharedInst;
zarray_t sharedInUse;
var jobs;
var queues;
var workers;
var users;
var projects;
pthread_t jobpicker;
pthread_t allocator;
cbuf_t wbuf; /* action to trigger scheduling */
cbuf_t jbuf; /* job picked for allocation */
cbuf_t decisions; /* scheduler decisions */
_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 */
select_eval_ctx_t* sel_eval_ctx; /* session-level memoisation context; NULL between sessions */
xce_cluster_config_t cluster_cfg; /* HA candidate list — empty means single-host mode */
xce_sched_core_t core;
xcecli_ctx_t apictx;
xce_cluster_config_t
cluster_cfg; /* HA candidate list — empty means single-host mode */
} xce_scheduler_t;
extern int sched_init(xce_scheduler_t*, xce_client_config_t*,
@ -156,21 +131,14 @@ extern int sched_update(xce_scheduler_t*);
extern void sched_go(xce_scheduler_t*);
extern void sched_begin_session(xce_scheduler_t*);
extern void sched_end_session(xce_scheduler_t*);
extern xce_sched_job_t* sched_get_job(xce_scheduler_t*, uint64_t);
extern xce_sched_queue_t* sched_get_queue(xce_scheduler_t*, char*);
extern xce_sched_worker_t* sched_get_worker(xce_scheduler_t*, char*);
extern xce_sched_user_t* sched_get_user(xce_scheduler_t*, char*);
extern xce_sched_project_t* sched_get_project(xce_scheduler_t*, char*);
extern int sched_add_job(xce_scheduler_t*, xce_sched_job_t*);
extern int sched_add_queue(xce_scheduler_t*, xce_sched_queue_t*);
/* sched_get_job/sched_add_job/sched_get_queue/sched_add_queue/
* sched_get_worker/sched_add_worker/sched_get_user/sched_add_user/
* sched_get_project/sched_add_project/sched_job_is_ready/
* sched_queue_is_ready relocated to core.h (scheduler-core spec, task 4.1)
* -- they now operate on xce_sched_core_t*, not 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_add_user(xce_scheduler_t*, xce_sched_user_t*);
extern int sched_add_worker(xce_scheduler_t*, xce_sched_worker_t*);
extern int sched_add_project(xce_scheduler_t*, xce_sched_project_t*);
extern int sched_job_is_ready(xce_sched_job_t*);
extern int sched_queue_is_ready(xce_sched_queue_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*);
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*);