From 95d8085fa42ca73773a06f2bd5622f69aef0e598 Mon Sep 17 00:00:00 2001
From: Nat Goodspeed <nat@lindenlab.com>
Date: Mon, 10 May 2021 15:21:51 -0400
Subject: [PATCH] SL-10297: Make LLSingletonBase::llerrs() et al. runtime
 variadic.

Instead of accepting a fixed list of (const char* p1="", etc.), accept
(std::initializer_list<std::string_view>). Accepting a
std::initializer_list<T> in your parameter list allows coding (e.g.)
func({T0, T1, T2, ... });
-- in other words, you can pass the initializer_list a brace-enclosed list of
an arbitrary number of instances of T.

Using std::string_view instead of const char* means we can pass *either* const
char* or std::string. string_view is cheaply constructed from either, allowing
uniform treatment within the function.

Constructing string_view from std::string simply extracts the pointer and
length from the std::string.

Constructing string_view from const char* (e.g. a "string literal") requires
scanning the string for its terminating nul byte -- but that would be
necessary even if the scan were deferred until the function body. Since
string_view stores the length, the scan still happens only once.
---
 indra/llcommon/llsingleton.cpp | 66 ++++++++++++++-------------
 indra/llcommon/llsingleton.h   | 81 ++++++++++++++++------------------
 2 files changed, 75 insertions(+), 72 deletions(-)

diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp
index ad933154c21..d0dcd463ff8 100644
--- a/indra/llcommon/llsingleton.cpp
+++ b/indra/llcommon/llsingleton.cpp
@@ -39,8 +39,7 @@
 #include <stdexcept>
 
 namespace {
-void log(LLError::ELevel level,
-         const char* p1, const char* p2, const char* p3, const char* p4);
+void log(LLError::ELevel level, std::initializer_list<std::string_view>);
 } // anonymous namespace
 
 // Our master list of all LLSingletons is itself an LLSingleton. We used to
@@ -218,8 +217,8 @@ void LLSingletonBase::pop_initializing()
 
     if (list.empty())
     {
-        logerrs("Underflow in stack of currently-initializing LLSingletons at ",
-                classname(this).c_str(), "::getInstance()");
+        logerrs({"Underflow in stack of currently-initializing LLSingletons at ",
+                classname(this), "::getInstance()"});
     }
 
     // Now we know list.back() exists: capture it
@@ -240,9 +239,9 @@ void LLSingletonBase::pop_initializing()
     // Now validate the newly-popped LLSingleton.
     if (back != this)
     {
-        logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ",
-                classname(this).c_str(), "::getInstance() trying to pop ",
-                classname(back).c_str());
+        logerrs({"Push/pop mismatch in stack of currently-initializing LLSingletons: ",
+                classname(this), "::getInstance() trying to pop ",
+                classname(back)});
     }
 
     // log AFTER popping so logging singletons don't cry circularity
@@ -331,15 +330,15 @@ void LLSingletonBase::capture_dependency()
                 //
                 // Example: LLNotifications singleton initializes default channels.
                 // Channels register themselves with singleton once done.
-                logdebugs("LLSingleton circularity: ", out.str().c_str(),
-                    classname(this).c_str(), "");
+                logdebugs({"LLSingleton circularity: ", out.str(),
+                          classname(this)});
             }
             else
             {
                 // Actual circularity with other singleton (or single singleton is used extensively).
                 // Dependency can be unclear.
-                logwarns("LLSingleton circularity: ", out.str().c_str(),
-                    classname(this).c_str(), "");
+                logwarns({"LLSingleton circularity: ", out.str(),
+                         classname(this)});
             }
         }
         else
@@ -352,8 +351,8 @@ void LLSingletonBase::capture_dependency()
             if (current->mDepends.insert(this).second)
             {
                 // only log the FIRST time we hit this dependency!
-                logdebugs(classname(current).c_str(),
-                          " depends on ", classname(this).c_str());
+                logdebugs({classname(current),
+                          " depends on ", classname(this)});
             }
         }
     }
@@ -401,7 +400,7 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort()
 
 void LLSingletonBase::cleanup_()
 {
-    logdebugs("calling ", classname(this).c_str(), "::cleanupSingleton()");
+    logdebugs({"calling ", classname(this), "::cleanupSingleton()"});
     try
     {
         cleanupSingleton();
@@ -427,23 +426,23 @@ void LLSingletonBase::deleteAll()
             if (! sp->mDeleteSingleton)
             {
                 // This Should Not Happen... but carry on.
-                logwarns(name.c_str(), "::mDeleteSingleton not initialized!");
+                logwarns({name, "::mDeleteSingleton not initialized!"});
             }
             else
             {
                 // properly initialized: call it.
-                logdebugs("calling ", name.c_str(), "::deleteSingleton()");
+                logdebugs({"calling ", name, "::deleteSingleton()"});
                 // From this point on, DO NOT DEREFERENCE sp!
                 sp->mDeleteSingleton();
             }
         }
         catch (const std::exception& e)
         {
-            logwarns("Exception in ", name.c_str(), "::deleteSingleton(): ", e.what());
+            logwarns({"Exception in ", name, "::deleteSingleton(): ", e.what()});
         }
         catch (...)
         {
-            logwarns("Unknown exception in ", name.c_str(), "::deleteSingleton()");
+            logwarns({"Unknown exception in ", name, "::deleteSingleton()"});
         }
     }
 }
@@ -451,40 +450,47 @@ void LLSingletonBase::deleteAll()
 /*---------------------------- Logging helpers -----------------------------*/
 namespace {
 
-void log(LLError::ELevel level,
-         const char* p1, const char* p2, const char* p3, const char* p4)
+void log(LLError::ELevel level, std::initializer_list<std::string_view> args)
 {
-    LL_VLOGS(level, "LLSingleton") << p1 << p2 << p3 << p4 << LL_ENDL;
+    LL_VLOGS(level, "LLSingleton");
+        for (auto arg : args)
+        {
+            LL_CONT << arg;
+        }
+    LL_ENDL;
 }
 
 } // anonymous namespace        
 
 //static
-void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, const char* p4)
+void LLSingletonBase::logwarns(std::initializer_list<std::string_view> args)
 {
-    log(LLError::LEVEL_WARN, p1, p2, p3, p4);
+    log(LLError::LEVEL_WARN, args);
 }
 
 //static
-void LLSingletonBase::loginfos(const char* p1, const char* p2, const char* p3, const char* p4)
+void LLSingletonBase::loginfos(std::initializer_list<std::string_view> args)
 {
-    log(LLError::LEVEL_INFO, p1, p2, p3, p4);
+    log(LLError::LEVEL_INFO, args);
 }
 
 //static
-void LLSingletonBase::logdebugs(const char* p1, const char* p2, const char* p3, const char* p4)
+void LLSingletonBase::logdebugs(std::initializer_list<std::string_view> args)
 {
-    log(LLError::LEVEL_DEBUG, p1, p2, p3, p4);
+    log(LLError::LEVEL_DEBUG, args);
 }
 
 //static
-void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4)
+void LLSingletonBase::logerrs(std::initializer_list<std::string_view> args)
 {
-    log(LLError::LEVEL_ERROR, p1, p2, p3, p4);
+    log(LLError::LEVEL_ERROR, args);
     // The other important side effect of LL_ERRS() is
     // https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG)
     std::ostringstream out;
-    out << p1 << p2 << p3 << p4;
+    for (auto arg : args)
+    {
+        out << arg;
+    }
     auto crash = LLError::getFatalFunction();
     if (crash)
     {
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index 30a5b21cf86..b9570d42db1 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -34,6 +34,7 @@
 #include "lockstatic.h"
 #include "llthread.h"               // on_main_thread()
 #include "llmainthreadtask.h"
+#include <initializer_list>
 
 class LLSingletonBase: private boost::noncopyable
 {
@@ -111,14 +112,10 @@ class LLSingletonBase: private boost::noncopyable
     void capture_dependency();
 
     // delegate logging calls to llsingleton.cpp
-    static void logerrs(const char* p1, const char* p2="",
-                        const char* p3="", const char* p4="");
-    static void logwarns(const char* p1, const char* p2="",
-                         const char* p3="", const char* p4="");
-    static void loginfos(const char* p1, const char* p2="",
-                         const char* p3="", const char* p4="");
-    static void logdebugs(const char* p1, const char* p2="",
-                          const char* p3="", const char* p4="");
+    static void logerrs  (std::initializer_list<std::string_view>);
+    static void logwarns (std::initializer_list<std::string_view>);
+    static void loginfos (std::initializer_list<std::string_view>);
+    static void logdebugs(std::initializer_list<std::string_view>);
     static std::string demangle(const char* mangled);
     // these classname() declarations restate template functions declared in
     // llerror.h because we avoid #including that here
@@ -327,8 +324,8 @@ class LLSingleton : public LLSingletonBase
             // init stack to its previous size BEFORE logging so log-machinery
             // LLSingletons don't record a dependency on DERIVED_TYPE!
             LLSingleton_manage_master<DERIVED_TYPE>().reset_initializing(prev_size);
-            logwarns("Error constructing ", classname<DERIVED_TYPE>().c_str(),
-                     ": ", err.what());
+            logwarns({"Error constructing ", classname<DERIVED_TYPE>(),
+                     ": ", err.what()});
             // There isn't a separate EInitState value meaning "we attempted
             // to construct this LLSingleton subclass but could not," so use
             // DELETED. That seems slightly more appropriate than UNINITIALIZED.
@@ -356,8 +353,8 @@ class LLSingleton : public LLSingletonBase
             // BEFORE logging, so log-machinery LLSingletons don't record a
             // dependency on DERIVED_TYPE!
             pop_initializing(lk->mInstance);
-            logwarns("Error in ", classname<DERIVED_TYPE>().c_str(),
-                     "::initSingleton(): ", err.what());
+            logwarns({"Error in ", classname<DERIVED_TYPE>(),
+                     "::initSingleton(): ", err.what()});
             // Get rid of the instance entirely. This call depends on our
             // recursive_mutex. We could have a deleteSingleton(LockStatic&)
             // overload and pass lk, but we don't strictly need it.
@@ -506,9 +503,9 @@ class LLSingleton : public LLSingletonBase
             case CONSTRUCTING:
                 // here if DERIVED_TYPE's constructor (directly or indirectly)
                 // calls DERIVED_TYPE::getInstance()
-                logerrs("Tried to access singleton ",
-                        classname<DERIVED_TYPE>().c_str(),
-                        " from singleton constructor!");
+                logerrs({"Tried to access singleton ",
+                        classname<DERIVED_TYPE>(),
+                        " from singleton constructor!"});
                 return nullptr;
 
             case INITIALIZING:
@@ -523,9 +520,9 @@ class LLSingleton : public LLSingletonBase
 
             case DELETED:
                 // called after deleteSingleton()
-                logwarns("Trying to access deleted singleton ",
-                         classname<DERIVED_TYPE>().c_str(),
-                         " -- creating new instance");
+                logwarns({"Trying to access deleted singleton ",
+                         classname<DERIVED_TYPE>(),
+                         " -- creating new instance"});
                 // fall through
             case UNINITIALIZED:
             case QUEUED:
@@ -552,8 +549,8 @@ class LLSingleton : public LLSingletonBase
         } // unlock 'lk'
 
         // Per the comment block above, dispatch to the main thread.
-        loginfos(classname<DERIVED_TYPE>().c_str(),
-                 "::getInstance() dispatching to main thread");
+        loginfos({classname<DERIVED_TYPE>(),
+                 "::getInstance() dispatching to main thread"});
         auto instance = LLMainThreadTask::dispatch(
             [](){
                 // VERY IMPORTANT to call getInstance() on the main thread,
@@ -563,16 +560,16 @@ class LLSingleton : public LLSingletonBase
                 // the main thread processes them, only the FIRST such request
                 // actually constructs the instance -- every subsequent one
                 // simply returns the existing instance.
-                loginfos(classname<DERIVED_TYPE>().c_str(),
-                         "::getInstance() on main thread");
+                loginfos({classname<DERIVED_TYPE>(),
+                         "::getInstance() on main thread"});
                 return getInstance();
             });
         // record the dependency chain tracked on THIS thread, not the main
         // thread (consider a getInstance() overload with a tag param that
         // suppresses dep tracking when dispatched to the main thread)
         capture_dependency(instance);
-        loginfos(classname<DERIVED_TYPE>().c_str(),
-                 "::getInstance() returning on requesting thread");
+        loginfos({classname<DERIVED_TYPE>(),
+                 "::getInstance() returning on requesting thread"});
         return instance;
     }
 
@@ -641,16 +638,16 @@ class LLParamSingleton : public LLSingleton<DERIVED_TYPE>
         // For organizational purposes this function shouldn't be called twice
         if (lk->mInitState != super::UNINITIALIZED)
         {
-            super::logerrs("Tried to initialize singleton ",
-                           super::template classname<DERIVED_TYPE>().c_str(),
-                           " twice!");
+            super::logerrs({"Tried to initialize singleton ",
+                           super::template classname<DERIVED_TYPE>(),
+                           " twice!"});
             return nullptr;
         }
         else if (on_main_thread())
         {
             // on the main thread, simply construct instance while holding lock
-            super::logdebugs(super::template classname<DERIVED_TYPE>().c_str(),
-                             "::initParamSingleton()");
+            super::logdebugs({super::template classname<DERIVED_TYPE>(),
+                             "::initParamSingleton()"});
             super::constructSingleton(lk, std::forward<Args>(args)...);
             return lk->mInstance;
         }
@@ -662,8 +659,8 @@ class LLParamSingleton : public LLSingleton<DERIVED_TYPE>
             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");
+            super::loginfos({super::template classname<DERIVED_TYPE>(),
+                            "::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
@@ -674,12 +671,12 @@ class LLParamSingleton : public LLSingleton<DERIVED_TYPE>
             // references.
             auto instance = LLMainThreadTask::dispatch(
                 [&](){
-                    super::loginfos(super::template classname<DERIVED_TYPE>().c_str(),
-                                    "::initParamSingleton() on main thread");
+                    super::loginfos({super::template classname<DERIVED_TYPE>(),
+                                    "::initParamSingleton() on main thread"});
                     return initParamSingleton_(std::forward<Args>(args)...);
                 });
-            super::loginfos(super::template classname<DERIVED_TYPE>().c_str(),
-                            "::initParamSingleton() returning on requesting thread");
+            super::loginfos({super::template classname<DERIVED_TYPE>(),
+                            "::initParamSingleton() returning on requesting thread"});
             return instance;
         }
     }
@@ -707,14 +704,14 @@ class LLParamSingleton : public LLSingleton<DERIVED_TYPE>
         {
         case super::UNINITIALIZED:
         case super::QUEUED:
-            super::logerrs("Uninitialized param singleton ",
-                           super::template classname<DERIVED_TYPE>().c_str());
+            super::logerrs({"Uninitialized param singleton ",
+                           super::template classname<DERIVED_TYPE>()});
             break;
 
         case super::CONSTRUCTING:
-            super::logerrs("Tried to access param singleton ",
-                           super::template classname<DERIVED_TYPE>().c_str(),
-                           " from singleton constructor!");
+            super::logerrs({"Tried to access param singleton ",
+                           super::template classname<DERIVED_TYPE>(),
+                           " from singleton constructor!"});
             break;
 
         case super::INITIALIZING:
@@ -726,8 +723,8 @@ class LLParamSingleton : public LLSingleton<DERIVED_TYPE>
             return lk->mInstance;
 
         case super::DELETED:
-            super::logerrs("Trying to access deleted param singleton ",
-                           super::template classname<DERIVED_TYPE>().c_str());
+            super::logerrs({"Trying to access deleted param singleton ",
+                           super::template classname<DERIVED_TYPE>()});
             break;
         }
 
-- 
GitLab