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

Reply via email to