Fix torrent loss on restart, session-shutdown ANR, and player icon overlap#17
Merged
Merged
Conversation
…erlap Torrents could disappear from the list after an app restart even though their data was still on disk. Three causes, all fixed: - onStop saved resume data on lifecycleScope (cancelled at ON_DESTROY) while onDestroy released the session on the main thread; when release won the race, nothing was saved. A new SessionLifecycleCoordinator now serializes init/save/release on a FIFO queue owned by a process-wide scope, so release can never run before a pending save. - Resume files were written non-atomically; a process kill mid-write truncated previously-good files. Writes now go through tmp + rename, and corrupt files are quarantined as .corrupt and recovered via a magnet re-add (the filename embeds the info-hash). - Resume data was only saved at onStop. It is now also requested eagerly on add_torrent, metadata_received, and torrent_finished alerts. The session-recreate path (save path change) caused an ANR: nativeRelease held the global mutex while libtorrent waited on tracker stop-announces (~27s), blocking a main-thread addMagnet call. nativeRelease now aborts the session under the lock and finishes shutdown on a detached thread via session_proxy, and all native calls moved off the main thread: the repository owns the threading contract, routing every mutating call through the session queue (which also waits for pending init, so calls can no longer hit an uninitialized session). Also fixed: subtitle (CC) and audio-track buttons in the player rendered stacked on top of each other (Box -> Row), Copy magnet silently writing an empty string to the clipboard, and dead background-mode plumbing (onAppBackground/onAppForeground, setBackgroundMode) removed. Tests: 9 JVM unit tests for the coordinator (including a regression test for the save/release race) and 5 instrumented persistence tests (restart round-trip, eager save, quarantine, magnet recovery, removal).
jpcottin
added a commit
that referenced
this pull request
Jun 11, 2026
…epository The DataRepository interface changes in #17 (suspend mutating methods, setBackgroundMode removed) missed MainScreenTest's inline fake, breaking compileDebugAndroidTestKotlin in CI. The local run before pushing had reused a stale test APK, which masked the error.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Root causes & fixes
Resume-data persistence
onStopsaved resume data onlifecycleScope(cancelled atON_DESTROY) whileonDestroyreleased the session on the main thread; if release won the race, nothing was saved. A newSessionLifecycleCoordinatorserializes init/save/release on a FIFO queue owned by a process-wide scope — release can never run before a pending save..corruptand recovered via magnet re-add (the filename embeds the info-hash, so libtorrent rechecks the data already on disk).onStop; it is now also requested eagerly onadd_torrent,metadata_received, andtorrent_finishedalerts.ANR
nativeReleaseheld the global native mutex while libtorrent waited on tracker stop-announces (~27 s observed), blocking a main-threadaddMagnetcall. It nowabort()s under the lock and finishes shutdown on a detached thread viasession_proxy(re-init latency went from ~27 s to ~1 ms).DefaultDataRepositorynow owns the threading contract, routing every mutating call through the session queue — which also waits for pending init, so calls can no longer hit an uninitialized session.UI
Box→Row(they were overlapping).onAppBackground/onAppForeground,setBackgroundMode).Tests
onDestroy_neverReleasesSessionBeforePendingSave).