summaryrefslogtreecommitdiff
path: root/indra/llcommon/lockstatic.h
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/lockstatic.h')
-rw-r--r--indra/llcommon/lockstatic.h73
1 files changed, 73 insertions, 0 deletions
diff --git a/indra/llcommon/lockstatic.h b/indra/llcommon/lockstatic.h
new file mode 100644
index 0000000000..96c53c6473
--- /dev/null
+++ b/indra/llcommon/lockstatic.h
@@ -0,0 +1,73 @@
+/**
+ * @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 <typename Static>
+class LockStatic
+{
+ typedef std::unique_lock<decltype(Static::mMutex)> 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::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<mutex_t*> -- but a default-constructed
+ // std::atomic<T> 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;
+ }
+};
+
+} // llthread namespace
+
+#endif /* ! defined(LL_LOCKSTATIC_H) */