From c5ee8fb105e58f6cf1c276c690d09e77ed18400e Mon Sep 17 00:00:00 2001 From: Alan Bradburne Date: Tue, 27 Oct 2026 17:48:54 +0900 Subject: [PATCH] frontend: start up as an ordinary task, not a real-time one The main thread went real-time right after the settings were read and then ran to the first frame without a sleep. Under the 200 ms RLIMIT_RTTIME a PipeWire client passes on to the processes it starts (DSlot's shelf is one), the kernel's RT watchdog killed it with a bare SIGKILL between the settings and SDL_Init whenever that stretch took over 200 ms of CPU: on a slow clock, reproduced at 408 MHz on the RG DS+, and reported from the field on alpha3 as "DSperate stopped at once (signal 9)" with no crash line. The OffRealtime guard that already covered the ROM load now starts right after the scheduling latch, so start-up runs as an ordinary task; the workers take the latched policy themselves, and the guard still lifts, with a nap, where emulation begins. Co-Authored-By: Claude Fable 5.1 --- src/frontend/sdl/main.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/frontend/sdl/main.cpp b/src/frontend/sdl/main.cpp index 37e4e375..d3f15833 100644 --- a/src/frontend/sdl/main.cpp +++ b/src/frontend/sdl/main.cpp @@ -1221,6 +1221,13 @@ static int run(int argc, char** argv) { } ds::latch_worker_sched(); // what the video workers run at, whichever thread starts them } + // Latched, the main thread drops back to an ordinary task for the rest of start-up + // (rt_budget.h): from here to the first frame it never sleeps, and under the 200 ms + // RLIMIT_RTTIME a PipeWire client passes on to the processes it starts (DSlot's shelf is + // one), the kernel killed it with a bare SIGKILL between the settings and SDL_Init on a + // slow clock. The workers take the latched policy themselves; the guard is lifted, with a + // nap, where emulation begins. + std::optional startup_off_rt(std::in_place); // CPU timing model (core/cpu/timing_mode.h), fixed before anything runs or // the recompiler emits its stubs. DS_TIMING wins. if (cfg.has("emu.timing") && !std::getenv("DS_TIMING")) ds::g_fast_timing = cfg.str("emu.timing") != "exact"; @@ -1755,9 +1762,8 @@ static int run(int argc, char** argv) { if (!nds.load_dsi_nand_title(sc.title_lo, &err)) { std::fprintf(stderr, "dsi: %s: %s\n", path.c_str(), err.c_str()); return false; } return true; }; - // From the ROM to the starting state: one-shot work, off the real-time - // class (rt_budget.h). Inflating a big zipped ROM alone can outlast RLIMIT_RTTIME. - std::optional startup_off_rt(std::in_place); + // From the ROM to the starting state: still off the real-time class (startup_off_rt + // above). Inflating a big zipped ROM alone can outlast RLIMIT_RTTIME. if (!bios_error.empty()) { start_failed(rom_path, bios_error.find("firmware") != std::string::npos ? "FIRMWARE IS BAD" : "DS BIOS IS BAD"); return quit_early(1);