Crash in AudioHardware.remove

#74 · closed · 4 comments

View on GitHub ↗

andykent

We see occasional crashes in AudioHardware, I don' have a reliable way to reproduce them but here is an example. ``` Thread 12 Crashed: 0 My_App Beta 0x20382e9f8 [inlined] range 1 My_App Beta 0x20382e9f8 [inlined] Array._checkSubscript 2 My_App Beta 0x20382e9f8 [inlined] Array.subscript.getter 3 My_App Beta 0x20382e9f8 [inlined] Array.subscript.read 4 My_App Beta 0x20382e9f8 [inlined] [A] 5 My_App Beta 0x20382e9f8 MutableCollection._halfStablePartition 6 My_App Beta 0x203832160 [inlined] MutableCollection._halfStablePartition 7 My_App Beta 0x203832160 [inlined] AudioHardware.remove 8 My_App Beta 0x203832160 propertyListener (AudioHardware.swift:181) 9 CoreAudio 0x31397a9f0 HALObject::PropertiesChanged 10 CoreAudio 0x3137ff1e4 HALSystem::PropertiesChanged 11 CoreAudio 0x3137fefc0 HALSystem::ObjectsPublishedAndDied 12 CoreAudio 0x313803b04 HALSystem::AudioObjectsPublishedAndDied 13 CoreAudio 0x3138cec58 HALC_ShellPlugIn::ProxyObject_PropertiesChanged 14 CoreAudio 0x3139261b8 HALC_ProxyNotifications::CallListener_f 15 libdispatch.dylib 0x30f1d03fc _dispatch_client_callout 16 libdispatch.dylib 0x30f1d7a84 _dispatch_lane_serial_drain 17 libdispatch.dylib 0x30f1d8628 _dispatch_lane_invoke 18 libdispatch.dylib 0x30f1d98e4 _dispatch_workloop_invoke 19 libdispatch.dylib 0x30f1e3240 _dispatch_workloop_worker_thread 20 libsystem_pthread.dylib 0x30f529070 _pthread_wqthread ``` It seems like AudioHardware.remove, and probably other functions in AudioHardware too, are being called concurrently and access to `allKnownDevices` is not thread safe. I tried to investigate `AudioObjectAddPropertyListener` further but it's not really clear to me from the very limited docs what guarantees it gives around notifying on different threads or concurrency. Given `DispatchQueue.main.async` is already used in the propertyListener I have to assume that the listener calls back on different threads. One easy path might be extend the DispatchQueue block to enclose all of the propertyListener thus serialising these calls at least. I don't think this would fix the root issue though, for that I think `allKnownDevices` probably needs to have a thread safe implementation or locks around all accesses. I'm happy to take a stab at a PR here but I'm not entirely sure what the right or preferred solution is?

Comments

rnine

Hey @andykent — thanks for your report! Yes, the `AudioObject` property listener is not called on the main thread and I can see how that can certainly become an issue when modifying the `allKnownDevices` array. So I have just taken a quick stab at this. Could you please check out the [audio-hardware-ts](/rnine/SimplyCoreAudio/tree/audio-hardware-ts) branch and see if it fixes the issue? — thanks!

andykent

💥 wow, that was quick! Thanks so much, seems OK for me locally but I'll roll it out to beta testers later today and report back later in the week if I spot any weirdness. Thanks again ❤️

rnine

@andykent No worries! By the way, I have just added a couple of further optimizations to the [audio-hardware-ts](/rnine/SimplyCoreAudio/tree/audio-hardware-ts) branch so the adding and removal of all devices is now performed in a single synchronized operation — it should help a bit with performance!

andykent

Just to report back. We've been running this code in beta with a couple of hundred users for the last 3 days and no crashes with it yet, seems stable so far.