From: Ross Bencina Date: Sun, 11 Sep 2016 16:12:01 +0000 (+1000) Subject: hotplug wmme: Fix hangs in ReadStream and WriteStream caused by continuous retry... X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=a1479a5c9deacb70ee3423d158d037e53d387214;p=portaudio hotplug wmme: Fix hangs in ReadStream and WriteStream caused by continuous retry with no timeout. The method limits both the number of wait attempts, and the total duration of all waits to 1.5 times the total duration of all buffers. This was reported by Jitsi as part of patch [lyubomir a45b993, Thank you!], although the fix here is different. --- diff --git a/src/hostapi/wmme/pa_win_wmme.c b/src/hostapi/wmme/pa_win_wmme.c index e4fe92c..d147d5c 100644 --- a/src/hostapi/wmme/pa_win_wmme.c +++ b/src/hostapi/wmme/pa_win_wmme.c @@ -4080,9 +4080,19 @@ static PaError ReadStream( PaStream* s, unsigned long framesProcessed; signed int hostInputBufferIndex; DWORD waitResult; - DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); unsigned int channel, i; + /* timeout variables. see StopStream for a discussion. */ + DWORD totalTimeoutMs = (DWORD)(stream->allBuffersDurationMs * 1.5); + /* poll every 1.5 buffer durations. */ + DWORD timeoutMs = (DWORD)((1.5 * stream->allBuffersDurationMs) / stream->output.bufferCount) + 1; + /* for a total of maxWaitCount iterations, duration: maxWaitCount*timeoutMs = 1.5 * stream->allBuffersDurationMs */ + unsigned int maxWaitCount = stream->output.bufferCount; + + unsigned int waitCount; + DWORD waitStartTime = 0; + DWORD elapsedMs; + if( PA_IS_INPUT_STREAM_(stream) ) { /* make a local copy of the user buffer pointer(s). this is necessary @@ -4102,9 +4112,13 @@ static PaError ReadStream( PaStream* s, ((void**)userBuffer)[i] = ((void**)buffer)[i]; } + waitCount = 0; + do{ if( CurrentInputBuffersAreDone( stream ) ) { + waitCount = 0; + if( NoBuffersAreQueued( &stream->input ) ) { /** @todo REVIEW: consider what to do if the input overflows. @@ -4149,8 +4163,13 @@ static PaError ReadStream( PaStream* s, framesRead += framesProcessed; }else{ + + if( waitCount == 0 ) + waitStartTime = GetTickCount(); + ++waitCount; + /* wait for MME to signal that a buffer is available */ - waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout ); + waitResult = WaitForSingleObject( stream->input.bufferEvent, timeoutMs ); if( waitResult == WAIT_FAILED ) { result = paUnanticipatedHostError; @@ -4158,10 +4177,25 @@ static PaError ReadStream( PaStream* s, } else if( waitResult == WAIT_TIMEOUT ) { - /* if a timeout is encountered, continue, - perhaps we should give up eventually - */ - } + /* + Repeated timeouts with no forward progress on done buffers + has been reported to happen after disconnecting a device (Jitsi). + + Limit the number and duration of waits, then time out. + */ + if( waitCount >= maxWaitCount ) + { + result = paTimedOut; + break; + } + + elapsedMs = GetTickCount() - waitStartTime; + if( elapsedMs >= totalTimeoutMs ) + { + result = paTimedOut; + break; + } + } } }while( framesRead < frames ); } @@ -4185,10 +4219,19 @@ static PaError WriteStream( PaStream* s, unsigned long framesProcessed; signed int hostOutputBufferIndex; DWORD waitResult; - DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5); unsigned int channel, i; - + /* timeout variables. see StopStream for a discussion. */ + DWORD totalTimeoutMs = (DWORD)(stream->allBuffersDurationMs * 1.5); + /* poll every 1.5 buffer durations. */ + DWORD timeoutMs = (DWORD)((1.5 * stream->allBuffersDurationMs) / stream->output.bufferCount) + 1; + /* for a total of maxWaitCount iterations, duration: maxWaitCount*timeoutMs = 1.5 * stream->allBuffersDurationMs */ + unsigned int maxWaitCount = stream->output.bufferCount; + + unsigned int waitCount; + DWORD waitStartTime = 0; + DWORD elapsedMs; + if( PA_IS_OUTPUT_STREAM_(stream) ) { /* make a local copy of the user buffer pointer(s). this is necessary @@ -4208,9 +4251,13 @@ static PaError WriteStream( PaStream* s, ((const void**)userBuffer)[i] = ((const void**)buffer)[i]; } + waitCount = 0; + do{ if( CurrentOutputBuffersAreDone( stream ) ) { + waitCount = 0; + if( NoBuffersAreQueued( &stream->output ) ) { /** @todo REVIEW: consider what to do if the output @@ -4257,8 +4304,12 @@ static PaError WriteStream( PaStream* s, } else { + if( waitCount == 0 ) + waitStartTime = GetTickCount(); + ++waitCount; + /* wait for MME to signal that a buffer is available */ - waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout ); + waitResult = WaitForSingleObject( stream->output.bufferEvent, timeoutMs ); if( waitResult == WAIT_FAILED ) { result = paUnanticipatedHostError; @@ -4266,9 +4317,24 @@ static PaError WriteStream( PaStream* s, } else if( waitResult == WAIT_TIMEOUT ) { - /* if a timeout is encountered, continue, - perhaps we should give up eventually - */ + /* + Repeated timeouts with no forward progress on done buffers + has been reported to happen after disconnecting a device (Jitsi). + + Limit the number and duration of waits, then time out. + */ + if( waitCount >= maxWaitCount ) + { + result = paTimedOut; + break; + } + + elapsedMs = GetTickCount() - waitStartTime; + if( elapsedMs >= totalTimeoutMs ) + { + result = paTimedOut; + break; + } } } }while( framesWritten < frames );