feat: Profiles switch safety - #21815
Open
criticalAY wants to merge 6 commits into
Open
criticalAY wants to merge 6 commits into
criticalAY wants to merge 6 commits into
Conversation
david-allison
requested changes
Sep 11, 2026
david-allison
left a comment
Member
There was a problem hiding this comment.
Tests are excellent. Blocker on ProcessPhoenix (or vendoring; probably best to take on the dependency).
Note the 'Licenses' section of the PR template
criticalAY
force-pushed
the
profiles/switch-safety
branch
from
September 11, 2026 22:29
98c6ee8 to
403cd3c
Compare
david-allison
self-requested a review
September 11, 2026 23:10
david-allison
requested changes
Sep 12, 2026
criticalAY
force-pushed
the
profiles/switch-safety
branch
from
September 12, 2026 18:30
403cd3c to
641f129
Compare
criticalAY
force-pushed
the
profiles/switch-safety
branch
from
September 12, 2026 19:02
641f129 to
d0cc9e5
Compare
ProcessPhoenix will do the restart. It relaunches the app from a separate process once this one has been killed, so the relaunch is never handed to a process that is still dying. It is Apache 2.0 and has no dependencies of its own.
PhoenixActivity runs in its own :phoenix process, and Android creates the Application in every process of the app. AnkiDroidApp would start the backend and the rest of the app there, in a process that only lives long enough to kill the old one and launch the new one. It now returns straight away there. Assisted-by: Claude Opus 5 (multiple iterations over original code)
A profile switch restarts the process, so it has to wait for work that outlives the collection queue, and keep new work from starting. A backup keeps writing after the backend releases the collection, and syncs run in workers. Each now holds a lock while it runs, for the restart to take. A worker that finds its lock taken drops its work rather than rescheduling it, since the work carries the AnkiWeb key of the profile being left. The backend cannot be asked to wait for a backup from elsewhere: it hands its running backup to the first caller that asks, and createBackup asks as soon as the backup starts. Assisted-by: Claude Opus 5 (multiple iterations over original code)
A profile switch cannot be applied in place: the backend, WebView and SharedPreferences all cache paths from the profile that was active when the process started, so the process has to die and come back. The restart takes the sync lock first, waiting for a sync that is already running, since that sync queues a media sync before it lets go of its lock. Sync work is then cancelled, since it carries this profile's AnkiWeb key, which also stops a running media sync rather than waiting it out. The media sync and backup locks come next. All three are kept until ProcessPhoenix kills the process, so nothing can start in the meantime, and released if the handover fails. The collection is then closed: the backend holds it open, and killing the process mid-write risks corrupting it. ensureClosed runs on the collection queue, so it also waits for an operation that is already running. Assisted-by: Claude Opus 5 (multiple iterations over original code)
A switch is followed by a process restart, and SharedPreferences.apply() writes to disk asynchronously. Android does not flush pending apply() writes when the process is killed, so the new profile id could be lost and the app would come back on the old profile. Commit the switch write. Startup keeps the asynchronous write, since nothing kills the process after it.
criticalAY
force-pushed
the
profiles/switch-safety
branch
from
September 29, 2026 07:30
d0cc9e5 to
fc4fc68
Compare
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.
Note
Assisted-by: Assisted-by: Claude Opus 5
Purpose / Description
Switching profiles has to restart the app, because the backend, WebView and SharedPreferences all hold on to the old profile's paths. Nothing did that restart yet, and nothing stopped a switch from killing a sync or backup halfway through. This adds the restart and the checks that guard it. Nothing calls them yet; wiring them to the profile list is the next PR.
Fixes
Approach
See commits
How Has This Been Tested?
Unit tests cover the restart
Checklist
Please, go through these checks before submitting the PR.
Licenses
Maintainers: