From 5f7129213150077af0762abe95245688c6750c65 Mon Sep 17 00:00:00 2001 From: "NathanNeurotic (Ripto)" <109461996+NathanNeurotic@users.noreply.github.com> Date: Mon, 20 Jul 2026 10:00:55 -0700 Subject: [PATCH] fix(mmce): quiesce the art worker BEFORE the VCD launch handoff (FifthFox's bombed VCD launch) FifthFox (HW): "MMCE VCD launching is either poisoned or not working. It was working not that long ago. I bombed one launch of a VCD on the MMCE." ROOT CAUSE (ordering, probabilistic -- which is exactly why it "worked until it didn't"): the MMCE art quiesce (cacheAbortMmceImageLoadsTimed, March) drains in-flight art reads before the launch path touches the card. The VCD by-name handoff (ccd1d7a4, 2026-06-24) was added ABOVE it as an early return -- so a VCD launch skipped the quiesce entirely and mmceLaunchVcd's POPSTARTER resolution + BDMA equip performed card IO on the SAME shared mmceman SIO2 channel while the art worker could be mid-read. The disc path has always quiesced first; the VCD path never did. FIX: move the single quiesce guard ABOVE the VCD handoff so both launch paths are covered before any card IO. The wedged-worker branch keeps the full #120 abandon-and-retry rationale (no TerminateThread, toast + bail, never a hard freeze) and now protects the VCD path too -- POPSTARTER's own reads would hit the same wedged channel and dead-launch to OSDSYS. Idea source: PR #236 commit 66eeaa7d (the unified agent PR), vetted against this tree -- the reorder is correct; re-landed here on current master with the explanatory comment kept instead of trimmed. Builds clean (make opl.elf, exit 0). HW-pending: FifthFox's MMCE VCD launches. Co-Authored-By: Claude Fable 5 --- src/mmcesupport.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/mmcesupport.c b/src/mmcesupport.c index 3bccf192a5..3f5af7bb57 100644 --- a/src/mmcesupport.c +++ b/src/mmcesupport.c @@ -614,23 +614,34 @@ void mmceLaunchGame(item_list_t *itemList, int id, config_set_t *configSet) return; sbSetBrowseSub(folderGetSub(itemList->mode)); - // VCD view: hand off to POPSTARTER (by name) instead of the disc path below. Menu-launch only. - if (gAutoLaunchBDMGame == NULL && game != NULL && vcdViewActive(itemList->mode)) { - mmceLaunchVcd(itemList, game->name, configSet); - return; - } - + // Quiesce every in-flight MMCE art read BEFORE either launch path touches the card. The VCD + // handoff below resolves POPSTARTER and may equip BDMA modules -- real reads/writes on the SAME + // shared mmceman SIO2 channel -- and its early return used to run BEFORE this guard, so a VCD + // launch could collide with the art worker mid-read (FifthFox: "bombed one launch of a VCD on + // the MMCE"; the disc path below has always quiesced first). The by-name handoff (ccd1d7a4) + // landed AFTER the quiesce existed and slotted in above it -- ordering bug since, probabilistic + // by nature, which is why it "worked until it didn't". Idea source: PR #236's quiesce-reorder, + // vetted against this tree and re-landed with the full #120 rationale kept. if (!cacheAbortMmceImageLoadsTimed(MMCE_ART_ABORT_WAIT_TICKS)) { // #120: the art worker is wedged in a blocking fileXio on a slow/desynced card. Do NOT cacheEnd(1) // here -- its TerminateThread(gArtThreadId) kills the worker MID-RPC and orphans the SHARED mmceman // channel (TK>0). The launch reads below then FAIL and RETURN to a still-running OPL (unlike a launch // that LoadExecPS2's away), poisoning every later card read. Abandon-and-retry instead (mirrors // thmLoad's redesign): toast and bail so the user retries once the card is calm. NEVER a hard - // freeze -- guiWarning is non-blocking. + // freeze -- guiWarning is non-blocking. Applies to the VCD handoff too: POPSTARTER's own reads + // would hit the same wedged channel and dead-launch to OSDSYS. guiWarning(_l(_STR_ERR_FILE_INVALID), 8); return; } + // VCD view: hand off to POPSTARTER (by name) instead of the disc path below. Menu-launch only. + if (gAutoLaunchBDMGame == NULL && game != NULL && vcdViewActive(itemList->mode)) { + mmceLaunchVcd(itemList, game->name, configSet); + return; + } + + // (The MMCE art quiesce runs ABOVE the VCD handoff now -- both launch paths are covered by the + // single guard before any card IO.) void *irx = &mmce_cdvdman_irx; int irx_size = size_mmce_cdvdman_irx; compatmask = sbPrepare(game, configSet, irx_size, irx, &index);