From 66bda13f84944b8d282c52141117e2234dca8ed6 Mon Sep 17 00:00:00 2001 From: Ross Bencina Date: Sat, 3 Sep 2016 22:53:30 +1000 Subject: [PATCH] Ports Portaudio WMME device timeout system to MacOsX CoreAudio in order to avoid deadlock when a device is unplugged. Vincent Lucas committed on Jan 10, 2013 2a20693 from https://github.com/jitsi/libsrc/commits/master/portaudio.zip --- src/hostapi/coreaudio/pa_mac_core.c | 25 +++++++++-- src/hostapi/coreaudio/pa_mac_core_blocking.c | 45 ++++++++++++++++++++ src/hostapi/coreaudio/pa_mac_core_blocking.h | 3 ++ 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/hostapi/coreaudio/pa_mac_core.c b/src/hostapi/coreaudio/pa_mac_core.c index 25cb2c5..08a38aa 100644 --- a/src/hostapi/coreaudio/pa_mac_core.c +++ b/src/hostapi/coreaudio/pa_mac_core.c @@ -2460,15 +2460,32 @@ static PaError StartStream( PaStream *s ) // it's not clear from appl's docs that this really waits // until all data is flushed. -static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioUnitElement element ) +static ComponentResult BlockWhileAudioUnitIsRunning( + AudioUnit audioUnit, + AudioUnitElement element) { + long waitTime = 100; + // If PaUtil_GetRingBufferWriteAvailable starts repetitively and + // consecutively returning that there is no data available, do eventually + // give up in order to allow the caller to handle such cases. + long totalTimeout = 0; Boolean isRunning = 1; while( isRunning ) { UInt32 s = sizeof( isRunning ); ComponentResult err = AudioUnitGetProperty( audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, element, &isRunning, &s ); if( err ) return err; - Pa_Sleep( 100 ); + Pa_Sleep( waitTime ); + + // If a timeout is encountered, continue. However, testing has + // shown that it is possible to unplug a device and to wait here + // forever. In order to allow the caller to handle such cases of + // repeated timeouts, do eventually given up. + totalTimeout += waitTime; + if( PA_COREAUDIO_MIN_TIMEOUT_MSEC_ <= totalTimeout) + { + return paTimedOut; + } } return noErr; } @@ -2481,7 +2498,7 @@ static PaError StopStream( PaStream *s ) VVDBUG(("StopStream()\n")); VDBUG( ("Waiting for BLIO.\n") ); - waitUntilBlioWriteBufferIsFlushed( &stream->blio ); + waitUntilBlioWriteBufferIsFlushed( &stream->blio); VDBUG( ( "Stopping stream.\n" ) ); stream->state = STOPPING; @@ -2507,7 +2524,7 @@ static PaError StopStream( PaStream *s ) if( stream->outputUnit ) { ERR_WRAP(AudioOutputUnitStop(stream->outputUnit)); - ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->outputUnit,0) ); + ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->outputUnit,0)); ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0)); } } diff --git a/src/hostapi/coreaudio/pa_mac_core_blocking.c b/src/hostapi/coreaudio/pa_mac_core_blocking.c index 5a98826..c51fd52 100644 --- a/src/hostapi/coreaudio/pa_mac_core_blocking.c +++ b/src/hostapi/coreaudio/pa_mac_core_blocking.c @@ -415,6 +415,10 @@ PaError ReadStream( PaStream* stream, while( frames > 0 ) { long avail; long toRead; + // If PaUtil_GetRingBufferReadAvailable starts repetitively and + // consecutively returning that there is no data available, do eventually + // give up in order to allow the caller to handle such cases. + long totalTimeout = 0; do { avail = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ); /* @@ -438,6 +442,16 @@ PaError ReadStream( PaStream* stream, return ret; #else Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); + + // If a timeout is encountered, continue. However, testing has + // shown that it is possible to unplug a device and to wait here + // forever. In order to allow the caller to handle such cases of + // repeated timeouts, do eventually given up. + totalTimeout += PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL; + if( PA_COREAUDIO_MAX_TIMEOUT_MSEC_ <= totalTimeout) + { + return paTimedOut; + } #endif } } while( avail == 0 ); @@ -491,6 +505,10 @@ PaError WriteStream( PaStream* stream, while( frames > 0 ) { long avail = 0; long toWrite; + // If PaUtil_GetRingBufferWriteAvailable starts repetitively and + // consecutively returning that there is no data available, do eventually + // give up in order to allow the caller to handle such cases. + long totalTimeout = 0; do { avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); @@ -515,6 +533,16 @@ PaError WriteStream( PaStream* stream, return ret; #else Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); + + // If a timeout is encountered, continue. However, testing has + // shown that it is possible to unplug a device and to wait here + // forever. In order to allow the caller to handle such cases of + // repeated timeouts, do eventually given up. + totalTimeout += PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL; + if( PA_COREAUDIO_MAX_TIMEOUT_MSEC_ <= totalTimeout) + { + return paTimedOut; + } #endif } } while( avail == 0 ); @@ -561,13 +589,30 @@ PaError WriteStream( PaStream* stream, void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio ) { if( blio->outputRingBuffer.buffer ) { + // If PaUtil_GetRingBufferWriteAvailable starts repetitively and + // consecutively returning that there is no data available, do eventually + // give up in order to allow the caller to handle such cases. + long totalTimeout = 0; long avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); while( avail != blio->outputRingBuffer.bufferSize ) { if( avail == 0 ) + { Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); + + // If a timeout is encountered, continue. However, testing has + // shown that it is possible to unplug a device and to wait here + // forever. In order to allow the caller to handle such cases of + // repeated timeouts, do eventually given up. + totalTimeout += PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL; + if( PA_COREAUDIO_MIN_TIMEOUT_MSEC_ <= totalTimeout) + { + return paTimedOut; + } + } avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); } } + return paNoError; } diff --git a/src/hostapi/coreaudio/pa_mac_core_blocking.h b/src/hostapi/coreaudio/pa_mac_core_blocking.h index 971223b..690a5e2 100644 --- a/src/hostapi/coreaudio/pa_mac_core_blocking.h +++ b/src/hostapi/coreaudio/pa_mac_core_blocking.h @@ -76,6 +76,9 @@ #define PA_MAC_BLIO_MUTEX */ +#define PA_COREAUDIO_MIN_TIMEOUT_MSEC_ (1000) +#define PA_COREAUDIO_MAX_TIMEOUT_MSEC_ (2000) + typedef struct { PaUtilRingBuffer inputRingBuffer; PaUtilRingBuffer outputRingBuffer; -- 2.43.0