Iterate-2.BE Path β: tick vsync from coord_idle_advance

The iterate-2.BE host-driven synchronous ISR dispatcher relies on
something queueing v-syncs. In lockstep that's `tick_vsync_instr`,
called from `coord_pre_round` per round. If the scheduler stalls into
`coord_idle_advance` (no Ready threads), the instruction counter
freezes — the accumulator stops incrementing, the ticker stops
queueing, and the dispatcher is left starved for the duration of the
idle wait.

Tick `tick_vsync_wallclock` at the top of `coord_idle_advance` so
v-syncs keep firing on host time even when the guest scheduler is
parked. The dispatcher in the outer loop drains whatever we queue on
the next iteration. Same MMIO `D1MODE_VBLANK_VLINE_STATUS` bit-set as
the production path.

Note: empirically in Sylpheed at 50M/500M instruction horizons,
`coord_idle_advance` is never reached (tids 9/10/12 stay Ready through
the early-boot deadlock), so this commit doesn't move
`gpu.interrupt.delivered{source=0}` off 54 for this title at these
horizons. It is the correct fix for the documented starvation pattern
and will activate as soon as the kernel reaches a state where Ready
threads drop to zero with timers/waits pending.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-06 19:22:03 +02:00
parent 9a93152981
commit 51489e34db

View File

@@ -2016,6 +2016,24 @@ fn coord_idle_advance(
shutdown: &Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
stats: &ExecStats,
) -> RoundCtl {
// Path β (iterate-2.BE follow-up): when the scheduler has no Ready
// threads, `coord_pre_round`'s instruction-count vsync ticker stops
// advancing (instruction_count is frozen). That starves the
// host-driven graphics ISR dispatcher: queue stays empty, no
// deliveries occur, and the very stall we're trying to break out of
// gets worse. Tick vsync from wallclock here unconditionally — it's
// a host-clock read, independent of instruction count, and the
// dispatcher in the outer loop will drain whatever we queue on the
// next pass. Mirrors the `--parallel` ticker choice in
// `coord_pre_round` (`tick_vsync_wallclock` branch).
if kernel.interrupts.tick_vsync_wallclock() {
use std::sync::atomic::Ordering;
let mmio = kernel.gpu.mmio();
let prev = mmio.d1mode_vblank_vline_status.load(Ordering::Relaxed);
mmio.d1mode_vblank_vline_status
.store(prev | 0x1, Ordering::Relaxed);
}
let next_timer = kernel.earliest_timer_deadline();
let next_wait = kernel.scheduler.earliest_wait_deadline();
let target = match (next_timer, next_wait) {