From a0bef529dd13367ccbbd17f272a209b29da5feab Mon Sep 17 00:00:00 2001
From: Lyubomir Marinov <lyubomir.marinov@jitsi.org>
Date: Wed, 19 Dec 2012 20:08:27 +0000
Subject: [PATCH] Attempts to handle timeouts in Pa_CloseStream more
 gracefully.

---
 .../protocol/portaudio/PortAudioStream.java   | 86 ++++++++++++++-----
 1 file changed, 65 insertions(+), 21 deletions(-)

diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java
index 0871f2e5..755cfbe9 100644
--- a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java
+++ b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java
@@ -104,7 +104,6 @@ public void didPaUpdateAvailableDeviceList()
                                 if (deviceIndex != Pa.paNoDevice)
                                 {
                                     setDeviceIndex(deviceIndex);
-
                                     if (start)
                                         start();
                                 }
@@ -481,36 +480,81 @@ synchronized void setDeviceIndex(int deviceIndex)
 
             if (stream != 0)
             {
+                /*
+                 * For the sake of completeness, attempt to stop this instance
+                 * before disconnecting it.
+                 */
+                if (started)
+                {
+                    try
+                    {
+                        stop();
+                    }
+                    catch (IOException ioe)
+                    {
+                        /*
+                         * The exception should have already been logged by the
+                         * method #stop(). Additionally and as said above, we
+                         * attempted it out of courtesy.
+                         */
+                    }
+                }
+
+                boolean closed = false;
+
                 try
                 {
                     Pa.CloseStream(stream);
+                    closed = true;
                 }
-                catch (PortAudioException paex)
+                catch (PortAudioException pae)
                 {
-                    logger.error(
-                            "Failed to close " + getClass().getSimpleName(),
-                            paex);
+                    /*
+                     * The function Pa_CloseStream is not supposed to time out
+                     * under normal execution. However, we have modified it to
+                     * do so under exceptional circumstances on Windows at least
+                     * in order to overcome endless loops related to
+                     * hotplugging. In such a case, presume the native PortAudio
+                     * stream closed in order to maybe avoid a crash at the risk
+                     * of a memory leak.
+                     */
+                    if (pae.getErrorCode() == Pa.paTimedOut)
+                        closed = true;
+
+                    if (!closed)
+                    {
+                        logger.error(
+                                "Failed to close " + getClass().getSimpleName(),
+                                pae);
 
-                    IOException ioex
-                        = new IOException(paex.getLocalizedMessage());
+                        IOException ioe
+                            = new IOException(pae.getLocalizedMessage());
 
-                    ioex.initCause(paex);
-                    throw ioex;
+                        ioe.initCause(pae);
+                        throw ioe;
+                    }
                 }
-                stream = 0;
-                if (inputParameters != 0)
+                finally
                 {
-                    Pa.StreamParameters_free(inputParameters);
-                    inputParameters = 0;
-                }
+                    if (closed)
+                    {
+                        stream = 0;
 
-                /*
-                 * Make sure this AbstractPullBufferStream asks its DataSource
-                 * for the Format in which it is supposed to output audio data
-                 * next time it's opened instead of using its Format from a
-                 * previous open.
-                 */
-                this.format = null;
+                        if (inputParameters != 0)
+                        {
+                            Pa.StreamParameters_free(inputParameters);
+                            inputParameters = 0;
+                        }
+
+                        /*
+                         * Make sure this AbstractPullBufferStream asks its
+                         * DataSource for the Format in which it is supposed to
+                         * output audio data the next time it is opened instead
+                         * of using its Format from a previous open.
+                         */
+                        this.format = null;
+                    }
+                }
             }
         }
         this.deviceIndex = deviceIndex;
-- 
GitLab