OpenAL on iOS should thread safe, but seems not

Hello everyone, I am a cocos2d-x game framework maintainer.


Cocos2d-x implement its own audio engine by packing OpenAL API on iOS and macOS. Recently a critical audio crash issue occurs on iOS platform, but not macOS platform, even though the audio logic is same between both platform. So I think the OpenAL on macOS is well implemented than iOS.


Situation


Streaming in OpenAL was used on cocos2d-x to play bigger audio file. The audio was started and stoped on main thread, and rotate albuffers in sub-thread. When more than one play instance frequently do audio start and stop action at same time, crash will occur. As I said before, only crash on iOS, not any crash on macOS.


Reproduce


  1. clone cocos2d-x, https://github.com/cocos2d/cocos2d-x
  2. open cocos2d-x/build/cocos2d_test.xcodeproj by following the cocos2d-x/README.md
  3. merge PR https://github.com/cocos2d/cocos2d-x/pull/18865 to add test case
  4. run cpp-tests project into iOS device, tap “8:Audio - NewAudioEngine” to run test case


Relate issues, https://github.com/cocos2d/cocos2d-x/issues/18597

Crash occur at alBufferData , details log, https://pastebin.com/HQmVuGdH


I have no ideas to fix this, so I ask help here, hope to get some help.

Replies

When stop both audio play instance, those are streaming, still existed un-processed albuffers. And start both audio play instance at same time, crash might occur. But wait un-processed albuffers play over, and call alSourceUnqueueBuffers when do stop action, carsh will not occur.


From the official OpenAL Guide https://www.openal.org/documentation/OpenAL_Programmers_Guide.pdf, not any requirements to call alSourceUnqueueBuffers when stop a streaming OpenAL instance, but iOS needed.


Add this code fixed this crash, so I think some specific OpenAL implement defects existed on iOS.


                ALint bufferProcessed = 0;
                alGetSourcei(_alSource, AL_BUFFERS_PROCESSED, &bufferProcessed);
                while (bufferProcessed < QUEUEBUFFER_NUM) {
                    std::this_thread::sleep_for(std::chrono::milliseconds(10));
                    alGetSourcei(_alSource, AL_BUFFERS_PROCESSED, &bufferProcessed);
                }
                alSourceUnqueueBuffers(_alSource, QUEUEBUFFER_NUM, _bufferIds); CHECK_AL_ERROR_DEBUG();

Any apple developer help to explain this? I have tried my best.