--- id: "bug-1499" status: "fixed" status_updated: "2026-09-24" status_note: "" component: "scheduler" severity: "unknown" timestamp: "2026-08-05T00:00:00Z" last_seen: "2026-09-19T09:00:00Z" occurrences: 1 files: ["src/scheduler/src/driver.c"] tags: ["scheduler-core", "task-9.2", "driver.c", "deadlock", "xce_core_signal_go", "accidental-deletion-in-edit", "xce_sched_driver_run_normal_mode_round"] related: [] legacy: true --- # bug-1499: sched_driver_session_ordering_test TIMEOUT (30s, killed by SIGTERM) after adding task 9.2's… ## Description sched_driver_session_ordering_test TIMEOUT (30s, killed by SIGTERM) after adding task 9.2's recording-write hooks to xce_sched_driver_run_normal_mode_round() -- picker thread, allocator thread, and the main round all blocked forever. ## Environment _Not recorded (migrated from buglog.json)._ ## Reproduction _Not recorded (migrated from buglog.json)._ ## Root cause While inserting the new 'if (sched->recording) { rec_ctx = sched_record_session_begin(...); }' block between plugin_begin_session()/sched_account_jobs() and the decision-drain loop, the pre-existing `xce_core_signal_go(&(sched->core));` call was accidentally dropped from the Edit's old_string/new_string pair -- it was the last line of the block being replaced but was not included in the replacement text. Without xce_core_signal_go(), the picker thread's cbuf_get(&core->wbuf) rendezvous never unblocks, which cascades: the stand-in allocator thread never receives a jobid from cbuf_get(&core->jbuf), so it never pushes the end-of-session NULL decision, so the main thread's own cbuf_get(&core->decisions) drain loop blocks forever -- a 3-way deadlock across all three threads. ## Fix Restored `xce_core_signal_go(&(sched->core));` immediately after the new recording session-begin block (matching requirements.md's own annotated pseudocode order: build ReplaySession{beginRound} THEN sched_go()). Caught by re-running the full scheduler test suite after the recording hooks were added -- test_driver_session_ordering.c is the ONLY test in the suite that drives a real picker/allocator thread pair through this exact function, so it was the only test capable of catching a signal_go() regression. ## Verification _Not recorded (migrated from buglog.json)._ ## References _Not recorded (migrated from buglog.json)._