iosxamarin.iosmonocore-audioaudiounit

Mono / Xamarin.iOS SIGTRAP gc.safepoint_poll crash


I have a Xamarin.iOS application in production that uses a lot of MIDI with soundfonts. I'm using appcenter.ms to capture crashes and log events. One crash keeps happening sporadically (and pretty rarely) to my users that I'm not able to recreate and troubleshoot. According to the crash reports it is happening across many different devices and iOS versions. It seems random. I'm also using Sentry.io with breadcrumbs but nothing ever gets logged there with the same date/time stamp that I can match up with this crash. The garbage collector seems to be involved and it also seems to be related to loading soundfonts (i.e. GlobalState::LoadInstrumentFromDLSCollection from the stack trace).

Does anyone have any suggestions on what might cause this or how to track it down? Is there some adverse interaction occurring between the garbage collector and the underlying low level c++ operation? Is there a way to ensure the garbage collector doesn't try to run in the middle of loading a soundfont?

Here's my code for loading soundfonts followed by the complete stack trace from appcenter (by the way, the try/catch doesn't actually catch any exceptions when this happens but I'm including it anyway). Of course I could be wrong about this code being the culprit but that's the only that loads soundfonts.

I've googled each of the iOS related c++ calls (e.g. SampleManager::InsertSample(unsigned long, Sample*)) but can't find anyone else reporting similar issues.

public bool LoadInstrument(string soundFontPath)
        {
           
            if (_currentInstrumentPath != soundFontPath)
                _currentInstrumentPath = soundFontPath;
            try
            {
                var soundFontUrl = CFUrl.FromFile(soundFontPath);
                if (_samplerUnit == null)
                    return false;
                if (!_samplerUnit.IsPlaying)
                    _samplerUnit.LoadInstrument(new SamplerInstrumentData(soundFontUrl, InstrumentType.SF2Preset)
                    {
                        BankLSB = SamplerInstrumentData.DefaultBankLSB,
                        BankMSB = SamplerInstrumentData.DefaultMelodicBankMSB,
                        PresetID = (byte)0
                    });
            }
            catch (Exception ex)
            {
                App.DiagnosticsLogger.WriteLog(ex);
                _eventAggregator.PublishOnUIThreadAsync(new TUE.Messages.ExceptionThrownMessage
                {
                    SourceClass = "MidiSequencePlayer",
                    Exception = ex,
                    DisplayMessage = "Failed to load the soundfont, Path: " + soundFontPath
                });
                var fileName = System.IO.Path.GetFileName(soundFontPath);
                var properties = new Dictionary<string, string>();
                properties.Add("ExceptionMessage", ex.Message);
                properties.Add("MethodName", "LoadInstrument");
                properties.Add("FileName", fileName);
                properties.Add("Path", soundFontPath);
                App.AnalyticsLogger.LogEvent("MSQ.LoadInstrument", "MIDI", properties, Sentry.Protocol.BreadcrumbLevel.Error);
                return false;
            }
            return true;
        }

StackTrace:

libEmbeddedSystemAUs.dylib
    SampleManager::InsertSample(unsigned long, Sample*)
    libEmbeddedSystemAUs.dylib
    InstrumentManager::AddSample(DlsWave*, __CFURL const*, bool, unsigned int)
    
libEmbeddedSystemAUs.dylib
    GlobalState::LoadInstrumentFromDLSCollection(InstrumentState*, __CFURL const*, unsigned int, unsigned int, bool)
    
libEmbeddedSystemAUs.dylib
    Sampler::SetProperty(unsigned int, unsigned int, unsigned int, void const*, unsigned int)
libEmbeddedSystemAUs.dylib
    AUBase::DispatchSetProperty(unsigned int, unsigned int, unsigned int, void const*, unsigned int)

libEmbeddedSystemAUs.dylib
    AUMethodSetProperty(void*, unsigned int, unsigned int, unsigned int, void const*, unsigned int)
gc.safepoint_poll
     
gc.safepoint_poll
    
gc.safepoint_poll
    
gc.safepoint_poll
    
gc.safepoint_poll
gc.safepoint_poll
     
gc.safepoint_poll
    
gc.safepoint_poll
    
gc.safepoint_poll
    
gc.safepoint_poll
    
gc.safepoint_poll
    
gc.safepoint_poll
gc.safepoint_poll
     
gc.safepoint_poll
gc.safepoint_poll
     gc.safepoint_poll
plcrash::MS::async::dwarf_cfa_state_iterator<unsigned long long, long long>::next(unsigned int*,
plcrash::MS::async::plcrash_dwarf_cfa_reg_rule_t*, unsigned long long*)
TUE.iOS plcrash::MS::async::dwarf_cfa_state_iterator<unsigned long long, long long>::next(unsigned int*,
plcrash::MS::async::plcrash_dwarf_cfa_reg_rule_t*, unsigned long long*)
Foundation
    __NSThreadPerformPerform
C
CoreFoundation
    __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
CoreFoundation
    __CFRunLoopDoSource0
CoreFoundation
    __CFRunLoopDoSources0
 
CoreFoundation
    __CFRunLoopRun
 
CoreFoundation

CFRunLoopRunSpecific

GraphicsServices
    GSEventRunModal
 
UIKitCore
    -[UIApplication _run]
 
UIKitCore
    UIApplicationMain

TUE.iOS
    gc.safepoint_poll
 
TUE.iOS
    gc.safepoint_poll
 
TUE.iOS
    gc.safepoint_poll
 
TUE.iOS
    gc.safepoint_poll
 
TUE.iOS
    gc.safepoint_poll
 
TUE.iOS
    gc.safepoint_poll
 
TUE.iOS
    gc.safepoint_poll
 
TUE.iOS

xamarin_release_block_on_main_thread

TUE.iOS

plcrash::MS::async::dwarf_cfa_state_iterator<unsigned long long, long long>::next(unsigned int*,
plcrash::MS::async::plcrash_dwarf_cfa_reg_rule_t*, unsigned long long*)
libdyld.dylib

start

Solution

  • I wrapped my call to LoadInstrument inside of MainThread.BeginInvokeOnMainThread. This seems to have completely resolved the issue as I've never gotten another report on this again:

    MainThread.BeginInvokeOnMainThread(() =>
                            _samplerUnit.LoadInstrument(new SamplerInstrumentData(soundFontUrl, InstrumentType.SF2Preset)
                            {
                                BankLSB = SamplerInstrumentData.DefaultBankLSB,
                                BankMSB = SamplerInstrumentData.DefaultMelodicBankMSB,
                                PresetID = (byte)0
                            }));