From fa156741340c201f2b2963cb98bbd9810e632938 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 13 Oct 2017 01:15:29 -0400 Subject: [PATCH] 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 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/audio/coreaudio/SDL_coreaudio.m b/src/audio/coreaudio/SDL_coreaudio.m index 33c239d736582..dc04dbe28f38a 100644 --- a/src/audio/coreaudio/SDL_coreaudio.m +++ b/src/audio/coreaudio/SDL_coreaudio.m @@ -541,15 +541,18 @@ static BOOL update_audio_session(_THIS, SDL_bool open) update_audio_session(this, SDL_FALSE); #endif - if (this->hidden->thread) { - SDL_AtomicSet(&this->hidden->shutdown, 1); - SDL_WaitThread(this->hidden->thread, NULL); - } + /* if callback fires again, feed silence; don't call into the app. */ + SDL_AtomicSet(&this->paused, 1); if (this->hidden->audioQueue) { AudioQueueDispose(this->hidden->audioQueue, 1); } + if (this->hidden->thread) { + SDL_AtomicSet(&this->hidden->shutdown, 1); + SDL_WaitThread(this->hidden->thread, NULL); + } + if (this->hidden->ready_semaphore) { SDL_DestroySemaphore(this->hidden->ready_semaphore); } @@ -731,12 +734,9 @@ static BOOL update_audio_session(_THIS, SDL_bool open) CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1); } - if (this->iscapture) { /* just stop immediately for capture devices. */ - AudioQueueStop(this->hidden->audioQueue, 1); - } else { /* Drain off any pending playback. */ + if (!this->iscapture) { /* Drain off any pending playback. */ const CFTimeInterval secs = (((this->spec.size / (SDL_AUDIO_BITSIZE(this->spec.format) / 8)) / this->spec.channels) / ((CFTimeInterval) this->spec.freq)) * 2.0; CFRunLoopRunInMode(kCFRunLoopDefaultMode, secs, 0); - AudioQueueStop(this->hidden->audioQueue, 0); } return 0;