feat(scheduler-core): implement what-if branching and simulation output recording (task 11.5)
Add sim_record.c (scheduler_t-free .replay writer: ReplayHeader + loaded SnapshotEntry once, then per-event pass-through and per-session ReplaySession finalization) and wire --sim-alt-events event-source selection into sim_loop.c's drain loop, coexisting with task 11.2's stdout default and task 11.4's --sim-verify. Extract scheduler_t-free halves out of recording_session.c's begin/end so sim-side code can reuse the decision-encoding path without gaining an xce_scheduler_t* dependency, preserving Req 5 AC 17 isolation. Per Req 5 AC 16/19.
This commit is contained in:
parent
a6083d803e
commit
564408e967
14 changed files with 1750 additions and 91 deletions
|
|
@ -273,6 +273,22 @@ typedef struct sched_record_session_ctx_s sched_record_session_ctx_t;
|
|||
extern sched_record_session_ctx_t*
|
||||
sched_record_session_begin(xce_scheduler_t* sched, uint64_t begin_round_ts);
|
||||
|
||||
/*
|
||||
* sched_record_session_ctx_new - scheduler_t-free half of
|
||||
* sched_record_session_begin() above (scheduler-core spec, task 11.5, Req 5
|
||||
* AC 19): allocates and stamps a fresh sched_record_session_ctx_t with
|
||||
* `begin_round_ts`, with no `sched->recording`/`sched->record_file` gate at
|
||||
* all. sched_record_session_begin() is a thin wrapper around this function
|
||||
* (gate check, then this call) -- driver.c keeps calling
|
||||
* sched_record_session_begin() unchanged; this entry point exists so a
|
||||
* caller with NO xce_scheduler_t* (the simulation driver, sim_record.c,
|
||||
* Req 5 AC 17) can still build the identical accumulator, gating on
|
||||
* whatever it has instead (sim->record_file != NULL). Returns NULL only on
|
||||
* allocation failure.
|
||||
*/
|
||||
extern sched_record_session_ctx_t*
|
||||
sched_record_session_ctx_new(uint64_t begin_round_ts);
|
||||
|
||||
/*
|
||||
* sched_record_session_add_decision - appends one drained decision to
|
||||
* `ctx`'s in-progress ReplaySession.decisions list, tagged with `ts`. A
|
||||
|
|
@ -303,6 +319,28 @@ extern int sched_record_session_end(xce_scheduler_t* sched,
|
|||
sched_record_session_ctx_t* ctx,
|
||||
uint64_t end_ts);
|
||||
|
||||
/*
|
||||
* sched_record_session_finalize - scheduler_t-free half of
|
||||
* sched_record_session_end() above (scheduler-core spec, task 11.5, Req 5
|
||||
* AC 19): builds the ReplaySession wire message from `ctx`'s accumulated
|
||||
* decisions and `end_ts`, writes it to `f` directly, then frees `ctx` and
|
||||
* everything it owns. sched_record_session_end() is a thin wrapper around
|
||||
* this function (its own gate check plus the same
|
||||
* sched_record_abort_on_error() call on failure); this entry point exists
|
||||
* so a caller with no xce_scheduler_t* (the simulation driver,
|
||||
* sim_record.c, Req 5 AC 17) can finalize+write its own accumulated
|
||||
* session against its own FILE* (sim->record_file) without that gate.
|
||||
* Returns 0 on success, a negative errno from write_capnp_message() on
|
||||
* failure -- same contract sched_record_session_end() itself documents.
|
||||
* `ctx` must be non-NULL (unlike sched_record_session_end(), this function
|
||||
* has no NULL-ctx no-op case -- the caller already knows it has a live ctx
|
||||
* by construction, since it only ever calls this after a successful
|
||||
* sched_record_session_ctx_new()).
|
||||
*/
|
||||
extern int sched_record_session_finalize(FILE* f,
|
||||
sched_record_session_ctx_t* ctx,
|
||||
uint64_t end_ts);
|
||||
|
||||
/*
|
||||
* sched_record_status - Req 5 AC 10 (scheduler-core spec, task 9.4,
|
||||
* "Reply field semantics by action" -- the `recordStatus` column). A pure
|
||||
|
|
|
|||
|
|
@ -52,6 +52,19 @@
|
|||
* it; this task only stores it)
|
||||
* bool interactive; -- --interactive/-i (task 12 acts on
|
||||
* it; this task only stores it)
|
||||
*
|
||||
* `snapshot_ts` below is ONE field beyond design.md's literal 8-field
|
||||
* struct sketch (scheduler-core spec, task 11.5, Req 5 AC 19) -- added, not
|
||||
* merely implied, because writing a `--sim-output` file's initial
|
||||
* SnapshotEntry needs the EXACT timestamp Step 2's snapshot scan
|
||||
* (sim_locate_and_load_snapshot(), sim.c) chose, and re-deriving it via
|
||||
* xce_core_now(&sim->core) from inside sim_loop.c's event loop is NOT
|
||||
* equivalent for any --sim-speed other than frozen (0): the virtual clock
|
||||
* keeps advancing with real wall-clock time between
|
||||
* sched_sim_driver_start() returning and sim_loop.c's first opportunity to
|
||||
* write the output header/snapshot, so a re-derived value would drift from
|
||||
* "the same SnapshotEntry that was loaded" (AC 19's own wording -- same ts,
|
||||
* not merely a same-shaped one taken moments later).
|
||||
*/
|
||||
typedef struct {
|
||||
xce_sched_core_t core;
|
||||
|
|
@ -62,6 +75,7 @@ typedef struct {
|
|||
uint64_t sim_speed_den;
|
||||
bool verify;
|
||||
bool interactive;
|
||||
uint64_t snapshot_ts;
|
||||
} xce_sched_sim_driver_t;
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue