From 7e9c5dd0a3e583a9bec4e99d99b2efca358c6612 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Thu, 12 Dec 2019 17:17:06 -0500
Subject: [PATCH] DRTVWR-494: LLParamSingleton::initParamSingleton() on main
 thread.

When calling LLParamSingleton::initParamSingleton() on a secondary thread, use
LLMainThreadTask::dispatch() to construct the instance on the main thread --
as with LLSingleton::getInstance().
---
 indra/llcommon/llsingleton.h | 47 +++++++++++++++++++++++++++++++++---
 1 file changed, 43 insertions(+), 4 deletions(-)

diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index 39d0e9b0135..314ee2caa89 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -59,6 +59,7 @@ class LLSingletonBase: private boost::noncopyable
     typedef enum e_init_state
     {
         UNINITIALIZED = 0,          // must be default-initialized state
+        QUEUED,                     // construction queued, not yet executing
         CONSTRUCTING,               // within DERIVED_TYPE constructor
         INITIALIZING,               // within DERIVED_TYPE::initSingleton()
         INITIALIZED,                // normal case
@@ -552,6 +553,11 @@ class LLSingleton : public LLSingletonBase
                          " -- creating new instance");
                 // fall through
             case UNINITIALIZED:
+            case QUEUED:
+                // QUEUED means some secondary thread has already requested an
+                // instance, but for present purposes that's semantically
+                // identical to UNINITIALIZED: either way, we must ourselves
+                // request an instance.
                 break;
             }
 
@@ -564,10 +570,13 @@ class LLSingleton : public LLSingletonBase
                 capture_dependency(lk->mInstance);
                 return lk->mInstance;
             }
+
+            // Here we need to construct a new instance, but we're on a secondary
+            // thread.
+            lk->mInitState = QUEUED;
         } // unlock 'lk'
 
-        // Here we need to construct a new instance, but we're on a secondary
-        // thread. Per the comment block above, dispatch to the main thread.
+        // Per the comment block above, dispatch to the main thread.
         loginfos(classname<DERIVED_TYPE>().c_str(),
                  "::getInstance() dispatching to main thread");
         auto instance = LLMainThreadTask::dispatch(
@@ -588,7 +597,7 @@ class LLSingleton : public LLSingletonBase
         // suppresses dep tracking when dispatched to the main thread)
         capture_dependency(instance);
         loginfos(classname<DERIVED_TYPE>().c_str(),
-                 "::getInstance() returning on invoking thread");
+                 "::getInstance() returning on requesting thread");
         return instance;
     }
 
@@ -672,11 +681,40 @@ class LLParamSingleton : public LLSingleton<DERIVED_TYPE>
                            " twice!");
             return nullptr;
         }
-        else
+        else if (on_main_thread())
         {
+            // on the main thread, simply construct instance while holding lock
             super::constructSingleton(lk, std::forward<Args>(args)...);
             return lk->mInstance;
         }
+        else
+        {
+            // on secondary thread, dispatch to main thread --
+            // set state so we catch any other calls before the main thread
+            // picks up the task
+            lk->mInitState = super::QUEUED;
+            // very important to unlock here so main thread can actually process
+            lk.unlock();
+            super::loginfos(super::template classname<DERIVED_TYPE>().c_str(),
+                            "::initParamSingleton() dispatching to main thread");
+            // Normally it would be the height of folly to reference-bind
+            // 'args' into a lambda to be executed on some other thread! By
+            // the time that thread executed the lambda, the references would
+            // all be dangling, and Bad Things would result. But
+            // LLMainThreadTask::dispatch() promises to block until the passed
+            // task has completed. So in this case we know the references will
+            // remain valid until the lambda has run, so we dare to bind
+            // references.
+            auto instance = LLMainThreadTask::dispatch(
+                [&](){
+                    super::loginfos(super::template classname<DERIVED_TYPE>().c_str(),
+                                    "::initParamSingleton() on main thread");
+                    return initParamSingleton(std::forward<Args>(args)...);
+                });
+            super::loginfos(super::template classname<DERIVED_TYPE>().c_str(),
+                            "::initParamSingleton() returning on requesting thread");
+            return instance;
+        }
     }
 
     static DERIVED_TYPE* getInstance()
@@ -688,6 +726,7 @@ class LLParamSingleton : public LLSingleton<DERIVED_TYPE>
         switch (lk->mInitState)
         {
         case super::UNINITIALIZED:
+        case super::QUEUED:
             super::logerrs("Uninitialized param singleton ",
                            super::template classname<DERIVED_TYPE>().c_str());
             break;
-- 
GitLab