feat(scheduler-core): implement simulation event loop and virtual clock advancement (task 11.2)

Add sim_loop.c implementing sched_sim_driver_run(): decode .replay
messages in file order, jump the virtual clock to each ReplayEvent's
exact timestamp as it's queued, and run a real push-events/go/drain/
end-session cycle on each ReplaySession boundary (AC 14, AC 18).

Task 11.1 left no picker registered against the isolated core and no
thread backing its cbufs, which deadlocks the decision drain the moment
a real event loop exists. Add plugin_register_core() (registers a
picker against a bare xce_sched_core_t*, no xce_scheduler_t* required)
and extract builtin_fcfs_new_plugin() from register_builtin_fcfs() as a
behavior-preserving factory, plus a picker-driving thread for the sim
driver. Since real resource allocation is unreachable without a live
xce_scheduler_t* (scheduler-allocation spec's job), the sim picker
thread conservatively PENDs every candidate rather than dispatching.

28/28 scheduler suite, 32/32 whole-project tests green.
This commit is contained in:
Rongsong Shen 2026-08-06 10:27:40 +00:00
parent 5d587ecf32
commit 7e1e861e4f
Signed by: rshen
SSH key fingerprint: SHA256:8Vf2pZ3h6rJa7EhGK+WaAQ9Mta/apZvteW6g9U/WKKk
11 changed files with 1248 additions and 76 deletions

View file

@ -123,6 +123,43 @@ extern int register_parjob_allocator(void*);
* init() below only ever holds/passes a pointer to it. */
typedef struct xce_sched_core_s xce_sched_core_t;
/*
* builtin_fcfs_new_plugin - factory half of register_builtin_fcfs()'s
* template-build step (plugins/builtin_fcfs.c), extracted so a caller with
* no xce_scheduler_t* (e.g. the simulation driver, via
* plugin_register_core() below) can obtain a freshly calloc'd, fully
* populated `xce_sched_plugin_t` for builtin_fcfs -- same picker_iface/
* init_fn/fini_fn template register_builtin_fcfs() itself uses, just not
* yet registered anywhere. Returns NULL on allocation failure. Caller owns
* the returned plugin (frees it only on a subsequent registration failure
* -- once registered, the plugin registry owns it, same lifetime rule
* every other plugin category in this codebase already follows).
*/
extern xce_sched_plugin_t* builtin_fcfs_new_plugin(void);
/*
* plugin_register_core - Req 5 AC 17 (scheduler-core spec, task 11.2): the
* "core.h-scoped plugin registration path" sim.h's own doc comment on
* sched_sim_driver_start() anticipated but did not build (task 11.1's
* documented "Known scope boundary"). plugin_register() above requires a
* genuine `xce_scheduler_t*` (it dereferences `sched->core.plugins` and
* stores `plugin->sched = sched` for plugin_unregister()'s later use) --
* unavailable to a driver like the simulation driver that deliberately has
* no such type. This is the SAME registration mechanism minus that one
* dependency: run the plugin's init_fn (same "driver thread, at plugin
* register" timing plugin.h's own hook-category doc comment documents),
* then key_add() it into `core->plugins` directly -- identical steps
* plugin_register() itself performs, in the same order, just addressed at
* xce_sched_core_t* instead of xce_scheduler_t*. `plugin->sched` is left
* NULL (no xce_scheduler_t* exists to store) -- safe, since
* plugin_unregister() is a no-op on a NULL `plugin->sched` and nothing
* calls it for a plugin registered this way (no sim-driver teardown path
* exists yet). Returns the assigned plugin_id (>= 0) on success, or a
* negative errno: -EINVAL for a NULL core/plugin, whatever init_fn
* returned if it failed, or key_add()'s own negative return.
*/
extern int plugin_register_core(xce_sched_core_t*, xce_sched_plugin_t*);
/*
* Hook categories and the thread each category runs in (Req 1 AC 3):
*

View file

@ -205,22 +205,19 @@ extern int sched_sim_driver_start(xce_sched_sim_driver_t* sim,
* void* args)`, adapted to take the concrete sched_sim_args_t* this header
* defines rather than an opaque void* -- see sched_sim_args_t's own doc
* comment for why). This is what main.c's mode-selection branch calls when
* `--sim` is present (design.md's own Integration note).
* `--sim` is present (design.md's own Integration note). Defined in
* sim_loop.c (scheduler-core spec, task 11.2), not sim.c: calls
* sched_sim_driver_start() above (task 11.1) then runs Req 5 AC 13 step
* 5's event loop (Req 5 AC 14/18) -- decode each `.replay` message in file
* order, jump the virtual clock to each ReplayEvent's own timestamp as it
* is queued (AC 14), and on each ReplaySession boundary run one complete
* push-events -> start-session -> drain-decisions -> end-session round.
*
* Calls sched_sim_driver_start() above (task 11.1's own work, this task's
* Observable) then -- since task 11.2's event loop (Req 5 AC 13 step 5 /
* AC 14/18) does not exist yet -- fails loudly with -ENOSYS rather than
* silently returning success having processed zero events. This is the
* same "honest, temporary placeholder" precedent main.c's own pre-task-11.1
* `--sim` stub already established (task 5.2): startup is now genuinely
* real, working behavior; only the event loop remains a documented gap for
* task 11.2 to fill in, without requiring another main.c edit when it does
* (task 11.2 only needs to extend THIS function's body, after the
* sched_sim_driver_start() call, with the real event loop).
*
* Returns 0 (once task 11.2 lands) or a negative errno: whatever
* sched_sim_driver_start() returned on a startup failure, or -ENOSYS if
* startup succeeded but the event loop is not yet implemented.
* Returns 0 once the replay stream is exhausted (its ReplayEvent{endSession}
* marker is reached, Req 5 AC 20), or a negative errno: whatever
* sched_sim_driver_start() returned on a startup failure, or a pthread
* error standing up the picker-driving thread the event loop needs (see
* sim_loop.c's own file header comment for why one exists).
*/
extern int sched_sim_driver_run(xce_sched_sim_driver_t* sim,
const sched_sim_args_t* args);