Skip to content

Commit

Permalink
coreaudio: changed device close procedure to prevent long hangs in so…
Browse files Browse the repository at this point in the history
…me 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.
  • Loading branch information
icculus committed Oct 13, 2017
1 parent 0506d4f commit fa15674
Showing 1 changed file with 8 additions and 8 deletions.
16 changes: 8 additions & 8 deletions src/audio/coreaudio/SDL_coreaudio.m
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit fa15674

Please sign in to comment.