Skip to content

Comments

fixed ios 16 md5 hash failing; fixed bios watcher crashing#2409

Merged
JoeMatt merged 3 commits intoProvenance-Emu:developfrom
pabloarista:bug/ios16-md5-fails-bios-watcher-crash
Mar 25, 2025
Merged

fixed ios 16 md5 hash failing; fixed bios watcher crashing#2409
JoeMatt merged 3 commits intoProvenance-Emu:developfrom
pabloarista:bug/ios16-md5-fails-bios-watcher-crash

Conversation

@pabloarista
Copy link
Contributor

@pabloarista pabloarista commented Mar 23, 2025

User description

What does this PR do

  1. Fixes getting MD5 hash on iOS 16
  2. Fixes crashes initially when the user installs the app for the first time and the BIO directory observer accesses set, but on a different thread it's being modified

Where should the reviewer start

  1. BIOSDirectoryWatcher.swift changes are all in the same file
  2. MD5 changes are in multiple files, change is to just accept a URL since all callers do have access to the URL, so no need to recreate a URL

How should this be manually tested

  1. Install the app on any iOS 16 device and add a ROM that has spaces in it
  2. Uninstall the app and reinstall it and ensure the app does NOT crash. You can also confirm by doing this a few more times.

Any background context you want to provide

  1. Just discovered this while working on another PR

What are the relevant tickets

  1. N/A

Screenshots (important for UI changes)

  1. N/A

Questions

  1. N/A

PR Type

Bug fix, Enhancement


Description

  • Fixed MD5 hash computation for iOS 16 compatibility.

  • Resolved BIOS directory watcher crashes due to threading issues.

  • Refactored MD5-related methods to use URL instead of String paths.

  • Introduced FileOperationTasks actor for thread-safe file operation management.


Changes walkthrough 📝

Relevant files
Enhancement
11 files
ImportExtension.swift
Updated MD5 hash method to use `URL` parameter.                   
+1/-1     
MD5Provider.swift
Updated MD5Provider protocol to use `URL` parameter.         
+1/-1     
NSFileManager+Hashing.swift
Refactored MD5 computation to use `URL` instead of `String`.
+1/-10   
RomDatabase.swift
Updated MD5 hash calls to use `URL` parameter.                     
+2/-1     
ImportQueueItem.swift
Updated MD5 computation to use `URL` parameter.                   
+1/-1     
GameImporterDatabaseService.swift
Adjusted file handling to use `URL` and fixed relative root handling.
+6/-6     
PVFile.swift
Updated MD5 computation logic to use `URL`.                           
+1/-2     
LocalFile.swift
Updated MD5 computation to use `URL` parameter.                   
+1/-1     
FileInfoProvider.swift
Refactored MD5 computation to use `URL` instead of `String`.
+1/-1     
PVRetroArchCoreManager.swift
Updated MD5 hash method to use `URL` parameter.                   
+1/-1     
GameLaunchingViewController.swift
Refactored MD5 hash computation to use `URL`.                       
+3/-3     
Bug fix
1 files
BIOSDirectoryWatcher.swift
Added `FileOperationTasks` actor for thread-safe operations.
+53/-24 
Tests
1 files
GameImporterSystemsServiceTests.swift
Updated mock MD5Provider to use `URL` parameter.                 
+1/-1     

Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • @qodo-code-review
    Copy link

    Qodo Merge was enabled for this repository. To continue using it, please link your Git account with your Qodo account here.

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Task Leak

    In the handleBIOSFileFound method, a nested Task is created but its result is never awaited, potentially causing a task leak. The inner task that awaits the task.value and removes it from fileOperationTasks is created but not stored or awaited.

    Task {
        // Store the task for potential cancellation
        await fileOperationTasks.insert(task)
    
        // Set up cleanup when task completes
        await Task {
            await task.value
            await fileOperationTasks.remove(task)
        }
    }
    Deinit Task

    The deinit method creates a Task to cancel all file operations, but since deinit is synchronous and the task is asynchronous, there's no guarantee the cancellation will complete before the object is deallocated.

    Task {
        await fileOperationTasks.cancelAllFileOperations()
    }
    Inconsistent Root

    PVFile and PVImageFile are created with inconsistent relativeRoot values. Some use .iCloud and others use .documents, which might cause issues with file path resolution.

    let file = PVFile(withURL: destinationUrl, relativeRoot: .iCloud)
    let game = PVGame(withFile: file, system: system)

    @qodo-code-review
    Copy link

    qodo-code-review bot commented Mar 23, 2025

    Qodo Merge was enabled for this repository. To continue using it, please link your Git account with your Qodo account here.

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Fix incorrect variable reference
    Suggestion Impact:The commit implemented exactly what was suggested - changing the variable reference from 'path' to 'url' in the md5ForFile method call, fixing a potential compilation error

    code diff:

    -            guard let calculatedMD5 = FileManager.default.md5ForFile(at: path, fromOffset: 0) else {
    +            guard let calculatedMD5 = FileManager.default.md5ForFile(at: url, fromOffset: 0) else {

    The variable path is used but it should be url. The method was updated to use
    at: URL instead of atPath: String, but this line is using a non-existent path
    variable instead of the available url variable.

    PVLibrary/Sources/PVRealm/RealmPlatform/Entities/Files/PVFile.swift [123]

    -guard let calculatedMD5 = FileManager.default.md5ForFile(at: path, fromOffset: 0) else {
    +guard let calculatedMD5 = FileManager.default.md5ForFile(at: url, fromOffset: 0) else {

    [Suggestion has been applied]

    Suggestion importance[1-10]: 9

    __

    Why: The code is using a non-existent path variable instead of the available url variable, which would cause a compilation error. This is a critical issue that would prevent the code from working properly.

    High
    Fix task awaiting pattern

    The nested Task is incorrectly awaited directly, which will cause the outer task
    to complete immediately without waiting for the inner task. The inner task
    should be created and then awaited with await innerTask.value.

    PVLibrary/Sources/PVLibrary/DirectoryWatcher/BIOSDirectoryWatcher.swift [545-553]

     @objc private func handleBIOSFileFound(_ notification: Notification) {
         guard let fileURL = notification.object as? URL else { return }
     
         // Process the file off the main thread
         let task = Task.detached(priority: .utility) {
             await self.processBIOSFiles([fileURL])
         }
         Task {
             // Store the task for potential cancellation
             await fileOperationTasks.insert(task)
             
             // Set up cleanup when task completes
    -        await Task {
    +        let innerTask = Task {
                 await task.value
                 await fileOperationTasks.remove(task)
             }
    +        await innerTask.value
         }
     }

    [To ensure code accuracy, apply this suggestion manually]

    Suggestion importance[1-10]: 8

    __

    Why: The current implementation incorrectly awaits the Task creation directly, which would cause the outer task to complete immediately without waiting for the inner task to finish. This could lead to resource leaks or race conditions.

    Medium
    Fix notification posting syntax

    The notification is being created with a dot-prefixed name
    (.RomDatabaseInitialized), but this syntax requires a static property on
    Notification.Name. Ensure that this notification name is properly defined as a
    static extension on Notification.Name elsewhere in the codebase, or use a
    string-based name.

    PVLibrary/Sources/PVLibrary/Database/Realm Database/RomDatabase.swift [344]

    -NotificationCenter.default.post(Notification(name: .RomDatabaseInitialized))
    +NotificationCenter.default.post(name: .RomDatabaseInitialized, object: nil)
    • Apply this suggestion
    Suggestion importance[1-10]: 7

    __

    Why: The current notification posting syntax is incorrect. Using the recommended post(name:object:) method is more idiomatic and less error-prone than creating a Notification object directly.

    Medium
    • Update

    Copy link
    Member

    @JoeMatt JoeMatt left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    very nice!

    @JoeMatt
    Copy link
    Member

    JoeMatt commented Mar 25, 2025

    the builds worked but it error'd on the curl to discord, I'll update the tests to not fail on curl issues next time.

    @JoeMatt JoeMatt merged commit 40a139a into Provenance-Emu:develop Mar 25, 2025
    2 of 7 checks passed
    @github-actions
    Copy link

    @pabloarista pabloarista deleted the bug/ios16-md5-fails-bios-watcher-crash branch March 26, 2025 01:57
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants