From 8c6f752982e83a50e328b9c83f762721f38836d7 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Sep 2011 22:07:49 -0400 Subject: STORM-1541: Hoist LLInstanceTracker::getMap_() to base getStatic(). Generalize the notion of getting some chunk of "static" storage: introduce LLInstanceTrackerBase::getStatic() template method. Define StaticData struct containing the InstanceMap (or InstanceSet, for that specialization) along with the S32 that caused the VS2010 linker so much grief. Completely eliminate that S32 as an actual class-static member, qualifying all references with the struct returned by getStatic(). In LLInstanceTrackerBase::getInstances(), use one std::map lookup instead of three. --- indra/llcommon/llinstancetracker.cpp | 19 ++++--- indra/llcommon/llinstancetracker.h | 105 ++++++++++++++++++++--------------- 2 files changed, 71 insertions(+), 53 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llinstancetracker.cpp b/indra/llcommon/llinstancetracker.cpp index f576204511..5dc3ea5d7b 100644 --- a/indra/llcommon/llinstancetracker.cpp +++ b/indra/llcommon/llinstancetracker.cpp @@ -35,14 +35,15 @@ //static void * & LLInstanceTrackerBase::getInstances(std::type_info const & info) { - static std::map instances; + typedef std::map InstancesMap; + static InstancesMap instances; - std::string k = info.name(); - if(instances.find(k) == instances.end()) - { - instances[k] = NULL; - } - - return instances[k]; + // std::map::insert() is just what we want here. You attempt to insert a + // (key, value) pair. If the specified key doesn't yet exist, it inserts + // the pair and returns a std::pair of (iterator, true). If the specified + // key DOES exist, insert() simply returns (iterator, false). One lookup + // handles both cases. + return instances.insert(InstancesMap::value_type(info.name(), + InstancesMap::mapped_type())) + .first->second; } - diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index afb714c71c..936bef850a 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -29,6 +29,7 @@ #define LL_LLINSTANCETRACKER_H #include +#include #include "string_table.h" #include @@ -37,10 +38,40 @@ #include #include +/** + * Base class manages "class-static" data that must actually have singleton + * semantics: one instance per process, rather than one instance per module as + * sometimes happens with data simply declared static. + */ class LL_COMMON_API LLInstanceTrackerBase : public boost::noncopyable { - protected: - static void * & getInstances(std::type_info const & info); +protected: + /// Get a process-unique void* pointer slot for the specified type_info + static void * & getInstances(std::type_info const & info); + + /// Find or create a STATICDATA instance for the specified TRACKED class. + /// STATICDATA must be default-constructible. + template + static STATICDATA& getStatic() + { + void *& instances = getInstances(typeid(TRACKED)); + if (! instances) + { + instances = new STATICDATA; + } + return *static_cast(instances); + } + + /// It's not essential to derive your STATICDATA (for use with + /// getStatic()) from StaticBase; it's just that both known + /// implementations do. + struct StaticBase + { + StaticBase(): + sIterationNestDepth(0) + {} + S32 sIterationNestDepth; + }; }; /// This mix-in class adds support for tracking all instances of the specified class parameter T @@ -50,8 +81,15 @@ class LL_COMMON_API LLInstanceTrackerBase : public boost::noncopyable template class LLInstanceTracker : public LLInstanceTrackerBase { - typedef typename std::map InstanceMap; typedef LLInstanceTracker MyT; + typedef typename std::map InstanceMap; + struct StaticData: public StaticBase + { + InstanceMap sMap; + }; + static StaticData& getStatic() { return LLInstanceTrackerBase::getStatic(); } + static InstanceMap& getMap_() { return getStatic().sMap; } + public: class instance_iter : public boost::iterator_facade { @@ -61,12 +99,12 @@ public: instance_iter(const typename InstanceMap::iterator& it) : mIterator(it) { - ++sIterationNestDepth; + ++getStatic().sIterationNestDepth; } ~instance_iter() { - --sIterationNestDepth; + --getStatic().sIterationNestDepth; } @@ -95,18 +133,18 @@ public: key_iter(typename InstanceMap::iterator it) : mIterator(it) { - ++sIterationNestDepth; + ++getStatic().sIterationNestDepth; } key_iter(const key_iter& other) : mIterator(other.mIterator) { - ++sIterationNestDepth; + ++getStatic().sIterationNestDepth; } ~key_iter() { - --sIterationNestDepth; + --getStatic().sIterationNestDepth; } @@ -159,8 +197,8 @@ protected: virtual ~LLInstanceTracker() { // it's unsafe to delete instances of this type while all instances are being iterated over. - llassert(sIterationNestDepth == 0); - remove_(); + llassert(getStatic().sIterationNestDepth == 0); + remove_(); } virtual void setKey(KEY key) { remove_(); add_(key); } virtual const KEY& getKey() const { return mInstanceKey; } @@ -176,31 +214,24 @@ private: getMap_().erase(mInstanceKey); } - static InstanceMap& getMap_() - { - void * & instances = getInstances(typeid(MyT)); - if (! instances) - { - instances = new InstanceMap; - } - return * static_cast(instances); - } - private: - KEY mInstanceKey; - static S32 sIterationNestDepth; }; -template S32 LLInstanceTracker::sIterationNestDepth = 0; - /// explicit specialization for default case where KEY is T* /// use a simple std::set template class LLInstanceTracker : public LLInstanceTrackerBase { - typedef typename std::set InstanceSet; typedef LLInstanceTracker MyT; + typedef typename std::set InstanceSet; + struct StaticData: public StaticBase + { + InstanceSet sSet; + }; + static StaticData& getStatic() { return LLInstanceTrackerBase::getStatic(); } + static InstanceSet& getSet_() { return getStatic().sSet; } + public: /// for completeness of analogy with the generic implementation @@ -213,18 +244,18 @@ public: instance_iter(const typename InstanceSet::iterator& it) : mIterator(it) { - ++sIterationNestDepth; + ++getStatic().sIterationNestDepth; } instance_iter(const instance_iter& other) : mIterator(other.mIterator) { - ++sIterationNestDepth; + ++getStatic().sIterationNestDepth; } ~instance_iter() { - --sIterationNestDepth; + --getStatic().sIterationNestDepth; } private: @@ -250,13 +281,13 @@ public: protected: LLInstanceTracker() { - // it's safe but unpredictable to create instances of this type while all instances are being iterated over. I hate unpredictable. This assert will probably be turned on early in the next development cycle. + // it's safe but unpredictable to create instances of this type while all instances are being iterated over. I hate unpredictable. This assert will probably be turned on early in the next development cycle. getSet_().insert(static_cast(this)); } virtual ~LLInstanceTracker() { // it's unsafe to delete instances of this type while all instances are being iterated over. - llassert(sIterationNestDepth == 0); + llassert(getStatic().sIterationNestDepth == 0); getSet_().erase(static_cast(this)); } @@ -264,20 +295,6 @@ protected: { getSet_().insert(static_cast(this)); } - - static InstanceSet& getSet_() - { - void * & instances = getInstances(typeid(MyT)); - if (! instances) - { - instances = new InstanceSet; - } - return * static_cast(instances); - } - - static S32 sIterationNestDepth; }; -template S32 LLInstanceTracker::sIterationNestDepth = 0; - #endif -- cgit v1.2.3 From b4a1d339fc9bc69c1045a622bf59df815fdc77ad Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Sep 2011 17:29:08 -0400 Subject: STORM-1541: Add LLInstanceTracker tests for active-iterator asserts. The recent class-static LLInstanceTracker::instance_iter and key_iter reference count is intended to guard against deleting an instance of an LLInstanceTracker subclass during iteration. Add tests for that functionality. --- indra/llcommon/tests/llinstancetracker_test.cpp | 64 +++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'indra') diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp index 80b35bbdc3..b34d1c5fd3 100644 --- a/indra/llcommon/tests/llinstancetracker_test.cpp +++ b/indra/llcommon/tests/llinstancetracker_test.cpp @@ -40,6 +40,7 @@ #include // other Linden headers #include "../test/lltut.h" +#include "wrapllerrs.h" struct Keyed: public LLInstanceTracker { @@ -165,4 +166,67 @@ namespace tut ensure_equals("unreported instance", instances.size(), 0); } + + template<> template<> + void object::test<5>() + { + set_test_name("delete Keyed with outstanding instance_iter"); + std::string what; + Keyed* keyed = new Keyed("one"); + { + WrapLL_ERRS wrapper; + Keyed::instance_iter i(Keyed::beginInstances()); + try + { + delete keyed; + } + catch (const WrapLL_ERRS::FatalException& e) + { + what = e.what(); + } + } + ensure(! what.empty()); + } + + template<> template<> + void object::test<6>() + { + set_test_name("delete Keyed with outstanding key_iter"); + std::string what; + Keyed* keyed = new Keyed("one"); + { + WrapLL_ERRS wrapper; + Keyed::key_iter i(Keyed::beginKeys()); + try + { + delete keyed; + } + catch (const WrapLL_ERRS::FatalException& e) + { + what = e.what(); + } + } + ensure(! what.empty()); + } + + template<> template<> + void object::test<7>() + { + set_test_name("delete Unkeyed with outstanding instance_iter"); + std::string what; + Unkeyed* unkeyed = new Unkeyed; + { + WrapLL_ERRS wrapper; + Unkeyed::instance_iter i(Unkeyed::beginInstances()); + try + { + delete unkeyed; + } + catch (const WrapLL_ERRS::FatalException& e) + { + what = e.what(); + } + } + ensure(! what.empty()); + } } // namespace tut -- cgit v1.2.3 From 286342f705fa246c4c417a874be6b03cd6fe38d3 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 7 Sep 2011 18:03:25 -0400 Subject: STORM-1541: Change llassert() to llassert_always(): unit tests expect. Now that we have unit tests that require assertion failure if you try to delete an LLInstanceTracker subclass instance with an iterator loose, having llassert() "sometimes" compile away (whimsically, depending on platform as well as build type!) makes those tests fail. Use llassert_always() instead. --- indra/llcommon/llinstancetracker.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra') diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 936bef850a..5a3990a8df 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -197,7 +197,7 @@ protected: virtual ~LLInstanceTracker() { // it's unsafe to delete instances of this type while all instances are being iterated over. - llassert(getStatic().sIterationNestDepth == 0); + llassert_always(getStatic().sIterationNestDepth == 0); remove_(); } virtual void setKey(KEY key) { remove_(); add_(key); } @@ -287,7 +287,7 @@ protected: virtual ~LLInstanceTracker() { // it's unsafe to delete instances of this type while all instances are being iterated over. - llassert(getStatic().sIterationNestDepth == 0); + llassert_always(getStatic().sIterationNestDepth == 0); getSet_().erase(static_cast(this)); } -- cgit v1.2.3