pa_mac_core_blocking: fix hang when running stream stopped
One thread was filling the buffer while another thread was waiting for it to be empty. Add thread safety warnings. Fix ticket #252
This commit is contained in:
parent
05da7a854d
commit
0bc7cf9465
5 changed files with 235 additions and 41 deletions
|
|
@ -2724,18 +2724,10 @@ static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioU
|
|||
return noErr;
|
||||
}
|
||||
|
||||
static PaError StopStream( PaStream *s )
|
||||
static PaError FinishStoppingStream( PaMacCoreStream *stream )
|
||||
{
|
||||
PaMacCoreStream *stream = (PaMacCoreStream*)s;
|
||||
OSStatus result = noErr;
|
||||
PaError paErr;
|
||||
VVDBUG(("StopStream()\n"));
|
||||
|
||||
VDBUG( ("Waiting for BLIO.\n") );
|
||||
waitUntilBlioWriteBufferIsFlushed( &stream->blio );
|
||||
VDBUG( ( "Stopping stream.\n" ) );
|
||||
|
||||
stream->state = STOPPING;
|
||||
|
||||
#define ERR_WRAP(mac_err) do { result = mac_err ; if ( result != noErr ) return ERR(result) ; } while(0)
|
||||
/* -- stop and reset -- */
|
||||
|
|
@ -2787,12 +2779,30 @@ static PaError StopStream( PaStream *s )
|
|||
#undef ERR_WRAP
|
||||
}
|
||||
|
||||
/* Block until buffer is empty then stop the stream. */
|
||||
static PaError StopStream( PaStream *s )
|
||||
{
|
||||
PaError paErr;
|
||||
PaMacCoreStream *stream = (PaMacCoreStream*)s;
|
||||
VVDBUG(("StopStream()\n"));
|
||||
|
||||
// Tell WriteStream to stop filling the buffer while we are waiting for it to drain.
|
||||
stream->state = STOPPING;
|
||||
|
||||
VDBUG( ("Waiting for BLIO.\n") );
|
||||
paErr = waitUntilBlioWriteBufferIsEmpty( &stream->blio, stream->sampleRate );
|
||||
VDBUG( ( "waitUntilBlioWriteBufferIsEmpty returned %d\n", paErr ) );
|
||||
|
||||
return FinishStoppingStream( stream );
|
||||
}
|
||||
|
||||
/* Immediately stop the stream. */
|
||||
static PaError AbortStream( PaStream *s )
|
||||
{
|
||||
VVDBUG(("AbortStream()->StopStream()\n"));
|
||||
VDBUG( ( "Aborting stream.\n" ) );
|
||||
/* We have nothing faster than StopStream. */
|
||||
return StopStream(s);
|
||||
PaMacCoreStream *stream = (PaMacCoreStream*)s;
|
||||
VDBUG( ( "AbortStream()\n" ) );
|
||||
stream->state = STOPPING;
|
||||
return FinishStoppingStream( stream );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ PaError ReadStream( PaStream* stream,
|
|||
#endif
|
||||
}
|
||||
} while( framesAvailable == 0 );
|
||||
framesToTransfer = MIN( framesAvailable, framesRequested );
|
||||
framesToTransfer = (ring_buffer_size_t) MIN( framesAvailable, framesRequested );
|
||||
framesTransferred = PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, framesToTransfer );
|
||||
cbuf += framesTransferred * blio->inputSampleSizeActual * blio->inChan;
|
||||
framesRequested -= framesTransferred;
|
||||
|
|
@ -499,12 +499,13 @@ PaError WriteStream( PaStream* stream,
|
|||
const void *buffer,
|
||||
unsigned long framesRequested )
|
||||
{
|
||||
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
|
||||
PaMacCoreStream *macStream = (PaMacCoreStream*)stream;
|
||||
PaMacBlio *blio = & macStream -> blio;
|
||||
char *cbuf = (char *) buffer;
|
||||
PaError ret = paNoError;
|
||||
VVDBUG(("WriteStream()\n"));
|
||||
|
||||
while( framesRequested > 0 ) {
|
||||
while( framesRequested > 0 && macStream->state != STOPPING ) {
|
||||
ring_buffer_size_t framesAvailable;
|
||||
ring_buffer_size_t framesToTransfer;
|
||||
ring_buffer_size_t framesTransferred;
|
||||
|
|
@ -534,7 +535,12 @@ PaError WriteStream( PaStream* stream,
|
|||
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
|
||||
#endif
|
||||
}
|
||||
} while( framesAvailable == 0 );
|
||||
} while( framesAvailable == 0 && macStream->state != STOPPING );
|
||||
|
||||
if( macStream->state == STOPPING )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
framesToTransfer = MIN( framesAvailable, framesRequested );
|
||||
framesTransferred = PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, framesToTransfer );
|
||||
|
|
@ -559,34 +565,46 @@ PaError WriteStream( PaStream* stream,
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Report either paNoError or paOutputUnderflowed. */
|
||||
/* may also want to report other errors, but this is non-standard. */
|
||||
/* FIXME should not clobber ret, use if(blio->statusFlags & paInputOverflow) */
|
||||
ret = blio->statusFlags & paOutputUnderflow;
|
||||
if ( macStream->state == STOPPING )
|
||||
{
|
||||
ret = paInternalError;
|
||||
}
|
||||
else if (ret == paNoError )
|
||||
{
|
||||
/* Test for underflow. */
|
||||
ret = blio->statusFlags & paOutputUnderflow;
|
||||
|
||||
/* report underflow only once: */
|
||||
if( ret ) {
|
||||
OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags );
|
||||
ret = paOutputUnderflowed;
|
||||
/* report underflow only once: */
|
||||
if( ret ) {
|
||||
OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags );
|
||||
ret = paOutputUnderflowed;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Wait until the data in the buffer has finished playing.
|
||||
*/
|
||||
void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio )
|
||||
PaError waitUntilBlioWriteBufferIsEmpty( PaMacBlio *blio, double sampleRate )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
if( blio->outputRingBuffer.buffer ) {
|
||||
/* FIXME loop until PaUtil_GetRingBufferReadAvailable==0 */
|
||||
ring_buffer_size_t framesAvailable = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
|
||||
while( framesAvailable != blio->outputRingBuffer.bufferSize ) {
|
||||
if( framesAvailable == 0 )
|
||||
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
|
||||
framesAvailable = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
|
||||
}
|
||||
int timeout = 5; // don't wait forever
|
||||
ring_buffer_size_t framesInBuffer = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
|
||||
while( framesInBuffer > 0 && timeout-- > 0 ) {
|
||||
long msecEstimated = 1 + (long)( 1000.0 * framesInBuffer / sampleRate);
|
||||
VDBUG(( "waitUntilBlioWriteBufferIsFlushed: framesInBuffer = %d, msecEstimated = %ld\n", framesInBuffer, msecEstimated ));
|
||||
Pa_Sleep( msecEstimated );
|
||||
framesInBuffer = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
|
||||
}
|
||||
if( framesInBuffer > 0 )
|
||||
{
|
||||
result = paTimedOut;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,6 @@ int BlioCallback(
|
|||
PaStreamCallbackFlags statusFlags,
|
||||
void *userData );
|
||||
|
||||
void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio );
|
||||
PaError waitUntilBlioWriteBufferIsEmpty( PaMacBlio *blio, double sampleRate );
|
||||
|
||||
#endif /*PA_MAC_CORE_BLOCKING_H_*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue