summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2023-09-12 10:06:03 -0400
committerNat Goodspeed <nat@lindenlab.com>2023-09-12 10:06:03 -0400
commit24d405048fa0b9b26d1cb1d9e8ea8b113b867a14 (patch)
treec4d41334c00ea443193d201a4d7314e6965acd24 /indra
parent796085fc537e6bac7999e4b6624f02196eeaf4ad (diff)
DRTVWR-588: Move LLSingleton dependency on LLMainThreadTask to .cpp.
Introduce LLSingletonBase::getInstanceForSecondaryThread(), used both by LLSingleton and LLParamSingleton. Because it's a method of the non-template base class, because it's not itself a template method, getInstanceForSecondaryThread()'s definition can live in llsingleton.cpp. This is what calls LLMainThreadTask::dispatch(). To support LLParamSingleton, though, getInstanceForSecondaryThread() must be capable of handling arguments. For that, it accepts a nullary std::function returning the LLSingletonBase* of interest. Packing initParamSingleton() arguments into a nullary std::function to pass to getInstanceForSecondaryThread() sounds like a job for std::bind(). Unfortunately std::bind() has trouble forwarding int and string literals to a function that infers its argument types. To work around that, use boost::call_traits::param_type and a lambda with an explicit tuple.
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/llsingleton.cpp31
-rw-r--r--indra/llcommon/llsingleton.h72
2 files changed, 70 insertions, 33 deletions
diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp
index 6b1986d0e9..5c99048123 100644
--- a/indra/llcommon/llsingleton.cpp
+++ b/indra/llcommon/llsingleton.cpp
@@ -27,11 +27,12 @@
#include "linden_common.h"
#include "llsingleton.h"
+#include "llcoros.h"
+#include "lldependencies.h"
#include "llerror.h"
#include "llerrorcontrol.h"
-#include "lldependencies.h"
#include "llexception.h"
-#include "llcoros.h"
+#include "llmainthreadtask.h"
#include <boost/foreach.hpp>
#include <algorithm>
#include <iostream> // std::cerr in dire emergency
@@ -486,3 +487,29 @@ std::string LLSingletonBase::demangle(const char* mangled)
{
return LLError::Log::demangle(mangled);
}
+
+LLSingletonBase* LLSingletonBase::getInstanceForSecondaryThread(
+ const std::string& name,
+ const std::string& method,
+ const std::function<LLSingletonBase*()>& getInstance)
+{
+ // 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
+ // the calling thread 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.
+ return LLMainThreadTask::dispatch(
+ [&name, &method, &getInstance](){
+ // VERY IMPORTANT to call getInstance() on the main thread,
+ // rather than going straight to constructSingleton()!
+ // During the time window before mInitState is INITIALIZED,
+ // multiple requests might be queued. It's essential that, as
+ // the main thread processes them, only the FIRST such request
+ // actually constructs the instance -- every subsequent one
+ // simply returns the existing instance.
+ loginfos({name, "::", method, " on main thread"});
+ return getInstance();
+ });
+}
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index 51ef514cf7..caecc3594f 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -25,16 +25,18 @@
#ifndef LLSINGLETON_H
#define LLSINGLETON_H
+#include <boost/call_traits.hpp>
#include <boost/noncopyable.hpp>
#include <boost/unordered_set.hpp>
+#include <functional>
#include <initializer_list>
#include <list>
#include <typeinfo>
#include <vector>
#include "mutex.h"
#include "lockstatic.h"
+#include "apply.h"
#include "llthread.h" // on_main_thread()
-#include "llmainthreadtask.h"
class LLSingletonBase: private boost::noncopyable
{
@@ -134,6 +136,17 @@ protected:
// internal wrapper around calls to cleanupSingleton()
void cleanup_();
+ // This method is where we dispatch to LLMainThreadTask to acquire the
+ // subclass LLSingleton instance when the first getInstance() call is from
+ // a secondary thread. We delegate to the .cpp file to untangle header
+ // circularity. It accepts a std::function referencing the subclass
+ // getInstance() method -- which can't be virtual because it's static; we
+ // don't yet have an instance! For messaging, it also accepts the name of
+ // the subclass and the subclass method.
+ static LLSingletonBase* getInstanceForSecondaryThread(
+ const std::string& name, const std::string& method,
+ const std::function<LLSingletonBase*()>& getInstance);
+
// deleteSingleton() isn't -- and shouldn't be -- a virtual method. It's a
// class static. However, given only Foo*, deleteAll() does need to be
// able to reach Foo::deleteSingleton(). Make LLSingleton (which declares
@@ -555,19 +568,11 @@ public:
// Per the comment block above, dispatch to the main thread.
loginfos({classname<DERIVED_TYPE>(),
"::getInstance() dispatching to main thread"});
- auto instance = LLMainThreadTask::dispatch(
- [](){
- // VERY IMPORTANT to call getInstance() on the main thread,
- // rather than going straight to constructSingleton()!
- // During the time window before mInitState is INITIALIZED,
- // multiple requests might be queued. It's essential that, as
- // 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>(),
- "::getInstance() on main thread"});
- return getInstance();
- });
+ auto instance = static_cast<DERIVED_TYPE*>(
+ getInstanceForSecondaryThread(
+ classname<DERIVED_TYPE>(),
+ "getInstance()",
+ 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)
@@ -632,8 +637,14 @@ private:
// Passes arguments to DERIVED_TYPE's constructor and sets appropriate
// states, returning a pointer to the new instance.
+ // If we just let initParamSingleton_() infer its argument types, the
+ // compiler has trouble passing int and string literals. Use
+ // boost::call_traits::param_type to smooth parameter passing. This
+ // construction requires, though, that each invocation of this method
+ // explicitly specify template arguments, instead of inferring them.
template <typename... Args>
- static DERIVED_TYPE* initParamSingleton_(Args&&... args)
+ static LLSingletonBase* initParamSingleton_(
+ typename boost::call_traits<Args>::param_type... args)
{
// In case racing threads both call initParamSingleton() at the same
// time, serialize them. One should initialize; the other should see
@@ -652,7 +663,7 @@ private:
// on the main thread, simply construct instance while holding lock
super::logdebugs({super::template classname<DERIVED_TYPE>(),
"::initParamSingleton()"});
- super::constructSingleton(lk, std::forward<Args>(args)...);
+ super::constructSingleton(lk, args...);
return lk->mInstance;
}
else
@@ -665,20 +676,19 @@ private:
lk.unlock();
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
- // 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>(),
- "::initParamSingleton() on main thread"});
- return initParamSingleton_(std::forward<Args>(args)...);
- });
+ auto instance = static_cast<DERIVED_TYPE*>(
+ super::getInstanceForSecondaryThread(
+ super::template classname<DERIVED_TYPE>(),
+ "initParamSingleton()",
+ // This lambda does what std::bind() is supposed to do --
+ // but when the actual parameter is (e.g.) a string
+ // literal, type inference makes it fail. Apply param_type
+ // to each incoming type to make it work.
+ [args=std::tuple<typename boost::call_traits<Args>::param_type...>(args...)]
+ ()
+ {
+ return LL::apply(initParamSingleton_<Args...>, args);
+ }));
super::loginfos({super::template classname<DERIVED_TYPE>(),
"::initParamSingleton() returning on requesting thread"});
return instance;
@@ -695,7 +705,7 @@ public:
template <typename... Args>
static DERIVED_TYPE& initParamSingleton(Args&&... args)
{
- return *initParamSingleton_(std::forward<Args>(args)...);
+ return *static_cast<DERIVED_TYPE*>(initParamSingleton_<Args...>(args...));
}
static DERIVED_TYPE* getInstance()