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.
master
Nat Goodspeed 2021-05-10 15:21:51 -04:00
parent ce65bc2f13
commit 95d8085fa4
2 changed files with 75 additions and 72 deletions

View File

@ -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)
{

View File

@ -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 @@ protected:
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 @@ private:
// 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 @@ private:
// 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 @@ public:
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 @@ public:
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 @@ public:
} // 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 @@ public:
// 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 @@ private:
// 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 @@ private:
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 @@ private:
// 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 @@ public:
{
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 @@ public:
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;
}