From 7e9c5dd0a3e583a9bec4e99d99b2efca358c6612 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 12 Dec 2019 17:17:06 -0500 Subject: 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(-) (limited to 'indra/llcommon/llsingleton.h') diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 39d0e9b013..314ee2caa8 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -59,6 +59,7 @@ protected: 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 @@ public: " -- 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 @@ public: 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().c_str(), "::getInstance() dispatching to main thread"); auto instance = LLMainThreadTask::dispatch( @@ -588,7 +597,7 @@ public: // suppresses dep tracking when dispatched to the main thread) capture_dependency(instance); loginfos(classname().c_str(), - "::getInstance() returning on invoking thread"); + "::getInstance() returning on requesting thread"); return instance; } @@ -672,11 +681,40 @@ public: " 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)...); 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().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().c_str(), + "::initParamSingleton() on main thread"); + return initParamSingleton(std::forward(args)...); + }); + super::loginfos(super::template classname().c_str(), + "::initParamSingleton() returning on requesting thread"); + return instance; + } } static DERIVED_TYPE* getInstance() @@ -688,6 +726,7 @@ public: switch (lk->mInitState) { case super::UNINITIALIZED: + case super::QUEUED: super::logerrs("Uninitialized param singleton ", super::template classname().c_str()); break; -- cgit v1.2.3