From f17d549093a9653285f81ff5a14f4a41d395fbce Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 14 Sep 2016 08:50:33 -0700 Subject: [PATCH] pa_linux_alsa: fix hang in callback caused by abort The author of this patch is Alan Horstmann. Fixes ticket #251 --- src/hostapi/alsa/pa_linux_alsa.c | 35 ++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/hostapi/alsa/pa_linux_alsa.c b/src/hostapi/alsa/pa_linux_alsa.c index f286591..584cde8 100644 --- a/src/hostapi/alsa/pa_linux_alsa.c +++ b/src/hostapi/alsa/pa_linux_alsa.c @@ -3805,8 +3805,23 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr totalFds += self->playback.nfds; } +#ifdef PTHREAD_CANCELED + if( self->callbackMode ) + { + /* To allow 'Abort' to terminate the callback thread, enable cancelability just for poll() (& disable after) */ + pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL ); + } +#endif + pollResults = poll( self->pfds, totalFds, pollTimeout ); +#ifdef PTHREAD_CANCELED + if( self->callbackMode ) + { + pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, NULL ); + } +#endif + if( pollResults < 0 ) { /* XXX: Depend on preprocessor condition? */ @@ -4175,12 +4190,18 @@ static void *CallbackThreadFunc( void *userData ) int streamStarted = 0; assert( stream ); + /* Not implemented */ + assert( !stream->primeBuffers ); /* Execute OnExit when exiting */ pthread_cleanup_push( &OnExit, stream ); - - /* Not implemented */ - assert( !stream->primeBuffers ); +#ifdef PTHREAD_CANCELED + /* 'Abort' will use thread cancellation to terminate the callback thread, but the Alsa-lib functions + * are NOT cancel-safe, (and can end up in an inconsistent state). So, disable cancelability for + * the thread here, and just re-enable it for the poll() in PaAlsaStream_WaitForFrames(). */ + pthread_testcancel(); + pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, NULL ); +#endif /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the * stream is started immediately. The latter involves signaling the waiting main thread. @@ -4265,10 +4286,6 @@ static void *CallbackThreadFunc( void *userData ) { xrun = 0; -#ifdef PTHREAD_CANCELED - pthread_testcancel(); -#endif - /** @concern Xruns Under/overflows are to be reported to the callback */ if( stream->underrun > 0.0 ) { @@ -4299,11 +4316,12 @@ static void *CallbackThreadFunc( void *userData ) #if 0 CallbackUpdate( &stream->threading ); #endif + CalculateTimeInfo( stream, &timeInfo ); PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags ); cbFlags = 0; - /* CPU load measurement should include processing activivity external to the stream callback */ + /* CPU load measurement should include processing activity external to the stream callback */ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); framesGot = framesAvail; @@ -4334,7 +4352,6 @@ static void *CallbackThreadFunc( void *userData ) { /* Go back to polling for more frames */ break; - } if( paContinue != callbackResult ) -- 2.43.0