fix(scheduler): the simulator's threads stop when its run returns (bug-1632, bug-1763)

sched_sim_driver_run() started the picker and allocator threads on
&sim->core and returned with them still running; only
sched_sim_driver_fini() stopped them, and 24 test files (35 call sites)
run the driver on a stack-local sim without calling it. The leftover
threads kept a pointer into the dead frame, and ASAN occasionally caught
one reading it (stack-use-after-return in cbuf_get) in
sched_sim_verify_test and sched_sim_record_output_test.

sched_sim_driver_run() now wraps the loop and stops both threads on
every return (new sched_sim_driver_stop_threads(), shared with fini). If
the allocator fails to start, the running picker is stopped too. The
stop no longer pushes a jobid 0 to wake the allocator: cbuf_get/put wait
in pthread_cond_wait, a cancellation point, and the fake end-of-session
was visible to the allocator.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-09-30 02:47:24 +00:00
parent dc94da207a
commit cfa0a94166
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
4 changed files with 101 additions and 18 deletions

View file

@ -256,15 +256,26 @@ extern int sched_sim_driver_start(xce_sched_sim_driver_t* sim,
* 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).
* sim_loop.c's own file header comment for why one exists). Whatever it
* returns, the picker and allocator threads it started have been stopped
* (bug-1632/bug-1763): `sim` may go out of scope right after.
*/
extern int sched_sim_driver_run(xce_sched_sim_driver_t* sim,
const sched_sim_args_t* args);
/*
* sched_sim_driver_stop_threads - bug-1632/bug-1763: cancels and joins the
* picker and allocator threads sched_sim_driver_run() started (the way
* sched_fini() does in normal mode). sched_sim_driver_run() calls it on
* every return, so no thread outlives the run holding a pointer to `sim`
* (callers commonly keep it on the stack). A no-op when none are running.
*/
extern void sched_sim_driver_stop_threads(xce_sched_sim_driver_t* sim);
/*
* sched_sim_driver_fini - bug-1884: releases what a sched_sim_driver_run()
* that returned 0 left behind: cancels and joins the picker and allocator
* threads (the way sched_fini() does in normal mode), closes the replay,
* that returned 0 left behind: stops the picker and allocator threads if
* still running (see sched_sim_driver_stop_threads), closes the replay,
* alternate-events and output files, and finalizes the core. Call it only
* after sched_sim_driver_run() returned 0; a failed run has either
* finalized the core already (a startup failure) or is about to exit.