strummachine / sm-audiokit

0 stars 0 forks source link

Deadlock in __psynch_mutexwait on calling player.stop() #5

Closed banjerluke closed 2 years ago

banjerluke commented 2 years ago

Important preface: I went back to using a pool of AudioPlayer objects that are all instantiated before the engine is started; that fixed random engine-related errors I was getting, and also eliminated the overhead of creating new AudioPlayer objects all the time. As a bonus, I can now easily reuse buffered players for repeated samples! In master, each Channel has its own pool of AudioPlayer/SamplePlayer objects; I am experimenting with using a shared pool of AudioPlayers (in the dev/unified-pool branch) but that has no bearing on this issue...

If you hit "Play Drum Loop" and wait awhile (10-30 seconds usually), eventually it will stop responding. If then I pause execution, it's always inside __psynch_mutexwait:

libsystem_kernel.dylib`__psynch_mutexwait:
    0x1bd7bea3c <+0>:  mov    x16, #0x12d
    0x1bd7bea40 <+4>:  svc    #0x80
->  0x1bd7bea44 <+8>:  b.lo   0x1bd7bea60               ; <+36>
    0x1bd7bea48 <+12>: stp    x29, x30, [sp, #-0x10]!
    0x1bd7bea4c <+16>: mov    x29, sp
    0x1bd7bea50 <+20>: bl     0x1bd7bc9f4               ; cerror_nocancel
    0x1bd7bea54 <+24>: mov    sp, x29
    0x1bd7bea58 <+28>: ldp    x29, x30, [sp], #0x10
    0x1bd7bea5c <+32>: ret    
    0x1bd7bea60 <+36>: ret    

This is originating from [AVAudioPlayerNode stop], which is being called by AudioPlayer.stop() – sometimes because it's called directly, sometimes as part of a call to .load() or .play(). It seems like there's a small chance of that function deadlocking anytime it's called... which is obviously far from ideal!

Happens in both iOS 15 and iOS 14.4 simulators as well as my real iPhone.

banjerluke commented 2 years ago

This is the only related thread I could find on StackOverflow:

https://stackoverflow.com/questions/59080708/calling-stop-on-avaudioplayernode-after-finished-playing-causes-crash

Only I'm already on the main thread (DispatchQueue.main.async)...

banjerluke commented 2 years ago

It appears that this was caused by this:

self.player.play(from: offset, to: nil, at: time, completionCallbackType: .dataRendered)
                                                                          ^^^^^^^^^^^^^

I changed completionCallbackType back to .dataPlayedBack and it doesn't deadlock anymore. There are definitely still problems but I'll open new issue(s) for them.