perf: scan installed programs off the main actor#117
Merged
Conversation
updateInstalledPrograms() walked the Program Files trees and parsed every executable's PE header synchronously on the @mainactor Bottle, hitching the UI when opening or switching to a bottle with many installed programs. Move the heavy work off the main actor: a new nonisolated Bottle.discoverInstalledExecutables(driveC:blocklist:) does the directory walk, and PE parsing runs in the same detached task (PEFile is Sendable). Programs are then built on the main actor from the pre-parsed structs via a new Program(url:bottle:peFile:) initializer. A programsLoading flag drives a progress indicator in the programs list while the scan runs. Closes Whisky-App#574.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
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.
Closes the last performance gap from the upstream PR review: upstream #574 "Async bottle loading."
Problem
updateInstalledPrograms()walked bothProgram Filestrees and parsed every executable's PE header synchronously on the@MainActor-isolatedBottle. Opening or switching to a bottle with many installed programs hitched the UI while it scanned. (The audit flagged this as the one still-open item from Whisky-App#574.)Change
Move the heavy work off the main actor, following the existing
duplicate()/exportAsArchive()pattern in the same file:Bottle.discoverInstalledExecutables(driveC:blocklist:)— a newnonisolated statichelper (in WhiskyKit) that does theProgram Fileswalk + filtering (exe extension, ClickOnce cache, noise executables, blocklist). Pure and unit-tested.noiseExecutableNamesmoved into WhiskyKit alongside it.PEFileisSendable, so the detached task parses each executable and returns(URL, PEFile?). A newProgram(url:bottle:peFile:)initializer builds the program on the main actor from the pre-parsed struct (the existinginit(url:bottle:)is now a convenience initializer that parses then delegates — all existing call sites unchanged).programsLoading@Publishedflag drives a progress indicator: the programs-list toolbar shows a spinner in place of the rescan button while a scan runs, and the list refreshes when results publish.BottleViewstart-menu scan via.task, the rescan button,ContentView,ProgramMenuView,PinCreationView) updated toawaitthe now-async scan. A re-entrancy guard coalesces redundant concurrent calls.Tests
New
BottleProgramDiscoveryTests(6 cases) covering the pure helper: cross-tree discovery, non-exe exclusion, ClickOnce-cache exclusion, case-insensitive noise exclusion, blocklist exclusion, empty result.Verification
swift test --package-path WhiskyKit— new tests pass.xcodebuild ... -scheme Whisky— BUILD SUCCEEDED.swiftformat --lint .andswiftlint lint --strict— clean.