From 404e69e5945d357d70bd2da684b7a3dbedd1579d Mon Sep 17 00:00:00 2001
From: Andrey Kleshchev <andreykproductengine@lindenlab.com>
Date: Thu, 28 Oct 2021 18:41:10 +0300
Subject: SL-15462 Convert waitForChannel() into state machine

---
 indra/newview/llappviewer.cpp  |   6 ++
 indra/newview/llvoicevivox.cpp | 232 +++++++++++++++++++++++++++++------------
 2 files changed, 171 insertions(+), 67 deletions(-)

(limited to 'indra/newview')

diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 3ec6d3f90e..f668dc754d 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1520,6 +1520,12 @@ bool LLAppViewer::doFrame()
 			{
 				pauseMainloopTimeout();
 				saveFinalSnapshot();
+
+                if (LLVoiceClient::instanceExists())
+                {
+                    LLVoiceClient::getInstance()->terminate();
+                }
+
 				disconnectViewer();
 				resumeMainloopTimeout();
 			}
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 4aa6f2c6b2..c7a544f8eb 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -400,6 +400,11 @@ void LLVivoxVoiceClient::init(LLPumpIO *pump)
 
 void LLVivoxVoiceClient::terminate()
 {
+    if (sShuttingDown)
+    {
+        return;
+    }
+
     // needs to be done manually here since we will not get another pass in 
     // coroutines... that mechanism is long since gone.
     if (mIsLoggedIn)
@@ -1126,6 +1131,11 @@ bool LLVivoxVoiceClient::provisionVoiceAccount()
         LLVoiceVivoxStats::getInstance()->provisionAttemptStart();
         result = httpAdapter->postAndSuspend(httpRequest, url, LLSD(), httpOpts);
 
+        if (sShuttingDown)
+        {
+            return false;
+        }
+
         LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
         LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
 
@@ -1133,14 +1143,12 @@ bool LLVivoxVoiceClient::provisionVoiceAccount()
         {
             F32 timeout = pow(PROVISION_RETRY_TIMEOUT, static_cast<float>(retryCount));
             LL_WARNS("Voice") << "Provision CAP 404.  Retrying in " << timeout << " seconds. Retries: " << (S32)retryCount << LL_ENDL;
+            llcoro::suspendUntilTimeout(timeout);
+
             if (sShuttingDown)
             {
                 return false;
             }
-            else
-            {
-                llcoro::suspendUntilTimeout(timeout);
-            }
         }
         else if (!status)
         {
@@ -1515,6 +1523,11 @@ bool LLVivoxVoiceClient::requestParcelVoiceInfo()
 
     LLSD result = httpAdapter->postAndSuspend(httpRequest, url, LLSD());
 
+    if (sShuttingDown)
+    {
+        return false;
+    }
+
     LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
     LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
 
@@ -1620,6 +1633,11 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
 
     llcoro::suspend();
 
+    if (sShuttingDown)
+    {
+        return false;
+    }
+
     LLSD result;
 
     if (mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM)
@@ -1685,7 +1703,6 @@ bool LLVivoxVoiceClient::addAndJoinSession(const sessionStatePtr_t &nextSession)
 
         if (sShuttingDown)
         {
-            mIsJoiningSession = false;
             return false;
         }
 
@@ -1808,6 +1825,11 @@ bool LLVivoxVoiceClient::terminateAudioSession(bool wait)
 
                         result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, LOGOUT_ATTEMPT_TIMEOUT, timeoutResult);
 
+                        if (sShuttingDown)
+                        {
+                            return false;
+                        }
+
                         LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
                         if (result.has("session"))
                         {
@@ -1870,54 +1892,76 @@ bool LLVivoxVoiceClient::terminateAudioSession(bool wait)
     return status;
 }
 
+
+typedef enum e_voice_wait_for_channel_state
+{
+    VOICE_CHANNEL_STATE_LOGIN = 0, // entry point
+    VOICE_CHANNEL_STATE_CHECK_EFFECTS,
+    VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING,
+    VOICE_CHANNEL_STATE_PROCESS_CHANNEL,
+    VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY,
+    VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK,
+    VOICE_CHANNEL_STATE_LOGOUT,
+    VOICE_CHANNEL_STATE_RELOG,
+    VOICE_CHANNEL_STATE_DONE,
+} EVoiceWaitForChannelState;
+
 bool LLVivoxVoiceClient::waitForChannel()
 {
     LL_INFOS("Voice") << "Waiting for channel" << LL_ENDL;
 
+    EVoiceWaitForChannelState state = VOICE_CHANNEL_STATE_LOGIN;
+
     do 
     {
-        if (!loginToVivox())
-        {
-            return false;
-        }
-
         if (sShuttingDown)
         {
+            // terminate() forcefully disconects voice, no need for cleanup
             return false;
         }
 
-        if (LLVoiceClient::instance().getVoiceEffectEnabled())
+        switch (state)
         {
-            retrieveVoiceFonts();
-
-            if (sShuttingDown)
+        case VOICE_CHANNEL_STATE_LOGIN:
+            if (!loginToVivox())
             {
                 return false;
             }
+            state = VOICE_CHANNEL_STATE_CHECK_EFFECTS;
+            break;
 
-            // Request the set of available voice fonts.
-            refreshVoiceEffectLists(false);
-        }
+        case VOICE_CHANNEL_STATE_CHECK_EFFECTS:
+            if (LLVoiceClient::instance().getVoiceEffectEnabled())
+            {
+                retrieveVoiceFonts();
 
-#if USE_SESSION_GROUPS			
-        // Rider: This code is completely unchanged from the original state machine
-        // It does not seem to be in active use... but I'd rather not rip it out.
-        // create the main session group
-        setState(stateCreatingSessionGroup);
-        sessionGroupCreateSendMessage();
+                if (sShuttingDown)
+                {
+                    return false;
+                }
+
+                // Request the set of available voice fonts.
+                refreshVoiceEffectLists(false);
+            }
+
+#if USE_SESSION_GROUPS
+            // Rider: This code is completely unchanged from the original state machine
+            // It does not seem to be in active use... but I'd rather not rip it out.
+            // create the main session group
+            setState(stateCreatingSessionGroup);
+            sessionGroupCreateSendMessage();
 #endif
 
-        do
-        {
+            state = VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING;
+            break;
+
+        case VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING:
             mIsProcessingChannels = true;
             llcoro::suspend();
+            state = VOICE_CHANNEL_STATE_PROCESS_CHANNEL;
+            break;
 
-            if (sShuttingDown)
-            {
-                mRelogRequested = false;
-                break;
-            }
-
+        case VOICE_CHANNEL_STATE_PROCESS_CHANNEL:
             if (mTuningMode)
             {
                 performMicTuning();
@@ -1958,63 +2002,91 @@ bool LLVivoxVoiceClient::waitForChannel()
                 }
             }
 
-            if (!mNextAudioSession && !sShuttingDown)
+            state = VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY;
+            break;
+
+        case VOICE_CHANNEL_STATE_NEXT_CHANNEL_DELAY:
+            if (!mNextAudioSession)
             {
                 llcoro::suspendUntilTimeout(1.0);
             }
+            state = VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK;
+            break;
 
-            if (sShuttingDown)
+        case VOICE_CHANNEL_STATE_NEXT_CHANNEL_CHECK:
+            if (mVoiceEnabled && !mRelogRequested)
             {
-                mRelogRequested = false;
+                state = VOICE_CHANNEL_STATE_START_CHANNEL_PROCESSING;
+                break;
+            }
+            else
+            {
+                mIsProcessingChannels = false;
+                LL_DEBUGS("Voice")
+                    << "leaving inner waitForChannel loop"
+                    << " RelogRequested=" << mRelogRequested
+                    << " VoiceEnabled=" << mVoiceEnabled
+                    << LL_ENDL;
+                state = VOICE_CHANNEL_STATE_LOGOUT;
                 break;
             }
 
-        } while (!sShuttingDown && mVoiceEnabled && !mRelogRequested);
-
-        if (!sShuttingDown)
-        {
-            LL_DEBUGS("Voice")
-                << "leaving inner waitForChannel loop"
-                << " RelogRequested=" << mRelogRequested
-                << " VoiceEnabled=" << mVoiceEnabled
-                << LL_ENDL;
-        }
-        else
-        {
-            // if sShuttingDown is set, we already logged out
-            LL_DEBUGS("Voice") << "leaving inner waitForChannel loop." << LL_ENDL;
-            return false;
-        }
-
-        mIsProcessingChannels = false;
-
-        logoutOfVivox(!sShuttingDown /*bool wait*/);
+        case VOICE_CHANNEL_STATE_LOGOUT:
+            logoutOfVivox(true /*bool wait*/);
+            if (mRelogRequested)
+            {
+                state = VOICE_CHANNEL_STATE_RELOG;
+            }
+            else
+            {
+                state = VOICE_CHANNEL_STATE_DONE;
+            }
+            break;
 
-        if (mRelogRequested && !sShuttingDown)
-        {
+        case VOICE_CHANNEL_STATE_RELOG:
             LL_DEBUGS("Voice") << "Relog Requested, restarting provisioning" << LL_ENDL;
             if (!provisionVoiceAccount())
             {
+                if (sShuttingDown)
+                {
+                    return false;
+                }
                 LL_WARNS("Voice") << "provisioning voice failed; giving up" << LL_ENDL;
                 giveUp();
                 return false;
             }
+            if (mVoiceEnabled && mRelogRequested && isGatewayRunning())
+            {
+                state = VOICE_CHANNEL_STATE_LOGIN;
+            }
+            else
+            {
+                state = VOICE_CHANNEL_STATE_DONE;
+            }
+            break;
+        case VOICE_CHANNEL_STATE_DONE:
+            LL_DEBUGS("Voice")
+                << "exiting"
+                << " RelogRequested=" << mRelogRequested
+                << " VoiceEnabled=" << mVoiceEnabled
+                << LL_ENDL;
+            return !sShuttingDown;
         }
-    } while (!sShuttingDown && mVoiceEnabled && mRelogRequested && isGatewayRunning());
-
-    LL_DEBUGS("Voice")
-        << "exiting"
-        << " RelogRequested=" << mRelogRequested
-        << " VoiceEnabled=" << mVoiceEnabled
-        << LL_ENDL;
-    return !sShuttingDown;
+    } while (true);
 }
 
 bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
 {
     LL_INFOS("Voice") << "running new voice session " << session->mHandle << LL_ENDL;
 
-    if (!addAndJoinSession(session))
+    bool joined_session = addAndJoinSession(session);
+
+    if (sShuttingDown)
+    {
+        return false;
+    }
+
+    if (!joined_session)
     {
         notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
 
@@ -2038,9 +2110,19 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
     mIsInChannel = true;
     mMuteMicDirty = true;
 
-    while (mVoiceEnabled && isGatewayRunning() && !mSessionTerminateRequested && !mTuningMode)
+    while (!sShuttingDown
+           && mVoiceEnabled
+           && isGatewayRunning()
+           && !mSessionTerminateRequested
+           && !mTuningMode)
     {
         sendCaptureAndRenderDevices(); // suspends
+
+        if (sShuttingDown)
+        {
+            return false;
+        }
+
         if (mSessionTerminateRequested)
         {
             break;
@@ -2070,10 +2152,15 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
                 // cap for the parcel voice info.  If we can't request it
                 // then we don't have the cap URL so we do nothing and will
                 // recheck next time around
-                if (requestParcelVoiceInfo())
+                if (requestParcelVoiceInfo()) // suspends
                 {   // The parcel voice URI has changed.. break out and reconnect.
                     break;
                 }
+
+                if (sShuttingDown)
+                {
+                    return false;
+                }
             }
             // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
             enforceTether();
@@ -2092,6 +2179,12 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
 
         mIsInitialized = true;
         LLSD result = llcoro::suspendUntilEventOnWithTimeout(mVivoxPump, UPDATE_THROTTLE_SECONDS, timeoutEvent);
+
+        if (sShuttingDown)
+        {
+            return false;
+        }
+
         if (!result.has("timeout")) // logging the timeout event spams the log
         {
             LL_DEBUGS("Voice") << "event=" << ll_stream_notation_sd(result) << LL_ENDL;
@@ -2134,6 +2227,11 @@ bool LLVivoxVoiceClient::runSession(const sessionStatePtr_t &session)
         }
     }
 
+    if (sShuttingDown)
+    {
+        return false;
+    }
+
     mIsInChannel = false;
     LL_DEBUGS("Voice") << "terminating at end of runSession" << LL_ENDL;
     terminateAudioSession(true);
-- 
cgit v1.2.3