feat(scheduler-core): implement core event batch push interface (task 4.3)

Defines xce_core_event_type_t/xce_core_event_t (9 event kinds: config,
job, worker, resource, resval full/delta, job state change, job update,
queue update, job rusage) in core.h. Implements xce_core_push_events()
in a new core_events.c (split out of core.c to stay under the 800-line
cap): rejects -EBUSY mid-session, otherwise applies each event's core-
state mutation and fires the corresponding picker hook synchronously,
exactly once per event, before returning. CONFIG_PUSH decomposes the
pushed config's embedded queues/resources/resvals via shared helpers
(also reused by QUEUE_UPDATE's upsert path), mirroring the existing
sched_init_config()/sched_init_resources()/sched_init_sharedInst()/
sched_init_queues(). Narrows sched_resolve_job_picker() to take
xce_sched_core_t* (pure type change, zero behavior change). Purely
additive — not wired into the live scheduling path yet (task 5.x).

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 16:05:33 +00:00
parent e9b5c3f589
commit e6c0a2448a
Signed by: rshen
SSH key fingerprint: SHA256:8Vf2pZ3h6rJa7EhGK+WaAQ9Mta/apZvteW6g9U/WKKk
10 changed files with 1613 additions and 43 deletions

View file

@ -32,6 +32,7 @@
#include "dataq.h"
#include "mylock.h"
#include "plugin.h"
#include "sched/types.h"
#include "xce/types.h"
#include "zarray.h"
@ -114,4 +115,122 @@ 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*);
/*
* sched_resolve_job_picker - Req 1 AC 6 / Req 2 AC 5 (scheduler-core spec,
* task 2.4). Resolves core->job_pick_plugin against the plugin registry
* (core->plugins, the same plugin_register()/key_find() mechanism every
* other plugin category uses) and validates the result actually implements
* the picker MUST hooks (xce_job_picker_iface_t: next_job, begin_session,
* end_session, on_dispatch, on_pend). Returns 0 and sets *out on success; a
* negative errno on failure:
* -EINVAL bad arguments, or core->job_pick_plugin is unset (NULL), or
* the name IS registered but is not a usable picker
* -ENOENT the configured name is not registered at all
* Implemented in schedobj.c. Declared here (task 4.3, scheduler-core spec)
* -- moved from scheduler.h's xce_scheduler_t* signature to this
* xce_sched_core_t* signature so xce_core_push_events() (core.c, below) can
* call it directly without core.c depending on scheduler.h (forbidden --
* see this header's own boundary comment). Every existing call site already
* held an xce_scheduler_t* and now passes &sched->core instead; no
* resolution logic is duplicated.
*/
extern int sched_resolve_job_picker(xce_sched_core_t*, xce_sched_plugin_t**);
/*
* xce_core_event_type_t / xce_core_event_t - the core's typed input-event
* representation (scheduler-core spec, task 4.3, Req 5 AC 4). Exactly the 9
* event kinds task 4.3 scopes in: BEGIN_ROUND and END_SESSION belong to
* task 4.4's session-start signal, deliberately not added here.
*
* Each event carries a timestamp (`ts`, nanoseconds since epoch) and a
* type-specific payload (Req 5 AC 4's field tables). Six of the nine kinds
* additionally drive a picker state-change hook (xce_core_push_events()'s
* own doc comment, core.c, maps each type to its hook one-for-one);
* WORKERS_PUSH/RESOURCES_PUSH/RESVALS_FULL/RESVALS_DELTA are state-update
* only -- "picking is about job ordering, not resource availability"
* (plugin.h's own Req 1 AC 2 comment).
*/
typedef enum {
XCE_CORE_EVENT_CONFIG_PUSH = 1,
XCE_CORE_EVENT_JOBS_PUSH,
XCE_CORE_EVENT_WORKERS_PUSH,
XCE_CORE_EVENT_RESOURCES_PUSH,
XCE_CORE_EVENT_RESVALS_FULL,
XCE_CORE_EVENT_RESVALS_DELTA,
XCE_CORE_EVENT_JOB_STATE_CHANGE,
XCE_CORE_EVENT_JOB_UPDATE,
XCE_CORE_EVENT_QUEUE_UPDATE,
XCE_CORE_EVENT_JOB_RUSAGE,
} xce_core_event_type_t;
typedef struct {
xce_core_event_type_t type;
uint64_t ts; /* nanoseconds since epoch */
union {
struct {
xce_conf4sched_t* config;
} config_push;
struct {
int njobs;
xce_job_t** jobs;
} jobs_push;
struct {
int nworkers;
xce_worker_t** workers;
} workers_push;
struct {
int n;
xce_resource_t** resources;
} resources_push;
struct {
int n;
xce_resval_t** resvals;
} resvals_full;
struct {
xce_resval_list_t* deltas;
} resvals_delta;
struct {
uint64_t jobid;
uint32_t old_state;
uint32_t new_state;
int32_t exit_code;
} job_state_change;
struct {
uint64_t jobid;
uint32_t changed_fields;
xce_job_t* job;
} job_update;
struct {
xce_queue_t* queue;
} queue_update;
struct {
uint64_t jobid;
xce_job_rusages_t* rusages;
} job_rusage;
} u;
} xce_core_event_t;
/*
* xce_core_push_events - Req 5 AC 4 / AC 8 (scheduler-core spec, task 4.3;
* design.md "Driver -> Core Protocol", "Core Interface Contract"). Batch-
* pushes `nevents` events, applying each one's core-state mutation in
* order and -- for the six event kinds that have one -- synchronously
* calling the resolved picker's corresponding state-change hook exactly
* once per event, in the calling (driver) thread, before this call
* returns. See core.c for the full per-event-type contract and the
* type-to-hook mapping.
*
* Only valid between scheduling sessions (core->session_active == 0):
* this is what makes calling picker hooks synchronously, from the driver
* thread, safe -- the picker thread is blocked on cbuf_get(&wbuf) and the
* allocator thread on cbuf_get(&jbuf), so no session is concurrently
* reading the state this call mutates. Returns -EBUSY (no event applied)
* if a session is active; -EINVAL for bad arguments (core NULL, or events
* NULL with nevents > 0). Per-event failures (e.g. JOB_UPDATE naming an
* unknown jobid) are logged and do not abort the rest of the batch --
* same discipline scheduler.c's existing sched_update() dataq loop
* already follows.
*/
extern int xce_core_push_events(xce_sched_core_t*, xce_core_event_t*, int);
#endif