TheNotorius0 opened a new issue, #405: URL: https://github.com/apache/cordova-plugin-media/issues/405
# Bug Report I have a few crashes on iOS. This is the crash from Crashalytics: `[CDVSound audioFileForResource:withId:doValidation:forRecording:suppressValidationErrors:]` ## Problem I have a few different stack traces that leads to the same crash. For example: **Fatal exception: com.apple.root.default-qos EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x316e72403b08646b** ``` Crashed: com.apple.root.default-qos 0 libobjc.A.dylib 0x3008 objc_msgSend + 8 1 CoreFoundation 0x3918 -[__NSDictionaryM objectForKey:] + 168 2 Azmar 0x19d6c -[CDVSound audioFileForResource:withId:doValidation:forRecording:suppressValidationErrors:] + 224 (CDVSound.m:224) 3 Azmar 0x1a908 __30-[CDVSound startPlayingAudio:]_block_invoke + 416 (CDVSound.m:416) 4 libdispatch.dylib 0x1aac _dispatch_call_block_and_release + 32 5 libdispatch.dylib 0x1b584 _dispatch_client_callout + 16 6 libdispatch.dylib 0x3725c _dispatch_queue_override_invoke.cold.3 + 32 7 libdispatch.dylib 0x61f8 _dispatch_queue_override_invoke + 848 8 libdispatch.dylib 0x13db0 _dispatch_root_queue_drain + 364 9 libdispatch.dylib 0x1454c _dispatch_worker_thread2 + 156 10 libsystem_pthread.dylib 0x4624 _pthread_wqthread + 232 11 libsystem_pthread.dylib 0x19f8 start_wqthread + 8 ``` and also: **Fatal exception: com.apple.root.default-qos EXC_BREAKPOINT 0x0000000197cb6fa8** ``` Crashed: com.apple.root.default-qos 0 CoreFoundation 0x3fa8 -[__NSCFString isEqual:] + 248 1 CoreFoundation 0x3918 -[__NSDictionaryM objectForKey:] + 168 2 Azmar 0x19d6c -[CDVSound audioFileForResource:withId:doValidation:forRecording:suppressValidationErrors:] + 224 (CDVSound.m:224) 3 Azmar 0x1a908 __30-[CDVSound startPlayingAudio:]_block_invoke + 416 (CDVSound.m:416) 4 libdispatch.dylib 0x1aac _dispatch_call_block_and_release + 32 5 libdispatch.dylib 0x1b584 _dispatch_client_callout + 16 6 libdispatch.dylib 0x3725c _dispatch_queue_override_invoke.cold.3 + 32 7 libdispatch.dylib 0x61f8 _dispatch_queue_override_invoke + 848 8 libdispatch.dylib 0x13db0 _dispatch_root_queue_drain + 364 9 libdispatch.dylib 0x1454c _dispatch_worker_thread2 + 156 10 libsystem_pthread.dylib 0x4624 _pthread_wqthread + 232 11 libsystem_pthread.dylib 0x19f8 start_wqthread + 8 ``` ### What is expected to happen? The app shouldn't crash. ### What does actually happen? The app crashes. Maybe it's a race condition because I may load multiple sounds or stop multiple musics? Who knows. ## Information <!-- Include all relevant information that might help understand and reproduce the problem --> I may call many times the stop function for different musics, and also load quite a few musics and sounds, since my app is a game. I asked Gemini for a solution, and he mostly suggested me to add a `@synchronized(self) {` block in the `audioFileForResource`, which is the function that crashes, not sure how it's useful though: ``` - (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId doValidation:(BOOL)bValidate forRecording:(BOOL)bRecord suppressValidationErrors:(BOOL)bSuppress { BOOL bError = NO; CDVMediaError errcode = MEDIA_ERR_NONE_SUPPORTED; NSString* errMsg = @""; CDVAudioFile* audioFile = nil; NSURL* resourceURL = nil; // --- BEGIN SIMPLE WORKAROUND --- // 1. Basic check: Ensure mediaId is actually a string before using it. // This might not be the cause, but it's simple defensive coding. if (![mediaId isKindOfClass:[NSString class]]) { NSLog(@"CDVSound Error: Received non-string mediaId: %@", mediaId); bError = YES; errcode = MEDIA_ERR_ABORTED; errMsg = @"Internal error: Invalid media ID type"; // Go straight to error reporting goto HandleError; } // 2. Synchronize only the block of code that accesses the shared soundCache dictionary. @synchronized(self) { if ([self soundCache] == nil) { // Initialize the cache if it doesn't exist (thread-safe initialization) [self setSoundCache:[NSMutableDictionary dictionaryWithCapacity:1]]; } else { // Safely attempt to retrieve the object from the cache audioFile = [[self soundCache] objectForKey:mediaId]; // Line ~224 - Now wrapped } // If not found in cache, create and add it (still within the synchronized block) if (audioFile == nil) { // Basic validation of resourcePath (can stay inside for simplicity) if ((resourcePath == nil) || ![resourcePath isKindOfClass:[NSString class]] || [resourcePath isEqualToString:@""]) { bError = YES; // Mark error, handle after sync block errcode = MEDIA_ERR_ABORTED; errMsg = @"invalid media src argument"; } else { // Create and add the new audioFile object while synchronized audioFile = [[CDVAudioFile alloc] init]; audioFile.resourcePath = resourcePath; audioFile.resourceURL = nil; // Will be validated later [[self soundCache] setObject:audioFile forKey:mediaId]; } } } // --- END Synchronization Block --- // --- END SIMPLE WORKAROUND --- // If an error occurred during creation inside the sync block, handle it now if (bError) { goto HandleError; } // Proceed with URL validation if needed (outside the synchronized block) // Note: audioFile might be nil here if resourcePath validation failed inside the sync block. // However, the bError flag should catch that. We rely on audioFile being non-nil if bError is NO. if (bValidate && (audioFile.resourceURL == nil)) { if (bRecord) { resourceURL = [self urlForRecording:resourcePath]; } else { resourceURL = [self urlForPlaying:resourcePath]; } if ((resourceURL == nil) && !bSuppress) { bError = YES; errcode = MEDIA_ERR_ABORTED; errMsg = [NSString stringWithFormat:@"Cannot use audio file from resource '%@'", resourcePath]; } else { // It's generally safe to set properties on the audioFile object itself here, // as the object reference `audioFile` is local to this function execution. // The risk would be if another thread `release`d this specific mediaId // *between* the synchronized block above and this line. // The simplest approach assumes this is less likely than the dictionary corruption. audioFile.resourceURL = resourceURL; } } HandleError: // Common error handling point if (bError) { // Use the validated mediaId (or a placeholder if it was invalid) for the error callback NSString* errorMediaId = [mediaId isKindOfClass:[NSString class]] ? mediaId : @"<invalid_id>"; [self onStatus:MEDIA_ERROR mediaId:errorMediaId param: [self createMediaErrorWithCode:errcode message:errMsg]]; // Return nil upon error return nil; } // Return the found or created audioFile object return audioFile; } ``` ### Command or Code <!-- What command or code is needed to reproduce the problem? --> ### Environment, Platform, Device <!-- In what environment, on what platform or on which device are you experiencing the issue? --> Most iPhones ranging from iPhone 11 to iPhone 16, 20% iOS 17 and 80% iOS 18. ### Version information <!-- What are relevant versions you are using? For example: Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins Other Frameworks: Ionic Framework and CLI version Operating System, Android Studio, Xcode etc. --> ``` "cordova": "^12.0.0", "cordova-ios": "^7.1.1", ``` ## Checklist <!-- Please check the boxes by putting an x in the [ ] like so: [x] --> - [x] I searched for existing GitHub issues - [x] I updated all Cordova tooling to most recent version - [x] I included all the necessary information above -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: issues-unsubscr...@cordova.apache.org.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: issues-unsubscr...@cordova.apache.org For additional commands, e-mail: issues-h...@cordova.apache.org