Fixed bug 4169 - Crash due to audio session observer race condition
authorSam Lantinga <slouken@libsdl.org>
Thu, 24 May 2018 07:30:24 -0700
changeset 1200184c2582329b0
parent 12000 bb2e9f36b7ff
child 12002 9116d02d3feb
Fixed bug 4169 - Crash due to audio session observer race condition

Jona

The following explains why this bug was happening:
This crash was caused because the audio session was being set as active [session setActive:YES error:&err] when the audio device was actually being CLOSED. Certain cases the audio session being set to active would fail and the method would return right away. Because of the way the error was handled we never removed the SDLInterruptionListener thus leaking it. Later when an interruption was received the THIS_ object would contain a pointer to an already released device causing the crash.

The fix:
When only one device remained open and it was being closed we needed to set the audio session as NOT active and completely ignore the returned error to successfully release the SDLInterruptionListener. I think the user assumed that the open_playback_devices and open_capture_devices would equal 0 when all of them where closed but the truth is that at the end of the closing process that the open devices count is decremented.
src/audio/coreaudio/SDL_coreaudio.m
     1.1 --- a/src/audio/coreaudio/SDL_coreaudio.m	Wed May 23 17:15:37 2018 -0700
     1.2 +++ b/src/audio/coreaudio/SDL_coreaudio.m	Thu May 24 07:30:24 2018 -0700
     1.3 @@ -355,7 +355,7 @@
     1.4              return NO;
     1.5          }
     1.6  
     1.7 -        if (open_playback_devices + open_capture_devices == 1) {
     1.8 +        if (open && (open_playback_devices + open_capture_devices) == 1) {
     1.9              if (![session setActive:YES error:&err]) {
    1.10                  NSString *desc = err.description;
    1.11                  SDL_SetError("Could not activate Audio Session: %s", desc.UTF8String);
    1.12 @@ -392,10 +392,10 @@
    1.13              if (this->hidden->interruption_listener != NULL) {
    1.14                  SDLInterruptionListener *listener = nil;
    1.15                  listener = (SDLInterruptionListener *) CFBridgingRelease(this->hidden->interruption_listener);
    1.16 +                [center removeObserver:listener];
    1.17                  @synchronized (listener) {
    1.18                      listener.device = NULL;
    1.19                  }
    1.20 -                [center removeObserver:listener];
    1.21              }
    1.22          }
    1.23      }