coreaudio: changed device close procedure to prevent long hangs in some cases.
authorRyan C. Gordon <icculus@icculus.org>
Fri, 13 Oct 2017 01:15:29 -0400
changeset 116209658d3a4b03a
parent 11619 c40333204dec
child 11621 8c8e18a2c390
coreaudio: changed device close procedure to prevent long hangs in some cases.

The audioqueue thread needs to keep running, and processing the CFRunLoop
until the AudioQueue is disposed of, otherwise CoreAudio will hang waiting for
final data to feed the device.

At least, I think this is how it all works. It definitely fixes the bug here!

Since AudioQueueDispose() calls AudioQueueStop() internally, there's no need
for our thread to handle this, either, which is good because the AudioQueue
would be disposed by this point. So now the AudioQueue is disposed first, and
then our thread is joined, and everything works out okay.

Just in case, we mark the device "paused" before setting everything in motion,
so any further callbacks from CoreAudio will write silence and not fire the
app's audio callback again.

Fixes Bugzilla #3868.
src/audio/coreaudio/SDL_coreaudio.m
     1.1 --- a/src/audio/coreaudio/SDL_coreaudio.m	Thu Oct 12 17:21:57 2017 -0700
     1.2 +++ b/src/audio/coreaudio/SDL_coreaudio.m	Fri Oct 13 01:15:29 2017 -0400
     1.3 @@ -541,15 +541,18 @@
     1.4      update_audio_session(this, SDL_FALSE);
     1.5  #endif
     1.6  
     1.7 +    /* if callback fires again, feed silence; don't call into the app. */
     1.8 +    SDL_AtomicSet(&this->paused, 1);
     1.9 +
    1.10 +    if (this->hidden->audioQueue) {
    1.11 +        AudioQueueDispose(this->hidden->audioQueue, 1);
    1.12 +    }
    1.13 +
    1.14      if (this->hidden->thread) {
    1.15          SDL_AtomicSet(&this->hidden->shutdown, 1);
    1.16          SDL_WaitThread(this->hidden->thread, NULL);
    1.17      }
    1.18  
    1.19 -    if (this->hidden->audioQueue) {
    1.20 -        AudioQueueDispose(this->hidden->audioQueue, 1);
    1.21 -    }
    1.22 -
    1.23      if (this->hidden->ready_semaphore) {
    1.24          SDL_DestroySemaphore(this->hidden->ready_semaphore);
    1.25      }
    1.26 @@ -731,12 +734,9 @@
    1.27          CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1);
    1.28      }
    1.29  
    1.30 -    if (this->iscapture) {  /* just stop immediately for capture devices. */
    1.31 -        AudioQueueStop(this->hidden->audioQueue, 1);
    1.32 -    } else {  /* Drain off any pending playback. */
    1.33 +    if (!this->iscapture) {  /* Drain off any pending playback. */
    1.34          const CFTimeInterval secs = (((this->spec.size / (SDL_AUDIO_BITSIZE(this->spec.format) / 8)) / this->spec.channels) / ((CFTimeInterval) this->spec.freq)) * 2.0;
    1.35          CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0);
    1.36 -        AudioQueueStop(this->hidden->audioQueue, 0);
    1.37      }
    1.38  
    1.39      return 0;