Files
Brian Degenhardt cd20f6454d Android: flush memory card writes when the app is backgrounded
A memory card write does not necessarily reach the file system when it happens,
and on Android that is data loss rather than a detail.

A FOLDER card holds writes in an in-memory page cache and flushes two frames
after the last one, counted down by the per-frame tick that runs off vsync. So
pausing does not delay that flush, it stops it ever being reached -- the counter
does not advance at all while the VM is paused.

A FILE card writes through stdio with no flush anywhere in the path. Seeking on
an update stream pushes the previous write out, so a run of writes mostly
self-corrects, but the last write of a save sequence sits in the buffer until the
next card access or fclose.

Either way the pending write is lost if Android reclaims the process while it is
backgrounded, which it may do with no further callback. Save in-game, switch
apps, get reclaimed, and the save was never on disk.

The pause path already handles exactly this shape for the BIOS NVRAM
(cdvdSaveNVRAM, added because "the process is frequently killed while paused"),
so the card flush goes next to it, in both the Running and already-Paused
branches. It runs on the CPU thread, queued after SetPaused, so the console is
stopped and nothing can be written behind it, and it is fire-and-forget -- onPause
is on a deadline and blocking it risks an ANR.

  - FileMcd_Flush() / FileMemoryCard::Flush() / FolderMemoryCardAggregator::Flush()
    write out what is buffered without closing anything, so the console keeps
    playing afterwards.
  - FileMemoryCard::Flush deliberately does NOT stamp the running checksum the way
    Close() does. That value is a change-detector a savestate load compares to
    decide whether the card moved under the console, not an integrity check, and
    m_chkaddr is card data rather than a header field we own. A stale value costs
    one auto-eject on the next savestate load, which is the safe direction, so
    writing to the card on a path upstream never writes on buys nothing.
  - FolderMemoryCard::FlushNow clears the frame countdown so a resumed VM does not
    repeat the work. Flush() is already a no-op when nothing is cached, so calling
    this on a quiet card costs nothing.
  - Save() flushes each sector as it is written.

Also bounds the emulation-thread join in onDestroy. NativeApp.shutdown() already
gives up waiting after 5 s and returns anyway, so an unbounded join inherited a
wedged CPU thread and hung the destroy path until Android force-closed us.
2026-08-20 09:22:18 -07:00
..
2026-07-15 19:36:20 +02:00
2026-07-14 09:06:03 +02:00
2026-06-01 09:51:12 -04:00
2026-05-18 07:31:15 -04:00
2026-03-09 22:18:53 -04:00
2026-06-20 20:27:55 -07:00
2026-03-15 08:27:44 -04:00
2026-03-15 08:27:44 -04:00
2026-03-15 08:27:44 -04:00
2026-03-15 08:27:44 -04:00
2026-03-15 08:27:44 -04:00