From 1f7335fde34abdb9889e0c7b437fc02870570fcf Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 3 Dec 2019 20:44:26 -0500 Subject: DRTVWR-494: Extract LockStatic as a standalone template class. The pattern of requiring a lock to permit *any* access to a static instance of something seems generally useful. Break out lockstatic.h; recast LLInstanceTracker to use it. Moving LockStatic to an external template class instead of a nested class in LLInstanceTrackerBase leaves LLInstanceTrackerBase pretty empty. Get rid of it. And *that* means we can move the definition of the StaticData used by each LLInstanceTracker specialization into the class itself, rather than having to define it beforehand in namespace LLInstanceTrackerStuff. --- indra/llcommon/lockstatic.h | 56 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 indra/llcommon/lockstatic.h (limited to 'indra/llcommon/lockstatic.h') diff --git a/indra/llcommon/lockstatic.h b/indra/llcommon/lockstatic.h new file mode 100644 index 0000000000..5f08742cae --- /dev/null +++ b/indra/llcommon/lockstatic.h @@ -0,0 +1,56 @@ +/** + * @file lockstatic.h + * @author Nat Goodspeed + * @date 2019-12-03 + * @brief LockStatic class provides mutex-guarded access to the specified + * static data. + * + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ + * Copyright (c) 2019, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LOCKSTATIC_H) +#define LL_LOCKSTATIC_H + +#include "mutex.h" // std::unique_lock + +namespace llthread +{ + +// Instantiate this template to obtain a pointer to the canonical static +// instance of Static while holding a lock on that instance. Use of +// Static::mMutex presumes that Static declares some suitable mMutex. +template +class LockStatic +{ + typedef std::unique_lock lock_t; +public: + LockStatic(): + mData(getStatic()), + mLock(mData->mMutex) + {} + Static* get() const { return mData; } + operator Static*() const { return get(); } + Static* operator->() const { return get(); } + // sometimes we must explicitly unlock... + void unlock() + { + // but once we do, access is no longer permitted + mData = nullptr; + mLock.unlock(); + } +protected: + Static* mData; + lock_t mLock; +private: + Static* getStatic() + { + static Static sData; + return &sData; + } +}; + +} // llthread namespace + +#endif /* ! defined(LL_LOCKSTATIC_H) */ -- cgit v1.2.3 From ba8fe9b483aaa59e724cdbfa183c69ee5998dc1f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 4 Dec 2019 15:44:32 -0500 Subject: DRTVWR-494: Move explanatory comments from LLSingleton to LockStatic. --- indra/llcommon/lockstatic.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'indra/llcommon/lockstatic.h') diff --git a/indra/llcommon/lockstatic.h b/indra/llcommon/lockstatic.h index 5f08742cae..96c53c6473 100644 --- a/indra/llcommon/lockstatic.h +++ b/indra/llcommon/lockstatic.h @@ -46,6 +46,23 @@ protected: private: Static* getStatic() { + // Static::mMutex must be function-local static rather than class- + // static. Some of our consumers must function properly (therefore + // lock properly) even when the containing module's static variables + // have not yet been runtime-initialized. A mutex requires + // construction. A static class member might not yet have been + // constructed. + // + // We could store a dumb mutex_t*, notice when it's NULL and allocate a + // heap mutex -- but that's vulnerable to race conditions. And we can't + // defend the dumb pointer with another mutex. + // + // We could store a std::atomic -- but a default-constructed + // std::atomic does not contain a valid T, even a default-constructed + // T! Which means std::atomic, too, requires runtime initialization. + // + // But a function-local static is guaranteed to be initialized exactly + // once: the first time control reaches that declaration. static Static sData; return &sData; } -- cgit v1.2.3