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
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
}));