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: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]